I2C 驱动编程接口i2c_master_send()、i2c_master_recv()和i2c_transfer()比较

来源:互联网 发布:c语言中void 编辑:程序博客网 时间:2024/05/26 12:04

参考文章:http://blog.csdn.net/zclongembedded/article/details/8255977



一、数据包I2C_MSG

Name

struct i2c_msg — an I2C transaction segment beginning with START

struct <span style="color:#cc0000;">i2c_msg </span>{  __u16 addr;  __u16 flags;#define I2C_M_TEN0x0010#define I2C_M_RD0x0001#define I2C_M_NOSTART0x4000#define I2C_M_REV_DIR_ADDR0x2000#define I2C_M_IGNORE_NAK0x1000#define I2C_M_NO_RD_ACK0x0800#define I2C_M_RECV_LEN0x0400  __u16 len;  __u8 * buf;};  

Members

addr

Slave address, either seven or ten bits. When this is a ten bit address, I2C_M_TEN must be set in flags and the adapter must support I2C_FUNC_10BIT_ADDR.

flags

I2C_M_RD is handled by all adapters. No other flags may be provided unless the adapter exported the relevant I2C_FUNC_* flags through i2c_check_functionality.

len

Number of data bytes in buf being read from or written to the I2C slave address. For read transactions where I2C_M_RECV_LEN is set, the caller guarantees that this buffer can hold up to 32 bytes in addition to the initial length byte sent by the slave (plus, if used, the SMBus PEC); and this value will be incremented by the number of block data bytes received.

buf

The buffer into which data is read, or from which it's written.

Description

An i2c_msg is the low level representation of one segment of an I2C transaction. It is visible to drivers in the i2c_transfer() procedure, to userspace from i2c-dev, and to I2C adapter drivers through the i2c_adapter.master_xfer() method.

Except when I2C “protocol mangling” is used, all I2C adapters implement the standard rules for I2C transactions. Each transaction begins with a START. That is followed by the slave address, and a bit encoding read versus write. Then follow all the data bytes, possibly including a byte with SMBus PEC. The transfer terminates with a NAK, or when all those bytes have been transferred and ACKed. If this is the last message in a group, it is followed by a STOP. Otherwise it is followed by the next i2c_msg transaction segment, beginning with a (repeated) START.

Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then passing certain flags may have changed those standard protocol behaviors. Those flags are only for use with broken/nonconforming slaves, and with adapters which are known to support the specific mangling options they need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).

即:i2c发送或者接收一次数据都以数据包 struct i2c_msg 封装

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(本质是1);len是通信的数据字节数;buf 为发送或接收数据的指针地址。

二、i2c_master_send ()和 i2c_master_recv()

在设备驱动中我们通常调用 i2c-core 定义的接口 i2c_master_send() 和 i2c_master_recv() 来发送或接收一次数据。

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 (eg: 1 msg transmitted), return bytes number transmitted, else error code. 
    return (ret == 1) ? count : ret;           // 如果发送成功就返回字节数 

EXPORT_SYMBOL(i2c_master_send); 

参数介绍:client 为此次与主机通信的从机,buf 为发送的数据指针,count 为发送数据的字节数。

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 , return number of bytes transmitted, else error code. */ 
    return (ret == 1) ? count : ret;           // 如果接收成功就返回字节数 

EXPORT_SYMBOL(i2c_master_recv); 

参数介绍:client 为此次与主机通信的从机,buf 为接收的数据指针,count 为接收数据的字节数。

三、 i2c_transfer 接口的参数说明:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); 
其中 adap 为主机与从机通信的适配器;msgs 为通信的数据包,可以是单个或多个数据包;num 用于指定数据包的个数,如果大于1则表明将进行不止一次的通信。通信一次就需要寻址一次,如果需要多次通信就需要多次寻址,前面2个接口都是进行一次通信,所以 num 为1;有的情况下我们要读一个寄存器的值,就需要先向从机发送一个寄存器地址然后再接收数据,这样如果想自己封装一个接口就需要将 num 设置为2。接口的返回值如果失败则为负数,如果成功则返回传输的数据包个数。比如读一个寄存器的值的接口可以如下封装:
static int readI2cReg(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,  // 该地址用来存储reg寄存器的值 
        }, 
    }; 
 
    ret = i2c_transfer(client->adapter, msgs, 2);  // 这里 num = 2,通信成功 ret = 2 
    if (ret < 0) 
        print_err("%s error: %d\n", __func__, ret);
    return ret; 

还可调用前面所述的接口封装:
static unsigned char readI2cReg(struct i2c_client *client, unsigned char reg) 

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

四、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:
/**
 * 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解锁的参考代码如下:

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; 
}
0 0
原创粉丝点击