日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

手把手教你寫(xiě)Linux I2C設(shè)備驅(qū)動(dòng)

 昵稱(chēng)15515903 2014-08-14
手把手教你寫(xiě)Linux I2C設(shè)備驅(qū)動(dòng)

    Linux I2C驅(qū)動(dòng)是嵌入式Linux驅(qū)動(dòng)開(kāi)發(fā)人員經(jīng)常需要編寫(xiě)的一種驅(qū)動(dòng),因?yàn)榉彩窍到y(tǒng)中使用到的I2C設(shè)備,幾乎都需要編寫(xiě)相應(yīng)的I2C驅(qū)動(dòng)去配置和控制它,例如 RTC實(shí)時(shí)時(shí)鐘芯片、音視頻采集芯片、音視頻輸出芯片、EEROM芯片、AD/DA轉(zhuǎn)換芯片等等。

    Linux I2C驅(qū)動(dòng)涉及的知識(shí)點(diǎn)還是挺多的,主要分為L(zhǎng)inux I2C的總線(xiàn)驅(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),編寫(xiě)Linux I2C設(shè)備驅(qū)動(dòng)。

1.   i2c_driver結(jié)構(gòu)體對(duì)象     

    每一個(gè)I2C設(shè)備驅(qū)動(dòng),必須首先創(chuàng)造一個(gè)i2c_driver結(jié)構(gòu)體對(duì)象,該結(jié)構(gòu)體包含了I2C設(shè)備探測(cè)和注銷(xiāo)的一些基本方法和信息,示例如下:

  1. static struct i2c_driver tvp5158_i2c_driver = {  
  2.         .driver = {  
  3.             .name = "tvp5158_i2c_driver",  
  4.         },   
  5.         .attach_adapter = &tvp5158_attach_adapter,  
  6.         .detach_client  = &tvp5158_detach_client,  
  7.         .command        = NULL,  
  8. };  

    其中,name字段標(biāo)識(shí)本驅(qū)動(dòng)的名稱(chēng)(不要超過(guò)31個(gè)字符),attach_adapter和detach_client字段為函數(shù)指針,這兩個(gè)函數(shù)在I2C設(shè)備注冊(cè)的時(shí)候會(huì)自動(dòng)調(diào)用,需要自己實(shí)現(xiàn)這兩個(gè)函數(shù),后面將詳細(xì)講述。

2.   i2c_client 結(jié)構(gòu)體對(duì)象

    上面定義的i2c_driver對(duì)象,抽象為一個(gè)i2c的驅(qū)動(dòng)模型,提供對(duì)i2C設(shè)備的探測(cè)和注銷(xiāo)方法,而i2c_client結(jié)構(gòu)體則是代表著一個(gè)具體的i2c設(shè)備,該結(jié)構(gòu)體有一個(gè)data指針,可以指向任何私有的設(shè)備數(shù)據(jù),在復(fù)雜點(diǎn)的驅(qū)動(dòng)中可能會(huì)用到。示例如下:   

  1. struct tvp5158_obj{        
  2.     struct i2c_client client;        
  3.     int users; // how many users using the driver    
  4. };  
  5.  
  6. struct tvp5158_obj* g_tvp5158_obj;  

    其中,users為示例,用戶(hù)可以自己在tvp5158_obj這個(gè)結(jié)構(gòu)體里面添加感興趣的字段,但是i2c_client字段不可少。具體用法后面再詳細(xì)講。

