I2C使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | Initial release | 04/19/2024 |
1. 概述¶
1.1 I2C¶
I2C,全称Inter-Integrated Circuit,集成电路总线,使用多主从架构,是一种串行、同步、半双工的通信总线,包含串行时钟线(SCL)与串行数据线(SDA),都是双向IO线。通信过程中,时钟信号由主设备全程提供;数据信号取决于主设备是做读操作还是写操作,当进行写操作时,数据信号由主设备提供,当进行读操作时,数据信号由从设备提供。
1.2 MIIC¶
MIIC,即Master IIC device,是SigmaStar提供的专门作为I2C通信主设备的IP,可实现与各种外接的I2C slave device通讯,满足绝大部分I2C通讯协议设备的需求。
设备节点和硬件组别的关系如下表所示:
MIIC Group | bank addr | DEV |
---|---|---|
HW MIIC group0 | 1118H | /dev/i2c-0 |
HW MIIC group1 | 1119H | /dev/i2c-1 |
HW MIIC group2 | 1117H | /dev/i2c-2 |
2. 关键字¶
dts/dtsi:
Linux设备树文件,通常用于描述CPU所支持的外设,外设节点中包含的属性值可用于外设的配置。
padmux:
引脚复用,用于将模块功能引脚连接到具体的外部引脚上面,打通信号连接。
open-drain:
即开漏输出,不输出电压,控制输出低电平时的引脚接地,控制输出高电平时为高阻态,有外部电路负责高电平。
push-pull:
即推挽输出,既可以输出低电平,也可以输出高电平。一般由两个参数相同的三极管或MOSFET组成。有较强的驱动能力。
3. 功能描述¶
功能 |
说明 |
备注 |
---|---|---|
FIFO transfer | 每个传输信号都需要由CPU来参与,通过写入不同的MIIC寄存器发送不同的信号。 | 采用polling方式,CPU loading会比较大;单次最大传输数据量为8192字节;在每次传输量都比较小的时候建议使用此模式。 |
DMA transfer | 整个通信过程都由MIIC自行动作,仅需要提前填入传输内容,再触发即可;HW MIIC会根据已经填入的时序设定来进行通讯。 | 采用中断方式,CPU loading会比FIFO mode小;单次最大传输数据量为5086字节;每次传输量都比较大时建议使用此模式;当传输期间出现错误时HW会立即停止这次通讯,触发中断。 |
timing 调整 | 开放了几个I2C通讯时序可供调整,当具体某部分时序不满足从设备要求时,在dtsi节点中调整 | 可调时序: t-SU-STA, t-HD-STA, t-SU-STO, t-HD-STO |
rate | 支持通讯速率区间可调,不分档位,可根据实际需要在dtsi节点中调整 | 支持50kHz - 1500KHz,更大速率需求不做保证 |
输出方式调整 | 支持open-drain与push-pull两种输出方式,可在dtsi节点中调整 | MIIC不支持任意调整波形的上升/下降沿时间,仅可以通过调整输出方式来改变,且时间无法确认。push-pull上升沿时间会较快 |
padmux | 可配置不同的padmux使总线引到不同的引脚上面 | |
1toN | 部分padmux会同时连接几组PAD引脚,通讯时信号会同时提供到这几组PAD上面 | 该方式有如下限制: 所有的从设备必须类型一样且地址一样,而且MIIC只能写数据不能读数据。一般用于同时接入几个相同从设备,需要同步设定的情况。 |
外部上拉 | I2C总线上必须要接外部上拉 |
4. 总线与通信协议¶
4.1 总线¶
上面提到I2C为多主从架构,顾名思义一路总线上面可以有多个主设备与多个从设备,他们之间通过从设备地址与应答信号来建立彼此联系。
SigmaStar的I2C,每一路总线上仅提供了一个MIIC(master iic)主设备,从设备个数无要求,根据使用需求自行设定。
并且,遵从I2C标准协议,在总线的SDA与SCL上面必须要接入外部上拉,这是因为I2C通讯需要输出高电平的能力,而当器件输出为开漏输出时,是无法输出高电平的,这时就需要外部上拉的帮助,借此实现“线与”功能,决定最终电平。
如下为I2C总线示例图:

