嵌入式Linux驅(qū)動程序和QT.ppt
《嵌入式Linux驅(qū)動程序和QT.ppt》由會員分享,可在線閱讀,更多相關(guān)《嵌入式Linux驅(qū)動程序和QT.ppt(47頁珍藏版)》請在裝配圖網(wǎng)上搜索。
嵌入式Linux驅(qū)動程序開發(fā),遼寧工程技術(shù)大學(xué)電信學(xué)院,1嵌入式Linux的設(shè)備管理,,Linux將設(shè)備分成兩大類:一類是塊設(shè)備,類似磁盤以記錄塊或扇區(qū)為單位,成塊進(jìn)行輸入/輸出的設(shè)備;另一類是字符設(shè)備,類似鍵盤以字符為單位,逐個進(jìn)行輸入/輸出的設(shè)備。網(wǎng)路設(shè)備是介于塊設(shè)備和字符設(shè)備之間的一種特殊設(shè)備。塊設(shè)備接口僅支持面向塊的I/O操作,所有I/O操作都通過在內(nèi)核地址空間中的I/O緩沖區(qū)進(jìn)行,它可以支持隨機(jī)存取的功能。文件系統(tǒng)通常都建立在塊設(shè)備上。字符設(shè)備接口支持面向字符的I/O操作,由于它們不經(jīng)過系統(tǒng)的快速緩存,所以它們負(fù)責(zé)管理自己的緩沖區(qū)結(jié)構(gòu)。字符設(shè)備接口只支持順序存取的功能,一般不能進(jìn)行任意長度的I/O請求,而是限制I/O請求的長度必須是設(shè)備要求的基本塊長的倍數(shù)。處理器與設(shè)備間數(shù)據(jù)交換方式處理器與外設(shè)之間傳輸數(shù)據(jù)的控制方式通常有3種:查詢方式、中斷方式和直接內(nèi)存存?。―MA)方式。1.查詢方式設(shè)備驅(qū)動程序通過設(shè)備的I/O端口空間,以及存儲器空間完成數(shù)據(jù)的交換。例如,網(wǎng)卡一般將自己的內(nèi)部寄存器映射為設(shè)備的I/O端口,而顯示卡則利用大量的存儲器空間作為視頻信息的存儲空間。利用這些地址空間,驅(qū)動程序可以向外設(shè)發(fā)送指定的操作指令。通常來講,由于外設(shè)的操作耗時較長,因此,當(dāng)處理器實(shí)際執(zhí)行了操作指令之后,驅(qū)動程序可采用查詢方式等待外設(shè)完成操作。驅(qū)動程序在提交命令之后,開始查詢設(shè)備的狀態(tài)寄存器,當(dāng)狀態(tài)寄存器表明操作完成時,驅(qū)動程序可繼續(xù)后續(xù)處理。查詢方式的優(yōu)點(diǎn)是硬件開銷小,使用起來比較簡單。但在此方式下,CPU要不斷地查詢外設(shè)的狀態(tài),當(dāng)外設(shè)未準(zhǔn)備好時,就只能循環(huán)等待,不能執(zhí)行其他程序,這樣就浪費(fèi)了CPU的大量時間,降低了處理器的利用率。,2.中斷方式查詢方式白白浪費(fèi)了大量的處理器時間,而中斷方式才是多任務(wù)操作系統(tǒng)中最有效利用處理器的方式。當(dāng)CPU進(jìn)行主程序操作時,外設(shè)的數(shù)據(jù)已存入端口的數(shù)據(jù)輸入寄存器,或端口的數(shù)據(jù)輸出寄存器已空,此時由外設(shè)通過接口電路向CPU發(fā)出中斷請求信號。CPU在滿足一定條件下,暫停執(zhí)行當(dāng)前正在執(zhí)行的主程序,轉(zhuǎn)入執(zhí)行相應(yīng)能夠進(jìn)行輸入/輸出操作的子程序,待輸入/輸出操作執(zhí)行完畢之后,CPU再返回并繼續(xù)執(zhí)行原來被中斷的主程序。這樣,CPU就避免了把大量時間耗費(fèi)在等待、查詢外設(shè)狀態(tài)的操作上,使其工作效率得以大大提高。中斷方式的原理示意圖如下圖所示。能夠向CPU發(fā)出中斷請求的設(shè)備或事件稱為中斷源。中斷源向CPU發(fā)出中斷請求,若優(yōu)先級別最高,則CPU在滿足一定的條件時,可中斷當(dāng)前程序的運(yùn)行,保護(hù)好被中斷的主程序的斷點(diǎn)及現(xiàn)場信息,然后根據(jù)中斷源提供的信息,找到中斷服務(wù)子程序的入口地址,轉(zhuǎn)去執(zhí)行新的程序段,這就是中斷響應(yīng)。CPU響應(yīng)中斷是有條件的,如內(nèi)部允許中斷、中斷未被屏蔽、當(dāng)前指令執(zhí)行完等。CPU響應(yīng)中斷以后,就會中止當(dāng)前的程序,轉(zhuǎn)去執(zhí)行一個中斷服務(wù)子程序,以完成為相應(yīng)設(shè)備的服務(wù)。系統(tǒng)引入中斷機(jī)制后,CPU與外設(shè)處于“并行”工作狀態(tài),便于實(shí)現(xiàn)信息的實(shí)時處理和系統(tǒng)的故障處理。,3.直接訪問內(nèi)存(DMA)方式利用中斷,系統(tǒng)和設(shè)備之間可以通過設(shè)備驅(qū)動程序傳送數(shù)據(jù),但是,當(dāng)傳送的數(shù)據(jù)量很大時,因?yàn)橹袛嗵幚砩系难舆t,利用中斷方式的效率會大大降低。而直接內(nèi)存訪問(DMA)可以解決這一問題。DMA可允許設(shè)備和系統(tǒng)內(nèi)存間在沒有處理器參與的情況下傳輸大量數(shù)據(jù)。設(shè)備驅(qū)動程序在利用DMA之前,需要選擇DMA通道并定義相關(guān)寄存器,以及數(shù)據(jù)的傳輸方向,即讀取或?qū)懭耄缓髮⒃O(shè)備設(shè)定為利用該DMA通道傳輸數(shù)據(jù)。設(shè)備完成設(shè)置之后,可以立即利用該DMA通道在設(shè)備和系統(tǒng)的內(nèi)存之間傳輸數(shù)據(jù),傳輸完畢后產(chǎn)生中斷以便通知驅(qū)動程序進(jìn)行后續(xù)處理。在利用DMA進(jìn)行數(shù)據(jù)傳輸?shù)耐瑫r,處理器仍然可以繼續(xù)執(zhí)行指令。,設(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è)置,以及提供對設(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è)備的操作。,驅(qū)動程序結(jié)構(gòu),1.自動配置和初始化子程序,用來檢測所需驅(qū)動的硬件設(shè)備是否工作正常、對正常工作的設(shè)備及其相關(guān)驅(qū)動程序所需要的軟件狀態(tài)進(jìn)行初始化。2.服務(wù)于I/O請求的子程序,該子程序稱為驅(qū)動程序的上半部。這部分程序在執(zhí)行時,系統(tǒng)仍認(rèn)為與進(jìn)行調(diào)用的進(jìn)程屬于同一個進(jìn)程,只是由用戶態(tài)變成了核心態(tài),可以在其中調(diào)用sleep()等與進(jìn)程運(yùn)行環(huán)境有關(guān)的函數(shù)。3.中斷服務(wù)程序,又稱為驅(qū)動程序的下半部,由Linux系統(tǒng)來接收硬件中斷,再由系統(tǒng)調(diào)用中斷服務(wù)子程序。在系統(tǒng)內(nèi)部,I/O設(shè)備的存取通過一組固定的入口點(diǎn)來進(jìn)行,入口點(diǎn)也可以理解為設(shè)備的句柄,就是對設(shè)備進(jìn)行操作的基本函數(shù)。字符型設(shè)備驅(qū)動程序提供如下幾個入口點(diǎn):?open入口點(diǎn)。打開設(shè)備準(zhǔn)備I/O操作。對字符設(shè)備文件進(jìn)行打開操作,都會調(diào)用設(shè)備的open入口點(diǎn)。open子程序必須對將要進(jìn)行的I/O操作做好必要的準(zhǔn)備工作,如清除緩沖區(qū)等。如果設(shè)備是獨(dú)占的,即同一時刻只能有一個程序訪問此設(shè)備,則open子程序必須設(shè)置一些標(biāo)志以表示設(shè)備處于忙狀態(tài)。?close入口點(diǎn)。關(guān)閉一個設(shè)備。當(dāng)最后一次使用設(shè)備完成后,調(diào)用close子程序。獨(dú)占設(shè)備必須標(biāo)記設(shè)備方可再次使用。?read入口點(diǎn)。從設(shè)備上讀數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數(shù)據(jù)。對字符設(shè)備文件進(jìn)行讀操作將調(diào)用read子程序。?write入口點(diǎn)。往設(shè)備上寫數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是把數(shù)據(jù)寫入緩沖區(qū)里。對字符設(shè)備文件進(jìn)行寫操作將調(diào)用write子程序。?ioctl入口點(diǎn)。執(zhí)行讀、寫之外的操作。?select入口點(diǎn)。檢查設(shè)備,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。select系統(tǒng)調(diào)用在檢查與設(shè)備文件相關(guān)的文件描述符時使用select入口點(diǎn)。,structfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);int(*readdir)(structfile*,void*,filldir_t);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*flush)(structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);int(*fasync)(int,structfile*,int);int(*lock)(structfile*,int,structfile_lock*);ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);};?lseek,移動文件指針的位置,只能用于可以隨機(jī)存取的設(shè)備。?read,進(jìn)行讀操作,buf為存放讀取結(jié)果的緩沖區(qū),count為所要讀取的數(shù)據(jù)長度。?write,進(jìn)行寫操作,與read類似。?select,進(jìn)行選擇操作。?ioctl,進(jìn)行讀、寫以外的其他操作。?mmap,用于把設(shè)備的內(nèi)容映射到地址空間,一般只有塊設(shè)備驅(qū)動程序使用。?open,打開設(shè)備進(jìn)行I/O操作。返回0表示成功,返回負(fù)數(shù)表示失敗。?release,即close操作。,structfile主要用于與文件系統(tǒng)相關(guān)的設(shè)備驅(qū)動程序,可提供關(guān)于被打開的文件的信息,定義如下:structfile{structlist_headf_list;structdentry*f_dentry;structvfsmount*f_vfsmnt;structfile_operations*f_op;atomic_tf_count;unsignedintf_flags;mode_tf_mode;loff_tf_pos;unsignedlongf_reada,f_ramax,f_raend,f_ralen,f_rawin;structfown_structf_owner;unsignedintf_uid,f_gid;intf_error;unsignedlongf_version;/*neededforttydriver,andmaybeothers*/void*private_data;/*preallocatedhelperkiobuftospeedupO_DIRECT*/structkiobuf*f_iobuf;longf_iobuf_lock;};在用戶自己的驅(qū)動程序中,首先要根據(jù)驅(qū)動程序的功能,完成file_operations結(jié)構(gòu)中函數(shù)的實(shí)現(xiàn)。不需要的函數(shù)接口可以直接在file_operations結(jié)構(gòu)中初始化為NULL。file_operations中的變量會在驅(qū)動程序初始化時,注冊到系統(tǒng)內(nèi)部。每個進(jìn)程對設(shè)備的操作,都會根據(jù)主次設(shè)備號,轉(zhuǎn)換成對file_operations結(jié)構(gòu)的訪問。,2設(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ā)速度。實(shí)現(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ū)動控制下的某個獨(dú)立的設(shè)備。(3)實(shí)現(xiàn)初始化函數(shù)。在驅(qū)動程序中實(shí)現(xiàn)驅(qū)動的注冊和卸載。(4)設(shè)計所要實(shí)現(xiàn)的文件操作,定義file_operations結(jié)構(gòu)。(5)實(shí)現(xiàn)所需的文件操作調(diào)用,如read、write等。(6)實(shí)現(xiàn)中斷服務(wù),并用request_irq向內(nèi)核注冊,中斷并不是每個設(shè)備驅(qū)動所必需的。(7)編譯該驅(qū)動程序到內(nèi)核中,或者用insmod命令加載模塊。(8)測試該設(shè)備,編寫應(yīng)用程序,對驅(qū)動程序進(jìn)行測試。,模塊化驅(qū)動程序設(shè)計,在探討模塊之前,有必要先看一看內(nèi)核模塊與應(yīng)用程序之間的區(qū)別。一個應(yīng)用從頭到尾完成一個任務(wù),而模塊則是為以后處理某些請求而注冊自己,完成這個任務(wù)后,它的“主”函數(shù)就立即中止了。然而,內(nèi)核源碼僅能連接編譯到內(nèi)核模塊中,不像應(yīng)用那樣有眾多的支持庫,內(nèi)核能調(diào)用的僅是由內(nèi)核開放出來的那些函數(shù)。由于沒有庫連接到模塊中,所以源碼文件不應(yīng)該模塊化任何常規(guī)頭文件。與內(nèi)核有關(guān)的所有內(nèi)容都定義在目錄/usr/include/linux和/usr/include/asm下的頭文件中。1.內(nèi)核空間和用戶空間當(dāng)談到軟件時,我們通常稱執(zhí)行態(tài)為“內(nèi)核空間”和“用戶空間”,在Linux系統(tǒng)中,內(nèi)核在最高級執(zhí)行,也稱為“管理員態(tài)”,在這一級任何操作都可以執(zhí)行。而應(yīng)用程序則執(zhí)行在最低級,所謂的“用戶態(tài)”,在這一級處理器禁止對硬件的直接訪問和對內(nèi)存的未授權(quán)訪問。模塊是在所謂的“內(nèi)核空間”中運(yùn)行的,而應(yīng)用程序則是在“用戶空間”中運(yùn)行的。它們分別引用不同的內(nèi)存映射,也就是程序代碼使用不同的“地址空間”。Linux通過系統(tǒng)調(diào)用和硬件中斷完成從用戶空間到內(nèi)核空間的控制轉(zhuǎn)移。執(zhí)行系統(tǒng)調(diào)用的內(nèi)核代碼在進(jìn)程的上下文中執(zhí)行,它執(zhí)行調(diào)用進(jìn)程的操作而且可以訪問進(jìn)程地址空間的數(shù)據(jù)。但處理中斷與此不同,處理中斷的代碼相對進(jìn)程而言是異步的,而且與任何一個進(jìn)程都無關(guān)。模塊的作用就是擴(kuò)展內(nèi)核的功能,是運(yùn)行在內(nèi)核空間的模塊化的代碼。模塊的某些函數(shù)作為系統(tǒng)調(diào)用執(zhí)行,而某些函數(shù)則負(fù)責(zé)處理中斷。,各個模塊被分別編譯并鏈接成一組目標(biāo)文件,這些文件能被載入正在運(yùn)行的內(nèi)核,或從正在運(yùn)行的內(nèi)核中卸載。必要時內(nèi)核能請求內(nèi)核守護(hù)進(jìn)程Kerneld對模塊進(jìn)行加載或卸載。根據(jù)需要動態(tài)載入模塊可以保證內(nèi)核達(dá)到最小,并且具有很大的靈活性。內(nèi)核模塊一部分保存在Kernel中,另一部分在Modules包中。在項目一開始,很多地方對設(shè)備安裝、使用和改動都是通過編譯進(jìn)內(nèi)核來實(shí)現(xiàn)的,對驅(qū)動程序稍微做點(diǎn)改動,就要重新燒寫一遍內(nèi)核,而且燒寫內(nèi)核經(jīng)常容易出錯,還占用資源。模塊采用的則是另一種途徑,內(nèi)核提供一個插槽,它就像一個插件,在需要時,插入內(nèi)核中使用,不需要時從內(nèi)核中拔出。這一切都由一個稱為Kerneld的守護(hù)進(jìn)程自動處理。2.模塊化的優(yōu)缺點(diǎn)內(nèi)核模塊的動態(tài)加載具有以下優(yōu)點(diǎn):將內(nèi)核映像的尺寸保持在最小,并具有最大的靈活性。這便于檢驗(yàn)新的內(nèi)核代碼,而不需要重新編譯內(nèi)核并重新引導(dǎo)。但是,內(nèi)核模塊的引入也對系統(tǒng)性能和內(nèi)存的利用有負(fù)面影響。裝入的內(nèi)核模塊與其他內(nèi)核部分一樣,具有相同的訪問權(quán)限,由此可見,差的內(nèi)核模塊會導(dǎo)致系統(tǒng)崩潰。為了使內(nèi)核模塊能訪問所有內(nèi)核資源,內(nèi)核必須維護(hù)符號表,并在加載和卸載模塊時修改這些符號表。由于有些模塊要求利用其他模塊的功能,故內(nèi)核要維護(hù)模塊之間的依賴性。內(nèi)核必須能夠在卸載模塊時通知模塊,并且要釋放分配給模塊的內(nèi)存和中斷等資源。內(nèi)核版本和模塊版本的不兼容也可能導(dǎo)致系統(tǒng)崩潰,因此,嚴(yán)格的版本檢查是必需的。盡管內(nèi)核模塊的引入同時帶來不少問題,但是模塊機(jī)制確實(shí)是擴(kuò)充內(nèi)核功能的一種行之有效的方法,也是在內(nèi)核級進(jìn)行編程的有效途徑。,設(shè)備注冊和初始化,設(shè)備的驅(qū)動程序在加載的時候首先需要調(diào)用入口函數(shù)init_module(),該函數(shù)最重要的一個工作就是向內(nèi)核注冊該設(shè)備,對于字符設(shè)備調(diào)用register_chrdev()完成注冊。register_chrdev的定義為:intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);其中,major是為設(shè)備驅(qū)動程序向系統(tǒng)申請的主設(shè)備號,如果為0,則系統(tǒng)為此驅(qū)動程序動態(tài)分配一個主設(shè)備號。name是設(shè)備名,fops是對各個調(diào)用的入口點(diǎn)說明。此函數(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的值表示注冊成功。注冊以后,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(unsignedintmajor,constchar*name);此函數(shù)的參數(shù)為主設(shè)備號major和設(shè)備名name。Linux內(nèi)核把name和major在內(nèi)核注冊的名稱對比,如果不相等,卸載失敗,并返回?EINVAL;如果major大于最大的設(shè)備號,也返回?EINVAL。,包括設(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)運(yùn)行后面介紹的insmod命令插入模塊時,會調(diào)用init_module函數(shù)完成初始化工作。,中斷管理,設(shè)備驅(qū)動程序通過調(diào)用request_irq函數(shù)來申請中斷,通過free_irq來釋放中斷。它們在linux/sched.h中的定義如下:intrequest_irq(unsignedintirq,void(*handler)(intirq,voiddev_id,structpt_regs*regs),unsignedlongflags,constchar*device,void*dev_id);voidfree_irq(unsignedintirq,void*dev_id);通常從request_irq函數(shù)返回的值為0時,表示申請成功;負(fù)值表示出現(xiàn)錯誤。?irq表示所要申請的硬件中斷號。?handler為向系統(tǒng)登記的中斷處理子程序,中斷產(chǎn)生時由系統(tǒng)來調(diào)用,調(diào)用時所帶參數(shù)irq為中斷號,dev_id為申請時告訴系統(tǒng)的設(shè)備標(biāo)識,regs為中斷發(fā)生時寄存器內(nèi)容。?device為設(shè)備名,將會出現(xiàn)在/proc/interrupts文件里。?flag是申請時的選項,它決定中斷處理程序的一些特性,其中最重要的是決定中斷處理程序是快速處理程序(flag里設(shè)置了SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT)。下面的代碼將在SBC-2410X的Linux中注冊外部中斷2。eint_irq=IRQ_EINT2;set_external_irq(eint_irq,EXT_FALLING_EDGE,GPIO_PULLUP_DIS);ret_val=request_irq(eint_irq,eint2_handler,“S3C2410Xeint2”,0);if(ret_valGPIO_MODE_SHIFT)#defineGRAB_PULLUP(x)(((x)2.入口函數(shù)模塊的入口函數(shù)leds_init(),所做的工作是點(diǎn)亮I/O端口對應(yīng)的LED,這些二極管可以作為狀態(tài)指示之用。register_chrdev()完成字符設(shè)備在系統(tǒng)中的注冊,并建立與文件系統(tǒng)的并聯(lián)。staticint__initleds_init(void){intret;inti;/*在內(nèi)核中注冊設(shè)備*/ret=register_chrdev(LED_MAJOR,DEVICE_NAME,},3.字符設(shè)備定義staticdevfs_handle_tdevfs_handle;4.ioctlioctl入口,執(zhí)行讀、寫之外的操作,通過參數(shù)cmd設(shè)定命令參數(shù)。staticintleds_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg){switch(cmd){case0:case1:if(arg>4){return-EINVAL;}write_gpio_bit(led_table[arg],!cmd);default:return-EINVAL;}}5.文件系統(tǒng)接口定義staticstructfile_operationsleds_fops={owner:THIS_MODULE,ioctl:leds_ioctl,};6.模塊化module_init(leds_init);module_exit(leds_exit);用insmod命令加載模塊時,調(diào)用module_init();用rmmod命令來卸載模塊時,調(diào)用module_exit()函數(shù)。,加載運(yùn)行LED驅(qū)動程序,1.應(yīng)用程序設(shè)計#include#include#include#includeintmain(intargc,char**argv){inton;intled_no;intfd;if(argc!=3||sscanf(argv[1],"%d",}該程序首先讀取命令行的參數(shù)輸入,其中參數(shù)argv[1]賦值給led_no,表示發(fā)光二極管的序號;argv[2]賦值給on。led_no的取值范圍是1~3,on取值為0或1,0表示熄滅LED,1表示點(diǎn)亮LED。參數(shù)輸入后通過fd=open("/dev/leds",0)打開設(shè)備文件,在保證參數(shù)輸入正確和設(shè)備文件正確打開后,通過語句ioctl(fd,on,led_no)實(shí)現(xiàn)系統(tǒng)調(diào)用ioctl,并通過輸入的參數(shù)控制LED。在程序的最后關(guān)閉設(shè)備句柄。,嵌入式圖形用戶界面開發(fā)——Qt/Embedded,,GUI概述,目前的桌面機(jī)操作系統(tǒng)大多有著美觀、操作方便、功能齊全的GUI(圖形用戶界面),例如KDE或者GNOME。GUI(圖形用戶界面)是指計算機(jī)與其使用者之間的對話接口,可以說,GUI是當(dāng)今計算機(jī)技術(shù)的重大成就。它的存在為使用者提供了友好便利的界面,并大大地方便了非專業(yè)用戶的使用,使得人們從繁瑣的命令中解脫出來,可以通過窗口、菜單方便地進(jìn)行操作。UNIX環(huán)境下的圖形視窗標(biāo)準(zhǔn)為XWindowSystem,Linux是類UNIX系統(tǒng),所以頂層運(yùn)行的GUI系統(tǒng)是兼容X標(biāo)準(zhǔn)的XFree86系統(tǒng)。X標(biāo)準(zhǔn)大致可以劃分XServer、GraphicLibrary(底層繪圖函數(shù)庫)、Toolkits、WindowManager等幾大部分。其好處是具有可擴(kuò)展性、可移植性等優(yōu)點(diǎn),但對于嵌入式系統(tǒng)而言無疑太過龐大、累贅、低效。目前流行的嵌入式GUI與X思路不同,這些GUI一般不局限于X標(biāo)準(zhǔn),更強(qiáng)調(diào)系統(tǒng)的空間和效率。,嵌入式GUI的特點(diǎn),在嵌入式系統(tǒng)中,GUI的地位也越來越重要,但是不同于桌面機(jī)系統(tǒng),嵌入式GUI要求簡單、直觀、可靠、占用資源小且反應(yīng)快速,以適應(yīng)系統(tǒng)硬件資源有限的條件。另外,由于嵌入式系統(tǒng)硬件本身的特殊性,嵌入式GUI應(yīng)具備高度可移植性與可裁減性,以適應(yīng)不同的硬件條件和使用需求。?體積??;?運(yùn)行時耗用系統(tǒng)資源??;?上層接口與硬件無關(guān),高度可移植;?高可靠性;?在某些應(yīng)用場合應(yīng)具備實(shí)時性。,Qt/Embedded的特點(diǎn),MiniGUI(1),提起國內(nèi)的開源軟件,就肯定會提到MiniGUI,它由魏永明先生和眾多志愿者開發(fā),是一個基于Linux的實(shí)時嵌入式系統(tǒng)的輕量級圖形用戶界面支持系統(tǒng)。MiniGUI分為最底層的GAL層和IAL層,向上為基于標(biāo)準(zhǔn)POSIX接口中pthread庫的Mini-thread架構(gòu)和基于Server/Client的Mini-Lite架構(gòu)。其中前者受限于thread模式對于整個系統(tǒng)的可靠性——進(jìn)程中某個thread的意外錯誤可能導(dǎo)致整個進(jìn)程的崩潰,該架構(gòu)應(yīng)用于系統(tǒng)功能較為單一的場合。Mini-Lite應(yīng)用于多進(jìn)程的應(yīng)用場合,采用多進(jìn)程運(yùn)行方式設(shè)計的Server/Client架構(gòu)能夠較好地解決各個進(jìn)程之間的窗口管理、Z序剪切等問題。MiniGUI還有一種從Mini-Lite衍生出的standalone運(yùn)行模式。與Lite架構(gòu)不同的是,standalone模式一次只能以窗口最大化的方式顯示一個窗口。這在顯示屏尺寸較小的應(yīng)用場合具有一定的應(yīng)用意義。,MiniGUI(2),MiniGUI的IAL層技術(shù)SVGAlib、LibGGI、基于framebuffer的native圖形引擎以及啞圖形引擎等,對于Trolltech公司的QVFB在XWindow下也有較好的支持。IAL層則支持Linux標(biāo)準(zhǔn)控制臺下的GPM鼠標(biāo)服務(wù)、觸摸屏、標(biāo)準(zhǔn)鍵盤等。MiniGUI下豐富的控件資源也是MiniGUI的特點(diǎn)之一。MiniGUI免費(fèi)版本是1.3.3。在該版本的控件中已經(jīng)添加了窗口皮膚、工具條等桌面GUI中的高級控件支持。對比其他系統(tǒng),“Mini”是MiniGUI的特色,輕量、高性能和高效率的MiniGUI已經(jīng)應(yīng)用在電視機(jī)頂盒、實(shí)時控制系統(tǒng)、掌上電腦等諸多場合。,Microwindows,MicrowindowsOpenSourceProject成立的宗旨在于針對體積小的裝置,建立一套先進(jìn)的視窗環(huán)境,在Linux桌面上通過交叉編譯可以很容易地制作出Microwindows的程序。Microwindows能夠在沒有任何操作系統(tǒng)或其他圖形系統(tǒng)的支持下運(yùn)行,它能對裸顯示設(shè)備進(jìn)行直接操作。這樣,Microwindows就顯得十分小巧,便于移植到各種硬件和軟件系統(tǒng)上。然而Microwindows的免費(fèi)版本進(jìn)展一直很慢,幾乎處于停頓狀態(tài),而且至今為止,國內(nèi)沒有任何一家對Microwindows提供全面技術(shù)支持、服務(wù)和擔(dān)保的專業(yè)公司。,常用GUI比較,Qt/Embedded架構(gòu),Qt/Embedded以原始Qt為基礎(chǔ),并做了許多出色的調(diào)整以適用于嵌入式環(huán)境。Qt/Embedded通過QtAPI與LinuxI/O設(shè)施直接交互,成為嵌入式Linux端口。同Qt/X11相比,Qt/Embedded很省內(nèi)存,因?yàn)樗恍枰粋€X服務(wù)器或是Xlib庫,它在底層拋棄了Xlib,采用framebuffer作為底層圖形接口。同時,將外部輸入設(shè)備抽象為keyboard和mouse輸入事件。,,Qt/Embedded窗口系統(tǒng),一個Qt/Embedded窗口系統(tǒng)包含了一個或多個進(jìn)程,其中的一個進(jìn)程可作為服務(wù)器。該服務(wù)進(jìn)程會分配客戶顯示區(qū)域,以及產(chǎn)生鼠標(biāo)和鍵盤事件。該服務(wù)進(jìn)程還能夠提供輸入方法和一個用戶接口給運(yùn)行起來的客戶應(yīng)用程序。該服務(wù)進(jìn)程其實(shí)就是一個有某些額外權(quán)限的客戶進(jìn)程。客戶與服務(wù)器之間的通信使用共享內(nèi)存的方法實(shí)現(xiàn),通信量應(yīng)該保持最小。這就是Qt/Embedded庫內(nèi)部層次分明的處理過程。QProcess類提供了另外一種異步的進(jìn)程間通信機(jī)制。它用于啟動一個外部的程序并且通過寫一個標(biāo)準(zhǔn)的輸入和讀取外部程序的標(biāo)準(zhǔn)輸出和錯誤碼來和它們通信。,Qt/Embedded字體(1),Qt/Embedded支持四種不同的字體格式:TrueType字體(TTF),PostscriptType1字體,位圖發(fā)布字體(BDF)和Qt的預(yù)呈現(xiàn)(Pre-rendered)字體(QPF)。Qt還可以通過增加QFontFactory的子類來支持其他字體,也可以支持以插件方式出現(xiàn)的反別名字體。每個TTF或者TYPE1類型的字體首次在圖形或者文本方式的環(huán)境下被使用時,這些字體的字形都會以指定的大小被預(yù)先呈現(xiàn)出來,呈現(xiàn)的結(jié)果會被緩沖。根據(jù)給定的字體尺寸(例如10或12點(diǎn)陣)預(yù)先呈現(xiàn)TTF或者TYPE1類型的字體文件并把結(jié)果以QPF的格式保存起來,這樣可以節(jié)省內(nèi)存和CPU的處理時間。,Qt/Embedded字體(2),QPF文件包含了一些必要的字體,這些字體可以通過makeqpf工具取得,或者通過運(yùn)行程序時加上“-savefonts”選項獲取。如果應(yīng)用程序中使用到的字體都是QPF格式,那么Qt/Embedded將被重新配置,并排除對TTF和TYPE1類型的字體的編譯,這樣就可以減少Q(mào)t/Embedded的庫的大小和存儲字體的空間。例如一個10點(diǎn)陣大小的包含所有ASCII字符的QPF字體文件的大小為1300Byte,這個文件可以直接從物理存儲格式映射成為內(nèi)存存儲格式。Qt/Embedded的字體通常包括Unicode字體的一部分子集,ASCII和Latin-1。,Qt/Embedded輸入設(shè)備及輸入法,Qt/Embedded3.0支持幾種鼠標(biāo)協(xié)議:BusMouse、IntelliMouse,Microsoft和MouseMan.Qt/Embedded還支持NECVr41XX和iPAQ的觸摸屏。通過從QWSMouseHandler或者Qcalibra-tedMouseHandler派生子類,開發(fā)人員可以讓Qt/Embedded支持更多的客戶指示設(shè)備。對于非拉丁語系字符(例如阿拉伯,中文,希伯來和日語)的輸入法,需要把它寫成過濾器的方式,并改變鍵盤的輸入。輸入法的作者應(yīng)該對全部的QtAPI的使用有完整的認(rèn)識。在一個無鍵盤的設(shè)備上,輸入法成了惟一的輸入字符的手段。Qtopia提供了4種輸入方法:筆跡識別器、圖形化的標(biāo)準(zhǔn)鍵盤、Unicode鍵盤和基于字典方式提取的鍵盤。,Qt/Embedded屏幕加速,通過子類化QScreen和QgfxRaster可以實(shí)現(xiàn)硬件加速,從而為屏幕操作帶來好處。Troll-tech提供了Mach64和Voodoo3視頻卡的硬件加速的驅(qū)動例子,同時可以按照協(xié)議編寫其他的驅(qū)動程序。,Qt的支撐工具,Qt包含了許多支持嵌入式系統(tǒng)開發(fā)的工具,有兩個最實(shí)用的工具是qmake和Qtdesigner(圖形設(shè)計器)。?qmake是一個為編譯Qt/Embedded庫和應(yīng)用而提供的Makefile生成器。它能夠根據(jù)一個工程文件(.pro)產(chǎn)生不同平臺下的Makefile文件。qmake支持跨平臺開發(fā)和影子生成(影子生成是指當(dāng)工程的源代碼共享給網(wǎng)絡(luò)上的多臺機(jī)器時,每臺機(jī)器編譯鏈接這個工程的代碼將在不同的子路徑下完成,這樣就不會覆蓋別人的編譯鏈接生成的文件。qmake還易于在不同的配置之間切換。)?Qt圖形設(shè)計器可以使開發(fā)者可視化地設(shè)計對話框而不需編寫代碼。使用Qt圖形設(shè)計器的布局管理可以生成能平滑改變尺寸的對話框。qmake和Qt圖形設(shè)計器是完全集成在一起的。,Qt/Embedded信號和插槽機(jī)制(1),信號和插槽機(jī)制是Qt的核心機(jī)制,要精通Qt編程就必須對信號和插槽有所了解。信號和插槽是一種高級接口,應(yīng)用于對象之間的通信,它是Qt的核心特性,也是Qt區(qū)別于其他工具包的重要地方。信號和插槽是Qt自行定義的一種通信機(jī)制,它獨(dú)立于標(biāo)準(zhǔn)的C/C++語言,必須借助一個稱為moc(MetaObjectCompiler)的Qt工具,該工具是一個C++預(yù)處理程序,它為高層次的事件處理自動生成所需要的附加代碼。信號與插槽機(jī)制是一種強(qiáng)有力的對象間通信機(jī)制,完全可以取代原始的回調(diào)和消息映射機(jī)制。在Qt中信號和插槽取代了上述這些凌亂的函數(shù)指針,使得用戶編寫這些通信程序更為簡潔明了。信號和插槽能攜帶任意數(shù)量和任意類型的參數(shù),他們是類型完全安全的,因此不會像回調(diào)函數(shù)那樣產(chǎn)生coredumps。,Qt/Embedded信號和插槽機(jī)制(2),所有從QObject或其子類(例如Qwidget)派生的類都能夠包含信號和插槽。當(dāng)對象改變狀態(tài)時,信號就由該對象發(fā)射(emit)出去了,這就是對象所要做的全部工作,它不知道另一端是誰在接收這個信號。這就是真正的信息封裝,它確保對象被當(dāng)作一個真正的軟件組件來使用。插槽用于接收信號,但它們是普通的對象成員函數(shù)。一個插槽并不知道是否有任何信號與自己相連接。而且,對象并不了解具體的通信機(jī)制。用戶可以將很多信號與單個插槽進(jìn)行連接,也可以將單個信號與很多插槽進(jìn)行連接,甚至將一個信號與另外一個信號相連接也是可能的,這時無論第一個信號什么時候發(fā)射,系統(tǒng)都將立刻發(fā)射第二個信號??傊盘柵c插槽構(gòu)造了一個強(qiáng)大的部件編程機(jī)制。,Qt/Embedded信號和插槽機(jī)制(3),,信號,當(dāng)某個信號對其客戶或所有者內(nèi)部狀態(tài)發(fā)生改變時,信號就被一個對象發(fā)射。只有定義了這個信號的類及其派生類才能夠發(fā)射這個信號。當(dāng)一個信號被發(fā)射時,與其相關(guān)聯(lián)的插槽將被立刻執(zhí)行,就像一個正常的函數(shù)調(diào)用一樣。信號-插槽機(jī)制完全獨(dú)立于任何GUI事件循環(huán)。只有當(dāng)所有的槽返回以后發(fā)射函數(shù)(emit)才返回。如果存在多個槽與某個信號相關(guān)聯(lián),那么,當(dāng)這個信號被發(fā)射時,這些槽將會一個接一個地執(zhí)行,但是它們執(zhí)行的順序?qū)请S機(jī)的、不確定的,用戶不能人為地指定哪個先執(zhí)行、哪個后執(zhí)行。,信號示例:signals:voidmySignal();voidmySignal(intx);voidmySignalParam(intx,inty);,插槽示例:publicslots:voidmySlot();voidmySlot(intx);voidmySignalParam(intx,inty);,插槽,插槽是普通的C++成員函數(shù),可以被正常調(diào)用,它們惟一的特殊性就是很多信號可以與其相關(guān)聯(lián)。插槽可以有參數(shù),但插槽的參數(shù)不能有缺省值。插槽函數(shù)也分為3種類型,即publicslots、privateslots和protectedslots。?publicslots:在這個區(qū)內(nèi)聲明的槽意味著任何對象都可將信號與之相連接。這對于組件編程非常有用,用戶可以創(chuàng)建彼此互不了解的對象,將它們的信號與槽進(jìn)行連接以便信息能夠正確地傳遞。?protectedslots:在這個區(qū)內(nèi)聲明的槽意味著當(dāng)前類及其子類可以將信號與之相連接。這適用于那些槽,它們是類實(shí)現(xiàn)的一部分,但是其界面接口卻面向外部。?privateslots:在這個區(qū)內(nèi)聲明的槽意味著只有類自己可以將信號與之相連接。這適用于聯(lián)系非常緊密的類。,信號與插槽關(guān)聯(lián),通過調(diào)用QObject對象的connect()函數(shù)可以將某個對象的信號與另外一個對象的插槽函數(shù)或信號相關(guān)聯(lián),當(dāng)發(fā)射者發(fā)射信號時,接收者的槽函數(shù)或信號將被調(diào)用。這個函數(shù)的作用就是將發(fā)射者sender對象中的信號signal與接收者receiver中的member插槽函數(shù)聯(lián)系起來。當(dāng)指定信號signal時必須使用Qt的宏SIGNAL(),當(dāng)指定插槽函數(shù)時必須使用宏SLOT()。如果發(fā)射者與接收者屬于同一個對象的話,那么在connect()調(diào)用中接收者參數(shù)可以省略。,boolQObject::connect(constQObject*sender,constchar*signal,constQObject*receiver,constchar*member)[static],解除信號與插槽關(guān)聯(lián),當(dāng)信號與槽沒有必要繼續(xù)保持關(guān)聯(lián)時,用戶可以使用disconnect()函數(shù)來斷開連接。有3種情況必須使用disconnect()函數(shù)。?斷開與某個對象相關(guān)聯(lián)的任何對象?斷開與某個特定信號的任何關(guān)聯(lián)?斷開兩個對象之間的關(guān)聯(lián),boolQObject::disconnect(constQObject*sender,constchar*signal,constObject*receiver,constchar*member)[static],常見通用窗口組合(1),常見通用窗口組合(2),- 1.請仔細(xì)閱讀文檔,確保文檔完整性,對于不預(yù)覽、不比對內(nèi)容而直接下載帶來的問題本站不予受理。
- 2.下載的文檔,不會出現(xiàn)我們的網(wǎng)址水印。
- 3、該文檔所得收入(下載+內(nèi)容+預(yù)覽)歸上傳者、原創(chuàng)作者;如果您是本文檔原作者,請點(diǎn)此認(rèn)領(lǐng)!既往收益都?xì)w您。
下載文檔到電腦,查找使用更方便
9.9 積分
下載 |
- 配套講稿:
如PPT文件的首頁顯示word圖標(biāo),表示該P(yáng)PT已包含配套word講稿。雙擊word圖標(biāo)可打開word文檔。
- 特殊限制:
部分文檔作品中含有的國旗、國徽等圖片,僅作為作品整體效果示例展示,禁止商用。設(shè)計者僅對作品中獨(dú)創(chuàng)性部分享有著作權(quán)。
- 關(guān) 鍵 詞:
- 嵌入式 Linux 驅(qū)動程序 QT
鏈接地址:http://www.szxfmmzy.com/p-11531299.html