久久婷婷香蕉热狠狠综合,精品无码国产自产拍在线观看蜜,寡妇房东在做爰3,中文字幕日本人妻久久久免费,国产成人精品三上悠亚久久

Hi,歡迎來到嵌入式培訓高端品牌 - 華清遠見教育科技集團<北京總部官網>,專注嵌入式工程師培養15年!
當前位置: > 華清遠見教育科技集團 > 嵌入式學習 > 講師博文 > 實例解析linux內核I2C體系結構(1)
實例解析linux內核I2C體系結構(1)
時間:2016-12-30作者:華清(qing)遠(yuan)見

一、概述

談(tan)到(dao)在linux系統下(xia)編寫(xie)I2C驅(qu)動,目前主要(yao)有兩(liang)種方式,一種是(shi)把(ba)I2C設備當(dang)作(zuo)一個(ge)普通的字符設備來(lai)處理,另一種是(shi)利(li)用linux I2C驅(qu)動體系結構來(lai)完成。下(xia)面比較下(xia)這兩(liang)種驅(qu)動。

第一種方法的好處(對應第二種方法的劣勢)有:
      ;  ●    思路比(bi)較直接(jie),不需(xu)要(yao)花時間去了解linux內核中復雜的I2C子系統的操作方法。

第一種方法問題(對應第二種方法的好處)有:
        ●    要求工程師不僅要對I2C設備的操作熟悉,而且要熟悉I2C的適配器操作;
        ●    要求工程師對I2C的設備器及I2C的設備操作方法都比較熟悉,重要的是寫出的程序可移植性差;
        ●    對(dui)內核(he)的(de)資源無(wu)法直接使用。因(yin)為內核(he)提供(gong)的(de)所有I2C設備器(qi)及設備驅(qu)動都(dou)是基(ji)于(yu)I2C子系(xi)統(tong)的(de)格式。I2C適配(pei)(pei)器(qi)的(de)操作簡單還好,如果遇到復雜的(de)I2C適配(pei)(pei)器(qi)(如:基(ji)于(yu)PCI的(de)I2C適配(pei)(pei)器(qi)),工作量就會大很(hen)多(duo)。

本(ben)文針對的(de)對象是熟悉I2C協議,并且想使用linux內核子系(xi)統的(de)開發人(ren)員。

網絡(luo)和一些書籍上有介紹I2C子(zi)系統的(de)源碼結構(gou)。但發現(xian)很多開(kai)發人員看了這些文章后(hou),還(huan)是不清楚自(zi)己究(jiu)竟該做些什么(me)。究(jiu)其原因還(huan)是沒弄清楚I2C子(zi)系統為我們做了些什么(me),以(yi)及(ji)我們怎樣利(li)(li)用(yong)I2C子(zi)系統。本(ben)文首先要解決(jue)是如何利(li)(li)用(yong)現(xian)有內(nei)核支持的(de)I2C適(shi)配器(qi),完成對I2C設備的(de)操作,然后(hou)再過度(du)到適(shi)配器(qi)代(dai)碼的(de)編寫。本(ben)文主要從解決(jue)問(wen)題的(de)角度(du)去(qu)寫,不會(hui)涉及(ji)特別(bie)詳細的(de)代(dai)碼跟(gen)蹤。

二、I2C設(she)備驅動程序編寫(xie)

首先要(yao)明確適(shi)配器驅動的作用是讓我們能夠(gou)通過(guo)它發出符合I2C標準協議的時序。

在Linux內核(he)源(yuan)代碼中的(de)(de)drivers/i2c/busses目錄下(xia)(xia)包含著一(yi)些適配器的(de)(de)驅動(dong)。如S3C2410的(de)(de)驅動(dong)i2c-s3c2410.c。當適配器加載到內核(he)后,接下(xia)(xia)來的(de)(de)工作就要針對具體的(de)(de)設備(bei)(bei)編寫設備(bei)(bei)驅動(dong)了。

編寫(xie)I2C設(she)(she)備(bei)(bei)(bei)驅(qu)動也有兩種方法(fa)。一(yi)種是(shi)利用(yong)系統給我們提供的(de)i2c-dev.c來實現(xian)一(yi)個i2c適配(pei)器的(de)設(she)(she)備(bei)(bei)(bei)文(wen)件(jian)。然后(hou)通過在應用(yong)層操(cao)作i2c適配(pei)器來控制i2c設(she)(she)備(bei)(bei)(bei)。另一(yi)種是(shi)為i2c設(she)(she)備(bei)(bei)(bei),獨立編寫(xie)一(yi)個設(she)(she)備(bei)(bei)(bei)驅(qu)動。注意:在后(hou)一(yi)種情況下,是(shi)不需要使用(yong)i2c-dev.c的(de)。