4.2 通信协议¶
一个完整的I2C通信,应包含:起始信号、从设备地址与读写位、应答信号、任意字节长度的数据以及停止信号。
起始信号: 一次完整I2C通信的第一个信号,由主设备提供。示意本次通讯开始。电平信号为 - 当SCL为高电平,SDA从高电平拉为低电平
从设备地址: 通信从设备的“身份证”,与读写位一起共享address frame。占据高7bit或高10bit。通过主设备发出来的地址来确认自己是否被选中。设备地址可以相同,但如果同时接在同一总线上,很容易导致通讯失败或数据错误。
读写位: 用于指示当次传输主设备要进行写操作还是读操作,b0为写操作,b1为读操作。与从设备地址一起共享address frame。占据最低位bit0。
例: 如果从设备地址为7bit的0x50,进行读操作,那么
address frame = (0x50 << 1) | 0x01 = 0xA1
应答信号: 数据接收方反馈给发送方的信号,低电平表示接收成功,高电平表示接收失败。接收方可以是主设备也可以是从设备。当主设备在读取数据时,发送完从设备地址之后,就会变成数据的接收方。但是clock信号始终都是由主设备提供的。
数据: 传输的数据,写操作或者读操作。以字节为单位。另外,一般在I2C通讯写操作时,从设备地址之后会跟随所要读写的从设备寄存器地址,可能是8bit长度,可能是16bit长度,他们也属于传输过程中的数据。
停止信号: 一次完整通讯的最终信号,由主设备提供。示意本次通讯到此结束。电平信号为 - 当SCL为高电平,SDA从低电平拉为高电平。
如下为I2C通信协议格式:

4.3 时序¶
在数据传输的信号行进中,进一步将每个时钟信号和数据位时间长度细分,形成了更细致的时序,也进一步保证了同步通讯的数据准确性。
如下为I2C时序:

在通信时序当中,SCL低电平允许数据信号跳变,在SDA高电平时进行采样,SCL高电平时,SDA至少要维持t-SU-DAT时间段,以供对方采样电平。
时序 |
说明 |
---|---|
t-SU-STA | 起始信号建立时间 |
t-HD-STA | 起始信号保持时间 |
t-SU-STO | 结束信号建立时间 |
t-HD-STO | 结束信号保持时间 |
t-LOW | 时钟低电平时间 |
t-HIGH | 时钟高电平时间 |
t-SU-DAT | 数据信号保持时间 |
5. UBoot使用说明¶
I2C通讯的时候,需要保证基本的如下步骤:
- 硬件设备连接,外部上拉确认;
- padmux设定;
- 软件调用,执行通讯。
5.1 UBoot调用框架¶
UBoot调用框架如图5-1所示,应用程序或uboot command,uboot command被i2c.c识别解析,通过DM arch进入SigmaStar miic driver,再设定HW MIIC register,实现通信。

