嵌入式Linux中I2C設(shè)備驅(qū)動(dòng)程序的研究與實(shí)現(xiàn)
摘要: I2C 作為一種目前通用的總線技術(shù),已廣泛應(yīng)用于EEPROM、實(shí)時(shí)鐘、小型LCD 等設(shè)備與CPU 的接口中。分析了嵌入式Linux 中I2C 驅(qū)動(dòng)程序的體系結(jié)構(gòu),I2C 驅(qū)動(dòng)程序中一些重要數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系以及I2C 驅(qū)動(dòng)程序的運(yùn)行機(jī)制。最后,結(jié)合具體的EEPROM 芯片AT24C08 說明了在嵌入式Linux 下開發(fā)I2C設(shè)備驅(qū)動(dòng)程序的一般流程。另外還創(chuàng)新了一般的驅(qū)動(dòng)方法,實(shí)現(xiàn)了單設(shè)備多驅(qū)動(dòng)的驅(qū)動(dòng)模式。
本文引用地址:http://cafeforensic.com/article/149709.htm引言
I2C是“Inter Integrated Circuit Bus”的縮寫,中文譯成“內(nèi)部集成電路總線”, 它是Philips 公司于20 世紀(jì)80 年代研發(fā)成功的一種具有多端控制功能的雙線雙向串行數(shù)據(jù)總線標(biāo)準(zhǔn), 其具有模塊化、電路結(jié)構(gòu)簡(jiǎn)單等優(yōu)點(diǎn)。在嵌入式系統(tǒng)中,I2C總線已經(jīng)成為器件接口的標(biāo)準(zhǔn)之一, 常用于連接RAM、EEPROM 以及LCD 控制器等設(shè)備。另外,總線的數(shù)據(jù)傳輸是以字節(jié)為單位的。
目前,標(biāo)準(zhǔn)的I2C的傳輸速率可以達(dá)到100kbit/s,能支持128 個(gè)設(shè)備,增強(qiáng)型I2C傳輸速率可達(dá)400kbit/s,能支持多達(dá)1024 個(gè)設(shè)備,高速模式下的I2C 傳輸速率更高達(dá)3.4Mbit/s。
1 Linux 驅(qū)動(dòng)程序
驅(qū)動(dòng)程序是指系統(tǒng)內(nèi)核與系統(tǒng)硬件之間的接口。Linux 中的每一個(gè)外圍物理設(shè)備等都有一個(gè)專門用于控制該設(shè)備的設(shè)備驅(qū)動(dòng)程序。設(shè)備驅(qū)動(dòng)可以完成初始化、釋放以及檢測(cè)硬件設(shè)備;差錯(cuò)和故障處理;負(fù)責(zé)內(nèi)核與硬件、應(yīng)用程序與硬件之間的數(shù)據(jù)傳輸與通信的一些重要工作。在嵌入式系統(tǒng)中,設(shè)備驅(qū)動(dòng)為嵌入式操作系統(tǒng)和應(yīng)用程序訪問硬件設(shè)備提供統(tǒng)一的接口。通過它, 操作系統(tǒng)和應(yīng)用程序就可以輕松地操作和驅(qū)動(dòng)硬件架構(gòu)的分層。
2 Linux 的I2C 體系結(jié)構(gòu)
2.1 Linux 下I2C 體系結(jié)構(gòu)分析
Linux 的I2C 體系結(jié)構(gòu)由3 大部分組成:
(1)I2C框架:I2C.h 和I2C-core.c 為I2C框架的主體,提供了核心數(shù)據(jù)結(jié)構(gòu)的定義、I2C 適配器驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)的注冊(cè)、注銷方法,I2C 通信方法(algorithm)上層的、與具體適配器無關(guān)的代碼、以及檢測(cè)設(shè)備地址的上層代碼等。作為核心的I2C-core.c 還為總線驅(qū)動(dòng)設(shè)備提供了一些統(tǒng)一的調(diào)用接口進(jìn)行讀寫和設(shè)置操作, 另外它還提供了將各種支持的總線設(shè)備驅(qū)動(dòng)添加到這個(gè)體系中的方法, 以及當(dāng)不再使用這些總線驅(qū)動(dòng)時(shí)從體系中刪除的方法。
(2)I2C 總線驅(qū)動(dòng)I2C總線驅(qū)動(dòng)是對(duì)I2C 硬件體系結(jié)構(gòu)中適配器端的實(shí)現(xiàn),I2C 總線驅(qū)動(dòng)主要包含了I2C 適配器數(shù)據(jù)結(jié)構(gòu)I2C_adapter, 以及描述在具體I2C 適配器上的總線通信方法i2c_algorithm 數(shù)據(jù)結(jié)構(gòu)。
(3)I2C 設(shè)備驅(qū)動(dòng):I2C 設(shè)備驅(qū)動(dòng)是對(duì)I2C 硬件體系結(jié)構(gòu)中設(shè)備端的實(shí)現(xiàn), 設(shè)備一般掛接在受CPU 控制的I2C 適配器上, 通過I2C 適配器與CPU 交換數(shù)據(jù)。I2C 設(shè)備驅(qū)動(dòng)主要包含了數(shù)據(jù)結(jié)構(gòu)i2c_driver 和i2c_client。
這三部分的關(guān)系如圖1 所示。
圖1Linux 中I2C 體系結(jié)構(gòu)
2.2 I2C驅(qū)動(dòng)程序中的重要數(shù)據(jù)結(jié)構(gòu)
在I2C 框架的i2c.h 這個(gè)頭文件中對(duì)4 個(gè)關(guān)鍵的結(jié)構(gòu)體進(jìn)行了定義, 它們分別是i2c_adapter、i2c_algorithm、i2c_driver 和i2c_client。結(jié)構(gòu)體i2c_adapter 是一個(gè)I2C控制器的邏輯抽象,并且作為最核心的數(shù)據(jù)結(jié)構(gòu)提供了I2C適配器的驅(qū)動(dòng)。i2c_algorithm對(duì)應(yīng)一套通信方法, 其封裝了對(duì)一個(gè)I2C 控制器的讀寫操作, 并且提供的通信函數(shù)可以控制適配器上產(chǎn)生特定的訪問周期,這套通信方法由驅(qū)動(dòng)開發(fā)者來完成。i2c_driver 則是對(duì)應(yīng)于一套驅(qū)動(dòng)方法,用于輔助作用的數(shù)據(jù)結(jié)構(gòu),不對(duì)應(yīng)任何物理實(shí)體,僅是提供了I2C 設(shè)備i2c_client 的驅(qū)動(dòng)。而i2c_client 對(duì)應(yīng)于真實(shí)的物理設(shè)備,描述具體設(shè)備可能的私有數(shù)據(jù)結(jié)構(gòu)。
2.3I2C驅(qū)動(dòng)程序中重要數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系
對(duì)于上述的4 個(gè)結(jié)構(gòu)體來說, 其中的i2c_driver 和i2c_client 是與具體I2C 設(shè)備相關(guān)的,而i2c_adapter 和i2c_algorithm則共同構(gòu)成I2C 總線適配器驅(qū)動(dòng)。一個(gè)algorithm 可以適用于多個(gè)I2C 總線上的不同adapters, 但具體的每個(gè)adapter 只能對(duì)應(yīng)于一個(gè)algorithm。在i2c_adapter 數(shù)據(jù)結(jié)構(gòu)中設(shè)計(jì)了clients指針數(shù)組, 用于記錄該總線上每個(gè)設(shè)備的i2c_client 數(shù)據(jù)結(jié)構(gòu)。
另外, 定義內(nèi)核中全局靜態(tài)指針數(shù)組adapters 和drivers 分別記錄已注冊(cè)的I2C 適配器驅(qū)動(dòng)和I2C 設(shè)備驅(qū)動(dòng)程序。值得注意的是同一個(gè)i2c_adapter 中的不同的i2c_client 可能使用同一個(gè)i2c_driver,而分屬于不同i2c_adapter 中的兩個(gè)i2c_client 也可能使用同一個(gè)i2c_driver。
3 一個(gè)具體的I2C 設(shè)備驅(qū)動(dòng)程序的開發(fā)
AT24C08 是由ATMEL 公司出品的一款EEPROM 存儲(chǔ)器。
作為一個(gè)標(biāo)準(zhǔn)的I2C 設(shè)備AT24C08 有4 個(gè)塊存儲(chǔ)區(qū), 一個(gè)塊有256 個(gè)數(shù)據(jù)存儲(chǔ)單元,整個(gè)AT24C08 具有1024 個(gè)存儲(chǔ)單元。由于每個(gè)數(shù)據(jù)存儲(chǔ)單元可存1 字節(jié)的數(shù)據(jù),所以整塊AT24C08 的存儲(chǔ)能力為1KB。
3.1 I2C 設(shè)備驅(qū)動(dòng)程序的一般結(jié)構(gòu)及運(yùn)行流程圖
開發(fā)一個(gè)具體的I2C 設(shè)備驅(qū)動(dòng)需要一個(gè)完整、標(biāo)準(zhǔn)的結(jié)構(gòu),而該結(jié)構(gòu)的實(shí)現(xiàn)是通過編寫兩個(gè)方面的接口而完成的, 一個(gè)是用以掛接I2C adapter 層來實(shí)現(xiàn)對(duì)I2C 總線及I2C設(shè)備具體的訪問方法, 即I2C 核心層的接口, 主要實(shí)現(xiàn)attach_adapter,detach_client,command 等接口函數(shù)。另一個(gè)是對(duì)用戶應(yīng)用層的接口, 提供用戶程序訪問I2C設(shè)備的接口, 包括實(shí)現(xiàn)open,release,read,write 以及ioctl 等標(biāo)準(zhǔn)文件操作的接口函數(shù)。下面將通過對(duì)核心層接口和應(yīng)用層接口的分析來說明I2C 設(shè)備驅(qū)動(dòng)程序的運(yùn)行機(jī)制。圖2 為I2C 設(shè)備驅(qū)動(dòng)程序運(yùn)行流程圖(圖中at 代表具體的設(shè)備AT24C08):
3.2 I2C 設(shè)備驅(qū)動(dòng)的I2C 核心層接口分析
如圖2 的用戶空間在通過insmod 命令加載設(shè)備驅(qū)動(dòng)程序時(shí), 設(shè)備驅(qū)動(dòng)將通過使用動(dòng)態(tài)模塊的方式加載并指向設(shè)備初始化函數(shù)at_init(),在初始化函數(shù)中使用regiSTer_chrdev()進(jìn)行字符型設(shè)備的注冊(cè), 并可以通過靜態(tài)和動(dòng)態(tài)兩種方法來申請(qǐng)注冊(cè)到系統(tǒng)中的設(shè)備號(hào)。另外將調(diào)用核心i2c -core.c 中提供的i2c_add_driver()函數(shù)注冊(cè)由at_driver 數(shù)據(jù)結(jié)構(gòu)描述的驅(qū)動(dòng)方法,該數(shù)據(jù)結(jié)構(gòu)中完成了對(duì)驅(qū)動(dòng)程序的標(biāo)示, 并包含了兩個(gè)重要的成員函數(shù)at_attach_adapter()和at_detach_client()。
在i2c_add_driver () 注冊(cè)at_driver 數(shù)據(jù)結(jié)構(gòu)后,at_attach_adapter()函數(shù)就會(huì)被自動(dòng)調(diào)用,其遍歷系統(tǒng)中的每個(gè)i2c 總線驅(qū)動(dòng), 探測(cè)想要訪問的設(shè)備, 連接符合i2c driver 特定條件的i2c adapter,并通過i2c adapter 實(shí)現(xiàn)對(duì)I2C 總線及其設(shè)備的訪問。
而at_attach_adapter()的功能則是依靠調(diào)用i2c-core.c 核心中的i2c_probe()函數(shù)來實(shí)現(xiàn)的,通過i2c_probe()函數(shù)可以認(rèn)領(lǐng)adapter所指向的適配器上的所有合適的設(shè)備。設(shè)備可能使用的地址由addr_data 數(shù)組指出。通過設(shè)備地址每次檢測(cè)到新設(shè)備后,i2c_probe()將使用它的第三個(gè)參數(shù)即回調(diào)函數(shù)初始化設(shè)備的數(shù)據(jù)結(jié)構(gòu)i2c_client,并用i2c_check_functiONality()確定I2C 適配器所支持的通信方法。另外再使用i2c_attach_client()知會(huì)I2C 核心系統(tǒng)中包含了一個(gè)新的I2C 設(shè)備。
通過rmmod 命令對(duì)設(shè)備驅(qū)動(dòng)進(jìn)行卸載時(shí), 在卸載函數(shù)at_exit()中將使用i2c_del_driver(),其調(diào)用會(huì)引起與數(shù)據(jù)結(jié)構(gòu)at_driver 關(guān)聯(lián)的每個(gè)i2c_client 與之解除關(guān)聯(lián), 隨后at_detach_client()函數(shù)也將因此而被調(diào)用,而at_detach_client()中的i2c_detach_client()又完成與i2c_attach_client()相反的過程,并使用kfree 釋放由client 所占的內(nèi)存。另外卸載函數(shù)at_exit()中還將使用unregister_chrdev()對(duì)字符型設(shè)備進(jìn)行注銷。
3.3I2C設(shè)備驅(qū)動(dòng)用戶應(yīng)用層接口分析
在注冊(cè)字符型設(shè)備時(shí), 設(shè)備驅(qū)動(dòng)中初始化了一個(gè)structfile_operations 文件操作結(jié)構(gòu)體變量用于鏈接字符設(shè)備驅(qū)動(dòng)程序和用戶應(yīng)用程序,在該結(jié)構(gòu)中定義了一組函數(shù)指針。系統(tǒng)就是通過這組函數(shù)指針對(duì)AT24C08 進(jìn)行具體的操作,系統(tǒng)首先通過設(shè)備文件的主設(shè)備號(hào)找到相應(yīng)的設(shè)備驅(qū)動(dòng)程序, 然后讀取這個(gè)數(shù)據(jù)結(jié)構(gòu)相應(yīng)的函數(shù)指針,找到相關(guān)的功能函數(shù),接著把控制權(quán)交給該函數(shù),從而就在上層屏蔽了設(shè)備驅(qū)動(dòng)的具體實(shí)現(xiàn)細(xì)節(jié),提供給用戶一個(gè)方便快捷的接口。該結(jié)構(gòu)中的at_open(),對(duì)應(yīng)于用戶應(yīng)用層的open()接口函數(shù),其通過mknod 創(chuàng)建的設(shè)備節(jié)點(diǎn)對(duì)設(shè)備文件進(jìn)行打開操作。而對(duì)應(yīng)用戶層release () 接口函數(shù)的at_release () 則負(fù)責(zé)設(shè)備文件的釋放操作。file_operations 中的at_ioctl()則主要是為用戶提供一些控制該AT24C08 的命令。對(duì)一塊具體設(shè)備進(jìn)行讀寫操作是編寫驅(qū)動(dòng)要達(dá)到目的,file_operations結(jié)構(gòu)體中所指向的讀寫函數(shù)at_read(),at_write()完成了對(duì)AT24C08 的寫入和讀出操作。
就寫函數(shù)而言, 在寫數(shù)據(jù)之前必須先輸入測(cè)試單元的起始地址, 然后再對(duì)寫入的數(shù)據(jù)分配相應(yīng)內(nèi)存, 然后使用copy_from_user 命令把從用戶空間獲得的數(shù)據(jù)拷貝到內(nèi)核空間,并構(gòu)造I2C 消息數(shù)據(jù),最終通過i2c-core.c 的i2c-transfer()函數(shù)進(jìn)行I2C消息數(shù)組的傳輸,而i2c_transfer()將指向總線驅(qū)動(dòng)中的算法i2c_algorithm 所對(duì)應(yīng)的具體適配器的master_xfer()方法,這樣就借助i2c-core.c 作為紐帶連接了設(shè)備驅(qū)動(dòng)和總線驅(qū)動(dòng),并完成了兩者之間的通信,其運(yùn)行流程如圖2 的內(nèi)核空間所示。
對(duì)于讀函數(shù)at_read(),同樣要對(duì)數(shù)據(jù)進(jìn)行內(nèi)存的分配,構(gòu)造I2C消息,傳輸I2C 消息以及轉(zhuǎn)換數(shù)據(jù)空間等。兩者的主要區(qū)別則體現(xiàn)在對(duì)I2C 消息的構(gòu)造上,在讀出數(shù)據(jù)之前,先要寫地址,根據(jù)寫入的地址來尋找將要讀出的數(shù)據(jù)的起始地址, 所以在讀函數(shù)中就需要構(gòu)造兩條I2C 消息,一條用于寫地址操作,另一條用于讀數(shù)據(jù)操作。另外在轉(zhuǎn)換數(shù)據(jù)空間時(shí), 讀函數(shù)將使用copy_to_user 把內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間。
3.4 AT24C08 的單設(shè)備多驅(qū)動(dòng)的實(shí)現(xiàn)方式
單設(shè)備多驅(qū)動(dòng)是本文的一個(gè)創(chuàng)新點(diǎn)。設(shè)計(jì)中實(shí)現(xiàn)了分3 個(gè)設(shè)備驅(qū)動(dòng)一對(duì)1 塊AT24C08 進(jìn)行操作。設(shè)備驅(qū)動(dòng)1 對(duì)AT24C08的第1 個(gè)塊操作,設(shè)備驅(qū)動(dòng)2 對(duì)第2 個(gè)塊操作,設(shè)備驅(qū)動(dòng)3 對(duì)第3 和第4 個(gè)塊進(jìn)行操作。對(duì)塊的分開操作體現(xiàn)在對(duì)設(shè)備地址的探測(cè)上,由于保存設(shè)備地址信息的是二元數(shù)組addr_data,所以在多驅(qū)動(dòng)對(duì)單一的AT24C08 操作時(shí)就需要在該二元數(shù)組中指明每個(gè)設(shè)備驅(qū)動(dòng)程序所控制的設(shè)備地址。對(duì)于控制第1 個(gè)塊的設(shè)備驅(qū)動(dòng)1,通過數(shù)組normal_addr 指出要進(jìn)行操作的設(shè)備地址為0x50,如下所示:
static unsigned short normal_addr[]={ 0x50,I2C_CLIENT_END};
再通過其對(duì)數(shù)組addr_data 進(jìn)行初始化, 這樣, 設(shè)備驅(qū)動(dòng)1就能檢測(cè)到數(shù)組中所指出的AT24C08 的第1 個(gè)塊,而跳過其他的塊, 達(dá)到了只對(duì)單一特定塊操作的目的。對(duì)于設(shè)備驅(qū)動(dòng)2 來說, 只需把數(shù)組normal_addr 中地址改為AT24C08 的第2 個(gè)塊的地址0x51 即可。同理,對(duì)設(shè)備驅(qū)動(dòng)3,只需把normal_addr 中的單一地址改為兩個(gè)地址即可,如下所示:
static unsigned short normal_addr [] = { 0x52,0x53, I2C_CLIENT_END};
這樣就可使設(shè)備驅(qū)動(dòng)只探測(cè)到后兩個(gè)塊,而跳過其他塊,以達(dá)到對(duì)單一AT24C08 中多個(gè)塊操作的目的。然后再用insmod命令加載編譯好的三個(gè).ko 驅(qū)動(dòng)模塊, 獲得3 個(gè)不同的設(shè)備號(hào)后,接著根據(jù)所獲得的設(shè)備號(hào)使用mknod 命令創(chuàng)建3 個(gè)不同的字符型設(shè)備節(jié)點(diǎn), 最后通過用戶層的3 個(gè)測(cè)試程序分別打開已創(chuàng)建的這3 個(gè)不同的設(shè)備節(jié)點(diǎn)就能分別對(duì)不同的塊進(jìn)行讀寫操作,至此就實(shí)現(xiàn)了單設(shè)備多驅(qū)動(dòng)的控制方式。
同樣除了分3 個(gè)驅(qū)動(dòng)外, 驅(qū)動(dòng)開發(fā)者也可以編寫4 個(gè)設(shè)備驅(qū)動(dòng)分別對(duì)每1 個(gè)塊進(jìn)行操作, 或者就只編寫1 個(gè)設(shè)備驅(qū)動(dòng)對(duì)4 個(gè)塊一起操作,也適用于綁定非連續(xù)塊進(jìn)行操作,比如用一個(gè)設(shè)備驅(qū)動(dòng)控制第1 和第3 個(gè)塊??傊?qū)動(dòng)開發(fā)人員可以根據(jù)不同的需要進(jìn)行不同的組合方式。
3.5 AT24C08 設(shè)備驅(qū)動(dòng)程序的驗(yàn)證與測(cè)試
設(shè)備驅(qū)動(dòng)程序的驗(yàn)證, 需要通過用戶層的測(cè)試程序來實(shí)現(xiàn),測(cè)試程序如下:
fd=open(/dev/at, O_RDWR); //打開設(shè)備文件,獲得設(shè)備文件的文件描述符。
scanf(%u, start_address); //輸入測(cè)試單元起始地址。
write(fd,buf,sizeof(buf)); //把以頁(yè)寫入方式把輸入的16 個(gè)數(shù)據(jù)寫入內(nèi)核空間。
4 結(jié)束語
作為當(dāng)前最流行的總線技術(shù)之一,I2C 總線具有結(jié)構(gòu)小巧,使用簡(jiǎn)單高效的特點(diǎn),目前在各種設(shè)計(jì)中已得到廣泛的應(yīng)用。本文分析了Linux 下I2C 的體系結(jié)構(gòu),較為詳盡的說明了I2C驅(qū)動(dòng)程序中的一些重要數(shù)據(jù)結(jié)構(gòu)以及這些數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系,并論述了I2C 驅(qū)動(dòng)程序體系的運(yùn)行機(jī)制, 最后以一個(gè)EEPROM 芯片AT24C08 為例,詳細(xì)的給出了一個(gè)具體設(shè)備驅(qū)動(dòng)的基本開發(fā)過程, 并說明了設(shè)備驅(qū)動(dòng)中的兩個(gè)重要接口,I2C 核心層接口和用戶應(yīng)用層接口。更重要的是本文還提供了一種單設(shè)備多驅(qū)動(dòng)的實(shí)現(xiàn)方式,這將帶給驅(qū)動(dòng)開發(fā)人員一定的啟示。另外,本文還進(jìn)行了設(shè)備驅(qū)動(dòng)程序的測(cè)試與驗(yàn)證工作, 保證了設(shè)備驅(qū)動(dòng)程序編寫的正確性。本文的設(shè)備驅(qū)動(dòng)設(shè)計(jì)方法也將對(duì)其他相關(guān)I2C設(shè)備驅(qū)動(dòng)的設(shè)計(jì)提供良好的借鑒作用。
本文作者創(chuàng)新點(diǎn):實(shí)現(xiàn)單設(shè)備多驅(qū)動(dòng)的驅(qū)動(dòng)模式。
linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)
評(píng)論