第一篇:北郵嵌入式實驗報告
嵌入式系統(tǒng)開發(fā)實驗報告
班
級: 姓
名: 班內(nèi)序號: 學(xué)
號: 日
期:
目錄
一、實驗?zāi)康?.............................................................................................1
二、實驗設(shè)備..............................................................................................1
三、基礎(chǔ)實驗(實驗一~實驗七)............................................................1
1.實驗五..................................................................................................2 2.實驗六..................................................................................................2 3.實驗七..................................................................................................2
四、驅(qū)動程序..............................................................................................3
1.設(shè)備驅(qū)動程序的概念..........................................................................3 2.驅(qū)動程序結(jié)構(gòu)......................................................................................3 3.設(shè)備注冊和初始化..............................................................................4 4.設(shè)備驅(qū)動程序的開發(fā)過程..................................................................6
五、基本接口實驗......................................................................................6
1.實驗十二 簡單設(shè)備驅(qū)動程序............................................................6 2.實驗十三 CPU GPIO驅(qū)動程序設(shè)計...................................................7 3.實驗十四 中斷實驗...........................................................................8 4.實驗十五 數(shù)碼管顯示實驗................................................................9 5.實驗十六 LED點陣驅(qū)動程序設(shè)計...................................................11 6.實驗十七 AD驅(qū)動實驗....................................................................12 7.實驗十八 DA驅(qū)動實驗....................................................................13
六、實驗中遇到的問題及解決方法........................................................15
七、實驗總結(jié)及心得................................................................................15
一、實驗?zāi)康?/p>
通過實驗熟悉Linux環(huán)境,并掌握一些基本接口驅(qū)動的寫法和用C語言編寫簡單的實驗程序。學(xué)習(xí)LINUX開發(fā)環(huán)境的搭建,通訊配置等。并熟練掌握LINUX驅(qū)動程序的編寫及開發(fā)流程。對嵌入式系統(tǒng)有進(jìn)一步的了解。
二、實驗設(shè)備
1.一套PXA270EP嵌入式實驗箱
2.安裝Redhat9的宿主PC機,并且配置好ARM Linux的開發(fā)環(huán)境
三、基礎(chǔ)實驗(實驗一~實驗七)
實驗一到六為基礎(chǔ)實驗,主要是為了在熟悉實驗操作平臺的同時為后續(xù)實驗搭建好軟、硬件環(huán)境,配置好相關(guān)的協(xié)議、服務(wù)。
其中實驗一是各個硬件的互聯(lián),搭建好了實驗的硬件環(huán)境。實驗二是在宿主PC端安裝虛擬機,提供了實驗需要的Linux操作系統(tǒng)。實驗三是宿主PC端開發(fā)環(huán)境的安裝與配置。
實驗四是配置宿主PC機端的超級終端,使PC機與PXA270目標(biāo)板之間可以通過串口通訊。在每次重啟宿主PC機時,都需要重新將超級終端掛載到虛擬機上,掛載之前須通過ifconfig命令查看該機的IP地址,若其已經(jīng)復(fù)位,須用命令:ifconfig eth0 192.168.0.100 up重置宿主PC機的IP地址。掛載虛擬機的代碼為:
root ifconfig eth0 192.168.0.50 up mount –o nolock 192.168.0.100:/ /mnt 實驗五是配置宿主PC機的TFTP服務(wù)。TFTP是簡單文件傳輸協(xié)議。每次重啟宿主PC機時,都要重啟該服務(wù),重啟命令為:
service xinetd restart。
實驗六是配置宿主PC機端NFS服務(wù)。NFS是指網(wǎng)絡(luò)文件系統(tǒng),它實現(xiàn)了文件在不同的系統(tǒng)間使用。當(dāng)使用者想用遠(yuǎn)端檔案時,只需調(diào)用“mount”就可以遠(yuǎn)端系統(tǒng)掛接在自己的檔案系統(tǒng)之下。每次重啟宿主PC機時,也都要重啟該服務(wù),重啟命令為: service nfs restart service nfs restart
1.實驗五
實驗五為宿主PC機配置了TFTP服務(wù)。TFTP(Trivial File Transfer Protocol)是簡單文件傳輸協(xié)議,由于特定開發(fā)環(huán)境的制約,這一服務(wù)是必須的。在配置完成后,每次重啟宿主PC機時,都須先輸入命令:service xinetd restart,以啟動TFTP服務(wù)。
2.實驗六
實驗六為宿主PC機配置了NFS服務(wù)。NFS(Network File System)指網(wǎng)絡(luò)文件系統(tǒng),它實現(xiàn)了文件在不同的系統(tǒng)間使用。當(dāng)我們想用遠(yuǎn)端檔案時,只需調(diào)用“mount”就可以遠(yuǎn)端系統(tǒng)掛接在自己的檔案系統(tǒng)之下。每次重啟宿主PC機時,都須先輸入命令:service nfs restart,以啟動nfs服務(wù)。
3.實驗七
實驗七通過用c語言編寫的簡單程序HelloWorld,測試前面幾個實驗是否成功配置好環(huán)境,從超級終端可以看到HelloWorld程序的運行結(jié)果。
四、驅(qū)動程序
1.設(shè)備驅(qū)動程序的概念
設(shè)備驅(qū)動程序?qū)嶋H是處理和操作硬件控制器的軟件,從本質(zhì)上講,是內(nèi)核中具有最高特權(quán)級的、駐留內(nèi)存的、可共享的底層硬件處理例程。驅(qū)動程序是內(nèi)核的一部分,是操作系統(tǒng)內(nèi)核與硬件設(shè)備的直接接口,驅(qū)動程序屏蔽了硬件的細(xì)節(jié),完成以下功能:
? 對設(shè)備初始化和釋放;
? 對設(shè)備進(jìn)行管理,包括實時參數(shù)設(shè)置,以及提供對設(shè)備的操作接口; ? 讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)或者回送應(yīng)用程序請求的數(shù)據(jù); ? 檢測和處理設(shè)備出現(xiàn)的錯誤。
Linux操作系統(tǒng)將所有的設(shè)備全部看成文件,并通過文件的操作界面進(jìn)行操作。對用戶程序而言,設(shè)備驅(qū)動程序隱藏了設(shè)備的具體細(xì)節(jié),對各種不同設(shè)備提供了一致的接口,一般來說,是把設(shè)備映射為一個特殊的設(shè)備文件,用戶程序可以像對其他文件一樣對此設(shè)備文件進(jìn)行操作。這意味著:
? 由于每一個設(shè)備至少由文件系統(tǒng)的一個文件代表,因而都有一個“文件名”。? 應(yīng)用程序通??梢酝ㄟ^系統(tǒng)調(diào)用open()打開設(shè)備文件,建立起與目標(biāo)設(shè)備的連接。
? 打開了代表著目標(biāo)設(shè)備的文件,即建立起與設(shè)備的連接后,可以通過read()、write()、ioctl()等常規(guī)的文件操作對目標(biāo)設(shè)備進(jìn)行操作。
設(shè)備文件的屬性由三部分信息組成:第一部分是文件的類型,第二部分是一個主設(shè)備號,第三部分是一個次設(shè)備號。其中類型和主設(shè)備號結(jié)合在一起惟一地確定了設(shè)備文件驅(qū)動程序及其界面,而次設(shè)備號則說明目標(biāo)設(shè)備是同類設(shè)備中的第幾個。
由于Linux 中將設(shè)備當(dāng)做文件處理,所以對設(shè)備進(jìn)行操作的調(diào)用格式與對文件的操作類似,主要包括open()、read()、write()、ioctl()、close()等。應(yīng)用程序發(fā)出系統(tǒng)調(diào)用命令后,會從用戶態(tài)轉(zhuǎn)到核心態(tài),通過內(nèi)核將open()這樣的系統(tǒng)調(diào)用轉(zhuǎn)換成對物理設(shè)備的操作。
2.驅(qū)動程序結(jié)構(gòu)
一個設(shè)備驅(qū)動程序模塊的基本框架
在系統(tǒng)內(nèi)部,I/O設(shè)備的存取通過一組固定的入口點來進(jìn)行,入口點也可以理解為設(shè)備的句柄,就是對設(shè)備進(jìn)行操作的基本函數(shù)。字符型設(shè)備驅(qū)動程序提供如下幾個入口點:
?
open入口點。打開設(shè)備準(zhǔn)備I/O操作。對字符設(shè)備文件進(jìn)行打開操作,都會調(diào)用設(shè)備的open入口點。open子程序必須對將要進(jìn)行的I/O操作做好必要的準(zhǔn)備工作,如清除緩沖區(qū)等。如果設(shè)備是獨占的,即同一時刻只能有一個程序訪問此設(shè)備,則open子程序必須設(shè)置一些標(biāo)志以表示設(shè)備處于忙狀態(tài)。
?
close入口點。關(guān)閉一個設(shè)備。當(dāng)最后一次使用設(shè)備完成后,調(diào)用close子程序。獨占設(shè)備必須標(biāo)記設(shè)備方可再次使用。
?
read入口點。從設(shè)備上讀數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數(shù)據(jù)。對字符設(shè)備文件進(jìn)行讀操作將調(diào)用read子程序。
?
write入口點。往設(shè)備上寫數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是把數(shù)據(jù)寫入緩沖區(qū)里。對字符設(shè)備文件進(jìn)行寫操作將調(diào)用write子程序。
?
ioctl入口點。執(zhí)行讀、寫之外的操作。
select入口點。檢查設(shè)備,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。select系統(tǒng)調(diào)用在檢查與設(shè)備文件相關(guān)的文件描述符時使用select入口點。
3.設(shè)備注冊和初始化
設(shè)備的驅(qū)動程序在加載的時候首先需要調(diào)用入口函數(shù)init_module(),該函數(shù)最重要的一個工作就是向內(nèi)核注冊該設(shè)備,對于字符設(shè)備調(diào)用register_chrdev()完成注冊。register_chrdev 的定義為:int register_chrdev(unsigned int major, const char *name, struct file_ operations *fops);其中,major是為設(shè)備驅(qū)動程序向系統(tǒng)申請的主設(shè)備號,如果為0,則系統(tǒng)為此驅(qū)動程序動態(tài)分配一個主設(shè)備號。name是設(shè)備名,fops是對各個調(diào)用的入口點說明。此函數(shù)返回0時表示成功;返回-EINVAL,表示申請的主設(shè)備號非法,主要原因是主設(shè)備號大于系統(tǒng)所允許的最大設(shè)備號;返回-EBUSY,表示所申請的主設(shè)備號正在被其他設(shè)備程序使用。如果動態(tài)分配主設(shè)備號成功,此函數(shù)將返回所分配 的主設(shè)備號。如果register_chrdev()操作成功,設(shè)備名就會出現(xiàn)在/proc/dvices文件中。
Linux在/dev目錄中為每個設(shè)備建立一個文件,用ls –l命令列出函數(shù)返回值,若小于0,則表示注冊失敗;返回0或者大于0的值表示注冊成功。注冊以后,Linux將設(shè)備名與主、次設(shè)備號聯(lián)系起來。當(dāng)有對此設(shè)備名的訪問時,Linux通過請求訪問的設(shè)備名得到主、次設(shè)備號,然后把此訪問分發(fā)到對應(yīng)的設(shè)備驅(qū)動,設(shè)備驅(qū)動再根據(jù)次設(shè)備號調(diào)用不同的函數(shù)。
當(dāng)設(shè)備驅(qū)動模塊從Linux內(nèi)核中卸載,對應(yīng)的主設(shè)備號必須被釋放。字符設(shè)備在cleanup_ module()函數(shù)中調(diào)用unregister_chrdev()來完成設(shè)備的注銷。unregister_chrdev()的定義為:int unregister_chrdev(unsigned int major, const char *name);包括設(shè)備注冊在內(nèi),設(shè)備驅(qū)動的初始化函數(shù)主要完成的功能是有以下5項。(1)對驅(qū)動程序管理的硬件進(jìn)行必要的初始化。
對硬件寄存器進(jìn)行設(shè)置。比如,設(shè)置中斷掩碼,設(shè)置串口的工作方式、并口的數(shù)據(jù)方向等。
(2)初始化設(shè)備驅(qū)動相關(guān)的參數(shù)。
一般說來,每個設(shè)備都要定義一個設(shè)備變量,用以保存設(shè)備相關(guān)的參數(shù)。在這一步驟里對設(shè)備變量中的項進(jìn)行初始化。
(3)在內(nèi)核注冊設(shè)備。
調(diào)用register_chrdev()函數(shù)來注冊設(shè)備。(4)注冊中斷。
如果設(shè)備需要IRQ支持,則要使用request_irq()函數(shù)注冊中斷。(5)其他初始化工作。
初始化部分一般還負(fù)責(zé)給設(shè)備驅(qū)動程序申請包括內(nèi)存、時鐘、I/O端口等在內(nèi)的系統(tǒng)資源,這些資源也可以在open子程序或者其他地方申請。這些資源不用時,應(yīng)該釋放,以利于資源的共享。
若驅(qū)動程序是內(nèi)核的一部分,初始化函數(shù)則要按如下方式聲明: int __init chr_driver_init(void);其中__init是必不可少的,在系統(tǒng)啟動時會由內(nèi)核調(diào)用chr_driver_init,完成驅(qū)動程序的初始化。
當(dāng)驅(qū)動程序是以模塊的形式編寫時,則要按照如下方式聲明: int init_module(void)當(dāng)運行后面介紹的insmod命令插入模塊時,會調(diào)用init_module函數(shù)完成初始化工作。
4.設(shè)備驅(qū)動程序的開發(fā)過程
由于嵌入式設(shè)備由于硬件種類非常豐富,在默認(rèn)的內(nèi)核發(fā)布版中不一定包括所有驅(qū)動程序。所以進(jìn)行嵌入式Linux系統(tǒng)的開發(fā),很大的工作量是為各種設(shè)備編寫驅(qū)動程序。除非系統(tǒng)不使用操作系統(tǒng),程序直接操縱硬件。嵌入式Linux系統(tǒng)驅(qū)動程序開發(fā)與普通Linux開發(fā)沒有區(qū)別。可以在硬件生產(chǎn)廠家或者Internet上尋找驅(qū)動程序,也可以根據(jù)相近的硬件驅(qū)動程序來改寫,這樣可以加快開發(fā)速度。實現(xiàn)一個嵌入式Linux設(shè)備驅(qū)動的大致流程如下。
(1)查看原理圖,理解設(shè)備的工作原理。一般嵌入式處理器的生產(chǎn)商提供參考電路,也可以根據(jù)需要自行設(shè)計。
(2)定義設(shè)備號。設(shè)備由一個主設(shè)備號和一個次設(shè)備號來標(biāo)識。主設(shè)備號惟一標(biāo)識了設(shè)備類型,即設(shè)備驅(qū)動程序類型,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表項的索引。次設(shè)備號僅由設(shè)備驅(qū)動程序解釋,區(qū)分被一個設(shè)備驅(qū)動控制下的某個獨立的設(shè)備。
(3)實現(xiàn)初始化函數(shù)。在驅(qū)動程序中實現(xiàn)驅(qū)動的注冊和卸載。(4)設(shè)計所要實現(xiàn)的文件操作,定義file_operations結(jié)構(gòu)。(5)實現(xiàn)所需的文件操作調(diào)用,如read、write等。
(6)實現(xiàn)中斷服務(wù),并用request_irq向內(nèi)核注冊,中斷并不是每個設(shè)備驅(qū)動所必需的。
(7)編譯該驅(qū)動程序到內(nèi)核中,或者用insmod命令加載模塊。(8)測試該設(shè)備,編寫應(yīng)用程序,對驅(qū)動程序進(jìn)行測試。
五、基本接口實驗
在完成了基本實驗后,我們開始著手基本接口實驗。在這些實驗中,我們學(xué)習(xí)如何編寫設(shè)備驅(qū)動程序,及如何用測試程序檢驗驅(qū)動程序是否正確,并通過改寫測試程序正常地對驅(qū)動程序進(jìn)行相關(guān)操作。
1.實驗十二 簡單設(shè)備驅(qū)動程序
本次實驗的任務(wù)是編寫一個字符型設(shè)備驅(qū)動程序,并學(xué)習(xí)在應(yīng)用程序中調(diào)用驅(qū)動??紤]到我們初次接觸驅(qū)動程序的編寫,對此還十分陌生,因此指導(dǎo)書中提供了本次實驗所要用到的程序源代碼。雖然這樣一個字符型設(shè)備驅(qū)動程序并沒有任何實際作用,但是它讓我們輕松掌握了嵌入式驅(qū)動的編寫過程,因為復(fù)雜繁瑣的驅(qū)動,其骨架都是相同的。因此,看懂本實驗的源代碼,學(xué)習(xí)并模仿其編寫方
法,對于后續(xù)實驗有著非常重要的意義。
2.實驗十三 CPU GPIO驅(qū)動程序設(shè)計
在本實驗中,我們要編寫第一個針對實際硬件的驅(qū)動程序。我們知道,凡是操作系統(tǒng)控制外部設(shè)備,即使是最簡單的硬件電路,也是需要驅(qū)動的。本實驗涉及的外部硬件只有電阻和發(fā)光二極管。我們使用自己編寫的驅(qū)動程序與應(yīng)用程序控制 GPIO96的電平,通過 LED 的亮滅來判斷,是否 CPU 做出了正確的響應(yīng)。
實驗第一步是編寫 PXA270 GPIO 驅(qū)動程序。驅(qū)動的寫法參照實驗十二,大體相同,主要區(qū)別如下:
//-------------------控制IO設(shè)備-----------------------switch(cmd){ case LED_ON : {GPCR3 |= 0x1;break;} 為1 case LED_OFF: {GPSR3 |= 0x1;break;} 為1
default :
{
//如果cmd=LED_OFF,那么GPSR3置//如果cmd=LED_ON,那么GPCR3置printk(“l(fā)cd control : no cmd run [--kernel--]n”);return(-EINVAL);} //-------------------驅(qū)動程序初始化--------------------------GPDR3 |= 0x00000001;//設(shè)置GPIO96輸出模式:開燈 GPSR3 |= 0x00000001;// 關(guān)燈
對Makefile中的目標(biāo)體和依賴文件也要做相應(yīng)的修改,此處省略。在測試程序中有這樣一段代碼: while(1){ ioctl(fd, LED_OFF);sleep(1);
// 休眠1秒
ioctl(fd,LED_ON);sleep(1);}
實驗作業(yè)要求在目標(biāo)板上LED閃爍產(chǎn)生亮7秒,滅5秒的效果,很容易實現(xiàn),只需將上面的代碼改為如下代碼即可:
while(1){ ioctl(fd, LED_OFF);sleep(5);
sleep(7);}
// 滅5秒 // 亮7秒 ioctl(fd,LED_ON);
3.實驗十四
中斷實驗
在理論課中,我們學(xué)習(xí)了許多中斷方面的知識,包括中斷向量、中斷優(yōu)先級、中斷過程等。在PXA270系統(tǒng)里,中斷控制器分外部設(shè)備和 PXA270X 處理器設(shè)備產(chǎn)生的兩個層次的中斷,前者是初級的中斷源,后者是次級中斷源,大量的次級中斷源通常被映射為一個初級中斷源。
在此實驗中,我們要編寫一個中斷程序,利用目標(biāo)板上的按鍵SW2來產(chǎn)生中斷,使得當(dāng)每次按下此按鍵時,在超級終端上打印出響應(yīng)的信息。編寫中斷程序與前兩個實驗的主要區(qū)別如下:
/*--------------------------初始化
request_irq申請硬件中斷,參數(shù)包括申請的硬件中斷號、設(shè)備id、中斷處理的
一些屬性(SA_INTERRUPT是快速處理程序,調(diào)用時屏蔽所有中斷)等
-----------------*/ ret = request_irq(SIMPLE_INT_IRQ, &SIMPLE_INT_interrupt, SA_INTERRUPT, “int_ctl”, NULL);//--------------------卸載,對應(yīng)request_irq釋放中斷---------------------free_irq(SIMPLE_INT_IRQ,NULL);通過此實驗,我了解了硬件中斷管腳與中斷號的對應(yīng)關(guān)系,以及中斷號與中斷處理程序的對應(yīng)關(guān)系,對于今后編寫更為復(fù)雜的中斷程序打下基礎(chǔ)。
4.實驗十五
數(shù)碼管顯示實驗
在此實驗中,我們要編寫針對 74LV164 的驅(qū)動程序,并用其串并轉(zhuǎn)換功能來控制八段LED數(shù)碼管的顯示。
在編寫驅(qū)動程序時,主要有以下需要注意的: // 按位寫入
void write_bit(int data){
{ } else
{GPCR2 |=(0x1 << 26);
GPCR2 |=(0x1 << 27);GPSR2 |=(0x1 << 26);if((data & 0x80)== 0x80)
}
GPSR2 |=(0x1 << 27);} // 按字節(jié)寫,一次寫8位
void write_byte(int data){ { } } int i;
write_bit(data << i);for(i=0;i<8;i++)
使用測試程序看到的測試結(jié)果是數(shù)碼管按0-9顯示輸出。實驗作業(yè)要求在上述基礎(chǔ)上,分別實現(xiàn)一下兩個功能:
① 要求您再編寫一個測試程序,實現(xiàn) PXA270-EP 目標(biāo)板上的 LED 數(shù)碼管循環(huán)顯示的數(shù)字9-0。
② 要求您再編寫一個測試程序,實現(xiàn) PXA270-EP 目標(biāo)板上的 LED 數(shù)碼管循環(huán)顯示的數(shù)字02468。
由于在測試程序中定義了數(shù)組buf[10]分別存儲了0-9是個數(shù),因此上述功能的實現(xiàn)方法是,分別對測試程序做如下修改:
原測試程序:
while(1){ { } } for(count=0;count<10;count++)
data[0] = buf[count];ret=write(fd,data,1);sleep(1);
實現(xiàn)功能①:
while(1){ { } } for(count=9;count>=0;count--)// 倒序顯示數(shù)字
data[0] = buf[count];ret=write(fd,data,1);sleep(1);
實現(xiàn)功能②:
while(1){ { } } for(count=0;count<9;count=count+2)// 更改顯數(shù)順序
data[0] = buf[count];ret=write(fd,data,1);sleep(1);
通過更改顯數(shù)的順序,很容易實現(xiàn)實驗作業(yè)里要求的功能。
5.實驗十六 LED點陣驅(qū)動程序設(shè)計
通過本實驗的操作,我們將 8X8 的點陣 LED 驅(qū)動起來并通過編寫測試程序,使其能夠按照您的意圖進(jìn)行顯示。要求您還編寫更多的測試程序
① 要求您再編寫一個測試程序,實現(xiàn)按橫的方向隔行順序掃描 LED 點陣數(shù)碼管。
② 要求您再編寫一個測試程序,實現(xiàn)按豎的方向順序掃描 LED 點陣數(shù)碼管。作業(yè)一,隔行掃描:
for(i=1;i<=8;i2++){
buf[0]=c;buf[1]=~r;// row for(j=1;j<=8;j++){
} r = 1;c = c<<1;write(fd,buf,2);printf(“buf[0],buf[1]: [%x,%x]n”,buf[0],buf[1]);usleep(200000);// sleep 0.2 second r=r<<1;buf[1]=~r;// column
作業(yè)二,豎向掃描:
for(i=1;i<=8;i++){
buf[0]=c;buf[1]=~r;// row for(j=1;j<=8;j++){
} r = 1;c = c<<1;write(fd,buf,2);printf(“buf[0],buf[1]: [%x,%x]n”,buf[0],buf[1]);usleep(200000);// sleep 0.2 second r=r<<1;buf[1]=~r;// column
6.實驗十七 AD驅(qū)動實驗
通過本實驗的操作,我們將 AD 轉(zhuǎn)換器驅(qū)動起來并通過編寫測試程序,使其能夠?qū)⒛M信 號量按照我們的要求轉(zhuǎn)換成數(shù)字信號量。為了更加清楚地理解 AD 轉(zhuǎn)換器的工作過程,請您再 編寫一個測試程序,將 UCB_ADC_INP_AD0 換成其他通道,來觀察其他 AD 通道情況。
主要代碼:
for(i=0;i<50;i++){ val0 = ioctl(fd,UCB_ADC_INP_AD1,0);usleep(100);val1 = ioctl(fd,UCB_ADC_INP_AD0,0);
usleep(100);val2 = ioctl(fd,UCB_ADC_INP_AD2,0);usleep(100);
7.實驗十八 DA驅(qū)動實驗
通過本實驗的操作,我們使用示波器看到了通過 DA 轉(zhuǎn)換而輸出的波形。在此基礎(chǔ)上,要求試寫一個實現(xiàn)輸出三角波的測試程序。
主要代碼:
while(flag_func_run == FUNC_RUN){ print_prompt();// print select functions scanf(“%d”,&flag_select_func);// user input select getchar();// get ENTER
switch(flag_select_func){ case DA_SIN : {da_create_sin(fd);break;} case DA_FANG case FUNC_QUIT :
{ flag_func_run = FUNC_NOT_RUN;printf(“Quit DA function.byebyen”);break;} case DA_TRI :{da_create_tri(fd);break;} default : { printf(“input = %xn”,flag_select_func);printf(“statys = %xn”,flag_func_run);
: {da_create_fang(fd);break;} printf(“--please input your select use 1 to 4--n”);} }
六、實驗中遇到的問題及解決方法
每一次上課重新啟動后,當(dāng)需要將宿主PC機的根目錄掛在到PXA270-EP目標(biāo)板的mnt目錄下(即在超級終端中輸入命令“mount –o soft,timeo=100,rsize=1024 192.168.0.100:/ /mnt”)時,常顯示無法掛載。
解決方法:在超級終端下的掛載命令應(yīng)該用”mount –o nolock 192.168.0.100:/ /mnt”,如果依然不能掛載需要重啟NFS服務(wù),即在PC機終端中輸入命令”service nfs restart”兩遍后就可以掛載,當(dāng)然有時候也可能是因為網(wǎng)線沒插好。
在每次重啟機器之后都需要將PC機終端的IP地址和開發(fā)板中的系統(tǒng)的IP地址設(shè)定正確,不然也無法掛載。
七、實驗總結(jié)及心得
本學(xué)期的所有實驗均在宿主PC機與PXA270-EP目標(biāo)板上進(jìn)行。通過這些為數(shù)不多課時的實驗,我逐步完成了建立實驗軟件開發(fā)平臺,搭建實驗編譯軟件環(huán)境,在PC上編輯、編譯一個應(yīng)用程序,并且在嵌入式系統(tǒng)上運行和調(diào)試它的過程。在實驗中,不難發(fā)現(xiàn),編譯驅(qū)動程序大體框架都是一樣的,比如里面的讀函數(shù)、寫函數(shù)、ioctl函數(shù)、打開、關(guān)閉以及函數(shù)模塊的初始化并且在超級終端上顯示出等。但所不同的是,要根據(jù)不同的實驗要求修改名稱,并且對其中必要的部分進(jìn)行修改。
除此之外,我認(rèn)為很多基礎(chǔ)知識對實驗的進(jìn)行也起著非常大的作用,例如數(shù)碼管的顯示原理。在掌握了基礎(chǔ)知識之后,上機的過程會顯得相對簡單,尤其是代碼框架已經(jīng)給出,我們所以需要做的就是根據(jù)需要稍作改動來得到我們想要的結(jié)果。
之后,我們又進(jìn)行了更加深入的應(yīng)用試驗,如人機接口方面的鍵盤驅(qū)動實驗、LCD控制實驗和觸摸屏數(shù)據(jù)采集與控制實驗,應(yīng)用方面的多線程應(yīng)用實驗等。由于涉及到嵌入式實驗板的開發(fā),我們在之前實驗基礎(chǔ)上自己動手編寫了程序,對算法和開發(fā)環(huán)境有了更深入的掌握,在自我與互相學(xué)習(xí)中解決了許多問題,受益匪淺。
整個實驗讓我了解了一套完整的嵌入式系統(tǒng)驅(qū)動程序開發(fā)的全過程,學(xué)到的內(nèi)容非常豐富,相信在學(xué)習(xí)了這些內(nèi)容后,在今后的學(xué)習(xí)工作中接觸到類似內(nèi)容,我不會感到無從下手,而是能夠有條不紊。
感謝老師的辛勤指導(dǎo)!
第二篇:北郵嵌入式實驗報告
北京郵電大學(xué)
嵌入式系統(tǒng)開發(fā)實驗報告
學(xué)院:
班級: 姓名: 學(xué)號:
序號:
目錄
一、實驗?zāi)康?.............................................................................................1
二、實驗設(shè)備..............................................................................................1
三、基礎(chǔ)實驗(實驗一~實驗七)............................................................1
1.實驗五..................................................................................................1 2.實驗六..................................................................................................1 3.實驗七..................................................................................................1
四、驅(qū)動程序..............................................................................................5
1.設(shè)備驅(qū)動程序的概念..........................................................................5 2.驅(qū)動程序結(jié)構(gòu)......................................................................................6 3.設(shè)備注冊和初始化..............................................................................7 4.設(shè)備驅(qū)動程序的開發(fā)過程..................................................................8
五、基本接口實驗......................................................................................8
1.實驗十二簡單設(shè)備驅(qū)動程序..............................................................9 2.實驗十三 CPU GPIO驅(qū)動程序設(shè)計...................................................9 3.實驗十四中斷實驗...........................................................................10 4.實驗十五數(shù)碼管顯示實驗................................................................12 5.實驗十六 LED點陣驅(qū)動程序設(shè)計...................................................19 6.實驗十七 AD驅(qū)動實驗....................................................................23 7.實驗十八 DA驅(qū)動實驗....................................................................26
六、實驗中遇到的問題及解決方法........................................................30
七、實驗總結(jié)及心得................................................................................31
一、實驗?zāi)康?/p>
通過實驗熟悉Linux環(huán)境,并掌握一些基本接口驅(qū)動的寫法和用C語言編寫簡單的實驗程序。學(xué)習(xí)LINUX開發(fā)環(huán)境的搭建,通訊配置等。并熟練掌握LINUX驅(qū)動程序的編寫及開發(fā)流程。對嵌入式系統(tǒng)有進(jìn)一步的了解。
二、實驗設(shè)備
1.一套PXA270EP嵌入式實驗箱
2.安裝Redhat9的宿主PC機,并且配置好ARM Linux的開發(fā)環(huán)境
三、基礎(chǔ)實驗(實驗一~實驗七)
實驗一~七為基礎(chǔ)實驗,目的是為后續(xù)實驗搭建好軟、硬件環(huán)境,配置好相關(guān)的協(xié)議、服務(wù),并通過編寫最簡單的HelloWorld程序進(jìn)行測試。由于后面的實驗都要依靠前面實驗的配置,故本段只著重敘述實驗七的具體實現(xiàn)。
1.實驗五
實驗五為宿主PC機配置了TFTP服務(wù)。TFTP(Trivial File Transfer Protocol)是簡單文件傳輸協(xié)議,由于特定開發(fā)環(huán)境的制約,這一服務(wù)是必須的。在配置完成后,每次重啟宿主PC機時,都須先輸入命令:service xinetd restart,以啟動TFTP服務(wù)。
2.實驗六
實驗六為宿主PC機配置了NFS服務(wù)。NFS(Network File System)指網(wǎng)絡(luò)文件系統(tǒng),它實現(xiàn)了文件在不同的系統(tǒng)間使用。當(dāng)我們想用遠(yuǎn)端檔案時,只需調(diào)用“mount”就可以遠(yuǎn)端系統(tǒng)掛接在自己的檔案系統(tǒng)之下。每次重啟宿主PC機時,都須先輸入命令:service nfs restart,以啟動nfs服務(wù)。
3.實驗七
實驗七通過用c語言編寫的簡單程序HelloWorld,測試前面幾個實驗是否成功配置好環(huán)境,從超級終端可以看到HelloWorld程序的運行結(jié)果。
實驗步驟如下: 1)硬件連接:
連接宿主 PC 機和一臺 PXA270-RP目標(biāo)板。2)打開宿主PC 機電源,進(jìn)入 Linux操作系統(tǒng)。
3)啟動RedHat 9.0 的圖形界面,如下圖,若您是以 root 身份登陸在文本模式下,則輸入命令startx啟動圖形界面。進(jìn)入RedHat 9.0 圖形界面后,打開一個終端窗(Terminal)。
4)輸入minicom然后回車,minicim設(shè)置為115200 8NI無流控。
5)打開PXA270_RP目標(biāo)板電源,按目標(biāo)板上的BOOT鍵,在minicom中應(yīng)該會看到如下圖:
6)在minicom終端窗口中,如圖,輸入下列四條命令 root ifconfig eth 192.168.0.50 up mount-o nolock 192.168.0.100:/ /mnt cd /mnt 此時,先將該窗口最小化,在后面的第 10 操作步驟中還將會回到該窗口中進(jìn)行操作。
7)宿主機上打開一個終端窗口(Terminal),點擊【紅帽/System Tools/Terminal】啟動終端窗口,輸入下列 4 條命令: ① cd /home
②mkdir HW
③ cd HW
④ vi
HelloWorld.c
/*請您輸入程序 7.1 程序清單*/
此時會顯示一個空白的屏幕,這條命令的含義是,使用 Vi 編輯器,對一個名叫HelloWorld.c的文件進(jìn)行編輯,我們看到的空白窗口是對文件進(jìn)行編輯的窗口,如下圖。就像在 Windows系統(tǒng)下面使用寫字板等一樣道理。
在 vi 里面先單擊鍵盤 A 鍵,然后左下角會變成—INSER。輸入程序的時候和其他編輯器是一樣的,如下圖。
當(dāng)輸入程序完畢后,單擊鍵盤 Esc 鍵,然后按“:”(冒號)此時左下角會出現(xiàn)冒號然后輸入“wq”最后按“Enter”確認(rèn)存盤退出 vi 編輯器,如下圖。
8)在上面同一個終端窗口中,輸入下列 2 條命令交叉編譯HelloWorld.c源程序,并查看生成的.o 目標(biāo)文件,如圖 7-10,圖7-11: ①
arm-linux-gcc–oHelloWorldHelloWorld.c ②ls 等到再次出現(xiàn)提示符,代表程序已經(jīng)正確編譯。如果此步出現(xiàn)錯誤信息,請查看錯誤信息,并且重新編輯原來的 C文件,修改錯誤。直到正確編譯。
9)重新打開第 7 步最小化的開有minicom的終端窗口,即到 PXA270-RP 目標(biāo)板的mnt目錄下,請您輸入下列 3 條命令,運行HelloWorld編譯成功的HelloWorld目標(biāo)程序:
① cd home/HW
/*回到minicom中目標(biāo)板的/mnt/home/HW目錄下*/ ②ls ③./ HelloWorld
/*此時會看到如下圖*/
四、驅(qū)動程序
1.設(shè)備驅(qū)動程序的概念
設(shè)備驅(qū)動程序?qū)嶋H是處理和操作硬件控制器的軟件,從本質(zhì)上講,是內(nèi)核中具有最高特權(quán)級的、駐留內(nèi)存的、可共享的底層硬件處理例程。驅(qū)動程序是內(nèi)核的一部分,是操作系統(tǒng)內(nèi)核與硬件設(shè)備的直接接口,驅(qū)動程序屏蔽了硬件的細(xì)節(jié),完成以下功能:
?對設(shè)備初始化和釋放;
?對設(shè)備進(jìn)行管理,包括實時參數(shù)設(shè)置,以及提供對設(shè)備的操作接口; ?讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)或者回送應(yīng)用程序請求的數(shù)據(jù); ?檢測和處理設(shè)備出現(xiàn)的錯誤。
Linux操作系統(tǒng)將所有的設(shè)備全部看成文件,并通過文件的操作界面進(jìn)行操作。對用戶程序而言,設(shè)備驅(qū)動程序隱藏了設(shè)備的具體細(xì)節(jié),對各種不同設(shè)備提供了一致的接口,一般來說,是把設(shè)備映射為一個特殊的設(shè)備文件,用戶程序可以像對其他文件一樣對此設(shè)備文件進(jìn)行操作。這意味著:
?由于每一個設(shè)備至少由文件系統(tǒng)的一個文件代表,因而都有一個“文件名”。?應(yīng)用程序通常可以通過系統(tǒng)調(diào)用open()打開設(shè)備文件,建立起與目標(biāo)設(shè)備的連接。
?打開了代表著目標(biāo)設(shè)備的文件,即建立起與設(shè)備的連接后,可以通過read()、write()、ioctl()等常規(guī)的文件操作對目標(biāo)設(shè)備進(jìn)行操作。
設(shè)備文件的屬性由三部分信息組成:第一部分是文件的類型,第二部分是一個主設(shè)備號,第三部分是一個次設(shè)備號。其中類型和主設(shè)備號結(jié)合在一起惟一地確定了設(shè)備文件驅(qū)動程序及其界面,而次設(shè)備號則說明目標(biāo)設(shè)備是同類設(shè)備中的第幾個。
由于Linux 中將設(shè)備當(dāng)做文件處理,所以對設(shè)備進(jìn)行操作的調(diào)用格式與對文件的操作類似,主要包括open()、read()、write()、ioctl()、close()等。應(yīng)用程序發(fā)出系統(tǒng)調(diào)用命令后,會從用戶態(tài)轉(zhuǎn)到核心態(tài),通過內(nèi)核將open()這樣的系統(tǒng)調(diào)用轉(zhuǎn)換成對物理設(shè)備的操作。
2.驅(qū)動程序結(jié)構(gòu)
一個設(shè)備驅(qū)動程序模塊的基本框架
在系統(tǒng)內(nèi)部,I/O設(shè)備的存取通過一組固定的入口點來進(jìn)行,入口點也可以理解為設(shè)備的句柄,就是對設(shè)備進(jìn)行操作的基本函數(shù)。字符型設(shè)備驅(qū)動程序提供如下幾個入口點:
?
open入口點。打開設(shè)備準(zhǔn)備I/O操作。對字符設(shè)備文件進(jìn)行打開操作,都會調(diào)用設(shè)備的open入口點。open子程序必須對將要進(jìn)行的I/O操作做好必要的準(zhǔn)備工作,如清除緩沖區(qū)等。如果設(shè)備是獨占的,即同一時刻只能有一個程序訪問此設(shè)備,則open子程序必須設(shè)置一些標(biāo)志以表示設(shè)備處于忙狀態(tài)。
?
close入口點。關(guān)閉一個設(shè)備。當(dāng)最后一次使用設(shè)備完成后,調(diào)用close子程序。獨占設(shè)備必須標(biāo)記設(shè)備方可再次使用。
?
read入口點。從設(shè)備上讀數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數(shù)據(jù)。對字符設(shè)備文件進(jìn)行讀操作將調(diào)用read子程序。
?
write入口點。往設(shè)備上寫數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是把數(shù)據(jù)寫入緩沖區(qū)里。對字符設(shè)備文件進(jìn)行寫操作將調(diào)用write子程序。
?ioctl入口點。執(zhí)行讀、寫之外的操作。
select入口點。檢查設(shè)備,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。select系統(tǒng)調(diào)用在檢查與設(shè)備文件相關(guān)的文件描述符時使用select入口點。
3.設(shè)備注冊和初始化
設(shè)備的驅(qū)動程序在加載的時候首先需要調(diào)用入口函數(shù)init_module(),該函數(shù)最重要的一個工作就是向內(nèi)核注冊該設(shè)備,對于字符設(shè)備調(diào)用register_chrdev()完成注冊。register_chrdev的定義為:intregister_chrdev(unsigned int major, const char *name, struct file_ operations *fops);其中,major是為設(shè)備驅(qū)動程序向系統(tǒng)申請的主設(shè)備號,如果為0,則系統(tǒng)為此驅(qū)動程序動態(tài)分配一個主設(shè)備號。name是設(shè)備名,fops是對各個調(diào)用的入口點說明。此函數(shù)返回0時表示成功;返回-EINVAL,表示申請的主設(shè)備號非法,主要原因是主設(shè)備號大于系統(tǒng)所允許的最大設(shè)備號;返回-EBUSY,表示所申請的主設(shè)備號正在被其他設(shè)備程序使用。如果動態(tài)分配主設(shè)備號成功,此函數(shù)將返回所分配的主設(shè)備號。如果register_chrdev()操作成功,設(shè)備名就會出現(xiàn)在/proc/dvices文件中。
Linux在/dev目錄中為每個設(shè)備建立一個文件,用ls–l命令列出函數(shù)返回值,若小于0,則表示注冊失??;返回0或者大于0的值表示注冊成功。注冊以后,Linux將設(shè)備名與主、次設(shè)備號聯(lián)系起來。當(dāng)有對此設(shè)備名的訪問時,Linux通過請求訪問的設(shè)備名得到主、次設(shè)備號,然后把此訪問分發(fā)到對應(yīng)的設(shè)備驅(qū)動,設(shè)備驅(qū)動再根據(jù)次設(shè)備號調(diào)用不同的函數(shù)。
當(dāng)設(shè)備驅(qū)動模塊從Linux內(nèi)核中卸載,對應(yīng)的主設(shè)備號必須被釋放。字符設(shè)備在cleanup_ module()函數(shù)中調(diào)用unregister_chrdev()來完成設(shè)備的注銷。unregister_chrdev()的定義為:intunregister_chrdev(unsigned int major, const char *name);包括設(shè)備注冊在內(nèi),設(shè)備驅(qū)動的初始化函數(shù)主要完成的功能是有以下5項。(1)對驅(qū)動程序管理的硬件進(jìn)行必要的初始化。
對硬件寄存器進(jìn)行設(shè)置。比如,設(shè)置中斷掩碼,設(shè)置串口的工作方式、并口的數(shù)據(jù)方向等。
(2)初始化設(shè)備驅(qū)動相關(guān)的參數(shù)。
一般說來,每個設(shè)備都要定義一個設(shè)備變量,用以保存設(shè)備相關(guān)的參數(shù)。在這一步驟里對設(shè)備變量中的項進(jìn)行初始化。
(3)在內(nèi)核注冊設(shè)備。
調(diào)用register_chrdev()函數(shù)來注冊設(shè)備。(4)注冊中斷。
如果設(shè)備需要IRQ支持,則要使用request_irq()函數(shù)注冊中斷。(5)其他初始化工作。
初始化部分一般還負(fù)責(zé)給設(shè)備驅(qū)動程序申請包括內(nèi)存、時鐘、I/O端口等在內(nèi)的系統(tǒng)資源,這些資源也可以在open子程序或者其他地方申請。這些資源不用時,應(yīng)該釋放,以利于資源的共享。
若驅(qū)動程序是內(nèi)核的一部分,初始化函數(shù)則要按如下方式聲明: int __initchr_driver_init(void);其中__init是必不可少的,在系統(tǒng)啟動時會由內(nèi)核調(diào)用chr_driver_init,完成驅(qū)動程序的初始化。
當(dāng)驅(qū)動程序是以模塊的形式編寫時,則要按照如下方式聲明: intinit_module(void)當(dāng)運行后面介紹的insmod命令插入模塊時,會調(diào)用init_module函數(shù)完成初始化工作。
4.設(shè)備驅(qū)動程序的開發(fā)過程
由于嵌入式設(shè)備由于硬件種類非常豐富,在默認(rèn)的內(nèi)核發(fā)布版中不一定包括所有驅(qū)動程序。所以進(jìn)行嵌入式Linux系統(tǒng)的開發(fā),很大的工作量是為各種設(shè)備編寫驅(qū)動程序。除非系統(tǒng)不使用操作系統(tǒng),程序直接操縱硬件。嵌入式Linux系統(tǒng)驅(qū)動程序開發(fā)與普通Linux開發(fā)沒有區(qū)別??梢栽谟布a(chǎn)廠家或者Internet上尋找驅(qū)動程序,也可以根據(jù)相近的硬件驅(qū)動程序來改寫,這樣可以加快開發(fā)速度。實現(xiàn)一個嵌入式Linux設(shè)備驅(qū)動的大致流程如下。
(1)查看原理圖,理解設(shè)備的工作原理。一般嵌入式處理器的生產(chǎn)商提供參考電路,也可以根據(jù)需要自行設(shè)計。
(2)定義設(shè)備號。設(shè)備由一個主設(shè)備號和一個次設(shè)備號來標(biāo)識。主設(shè)備號惟一標(biāo)識了設(shè)備類型,即設(shè)備驅(qū)動程序類型,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表項的索引。次設(shè)備號僅由設(shè)備驅(qū)動程序解釋,區(qū)分被一個設(shè)備驅(qū)動控制下的某個獨立的設(shè)備。
(3)實現(xiàn)初始化函數(shù)。在驅(qū)動程序中實現(xiàn)驅(qū)動的注冊和卸載。(4)設(shè)計所要實現(xiàn)的文件操作,定義file_operations結(jié)構(gòu)。(5)實現(xiàn)所需的文件操作調(diào)用,如read、write等。
(6)實現(xiàn)中斷服務(wù),并用request_irq向內(nèi)核注冊,中斷并不是每個設(shè)備驅(qū)動所必需的。
(7)編譯該驅(qū)動程序到內(nèi)核中,或者用insmod命令加載模塊。(8)測試該設(shè)備,編寫應(yīng)用程序,對驅(qū)動程序進(jìn)行測試。
五、基本接口實驗
在完成了基本實驗后,我們開始著手基本接口實驗。在這些實驗中,我們學(xué)習(xí)如何編寫設(shè)備驅(qū)動程序,及如何用測試程序檢驗驅(qū)動程序是否正確,并通過改寫測試程序正常地對驅(qū)動程序進(jìn)行相關(guān)操作。
1.實驗十二 簡單設(shè)備驅(qū)動程序
本次實驗的任務(wù)是編寫一個字符型設(shè)備驅(qū)動程序,并學(xué)習(xí)在應(yīng)用程序中調(diào)用驅(qū)動。考慮到我們初次接觸驅(qū)動程序的編寫,對此還十分陌生,因此指導(dǎo)書中提供了本次實驗所要用到的程序源代碼。雖然這樣一個字符型設(shè)備驅(qū)動程序并沒有任何實際作用,但是它讓我們輕松掌握了嵌入式驅(qū)動的編寫過程,因為復(fù)雜繁瑣的驅(qū)動,其骨架都是相同的。因此,看懂本實驗的源代碼,學(xué)習(xí)并模仿其編寫方法,對于后續(xù)實驗有著非常重要的意義。
2.實驗十三 CPU GPIO驅(qū)動程序設(shè)計
在本實驗中,我們要編寫第一個針對實際硬件的驅(qū)動程序。我們知道,凡是操作系統(tǒng)控制外部設(shè)備,即使是最簡單的硬件電路,也是需要驅(qū)動的。本實驗涉及的外部硬件只有電阻和發(fā)光二極管。我們使用自己編寫的驅(qū)動程序與應(yīng)用程序控制 GPIO96的電平,通過 LED 的亮滅來判斷,是否 CPU 做出了正確的響應(yīng)。
補充代碼(1)
//-------------------WRITE-----------------------ssize_tSIMPLE_GPIO_LED_write(struct file * file ,const char * buf, size_t count, loff_t * f_ops){ #ifdef OURS_GPIO_LED_DEBUG printk(“SIMPLE_GPIO_LED_write [--kernel--]n”);
#endif
return count;}
補充代碼(2)
//-------------------OPEN------------------------ssize_tSIMPLE_GPIO_LED_open(structinode * inode ,struct file * file){ #ifdef OURS_GPIO_LED_DEBUG printk(“SIMPLE_GPIO_LED_open [--kernel--]n”);
#endif
MOD_INC_USE_COUNT;
return 0;}
補充代碼(3)
//------------------structfile_operationsGPIO_LED_ctl_ops ={ open:SIMPLE_GPIO_LED_open, read:SIMPLE_GPIO_LED_read, write:SIMPLE_GPIO_LED_write, ioctl:SIMPLE_GPIO_LED_ioctl, release:SIMPLE_GPIO_LED_release, };實驗作業(yè)
要求在目標(biāo)板上LED閃爍產(chǎn)生亮7秒,滅2秒的效果 在測試程序中有這樣一段代碼: while(1){ ioctl(fd,LED_OFF);sleep(1);
sleep(1);while(1){ ioctl(fd,LED_OFF);sleep(2);
sleep(7);} 3.實驗十四
中斷實驗
// 滅2秒 // 亮7秒 ioctl(fd,LED_ON);}
// 休眠1秒
ioctl(fd,LED_ON);只需將上面的代碼改為如下代碼即可:
在理論課中,我們學(xué)習(xí)了許多中斷方面的知識,包括中斷向量、中斷優(yōu)先級、中斷過程等。在PXA270系統(tǒng)里,中斷控制器分外部設(shè)備和 PXA270X 處理器設(shè)備產(chǎn)生的兩個層次的中斷,前者是初級的中斷源,后者是次級中斷源,大量的次級中斷源通常被映射為一個初級中斷源。
補充代碼1 voidshowversion(void){ printk(“*********************************************n”);
printk(“t %s tn”, VERSION);
printk(“*********************************************nn”);
} static intSimpleINT_temp_count = 0;補充代碼2 //-------------------READ------------------------ssize_tSIMPLE_INT_read(struct file * file ,char * buf, size_t count, loff_t * f_ops){
#ifdef OURS_INT_DEBUG
#endif return count;printk(“SIMPLE_INT_read [--kernel--]n”);} 補充代碼3 //-------------------WRITE-----------------------ssize_tSIMPLE_INT_write(struct file * file ,const char * buf, size_t count, loff_t * f_ops){
#ifdef OURS_INT_DEBUG
} 補充代碼4 //------------------structfile_operationsINT_ctl_ops ={ open: SIMPLE_INT_open, read: SIMPLE_INT_read, #endif return count;printk(“SIMPL_INT_write [--kernel--]n”);write:SIMPLE_INT_write, ioctl:SIMPLE_INT_ioctl, release:SIMPLE_INT_release, };
通過此實驗,我了解了硬件中斷管腳與中斷號的對應(yīng)關(guān)系,以及中斷號與中斷處理程序的對應(yīng)關(guān)系,對于今后編寫更為復(fù)雜的中斷程序打下基礎(chǔ)。
4.實驗十五
數(shù)碼管顯示實驗
在此實驗中,我們要編寫針對 74LV164 的驅(qū)動程序,并用其串并轉(zhuǎn)換功能來控制八段LED數(shù)碼管的顯示。
補充代碼1 voidshowversion(void){ printk(“*********************************************n”);
printk(“t %s tn”, VERSION);
printk(“*********************************************nn”);
} 補充代碼2 //-------------------READ------------------------ssize_tSERIAL_LED_read(struct file * file ,char * buf, size_t count, loff_t * f_ops){ #ifdef OURS_HELLO_DEBUG
} 補充代碼3 //-------------------WRITE-----------------------ssize_tSERIAL_LED_write(struct file * file ,const char * buf, size_t count, loff_t * f_ops)return count;printk(“SERIAL_LED_read [--kernel--]n”);#endif { #ifdef OURS_HELLO_DEBUG
} 補充代碼4 //-------------------IOCTL-----------------------ssize_tSERIAL_LED_ioctl(structinode * inode ,struct file * file, unsigned intcmd, long data){ #ifdef OURS_HELLO_DEBUG
#endif
} 補充代碼5 //-------------------OPEN------------------------ssize_tSERIAL_LED_open(structinode * inode ,struct file * file){ #ifdef OURS_HELLO_DEBUG
#endif
return 0;} MOD_INC_USE_COUNT;printk(“SERIAL_LED_open [--kernel--]n”);return 0;printk(“SERIAL_LED_ioctl [--kernel--]n”);return count;#endif write_byte(* buf);printk(“SERIAL_LED_write [--kernel--]n”);補充代碼6 //-------------------RELEASE/CLOSE---------------ssize_tSERIAL_LED_release(structinode *inode ,struct file * file){ #ifdef OURS_HELLO_DEBUG
printk(“SERIAL_LED_release [--kernel--]n”);
#endif MOD_DEC_USE_COUNT;return 0;} 補充代碼7 //------------------structfile_operationsSERIAL_LED_ops ={ open: SERIAL_LED_open,read: SERIAL_LED_read,write:SERIAL_LED_write,ioctl:SERIAL_LED_ioctl,release:SERIAL_LED_release, };補充代碼8 staticint __initHW_SERIAL_LED_init(void){ int ret =-ENODEV;
ret =
devfs_register_chrdev(SERIAL_LED_MAJOR, &SERIAL_LED_ops);
showversion();if(ret < 0)“serial_led_ctl”,} {
} else { } return ret;printk(“ pxa270 serial_led_driver register success!![--kernel--]n”);printk(“ pxa270 init_module failed with %dn [--kernel--]”, ret);return ret;補充代碼9 staticint __init pxa270_SERIAL_LED_init(void){ int ret =-ENODEV;
printk(“pxa270_SERIAL_LED_init [--kernel--]n”);
#endif
ret = HW_SERIAL_LED_init();if(ret)return ret;return 0;} 補充代碼10 static void __exit cleanup_SERIAL_LED(void){ #ifdef OURS_HELLO_DEBUG #ifdef OURS_HELLO_DEBUG
#endif }
補充代碼11 MODULE_DESCRIPTION(“serial_led driver module”);
MODULE_AUTHOR(“l(fā)iduo”);
MODULE_LICENSE(“GPL”);
module_init(pxa270_SERIAL_LED_init);module_exit(cleanup_SERIAL_LED);使用測試程序看到的測試結(jié)果是數(shù)碼管按0-9顯示輸出。實驗作業(yè)要求在上述基礎(chǔ)上,分別實現(xiàn)一下兩個功能:
①要求您再編寫一個測試程序,實現(xiàn) PXA270-EP 目標(biāo)板上的 LED 數(shù)碼管循環(huán)顯示的數(shù)字9-0。
②要求您再編寫一個測試程序,實現(xiàn) PXA270-EP 目標(biāo)板上的 LED 數(shù)碼管循環(huán)顯示的數(shù)字02468。
由于在測試程序中定義了數(shù)組buf[10]分別存儲了0-9是個數(shù),因此上述功能的實現(xiàn)方法是,分別對測試程序做如下修改:
原測試程序: while(1){ for(count=0;count<10;count++){ data[0] = buf[count];ret=write(fd,data,1);sleep(1);} } 實現(xiàn)功能①: while(1){ for(count=9;count>=0;count--)} } 結(jié)果顯示
// 倒序顯示數(shù)字
{ data[0] = buf[count];ret=write(fd,data,1);sleep(1);devfs_unregister_chrdev(SERIAL_LED_MAJOR, “serial_led”);printk(“cleanup_SERIAL_LED [--kernel--]n”);實現(xiàn)功能②: while(1){ for(count=0;count<9;count=count+2)} } 結(jié)果顯示
// 更改顯數(shù)順序
{ data[0] = buf[count];ret=write(fd,data,1);sleep(1);
通過更改顯數(shù)的順序,很容易實現(xiàn)實驗作業(yè)里要求的功能。
5.實驗十六 LED點陣驅(qū)動程序設(shè)計
通過本實驗的操作,我們將 8X8 的點陣 LED 驅(qū)動起來并通過編寫測試程序,使其能夠按照您的意圖進(jìn)行顯示。要求您還編寫更多的測試程序
補充代碼1 voidshowversion(void){ printk(“*********************************************n”);printk(“t %s tn”, VERSION);printk(“*********************************************nn”);
} 補充代碼2 //-------------------READ------------------------ssize_tSIMPLE_LED_read(struct file * file ,char * buf, size_t count, loff_t * f_ops){ #ifdef OURS_LED_DEBUG
#endif return count;printk(“SIMPLE_LED_read [--kernel--]n”);} 補充代碼3 //-------------------IOCTL-----------------------ssize_tSIMPLE_LED_ioctl(structinode * inode ,struct file * file, unsigned intcmd, long data){
#endif
} 補充代碼4 //------------------structfile_operationsLED_ctl_ops ={ open: SIMPLE_LED_open, read:
SIMPLE_LED_read, write: SIMPLE_LED_write, ioctl: SIMPLE_LED_ioctl, release:SIMPLE_LED_release, };補充代碼5 staticint __init pxa270_LED_CTL_init(void){ int ret =-ENODEV;
printk(“pxa270_LED_CTL_init [--kernel--]n”);
#endif
ret = HW_LED_CTL_init();if(ret)
return ret;#ifdef OURS_LED_DEBUG return 0;printk(“SIMPLE_LED_ioctl [--kernel--]n”);#ifdef OURS_LED_DEBUG return 0;} 補充代碼6 static void __exit cleanup_LED_ctl(void){
#ifdef OURS_LED_DEBUG
#endif
} ①要求您再編寫一個測試程序,實現(xiàn)按橫的方向隔行順序掃描 LED 點陣數(shù)碼管。
②要求您再編寫一個測試程序,實現(xiàn)按豎的方向順序掃描 LED 點陣數(shù)碼管。作業(yè)一,隔行掃描:
printk(“cleanup_LED_ctl [--kernel--]n”);outw(0x0000,ioremap_addr);
devfs_unregister_chrdev(SIMPLE_LED_MAJOR, “l(fā)ed_ary_ctl”);for(i=1;i<=8;i2++){
buf[0]=c;buf[1]=~r;// row for(j=1;j<=8;j++){
} r = 1;c = c<<1;
write(fd,buf,2);
printf(“buf[0],buf[1]: [%x,%x]n”,buf[0],buf[1]);usleep(200000);// sleep 0.2 second r=r<<1;
buf[1]=~r;// column
結(jié)果顯示
作業(yè)二,豎向掃描:
for(i=1;i<=8;i++){
buf[0]=c;buf[1]=~r;// row for(j=1;j<=8;j++){
} r = 1;c = c<<1;
write(fd,buf,2);
printf(“buf[0],buf[1]: [%x,%x]n”,buf[0],buf[1]);usleep(200000);// sleep 0.2 second r=r<<1;
buf[1]=~r;// column
結(jié)果顯示
6.實驗十七 AD驅(qū)動實驗
通過本實驗的操作,我們將 AD 轉(zhuǎn)換器驅(qū)動起來并通過編寫測試程序,使其能夠?qū)⒛M信號量按照我們的要求轉(zhuǎn)換成數(shù)字信號量。為了更加清楚地理解 AD 轉(zhuǎn)換器的工作過程,請您再編寫一個測試程序,將 UCB_ADC_INP_AD0 換成其他通道,來觀察其他 AD 通道情況。
補充代碼1 voidshowversion(void){ printk(“%sn”,VERSION);} struct ucb1x00 *ad_ucb;
補充代碼2 //-------------------READ------------------------staticssize_tadctl_read(struct file * file ,char *buf, size_t count, loff_t *offset){
} 補充代碼3 //-------------------WRITE-----------------------ssize_tadctl_write(struct file * file ,const char *buf, size_t count, loff_t *offset){
#ifdef OURS_HELLO_DEBUG printk(“writen”);
#endif
} 補充代碼4 //-------------------OPEN------------------------ssize_tadctl_open(structinode * inode ,struct file * file){
#ifdef OURS_HELLO_DEBUG printk(“openn”);
#endif
}
補充代碼5 //-------------------RELEASE/CLOSE---------------ssize_tadctl_release(structinode *inode ,struct file * file){
#ifdef OURS_HELLO_DEBUG printk(“releasen”);
#endif return 0;return 0;return count;#ifdef OURS_HELLO_DEBUG printk(“readn”);#endif return count;} 補充代碼6 staticstructfile_operationsadctl_ops = {
};補充代碼7 //-------------------INIT------------------------staticint __initHW_AD_CTL_init(void){
return ret;}
補充代碼8 staticint __init pxa270_AD_CTL_init(void){ int ret =-ENODEV;#ifdef OURS_HELLO_DEBUG int ret =-ENODEV;ret = devfs_register_chrdev(ADCTL_MAJOR, “adctl”, &adctl_ops);showversion();ad_ucb=ucb1x00_get();if(ret < 0){
} else { } adctl_dev_handle = devfs_register(NULL, “ad_ctl”, DEVFS_FL_DEFAULT, printk(“adctl driver register success!n”);printk(“fail %dn”,ret);return 0;read: ioctl: adctl_read, adctl_ioctl, write: adctl_write, open: adctl_open, release:adctl_release,ADCTL_MAJOR, 0, S_IFCHR, &adctl_ops, NULL);printk(“initn”);#endif ret=HW_AD_CTL_init();if(ret)}
補充代碼9 static void __exit cleanup_AD_ctl(void){
}
7.實驗十八 DA驅(qū)動實驗
通過本實驗的操作,我們使用示波器看到了通過DA轉(zhuǎn)換而輸出的波形。在此基礎(chǔ)上,要求試寫一個實現(xiàn)輸出三角波的測試程序。
補充代碼1 #include
} printk(“t %st n”,VERSION);printk(“*****************************n”);static long ioremap_addr;補充代碼3 //-------------------READ------------------------ssize_tSIMPLE_DA_read(struct file * file ,char * buf, size_t count, loff_t * f_ops){ #ifdef OURS_DA_DEBUG
} 補充代碼4 //-------------------WRITE-----------------------ssize_tSIMPLE_DA_write(struct file * file ,const char * buf, size_t count, loff_t * f_ops){
printk(“SIMPLE_DA_write[--kernel--]n”);
#endif
return count;} 補充代碼5 //-------------------IOCTL-----------------------ssize_tSIMPLE_DA_ioctl(structinode * inode ,struct file * file, unsigned intcmd, outb(buf[0],ioremap_addr);#ifdef OURS_DA_DEBUG return count;#endif printk(“SIMPLE_DA_read[--kernel--]n”);long data){ #ifdef OURS_DA_DEBUG
printk(“SIMPLE_DA_ioctl[--kernel--]n”);
#endif return 0;} 補充代碼6 //-------------------OPEN------------------------ssize_tSIMPLE_DA_open(structinode * inode ,struct file * file){
#ifdef OURS_DA_DEBUG printk(“SIMPLE_DA_open [--kernel--]n”);
MOD_INC_USE_COUNT;return 0;
#endif } 補充代碼7 /------------------structfile_operationsDA_ctl_ops ={
read: SIMPLE_DA_read,};
補充代碼8 release:
SIMPLE_DA_release, ioctl:
SIMPLE_DA_ioctl, write:
SIMPLE_DA_write, //-------------------INIT------------------------staticint __initHW_DA_CTL_init(void){ int ret =-ENODEV;
}
補充代碼9 staticint __init pxa270_DA_CTL_init(void){ int ret =-ENODEV;
printk(“pxa270_DA_CTL_init [--kernel--]n”);
#endif #ifdef OURS_DA_DEBUG } printk(“ pxa270 led_driver register success!![--kernel--]n”);{ else } return ret;printk(“ pxa270: init_module failed with %dn [--kernel--]”, ret);{ if(ret < 0)showversion();ret = devfs_register_chrdev(SIMPLE_DA_MAJOR, “da_ctl”, &DA_ctl_ops);
ret = HW_DA_CTL_init();if(ret)
return ret;return 0;} 補充代碼10 static void __exit cleanup_DA_ctl(void){
#endif } 補充代碼11 MODULE_DESCRIPTION(“DA_ctl driver module”);MODULE_AUTHOR(“l(fā)iduo”);MODULE_LICENSE(“GPL”);module_init(pxa270_DA_CTL_init);module_exit(cleanup_DA_ctl);printk(“cleanup_DA_ctl [--kernel--]n”);#ifdef OURS_DA_DEBUG
六、實驗中遇到的問題及解決方法
每一次上課重新啟動后,當(dāng)需要將宿主PC機的根目錄掛在到PXA270-EP目標(biāo)板的mnt目錄下(即在超級終端中輸入命令“mount –o soft,timeo=100,rsize=1024 192.168.0.100:/ /mnt”)時,常顯示無法掛載。
解決方法:在超級終端下的掛載命令應(yīng)該用”mount –o nolock 192.168.0.100:/ /mnt”,如果依然不能掛載需要重啟NFS服務(wù),即在PC機終端中輸入命令”service nfs restart”兩遍后就可以掛載,當(dāng)然有時候也可能是因為網(wǎng)線沒插好。
在每次重啟機器之后都需要將PC機終端的IP地址和開發(fā)板中的系統(tǒng)的IP地址設(shè)定正確,不然也無法掛載。
七、實驗總結(jié)及心得
本學(xué)期的所有實驗均在宿主PC機與PXA270-EP目標(biāo)板上進(jìn)行。在實驗中,我們先建立硬件實驗平臺,又建立主機軟件開發(fā)環(huán)境,接著為實驗進(jìn)行各項配置,最后完成了各個實驗中的多種功能。值得注意的是,前期的硬件、軟件準(zhǔn)備必須完整無誤地實現(xiàn),后續(xù)的實驗才能順利進(jìn)行。所以,打基礎(chǔ)的工作一定要仔細(xì)謹(jǐn)慎。后續(xù)實驗中雖然給出了驅(qū)動程序的框架,仍需要我們自己補充完整,并開動腦筋舉一反三,在原代碼的基礎(chǔ)上進(jìn)行一定修改以實現(xiàn)新的功能。
通過這學(xué)期的實驗,我逐步完成了建立實驗軟件開發(fā)平臺,搭建實驗編譯軟件環(huán)境,在PC上編輯、編譯一個應(yīng)用程序,并且在嵌入式系統(tǒng)上運行和調(diào)試它的過程。在實驗中,不難發(fā)現(xiàn),編譯驅(qū)動程序大體框架都是一樣的,比如里面的讀函數(shù)、寫函數(shù)、ioctl函數(shù)、打開、關(guān)閉以及函數(shù)模塊的初始化并且在超級終端上顯示出等。但所不同的是,要根據(jù)不同的實驗要求修改名稱,并且對其中必要的部分進(jìn)行修改。
除此之外,我認(rèn)為很多基礎(chǔ)知識對實驗的進(jìn)行也起著非常大的作用,例如數(shù)碼管的顯示原理。在掌握了基礎(chǔ)知識之后,上機的過程會顯得相對簡單,尤其是代碼框架已經(jīng)給出,我們所以需要做的就是根據(jù)需要稍作改動來得到我們想要的結(jié)果。
在實驗過程中常常會遇到各種各樣的問題,剛開始時我不知如何是好,只能求助于老師和同學(xué),后來隨著實驗的進(jìn)行,我對實驗的內(nèi)容和虛擬機都有了一定的了解,遇到問題時也可以靜下心來思考其原因,自己嘗試各種方法去解決問題。整個實驗讓我了解了一套完整的嵌入式系統(tǒng)驅(qū)動程序開發(fā)的全過程,學(xué)到的內(nèi)容非常豐富,相信在學(xué)習(xí)了這些內(nèi)容后,在今后的學(xué)習(xí)工作中接觸到類似內(nèi)容,我不會感到無從下手,而是能夠有條不紊。
感謝老師的辛勤指導(dǎo)!
第三篇:北郵電子院嵌入式實驗報告大四上
嵌入式實驗報告
學(xué)院: 電子工程學(xué)院
一、實驗?zāi)康?/p>
1、了解嵌入式系統(tǒng)及其相關(guān)基礎(chǔ)知識。
2、了解宿主PC機與PXA270目標(biāo)版,能正確連接宿主PC機與PXA270目標(biāo)版。
3、學(xué)會在宿主機上安裝Linux操作系統(tǒng)——RedHat9.0。、4、學(xué)會建立宿主PC機端的開發(fā)環(huán)境。
5、學(xué)會配置宿主PC機端的超級終端。
6、配置宿主PC機端的TFTP服務(wù),并開通此服務(wù)。
7、配置宿主PC機端的NFS服務(wù),并開通此服務(wù)。
8、學(xué)會簡單Linux驅(qū)動程序的設(shè)計。
二、實驗內(nèi)容
(一)基本實驗
實驗一到六為基礎(chǔ)實驗,主要是為了在熟悉實驗操作平臺的同時為后續(xù)實驗搭建好軟、硬件環(huán)境,配置好相關(guān)的協(xié)議、服務(wù)。
其中實驗一是各個硬件的互聯(lián),搭建好了實驗的硬件環(huán)境。實驗二是在宿主PC端安裝虛擬機,提供了實驗需要的Linux操作系統(tǒng)。實驗三是宿主PC端開發(fā)環(huán)境的安裝與配置。
實驗四是配置宿主PC機端的超級終端,使PC機與PXA270目標(biāo)板之間可以通過串口通訊。在每次重啟宿主PC機時,都需要重新將超級終端掛載到虛擬機上,掛載之前須通過ifconfig命令查看該機的IP地址,若其已經(jīng)復(fù)位,須用命令:ifconfig eth0 192.168.0.100 up重置宿主PC機的IP地址。掛載虛擬機的代碼為:
root ifconfig eth0 192.168.0.50 up mount –o nolock 192.168.0.100:/ /mnt 實驗五是配置宿主PC機的TFTP服務(wù)。TFTP是簡單文件傳輸協(xié)議。每次重啟宿主PC機時,都要重啟該服務(wù),重啟命令為:
service xinetd restart。
實驗六是配置宿主PC機端NFS服務(wù)。NFS是指網(wǎng)絡(luò)文件系統(tǒng),它實現(xiàn)了文件在不同的系統(tǒng)間使用。當(dāng)使用者想用遠(yuǎn)端檔案時,只需調(diào)用“mount”就可以遠(yuǎn)端系統(tǒng)掛接在自己的檔案系統(tǒng)之下。每次重啟宿主PC機時,也都要重啟該服務(wù),重啟命令為: service nfs restart service nfs restart
(二)基本接口實驗
實驗
十二、簡單設(shè)備驅(qū)動程序
本次實驗的目的是讓我們動手實踐一個簡單的字符型設(shè)備驅(qū)動程序,學(xué)習(xí)Linux驅(qū)動程序構(gòu)架,學(xué)習(xí)在應(yīng)用程序中調(diào)用驅(qū)動。驅(qū)動程序代碼及注釋為: // 頭文件
#include
#define VERSION “PXA2700EP-SIMPLE_HELLO-V1.00-060530” // 定義版本號 void showversion(void)//顯示版本的函數(shù) { printk(“***************************************”);printk(“t %s tn”,VERSION);printk(“***************************************”);}
/*-------read:用于在指定文件描述符中讀取數(shù)據(jù) file:是文件指針 buf:讀取數(shù)據(jù)緩存區(qū) count:請求傳輸?shù)淖止?jié)數(shù) f_ops:文件當(dāng)前偏移量
當(dāng)讀取標(biāo)識符OURS_HELLO_DEBUG時,打印信息,然后返回count----------*/ ssize_t SIMPLE_HELLO_read(struct file * file ,char * buf, size_t count, loff_t * f_ops){ #ifdef OURS_HELLO_DEBUG
printk(“SIMPLE_HELLO_read[--kernel--]n”);#endif return count;}
/*-----write:用于向打開的文件寫數(shù)據(jù) file:是文件指針 buf:寫入數(shù)據(jù)緩存區(qū) count:求傳輸?shù)淖止?jié)數(shù) f_ops:文件當(dāng)前偏移量
當(dāng)讀取標(biāo)識符OURS_HELLO_DEBUG時,打印信息,然后返回count----------*/ ssize_t SIMPLE_HELLO_write(struct file * file ,const char * buf, size_t count, loff_t * f_ops){ #ifdef OURS_HELLO_DEBUG
printk(“SIMPLE_HELLO_write[--kernel--]n”);#endif
return count;}
/*-----ioctl:對設(shè)備的I/O通道進(jìn)行管理的函數(shù) inode:設(shè)備節(jié)點
flip:打開的一個文件
cmd:驅(qū)動程序的特殊命令編號 data:接收剩余參數(shù)
----------*/ ssize_t SIMPLE_HELLO_ioctl(struct inode * inode ,struct file * file, unsigned int cmd, long data){ #ifdef OURS_HELLO_DEBUG
printk(“SIMPLE_HELLO_ioctl[--kernel--]n”);#endif return 0;}
/*----------open:打開函數(shù)
inode:打開文件所對應(yīng)的i節(jié)點,獲取從設(shè)備號 flip:打開的一個文件
open()方法最重要的是調(diào)用了宏MOD_INC_USE_COUNT,這個宏主要用來使驅(qū)動程序使用計數(shù)器,避免不正確卸載程序
----------*/ ssize_t SIMPLE_HELLO_open(struct inode * inode ,struct file * file){ #ifdef OURS_HELLO_DEBUG
printk(“SIMPLE_HELLO_open[--kernel--]n”);#endif MOD_INC_USE_COUNT;return 0;}
/*----------released:關(guān)閉函數(shù)
Inode:打開文件所對應(yīng)的i節(jié)點,主要獲取從設(shè)備號 flip:打開的一個文件
release()方法最重要的是調(diào)用了宏MOD_DEC_INC_USE_COUNT,這個宏主要用來減少驅(qū)動程序使用計數(shù)器
----------*/ ssize_t SIMPLE_HELLO_release(struct inode * inode ,struct file * file){ #ifdef OURS_HELLO_DEBUG
printk(“SIMPLE_HELLO_release[--kernel--]n”);#endif MOD_DEC_INC_USE_COUNT;return 0;}
struct file_operations HELLO_ops ={ // SIMPLE_HELLO設(shè)備向系統(tǒng)注冊
open: SIMPLE_HELLO_open, read: SIMPLE_HELLO_read, write: SIMPLE_HELLO_write, ioctl: SIMPLE_HELLO_ioctl, release: SIMPLE_HELLO_release, };
/*----------INIT:驅(qū)動程序初始化
devfs_register_chrdev(SIMPLE_HELLO_MAJOR,“hello_serial_ctl”,& HELLO_ops)最為主要
devfs_register_chrdev注冊設(shè)備驅(qū)動程序,包括主設(shè)備號、驅(qū)動程序名、結(jié)構(gòu)體指針----------*/ static int __init HW_ HELLO_init(void){ int ret =-ENODEV;
ret = devfs_register_chrdev(SIMPLE_HELLO_MAJOR, “hello_serial_ctl”,& HELLO_ops);
showversion();
if(ret<0)
{
printk(“pxa270 init_module failed with %d n[--kernel--]”,ret);
}
else
{
printk(“pxa270 hello_driver register success!![--kernel--]n”);
} return ret;}
static int __init pxa270_ HELLO_init(void)//模塊初始化函數(shù),調(diào)用HW_ HELLO_init 函數(shù)
{ int ret =-ENODEV;
#ifdef OURS_HELLO_DEBUG
printk(“pxa270_ HELLO_init[--kernel--]n”);
#endif ret = HW_ HELLO_init();if(ret)return ret;return 0;}
/*----------模塊卸載函數(shù)
devfs_unregister_chrdev(SIMPLE_HELLO_MAJOR,“hello _ctl”)最為主要 devfs_unregister_chrdev卸載設(shè)備驅(qū)動程序,包括主設(shè)備號、驅(qū)動程序名
----------*/ static void __exit cleanup_ HELLO_ctl(void){ #ifdef OURS_HELLO_DEBUG
printk(“cleanup_HELLO_ctl[--kernel--]n”);#endif devfs_unregister_chrdev(SIMPLE_HELLO_MAJOR, “hello_ctl”);}
MODULE_DESCRIPTION(“simple hello driver module”);//描述信息 MODULE_AUTHOR(“l(fā)iduo”);
//驅(qū)動程序作者姓名 MODULE_LICENSE(“GPL”);module_init(pxa270_HELLO_init);
//指定驅(qū)動程序初始化函數(shù) module_exit(cleanup _HELLO_ctl);
//指定驅(qū)動程序卸載函數(shù)
Makefile文件代碼:
#TOPDIR:=$(shell cd..;pwd)TOPDIR:=.KERNELDIR=/pxa270_linux/linux INCLUDEDIR=$(KERNELDIR)/include CROSS_COMPILE=arm-linux-
AS =$(CROSS_COMPILE)as LD =$(CROSS_COMPILE)ld CC =$(CROSS_COMPILE)gcc CPP =$(CC)-E AR =$(CROSS_COMPILE)ar NM =$(CROSS_COMPILE)nm STRIP =$(CROSS_COMPILE)strip OBJCOPY=$(CROSS_COMPILE)objcopy OBJDUMP=$(CROSS_COMPILE)objdump
CFLAGS+=-I..CFLAGS+=-Wall –O –D_KERNEL_-DMODULE –I$(INCLUDEDIR)
TARGET = pxa270_hello_drv.o modules: $(TARGET)
all: $(TARGET)
pxa270_hello_drv.o:pxa270_hello_drv.c $(CC)-c $(CFLAGS)$^-o $@
clean: rm-f *.o *~ core.depend Makefile文件的內(nèi)容用于執(zhí)行編譯工作,一個Makefile文件包括:① 由make工具創(chuàng)建的目標(biāo)體(target),通常是目標(biāo)文件或可執(zhí)行文件;② 要創(chuàng)建目標(biāo)體所依賴的文件(dependency_file);③ 創(chuàng)建每個目標(biāo)需要運行的命令(command)。
以上兩個文件編輯完成后后,用make modules編譯驅(qū)動程序,編寫測試文件simple_test_driver.c,然后GCC編輯器編譯測試程序生成測試文件。成功生成測試文件后用超級終端開始掛載,加載驅(qū)動程序,使用命令./test測試,觀察測試結(jié)果,實驗完成。
實驗十三 CPU GPIO驅(qū)動程序設(shè)計
本實驗是讓我們在linux系統(tǒng)中插入自己的驅(qū)動程序,調(diào)用它。實現(xiàn)用CPU GPIO控制外部LED,利用PXA270核心板上的LED驗證我們的工作。驅(qū)動程序代碼:
驅(qū)動程序代碼與實驗十二無大區(qū)別,下面列出需要補充的代碼。
一、補充代碼
補充代碼1:
#ifdef OURS_GPIO_LED_DEBUG printk(“SIMPLE_GPIO_LED_write [--kernel--]n”);#endif return count;
補充代碼2:
#ifdef OURS_GPIO_LED_DEBUG printk(“SIMPLE_GPIO_LED_open [--kernel--]n”);#endif
補充代碼3:
open: SIMPLE_GPIO_LED_open, read: SIMPLE_GPIO_LED_read, write: SIMPLE_GPIO_LED_write, ioctl: SIMPLE_GPIO_LED_ioctl, release: SIMPLE_GPIO_LED_release, 不同之處:GPIO_LED,主文件名、二、Makefile文件:
將實驗十二相關(guān)代碼作如下修改即可: TARGET = pxa270_gpio_led_drv.o modules: $(TARGET)
all: $(TARGET)
pxa270_gpio_led_drv.o:pxa270_gpio_led_drv.c $(CC)-c $(CFLAGS)$^-o $@
三、作業(yè)代碼
要求:使得目標(biāo)板的核心板上的LED閃爍產(chǎn)生亮7秒,滅5秒的效果。作業(yè)主要代碼:
while(1)
{ ioctl(fd,LED_OFF);
sleep(5);//原來為sleep(1);
ioctl(fd,LED_ON);sleep(7);//原來為sleep(1); }
不同之處:改變代碼中加粗位置括號數(shù)字,可以改變燈亮和熄滅的時間比
四、測試顯示
測試時,超級終端上的顯示如下:
實驗十四 中斷實驗
本實驗是讓我們學(xué)習(xí)中斷的相關(guān)概念,以及Linux系統(tǒng)是如何處理中斷的,并且學(xué)會編寫獲取和處理外中斷的驅(qū)動程序。
一、補充代碼
補充代碼1:
printk(“*****************************************n”);printk(“t %s tn”,VERSION);printk(“************************************************nn”);補充代碼2:
#ifdef OURS_GPIO_LED_DEBUG printk(“SIMPLE_GPIO_LED_read [--kernel--]n”);#endif return count;
補充代碼3:
#ifdef OURS_GPIO_LED_DEBUG printk(“SIMPLE_GPIO_LED_write [--kernel--]n”);#endif return count;
補充代碼4:
open: SIMPLE_INT_open, read: SIMPLE_INT_read, write: SIMPLE_INT_write, ioctl: SIMPLE_INT_ioctl, release: SIMPLE_INT_release, 二、Makefile文件如實驗十三做相應(yīng)修改。
三、測試時,超級終端上顯示如下:
實驗十五 數(shù)碼管顯示驅(qū)動實驗
本實驗中,我們要編驅(qū)動程序以實現(xiàn)在Linux系統(tǒng)下控制LED數(shù)碼管的顯示。
一、補充代碼
補充代碼1:
printk(“*****************************************n”);printk(“t %s tn”,VERSION);printk(“************************************************nn”);
補充代碼2: #ifdef OURS_HELLO_DEBUG printk(“SERIAL_LED_read [--kernel--]n”);#endif return count;
補充代碼3: #ifdef OURS_HELLO_DEBUG printk(“SERIAL_LED_write [--kernel--]n”);#endif return count;
補充代碼4: #ifdef OURS_HELLO_DEBUG printk(“SERIAL_LED_ioctl [--kernel--]n”);#endif return 0;
補充代碼5: #ifdef OURS_HELLO_DEBUG printk(“SERIAL_LED_open [--kernel--]n”);#endif MOD_INC_USE_COUNT;return 0;
補充代碼6: #ifdef OURS_HELLO_DEBUG printk(“SERIAL_LED_release [--kernel--]n”);#endif MOD_DEC_USE_COUNT;return 0;
補充代碼7: open: SERIAL_LED_open, read: SERIAL_LED_read, write: SERIAL_LED_write, ioctl: SERIAL_LED_ioctl, release: SERIAL_LED_release
補充代碼8: int ret =-ENODEV;ret = devfs_register_chrdev(SERIAL_LED_MAJOR, “serial_led_ctl”, &SERIAL_LED_ops);Showversion();If(ret<0){ printk(“pxa270 init_module failed with %dn [--kernel--]”,ret);return ret;} else { printk(“pxa270 serial_led_driver register success!![--kernel--]n”);} return ret;
補充代碼9: int ret =-ENODEV;#ifdef OURS_HELLO_DEBUG printk(“pxa270_SERIAL_LED_init [--kernel--]n”);#endif ret = HW_SERIAL_LED_init();if(ret)return ret;return 0;
補充代碼10: #ifdef OURS_HELLO_DEBUG printk(“cleanup_SERIAL_LED [--kernel--]n”);#endif devfs_unregister_chrdev(SERIAL_LED_MAJOR, “serial_led”);
補充代碼11: MODULE_DESCRIPTION(“serial_led driver module”);MODULE_AUTHOR(“l(fā)iduo”);MODULE_LICENSE(“GPL”);module_init(pxa270_SERIAL_LED_init);module_exit(cleanup_SERIAL_LED);
二、Makefile文件與實驗十四相同,只需作相應(yīng)修改即可
三、作業(yè)代碼
1、實現(xiàn)目標(biāo)板上的LED數(shù)碼管循環(huán)顯示數(shù)字9-0。
for(count=0;count<10;count++)
{ data[0] = buf[9-count];//原來為data[0] = buf[count] ret=write(fd,data,1);sleep(1);} 修改之處:將顯示的數(shù)有buf[count]改為buf[9-count],實現(xiàn)反向循環(huán)顯示。
2、實現(xiàn)目標(biāo)板上的LED數(shù)碼管循環(huán)顯示數(shù)字2、4、6、8、0或者8、6、4、2、0。代碼: for(count=0;count<10;count+=2)//原來為count++
{data[0] = buf[count];ret=write(fd,data,1);sleep(1);}
修改之處:修改count的變化方式,讓其每次增加2,而不是1,使0、2、4、6、8循環(huán)顯示,如要循環(huán)顯示8、6、4、2、0的話,只要在上述代碼中將buf[count]改為buf[8-count]即可。
四、測試顯示:
測試時,顯示如下:
作業(yè)1: 作業(yè)2:
實驗十六 LED點陣驅(qū)動程序設(shè)計
本實驗要求我們學(xué)會編寫驅(qū)動程序,實現(xiàn)在Linux系統(tǒng)下控制LED點陣顯示,并在此基礎(chǔ)上稍加改進(jìn),實現(xiàn)對LED的控制。驅(qū)動程序代碼:
一、補充代碼
補充代碼1:
printk(“*****************************************n”);printk(“t %s tn”,VERSION);printk(“************************************************nn”);
補充代碼2:
#ifdef OURS_LED_DEBUG printk(“SIMPLW_LED_read [--kernel--]n”);#endif return count;
補充代碼3:
#ifdef OURS_LED_DEBUG printk(“SIMPLE_LED_ioctl [--kernel--]n”);#endif return 0;
補充代碼4:
open: SIMPLE_LED_open, read: SIMPLE_LED_read, write: SIMPLE_LED_write, ioctl: SIMPLE_LED_ioctl, release: SIMPLE_LED_release
補充代碼5:
int ret =-ENODEV;#ifdef OURS_LED_DEBUG printk(“pxa270_LED_CTL_init [--kernel--]n”);#endif ret = HW_LED_CTL_init();if(ret)return ret;return 0;
補充代碼6:
#ifdef OURS_LED_DEBUG printk(“cleanup_LED_ctl [--kernel--]n”);#endif devfs_unregister_chrdev(SIMPLE_LED_MAJOR, “l(fā)ed_ctl”);
二、Makefile程序仍然可以用前一個實驗的,只要把相關(guān)函數(shù)名改了就可以,此處不再贅述。
三、作業(yè)代碼
1、按橫方向隔行掃描led點陣數(shù)碼管。代碼:
for(i=1;i<=4;i++){ //原來為i<8
buf[0]=c;
buf[1]=~r;// row
for(j=1;j<=8;j++){
write(fd,buf,2);
printf(“buf[0],buf[1]: [%x,%x]n”,buf[0],buf[1]);
usleep(200000);// sleep 0.2 second
c = c<<1;
buf[0]=c;// column
}
c = 1;
r = r<<2;
} //原來為r=r<<1
修改之處:外層for循環(huán)中間i<8改為i<4,同時,將else中的r=r<<1改為r<<2。這樣就可以讓點陣在顯示時跳躍一行進(jìn)行顯示。
2、按豎方向順序掃描led點陣數(shù)碼管。代碼:
for(i=1;i<=8;i++){
buf[0]=c;
buf[1]=~r;// row
for(j=1;j<=8;j++){
write(fd,buf,2);
printf(“buf[0],buf[1]: [%x,%x]n”,buf[0],buf[1]);
usleep(200000);// sleep 0.2 second
r = r<<1;//原來此處為c=c<<1 buf[1]=~r;//原來此處為buf[1]=~c } r = 1;//原來此處為c=1 c = c<<1;//原來此處為r=r<<1 修改之處(現(xiàn)對于最初的測試程序,而不是作業(yè)1的測試程序):將r和c 進(jìn)行對調(diào)。實現(xiàn)將橫和豎的對調(diào),已達(dá)到豎方向掃描的目的。四、測試顯示
測試時,超級終端顯示如下:
作業(yè)1: 作業(yè)2:
實驗十七 AD驅(qū)動程序
本實驗要求我們學(xué)會編寫驅(qū)動程序?qū)δM量輸入進(jìn)行采集,并轉(zhuǎn)換為數(shù)字量顯示在超級終端上,從而實現(xiàn)AD轉(zhuǎn)換。
驅(qū)動程序代碼
一、補充代碼
補充代碼1:
printk(“*****************************************n”);printk(“t %s tn”,VERSION);
printk(“************************************************nn”);
補充代碼2:
#ifdef OURS_HELLO_DEBUG printk(“SIMPLE_HELLO_read [--kernel--]n”);#endif return count;
補充代碼3:
#ifdef OURS_HELLO_DEBUG printk(“SIMPLE_HELLO_write [--kernel--]n”);#endif return count;
補充代碼4:
#ifdef OURS_HELLO_DEBUG printk(“SIMPLE_HELLO_open [--kernel--]n”);#endif MOD_INC_USE_COUNT;return 0;
補充代碼5:
#ifdef OURS_HELLO_DEBUG printk(“SIMPLE_HELLO_release [--kernel--]n”);#endif MOD_DEC_USE_COUNT;return 0;
補充代碼6:
open: SIMPLE_HELLO_open, read: SIMPLE_HELLO_read, write: SIMPLE_HELLO_write, ioctl: SIMPLE_HELLO_ioctl, release: SIMPLE_HELLO_release
補充代碼7:
ad_ucb = ucb1x00_get();
int ret =-ENODEV;ret = devfs_register_chrdev(ADCTL_MAJOR, “ad_ctl”, &adctl_ops);Showversion();If(ret<0){ printk(“pxa270 init_module failed with %dn [--kernel--]”,ret);return ret;} else { printk(“pxa270 serial_led_driver register success!![--kernel--]n”);} return ret;
補充代碼8:
int ret =-ENODEV;#ifdef OURS_HELLO_DEBUG printk(“pxa270_AD_CTL_init [--kernel--]n”);#endif ret = HW_AD_CTL_init();if(ret)return ret;return 0;
補充代碼9:
#ifdef OURS_HELLO_DEBUG printk(“cleanup_AD_ctl [--kernel--]n”);#endif devfs_unregister_chrdev(ADCTL_MAJOR, “ad_ctl”);
二、Makefile文件可以用前一個程序的文件,只要將相應(yīng)部分的代碼修改即可
三、作業(yè)代碼
要求:將UCB_ADC_INP_AD0換為其他通道并觀察。代碼:
for(i=0;i<50;i++)
{ Val0 = ioctl(fd,UCB_ADC_INP_AD1,0);usleep(100);val1 = ioctl(fd,UCB_ADC_INP_AD0,0);printf(“val0 = %dtval1 = %dn”,val0,val1;usleep(500000);
}
修改之處:只需交換AD1和AD0即可實現(xiàn)輸出通道的改變。四、測試時顯示
測試時、超級終端顯示如下:
實驗十八 DA驅(qū)動程序
本實驗要求我們編寫驅(qū)動程序,實現(xiàn)將數(shù)字信號轉(zhuǎn)換成模擬信號并在示波器上顯示出模擬信號波形,即實現(xiàn)DA轉(zhuǎn)換。驅(qū)動程序代碼:
一、補充代碼
補充代碼1:
#include“../AD/pxa_ad_drv.h” /引用AD驅(qū)動程序的頭文件/
補充代碼2:
printk(“*****************************************n”);printk(“t %s tn”,VERSION);
printk(“************************************************nn”);
補充代碼3:
#ifdef OURS_DA_DEBUG printk(“SIMPLE_DA_read [--kernel--]n”);#endif return count;
補充代碼4:
#ifdef OURS_DA_DEBUG printk(“SIMPLE_DA_write [--kernel--]n”);#endif return count;
補充代碼5:
#ifdef OURS_DA_DEBUG printk(“SIMPLE_DA_ioctl [--kernel--]n”);#endif return 0;
補充代碼6:
#ifdef OURS_DA_DEBUG printk(“SIMPLE_DA_open [--kernel--]n”);#endif MOD_INC_USE_COUNT;return 0;
補充代碼7:
open: SIMPLE_DA_open, read: SIMPLE_DA_read, write: SIMPLE_DA_write, ioctl: SIMPLE_DA_ioctl, release: SIMPLE_DA_release
補充代碼8:
int ret =-ENODEV;ret = devfs_register_chrdev(SIMPLE_DA_MAJOR, “DA_ctl”, &DA_ctl_ops);Showversion();If(ret<0){ printk(“pxa270 init_module failed with %dn [--kernel--]”,ret);return ret;} else { printk(“pxa270 serial_led_driver register success!![--kernel--]n”);} return ret;
補充代碼9:
int ret =-ENODEV;#ifdef OURS_DA_DEBUG printk(“pxa270_DA_CTL_init [--kernel--]n”);#endif ret = HW_DA_CTL_init();if(ret)return ret;return 0;
補充代碼10:
#ifdef OURS_DA_DEBUG printk(“cleanup_DA_ctl [--kernel--]n”);#endif devfs_unregister_chrdev(SIMPLE_DA_MAJOR, “DA_ctl”);補充代碼11:
MODULE_DESCRIPTION(“serial_led driver module”);MODULE_AUTHOR(“l(fā)iduo”);MODULE_LICENSE(“GPL”);module_init(pxa270_DA_CTL_init);module_exit(cleanup_DA_ctl);
二、Makefile文件可以繼續(xù)用前面程序Mekefile的代碼,只需要將相應(yīng)部分的代碼修改即可。
三、作業(yè)代碼
要求:輸出三角波。代碼:(需要修改部分)
//---------------------print--------------------void da_create_sin(int fd){ unsigned char buf[(int)POINT];unsigned char*c;unsigned long I;int j;double x;for(j=0;j
x=(j/POINT)*(5*M_PI);//此處原來為x=sin((j/POINT
//*(2*M_PI))#ifdef OURS_DEBUG printf(“%ft”,x);#endif buf[j] =(unsigned char)255*(x/2+1)/2;#ifdef OURS_DEBUG printf(“%xn”, buf[j]);#endif } printf(“create sin waven”);printf(“Use”Ctrl + c“quit the functionn”);while(1){ c = buf;for(j=0;j
四、測試顯示:(以下為三角波)
(以下為sin)
三、實驗總結(jié):
在本次嵌入式實驗中,我們首先在老師的指導(dǎo)下了解了嵌入式系統(tǒng),初步接觸了Linux環(huán)境。我們的實驗板是OURS-PXA270-EP,它是一款基于INTEL XSCALE PXA270處理器,針對高校嵌入式系統(tǒng)教學(xué)和實驗科研的平臺。這款設(shè)備主要包括核心板與底板兩個部分,核心板主要集成了高速的PXA270 CPU,配套的存儲器,網(wǎng)卡等設(shè)備;底板主要是各種類型的接口與擴展口。
了解了實驗的平臺后,在接下來的基本實驗中我們學(xué)會了嵌入式開發(fā)系統(tǒng)硬件環(huán)境的搭建、Linux操作系統(tǒng)RedHat9的安裝、軟件環(huán)境的搭建,以及配置超級終端,配置通訊服務(wù)。這些實驗內(nèi)容只要按照實驗指導(dǎo)書上的步驟一步一步做即可,不會出現(xiàn)難以解決的問題,一般都會做的很順利。有三個需要注意的地方時,在配置端口時,一定要確定實驗箱接的是端口一,還是端口二。否則會出現(xiàn)無法建立呼叫的問題(其表現(xiàn)為超級終端接口內(nèi)沒有輸出內(nèi)容)。其次要確定虛擬機上網(wǎng)橋的設(shè)定是否正確。不然也會出現(xiàn)無法呼叫現(xiàn)象。最后,要確定網(wǎng)線是否連接上。在實驗時,由于有些電腦的網(wǎng)線接口有斷裂的現(xiàn)象,如果插口沒接好的話,將會出現(xiàn)nfs連接錯誤。
在基本實驗之后,進(jìn)行的就是接口實驗。總的來說,實驗的難度不大。當(dāng)然這是建立在對實驗代碼有一定理解的基礎(chǔ)之上的。在實驗十二中,我們對實驗的接口代碼規(guī)則已經(jīng)有了一定的了解。而之后的幾個實驗都是基于實驗十二進(jìn)行相應(yīng)的改動即可。所以完成下來難度不是很大。而對應(yīng)的作業(yè)中,我們僅需要對測試代碼進(jìn)行相應(yīng)的改寫。在對c語言有一定的了解的前提下,可以很容易相應(yīng)代碼所實現(xiàn)的功能,僅需要對相應(yīng)代碼做些修改即可。
不過,值得注意的還有兩點,第一:代碼的編寫一定要符合規(guī)則,同時,代碼的輸入要避免輸入錯誤。否則,在需要一次一次編譯一次次查看錯誤一次次改正錯誤,這會是個費時費力的工作。第二:每次實驗時,需要從新設(shè)定虛擬機的ip,即每次實驗開始時都需要重復(fù)做實驗五實驗六。不然在掛載時會出現(xiàn)無法掛載的現(xiàn)象。
通過這次實驗,我對嵌入式編程有了更深層次的理解,加深了我對理論知識的認(rèn)識,有助于今后的學(xué)習(xí)和工作。感謝黃惠英老師的細(xì)心指導(dǎo)。
第四篇:鄺堅_北郵嵌入式實驗報告
嵌入式系統(tǒng)期末實驗
一、實驗要求
題目:支持消息驅(qū)動模式的實時軟件框架
目的:在充分理解嵌入式處理器特點、RTOS 及強實時嵌入式系統(tǒng)軟件設(shè)計規(guī)范的基礎(chǔ)上,構(gòu)建自己的實時系統(tǒng)軟件框架基本功能,并在其上自擬應(yīng)用(如部分模擬TCP 的C/S兩端通信流程),測試軟件框架的相關(guān)功能。
環(huán)境:VxWorks 的VxSim 仿真環(huán)境或2440(ARM920T)內(nèi)容: 必選功能:
1.消息驅(qū)動的Task 統(tǒng)一框架,包含統(tǒng)一消息格式定義及使用規(guī)范; 2.支持消息驅(qū)動模式的軟定時器的機制; 3.Task 啟動同步功能;
4.體現(xiàn)前次實驗中實現(xiàn)的自定義內(nèi)存管理機制,最大限度降低外部碎片對系統(tǒng)可靠性的威脅。
可選功能(加分):
其它有利于實時處理的有效機制,如:無信號量(互斥)支持的臨界資源訪問方式,zero copy 等;
二、實現(xiàn)的功能
1.消息驅(qū)動的Task 統(tǒng)一框架,包含統(tǒng)一消息格式定義及使用規(guī)范; STATUS Task(){ Initialization(MBox, Data Structure, Timer, etc.)Forever{ MsgReceive If(…){ …… }else if(…){ …… } ……
} } typedef struct _MESSAGE { int mType;
/* 消息類型 0:timer->client *1:client->server 2:server->client*/ int mSendId;/* 發(fā)送任務(wù)的MESSAGE ID */ int mRecvId;/* 接收任務(wù)的MESSAGE ID */ int mData;/* 消息中傳遞的數(shù)據(jù) */ }MESSAGE;2.支持消息驅(qū)動模式的軟定時器的機制;
/* timer(id)向客戶端消息隊列定時發(fā)送的定時器*/ STATUS timer(int id){ MESSAGE* txMsg;/* 用于從消息隊列中接收消息 */ int tick;/*創(chuàng)建一個定時,用于提醒發(fā)送者任務(wù)定時發(fā)送消息*/ tick=sysClkRateGet();semTake(semSynStart,WAIT_FOREVER);FOREVER {
taskDelay((int)(tick*DELAY_SECOND));txMsg =(MESSAGE*)memMalloc(MAX_MSG_LEN);txMsg->mType = 0;txMsg->mSendId = MID_TIMER(id);txMsg->mRecvId = MID_CLIENT(id);txMsg->mData = 0;printf(“tTimer%d send message to tClient%d!n”,id,id);if(msgQSend(msgQIdClient[id],(char*)&txMsg,MAX_MSG_LEN,WAIT_FOREVER,MSG_{ return(ERROR);} PRI_NORMAL)== ERROR)
} } 3.Task 啟動同步功能;
由manager()創(chuàng)建的任務(wù)優(yōu)先級最高,先創(chuàng)建timer()、server()、client()的任務(wù),讓他們都在等待信號量semSynStart而被阻塞,最后創(chuàng)建manager()的任務(wù),占據(jù)CPU,等待其他所有任務(wù)都被阻塞,解鎖所有等待信號量的任務(wù),讓它們同時啟動。/* progStart()啟動實例程序*/ STATUS progStart(void){
int id;/* 用來區(qū)分不同的定時器或者客戶任務(wù) */ mallocPtr=&sysMalloc;mallocPtr->frontBlock = 0;return(OK);
initialPtr = initial();tidServer = tidManager = 0;for(id = 0;id < NUM_CLIENT;id++){ tidClient[id] = 0;} for(id = 0;id < NUM_TIMER;id++){
} /* 創(chuàng)建消息隊列 */ msgQIdServer = msgQCreate(MAX_MSGS, MAX_MSG_LEN, MSG_Q_FIFO|MSG_Q_EVENTSEND_ERR_NOTIFY);if(msgQIdServer == NULL){
} for(id = 0;id < NUM_CLIENT;id++){
} semSynStart = semBCreate(SEM_Q_FIFO | SEM_EVENTSEND_ERR_NOTIFY,SEM_EMPTY);semMalloc = semBCreate(SEM_Q_PRIORITY,SEM_FULL);semFree = semBCreate(SEM_Q_PRIORITY,SEM_FULL);/* 創(chuàng)建任務(wù) */ tidServer = taskSpawn(“tServer”, 220, 0, STACK_SIZE,(FUNCPTR)server,0,0,0,0,0,0,0,0,0,0);for(id = 0;id < NUM_CLIENT;id++){
char tempName[20];sprintf(tempName, “tClient%d”, id);tidClient[id] = taskSpawn(tempName, 210, 0, STACK_SIZE, msgQIdClient[id] = msgQCreate(MAX_MSGS, MAX_MSG_LEN, if(msgQIdClient[id] == NULL){ return(ERROR);} MSG_Q_FIFO|MSG_Q_EVENTSEND_ERR_NOTIFY);return(ERROR);tidTimer[id] = 0;(FUNCPTR)client,id,0,0,0,0,0,0,0,0,0);} for(id = 0;id < NUM_TIMER;id++){
} tidManager = taskSpawn(“tMannager”, 200, 0, STACK_SIZE,(FUNCPTR)manager,0,0,0,0,0,0,0,0,0,0);printf(“programe start!n”);return(OK);} /* manager()管理進(jìn)程,實現(xiàn)task同步*/ STATUS manager(){ int id;while(taskIsSuspended(tidServer)|| taskIsReady(tidServer))
{ while(taskIsSuspended(tidClient[id])|| taskDelay(10);taskIsReady(tidClient[id]))
} for(id = 0;id < NUM_TIMER;id++){
} semFlush(semSynStart);return(OK);} /* server()處理來自各個客戶任務(wù)的消息*/ STATUS server(void){ …… while(taskIsSuspended(tidTimer[id])|| taskIsReady(tidTimer[id]))taskDelay(10);taskDelay(10);for(id = 0;id < NUM_CLIENT;id++)char tempName[20];sprintf(tempName, “tTimer%d”, id);tidTimer[id] = taskSpawn(tempName, 230, 0, STACK_SIZE,(FUNCPTR)timer,id,0,0,0,0,0,0,0,0,0);semTake(semSynStart,WAIT_FOREVER);FOREVER { } return(OK);} /* timer(id)向客戶端定時發(fā)送的定時器*/ STATUS timer(int id){ ……
semTake(semSynStart,WAIT_FOREVER);FOREVER { } /*client(id)向服務(wù)器任務(wù)發(fā)請求消息*/ STATUS client(int id){ ……
semTake(semSynStart,WAIT_FOREVER);FOREVER { } return(OK);} 4.體現(xiàn)前次實驗中實現(xiàn)的自定義內(nèi)存管理機制,最大限度降低外部碎片對系統(tǒng)可靠性的威脅。
靜態(tài)內(nèi)存的數(shù)據(jù)結(jié)構(gòu)為單鏈表,采用頭插法,申請內(nèi)存時,修改firstavailable另其指向第二塊,將firstavailable指向的頭塊取出,回收內(nèi)存時,將回收的塊的frontBlock指向第一塊,修改firstavailable另其指向回收的塊,將回收的塊作為第一塊,數(shù)據(jù)結(jié)構(gòu)如下所示: …… ……
} return(OK);…… poolpoolHeadnextfirstavailable blockHeadfrontBlockblockHeadfrontBlockblockHeadfrontBlockblockHeadpoolHeadfirstavailableblockHeadfrontBlockblockHeadfrontBlockblockHeadfrontBlockblockHead 靜態(tài)分配了含有32個16B塊的內(nèi)存池和含有16個256B塊的內(nèi)存池,如果申請的內(nèi)存大于256B,調(diào)用系統(tǒng)malloc。
/*initial()初始化內(nèi)存池*/ pool* initial(void){ int i;pool* mem;pool* poolPtr;poolHead* poolHeadPtr;blockHead* blockHeadPtr;
mem=(pool*)malloc(6000);/*分配6000B內(nèi)存作為內(nèi)存池*/
/*初始化pool*/ poolPtr =(pool*)mem;poolPtr->poolNum = 2;poolPtr->pool =(poolHead*)((char*)mem + sizeof(pool));/*pool指向申請內(nèi)存區(qū)尾*/
/*初始化pool 1 該內(nèi)存池分配大小為16B的內(nèi)存*/ poolHeadPtr =(poolHead*)((char*)mem + sizeof(pool));/*初始化內(nèi)存池的首地址*/ poolHeadPtr->available = 32;/*初始化可用塊數(shù)32*/ poolHeadPtr->blockSize = 16;/*塊大小16B*/ blockHeadPtr =(blockHead*)((char*)poolHeadPtr+sizeof(poolHead));初始化塊的首地址*/ poolHeadPtr->firstavailable = blockHeadPtr;/*初始化第一塊可用塊的地址*/ poolHeadPtr->next=(poolHead*)((char*)poolHeadPtr + sizeof(poolHeadPtr)
/*+ 32*(sizeof(blockHead)+16));/*next指向第二個內(nèi)存池 */
blockHeadPtr->poolId =1;blockHeadPtr->frontBlock = 0;
for(i=1;i<32;i++)/*將該內(nèi)存池劃分為32個容量16B的內(nèi)存塊*/ {
址*/ } /*初始化pool 2 該內(nèi)存池分配大小為256B的內(nèi)存*/ poolHeadPtr = poolHeadPtr->next;poolHeadPtr->available = 16;/*初始化可用塊數(shù)16*/ poolHeadPtr->blockSize = 256;/*塊大小256*/
blockHeadPtr =(blockHead*)((char*)poolHeadPtr+sizeof(poolHead));
poolHeadPtr->firstavailable = blockHeadPtr;poolHeadPtr->next = 0;
blockHeadPtr->poolId =2;blockHeadPtr->frontBlock = 0;
for(i=1;i<16;i++)/*將該內(nèi)存池劃分為16個容量256B的內(nèi)存塊*/ {
} return(pool*)mem;} blockHeadPtr=(blockHead*)((char*)blockHeadPtr + blockHeadPtr->poolId = 2;/* pool號為2,表示他是256B容量的*/ blockHeadPtr->frontBlock = poolHeadPtr->firstavailable;poolHeadPtr->firstavailable = blockHeadPtr;(sizeof(blockHead)+256));
blockHeadPtr=(blockHead*)((char*)blockHeadPtr + blockHeadPtr->poolId = 1;/* pool號為1,表示他是16B容量的*/ blockHeadPtr->frontBlock = poolHeadPtr->firstavailable;/* 當(dāng)前首poolHeadPtr->firstavailable = blockHeadPtr;/* 求下一首個可用塊地(sizeof(blockHead)+16));/*塊的首址移動16加結(jié)構(gòu)體的開銷長度*/ 個可用塊地址賦給frontBlock */
/*memMalloc()分配內(nèi)存*/ void* memMalloc(int Size){ void* mem;poolHead* poolHeadPtr;blockHead* blockHeadPtr;
semTake(semMalloc,WAIT_FOREVER);
poolHeadPtr = initialPtr->pool;
if((Size <= 16)&&(poolHeadPtr->available!= 0))/*長度小于16時,分配長度為16的內(nèi)存空間*/ {
blockHeadPtr = poolHeadPtr->firstavailable;/*首個可用塊地址賦給poolHeadPtr->firstavailable = blockHeadPtr->frontBlock;/*改變下poolHeadPtr->available--;/*可用塊數(shù)減一*/ semGive(semMalloc);分配塊的首地址*/ 一第一可用塊的地址*/
return(void*)((char*)blockHeadPtr + sizeof(blockHead));/*分配內(nèi)存時加入塊頭開銷*/ } else if((Size <= 256)&&((poolHeadPtr->next)->available!= 0))
{
} else
{ printf(“n[Warning] : Too large for blocks or the blocks are /*其他情況用系統(tǒng)的內(nèi)存分配函數(shù)malloc分配*/ blockHeadPtr =(poolHeadPtr->next)->firstavailable;(poolHeadPtr->next)->firstavailable = blockHeadPtr->frontBlock;(poolHeadPtr->next)->available--;semGive(semMalloc);return(void*)((char*)blockHeadPtr + sizeof(blockHead));/*長度大于16小于256時,分配長度為256的內(nèi)存空間*/ exhausted n”);
} } /*memFree()釋放內(nèi)存空間*/ void memFree(void* dataPtr){ char* mem=(char*)dataPtr;poolHead* poolHeadPtr;blockHead* blockHeadPtr;
semTake(semFree,WAIT_FOREVER);
poolHeadPtr = initialPtr->pool;/*恢復(fù)內(nèi)存池首址*/ blockHeadPtr =(blockHead*)((char*)memsizeof(blockHead));Timer0Timer1Timer9ClientMsgQ0ClientMsgQ1……ClientMsgQ9Client0Client1Client9ServerMsgQserver6.zero copy 消息隊列存儲的是指向消息的指針,從而實現(xiàn)了零拷貝。#define MAX_MSG_LEN sizeof(MESSAGE*)MESSAGE* rxMsg;/* 用于從消息隊列中接收消息 */ MESSAGE* txMsg;/* 用于向消息隊列中發(fā)送消息 */ msgQReceive(msgQIdServer,(char*)&rxMsg,MAX_MSG_LEN,WAIT_FOREVER);
msgQSend(msgQIdClient[mSendId],(char*)&txMsg,MAX_MSG_LEN,WAIT_FOREVER,MSG_PRI_NORMAL);
三、運行結(jié)果
在shell中輸入progStart,觀察VxSim,輸入progStop結(jié)束。
四、心得
實驗中遇到了各種各樣的問題,特別是代碼調(diào)試,對報錯的分析,定位錯誤,但是通過不懈努力,完成了本次實驗,讓我對課堂上所講的內(nèi)容有了更深刻的認(rèn)識,對嵌入式實時操作系統(tǒng)有了更深的理解。
由于正值期末,考試任務(wù)繁重,時間緊迫,自身水平有限,難免會有疏漏,請老師指正。
第五篇:北郵數(shù)據(jù)庫實驗報告
數(shù)據(jù)庫實驗報告
(三)姓名:學(xué)號:班級:
1.用Transact-SQL語句、數(shù)據(jù)導(dǎo)入、SQL Server Management Studio(企業(yè)管理器)輸入的方法對所給定的8張表的數(shù)據(jù)輸入到數(shù)據(jù)庫中。自行決定每張表的數(shù)據(jù)導(dǎo)入辦法,但每種方法各針對二或三張表。
? Transact-SQL語句: 導(dǎo)入department,student, student_course表。
insertinto department select*from openrowset
('microsoft.jep.oledb.4.0','excel 5.0;hdr=yes;database=D:課件數(shù)據(jù)庫database2.xls',department$);
insertinto student select*from openrowset
('microsoft.jep.oledb.4.0','excel 5.0;hdr=yes;database=D:課件數(shù)據(jù)庫database2.xls',student$);
insertinto student_course select*from openrowset
('microsoft.jep.oledb.4.0','excel 5.0;hdr=yes;database=D:課件數(shù)據(jù)庫database2.xls',student_course$);
? 數(shù)據(jù)導(dǎo)入:
操作:選中數(shù)據(jù)庫studentsdb,右鍵-任務(wù)-導(dǎo)入數(shù)據(jù)。導(dǎo)入book, class, course表。
SQL Server Management Studio: 操作:右鍵需要編輯的表,選擇編輯前200行。
Teacher:
Teacher_course_class:
導(dǎo)入結(jié)果: Book:
Class:
Course:
Department:
Student:
Student_course:
Teacher:
Teacher_course_class:
2.用Transact-SQL向Course表中插入一條記錄,course_name為空,看運行的結(jié)果。
SQL語句:
INSERTINTO course VALUES('dep02_s002', null,'dep02_s002', '72', '5', '4');運行結(jié)果:
分析:course_name有not null的約束,因此這條語句不能執(zhí)行。
3.用Transact-SQL修改Course表中credit為5的記錄,將其credit改為7, credit小于4的改為2,看運行的結(jié)果。
SQL語句:
update course set credit=7 where credit=5;執(zhí)行結(jié)果:
分析:約束C1指定了credit的范圍為1至6.SQL語句:
update course set credit=2 where credit<4;執(zhí)行結(jié)果:
4.刪除一條學(xué)生記錄,看運行結(jié)果,對運行結(jié)果進(jìn)行分析。SQL語句:
deletefrom student where student_id='g9940201';執(zhí)行結(jié)果:
分析:因為有參照完整性約束,不能刪除。
5.用Transact-SQL完成將編號為dep04_b001的課程的選修信息插入到一個新的選課信息表中。
SQL語句:
Creattablestudent_course2(course_idchar(20), student_idchar(20)gradeint, creditint, semesterint,school_yearchar(20),primarykey(course_id,student_id));
insertintostudent_course2 select*fromstudent_course wherecourse_id='dep04_b001';執(zhí)行結(jié)果:
6.用Transact-SQL完成刪除單片機原理課程的選課信息,分析運行結(jié)果。
SQL語句:
deletefrom student_course where course_id in(select course_id from course
where course_name='單片機原理')執(zhí)行結(jié)果: 分析:所有課程號為dep04_s003的課程被刪除。
本實驗中遇到的問題和解決方法:
本實驗的順利完成需要預(yù)先作很多準(zhǔn)備工作。以下就是我在遇到缺少組件accessdatabaseengine時的解決過程的記錄。
AccessDatabaseEngine的安裝
accessdatabaseengine用于和office連接,導(dǎo)入導(dǎo)出數(shù)據(jù),本實驗中需要導(dǎo)入excel文件。安裝配合office的版本,我安裝的是accessdatabaseengine2017(English)版本。安裝32位版本,因office2016是32位。之前誤操作安裝了不能使用的老舊版本accessdatabaseengine2007,通過控制面板-應(yīng)用程序卸載將其卸載了。安裝accessdatabaseengine依然報錯,是因為microsoftofficeclicktorun阻礙sqlserver的一些功能,需要將其卸載。這是微軟推出的用于減少office打開速度的應(yīng)用程序,安裝office2016時會自動安裝上,原理是開機時將一部分內(nèi)容放到內(nèi)存中,因此打開文件時會更快一些。檢測自己的office是通過clickto run 還是MSI安裝的,可以在word中點擊文件-賬戶,查看產(chǎn)品信息,如果有下圖中“office更新”這個選項,則說明安裝過click to run。這個程序在控制面板-應(yīng)用程序中找不到,因此用刪除注冊表的方式卸載。快捷鍵“win+R”輸入“regedit”打開注冊表編輯器,左邊HKEY_CLASSES_ROOT-Installer-Product-00006開頭的選項,有四個。單擊這幾個選項,在右側(cè)查看詳細(xì)信息,可以看到ProductNam是Microsoft Access database engine 2007(我原來誤安裝的老版本)。刪除之前先備份注冊表。方法一:選中要刪除的文件,右鍵-導(dǎo)出,保存。只保存了要刪除的文件。方法二:注冊表編輯器,文件-導(dǎo)出,保存。保存了注冊表所有信息。這是因為如果誤刪了重要文件會導(dǎo)致嚴(yán)重后果,可能需要重裝系統(tǒng),留此備份是為了可以恢復(fù)系統(tǒng)。
備份完之后,選中要刪除的文件(00006開頭的四個),右鍵-刪除即可?;氐紸ccess database engine 32位的程序安裝包,安裝。我無法安裝64位,可能是因為office是32位。安裝成功之后就可以在sqlserver中導(dǎo)入excel文件了。