3.   設(shè)備注冊(cè)及探測(cè)功能

    這一步很關(guān)鍵,按照標(biāo)準(zhǔn)的要求來(lái)寫(xiě),則Linux系統(tǒng)會(huì)自動(dòng)調(diào)用相關(guān)的代碼去探測(cè)你的I2C設(shè)備,并且添加到系統(tǒng)的I2C設(shè)備列表中以供后面訪(fǎng)問(wèn)。

    我們知道,每一個(gè)I2C設(shè)備芯片,都通過(guò)硬件連接設(shè)定好了該設(shè)備的I2C設(shè)備地址。因此,I2C設(shè)備的探測(cè)一般是靠設(shè)備地址來(lái)完成的。那么,首先要在驅(qū)動(dòng)代碼中聲明你要探測(cè)的I2C設(shè)備地址列表,以及一個(gè)宏。示例如下:

  1. static unsigned short normal_i2c[] = {  
  2.         0xbc >> 1,  
  3.         0xbe >> 1,  
  4.         I2C_CLIENT_END  
  5. };  
  6. I2C_CLIENT_INSMOD;  

    normal_i2c 數(shù)組包含了你需要探測(cè)的I2C設(shè)備地址列表,并且必須以I2C_CLIENT_END作為結(jié)尾,注意,上述代碼中的0xbc和0xbe是我在硬件上為我的tvp5158分配的地址,硬件上我支持通過(guò)跳線(xiàn)將該地址設(shè)置為 0xbc 或者 0xbe,所以把這兩個(gè)地址均寫(xiě)入到探測(cè)列表中,讓系統(tǒng)進(jìn)行探測(cè)。如果你的I2C設(shè)備的地址是固定的,那么,這里可以只寫(xiě)你自己的I2C設(shè)備地址,注意必須向右移位1。

    宏 I2C_CLIENT_INSMOD 的作用網(wǎng)上有許多文章進(jìn)行了詳細(xì)的講解,這里我就不詳細(xì)描述了,記得加上就行,我們重點(diǎn)關(guān)注實(shí)現(xiàn)。

    下一步就應(yīng)該編寫(xiě)第1步中的兩個(gè)回調(diào)函數(shù),一個(gè)用于注冊(cè)設(shè)備,一個(gè)用于注銷(xiāo)設(shè)備。探測(cè)函數(shù)示例如下:

  1. static int tvp5158_attach_adapter(struct i2c_adapter *adapter)  
  2. {  
  3.     return i2c_probe(adapter, &addr_data, &tvp5158_detect_client);  

    這個(gè)回調(diào)函數(shù)系統(tǒng)會(huì)自動(dòng)調(diào)用,我們只需要按照上述代碼形式寫(xiě)好就行,這里調(diào)用了系統(tǒng)的I2C設(shè)備探測(cè)函數(shù),i2c_probe(),第三個(gè)參數(shù)為具體的設(shè)備探測(cè)回調(diào)函數(shù),系統(tǒng)會(huì)在探測(cè)設(shè)備的時(shí)候調(diào)用這個(gè)函數(shù),需要自己實(shí)現(xiàn)。示例如下:

  1. static int tvp5158_detect_client(struct i2c_adapter *adapter,int address,int kind)  
  2. {  
  3.     struct tvp5158_obj *pObj;  
  4.     int err = 0;  
  5.  
  6.     printk(KERN_INFO "I2C: tvp5158_detect_client at address %x ...\n", address);  
  7.  
  8.     if( g_tvp5158_obj != NULL  ) {  
  9.         //already allocated,inc user count, and return the allocated handle  
  10.         g_tvp5158_obj->users++;  
  11.         return 0;  
  12.     }  
  13.  
  14.     /* alloc obj */ 
  15.     pObj = kmalloc(sizeof(struct tvp5158_obj), GFP_KERNEL);  
  16.     if (pObj==0){  
  17.         return -ENOMEM;  
  18.     }  
  19.     memset(pObj, 0, sizeof(struct tvp5158_obj));  
  20.     pObj->client.addr    = address;  
  21.     pObj->client.adapter = adapter;  
  22.     pObj->client.driver  = &tvp5158_i2c_driver;  
  23.     pObj->client.flags   = I2C_CLIENT_ALLOW_USE;  
  24.     pObj->users++;  
  25.  
  26.     /* attach i2c client to sys i2c clients list */ 
  27.     if((err = i2c_attach_client(&pObj->client))){  
  28.         printk( KERN_ERR "I2C: ERROR: i2c_attach_client fail! address=%x\n",address);  
  29.         return err;  
  30.     }  
  31.  
  32.     // store the pObj  
  33.     g_tvp5158_obj = pObj;  
  34.  
  35.     printk( KERN_ERR "I2C: i2c_attach_client ok! address=%x\n",address);  
  36.  
  37.     return 0;  

    到此為止,探測(cè)并且注冊(cè)設(shè)備的代碼已經(jīng)完成,以后對(duì)該  I2C 設(shè)備的訪(fǎng)問(wèn)均可以通過(guò) g_tvp5158_obj 這個(gè)全局的指針進(jìn)行了。

4.    注銷(xiāo)I2C設(shè)備 

    同理,設(shè)備注銷(xiāo)的回調(diào)函數(shù)也會(huì)自動(dòng)被系統(tǒng)調(diào)用,只需要按照模板寫(xiě)好設(shè)備注銷(xiāo)代碼,示例如下:    

  1. static int tvp5158_detach_client(struct i2c_client *client)  
  2. {  
  3.     int err;  
  4.  
  5.     if( ! client->adapter ){  
  6.         return -ENODEV;  
  7.     }  
  8.  
  9.     if( (err = i2c_detach_client(client)) ) {  
  10.         printk( KERN_ERR "Client deregistration failed (address=%x), client not detached.\n", client->addr);  
  11.         return err;  
  12.     }  
  13.  
  14.     client->adapter = NULL;  
  15.  
  16.     if( g_tvp5158_obj ){  
  17.         kfree(g_tvp5158_obj);  
  18.     }  
  19.  
  20.     return 0;  

    到此為止,設(shè)備的注冊(cè)和注銷(xiāo)代碼已經(jīng)全部完成,下面要做的就是提供讀寫(xiě)I2C設(shè)備的方法。

 5.   I2C設(shè)備的讀寫(xiě)      

    對(duì)I2C設(shè)備的讀寫(xiě),Linux系統(tǒng)提供了多種接口,可以在內(nèi)核的 i2c.h 中找到,這里簡(jiǎn)單介紹其中的兩種接口。

   【接口一】:

  1. extern int i2c_master_send(struct i2c_client *,const char* ,int);  
  2.  
  3. extern int i2c_master_recv(struct i2c_client *,char* ,int);  

    第一個(gè)參數(shù)是 i2c_client 對(duì)象指針,第二個(gè)參數(shù)是要傳輸?shù)臄?shù)據(jù)buffer指針,第三個(gè)參數(shù)為buffer的大小。

   【接口二】:

  1. extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num); 

    這個(gè)接口支持一次向I2C設(shè)備發(fā)送多個(gè)消息,每一個(gè)消息可以是讀也可以是寫(xiě),讀或者寫(xiě)以及讀寫(xiě)的目標(biāo)地址(寄存器地址)均包含在msg消息參數(shù)里面。

    這些接口僅僅是最底層的讀寫(xiě)方法,關(guān)于具體怎么與I2C設(shè)備交互,比如具體怎么讀芯片的某個(gè)特定寄存器的值,這需要看具體的芯片手冊(cè),每個(gè)I2C芯片都會(huì)有具體的I2C寄存器讀寫(xiě)時(shí)序圖。因此,為了在驅(qū)動(dòng)中提供更好的訪(fǎng)問(wèn)接口,還需要根據(jù)具體的時(shí)序要求對(duì)這些讀寫(xiě)函數(shù)進(jìn)行進(jìn)一步封裝,這些內(nèi)容將在后面的文章中講述。

