IIC(Inter-Integrated Circuit,集成电路总线)由飞利浦公司在1980年代提出,此接口使用两根信号线实现一主多从的通信,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
Rockchip I2C 控制器支持下列功能︰
开发板上有多个I2C控制器,其中专门开放给用户操作的包括30PIN接口上面的I2C3和一个4PIN座子引出的I2C6,本文以I2C3为例操作,其位置如下
下面以操作I2C3下挂一个at24c32 为例介绍如何进行I2C读写。at24c32是32kbit的EEPROM,此器件I2C地址为0x50,寄存器为16位长度,有效地址为 0x0000~0x0FFF(4096字节),每一个寄存器就是一个字节的存储单元。接线图如下
目前的系统中,"/dev/i2c-3"的节点需要root权限才能执行读写操作,使用android studio编译出来的程序需要system操作权限。需要在adb或者串口环境下使用如下命令,将设备节点的所属者改为system。
su root
chown system:system /dev/i2c-3
确认I2C-3已经属于system
RK的Android系统默认编译了i2ctool来操作i2c,其中比较实用的两个工具是i2cdetect和i2ctransfer。另外还有i2cget和i2cset其实是i2ctransfer的简化版本。
检测i2c3下面有哪些设备
i2cdetect -y 3
操作结果如下图
连续写入at24c32从0x0100开头的三个字节数据0x07 0x08 0x09。其中w5代表要连续写入5字节,由于寄存器地址为2字节即16位,然后还有三个字节的数据,0x50是其I2C器件地址
i2ctransfer -f -y 3 w5@0x50 0x01 0x00 0x07 0x08 0x09
连续读取at24c32从0x0000开头的三个字节数据。其中w2代表要连续写入2字节,由于寄存器地址为2字节即16位,0x50是其I2C器件地址,r3代表要连续读操作3个寄存器
i2ctransfer -f -y 3 w2@0x50 0x01 0x00 r3
操作结果如下图
android 编程时可以使用C语言操作I2C,然后封装一个jni接口,由java来调用。C语言实现如下
int user_i2c_open(int i2c_num)
{
int fd = -1;
char str[40] = {0};
snprintf(str, sizeof(str) - 1, "/dev/i2c-%d", i2c_num);
if(access(str,F_OK) != 0) {
return -1;
}
fd = open(str, O_RDWR);
if (fd < 0) {
return -1;
}
return fd;
}
int user_i2c_read(int fd, unsigned short slave_addr, unsigned char *reg_addr, unsigned char addr_len, unsigned char *buf, unsigned short len)
{
int ret = 0;
struct i2c_rdwr_ioctl_data ioctl_data = {0};
struct i2c_msg msg[2] = {0};
if (len > I2C_SMBUS_BLOCK_MAX || addr_len > 2)
return -EINVAL;
ioctl_data.nmsgs = 2;
ioctl_data.msgs = msg;
(msg[0]).addr = slave_addr;
(msg[0]).flags = 0;
(msg[0]).len = addr_len;
(msg[0]).buf = reg_addr;
(msg[1]).len = len;
(msg[1]).addr = slave_addr;
(msg[1]).flags = I2C_M_RD;
(msg[1]).buf = buf;
ret = ioctl(fd, I2C_RDWR, (unsigned long)&ioctl_data);
if (ret < 0) {
return ret;
}
return 0;
}
int user_i2c_write(int fd, unsigned short slave_addr, unsigned char *reg_addr, unsigned char addr_len, unsigned char *buf, unsigned short len)
{
int ret = 0;
struct i2c_rdwr_ioctl_data ioctl_data = {0};
struct i2c_msg msg[1] = {0};
unsigned char *tmp = NULL;
if (len > I2C_SMBUS_BLOCK_MAX || addr_len > 2)
return -EINVAL;
ioctl_data.nmsgs = 1;
ioctl_data.msgs = msg;
tmp = static_cast<unsigned char *>(calloc(1, addr_len + len));
if(!tmp) {
return -ENOMEM;
}
memcpy(tmp, reg_addr, addr_len);
memcpy(tmp + addr_len, buf, len);
(msg[0]).addr = slave_addr;
(msg[0]).flags = 0;
(msg[0]).len = addr_len + len;
(msg[0]).buf = tmp;
ret = ioctl(fd, I2C_RDWR, (unsigned long)&ioctl_data);
free(tmp);
if (ret < 0) {
return ret;
}
return 0;
}
这里封装了 user_i2c_open 用于打开指定编号的I2C,user_i2c_read 用于读取I2C数据,user_i2c_write 用于写入I2C数据,关闭使用Linux原始的close即可
JNI接口代码如下,此处的功能是向0x200地址的五个字节写入0x11 0x12 0x13 0x14 0x15,然后读取出来,并对比写入内容和读出的内容,返回是一个字符串,保存了执行的结果
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_testdemo_MainActivity_I2CTest(JNIEnv *env, jobject /* this */)
{
int fd = -1;
int ret = 0;
unsigned char reg[2] = {0x02, 0x00};
unsigned char buf_write[5] = {0x11, 0x12, 0x13, 0x14, 0x15};
unsigned char buf_read[5] = {0};
memset(test_result,0,sizeof(test_result));
fd = user_i2c_open(3);
if (fd < 0) {
snprintf(reinterpret_cast<char *const>(test_result), sizeof(test_result) - 1, "user_i2c_open fail\n");
goto out;
}
ret = user_i2c_write(fd, 0x50, reg, 2, buf_write, 5);
if (ret < 0) {
snprintf(reinterpret_cast<char *const>(test_result), sizeof(test_result) - 1, "user_i2c_write fail\n");
goto out;
}
usleep(100 * 1000);
ret = user_i2c_read(fd, 0x50, reg, 2, buf_read, 5);
if (ret < 0) {
snprintf(reinterpret_cast<char *const>(test_result), sizeof(test_result) - 1, "user_i2c_read fail\n");
goto out;
}
if (memcmp(buf_write, buf_read, 5) == 0) {
snprintf(reinterpret_cast<char *const>(test_result), sizeof(test_result) - 1, "buf_write buf_read same\n");
} else {
snprintf(reinterpret_cast<char *const>(test_result), sizeof(test_result) - 1, "buf_write buf_read diff %d %d %d %d %d\n",
buf_read[0], buf_read[1], buf_read[2], buf_read[3], buf_read[4]);
}
out:
if (fd >= 0) {
close(fd);
}
LOGE("%s",test_result);
return env->NewStringUTF(reinterpret_cast<const char *>(test_result));
}
在上一章节创建的app基础上,增加一个按钮和一个文本框,按下按钮则执行此测试程序
在MainActivity的onCreate增加
button3.setText("i2c test");
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 设置要显示的文字
textDisplay.setText(" start i2c test... \n");
new Thread(new Runnable() {
@Override
public void run() {
final String i2c_test_result = I2CTest();
runOnUiThread(new Runnable() {
@Override
public void run() {
textDisplay.append("i2ctest: " + i2c_test_result);
}
});
}
}).start();
}
});
同时声明上面定义的JNI接口
public native String I2CTest();
测试时按下 I2C TEST 的按钮可以看到如下效果