手把手教你寫Linux I2C設(shè)備驅(qū)動(dòng)
Linux I2C驅(qū)動(dòng)是嵌入式Linux驅(qū)動(dòng)開發(fā)人員經(jīng)常需要編寫的一種驅(qū)動(dòng),因?yàn)榉彩窍到y(tǒng)中使用到的I2C設(shè)備,,幾乎都需要編寫相應(yīng)的I2C驅(qū)動(dòng)去配置和控制它,例如 RTC實(shí)時(shí)時(shí)鐘芯片,、音視頻采集芯片、音視頻輸出芯片,、EEROM芯片,、AD/DA轉(zhuǎn)換芯片等等。 Linux I2C驅(qū)動(dòng)涉及的知識(shí)點(diǎn)還是挺多的,,主要分為Linux I2C的總線驅(qū)動(dòng)(I2C BUS Driver)和設(shè)備驅(qū)動(dòng)(I2C Clients Driver),,本文主要關(guān)注如何快速地完成一個(gè)具體的I2C設(shè)備驅(qū)動(dòng)(I2C Clients Driver)。關(guān)于Linux I2C驅(qū)動(dòng)的整體架構(gòu),、核心原理等可以在網(wǎng)上搜索其他相關(guān)文章學(xué)習(xí),。 注意:本系列文章的I2C設(shè)備驅(qū)動(dòng)是基于Linux 2.6.18內(nèi)核。 本文主要參考了Linux內(nèi)核源碼目錄下的 ./Documentation/i2c/writing-clients 文檔,。以手頭的一款視頻采集芯片TVP5158為驅(qū)動(dòng)目標(biāo),,編寫Linux I2C設(shè)備驅(qū)動(dòng),。 1. i2c_driver結(jié)構(gòu)體對象 每一個(gè)I2C設(shè)備驅(qū)動(dòng),必須首先創(chuàng)造一個(gè)i2c_driver結(jié)構(gòu)體對象,,該結(jié)構(gòu)體包含了I2C設(shè)備探測和注銷的一些基本方法和信息,,示例如下:
其中,name字段標(biāo)識(shí)本驅(qū)動(dòng)的名稱(不要超過31個(gè)字符),,attach_adapter和detach_client字段為函數(shù)指針,,這兩個(gè)函數(shù)在I2C設(shè)備注冊的時(shí)候會(huì)自動(dòng)調(diào)用,需要自己實(shí)現(xiàn)這兩個(gè)函數(shù),,后面將詳細(xì)講述,。 2. i2c_client 結(jié)構(gòu)體對象 上面定義的i2c_driver對象,抽象為一個(gè)i2c的驅(qū)動(dòng)模型,,提供對i2C設(shè)備的探測和注銷方法,,而i2c_client結(jié)構(gòu)體則是代表著一個(gè)具體的i2c設(shè)備,該結(jié)構(gòu)體有一個(gè)data指針,,可以指向任何私有的設(shè)備數(shù)據(jù),,在復(fù)雜點(diǎn)的驅(qū)動(dòng)中可能會(huì)用到。示例如下:
其中,,users為示例,,用戶可以自己在tvp5158_obj這個(gè)結(jié)構(gòu)體里面添加感興趣的字段,但是i2c_client字段不可少,。具體用法后面再詳細(xì)講,。 3. 設(shè)備注冊及探測功能 這一步很關(guān)鍵,按照標(biāo)準(zhǔn)的要求來寫,,則Linux系統(tǒng)會(huì)自動(dòng)調(diào)用相關(guān)的代碼去探測你的I2C設(shè)備,并且添加到系統(tǒng)的I2C設(shè)備列表中以供后面訪問,。 我們知道,,每一個(gè)I2C設(shè)備芯片,都通過硬件連接設(shè)定好了該設(shè)備的I2C設(shè)備地址,。因此,,I2C設(shè)備的探測一般是靠設(shè)備地址來完成的。那么,,首先要在驅(qū)動(dòng)代碼中聲明你要探測的I2C設(shè)備地址列表,,以及一個(gè)宏。示例如下:
normal_i2c 數(shù)組包含了你需要探測的I2C設(shè)備地址列表,,并且必須以I2C_CLIENT_END作為結(jié)尾,,注意,上述代碼中的0xbc和0xbe是我在硬件上為我的tvp5158分配的地址,,硬件上我支持通過跳線將該地址設(shè)置為 0xbc 或者 0xbe,,所以把這兩個(gè)地址均寫入到探測列表中,,讓系統(tǒng)進(jìn)行探測。如果你的I2C設(shè)備的地址是固定的,,那么,,這里可以只寫你自己的I2C設(shè)備地址,注意必須向右移位1,。 宏 I2C_CLIENT_INSMOD 的作用網(wǎng)上有許多文章進(jìn)行了詳細(xì)的講解,,這里我就不詳細(xì)描述了,記得加上就行,,我們重點(diǎn)關(guān)注實(shí)現(xiàn),。 下一步就應(yīng)該編寫第1步中的兩個(gè)回調(diào)函數(shù),一個(gè)用于注冊設(shè)備,,一個(gè)用于注銷設(shè)備,。探測函數(shù)示例如下:
這個(gè)回調(diào)函數(shù)系統(tǒng)會(huì)自動(dòng)調(diào)用,我們只需要按照上述代碼形式寫好就行,,這里調(diào)用了系統(tǒng)的I2C設(shè)備探測函數(shù),,i2c_probe(),第三個(gè)參數(shù)為具體的設(shè)備探測回調(diào)函數(shù),,系統(tǒng)會(huì)在探測設(shè)備的時(shí)候調(diào)用這個(gè)函數(shù),,需要自己實(shí)現(xiàn)。示例如下:
到此為止,,探測并且注冊設(shè)備的代碼已經(jīng)完成,,以后對該 I2C 設(shè)備的訪問均可以通過 g_tvp5158_obj 這個(gè)全局的指針進(jìn)行了。 4. 注銷I2C設(shè)備 同理,,設(shè)備注銷的回調(diào)函數(shù)也會(huì)自動(dòng)被系統(tǒng)調(diào)用,,只需要按照模板寫好設(shè)備注銷代碼,示例如下:
到此為止,,設(shè)備的注冊和注銷代碼已經(jīng)全部完成,,下面要做的就是提供讀寫I2C設(shè)備的方法。 5. I2C設(shè)備的讀寫 對I2C設(shè)備的讀寫,,Linux系統(tǒng)提供了多種接口,,可以在內(nèi)核的 i2c.h 中找到,這里簡單介紹其中的兩種接口,。 【接口一】:
第一個(gè)參數(shù)是 i2c_client 對象指針,,第二個(gè)參數(shù)是要傳輸?shù)臄?shù)據(jù)buffer指針,第三個(gè)參數(shù)為buffer的大小,。 【接口二】:
這個(gè)接口支持一次向I2C設(shè)備發(fā)送多個(gè)消息,,每一個(gè)消息可以是讀也可以是寫,讀或者寫以及讀寫的目標(biāo)地址(寄存器地址)均包含在msg消息參數(shù)里面。 這些接口僅僅是最底層的讀寫方法,,關(guān)于具體怎么與I2C設(shè)備交互,,比如具體怎么讀芯片的某個(gè)特定寄存器的值,這需要看具體的芯片手冊,,每個(gè)I2C芯片都會(huì)有具體的I2C寄存器讀寫時(shí)序圖,。因此,為了在驅(qū)動(dòng)中提供更好的訪問接口,,還需要根據(jù)具體的時(shí)序要求對這些讀寫函數(shù)進(jìn)行進(jìn)一步封裝,,這些內(nèi)容將在后面的文章中講述。 6. 模塊初始化及其他 下一步就是整個(gè)模塊的初始化代碼和逆初始化代碼,,以及模塊聲明了,。
在初始化的代碼里面,添加本模塊的 i2c driver 對象,,在逆初始化代碼里面,,刪除本模塊的 i2c driver 對象。 7. 總結(jié) 到此為止,,算是從應(yīng)用的角度把編寫一個(gè)I2C的設(shè)備驅(qū)動(dòng)代碼講完了,,很多原理性的東西我都沒有具體分析(其實(shí)我也了解的不深),以后會(huì)慢慢更深入地學(xué)習(xí)和了解,,文中有什么講述不正確的地方,,歡迎留言或者來信[email protected]交流。 讀到最后,,大家可能還有一個(gè)疑問,,這個(gè)驅(qū)動(dòng)寫完了怎么在用戶空間(應(yīng)用層)去使用它呢?由于本文不想把代碼弄得太多太復(fù)雜,,怕提高理解的難度,,所以就沒有講,其實(shí)要想在用戶空間使用該I2C設(shè)備驅(qū)動(dòng),,則還需要借助字符設(shè)備驅(qū)動(dòng)來完成,,即為這個(gè)I2C設(shè)備驅(qū)動(dòng)封裝一層字符設(shè)備驅(qū)動(dòng),這樣,,用戶空間就可以通過對字符設(shè)備驅(qū)動(dòng)的訪問來訪問I2C設(shè)備,這個(gè)方法我會(huì)在后面的文章中講述,。 本文出自 “對影成三人” 博客,,請務(wù)必保留此出處http://ticktick.blog.51cto.com/823160/760020 |
|