1、利用i2c-dev.c操作適配器,進而控制i2c設備

i2c-dev.c并沒有針對特定(ding)的(de)(de)設(she)備(bei)(bei)而設(she)計,只(zhi)是提供了通用的(de)(de)read()、write()和ioctl()等(deng)接(jie)口,應用層可(ke)以(yi)借用這(zhe)些(xie)接(jie)口訪問掛(gua)接(jie)在適配器上的(de)(de)i2c設(she)備(bei)(bei)的(de)(de)存(cun)儲空間或寄存(cun)器,并控制I2C設(she)備(bei)(bei)的(de)(de)工作(zuo)方式。

需要特(te)別(bie)注意的(de)是(shi):i2c-dev.c的(de)read()、write()方(fang)法都只適合于如(ru)下方(fang)式(shi)的(de)數(shu)據(ju)格式(shi)(可查(cha)看內核(he)相關源碼)

圖(tu)1 單開始信號(hao)時序

所(suo)以(yi)不(bu)具有太強的通用(yong)性,如下面這種情況就不(bu)適用(yong)(通常出現(xian)在讀目(mu)標時)。

圖2 多開始信(xin)號時序

而(er)且(qie)read()、write()方法只適(shi)用用于適(shi)配(pei)器支持(chi)i2c算法的情況(kuang),如:

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
            .master_xfer = s3c24xx_i2c_xfer,
            .functionality = s3c24xx_i2c_func,
  &nbsp;  &nbsp;  };

而不適合適配器只支持smbus算法的情況,如:
        static const struct i2c_algorithm smbus_algorithm = {
            .smbus_xfer = i801_access,
            .functionality = i801_func,
        };

基于上(shang)面(mian)幾個原因,所以一般(ban)都不會使用i2c-dev.c的(de)read()、write()方(fang)法。常(chang)用的(de)是ioctl()方(fang)法。ioctl()方(fang)法可以實現上(shang)面(mian)所有的(de)情況(兩種數據格式、以及I2C算法和(he)smbus算法)。

針對i2c的算法,需要熟悉struct i2c_rdwr_ioctl_data 、struct i2c_msg。使用的命令是I2C_RDWR。
        struct i2c_rdwr_ioctl_data {
            struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
            __u32 nmsgs; /* number of i2c_msgs */
        };
        struct i2c_msg {
            _ _u16 addr; /* slave address */
            _ _u16 flags; /* 標志(讀、寫) */ 
            _ _u16 len; /* msg length */
            _ _u8 *buf; /* pointer to msg data */
         };

針對smbus算法,需要熟悉struct i2c_smbus_ioctl_data。使用的命令是I2C_SMBUS。對于smbus算法,不需要考慮“多開始信號時序”問題。
        struct i2c_smbus_ioctl_data {
            __u8 read_write; //讀、寫
            __u8 command; //命令
            __u32 size; //數據長度標識
            union i2c_smbus_data __user *data; //數據
     ;   };

下面以一個實例講解操(cao)作的具體過程(cheng)。通過S3C2410操(cao)作AT24C02 e2prom。實現在AT24C02中任(ren)意(yi)位(wei)置的讀、寫(xie)功能。

首先在內核中已經包含了對s3c2410 中的i2c控制器驅動的支持。提供了i2c算法(非smbus類型的,所以后面的ioctl的命令是I2C_RDWR)
        static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
            .master_xfer = s3c24xx_i2c_xfer,
            .functionality = s3c24xx_i2c_func,
  &nbsp;     };

另(ling)外一方面需(xu)要(yao)確(que)定(ding)(ding)為(wei)了實現對AT24C02 e2prom的操作,需(xu)要(yao)確(que)定(ding)(ding)AT24C02的地址及讀寫訪問時(shi)序。

● ;       AT24C02地(di)址(zhi)的(de)確定

原(yuan)理圖上(shang)將(jiang)A2、A1、A0都接(jie)地了(le),所以地址是0x50。

●   &nbsp;    AT24C02任(ren)意地址字(zi)節(jie)寫的(de)時序

可見此時(shi)序符(fu)合前面提(ti)到的“單開(kai)始信號時(shi)序”

●        AT24C02任意地(di)址字節(jie)讀(du)的時序(xu)

可(ke)見此時序符合前面(mian)提(ti)到的“多開(kai)始信號時序”

