久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    手把手教你寫Linux I2C設備驅動

     昵稱15515903 2014-08-14
    手把手教你寫Linux I2C設備驅動

        Linux I2C驅動是嵌入式Linux驅動開發人員經常需要編寫的一種驅動,因為凡是系統中使用到的I2C設備,幾乎都需要編寫相應的I2C驅動去配置和控制它,例如 RTC實時時鐘芯片、音視頻采集芯片、音視頻輸出芯片、EEROM芯片、AD/DA轉換芯片等等。

        Linux I2C驅動涉及的知識點還是挺多的,主要分為Linux I2C的總線驅動(I2C BUS Driver)和設備驅動(I2C Clients Driver),本文主要關注如何快速地完成一個具體的I2C設備驅動(I2C Clients Driver)。關于Linux I2C驅動的整體架構、核心原理等可以在網上搜索其他相關文章學習。

        注意:本系列文章的I2C設備驅動是基于Linux 2.6.18內核。

        本文主要參考了Linux內核源碼目錄下的 ./Documentation/i2c/writing-clients 文檔。以手頭的一款視頻采集芯片TVP5158為驅動目標,編寫Linux I2C設備驅動。

    1.   i2c_driver結構體對象     

        每一個I2C設備驅動,必須首先創造一個i2c_driver結構體對象,該結構體包含了I2C設備探測和注銷的一些基本方法和信息,示例如下:

    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字段標識本驅動的名稱(不要超過31個字符),attach_adapter和detach_client字段為函數指針,這兩個函數在I2C設備注冊的時候會自動調用,需要自己實現這兩個函數,后面將詳細講述。

    2.   i2c_client 結構體對象

        上面定義的i2c_driver對象,抽象為一個i2c的驅動模型,提供對i2C設備的探測和注銷方法,而i2c_client結構體則是代表著一個具體的i2c設備,該結構體有一個data指針,可以指向任何私有的設備數據,在復雜點的驅動中可能會用到。示例如下:   

    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為示例,用戶可以自己在tvp5158_obj這個結構體里面添加感興趣的字段,但是i2c_client字段不可少。具體用法后面再詳細講。

    3.   設備注冊及探測功能

        這一步很關鍵,按照標準的要求來寫,則Linux系統會自動調用相關的代碼去探測你的I2C設備,并且添加到系統的I2C設備列表中以供后面訪問。

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

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

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

        宏 I2C_CLIENT_INSMOD 的作用網上有許多文章進行了詳細的講解,這里我就不詳細描述了,記得加上就行,我們重點關注實現。

        下一步就應該編寫第1步中的兩個回調函數,一個用于注冊設備,一個用于注銷設備。探測函數示例如下:

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

        這個回調函數系統會自動調用,我們只需要按照上述代碼形式寫好就行,這里調用了系統的I2C設備探測函數,i2c_probe(),第三個參數為具體的設備探測回調函數,系統會在探測設備的時候調用這個函數,需要自己實現。示例如下:

    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;  

        到此為止,探測并且注冊設備的代碼已經完成,以后對該  I2C 設備的訪問均可以通過 g_tvp5158_obj 這個全局的指針進行了。

    4.    注銷I2C設備 

        同理,設備注銷的回調函數也會自動被系統調用,只需要按照模板寫好設備注銷代碼,示例如下:    

    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;  

        到此為止,設備的注冊和注銷代碼已經全部完成,下面要做的就是提供讀寫I2C設備的方法。

     5.   I2C設備的讀寫      

        對I2C設備的讀寫,Linux系統提供了多種接口,可以在內核的 i2c.h 中找到,這里簡單介紹其中的兩種接口。

       【接口一】:

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

        第一個參數是 i2c_client 對象指針,第二個參數是要傳輸的數據buffer指針,第三個參數為buffer的大小。

       【接口二】:

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

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

        這些接口僅僅是最底層的讀寫方法,關于具體怎么與I2C設備交互,比如具體怎么讀芯片的某個特定寄存器的值,這需要看具體的芯片手冊,每個I2C芯片都會有具體的I2C寄存器讀寫時序圖。因此,為了在驅動中提供更好的訪問接口,還需要根據具體的時序要求對這些讀寫函數進行進一步封裝,這些內容將在后面的文章中講述。

    6.  模塊初始化及其他

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

    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 對象,在逆初始化代碼里面,刪除本模塊的 i2c driver 對象。

    7.   總結

        到此為止,算是從應用的角度把編寫一個I2C的設備驅動代碼講完了,很多原理性的東西我都沒有具體分析(其實我也了解的不深),以后會慢慢更深入地學習和了解,文中有什么講述不正確的地方,歡迎留言或者來信lujun.hust@gmail.com交流。

        讀到最后,大家可能還有一個疑問,這個驅動寫完了怎么在用戶空間(應用層)去使用它呢?由于本文不想把代碼弄得太多太復雜,怕提高理解的難度,所以就沒有講,其實要想在用戶空間使用該I2C設備驅動,則還需要借助字符設備驅動來完成,即為這個I2C設備驅動封裝一層字符設備驅動,這樣,用戶空間就可以通過對字符設備驅動的訪問來訪問I2C設備,這個方法我會在后面的文章中講述。

    本文出自 “對影成三人” 博客,請務必保留此出處http://ticktick.blog.51cto.com/823160/760020

      本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發現有害或侵權內容,請點擊一鍵舉報。
      轉藏 分享 獻花(0

      0條評論

      發表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 婷婷综合久久中文字幕蜜桃三电影 | 久久久久亚洲AV成人网人人软件| 成人福利国产午夜AV免费不卡在线| 国产L精品国产亚洲区在线观看 | 熟妇啊轻点灬大JI巴太粗| 亚洲 自拍 另类 欧美 综合| 国产偷国产偷亚洲清高| 亚洲AV成人片不卡无码| 亚洲一区二区三区无码中文字幕 | 乱妇乱女熟妇熟女网站| 婷婷久久综合九色综合88| 国产精品久久久尹人香蕉| 国产女精品视频网站免费蜜芽| 无码成人午夜在线观看| 日韩人妻无码一区二区三区| 狠狠五月深爱婷婷网| 亚洲精品国自产拍在线观看| 少妇午夜啪爽嗷嗷叫视频| 少妇激情AV一区二区三区| 无码毛片一区二区本码视频| 噜噜综合亚洲av中文无码| 欧美日韩一区二区综合| 国产精品有码在线观看| 无码国产精品一区二区免费式芒果| 精品中文人妻在线不卡| 99久久99久久免费精品小说| 无码人妻丰满熟妇啪啪网不卡 | 野外做受三级视频| 亚洲欧美成人久久一区| 精品国产人妻一区二区三区久久 | 99久久激情国产精品| 亚洲综合无码精品一区二区三区| 不卡乱辈伦在线看中文字幕| 亚洲AV无码之国产精品网址| 任你躁国产自任一区二区三区| 中文字幕日韩人妻一区| 精品国产一区二区三区av性色 | 久久99热只有频精品6狠狠| 成人无码潮喷在线观看| 国产精品久久中文字幕| 色屁屁WWW影院免费观看入口|