程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> i2c 驅動編程接口

i2c 驅動編程接口

編輯:C++入門知識

1、通信接口
i2c發送或者接收一次數據都以數據包 struct i2c_msg 封裝
[cpp] 
struct i2c_msg { 
    __u16 addr;     // 從機地址 
    __u16 flags;    // 標志 
#define I2C_M_TEN   0x0010  // 十位地址標志 
#define I2C_M_RD    0x0001  // 接收數據標志 
    __u16 len;      // 數據長度 
    __u8 *buf;      // 數據指針 
}; 
其中addr為從機地址;flags則是這次通信的標志,發送數據為0,接收數據則為 I2C_M_RD;len為此次通信的數據字節數;buf 為發送或接收數據的指針。在設備驅動中我們通常調用 i2c-core 定義的接口 i2c_master_send 和 i2c_master_recv 來發送或接收一次數據。
[cpp]
int i2c_master_send(struct i2c_client *client,const char *buf ,int count) 

    int ret; 
    struct i2c_adapter *adap=client->adapter;  // 獲取adapter信息 
    struct i2c_msg msg;                        // 定義一個臨時的數據包 
 
    msg.addr = client->addr;                   // 將從機地址寫入數據包 
    msg.flags = client->flags & I2C_M_TEN;     // 將從機標志並入數據包 
    msg.len = count;                           // 將此次發送的數據字節數寫入數據包 
    msg.buf = (char *)buf;                     // 將發送數據指針寫入數據包 
 
    ret = i2c_transfer(adap, &msg, 1);         // 調用平台接口發送數據 
 
    /* If everything went ok (i.e. 1 msg transmitted), return #bytes
       transmitted, else error code. */ 
    return (ret == 1) ? count : ret;           // 如果發送成功就返回字節數 

EXPORT_SYMBOL(i2c_master_send); 
i2c_master_send 接口的三個參數:client 為此次與主機通信的從機,buf 為發送的數據指針,count 為發送數據的字節數。
[cpp] 
int i2c_master_recv(struct i2c_client *client, char *buf ,int count) 

    struct i2c_adapter *adap=client->adapter;  // 獲取adapter信息 
    struct i2c_msg msg;                        // 定義一個臨時的數據包 
    int ret; 
 
    msg.addr = client->addr;                   // 將從機地址寫入數據包 
    msg.flags = client->flags & I2C_M_TEN;     // 將從機標志並入數據包 
    msg.flags |= I2C_M_RD;                     // 將此次通信的標志並入數據包 
    msg.len = count;                           // 將此次接收的數據字節數寫入數據包 
    msg.buf = buf; 
 
    ret = i2c_transfer(adap, &msg, 1);         // 調用平台接口接收數據 
 
    /* If everything went ok (i.e. 1 msg transmitted), return #bytes
       transmitted, else error code. */ 
    return (ret == 1) ? count : ret;           // 如果接收成功就返回字節數 

EXPORT_SYMBOL(i2c_master_recv); 
i2c_master_recv 接口的三個參數:client 為此次與主機通信的從機,buf 為接收的數據指針,count 為接收數據的字節數。我們看一下 i2c_transfer 接口的參數說明:
[cpp
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); 
其中 adap 為此次主機與從機通信的適配器;msgs 為通信的數據包,這裡可以是單個或多個數據包;num 用於指定數據包的個數,如果大於1則表明將進行不止一次的通信。通信一次就需要尋址一次,如果需要多次通信就需要多次尋址,前面2個接口都是進行一次通信,所以 num 為1;有的情況下我們要讀一個寄存器的值,就需要先向從機發送一個寄存器地址然後再接收數據,這樣如果想自己封裝一個接口就需要將 num 設置為2。接口的返回值如果失敗則為負數,如果成功則返回傳輸的數據包個數。比如讀一個寄存器的接口可以按照如下方式封裝:
[cpp] 
static int read_reg(struct i2c_client *client, unsigned char reg, unsigned char *data) 

    int ret; 
 
    struct i2c_msg msgs[] = { 
        { 
            .addr   = client->addr, 
            .flags  = 0, 
            .len    = 1, 
            .buf    = &reg,  // 寄存器地址 
        }, 
        { 
            .addr   = client->addr, 
            .flags  = I2C_M_RD, 
            .len    = 1, 
            .buf    = data,  // 寄存器的值 
        }, 
    }; 
 
    ret = i2c_transfer(client->adapter, msgs, 2);  // 這裡 num = 2,通信成功 ret = 2 
    if (ret < 0) 
        tp_err("%s error: %d\n", __func__, ret); 
 
    return ret; 

