新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 嵌入式Linux中I2C設(shè)備驅(qū)動程序的研究與實(shí)現(xiàn)

嵌入式Linux中I2C設(shè)備驅(qū)動程序的研究與實(shí)現(xiàn)

作者: 時間:2012-02-13 來源:網(wǎng)絡(luò) 收藏

摘要: 作為一種目前通用的總線技術(shù),已廣泛應(yīng)用于EEPROM、實(shí)時鐘、小型LCD 等與CPU 的接口中。分析了 的體系結(jié)構(gòu), 中一些重要數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系以及I2C 的運(yùn)行機(jī)制。最后,結(jié)合具體的EEPROM 芯片AT24C08 說明了在 下開發(fā)I2C驅(qū)動程序的一般流程。另外還創(chuàng)新了一般的驅(qū)動方法,了單多驅(qū)動的驅(qū)動模式。

本文引用地址:http://2s4d.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)簡單等優(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 個設(shè)備,增強(qiáng)型I2C傳輸速率可達(dá)400kbit/s,能支持多達(dá)1024 個設(shè)備,高速模式下的I2C 傳輸速率更高達(dá)3.4Mbit/s。

1 驅(qū)動程序

驅(qū)動程序是指系統(tǒng)內(nèi)核與系統(tǒng)硬件之間的接口。Linux 中的每一個外圍物理設(shè)備等都有一個專門用于控制該設(shè)備的設(shè)備驅(qū)動程序。設(shè)備驅(qū)動可以完成初始化、釋放以及檢測硬件設(shè)備;差錯和故障處理;負(fù)責(zé)內(nèi)核與硬件、應(yīng)用程序與硬件之間的數(shù)據(jù)傳輸與通信的一些重要工作。在嵌入式系統(tǒng)中,設(shè)備驅(qū)動為嵌入式操作系統(tǒng)和應(yīng)用程序訪問硬件設(shè)備提供統(tǒng)一的接口。通過它, 操作系統(tǒng)和應(yīng)用程序就可以輕松地操作和驅(qū)動硬件架構(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ū)動和設(shè)備驅(qū)動的注冊、注銷方法,I2C 通信方法(algorithm)上層的、與具體適配器無關(guān)的代碼、以及檢測設(shè)備地址的上層代碼等。作為核心的I2C-core.c 還為總線驅(qū)動設(shè)備提供了一些統(tǒng)一的調(diào)用接口進(jìn)行讀寫和設(shè)置操作, 另外它還提供了將各種支持的總線設(shè)備驅(qū)動添加到這個體系中的方法, 以及當(dāng)不再使用這些總線驅(qū)動時從體系中刪除的方法。

(2)I2C 總線驅(qū)動I2C總線驅(qū)動是對I2C 硬件體系結(jié)構(gòu)中適配器端的,I2C 總線驅(qū)動主要包含了I2C 適配器數(shù)據(jù)結(jié)構(gòu)I2C_adapter, 以及描述在具體I2C 適配器上的總線通信方法i2c_algorithm 數(shù)據(jù)結(jié)構(gòu)。

(3)I2C 設(shè)備驅(qū)動:I2C 設(shè)備驅(qū)動是對I2C 硬件體系結(jié)構(gòu)中設(shè)備端的, 設(shè)備一般掛接在受CPU 控制的I2C 適配器上, 通過I2C 適配器與CPU 交換數(shù)據(jù)。I2C 設(shè)備驅(qū)動主要包含了數(shù)據(jù)結(jié)構(gòu)i2c_driver 和i2c_client。

這三部分的關(guān)系如圖1 所示。

圖1Linux 中I2C 體系結(jié)構(gòu)

2.2 I2C驅(qū)動程序中的重要數(shù)據(jù)結(jié)構(gòu)

在I2C 框架的i2c.h 這個頭文件中對4 個關(guān)鍵的結(jié)構(gòu)體進(jìn)行了定義, 它們分別是i2c_adapter、i2c_algorithm、i2c_driver 和i2c_client。結(jié)構(gòu)體i2c_adapter 是一個I2C控制器的邏輯抽象,并且作為最核心的數(shù)據(jù)結(jié)構(gòu)提供了I2C適配器的驅(qū)動。i2c_algorithm對應(yīng)一套通信方法, 其封裝了對一個I2C 控制器的讀寫操作, 并且提供的通信函數(shù)可以控制適配器上產(chǎn)生特定的訪問周期,這套通信方法由驅(qū)動開發(fā)者來完成。i2c_driver 則是對應(yīng)于一套驅(qū)動方法,用于輔助作用的數(shù)據(jù)結(jié)構(gòu),不對應(yīng)任何物理實(shí)體,僅是提供了I2C 設(shè)備i2c_client 的驅(qū)動。而i2c_client 對應(yīng)于真實(shí)的物理設(shè)備,描述具體設(shè)備可能的私有數(shù)據(jù)結(jié)構(gòu)。

