I2C使用参考
1. 概述¶
| I2C Group | DEV |
|---|---|
| HW I2C group0 | /dev/i2c-0 |
| HW I2C group1 | /dev/i2c-1 |
| HW I2C group2 | /dev/i2c-2 |
| HW I2C group3 | /dev/i2c-3 |
设备节点和硬件组别的关系如上表所示。
2. DTS定义¶
1. i2c0@0 {
2. compatible = "sstar,i2c";
3. reg = <0x1F222800 0x200>;
4. #address-cells = <1>;
5. #size-cells = <0>;
6. interrupts = <GIC_SPI INT_IRQ_MIIC IRQ_TYPE_LEVEL_HIGH>;
7. clocks = <&CLK_miic0>;
8. i2c-group = <0>;
9. i2c-en-dma = <1>;
10. i2c-speed = <400000>;
11. status = "ok";
12. }
I2C master驱动中支持配置的属性如下表所示:
| 属性 | 描述 | 备注 |
|---|---|---|
| compatible | 用于匹配驱动进行驱动注册,需与代码中一致 | 禁止修改 |
| reg | 用于指定IIC寄存器bank的地址 | 不需要修改 |
| interrupts | 用于指定使用的硬件中断号及属性 | 不需要更改 |
| clocks | 用于指定使用的时钟源 | 不需要更改 |
| #address-cells | 用于指定子节点的地址位宽 | 不需要更改 |
| #size-cells | 用于指定子节点的大小位宽 | 不需要更改 |
| i2c-group | 用于指定IIC外设编号序列号 | 不需要修改 |
| i2c-speed | 用于选择IIC Speed无该属性时,默认设定为400kHz | 可根据需要修改 |
| i2c-en-dma | 用于选择是否使用DMA模式 | 可根据需要修改 |
| status | 用于选择是否使能IIC master驱动 | 可根据需要修改 |
3. Padmux¶
3.1. 设定方法¶
[重要] 由于各外设驱动各自管理各自使用引脚的复用设置会引起某些引脚的复用关系冲突,故现已将引脚复用关系移至mercury6p-sscxxxx-s01a-padmux.dtsi中设定。
例:如使用 SSC025A-S01A-S 板时,对应请修改mercury6p-ssc023a-s01a-padmux.dtsi
1. <PAD_I2C1_SCL PINMUX_FOR_I2C1_MODE_1 MDRV_PUSE_I2C1_SCL>, 2. <PAD_I2C1_SDA PINMUX_FOR_I2C1_MODE_1 MDRV_PUSE_I2C1_SDA>,
如上所示,第一列为引脚索引号,可以在/drivers/sstar/inlcude/{chipname}/gpio.h中查到;第二列为模式定义,在/drivers/sstar/gpio/{chipname}/mhal_pinmux.c中m_stPadMuxTbl数组里,罗列了所有引脚的复用关系,查询该引脚支持哪些复用功能可以查询该数组;第三列为改组设定的索引号,可在/drivers/sstar/include/mdrv_puse.h里找到。
3.2. PAD罗列¶
| Group | Reg(16 bit) | Padmod | Pad Name | Pin Name |
|---|---|---|---|---|
| I2C-0 | bank h103C offset h6F bit[2:0] | 1 | PAD_I2C0_SCL | I2C0_SCL |
| PAD_I2C0_SDA | I2C0_SDA | |||
| 2 | PAD_PWM0 | I2C0_SCL | ||
| PAD_PWM1 | I2C0_SDA | |||
| 3 | PAD_UART1_RX | I2C0_SCL | ||
| PAD_UART1_TX | I2C0_SDA | |||
| 4 | PAD_SD_CLK | I2C0_SCL | ||
| PAD_SD_CMD | I2C0_SDA | |||
| I2C-1 | bank h103C offset h53 bit[2:0] | 1 | PAD_I2C1_SCL | I2C1_SCL0 |
| PAD_I2C1_SDA | I2C1_SDA0 | |||
| 2 | PAD_PWM0 | I2C1_SCL0 | ||
| PAD_PWM1 | I2C1_SDA0 | |||
| 3 | PAD_PM_LED0 | I2C1_SCL0 | ||
| PAD_PM_LED1 | I2C1_SDA0 | |||
| 4 | PAD_I2C1_SCL | I2C1_SCL0 | ||
| PAD_I2C1_SDA | I2C1_SDA0 | |||
| PAD_SR_IO12 | I2C1_SCL1 | |||
| PAD_SR_IO13 | I2C1_SDA1 | |||
| I2C-2 | bank h103C offset h6F bit[9:8] | 1 | PAD_SR_IO12 | I2C2_SCL |
| PAD_SR_IO13 | I2C2_SDA | |||
| 2 | PAD_PWM0 | I2C2_SCL | ||
| PAD_PWM1 | I2C2_SDA | |||
| 3 | PAD_SR_IO00 | I2C2_SCL | ||
| PAD_SR_IO01 | I2C2_SDA |
4. 使用I2C¶
4.1. 用户态读写I2C¶
通过标准的/dev文件来读写I2C,以下是一个使用的小例子。
1. #include <stdio.h>
2. #include <linux/types.h>
3. #include <stdlib.h>
4. #include <fcntl.h>
5. #include <unistd.h>
6. #include <sys/types.h>
7. #include <sys/ioctl.h>
8. #include <errno.h>
9. #include <assert.h>
10. #include <string.h>
11. #include <linux/i2c.h>
12. #include <linux/i2c-dev.h>
13.
14. #define FILE_NAME "/dev/i2c-0"
15.
16. static int i2c_write(int fd,unsigned char slave_addr, unsigned char reg_addr, unsigned char value)
17. {
18. unsigned char outbuf[2];
19. struct i2c_rdwr_ioctl_data packets;
20. struct i2c_msg messages[1];
21.
22. messages[0].addr = slave_addr;
23. messages[0].flags = 0;
24. messages[0].len = sizeof(outbuf);
25. messages[0].buf = outbuf;
26.
27. /* The first byte indicates which register we‘ll write */
28. outbuf[0] = reg_addr;
29.
30. /*
31. * The second byte indicates the value to write. Note that for many
32. * devices, we can write multiple, sequential registers at once by
33. * simply making outbuf bigger.
34. */
35. outbuf[1] = value;
36.
37. /* Transfer the i2c packets to the kernel and verify it worked */
38. packets.msgs = messages;
39. packets.nmsgs = 1;
40. if(ioctl(fd, I2C_RDWR, &packets) < 0)
41. {
42. perror("Unable to send data");
43. return 1;
44. }
45.
46. return 0;
47. }
48.
49. static int i2c_read(int fd, unsigned char slave_addr, unsigned char reg_addr, unsigned char *value)
50. {
51. unsigned char inbuf, outbuf;
52. struct i2c_rdwr_ioctl_data packets;
53. struct i2c_msg messages[2];
54.
55. /*
56. * In order to read a register, we first do a "dummy write" by writing
57. * 0 bytes to the register we want to read from. This is similar to
58. * the packet in set_i2c_register, except it‘s 1 byte rather than 2.
59. */
60. outbuf = reg_addr;
61. messages[0].addr = slave_addr;
62. messages[0].flags = 0; //if you need stop msg before restart msg,
63. //set messages[0].flags |= 0x02;
64. messages[0].len = sizeof(outbuf);
65. messages[0].buf = &outbuf;
66.
67. /* The data will get returned in this structure */
68. messages[1].addr = slave_addr;
69. messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
70. messages[1].len = sizeof(inbuf);
71. messages[1].buf = &inbuf;
72.
73. /* Send the request to the kernel and get the result back */
74. packets.msgs = messages;
75. packets.nmsgs = 2;
76. if(ioctl(fd, I2C_RDWR, &packets) < 0)
77. {
78. perror("Unable to send data");
79. return 1;
80. }
81. *value = inbuf;
82.
83. return 0;
84. }
85.
86. int main(int argc, char **argv)
87. {
88. int fd;
89. unsigned int slave_addr=0, reg_addr=0, value = 0;
90.
91. if (argc < 4){
92. printf("Usage:\n%s r[w] start_addr reg_addr [value]\n",argv[0]);
93. return 0;
94. }
95.
96. fd = open(FILE_NAME, O_RDWR);
97. if (!fd)
98. {
99. printf("can not open file %s\n", FILE_NAME);
100. return 0;
101. }
102.
103. sscanf(argv[2], "%x", &slave_addr);
104. sscanf(argv[3], "%x", ®_addr);
105.
106. if(!strcmp(argv[1],"r"))
107. {
108. i2c_read(fd, slave_addr, reg_addr, (unsigned char*)&value);
109. }
110. else if(argc>4&&!strcmp(argv[1],"w"))
111. {
112. sscanf(argv[4], "%x", &value);
113. i2c_write(fd, slave_addr, reg_addr, value);
114. }
115.
116. close(fd);
117. return 0;
118. }
4.2. 内核态读写I2C¶
通过内核标准接口操作I2C,下面是个小例子:
1. struct i2c_adapter* adpa = NULL;
2. struct i2c_msg msg;
3. u8 data[4] = {0};
4.
5. adpa = i2c_get_adapter(nr); // 获取i2c-0 adapter
6.
7. data[0] = reg & 0xff;
8. data[1] = value & 0xff;
9.
10. msg.addr = slaveAddr>>1;
11. msg.flags = 0;
12. msg.buf = data;
13. msg.len = 2;
14.
15. i2c_transfer(adpa, &msg, 1);