下面開始具體代碼的分析(代碼在2.6.22內核上測試通過):
        /*i2c_test.c
        * hongtao_liu <lht@farsight.com.cn>
        */
        #include <stdio.h>
        #include <linux/types.h>
        #include <stdlib.h>
        #include <fcntl.h>
        #include <unistd.h>
        #include <sys/types.h>
        #include <sys/ioctl.h>
        #include <errno.h>
        #define I2C_RETRIES 0x0701
        #define I2C_TIMEOUT 0x0702
        #define I2C_RDWR 0x0707 
        /*********定義(yi)struct i2c_rdwr_ioctl_data和(he)struct i2c_msg,要和(he)內(nei)核(he)一致*******/

struct i2c_msg
        {
                unsigned short addr;
                unsigned short flags;
        #define I2C_M_TEN 0x0010
        #define I2C_M_RD 0x0001
                unsigned short len;
                unsigned char *buf;
    &nbsp;   };

struct i2c_rdwr_ioctl_data
        {
                struct i2c_msg *msgs;
                int nmsgs; 
        /* nmsgs這個數量決定了有多少開始信號,對于“單開始時序”,取1*/
        };

/***********主程序***********/
        int main()
        {
                int fd,ret;
                struct i2c_rdwr_ioctl_data e2prom_data;
                fd=open("/dev/i2c-0",O_RDWR);
        /*
        */dev/i2c-0是在注冊i2c-dev.c后產生的,代表一個可操作的適配器。如果不使用i2c-dev.c
        *的方式,就沒有,也不需要這個節點。
        */
                if(fd<0)
                {
                        perror("open error");
                }
                e2prom_data.nmsgs=2; 
        /*
        *因為操作時序中,多是用到2個開始信號(字節讀操作中),所以此將
        *e2prom_data.nmsgs配置為2
        */
                e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));
                if(!e2prom_data.msgs)
                {
                        perror("malloc error");
                        exit(1);
                }
                ioctl(fd,I2C_TIMEOUT,1);/*超時時間*/
                ioctl(fd,I2C_RETRIES,2);/*重復次數*/
                /***write data to e2prom**/

                e2prom_data.nmsgs=1;
                (e2prom_data.msgs[0]).len=2; //1個 e2prom 寫入目標的地址和1個數據 
                (e2prom_data.msgs[0]).addr=0x50;//e2prom 設備地址
                (e2prom_data.msgs[0]).flags=0; //write
                (e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);
                (e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 寫入目標的地址
  &nbsp; ;            (e2prom_data.msgs[0]).buf[1]=0x58;//the data to write

        ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
                if(ret<0)
                {
                        perror("ioctl error1");
                }
                sleep(1);
        /******read data from e2prom*******/
                e2prom_data.nmsgs=2;
                (e2prom_data.msgs[0]).len=1; //e2prom 目標數據的地址
                (e2prom_data.msgs[0]).addr=0x50; // e2prom 設備地址
                (e2prom_data.msgs[0]).flags=0;//write
                (e2prom_data.msgs[0]).buf[0]=0x10;//e2prom數據地址
                (e2prom_data.msgs[1]).len=1;//讀出的數據
                (e2prom_data.msgs[1]).addr=0x50;// e2prom 設備地址 
                (e2prom_data.msgs[1]).flags=I2C_M_RD;//read
                (e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。
               &nbsp;(e2prom_data.msgs[1]).buf[0]=0;//初始化讀(du)緩沖

        ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
                if(ret<0)
                {
                        perror("ioctl error2");
                }
                printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);
        /***打印讀出的值,沒錯的話,就應該是前面寫的0x58了***/
                close(fd);
                return 0;
  &nbsp; ;    }

以上講述了一種比較常用的利用i2c-dev.c操作i2c設備的方法,這種方法可以說是在應用層完成了對具體i2c設備的驅動工作。
計劃下一篇總結以下幾點:

(1)在內核里寫i2c設備驅動的兩(liang)種(zhong)方式:

●    Probe方式(new style),如:
                static struct i2c_driver pca953x_driver = {
                        .driver = {
                                .name = "pca953x",
                        },
                        .probe = pca953x_probe,
                        .remove = pca953x_remove,
                        .id_table = pca953x_id,
           &nbsp;    };

●    Adapter方式(LEGACY),如:
                static struct i2c_driver pcf8575_driver = {
                        .driver = {
                                .owner = THIS_MODULE,
                                .name = "pcf8575",
                        },
                        .attach_adapter = pcf8575_attach_adapter,
                        .detach_client = pcf8575_detach_client,
               &nbsp;};

(2)適配器驅動編寫方法

(3)分享一些項目中遇到的問題
        希望大家(jia)多提意(yi)見(jian),多多交流。

發表評論
評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)