2.3I2C驅(qū)動程序中重要數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系

對于上述的4 個結(jié)構(gòu)體來說, 其中的i2c_driver 和i2c_client 是與具體I2C 設(shè)備相關(guān)的,而i2c_adapter 和i2c_algorithm則共同構(gòu)成I2C 總線適配器驅(qū)動。一個algorithm 可以適用于多個I2C 總線上的不同adapters, 但具體的每個adapter 只能對應(yīng)于一個algorithm。在i2c_adapter 數(shù)據(jù)結(jié)構(gòu)中設(shè)計了clients指針數(shù)組, 用于記錄該總線上每個設(shè)備的i2c_client 數(shù)據(jù)結(jié)構(gòu)。

另外, 定義內(nèi)核中全局靜態(tài)指針數(shù)組adapters 和drivers 分別記錄已注冊的I2C 適配器驅(qū)動和I2C 設(shè)備驅(qū)動程序。值得注意的是同一個i2c_adapter 中的不同的i2c_client 可能使用同一個i2c_driver,而分屬于不同i2c_adapter 中的兩個i2c_client 也可能使用同一個i2c_driver。

3 一個具體的I2C 設(shè)備驅(qū)動程序的開發(fā)

AT24C08 是由ATMEL 公司出品的一款EEPROM 存儲器。

作為一個標(biāo)準(zhǔn)的I2C 設(shè)備AT24C08 有4 個塊存儲區(qū), 一個塊有256 個數(shù)據(jù)存儲單元,整個AT24C08 具有1024 個存儲單元。由于每個數(shù)據(jù)存儲單元可存1 字節(jié)的數(shù)據(jù),所以整塊AT24C08 的存儲能力為1KB。

3.1 I2C 設(shè)備驅(qū)動程序的一般結(jié)構(gòu)及運(yùn)行流程圖

開發(fā)一個具體的I2C 設(shè)備驅(qū)動需要一個完整、標(biāo)準(zhǔn)的結(jié)構(gòu),而該結(jié)構(gòu)的實(shí)現(xiàn)是通過編寫兩個方面的接口而完成的, 一個是用以掛接I2C adapter 層來實(shí)現(xiàn)對I2C 總線及I2C設(shè)備具體的訪問方法, 即I2C 核心層的接口, 主要實(shí)現(xiàn)attach_adapter,detach_client,command 等接口函數(shù)。另一個是對用戶應(yīng)用層的接口, 提供用戶程序訪問I2C設(shè)備的接口, 包括實(shí)現(xiàn)open,release,read,write 以及ioctl 等標(biāo)準(zhǔn)文件操作的接口函數(shù)。下面將通過對核心層接口和應(yīng)用層接口的分析來說明I2C 設(shè)備驅(qū)動程序的運(yùn)行機(jī)制。圖2 為I2C 設(shè)備驅(qū)動程序運(yùn)行流程圖(圖中at 代表具體的設(shè)備AT24C08):

3.2 I2C 設(shè)備驅(qū)動的I2C 核心層接口分析

如圖2 的用戶空間在通過insmod 命令加載設(shè)備驅(qū)動程序時, 設(shè)備驅(qū)動將通過使用動態(tài)模塊的方式加載并指向設(shè)備初始化函數(shù)at_init(),在初始化函數(shù)中使用regiSTer_chrdev()進(jìn)行字符型設(shè)備的注冊, 并可以通過靜態(tài)和動態(tài)兩種方法來申請注冊到系統(tǒng)中的設(shè)備號。另外將調(diào)用核心i2c -core.c 中提供的i2c_add_driver()函數(shù)注冊由at_driver 數(shù)據(jù)結(jié)構(gòu)描述的驅(qū)動方法,該數(shù)據(jù)結(jié)構(gòu)中完成了對驅(qū)動程序的標(biāo)示, 并包含了兩個重要的成員函數(shù)at_attach_adapter()和at_detach_client()。

