RTOS_I2C使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | Initial release | 01/15/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 |
---|---|
HW MIIC group0 | 0x1118 |
HW MIIC group1 | 0x1119 |
HW MIIC group2 | 0x1117 |
2. 关键字¶
sysdesc:
RTOS用于描述外设硬件属性的文件,外设节点中包含的属性值可用于外设的配置,类似Linux的设备树文件
padmux:
引脚复用,用于将模块功能引脚连接到具体的外部引脚,打通信号连接。
open-drain:
开漏输出,也称为“开漏极输出”,是指输出端被连接到一个晶体管的漏极上,并且输出信号只能是低电平(0V)或无连接状态。当输出为低电平时,晶体管处于导通状态,输出端可以提供一定的电流;当输出为无连接状态时,晶体管处于截止状态,输出端的电平由外部电路决定。
push-pull:
推挽输出,也称为“全双工输出”,是指输出端被连接到两个互补的晶体管上,并且输出信号可以是高电平或低电平。当输出为高电平时,一个晶体管处于导通状态,另一个晶体管处于截止状态,输出端提供高电平电压;当输出为低电平时,两个晶体管均处于导通状态,输出端提供低电平电压。有较强的驱动能力。
3. 功能描述¶
功能 | 说明 | 备注 |
---|---|---|
FIFO transfer | 每个传输信号都需要由CPU参与,通过写入不同的MIIC寄存器发送不同的信号。 | 采用polling方式,CPU loading较高;在每次传输数据较少时可以使用此模式。 |
DMA transfer | 整个通信过程都由MIIC自行动作,仅需要提前填入传输内容,再触发即可;HW MIIC会根据已经填入的时序设定来进行通讯。 | 采用中断方式,CPU loading会比FIFO mode小;每次传输量都比较大时建议使用此模式;当传输期间出现错误时HW会立即停止当前通讯。 |
timing 调整 | 开放了几个I2C通讯时序可供调整,当具体某部分时序不满足从设备要求时,可在sysdesc文件中的i2c节点调整 | 可调时序: t-SU-STA, t-HD-STA, t-SU-STO, t-HD-STO |
rate | 支持通讯速率区间可调,不分档位,可根据实际需要在sysdesc节点中调整 | 支持50kHz - 1500KHz,更大速率需求不做保证 |
输出方式调整 | 支持open-drain与push-pull两种输出方式,可在sysdesc节点中调整 | MIIC不支持任意调整波形的上升/下降沿时间,仅可以通过调整输出方式来改变,且时间无法确认。push-pull上升沿时间会较快 |
padmux | 可配置不同的padmux将I2C功能复用到不同的引脚面 | |
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. RTOS用法介绍¶
I2C通讯的时候,需要保证基本的如下步骤:
- 硬件设备连接,外部上拉电阻确认;
- CONFIG支持MIIC驱动;
- SYSDESC文件配置需求属性;
- PADMUX设定;
- API调用,执行通讯。
5.1 DRIVER PATH¶
sc/driver/sysdriver/i2c/os/iic_os.h sc/driver/sysdriver/i2c/drv/pub/drv_iic.h sc/driver/sysdriver/i2c/drv/src/drv_iic.c sc/driver/sysdriver/i2c/drv/src/drv_iic_test.c sc/driver/sysdriver/i2c/hal/ifado/src/hal_iic.c sc/driver/sysdriver/i2c/hal/ifado/inc/hal_iic.h sc/driver/sysdriver/i2c/hal/ifado/inc/hal_iic_reg.h sc/driver/sysdriver/i2c/hal/ifado/inc/hal_iic_cfg.h
5.2 CONFIG配置¶
#make menuconfig BSP Driver Options ---> <*> Support I2C driver
5.3 SYSDESC配置¶
ifado_*.sys文件位于sc/driver/sysdriver/sysdesc/hal/ifado/pub
<i2c1> [reg_u32] 0x1F223200; [interrupts_u8] INT_IRQ_MIIC_1; [camclk_u16] CAMCLK_miic1; [speed_u32] 200000; [dma_u8] 1; [output_mode_u8] 2; [t_hd_sto_u16] 0; [t_su_sta_u16] 0; [t_hd_sta_u16] 0; [t_su_sto_u16] 0; [status_u8] 1; [padmux_u8] 1;
Master IIC驱动中支持配置的属性如下表:
属性 | 描述 | 设定值 | 备注 |
---|---|---|---|
reg_u32 | 指定bank的地址 | 不需要修改 | |
interrupts_u8 | 指定硬件中断号 | 不需要更改 | |
camclk_u16 | 指定使用的时钟 | 不需要修改 | |
dma_u8 | 选择是否使能DMA模式 | 1-开启;0-关闭 | 可根据需要修改 |
output_mode_u8 | 选择输出模式 | 1:开漏输出; 2:开漏推1T输出; 3:开漏推1T强上拉;4:推挽输出 | 可根据需要修改,1->4上升沿时间依次缩短 |
speed_u32 | 设定通讯频率,单位HZ | 范围50K~1500K HZ | 可根据需要修改 |
t_hd_sto_u16 | 设定结束信号保持时间 | count value | 可根据需要修改 |
t_su_sta_u16 | 设定起始信号保持时间 | count value | 可根据需要修改 |
t_hd_sta_u16 | 设定起始信号建立时间 | count value | 可根据需要修改 |
t_su_sto_u16 | 设定结束信号建立时间 | count value | 可根据需要修改 |
camclk_u16 | 指定时钟源 | 不需要修改 | |
status_u8 | 选择是否使能驱动 | 1-enable;0-disable | 可根据需要修改 |
详细说明:
下图5-1为miic对详细时序的分解,图5-2为可调整的几个时序的位置。
图5-1 miic通信时序
图5-2 miic起始信号与结束信号时序调整
sysdesc中开放的几个时序调整,对应波形位置为:
时序 |
波形位置 |
---|---|
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
5.4 PADMUX设定¶
方法一:CONFIG_PADMUX_SUPPORT=Y
该方法通过使能PADMUX驱动,在ifado-*-padmux.c
文件配置引脚复用功能,该文件位于sc/driver/sysdriver/padmux/hal/ifado/src
,只需要在对应的schematic
属性添加如下内容中设定:
pad_info_t schematic[] = { {PAD_I2C1_SCL, PINMUX_FOR_I2C1_MODE_1, MDRV_PUSE_I2C1_SCL}, {PAD_I2C1_SDA, PINMUX_FOR_I2C1_MODE_1, MDRV_PUSE_I2C1_SDA}, };
第一列为引脚索引号,可以在sc/driver/sysdriver/gpio/hal/ifado/pub/gpio.h
中查询;
第二列为模式定义,可以在sc/driver/sysdriver/gpio/hal/ifado/pub/padmux.h
中查询;
第三列为引脚及搭配模式的索引名称,可以在sc/driver/sysdriver/padmux/drv/pub/drv_puse.h
中查询;
方法二:CONFIG_PADMUX_SUPPORT=N && CONFIG_GPIO_SUPPORT=Y
该方法通过SYSDESC在ifado_*.sys
文件配置引脚复用功能,该文件位于sc/driver/sysdriver/sysdesc/hal/ifado/pub
,可在对应的I2C节点属性padmux_u8
配置padmux.h
定义的模式:
<i2c0> [reg_u32_u16] 0x1F222800 0x200; [interrupts_u8] INT_IRQ_MIIC; [dma_u8] 1; [output_mode_u8] 2; [speed_u32] 200000; [t_hd_sto_u16] 0; [t_su_sta_u16] 0; [t_hd_sta_u16] 0; [t_su_sto_u16] 0; [camclk_u16] CAMCLK_miic0; [padmux_u8] PINMUX_FOR_I2C1_MODE_1; [status_u8] 1;
方法三:CONFIG_PADMUX_SUPPORT=N && CONFIG_GPIO_SUPPORT=N
该方法通过SYSDESC在ifado_*.sys
文件配置引脚复用功能,该文件位于sc/driver/sysdriver/sysdesc/hal/ifado/pub
,可在对应的I2C节点属性padmux_u8
配置引脚模式:
<i2c0> [reg_u32_u16] 0x1F222800 0x200; [interrupts_u8] INT_IRQ_MIIC; [dma_u8] 1; [output_mode_u8] 2; [speed_u32] 200000; [t_hd_sto_u16] 0; [t_su_sta_u16] 0; [t_hd_sta_u16] 0; [t_su_sto_u16] 0; [camclk_u16] CAMCLK_miic0; [padmux_u8] 1; [status_u8] 1;
各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[1:0] | 1 | PAD_I2C1_SDA | I2C1_SDA0 |
PAD_I2C1_SCL | I2C1_SCL | |||
2 | PAD_PWM1 | I2C1_SDA | ||
PAD_PWM0 | I2C1_SCL | |||
3 | PAD_PM_LED1 | I2C1_SDA | ||
PAD_PM_LED0 | I2C1_SCL | |||
I2C1-1TO2 | bank 103cH offset 53H bit[2:0] | 1 | PAD_I2C1_SDA | I2C1_SDA0 |
PAD_I2C1_SCL | I2C1_SCL0 | |||
PAD_SR_IO13 | I2C1_SDA1 | |||
PAD_SR_IO12 | I2C1_SCL1 | |||
I2C-2 | bank 11feH offset 10H bit[5:4] | 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 | I2C2_SCL | |||
4 | PAD_PM_GPIO2 | I2C2_SDA | ||
PAD_PM_IRIN_GPIO | I2C2_SCL |
5.5 API说明¶
头文件位于sc/driver/sysdriver/i2c/drv/pub/drv_iic.h
typedef struct i2c_msg { u16 addr; /*slave address*/ u16 flags; #define CAM_I2C_RD 0x0001 // support #define CAM_I2C_STOP_BEFORE_RESTART 0x0002 // support #define CAM_I2C_TEN 0x0010 #define CAM_I2C_DMA_SAFE 0x0200 #define CAM_I2C_RECV_LEN 0x0400 #define CAM_I2C_NO_RD_ACK 0x0800 #define CAM_I2C_IGNORE_NAK 0x1000 #define CAM_I2C_REV_DIR_ADDR 0x2000 #define CAM_I2C_NOSTART 0x4000 // support #define CAM_I2C_STOP 0x8000 u16 len; /*msg length*/ u8 *buf; /*pointer to msg data*/ }tI2cMsg;
s32 drv_i2c_master_xfer(u8 para_group, struct i2c_msg *para_msg, s32 para_num)
-
参数
参数名称 描述 para_group i2c bus para_msg struct i2c_msg 用于描述单次通讯的信息 para_num i2c_msg的数量 -
返回值
结果 描述 成功 返回已通讯成功的i2c_msg数量 失败 返回0
s32 drv_i2c_set_speed(u8 para_group, u32 speed)
-
参数
参数名称 描述 para_group i2c bus speed 通讯频率,单位HZ -
返回值
结果 描述 成功 返回0 失败 返回-1
5.6 DEMO¶
demo源码位于sc/driver/sysdriver/i2c/drv/src/drv_iic_test.c
static int i2c_ut_test(CLI_t * cli, char * p) { u8 i; u8 argc; u32 port; u32 slave; char *cmd; tI2cMsg msg; u32 addr; u32 value; u8 data[32]; u8 e_data[32]; int ret = 0; u8 r_flag = 0; u32 speed = 0; argc = CliTokenCount(cli); if (argc < 2) return eCLI_PARSE_INPUT_ERROR; cmd = CliTokenPop(cli); if (strcmp(cmd, "r") == 0) { argc = CliTokenCount(cli); if (argc != 4) return eCLI_PARSE_INPUT_ERROR; r_flag = 1; } else if (strcmp(cmd, "w") == 0) { argc = CliTokenCount(cli); if (argc < 5 || argc > 6) return eCLI_PARSE_INPUT_ERROR; } else { return eCLI_PARSE_INPUT_ERROR; } if (CliTokenPopNum(cli, &port, 0) != eCLI_PARSE_OK) { return eCLI_PARSE_INPUT_ERROR; } if (CliTokenPopNum(cli, &slave, 0) != eCLI_PARSE_OK) { return eCLI_PARSE_INPUT_ERROR; } cmd = CliTokenPop(cli); if (strcmp(cmd, "A16D8") == 0) { msg.addr = (u16)slave; if (r_flag) { if (CliTokenPopNum(cli, &addr, 0) != eCLI_PARSE_OK) { return eCLI_PARSE_INPUT_ERROR; } //write msg.flags = 0; data[0] = (u8)((addr >> 8) & 0xff); data[1] = (u8)(addr & 0xff); msg.buf = data; msg.len = 2; drv_i2c_master_xfer(port, &msg, 1); //read msg.flags = CAM_I2C_RD; msg.buf = data; msg.len = 1; drv_i2c_master_xfer(port, &msg, 1); cliPrintf(" %04x : %02x\r\n", (u16)addr, (u8)data[0]); } else { if (CliTokenPopNum(cli, &addr, 0) != eCLI_PARSE_OK) { return eCLI_PARSE_INPUT_ERROR; } if (CliTokenPopNum(cli, &value, 0) != eCLI_PARSE_OK) { return eCLI_PARSE_INPUT_ERROR; } CliTokenPopNum(cli, &speed, 0); data[0] = (u8)((addr >> 8) & 0xff); data[1] = (u8)(addr & 0xff); data[2] = (u8)(value & 0xff); //write msg.flags = 0; msg.buf = data; msg.len = 3; //set i2c speed if (speed) drv_i2c_set_speed(port, speed); //transfer drv_i2c_master_xfer(port, &msg, 1); } } return eCLI_PARSE_OK; }
6. DEBUG方法¶
当出现通讯异常时,可以参考如下方面进行问题调试,提供了几种较为常见的排查方向,另调试过程建议抓取波形方便分析。
排查方向 | 常见问题 |
备注 |
---|---|---|
外部上拉 | 1. 提示通讯超时错误信息;2. 抓取波形无变化,SCL始终为低电平; 3. 波形读写的数据都为0x00 | |
padmux | 1. 提示无ACK信号;2. 波形没有变化,电平一直维持 | 参考PADMUX章节,或padmux模块说明 |
通信速率 | 通讯失败,无ACK信号 | 可查找从设备手册,确认正常工作的通信速率范围 |
时钟源 | 1. 提示通讯超时错误信息;2. 无波形 | 参考CLKGEN模块说明 |
时序 | 通讯无ACK信号 | 若从设备会对各部分详细时序有特定要求,可从设备手册查找确认 |
从设备工作状态 | 通讯失败,无ACK | 当从设备没有处在正常的工作状态的时候,无法对主设备发起的信号进行响应 |