I2C操作
[!Note]
1.目前仅SV50P系列模组支持该功能。
2.使用前,需要在模组配置中使能 TWI 功能,用生成的新系统包升级,才能正常使用。
3.更多有关模组的使用教程。
引入头文件
#include "utils/I2CHelper.h"
具体操作
#include "utils/I2CHelper.h"
#define CFG_L 0x47
#define CFG_H 0x80
#define VER_L 0x41
#define VER_H 0x81
static void testI2C() {
uint8_t tx[512], rx[512];
memset(tx, 0, 512);
memset(rx, 0, 512);
/**
* 定义变量
*
* 参数1: i2c总线号
* 参数2: 从机地址, 一定要注意是 7bit地址
* 参数3: 超时时间,最小10ms 单位: ms
* 参数4: 重试次数
*/
I2CHelper i2c(0, 0x5e, 1000, 5);
tx[0] = CFG_H;
tx[1] = CFG_L;
/**
* 单工写
*
* 参数1: 写数据地址
* 参数2: 数据长度
*/
if (!i2c.write(tx, 2)) {
LOGD("i2c tx cfg error!\n");
}
/**
* 单工读
*
* 参数1: 读数据地址
* 参数2: 数据长度
*/
if (!i2c.read(rx, 1)) {
LOGD("i2c rx cfg error!\n");
}
LOGD("i2c reg[0x%x%x]=%x\n", CFG_H, CFG_L, rx[0]);
memset(rx, 0, 512);
/**
* 半双工传输,即共用读写,中间无stop信号
*
* 参数1: 写数据地址
* 参数2: 写数据长度
* 参数3: 读数据地址
* 参数4: 读数据长度
*/
if (!i2c.transfer(tx, 2, rx, 1)) {
LOGD("i2c i2c_transfer cfg error!\n");
}
LOGD("i2c reg[0x%x%x]=%x\n", CFG_H, CFG_L, rx[0]);
tx[0] = VER_H;
tx[1] = VER_L;
if (!i2c.write(tx, 2)) {
LOGD("i2c tx ver error!\n");
}
if (!i2c.read(rx, 1)) {
LOGD("i2c rx ver error!\n");
}
LOGD("i2c reg[0x%x%x]=%x\n", VER_H, VER_L, rx[0]);
memset(rx, 0, 512);
if (!i2c.transfer(tx, 2, rx, 1)) {
LOGD("twi i2c_transfer ver error!\n");
}
LOGD("i2c reg[0x%x%x]=%x\n", VER_H, VER_L, rx[0]);
}
其他接口操作请参见头文件注释说明。
I2C操作—EEPROM读写案例
以AD24C32存储器为例,我们主要关注器件地址即从机地址和设备的电气特性。
//实例一个I2C对象,参数2从机地址为7bit地址,由上图3和图10可知器件地址为1010 000R/W,最低位为读写位,读写位底层会根据调用的方法read/write来决定,此地址右移一位的结果为0101 0000即0x50
static I2CHelper i2c(1, 0x50, 1000, 5);
/**
* 读操作
* I2C对象在构造时已经传入器件地址,在调用read/write方法时器件地址会自动拼接
* 参数1:字地址,即读数据的起始地址
* 参数2:存放读出数据的容器
* 参数3:读数据的长度
*/
int i2c_readReg(unsigned short reg_addr, unsigned char *buf, int len)
{
int res = 0;
//由图14可知,读数据之前需要先写字地址,即告诉芯片要从什么位置开始读数据
unsigned char buff[2];
buff[0] = (reg_addr >> 8);
buff[1] = reg_addr;
//写2个字节的字地址
i2c.write(buff, 2);
usleep(2*1000);
res = i2c.read(buf,len);
return res;
}
/**
* 页写操作
* 参数1:字地址,即读数据的起始地址
* 参数2:待写入的数据
* 参数3:待写入的数据的长度
*/
int i2c_writeReg(unsigned short reg_addr, unsigned char *buf, int len)
{
int res = 0,i;
unsigned char *buff = 0;
buff = (unsigned char *)malloc((len+2));
//由图12可知,写数据时需要数据帧需要带字地址,即告诉芯片要从什么位置开始写数据
buff[0] = (reg_addr >> 8);
buff[1] = reg_addr;
//由图12可知,字地址之后紧接是待写入的数据内容
for(i = 0; i < len; i++)
buff[(i+2)] = buf[i];
res = i2c.write(buff, (len+2));
free(buff);
return res;
}
/**
* 测试代码
* 测试代码先写入数据,然后再读取数据,对比写入和读出的数据内容
*/
void I2C_Test_EEPROM(){
char wbuf[32] = {0};
memset(wbuf,0,32);
strcpy(wbuf,"www.zkswe.com");
LOGD("\n");
if(i2c_writeReg(0,(unsigned char*)wbuf,strlen("www.zkswe.com"))){
LOGD("write \"%s\" success!",wbuf);
}
//由电器特性可知,写周期时间为2-5ms,在下面读之前有写的操作,所以这里需要延时一定的时间
usleep(5*1000);
unsigned char rbuf[32] = {0};
memset(rbuf,0,32);
if(i2c_readReg(0,rbuf,32)){
LOGD("read content:");
for(unsigned int j= 0;j<strlen("www.zkswe.com");j++){
LOGD("%d:%c\n",j,rbuf[j]);
}
}else{
LOGD("Read FAIL");
}
LOGD("\n");
}
/**
* 日志
* 由日志可知,读出的数据和写入的数据比较无误。
* 综上,I2C操作主要看从设备的通信协议,还需要关注从设备的电器特性,否则通讯可能会出现异常。
*/
D/zkgui ( 917):
D/zkgui ( 917): write "www.zkswe.com" success!
D/zkgui ( 917): read content:
D/zkgui ( 917): 0:w
D/zkgui ( 917): 1:w
D/zkgui ( 917): 2:w
D/zkgui ( 917): 3:.
D/zkgui ( 917): 4:z
D/zkgui ( 917): 5:k
D/zkgui ( 917): 6:s
D/zkgui ( 917): 7:w
D/zkgui ( 917): 8:e
D/zkgui ( 917): 9:.
D/zkgui ( 917): 10:c
D/zkgui ( 917): 11:o
D/zkgui ( 917): 12:m
D/zkgui ( 917):