在i2c_add_driver () 注冊at_driver 數(shù)據(jù)結(jié)構(gòu)后,at_attach_adapter()函數(shù)就會被自動調(diào)用,其遍歷系統(tǒng)中的每個i2c 總線驅(qū)動, 探測想要訪問的設(shè)備, 連接符合i2c driver 特定條件的i2c adapter,并通過i2c adapter 實(shí)現(xiàn)對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è)備地址每次檢測到新設(shè)備后,i2c_probe()將使用它的第三個參數(shù)即回調(diào)函數(shù)初始化設(shè)備的數(shù)據(jù)結(jié)構(gòu)i2c_client,并用i2c_check_functiONality()確定I2C 適配器所支持的通信方法。另外再使用i2c_attach_client()知會I2C 核心系統(tǒng)中包含了一個新的I2C 設(shè)備。

通過rmmod 命令對設(shè)備驅(qū)動進(jìn)行卸載時, 在卸載函數(shù)at_exit()中將使用i2c_del_driver(),其調(diào)用會引起與數(shù)據(jù)結(jié)構(gòu)at_driver 關(guān)聯(lián)的每個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()對字符型設(shè)備進(jìn)行注銷。

3.3I2C設(shè)備驅(qū)動用戶應(yīng)用層接口分析

在注冊字符型設(shè)備時, 設(shè)備驅(qū)動中初始化了一個structfile_operations 文件操作結(jié)構(gòu)體變量用于鏈接字符設(shè)備驅(qū)動程序和用戶應(yīng)用程序,在該結(jié)構(gòu)中定義了一組函數(shù)指針。系統(tǒng)就是通過這組函數(shù)指針對AT24C08 進(jìn)行具體的操作,系統(tǒng)首先通過設(shè)備文件的主設(shè)備號找到相應(yīng)的設(shè)備驅(qū)動程序, 然后讀取這個數(shù)據(jù)結(jié)構(gòu)相應(yīng)的函數(shù)指針,找到相關(guān)的功能函數(shù),接著把控制權(quán)交給該函數(shù),從而就在上層屏蔽了設(shè)備驅(qū)動的具體實(shí)現(xiàn)細(xì)節(jié),提供給用戶一個方便快捷的接口。該結(jié)構(gòu)中的at_open(),對應(yīng)于用戶應(yīng)用層的open()接口函數(shù),其通過mknod 創(chuàng)建的設(shè)備節(jié)點(diǎn)對設(shè)備文件進(jìn)行打開操作。而對應(yīng)用戶層release () 接口函數(shù)的at_release () 則負(fù)責(zé)設(shè)備文件的釋放操作。file_operations 中的at_ioctl()則主要是為用戶提供一些控制該AT24C08 的命令。對一塊具體設(shè)備進(jìn)行讀寫操作是編寫驅(qū)動要達(dá)到目的,file_operations結(jié)構(gòu)體中所指向的讀寫函數(shù)at_read(),at_write()完成了對AT24C08 的寫入和讀出操作。

就寫函數(shù)而言, 在寫數(shù)據(jù)之前必須先輸入測試單元的起始地址, 然后再對寫入的數(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ū)動中的算法i2c_algorithm 所對應(yīng)的具體適配器的master_xfer()方法,這樣就借助i2c-core.c 作為紐帶連接了設(shè)備驅(qū)動和總線驅(qū)動,并完成了兩者之間的通信,其運(yùn)行流程如圖2 的內(nèi)核空間所示。

對于讀函數(shù)at_read(),同樣要對數(shù)據(jù)進(jìn)行內(nèi)存的分配,構(gòu)造I2C消息,傳輸I2C 消息以及轉(zhuǎn)換數(shù)據(jù)空間等。兩者的主要區(qū)別則體現(xiàn)在對I2C 消息的構(gòu)造上,在讀出數(shù)據(jù)之前,先要寫地址,根據(jù)寫入的地址來尋找將要讀出的數(shù)據(jù)的起始地址, 所以在讀函數(shù)中就需要構(gòu)造兩條I2C 消息,一條用于寫地址操作,另一條用于讀數(shù)據(jù)操作。另外在轉(zhuǎn)換數(shù)據(jù)空間時, 讀函數(shù)將使用copy_to_user 把內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間。

3.4 AT24C08 的單設(shè)備多驅(qū)動的實(shí)現(xiàn)方式

單設(shè)備多驅(qū)動是本文的一個創(chuàng)新點(diǎn)。設(shè)計中實(shí)現(xiàn)了分3 個設(shè)備驅(qū)動一對1 塊AT24C08 進(jìn)行操作。設(shè)備驅(qū)動1 對AT24C08的第1 個塊操作,設(shè)備驅(qū)動2 對第2 個塊操作,設(shè)備驅(qū)動3 對第3 和第4 個塊進(jìn)行操作。對塊的分開操作體現(xiàn)在對設(shè)備地址的探測上,由于保存設(shè)備地址信息的是二元數(shù)組addr_data,所以在多驅(qū)動對單一的AT24C08 操作時就需要在該二元數(shù)組中指明每個設(shè)備驅(qū)動程序所控制的設(shè)備地址。對于控制第1 個塊的設(shè)備驅(qū)動1,通過數(shù)組normal_addr 指出要進(jìn)行操作的設(shè)備地址為0x50,如下所示:

static unsigned short normal_addr[]={ 0x50,I2C_CLIENT_END};

再通過其對數(shù)組addr_data 進(jìn)行初始化, 這樣, 設(shè)備驅(qū)動1就能檢測到數(shù)組中所指出的AT24C08 的第1 個塊,而跳過其他的塊, 達(dá)到了只對單一特定塊操作的目的。對于設(shè)備驅(qū)動2 來說, 只需把數(shù)組normal_addr 中地址改為AT24C08 的第2 個塊的地址0x51 即可。同理,對設(shè)備驅(qū)動3,只需把normal_addr 中的單一地址改為兩個地址即可,如下所示:

static unsigned short normal_addr [] = { 0x52,0x53, I2C_CLIENT_END};

這樣就可使設(shè)備驅(qū)動只探測到后兩個塊,而跳過其他塊,以達(dá)到對單一AT24C08 中多個塊操作的目的。然后再用insmod命令加載編譯好的三個.ko 驅(qū)動模塊, 獲得3 個不同的設(shè)備號后,接著根據(jù)所獲得的設(shè)備號使用mknod 命令創(chuàng)建3 個不同的字符型設(shè)備節(jié)點(diǎn), 最后通過用戶層的3 個測試程序分別打開已創(chuàng)建的這3 個不同的設(shè)備節(jié)點(diǎn)就能分別對不同的塊進(jìn)行讀寫操作,至此就實(shí)現(xiàn)了單設(shè)備多驅(qū)動的控制方式。

同樣除了分3 個驅(qū)動外, 驅(qū)動開發(fā)者也可以編寫4 個設(shè)備驅(qū)動分別對每1 個塊進(jìn)行操作, 或者就只編寫1 個設(shè)備驅(qū)動對4 個塊一起操作,也適用于綁定非連續(xù)塊進(jìn)行操作,比如用一個設(shè)備驅(qū)動控制第1 和第3 個塊。總之驅(qū)動開發(fā)人員可以根據(jù)不同的需要進(jìn)行不同的組合方式。

3.5 AT24C08 設(shè)備驅(qū)動程序的驗(yàn)證與測試

設(shè)備驅(qū)動程序的驗(yàn)證, 需要通過用戶層的測試程序來實(shí)現(xiàn),測試程序如下:

  fd=open(/dev/at, O_RDWR); //打開設(shè)備文件,獲得設(shè)備文件的文件描述符。

  scanf(%u, start_address); //輸入測試單元起始地址。

  write(fd,buf,sizeof(buf)); //把以頁寫入方式把輸入的16 個數(shù)據(jù)寫入內(nèi)核空間。

4 結(jié)束語

作為當(dāng)前最流行的總線技術(shù)之一,I2C 總線具有結(jié)構(gòu)小巧,使用簡單高效的特點(diǎn),目前在各種設(shè)計中已得到廣泛的應(yīng)用。本文分析了Linux 下I2C 的體系結(jié)構(gòu),較為詳盡的說明了I2C驅(qū)動程序中的一些重要數(shù)據(jù)結(jié)構(gòu)以及這些數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系,并論述了I2C 驅(qū)動程序體系的運(yùn)行機(jī)制, 最后以一個EEPROM 芯片AT24C08 為例,詳細(xì)的給出了一個具體設(shè)備驅(qū)動的基本開發(fā)過程, 并說明了設(shè)備驅(qū)動中的兩個重要接口,I2C 核心層接口和用戶應(yīng)用層接口。更重要的是本文還提供了一種單設(shè)備多驅(qū)動的實(shí)現(xiàn)方式,這將帶給驅(qū)動開發(fā)人員一定的啟示。另外,本文還進(jìn)行了設(shè)備驅(qū)動程序的測試與驗(yàn)證工作, 保證了設(shè)備驅(qū)動程序編寫的正確性。本文的設(shè)備驅(qū)動設(shè)計方法也將對其他相關(guān)I2C設(shè)備驅(qū)動的設(shè)計提供良好的借鑒作用。

本文作者創(chuàng)新點(diǎn):實(shí)現(xiàn)單設(shè)備多驅(qū)動的驅(qū)動模式。

linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)


評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