嵌入式Linux系統(tǒng)中I2C總線設(shè)備的驅(qū)動(dòng)設(shè)計(jì)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
摘要: 本文分析了Linux系統(tǒng)中I2C驅(qū)動(dòng)程序的結(jié)構(gòu),并以AT91RM9200和X1227為例,介紹了如何在嵌入式Linux系統(tǒng)中實(shí)現(xiàn)I2C總線適配器及I2C設(shè)備驅(qū)動(dòng)。
關(guān)鍵詞: Linux;I2C總線;I2C設(shè)備;驅(qū)動(dòng)
引言
I2C總線是PHILIPS公司推出的兩線式串行總線,用于連接微控制器及其外圍設(shè)備,具有簡(jiǎn)單、高效等特點(diǎn)。由于其接口直接在組件之上,因此I2C總線占用的空間非常小,減少了電路板的空間和芯片引腳的數(shù)量,降低了互聯(lián)成本,特別適用于嵌入式產(chǎn)品。
而Linux系統(tǒng)具有開(kāi)源、免費(fèi)、網(wǎng)上資源豐富等優(yōu)點(diǎn),目前已成為嵌入式系統(tǒng)的主流選擇。因此如何在嵌入式Linux系統(tǒng)中實(shí)現(xiàn)I2C功能成為實(shí)際開(kāi)發(fā)中的問(wèn)題。
I2C總線
I2C 總線通過(guò)串行數(shù)據(jù)SDA 和串行時(shí)鐘SCL線在連接到總線的器件間傳遞信息,每個(gè)器件都有一個(gè)唯一的地址識(shí)別。根據(jù)數(shù)據(jù)傳輸時(shí)的功能不同,把器件分為主機(jī)和從機(jī)。主機(jī)是初始化總線的數(shù)據(jù)傳輸并產(chǎn)生允許傳輸?shù)臅r(shí)鐘信號(hào)的器件,通常是微控制器。此時(shí),任何被尋址的器件都被認(rèn)為是從機(jī),例如LCD驅(qū)動(dòng)器、E2PROM等。
I2C總線協(xié)議規(guī)定,各主機(jī)進(jìn)行通信時(shí)都要有起始、結(jié)束、發(fā)送數(shù)據(jù)和應(yīng)答信號(hào)。這些信號(hào)都是通信過(guò)程中的基本單元。起始信號(hào)就是在SCL線為高時(shí)SDA線從高變化到低;停止信號(hào)就是在SCL線為高時(shí)SDA線從低變化到高;應(yīng)答信號(hào)是在SCL為高時(shí)SDA為低;非應(yīng)答信號(hào)相反,是在SCL為高時(shí)SDA為高。
總線傳送的每1幀數(shù)據(jù)均是1個(gè)字節(jié)。協(xié)議規(guī)定,在啟動(dòng)總線后的第1個(gè)字節(jié)的高7位是對(duì)從機(jī)的尋址地址,第8位為方向位(“0”表示主機(jī)對(duì)從機(jī)的寫操作;“1”表示主機(jī)對(duì)從機(jī)的讀操作),其余的字節(jié)為操作數(shù)據(jù)。數(shù)據(jù)傳送過(guò)程是:在I2C總線發(fā)送起始信號(hào)后,發(fā)送從機(jī)的7位尋址地址和1位表示這次操作性質(zhì)的讀寫位,在有應(yīng)答信號(hào)后開(kāi)始傳送數(shù)據(jù),直到發(fā)送停止信號(hào)。主機(jī)每發(fā)送1個(gè)字節(jié)就要檢測(cè)SDA線上有沒(méi)有收到應(yīng)答信號(hào),有則繼續(xù)發(fā)送,否則將停止發(fā)送數(shù)據(jù)。
Linux中I2C總線驅(qū)動(dòng)結(jié)構(gòu)
Linux系統(tǒng)對(duì)I2C總線具有很好的支持。與硬件物理連接相對(duì)應(yīng)的,Linux的I2C框架中各個(gè)部分的關(guān)系如圖1所示。
圖1 Linux內(nèi)核I2C總線驅(qū)動(dòng)程序構(gòu)架
內(nèi)核中I2C相關(guān)代碼可以分為三個(gè)層次:
1. I2C core框架:提供了核心數(shù)據(jù)結(jié)構(gòu)的定義和相關(guān)接口函數(shù),用來(lái)實(shí)現(xiàn)I2C適配器驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)的注冊(cè)、注銷管理,以及I2C通信方法上層的、與具體適配器無(wú)關(guān)的代碼,為系統(tǒng)中每個(gè)I2C總線增加相應(yīng)的讀寫方法。
2. I2C總線適配器驅(qū)動(dòng):定義描述具體I2C總線適配器的i2c_adapter數(shù)據(jù)結(jié)構(gòu)、實(shí)現(xiàn)在具體I2C適配器上的I2C總線通信方法,并由i2c_algorithm數(shù)據(jù)結(jié)構(gòu)進(jìn)行描述。
3. I2C 設(shè)備驅(qū)動(dòng):定義描述具體設(shè)備的i2c_client和可能的私有數(shù)據(jù)結(jié)構(gòu)、借助I2C core提供的函數(shù)接口完成設(shè)備在內(nèi)核的注冊(cè),并實(shí)現(xiàn)具體的功能,包括read, write以及ioctl等對(duì)用戶層操作的接口。
總體而言,Linux中I2C總線的驅(qū)動(dòng)分為兩個(gè)部分:總線驅(qū)動(dòng)(BUS)和設(shè)備驅(qū)動(dòng)(DEVICE)。I2C core與I2C總線適配器驅(qū)動(dòng)完成了硬件上的主機(jī)總線驅(qū)動(dòng)(BUS),而I2C driver則實(shí)現(xiàn)了從機(jī)設(shè)備驅(qū)動(dòng)。在設(shè)計(jì)中,I2C core提供的接口是統(tǒng)一的,不需要修改,我們只需要實(shí)現(xiàn)特定I2C總線適配器驅(qū)動(dòng)和I2C設(shè)備驅(qū)動(dòng),這樣大大提高了系統(tǒng)的可移植性。
筆者在某個(gè)產(chǎn)品中曾用AT91RM9200和X1227構(gòu)成嵌入式系統(tǒng)的時(shí)鐘模塊。在該設(shè)計(jì)中,AT91RM9200作為I2C的主機(jī)部分,X1227作為從機(jī)。下面以此為例,具體介紹這兩部分驅(qū)動(dòng)的實(shí)現(xiàn)。
AT91RM9200 I2C總線驅(qū)動(dòng)實(shí)現(xiàn)
AT91RM9200是ARM920T處理器,它提供標(biāo)準(zhǔn)的兩線接口TWI,即I2C接口,主機(jī)工作模式。通過(guò)TWI 控制寄存器TWI_CR設(shè)置I2C工作模式和狀態(tài)。時(shí)鐘由寄存器TWI_CWGR中編程值產(chǎn)生。該寄存器定義了TWCK 信號(hào),使接口適應(yīng)寬范圍時(shí)鐘。
具體在linux中AT91RM9200 I2C總線適配器驅(qū)動(dòng)的實(shí)現(xiàn),首先初始化AT91RM9200 I2C的工作模式,然后裝載I2C總線驅(qū)動(dòng),這需要兩個(gè)結(jié)構(gòu)模塊來(lái)描述:struct i2c_adapter和struct i2c_algorithm。
初始化i2c_adapter結(jié)構(gòu)成員如下:
static struct i2c_adapter at91rm9200_adapter = {
name: "AT91RM9200",
id: I2C_ALGO_SMBUS,
algo: &at91_algorithm,
algo_data: NULL,
inc_use: at91_inc,
dec_use: at91_dec,
... ...
};
這個(gè)模塊并未提供讀寫函數(shù),具體的讀寫方法由第二個(gè)模塊struct i2c_algorithm提供。
static struct i2c_algorithm at91_algorithm = {
name: "at91 i2c",
id: I2C_ALGO_SMBUS,
smbus_xfer: at91_smbus_xfer,
master_xfer: at91_xfer,
functionality: at91_func,
};
通過(guò)調(diào)用I2C core中的接口函數(shù)i2c_add_adapter將這兩個(gè)模塊注冊(cè)到操作系統(tǒng)里,總線驅(qū)動(dòng)就算裝上了。由此可見(jiàn),i2c_algorithm實(shí)現(xiàn)了i2c通信具體方法。針對(duì)本文at91rm9200 I2C適配器, at91_xfer最為關(guān)鍵。分析內(nèi)核可知,I2C core框架中提供給主機(jī)使用的數(shù)據(jù)傳輸接口:i2c_master_send,i2c_master_recv,i2c_transfer最終都是通過(guò)調(diào)用at91_xfer實(shí)現(xiàn)。
數(shù)據(jù)傳輸處理如下:數(shù)據(jù)發(fā)送主機(jī)初始化Start狀態(tài)后,向主機(jī)模式寄存器TWI_MMR中DADR發(fā)送一個(gè)7位從機(jī)地址,以通知從機(jī)器件。從機(jī)地址后的位表示傳輸方向(寫或讀)。該位為0,說(shuō)明是寫操作(發(fā)送操作);若該位為1,說(shuō)明為數(shù)據(jù)讀請(qǐng)求( 接收操作)。TWI 傳輸要求從機(jī)每收到一個(gè)字節(jié)后均要給出應(yīng)答。在應(yīng)答時(shí)鐘脈沖中,主機(jī)釋放數(shù)據(jù)線(HIGH),將從機(jī)拉低以產(chǎn)生應(yīng)答。主機(jī)在該時(shí)鐘脈沖中輪詢數(shù)據(jù)線,可使用輪詢或中斷方式來(lái)檢驗(yàn)狀態(tài)位。若從機(jī)未應(yīng)答該字節(jié),將置位狀態(tài)寄存器TWI_SR的NAK 位。
寫操作則發(fā)送數(shù)據(jù)至保持寄存器TWI_THR,設(shè)置TWI_CR的START 位以啟動(dòng)傳輸。數(shù)據(jù)在內(nèi)部移位寄存器中移位,當(dāng)檢測(cè)到應(yīng)答,TXRDY位置位,直到TWI_THR中有新數(shù)據(jù)寫入,才清除該位。主機(jī)產(chǎn)生STOP 狀態(tài)來(lái)結(jié)束傳輸。設(shè)置START 后開(kāi)始讀序列。當(dāng)狀態(tài)寄存器中RXRDY 位置位時(shí),接收保持寄存器(TWI_RHR)以收到一個(gè)字符。當(dāng)讀TWI_RHR 時(shí)RXRDY 位復(fù)位。
TWI接口可執(zhí)行多種傳輸格式:7位從機(jī)地址和10位從機(jī)地址。通過(guò)主機(jī)模式寄存器TWI_MMR配置三個(gè)內(nèi)部地址字節(jié)。若從機(jī)僅支持7 位地址,IADRSZ 必須置為0。若從機(jī)地址大于7 位,用戶必須配置地址大小IADRSZ 并在內(nèi)部地址寄存器TWI_IADR中設(shè)置其他從機(jī)地址位。
X1227的設(shè)備驅(qū)動(dòng)實(shí)現(xiàn)
X1227 是一個(gè)帶有時(shí)鐘、日歷、CPU 監(jiān)控電路和兩路查詢報(bào)警的實(shí)時(shí)時(shí)鐘。時(shí)鐘使用一個(gè)低成本的32.768kHz 的晶體作為輸入,可精密地用秒、分鐘、小時(shí)、日期、星期、月、年來(lái)顯示時(shí)間,它可以自動(dòng)調(diào)整閏年至2096年。同時(shí)X1227有一個(gè)看門狗定時(shí)器、3個(gè)超時(shí)時(shí)間可供選擇。另外,X1227有一個(gè)4K位的EEPROM陣列,可用作某些用戶配置數(shù)據(jù)的存儲(chǔ)器。下面以X1227為例,說(shuō)明一個(gè)具體的I2C設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)要點(diǎn)。
如前所述,I2C總線驅(qū)動(dòng)只是提供了對(duì)一條總線的讀寫機(jī)制,本身并不會(huì)去做通信。通信是由I2C設(shè)備驅(qū)動(dòng)來(lái)做的,設(shè)備驅(qū)動(dòng)透過(guò)I2C總線同具體的設(shè)備進(jìn)行通訊。一個(gè)設(shè)備驅(qū)動(dòng)有兩個(gè)模塊來(lái)描述,struct i2c_client和struct i2c_driver。i2c_client用來(lái)描述一個(gè)具體的I2C設(shè)備,i2c_driver結(jié)構(gòu)提供了i2c_adapter與i2c_client之間的通信方式。
struct i2c_driver x1227_driver = {
name: 襒1227?
id: I2C_DRIVERID_X1227,
flags: I2C_DF_NOTIFY,
attach_adapter: x1227_probe,
detach_client: x1227_detach,
command: x1227_command
};
其中:attach_adapter利用適配器驅(qū)動(dòng)提供的I2C總線訪問(wèn)方法,利用設(shè)備驅(qū)動(dòng)程序模塊中提供的地址線索信息,檢測(cè)可能存在的設(shè)備及其地址。如果成功發(fā)現(xiàn)設(shè)備,則創(chuàng)建一個(gè)struct i2c_client來(lái)標(biāo)識(shí)這個(gè)設(shè)備,并向該適配器的數(shù)據(jù)結(jié)構(gòu)注冊(cè)。detach_client用于從總線上注銷設(shè)備、并釋放i2c_client及相應(yīng)的私有數(shù)據(jù)結(jié)構(gòu)。command是用戶接口中的ioctl功能的底層實(shí)現(xiàn)。
I2C設(shè)備驅(qū)動(dòng)需要實(shí)現(xiàn)兩個(gè)方面的接口,一個(gè)是對(duì)I2C core框架的接口,設(shè)備初始化時(shí)通過(guò)函數(shù)i2c_add_driver調(diào)用,來(lái)實(shí)現(xiàn)驅(qū)動(dòng)的注冊(cè)。這個(gè)i2c_driver一旦裝入完成,其中的attach_adapter函數(shù)就會(huì)被調(diào)用。
另一個(gè)是對(duì)用戶應(yīng)用層的接口,提供用戶程序訪問(wèn)I2C設(shè)備的接口,包括實(shí)現(xiàn)open,release,read,write以及最重要的ioctl等標(biāo)準(zhǔn)文件操作的接口函數(shù)。每個(gè)設(shè)備驅(qū)動(dòng)程序都有一個(gè)稱為file_operations的數(shù)據(jù)結(jié)構(gòu),用來(lái)實(shí)現(xiàn)接口函數(shù)。
static struct file_operations rtc_fops = {
owner: THIS_MODULE,
ioctl: x1227_rtc_ioctl,
open: x1227_rtc_open,
release: x1227_rtc_release,
};
其中open和release用來(lái)打開(kāi)和關(guān)閉X1227,x1227_rtc_ioctl則向用戶提供的一系列控制時(shí)鐘芯片的具體命令:RTC_GET_TIME(以固定的數(shù)據(jù)格式讀取實(shí)時(shí)時(shí)鐘的時(shí)間)、RTC_SET_TIME(以固定的數(shù)據(jù)格式設(shè)定實(shí)時(shí)時(shí)鐘的時(shí)間)以及E2PROM讀寫等。
對(duì)于X1227,一般注冊(cè)為一個(gè)miscdevice設(shè)備(所有miscdevice設(shè)備共同一個(gè)主設(shè)備號(hào),不同的次設(shè)備號(hào))。
static struct miscdevice x1227_rtc_miscdev = {
RTC_MINOR,
tc?
&rtc_fops
};
初始化時(shí),通過(guò)misc_register (&x1227_rtc_miscdev)注冊(cè)X1227,這樣用戶程序可以通過(guò)主設(shè)備號(hào)10 次設(shè)備號(hào) 135的設(shè)備節(jié)點(diǎn)/dev/rtc來(lái)訪問(wèn)X1227。
要測(cè)試X1227的時(shí)鐘功能,首先把AT91RM9200的I2C總線驅(qū)動(dòng)模塊和X1227模塊在系統(tǒng)啟動(dòng)時(shí)先后加載。需要指出的是,Linux將時(shí)鐘分為系統(tǒng)時(shí)鐘和硬件時(shí)鐘兩種。系統(tǒng)時(shí)鐘是指當(dāng)前Linux Kernel中的時(shí)鐘,而硬件時(shí)鐘則是主板上由電池供電的那個(gè)主板硬件時(shí)鐘,也就是本文中的X1227。
在Linux中,用于時(shí)鐘查看和設(shè)置的命令主要有date、hwclock。首先設(shè)置系統(tǒng)時(shí)鐘,比如設(shè)置為2006年8月17日12點(diǎn)30分:date 081712302006,然后設(shè)置硬件時(shí)鐘為當(dāng)前系統(tǒng)時(shí)鐘時(shí)間,使用命令/sbin/hwclock 衧ystohc,則X1227中的時(shí)間設(shè)置為當(dāng)前系統(tǒng)時(shí)間。然后,通常在操作系統(tǒng)啟動(dòng)時(shí)設(shè)置啟動(dòng)腳本/sbin/hwclock 衕ctosys,利用X1227內(nèi)的時(shí)間更新系統(tǒng)時(shí)鐘,然后直到重啟或關(guān)閉系統(tǒng),由系統(tǒng)時(shí)鐘來(lái)記錄時(shí)間。
結(jié)語(yǔ)
本文介紹了I2C總線適配器及I2C設(shè)備驅(qū)動(dòng)的實(shí)現(xiàn)。該設(shè)計(jì)成功用于某網(wǎng)絡(luò)測(cè)試設(shè)備的主控模塊上,實(shí)現(xiàn)了設(shè)備的實(shí)時(shí)時(shí)鐘功能,便于整個(gè)系統(tǒng)的監(jiān)控。I2C總線在目前的嵌入式領(lǐng)域中應(yīng)用非常廣泛,如音/視頻的控制,存儲(chǔ)設(shè)備的通訊等,而Linux也已成為嵌入式系統(tǒng)的主流。從linux內(nèi)核看,I2C的驅(qū)動(dòng)程序具有清晰的層次結(jié)構(gòu),為編程者開(kāi)發(fā)I2C相關(guān)驅(qū)動(dòng)提供了規(guī)范的框架。
參考文獻(xiàn):
1. lessandro Rubini,Jonathan Corbet. Linux Device Drivers,second edition[M].O誖eilly & Associates,2002.
2. 鄭旭陽(yáng)、李兵兵、黃新平,模擬I2C總線多主通信研究與軟件設(shè)計(jì),單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2005,12:29_32
3. Philips Corporation, I2C bus specification version 2.1, 2000
4. Atmel Corporation, AT91RM9200 Datasheet, version E, 2005
5. Xicor Corporation, X1227 Datasheet, version1.3, 2004