6.  模塊初始化及其他

    下一步就是整個(gè)模塊的初始化代碼和逆初始化代碼,以及模塊聲明了。    

  1. static int __init tvp5158_i2c_init(void
  2.     g_tvp5158_obj = NULL; 
  3.      
  4.     return i2c_add_driver(&tvp5158_i2c_driver); 
  5.  
  6. static void __exit tvp5158_i2c_exit(void
  7.     i2c_del_driver(&tvp5158_i2c_driver); 
  8.  
  9. module_init(tvp5158_i2c_init); 
  10. module_exit(tvp5158_i2c_exit); 
  11.  
  12. MODULE_DESCRIPTION("TVP5158 i2c driver"); 
  13. MODULE_AUTHOR("Lujun @hust"); 
  14. MODULE_LICENSE("GPL"); 

    在初始化的代碼里面,添加本模塊的 i2c driver 對(duì)象,在逆初始化代碼里面,刪除本模塊的 i2c driver 對(duì)象。

7.   總結(jié)

    到此為止,算是從應(yīng)用的角度把編寫(xiě)一個(gè)I2C的設(shè)備驅(qū)動(dòng)代碼講完了,很多原理性的東西我都沒(méi)有具體分析(其實(shí)我也了解的不深),以后會(huì)慢慢更深入地學(xué)習(xí)和了解,文中有什么講述不正確的地方,歡迎留言或者來(lái)信lujun.hust@gmail.com交流。

    讀到最后,大家可能還有一個(gè)疑問(wèn),這個(gè)驅(qū)動(dòng)寫(xiě)完了怎么在用戶(hù)空間(應(yīng)用層)去使用它呢?由于本文不想把代碼弄得太多太復(fù)雜,怕提高理解的難度,所以就沒(méi)有講,其實(shí)要想在用戶(hù)空間使用該I2C設(shè)備驅(qū)動(dòng),則還需要借助字符設(shè)備驅(qū)動(dòng)來(lái)完成,即為這個(gè)I2C設(shè)備驅(qū)動(dòng)封裝一層字符設(shè)備驅(qū)動(dòng),這樣,用戶(hù)空間就可以通過(guò)對(duì)字符設(shè)備驅(qū)動(dòng)的訪(fǎng)問(wèn)來(lái)訪(fǎng)問(wèn)I2C設(shè)備,這個(gè)方法我會(huì)在后面的文章中講述。

本文出自 “對(duì)影成三人” 博客,請(qǐng)務(wù)必保留此出處http://ticktick.blog.51cto.com/823160/760020

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多