孟令公,朱 宏,楊忠孝,楊裕輝
摘 要:在嵌入式中,Linux漸漸成為一種流行操作系統(tǒng),Linux驅(qū)動(dòng)開(kāi)發(fā)也成為嵌入式開(kāi)發(fā)中的必備環(huán)節(jié)。介紹Linux環(huán)境下基于I2C協(xié)議的RTC驅(qū)動(dòng)程序開(kāi)發(fā)與實(shí)現(xiàn)。首先研究了Linux環(huán)境下字符設(shè)備驅(qū)動(dòng)程序框架,然后介紹I2C協(xié)議,在此基礎(chǔ)上開(kāi)發(fā)基于I2C協(xié)議的RTC字符設(shè)備驅(qū)動(dòng)程序。對(duì)于驅(qū)動(dòng)程序,這里詳細(xì)介紹其整體架構(gòu)和各模塊實(shí)現(xiàn)細(xì)節(jié)。最終成功實(shí)現(xiàn)了基于I2C協(xié)議的RTC驅(qū)動(dòng)程序,并移植到Linux操作系統(tǒng)中。
關(guān)鍵詞:嵌入式系統(tǒng);Linux;驅(qū)動(dòng)程序;I2C
中圖分類號(hào):TP316.81文獻(xiàn)標(biāo)識(shí)碼:B
文章編號(hào):1004-373X(2009)20-032-04
Development of RTC Driver Based on I2C under Linux
MENG Linggong,ZHU Hong,YANG Zhongxiao,YANG Yuhui
(University of Electronic Science and Technology of China,Chengdu,610054,China)
Abstract:In embedded field,Linux OS has been more and more popular,the development of Linux driver is becoming a constituent job.Reaserch and development of RTC driver based on I2C under Linux OS are introduced.First,research on the character device driver framework is given.Then,I2C protocol is introduced,RTC driver based on I2C under Linux OS is developed,the scheme and realization are proposed.Finally,RTC driver based on I2C is realized and transplanted into Linux.
Keywords:embedded system;Linux;driver;I2C
0 引 言
近年來(lái)嵌入式系統(tǒng)的研究與開(kāi)發(fā)漸漸成為熱點(diǎn),在嵌入式系統(tǒng)中,Linux操作系統(tǒng)以其開(kāi)源、穩(wěn)定、可移植等種種優(yōu)點(diǎn),漸漸成為一種流行的操作系統(tǒng)。Linux下各種驅(qū)動(dòng)程序的開(kāi)發(fā)經(jīng)常是軟件開(kāi)發(fā)中必不可缺的環(huán)節(jié),Linux對(duì)其驅(qū)動(dòng)程序提供了很好的支持框架。I2C總線是一種由Philips公司開(kāi)發(fā)的兩線式串行總線,用于連接微控制器及其外圍設(shè)備。I2C總線產(chǎn)生于在20世紀(jì)80年代,最主要的優(yōu)點(diǎn)是其簡(jiǎn)單性和有效性。這里介紹Linux字符設(shè)備驅(qū)動(dòng)以及I2C總線協(xié)議,并在此基礎(chǔ)上開(kāi)發(fā)基于I2C總線的RTC驅(qū)動(dòng)程序。
1 Linux字符設(shè)備驅(qū)動(dòng)框架
在Linux內(nèi)核中每個(gè)字符驅(qū)動(dòng)程序都是基于以下框架進(jìn)行設(shè)計(jì)的[1,2]:
struct file_operations {
ssize_t (*read) (struct file *,char __user *,size_t,loff_t *);
ssize_t (*write) (struct file *,const char __user *,size_t,loff_t *);
int (*ioctl) (struct inode *,struct file *,unsigned int,unsigned long);
int (*open) (struct inode *,struct file *);
int (*release) (struct inode *,struct file *);
… };
其中每個(gè)驅(qū)動(dòng)程序必須實(shí)現(xiàn)的函數(shù)主要有:
(1) open()
在Linux或應(yīng)用程序使用設(shè)備前都會(huì)調(diào)用驅(qū)動(dòng)程序的這個(gè)方法。在驅(qū)動(dòng)程序中open()方法一般用于對(duì)驅(qū)動(dòng)程序支持的設(shè)備進(jìn)行初始化。
(2) release()
當(dāng)Linux或應(yīng)用程序不再使用設(shè)備時(shí)會(huì)調(diào)用驅(qū)動(dòng)程序的這個(gè)方法。release()方法主要用于在使用完畢支持驅(qū)動(dòng)程序的設(shè)備后,對(duì)設(shè)備進(jìn)行關(guān)閉操作。
(3) read()
read()方法主要用于供應(yīng)用程序或Linux從字符設(shè)備中讀取數(shù)據(jù)。read()方法讀取的數(shù)據(jù)會(huì)從內(nèi)核態(tài)拷貝到用戶態(tài),供應(yīng)用程序使用[3]。
(4) write()
Linux或應(yīng)用程序會(huì)調(diào)用write()方法寫(xiě)多個(gè)字節(jié)數(shù)據(jù)到字符設(shè)備中。應(yīng)用程序的數(shù)據(jù)會(huì)首先從用戶態(tài)拷貝到內(nèi)核態(tài),然后再傳給write()方法。
(5) ioctl()
ioctl()方法向應(yīng)用程序提供了一些獨(dú)特的操作,這些操作不易通過(guò)read()方法或write()方法來(lái)實(shí)現(xiàn)[4,5]。
2 I2C協(xié)議以及RTC芯片介紹
2.1 I2C協(xié)議
I2C總線是一種由Philips公司開(kāi)發(fā)的兩線式串行總線標(biāo)準(zhǔn),用于鏈接微控制器及其外圍設(shè)備。I2C由數(shù)據(jù)線(SDA)和時(shí)鐘線(SCL)構(gòu)成的同步串行總線,可發(fā)送和接收數(shù)據(jù)。在處理器與被控制芯片之間,芯片與芯片之間進(jìn)行雙向傳送[6]。
I2C總線在傳送數(shù)據(jù)過(guò)程中共有三種類型信號(hào), 它們分別是開(kāi)始信號(hào)、結(jié)束信號(hào)和應(yīng)答信號(hào)[3]。
開(kāi)始信號(hào) SCL為高電平時(shí),SDA由高電平向低電平跳變,開(kāi)始傳送數(shù)據(jù)。
結(jié)束信號(hào) SCL為低電平時(shí),SDA由低電平向高電平跳變,結(jié)束傳送數(shù)據(jù)。
應(yīng)答信號(hào) 接收數(shù)據(jù)的設(shè)備在接收到8位數(shù)據(jù)后,向發(fā)送數(shù)據(jù)的設(shè)備發(fā)出特定的低電平脈沖,表示已收到數(shù)據(jù)。
(1) 控制字節(jié)。
在起始條件之后,必須是器件控制字節(jié),其中高4位為器件類型識(shí)別符(不同芯片類型有不同定義),接著3位為地址片選,最后1位為讀寫(xiě)位,當(dāng)為1時(shí)讀操作,為0時(shí)寫(xiě)操作。
(2) 寫(xiě)操作。
在寫(xiě)入時(shí),I2C主控設(shè)備先發(fā)起起始位(S),搶占總線;然后,發(fā)送7位設(shè)備地址和1位0,表示對(duì)設(shè)備的寫(xiě)入;接著就是向設(shè)備傳送數(shù)據(jù)。
(3) 讀操作。
在讀取時(shí),要改變總線數(shù)據(jù)的傳輸方向,其流程如下:I2C主控設(shè)備發(fā)起起始位(S),搶占總線;發(fā)送7位設(shè)備地址和1位0,表示對(duì)設(shè)備寫(xiě)入;發(fā)送要寫(xiě)入的寄存器地址;再次發(fā)起起始位(S);發(fā)送7位的設(shè)備地址和1位1,表示對(duì)設(shè)備讀取;從設(shè)備讀取數(shù)據(jù)[7]。
2.2 RTC時(shí)鐘芯片介紹
在此,采用精密時(shí)鐘ISL1208向系統(tǒng)提供實(shí)時(shí)時(shí)間。ISL1208是Intersil公司的一款低功耗實(shí)時(shí)時(shí)鐘集成電路。ISL1208寄存器可由后備電池供電。在從地址“1101111x”之后,可以訪問(wèn),可以讀或?qū)懙降刂穂00h~13h],可以通過(guò)對(duì)任意寄存器地址直接以字節(jié)寫(xiě)或頁(yè)面寫(xiě)操作來(lái)修改寄存器的內(nèi)容。
寄存器分成4段,它們是:實(shí)時(shí)時(shí)鐘(7 B):地址為00h~06h;控制與狀態(tài)(5 B):地址為07h~0Bh;報(bào)警(6 B):地址為0ch~11h;用戶SRAM(2 B):地址為12h~13h。
由于只使用了其中的實(shí)時(shí)時(shí)鐘寄存器,所以只對(duì)其中用到的寄存器進(jìn)行描述。實(shí)時(shí)時(shí)鐘寄存器主要包括SC(秒)、MN(分)、HR(時(shí))、DT(日期)、MO(月)、YR(年)、DW(星期)。
2.3 IXP425網(wǎng)絡(luò)處理器對(duì)I2C協(xié)議的支持
IXP425本身沒(méi)有基于I2C協(xié)議的端口,但是ixp425有16個(gè)GPIO(GPIO0~GPIO15),即通用輸入輸出端口。這些GPIO可以由軟件控制輸出0或1,并且也可由軟件去讀取GPIO上的狀態(tài)[8]。雖然Ixp425沒(méi)有基于I2C協(xié)議的接口,但是可以采用它的GPIO來(lái)實(shí)現(xiàn)I2C信號(hào)發(fā)送與接受。具體采用:
GPIO7SDA數(shù)據(jù)信號(hào)發(fā)送與接受
GPIO6SCK時(shí)鐘信號(hào)發(fā)送
3 基于I2C協(xié)議的RTC驅(qū)動(dòng)程序設(shè)計(jì)
3.1 驅(qū)動(dòng)程序總體框架
整個(gè)驅(qū)動(dòng)程序可以分成兩個(gè)模塊,第一個(gè)模塊是I2C協(xié)議實(shí)現(xiàn)部分,稱之為I2C協(xié)議模塊。第二個(gè)模塊負(fù)責(zé)與Linux內(nèi)核及應(yīng)用程序交互,稱之為RTC驅(qū)動(dòng)模塊[9],如圖1所示。
圖1 模塊圖
3.2 I2C協(xié)議實(shí)現(xiàn)模塊
該模塊中用到的宏與函數(shù):
#define I2C_SDA_BIT 7
//gpio7配置為數(shù)據(jù)信號(hào)口SDA
#define I2C_SCK_BIT6
//gpio6配置為時(shí)鐘信號(hào)口SCL
GPIO_DATA_BIT_WRITE_HIGH();
//設(shè)置GPIO狀態(tài)為高
GPIO_DATA_BIT_WRITE_LOW();
//設(shè)置GPIO狀態(tài)為底
(1) void GpioI2CStart(void)
功能:產(chǎn)生一次I2C開(kāi)始信號(hào)。
實(shí)現(xiàn):在SCL為高的情況下,SDA由高電平向低電平跳變。
函數(shù)流程圖如圖2所示。
(2) void GpioI2CStop(void)
功能:產(chǎn)生一次I2C傳送結(jié)束信號(hào)。
實(shí)現(xiàn):在SCL為高的情況下,SDA由低電平向高電平跳變。
函數(shù)流程圖如圖3所示。
圖2 函數(shù)流程圖(一)
圖3 函數(shù)流程圖(二)
(3) void GpioI2CSendByte(BYTE ucData)
功能:在I2C總線上發(fā)送一個(gè)字節(jié)(8位數(shù)據(jù))。
參數(shù):BYTE ucData 要發(fā)送的字節(jié)(8位數(shù)據(jù))
實(shí)現(xiàn):現(xiàn)在總線上寫(xiě)一個(gè)8位數(shù)據(jù),然后等待ACK。函數(shù)流程如圖4所示。
圖4 函數(shù)流程圖(三)
(4) UINT8 GpioI2CReceiveByte(void)
功能:在I2C總線上讀取一個(gè)字節(jié)(8位數(shù)據(jù))。
返回值:在I2C總線上讀取到的數(shù)據(jù)。
實(shí)現(xiàn):在總線上等待數(shù)據(jù)的讀取。
函數(shù)流程如圖5所示。
3.3 RTC驅(qū)動(dòng)實(shí)現(xiàn)模塊
RTC驅(qū)動(dòng)模塊主要實(shí)現(xiàn)struct file_operations結(jié)構(gòu)框架。這里主要實(shí)現(xiàn)了該框架的read,write,ioctl,release幾個(gè)函數(shù)成員。其中主要功能在ioctl中實(shí)現(xiàn)。
圖5 函數(shù)流程圖(四)
該結(jié)構(gòu)的定義如下[10]:
struct file_operations I2C_rtc_fops =
{
.open = I2CRtcDrvOpen,
.release = I2CRtcDrvClose,
.read= I2CRtcDrvRead,
.write= I2CRtcDrvWrite,
.ioctl = I2CRtcDrvIoctl,
};
由于所有功能都在ioctl函數(shù)成員中實(shí)現(xiàn),所以以下將主要介紹ioctl函數(shù)成員。
(1) ioctl函數(shù)用到的數(shù)據(jù)結(jié)構(gòu)與調(diào)用的函數(shù)
typedef struct
{
unsigned char second;//秒
unsigned char minute;//分鐘
unsigned char hour;//小時(shí)
unsigned char date;//日
unsigned char month;//月
unsigned char year;//年
}I2C_RTC_DATA_T;
該結(jié)構(gòu)表示系統(tǒng)時(shí)間。在I2C驅(qū)動(dòng)中,主要用來(lái)存儲(chǔ)從I2C總線上讀取的RTC時(shí)間。I2C協(xié)議模塊把從RTC時(shí)鐘中讀取的時(shí)間轉(zhuǎn)化為該類型,然后傳給RTC驅(qū)動(dòng)模塊。RTC驅(qū)動(dòng)模塊再把該類型的系統(tǒng)時(shí)間傳給用戶態(tài)。
INT32 GpioI2CRead(UINT8 * address,UINT16 rom_address,UINT16 count);
INT32 GpioI2CWrite(UINT8 * address,UINT16 rom_address,UINT16 count);
這兩個(gè)函數(shù)用于讀和寫(xiě)RTC時(shí)鐘上的寄存器,其中address參數(shù)為內(nèi)存區(qū)緩沖地址,rom_address為RTC上寄存器地址,count為要讀取的字節(jié)數(shù)。它們是基于I2C協(xié)議的調(diào)用來(lái)實(shí)現(xiàn)的。在以下函數(shù)中將會(huì)用到。
(2) int I2CRtcDrvIoctl(struct inode *inode,struct file *file,UINT32 cmd,unsigned long arg)
功能:這個(gè)是I2C驅(qū)動(dòng)模塊中的ioctl函數(shù)成員。
參數(shù)描述如表1所示。
表1 函數(shù)的參數(shù)描述
參數(shù)名類型說(shuō)明
cmdUINT32用戶態(tài)向內(nèi)核態(tài)傳入的命令
argunsigned long用戶態(tài)向內(nèi)核態(tài)傳入的參數(shù)
cmd中傳入的命令:
RTC_RD_TIME 讀取RTC時(shí)鐘時(shí)間
RTC_SET_TIME 設(shè)置RTC時(shí)鐘時(shí)間
arg參數(shù)為一個(gè)指向I2C_RTC_DATA_T類型的指針,用于存取時(shí)間。
函數(shù)流程如圖6所示,圖中I2C_RTC_DATA_T gI2cRtcData;全局?jǐn)?shù)據(jù),表示時(shí)間。
圖6 函數(shù)流程圖(五)
該函數(shù)讀取用戶傳入的命令(cmd),如果用戶要獲取時(shí)間,則調(diào)用底層函數(shù)讀RTC寄存器,獲取時(shí)間。如果用戶要設(shè)置時(shí)間,則調(diào)用底層函數(shù)寫(xiě)RTC寄存器,設(shè)置時(shí)間。
4 結(jié) 語(yǔ)
這里介紹了Linux操作系統(tǒng)下基于I2C協(xié)議的RTC驅(qū)動(dòng)程序的開(kāi)發(fā),主要介紹了I2C協(xié)議以及Linux字符設(shè)備驅(qū)動(dòng)程序框架,并在此基礎(chǔ)上給出了基于IXP425處理器、I2C協(xié)議的RTC字符設(shè)備驅(qū)動(dòng)程序。該設(shè)備驅(qū)動(dòng)程序包括最底層的協(xié)議開(kāi)發(fā),以及上層的驅(qū)動(dòng)程序框架,具有很強(qiáng)的移植性。最終成功開(kāi)發(fā)并實(shí)現(xiàn)了該驅(qū)動(dòng)程序。
參考文獻(xiàn)
[1]劉名博,鄧中亮.基于ARM的嵌入式Linux操作系統(tǒng)移植的研究[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2006,44(11):87-88.
[2]劉軍芳,胡和智.基于ARM的嵌入式 Linux操作系統(tǒng)移植研究[J].科技信息:學(xué)術(shù)版,2006,22(5):29-31.
[3]王淑貞.U-Boot在S3C2410上的移植[J].微計(jì)算機(jī)應(yīng)用,2008,29(4):95-97.
[4]劉廣路.基于ARM的μCLinux片級(jí)移植[J].現(xiàn)代計(jì)算機(jī),2008,33(1):131-133.
[5]孫紀(jì)坤,張小全.嵌入式Linux系統(tǒng)技術(shù)開(kāi)發(fā)詳解——基于ARM[M].北京:人民郵電出版社,2006.
[6]周立功,陳明計(jì),陳渝.ARM嵌入式Linux系統(tǒng)構(gòu)建與驅(qū)動(dòng)開(kāi)發(fā)范例[M].北京:北京航空航天大學(xué)出版社,2006.
[7]許振山,劉崢嶸.嵌入式Linux系統(tǒng)應(yīng)用開(kāi)發(fā)詳解[M].北京:電力工業(yè)出版社,2007.
[8]李駒光,鄭秋,江澤明.嵌入式Linux系統(tǒng)技術(shù)開(kāi)發(fā)詳解基于EP93XX系列ARM[M].北京:清華大學(xué)出版社,2006.
[9]Daniel P Bovet,Marco Cesa.深入理解Linux內(nèi)核[M].2版.陳莉君,馮銳,牛欣源,譯.北京:中國(guó)電子出版社,2004.
[10]陳渝,李明,楊曄.源碼開(kāi)放的嵌入式系統(tǒng)軟件分析與實(shí)踐[M].北京:北京航空航天大學(xué)出版社,2004.