I2C使用参考
1. 概述¶
| I2C Group | SCL | SDA | DEV |
|---|---|---|---|
| HW I2C group0 | PAD_I2C0_SCL | PAD_I2C0_SDA | /dev/i2c-0 |
| HW I2C group1 | PAD_I2C1_SCL | PAD_I2C1_SDA | /dev/i2c-1 |
| HW I2C group2 | PAD_I2C2_SCL | PAD_I2C2_SDA | /dev/i2c-2 |
| HW I2C group3 | PAD_I2C3_SCL | PAD_I2C3_SDA | /dev/i2c-3 |
| HW I2C group4 | PAD_I2C4_SCL | PAD_I2C4_SDA | /dev/i2c-4 |
| HW I2C group5 | PAD_I2C5_SCL | PAD_I2C5_SDA | /dev/i2c-5 |
提供6组HW I2C:
-
第一组HW I2C 对应pad 是PAD_I2C0_SCL/PAD_I2C0_SDA,对应节点是/dev/i2c-0;
-
第二组HW I2C 对应pad 是PAD_I2C1_SCL/PAD_I2C1_SDA,对应节点是/dev/i2c-1;
-
第三组HW I2C 对应pad 是PAD_I2C2_SCL/PAD_I2C2_SDA,对应节点是/dev/i2c-2;
-
第四组、第五组、第六组以此类推。
2. DTS定义¶

以下属性一般不需要修改:
-
compatible 属性:用于匹配驱动进行驱动注册,该属性需与代码中一致,正常情况下请不要修改该属性内容
-
reg 属性:从左到右分别为I2C bank base、Padmux bank base和Clkgen bank base,一般不需要更改
-
interrupts 属性:指定使用的硬件中断号及属性,一般不需要更改
-
clocks 属性:指定使用的时钟源,一般不需要更改
-
#address-cells:指定子节点的地址位宽,不需要更改
-
#size-cells:指定子节点的大小位宽,不需要更改
-
i2c-group属性:指定I2C IP序列号,不需要更改
以下属性可以根据需求修改:
-
status属性:选择是否使用I2C Driver,可根据需求开关
-
i2c-speed属性:选择I2C Speed,可以根据需求选择,无该属性时,默认设定为400kHz,各取值对应的速率如下:
| 属性 | 取值 | 对应速率 |
|---|---|---|
| i2c-speed | 0 | 25k Hz |
| 1 | 50k Hz | |
| 2 | 100k Hz | |
| 3 | 200k Hz | |
| 4 | 300k Hz | |
| 5 | 400k Hz | |
| 6 | 600k Hz | |
| 7 | 800k Hz | |
| 8 | 1000k Hz | |
| 9 | 1500k Hz |
[重要] 由于各IP各自管理各自使用引脚的复用设置会引起某些引脚的复用关系冲突,故现已将引脚复用关系移至mercury6-sscxxxx-s01b-padmux.dtsi中设定。
例:如使用 SSC016A-S01B-S 板时,对应请修改mercury6-ssc016a-s01b-padmux.dtsi

如上图,第一列为引脚索引号,可在/drivers/sstar/inlcude/{chipname}/gpio.h中查到;第二列为模式定义,在/drivers/sstar/gpio/{chipname}/mhal_pinmux.c中m_stPadMuxTbl数组罗列了所有引脚的复用关系,查询该引脚支持的复用功能可以查询该数组;第三列为改组设定的索引号,可在/drivers/sstar/include/mdrv_puse.h里找到。
3. 使用I2C¶
3.1. 用户态读写I2C¶
通过标准的/dev文件来读写I2C,示例如下。
#include <stdio.h> #include <linux/types.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/ioctl.h> #include <errno.h> #include <assert.h> #include <string.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #define FILE_NAME "/dev/i2c-0" static int i2c_write(int fd,unsigned char slave_addr, unsigned char reg_addr, unsigned char value) { unsigned char outbuf[2]; struct i2c_rdwr_ioctl_data packets; struct i2c_msg messages[1]; messages[0].addr = slave_addr; messages[0].flags = 0; messages[0].len = sizeof(outbuf); messages[0].buf = outbuf; /* The first byte indicates which register we‘ll write */ outbuf[0] = reg_addr; /* * The second byte indicates the value to write. Note that for many * devices, we can write multiple, sequential registers at once by * simply making outbuf bigger. */ outbuf[1] = value; /* Transfer the i2c packets to the kernel and verify it worked */ packets.msgs = messages; packets.nmsgs = 1; if(ioctl(fd, I2C_RDWR, &packets) < 0) { perror("Unable to send data"); return 1; } return 0; } static int i2c_read(int fd, unsigned char slave_addr, unsigned char reg_addr, unsigned char *value) { unsigned char inbuf, outbuf; struct i2c_rdwr_ioctl_data packets; struct i2c_msg messages[2]; /* * In order to read a register, we first do a "dummy write" by writing * 0 bytes to the register we want to read from. This is similar to * the packet in set_i2c_register, except it‘s 1 byte rather than 2. */ outbuf = reg_addr; messages[0].addr = slave_addr; messages[0].flags = 0; messages[0].len = sizeof(outbuf); messages[0].buf = &outbuf; /* The data will get returned in this structure */ messages[1].addr = slave_addr; messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/; messages[1].len = sizeof(inbuf); messages[1].buf = &inbuf; /* Send the request to the kernel and get the result back */ packets.msgs = messages; packets.nmsgs = 2; if(ioctl(fd, I2C_RDWR, &packets) < 0) { perror("Unable to send data"); return 1; } *value = inbuf; return 0; } int main(int argc, char **argv) { int fd; unsigned int slave_addr=0, reg_addr=0, value = 0; if (argc < 4){ printf("Usage:\n%s r[w] start_addr reg_addr [value]\n",argv[0]); return 0; } fd = open(FILE_NAME, O_RDWR); if (!fd) { printf("can not open file %s\n", FILE_NAME); return 0; } sscanf(argv[2], "%x", &slave_addr); sscanf(argv[3], "%x", ®_addr); if(!strcmp(argv[1],"r")) { i2c_read(fd, slave_addr, reg_addr, (unsigned char*)&value); } else if(argc>4&&!strcmp(argv[1],"w")) { sscanf(argv[4], "%x", &value); i2c_write(fd, slave_addr, reg_addr, value); } close(fd); return 0; }
3.2. 内核态读写I2C¶
通过内核标准接口操作I2C。示例如下:
struct i2c_adapter* adpa = NULL; struct i2c_msg msg; u8 data[4] = {0}; adpa = i2c_get_adapter(nr); // 获取i2c-0 adapter data[0] = reg & 0xff; // 需要发送的数据 data[1] = value & 0xff; // 需要发送的数据 msg.addr = slaveAddr>>1; // 填充i2c_msg结构体 msg.flags = 0; // 指定一些特殊操作0为master->slave msg.buf = data; msg.len = 2; i2c_transfer(adpa, &msg, 1) ; // 调用API开始传输 msg.flags = I2C_M_RD; // 指定传输方向slave->master i2c_transfer(adpa, &msg, 1); //调用API开始传输