I2C | i2c_msg

I2C | i2c_msg文章目录i2c协议的规定,host和client的通信每次都是由host主动发起,而且每次发起都要事先发送clientaddr做寻址操作。一个i2c_msg是Slave(i2cclient)和Host(i2ccontroller)的一次单向数据传输。常见的Slave有Touchscreen,Sensor;i2ccontroller实际上就是SOCARM上的一组i2cregiste…

I2C

背景介绍:

这两天在解决客户的一个i2c传输问题时发现对i2c的理解有很多盲点,冒出一身冷汗- -!,问题解决后赶紧复盘总结一下。

带着问题去学习是最快的方法
思考题

  1. 为什么常见的i2c外设驱动中 i2c read 函数要构造 2个 i2c_msg,而 i2c write 函数只要1个i2c_msg?
  2. 常见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函数实现方式如下:

  1. 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;
}

  1. 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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注