背景介绍:
这两天在解决客户的一个i2c传输问题时发现对i2c的理解有很多盲点,冒出一身冷汗- -!,问题解决后赶紧复盘总结一下。
带着问题去学习是最快的方法
思考题
:
- 为什么常见的i2c外设驱动中 i2c read 函数要构造 2个 i2c_msg,而 i2c write 函数只要1个i2c_msg?
- 常见i2c外设寄存器地址是8bits的,那么遇到16bits或者32bits的外设寄存器地址该如何构造i2c_msg呢?
如果以上问题你已经熟知,那么大佬,接下来的内容可以忽略不看了,如果有疑问,不妨在小店驻留几分钟。
一. 对 i2c_msg 的理解我认为应该分为两个主要层面:
- i2c_msg的设计角度:
一个i2c_msg
代表着Slave
(i2c client)和Host
(i2c controller)之间的一次单向数据传输
。 - 支持i2c传输协议的外设驱动程序使用 i2c_msg 的角度:
这里又分为四点- i2c 传输是以字节为单位的,具体到i2c_msg.len 指的是以字节指针i2c_msg.buf指向的buffer中字节个数。
- 外设驱动要实现 xxx_i2c_read_bytes 和 xxx_i2c_write_bytes 两个底层函数;
- xxx_i2c_read_bytes 由
2个i2c_msg组成的数组
构成
因为这存在host send和host receive两个数据传输方向;第一个msg.buf用来暂存host向slave发出slave目标寄存器地址,msg.len表示寄存器地址字节长度;第二个msg.buf用来接收slave向host返回数据,msg.len表示期望读到数据的字节长度; - xxx_i2c_write_bytes 仅由
1个i2c_msg
构成
因为只有host send to slave这一个数据传输方向;整个msg.buf暂存了从机目标寄存器地址和待写入其中的数据,msg.len表示整体的字节长度;
(注:数组的目的是为了访问连续,因为数组是连续内存存储的)
注:常见的Slave有Touchscreen,Sensor;i2c controller实际上就是SOC ARM上的一组i2c registers。
二. 常见的Slave 驱动程序对 i2c read和write函数实现方式如下:
- xxx_i2c_read_bytes
(host 发出 slave 地址来建立通信)(host驱动中实现)
host 向 slave 发出 slave 目标寄存器地址;(salve驱动中实现)
slave 向 host 返回 目标寄存器数据;(salve驱动中实现)
static int xxx_i2c_read_bytes(u8 index, u8 *rx_buff, u8 length)
{
int ret = -1;
struct i2c_msg msgs[] = {
/* 注:i2c read因为包含两个方向,所以只需要两个i2c_msg */
{
//第一个i2c_msg用来发送需要读取的从设备目标寄存器的地址
.addr = xxx_client->addr, /* chip address, 7bit */
.flags = 0, /* write */
.len = lenght1, /* bytes number(注意这里一定是byte为单位),这也是从设备目标寄存器地址的字节长度*/
.buf = &index, /* client register address pointer */
},
{
//第二个i2c_msg用来设置接收从设备目标寄存器返回数据的buff以及数据长度
.addr = xxx_client->addr,
.flags = I2C_M_RD, /* read */
.len = length2, /* bytes number(注意这里一定是byte为单位)*/
.buf = rx_buff, /* 用来接收数据的缓冲区指针*/
},
};
ret = i2c_transfer(this_client->adapter, msgs, 2); /* i2c host master_xfer callback func */
if (ret != 2)
PRINT_ERR("READ ERROR!ret=%d\n", ret);
return ret;
}
- xxx_i2c_write_bytes
(host 发出 slave 地址来建立通信)(host 驱动中实现)
host 向 slave 发出 slave 目标寄存器地址以及需要往目标寄存器中写入的数据;(salve驱动中实现)
static int xxx_i2c_write_bytes(u8 *tx_buff, u8 length)
{
int ret = -1;
struct i2c_msg msgs[1]; /*注:i2c write因为是单方向,所以只需要一个i2c_msg*/
msgs[0].addr = xxx_client->addr;
msgs[0].flags = 0; /* write */
msgs[0].len = length; /* bytes number(注意这里一定是byte为单位)*/
msgs[0].buf = tx_buff; /* 待写出的数据指针;数据通常为 client register address + data*/
ret = i2c_transfer(xxx_client->adapter, msgs, 1); /* i2c host master_xfer callback func */
if (ret != 1)
PRINT_ERR("WRITE ERROR!ret=%d\n", ret);
return ret;
}
总结:
好了,回答前面提出的两个问题:
第一题:一个i2c_msg代表着一次单项数据传输。因为i2c read包含host向slave发送寄存器地址和slave返回数据两个数据传输方向,所以需要两个i2c_msg;而 i2c write时host向slave发送寄存器地址和host往slave寄存器写数据属于同一个数据传输方向,所以只需要一个i2c_msg。
第二题:举例slave寄存器地址为16bits的解决方案,32bits的类似。当slave寄存器地址是16bits时,slave驱动构造的i2c read函数中的第一个i2c_msg对应的len成员表示寄存器地址字节长度,buf成员传入的是寄存器其地址指针;i2c write函数中方法同i2c read,只不过buf后面还跟着待写入寄存器的数据。
今天的文章I2C | i2c_msg分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/33776.html