還可調用前面所述的接口封裝:
[cpp] 
static unsigned char read_reg(struct i2c_client *client, unsigned char reg) 

    unsigned char buf; 
 
    i2c_master_send(client, &reg, 1);  // 發送寄存器地址 
    i2c_master_recv(client, &buf, 1);  // 接收寄存器的值 
 
    return  buf; 

2、reset 接口
最近因為平台的i2c總線經常發生死鎖,用邏輯分析儀檢測發現通常為SDA和SCL都被拉低,於是在i2c-core中加入了reset機制,總體思路如下:
(1)在i2c.driver和i2c.adapter的結構中加入reset接口,即每一個i2c設備都可以注冊reset函數,每條i2c總線都有相應的reset接口
(2)當發生死鎖時,首先根據i2c-timeout的信息獲取當前通信的設備地址和總線編號,然後依次執行當前總線下所有i2c設備的reset函數,再嘗試發送是否成功;如果總線仍然處於死鎖狀態則執行i2c.adapter的reset函數;如果總線還是處於死鎖狀態就重啟機器;總共3層reset機制
(3)i2c.driver的reset函數一般操作設備的reset pin或者電源(需要根據硬件設計進行相應操作)
(4)i2c.adapter的reset函數首選進行SCL的模擬解鎖方案,然後再是操作整個總線上設備的電源(需要根據硬件設計進行相應操作)
(5)重啟是最後的一層機制,此時無法恢復設備的正常使用就只能重啟了
因為i2c.adapter層的需要,在i2c-core中加入了遍歷當前總線所有設備並執行設備reset函數的接口i2c_reset_device:
[cpp]
/**
 * i2c_reset_device - reset I2C device when bus dead
 * @adapter: the adapter being reset
 * @addr: the device address
 */ 
static int __i2c_reset_device(struct device *dev, void *addrp) 

    struct i2c_client *client = to_i2c_client(dev); 
    int addr = *(int *)addrp; 
 
    if (client && client->driver && client->driver->reset) 
        return client->driver->reset(); 
 
    return 0; 

 
int i2c_reset_device(struct i2c_adapter *adapter, int addr) 

    return device_for_each_child(&adapter->dev, &addr, __i2c_reset_device); 

EXPORT_SYMBOL(i2c_reset_device); 
需要注意的是i2c.driver的reset函數返回值需要為0,不然device_for_each_child不會繼續後面的遍歷。用GPIO模擬SCL解鎖的參考代碼如下:
[cpp] 
static int i2c_reset_adapter(void) 

    int counter = 0; 
 
    gpio_request(I2C_BUS_DATA, "gpioxx"); 
    gpio_request(I2C_BUS_CLK, "gpioxx"); 
    /* try to recover I2C bus */ 
    gpio_direction_input(I2C_BUS_DATA); 
 
    if (!__gpio_get_value(I2C_BUS_DATA)) { 
        while((!__gpio_get_value(I2C_BUS_DATA)) && ++counter < 10) 
        { 
            udelay(5); 
            gpio_direction_output(I2C_BUS_CLK, 1); 
            udelay(5); 
            gpio_direction_output(I2C_BUS_CLK, 0); 
        } 
        i2c_err("try to recover i2c bus, retry times are %d\n",counter); 
        if (counter < 10) { 
            udelay(5); 
            gpio_direction_output(I2C_BUS_DATA, 0); 
            udelay(5); 
            gpio_direction_output(I2C_BUS_CLK, 1); 
            udelay(5); 
            gpio_direction_output(I2C_BUS_DATA, 1); 
            msleep(10); 
        } else { 
            i2c_err("try to recover i2c bus failed!\n"); 
        } 
    } 
 
    gpio_free(I2C_BUS_DATA); 
    gpio_free(I2C_BUS_CLK); 
 
    return 0; 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved