最近在linux下移植i2c的传感器驱动。移植了才发现各个设备的i2c读写都不太一样,对于这几个方式我做一个小总结。 首先我使用的是linux应用层通用的i2c读写,我的读写默认是使用smbus协议进行读写的。

smbus协议

先放一张图片合集,接下来就逐一看看这几种方式。

smbus Send Byte

这个方式比较少用到,一般我们的传感器都有地址位以及数据位,所以至少传输两位。但是我在使用SHT31这个传感器的就需要用到此方式。先看看SHT31寄存器的读写:

这里发现这个传感器没有寄存器地址,但是也要发两位数据,而且是高位先行。 所以这个时候就需要改写对应的读写函数如下:

直接按高低位写两位数据即可。

当然这里我也存在一个疑问,直接调用write函数,对应的i2c输出应该是一个地址位,一个数据位,但是我两次调用write函数,应该会有四个byte信号输出,但是按照读写情况,还是十分正常的。

DrvStatus_t SHT31_writeCommand(uint16_t cmd) {
uint8_t data[2];
data[0] = cmd >> 8, data[1] = cmd & 0x00FF;
if (write(sht31Fd, data, 2) < 0)
return COMPONENT_ERROR;
return COMPONENT_OK;
}

smbus Write Byte

这个应该是使用最多的的了。写一个数据到对应的寄存器中。这个就不赘述了。

smbus Write Word

对于smbus的读取和写入16位都应该算是上一操作的扩展,但是有时候也有不按常理出牌的地方。 因为标准的smbus协议读取16位数据是,都是默认先读到的为低8位,之后读取到的是高8位。 在使用smbus协议读取fdc2214传感器设备id就会出现问题。

fdc2214传感器读取协议:

可以发现他的读取是默认先高位,再低位。

所以我们在使用smbus协议读取到数据后要做好数据高低转换的措施:

/* 读取16位 先读高8位再读低8位 */
static uint16_t read16(uint8_t reg) {
/* 读取两字节 bmp280默认先输出高位再输出低位 */
/* smbus协议默认先低位再高位 所以需要转换 */
uint16_t temp = (uint16_t)i2c_smbus_read_word_data(bmp280fd, reg);
return temp >> 8 | temp << 8;
}

i2c传输位顺序

值得一提的是i2c传输中的每一位都是高位先行的,也只有这样我们接收到msb的数据后直接8位交换位置即可。

比如设备id为 0x3055 其二进制为:0011 0000 0101 0101 发送顺序为:0011 0000 0101 0101

smbus接收到后为: 高八位 0011 0000 -> 低八位 0011 0000
低八位 0101 0101 -> 高八位 0101 0101

拼接==> 0101 0101 0011 0000 = 0x5530

交换==> 0x5530 = 0x3055