5.2 config配置¶
需确认是否有打开SigmaStar miic编译配置,若要使用uboot command,要打开相关i2c cmd配置
#menuconfig Command line interface ---> Device access commands ---> [*] i2c [*] SigmaStar drivers ---> [*] SigmaStar IIC
5.3 uboot driver path¶
drivers/sstar/iic/drv_iic.c drivers/sstar/iic/drv_iic.h drivers/sstar/iic/os/iic_os.h drivers/sstar/iic/ifado/hal_iic.c drivers/sstar/iic/ifado/hal_iic.h drivers/sstar/iic/ifado/hal_iic_cfg.h drivers/sstar/iic/ifado/hal_iic_reg.h
5.4 DTS定义¶
UBoot2021下同样有使用dtsi方法用于对miic做一些设定,文件路径:
arch/arm/dts/ifado.dtsi
截取i2c0节点如下,当中的属性与kernel下的定义相同,可以参考kernel章节的说明。
i2c1: i2c1@1f223200 { compatible = "sstar,i2c"; reg = <0x1F223200 0x200>; clock-frequency = <200000>; //dma-enable; group = <1>; t-su-sta = <0>; t-hd-sta = <0>; t-su-sto = <0>; t-hd-sto = <0>; output-mode = <2>; status = "okay"; };
5.5 使用¶
如下参考cmd file: i2c.c梳理提供API调用流程
- i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len, struct udevice **devp);
- setting struct dm_i2c_chip *chip
- dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len);
- dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len);
如下提供过程中两个关键结构体说明:
/** * struct dm_i2c_chip - information about an i2c chip * * An I2C chip is a device on the I2C bus. It sits at a particular address * and normally supports 7-bit or 10-bit addressing. * * To obtain this structure, use dev_get_parent_plat(dev) where dev is * the chip to examine. **/ struct dm_i2c_chip { uint chip_addr; uint offset_len; uint flags; uint chip_addr_offset_mask; #ifdef CONFIG_SANDBOX struct udevice *emul; bool test_mode; int emul_idx; #endif };
Member | Description |
---|---|
chip_addr | Chip address on bus |
offset_len | Length of offset in bytes. A single byte offset can represent up to 256 bytes. A value larger than 1 may be needed for larger devices. |
flags | Flags for this chip (dm_i2c_chip_flags) |
chip_addr_offset_mask | Mask of offset bits within chip_addr. Used for devices which steal addresses as part of offset.If offset_len is zero, then the offset is encoded completely within the chip address itself. e.g. a devce with chip address of 0x2c with 512 registers might use the bottom bit of the address to indicate which half of the address space is being accessed while still only using 1 byte offset. This means it will respond to chip address 0x2c and 0x2d. A real world example is the Atmel AT24C04. It's datasheet explains it's usage of this addressing mode. |
emul | Emulator for this chip address (only used for emulation) |
emul_idx | Emulator index, used for of-platdata and set by each i2c chip's bind() method. This allows i2c_emul_find() to work with of-platdata. |
/** * struct i2c_msg - an I2C message */ struct i2c_msg { uint addr; uint flags; uint len; u8 *buf; };
Member | Description |
---|---|
addr | Slave address |
flags | Flags (see enum dm_i2c_msg_flags) |
len | Length of buffer in bytes, may be 0 for a probe |
buf | Buffer to send/receive, or NULL if no data |
5.6 UBoot Command¶
在uboot命令行下,可以使用uboot command进行一些i2c的测试或操作。这些命令由 cmd/i2c.c
提供,打开5.2节的config配置中i2c cmd项即可使用。例:
SigmaStar # i2c bus
以下命令介绍中,‘[]’代表这个参数可以不带
Command: i2c bus
说明:显示当前所有i2c bus信息
举例:假设bus 1我们有操作过,则会把我们最近操作过的信息也显示出来
SigmaStar # i2c bus Bus 0: i2c0@1F222800 Bus 1: i2c1@1F222A00 (active 1) 50: generic_50, offset len 2, flags 0 Bus 2: i2c2@1F222C00 Bus 3: i2c3@1F007C00
Command: i2c dev [dev]
说明:选择i2c bus
举例:将i2c bus切换到i2c 1
i2c dev 1
Command: i2c probe [address]
说明:扫描从设备。如果不带address参数,i2c会从0x00-0x7f(无偏移),全部地址都扫一遍,并显示有应答的地址,表示有这个地址的从设备接在总线上;如果带上address参数,表示直接测试是否有地址为address的设备接入,有则打印地址,没有就不打印。
举例:
SigmaStar # i2c probe Valid chip addresses: 50 SigmaStar # i2c probe 0x20 Valid chip addresses: SigmaStar # i2c probe 0x50 Valid chip addresses: 50
Command: i2c mw chip address[.0, .1, .2] value [count]
说明:写操作命令
参数 | 说明 |
备注 |
---|---|---|
chip | 从设备地址 | 无偏移 |
address | 寄存器地址.寄存器地址长度 | address[.0, .1, .2],如果不带地址长度,默认1(单位byte) |
value | 写的数据 | |
count | 重复次数 | 每次重复,寄存器地址会自动+1,数据不会变,可不填,默认 1 |
举例:
SigmaStar # i2c mw 0x50 0x0000.2 0x12 0x1 SigmaStar # i2c mw 0x50 0x0000.2 0x12 0x3
Command: i2c md chip address[.0, .1, .2] [length]
说明:读操作命令
参数 | 说明 |
备注 |
---|---|---|
chip | 从设备地址 | 无偏移 |
address | 寄存器地址.寄存器地址长度 | 如果不带地址长度,默认1(单位byte) |
length | 读取字节数 | 不带默认16byte |
举例:
SigmaStar # i2c md 0x50 0x0000.2 0x01
6. kernel使用说明¶
6.1 kernel调用框架¶
kernel I2C整体调用框架如图6-1所示,从用户层开始调用,通过打开设备节点,获取I2C的struct file_operations,再通过ioctl系统调用,期间经过用户空间和内核空间的转换与数据传递,一路执行到SigmaStar I2C driver,对硬件主设备进行设定操作,实现与从设备之间的通信。

6.2 config配置¶
#menuconfig Device Drivers ---> I2C support ---> <*> I2C device interface SStar SoC platform drivers ---> <*> Sstar I2C driver
6.3 kernel driver path¶
kernel/drivers/sstar/i2c/drv_iic.c kernel/drivers/sstar/i2c/drv_iic.h kernel/drivers/sstar/i2c/os/iic_os.h kernel/drivers/sstar/i2c/ifado/hal_iic.c kernel/drivers/sstar/i2c/ifado/hal_iic.h kernel/drivers/sstar/i2c/ifado/hal_iic_reg.h kernel/drivers/sstar/i2c/cam_drv_i2c.c (cam driver,仅开放给kernel space使用) kernel/drivers/sstar/include/cam_drv_i2c.h (cam driver,仅开放给kernel space使用)
6.4 DTS定义¶
i2c1: i2c@1f223200 { compatible = "sstar,i2c"; reg = <0x1F223200 0x200>; #address-cells = <1>; #size-cells = <0>; interrupts = <GIC_SPI INT_IRQ_MIIC_1 IRQ_TYPE_LEVEL_HIGH>; clocks = <&CLK_miic1>; group = <1>; dma-enable; speed = <200000>; t-su-sta = <0>; t-hd-sta = <0>; t-su-sto = <0>; t-hd-sto = <0>; output-mode = <2>; status = "ok"; };
I2C master驱动中支持配置的属性如下表:
属性 | 描述 | 设定值 | 备注 |
---|---|---|---|
compatible | 用于匹配驱动进行驱动注册,需与代码中一致 | “sstar,i2c” | 禁止修改 |
reg | 用于指定IIC寄存器bank的地址 | 不需要修改 | |
interrupts | 用于指定使用的硬件中断号及属性 | 不需要更改 | |
clocks | 用于指定使用的时钟源 | 不需要更改 | |
#address-cells | 用于指定子节点的地址位宽 | 不需要更改 | |
#size-cells | 用于指定子节点的大小位宽 | 不需要更改 | |
group | 用于指定IIC外设编号序列号 | 不需要修改 | |
speed | 用于选择IIC Speed,直接填写实际值 | 如:250000即250K | 可根据需要修改 |
dma-enable | 用于选择是否使用DMA模式 | bool | 不配置即关闭dma功能,注意i2c0不支持dma功能 |
t-su-sta | 用于设定起始信号建立时间 | count value | 可根据需要修改 |
t-hd-sta | 用于设定起始信号保持时间 | count value | 可根据需要修改 |
t-su-sto | 用于设定结束信号建立时间 | count value | 可根据需要修改 |
t-hd-sto | 用于设定结束信号保持时间 | count value | 可根据需要修改 |
output-mode | 用于输出方式选择 | 1:开漏输出;2:开漏推1T输出;3: 开漏推1T强上拉;4:推挽输出 | 可根据需要修改,1->4上升沿时间依次缩短 |
status | 用于选择是否使能IIC master驱动 | 可根据需要修改 |
几个详细说明:
下图6-2为miic对详细时序的分解,图6-3为可调整的几个时序的位置。


dtsi中开放的几个时序调整,对应波形位置为:
时序 |
波形位置 |
---|---|
t-su-sta | reg_start_setup_cnt |
t-hd-sta | reg_start_cnt |
t-su-sto | reg_stop_cnt |
t-hd-sto | reg_stop_hold_cnt |
如果节点中属性值为0,driver会按照既定计算得出count值,如果有设定则按照设定值,设定的理论最大值为0xFFFF,即65535,但当设置过大时,如果是RIU mode的轮询方式会导致轮询超时,导致失败返回。属性值与时间对应关系如下:
首先确定时钟源,当前MIIC根据speed选择时钟源:
50K <= speed <= 200K, 时钟源为12MHz;
200k < speed <= 700K, 时钟源为54MHz;
700K < speed <= 1500K, 时钟源为72MHz;
需要注意的是I2C3的时钟源只有24MHz.
计算公式:
设定时间 = (属性值)/(时钟源)
举例,设定时间需要4us,时钟源72MHz:
属性值 = 设定时间 * 时钟源 = (4 * 10^-6) * (72 * 10^6) = 288
6.5 用户态读写I2C¶
通过标准系统调用来读写I2C,使用范例可以参考:
kernel/drivers/sstar/i2c/ut/ut_i2cdev.c
flags变量的bit[1]用来指代i2c通讯中restart之前是否发送stop信号,即当次i2c_msg传输是否发送STOP信号。 一次ioctl下发信息中可以包含多个struct i2c_message信息,每个message发送完成之后可以选择是否发送stop信号,但最后一次必然会发送stop信号。同时,如果是读操作,之后也必须发送stop信号。
例如,当我们要读数据的时候,需要先发送写时序要读取的寄存器地址,此时我们可以通过flags的bit[1]来控制这次写时序是否要跟上stop信号:
messages[0].flags = 0 // S+WR+RS+RD+P messages[0].flags |= 0x02 // S+WR+P+RS+RD+P
6.6 内核态读写I2C¶
通过内核标准接口操作I2C,内核态的如下调用方式同样可以像用户态那样一次下发多个struct i2c_msg信息,同时也可以选择每个msg是否处理stop信号,与用户态一致,可参考如下示例:
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; msg.flags = 0; msg.buf = data; msg.len = 2; i2c_transfer(adpa, &msg, 1)
7. padmux¶
padmux即引脚复用,用于将MIIC设备信号引到具体的引脚上面,uboot和kernel的配置方式类似。
7.1 UBOOT下padmux设定¶
uboot的引脚复用关系目录及文件:
arch/arm/dts/ifado-xxxxxx-padmux.dtsi
例:如使用 ssc032a-s01a 时,对应请修改ifado-ssc032a-s01a-padmux.dtsi
/ { soc { padmux { compatible = "sstar,padmux"; schematic = ... <PAD_I2C0_SCL PINMUX_FOR_I2C0_MODE_1 MDRV_PUSE_I2C0_SCL>, <PAD_I2C0_SDA PINMUX_FOR_I2C0_MODE_1 MDRV_PUSE_I2C0_SDA>, .... }; }; };
7.2 kernel下padmux设定¶
kernel的引脚复用关系目录及文件:
arch/arm/boot/dts/ifado-sscxxxx-sxxx-padmux.dtsi
例:如使用 ssc032a-s01a 时,对应请修改ifado-ssc032a-s01a-padmux.dtsi
/ { soc { padmux { compatible = "sstar,padmux"; schematic = ... <PAD_I2C0_SCL PINMUX_FOR_I2C0_MODE_1 MDRV_PUSE_I2C0_SCL>, <PAD_I2C0_SDA PINMUX_FOR_I2C0_MODE_1 MDRV_PUSE_I2C0_SDA>, .... }; }; };
如上所示,第一列为引脚索引号,可以在/drivers/sstar/include/ifado/gpio.h中查到;第二列为padmux值,在/drivers/sstar/include/ifado/padmux.h中,罗列了所有引脚的复用关系,查询该引脚支持哪些复用功能可以查询该数组;第三列为该padmux设定的索引号,可在/drivers/sstar/include/drv_puse.h里找到。
7.3 各I2C硬件组padmux罗列¶
I2C BUS | register addr | padmod | PAD | PIN_NAME |
---|---|---|---|---|
I2C-0 | bank 103cH offset 6fH bit[2:0] | 1 | PAD_I2C0_SDA | I2C0_SDA |
PAD_I2C0_SCL | I2C0_SCL | |||
2 | PAD_PWM1 | I2C0_SDA | ||
PAD_PWM0 | I2C0_SCL | |||
3 | PAD_UART1_TX | I2C0_SDA | ||
PAD_UART1_RX | I2C0_SCL | |||
4 | PAD_SD_CMD | I2C0_SDA | ||
PAD_SD_CLK | I2C0_SCL | |||
I2C-1 | bank 103cH offset 53H bit[2:0] | 1 | PAD_I2C1_SDA | I2C1_SDA0 |
PAD_I2C1_SCL | I2C1_SCL0 | |||
2 | PAD_PWM1 | I2C1_SDA0 | ||
PAD_PWM0 | I2C1_SCL0 | |||
3 | PAD_PM_LED1 | I2C1_SDA0 | ||
PAD_PM_LED0 | I2C1_SCL0 | |||
4 | PAD_I2C1_SDA | I2C1_SDA0 | ||
PAD_I2C1_SCL | I2C1_SCL0 | |||
PAD_SR_IO13 | I2C1_SDA1 | |||
PAD_SR_IO12 | I2C1_SCL1 | |||
I2C-2 | bank 103cH offset 6FH bit[10:8] | 1 | PAD_SR_IO13 | I2C2_SDA |
PAD_SR_IO12 | I2C2_SCL | |||
2 | PAD_PWM1 | I2C2_SDA | ||
PAD_PWM0 | I2C2_SCL | |||
3 | PAD_SR_IO01 | I2C2_SDA | ||
PAD_SR_IO00/td> | I2C2_SCL | |||
4 | PAD_PM_GPIO2 | I2C2_SDA | ||
PAD_PM_IRIN_GPIO/td> | I2C2_SCL |
8. 调试¶
当出现通讯异常时,可以参考如下方面进行问题调试,提供了几种较为常见的排查方向,另调试过程建议抓取波形方便分析。
排查方向 | 常见问题 |
备注 |
---|---|---|
外部上拉 | 1. 提示通讯超时错误信息;2. 抓取波形无变化,SCL始终为低电平; 3. 波形读写的数据都为0x00 | |
padmux | 1. 提示无ACK信号;2. 波形没有变化,电平一直维持 | 参考PADMUX章节,或padmux模块说明 |
通信速率 | 通讯失败,无ACK信号 | 可查找从设备手册,确认正常工作的通信速率范围 |
时钟源 | 1. 提示通讯超时错误信息;2. 无波形 | 参考CLKGEN模块说明 |
时序 | 通讯无ACK信号 | 若从设备会对各部分详细时序有特定要求,可从设备手册查找确认 |
从设备工作状态 | 通讯失败,无ACK | 当从设备没有处在正常的工作状态的时候,无法对主设备发起的信号进行响应 |