RTOS_I2C使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 04/09/2025

    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通讯协议设备的需求。

    设备bank和硬件组别的关系如下表所示:

    MIIC Group bank addr
    HW MIIC group0 1114H
    HW MIIC group1 1115H
    HW MIIC group2 1116H
    HW MIIC group3 1117H
    HW MIIC group4 1118H

    2. 关键字

    sysdesc:

    RTOS用于描述外设硬件属性的文件,外设节点中包含的属性值可用于外设的配置,类似Linux的设备树文件

    padmux:

    引脚复用,用于将模块功能引脚连接到具体的外部引脚上面,打通信号连接。

    open-drain:

    即开漏输出,不输出电压,控制输出低电平时的引脚接地,控制输出高电平时为高阻态,有外部电路负责高电平。

    push-pull:

    即推挽输出,既可以输出低电平,也可以输出高电平。一般由两个参数相同的三极管或MOSFET组成, 有较强的驱动能力。

    3. 功能描述

    3.1 通信协议

    一个完整的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通信协议格式:


    图3-1 I2C通信协议格式

    3.2 功能配置

    功能
    说明
    备注
    FIFO transfer 每个传输信号都需要由CPU来参与,通过写入不同的MIIC寄存器发送不同的信号 采用polling方式,CPU loading会比较大;单次最大传输数据量为8192字节;在每次传输量都比较小的时候建议使用此模式
    DMA transfer 整个通信过程都由MIIC自行动作,仅需要提前填入传输内容,再触发即可;HW MIIC会根据已经填入的时序设定来进行通讯 采用中断方式,CPU loading会比FIFO mode小;单次最大传输数据量为4086字节;每次传输量都比较大时建议使用此模式;当传输期间出现错误时HW会立即停止这次通讯,触发中断
    timing 调整 开放了几个I2C通讯时序可供调整,当具体某部分时序不满足从设备要求时,在dtsi节点中调整 可调时序: t-SU-STA, t-HD-STA, t-SU-STO, t-HD-STO, t-SU-DAT, t-HD-DAT
    rate 支持通讯速率区间可调,不分档位,可根据实际需要在dtsi节点或sysfs节点中调整 支持50kHz - 1500KHz,更大速率需求不做保证
    输出方式调整 支持open-drain与push-pull两种输出方式,可在dtsi节点中调整 MIIC不支持任意调整波形的上升/下降沿时间,仅可以通过调整输出方式来改变,且时间无法确认push-pull上升沿时间会较快
    padmux 可配置不同的padmux使总线引到不同的引脚上面
    1toN 部分padmux会同时连接几组PAD引脚,通讯时信号会同时提供到这几组PAD上面 该方式有如下限制: 所有的从设备必须类型一样且地址一样,而且MIIC只能写数据不能读数据一般用于同时接入几个相同从设备,需要同步设定的情况
    外部上拉 I2C总线上必须要接外部上拉

    如下为I2C时序:


    图3-2 I2C时序

    MIIC支持配置的时序如下:

    时序
    说明
    t-SU-STA 起始信号建立时间
    t-HD-STA 起始信号保持时间
    t-SU-STO 结束信号建立时间
    t-HD-STO 结束信号保持时间
    t-LOW 时钟低电平时间
    t-HIGH 时钟高电平时间
    t-SU-DAT 数据信号建立时间
    t-HD-DAT 数据信号保持时间

    I2C将不同的通信速度按区间分成多种速度模式,如下:

    速度模式
    说明
    Standard-mode (Sm) 最大可达 100k bit/s
    Fast-mode (Fm) 最大可达 400k bit/s
    Fast-mode Plus (Fm+) 最大可达 1M bit/s
    High-speed mode (Hs-mode) 最大可达 3.4M bit/s
    Ultra Fast-mode (UFm) 最大可达 5M bit/s

    Sm/Fm/Fm+对时序要求如下图:


    图3-3 不同速度模式的时序要求

    4. 硬件连接介绍

    I2C为多主从架构,顾名思义一路总线上面可以有多个主设备与多个从设备,他们之间通过从设备地址与应答信号来建立彼此联系。

    SigmaStar的I2C,每一路总线上仅提供了一个MIIC(master iic)主设备,从设备个数无要求,根据使用需求自行设定。

    并且,遵从I2C标准协议,在总线的SDA与SCL上面必须要接入外部上拉,这是因为I2C通讯需要输出高电平的能力,而当器件输出为开漏输出时,是无法输出高电平的,这时就需要外部上拉的帮助,借此实现“线与”功能,决定最终电平。

    如下为I2C总线示例图:


    图4-1 I2C总线示例图

    5. RTOS用法介绍

    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/pcupid/src/hal_iic.c
    sc/driver/sysdriver/i2c/hal/pcupid/inc/hal_iic.h
    sc/driver/sysdriver/i2c/hal/pcupid/inc/hal_iic_reg.h
    sc/driver/sysdriver/i2c/hal/pcupid/inc/hal_iic_cfg.h
    

    5.2 CONFIG配置

    config文件位于mak/options_chipname_riscv_isw.mak,使能CONFIG_I2C_SUPPORT

    # Feature_Name = [DRV] I2C driver support
    # Description = I2C driver support
    # Option_Selection = TRUE, FALSE
    # CONFIG_I2C_SUPPORT = TRUE
    

    5.3 SYSDESC配置

    chipname-default.sys文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub

    <i2c0>
        [reg_u32_u16] 0x2222800 0x200;
        [interrupts_u8] INT_IRQ_MIIC;
        [camclk_u16] CAMCLK_miic0;
        [dma_u8] 1;
        [output_mode_u8] 2;
        [speed_u32] 200000;
        [t_su_sta_u32] 0;
        [t_hd_sta_u32] 0;
        [t_su_sto_u32] 0;
        [t_hd_sto_u32] 0;
        [t_su_dat_u32] 0;
        [t_hd_dat_u32] 0;
        [status_u8] 1;
    

    Master IIC驱动中支持配置的属性如下表:

    属性 描述 设定值 备注
    reg_u32_u16 指定bank的地址 不需要修改
    interrupts_u8 指定硬件中断号 不需要更改
    dma_u8 选择是否使能DMA模式 1-开启;0-关闭 可根据需要修改
    output_mode_u8 选择输出模式 1:开漏输出;
    2:开漏推1T输出;
    3:开漏推1T强上拉;
    4:推挽输出
    可根据需要修改,
    1->4上升沿时间依次缩短
    speed_u32 设定通讯频率,单位HZ 范围50K~1500K HZ 可根据需要修改
    t_su_sta_u32 设定起始信号保持时间 ns value 可根据需要修改
    t_hd_sta_u32 设定起始信号建立时间 ns value 可根据需要修改
    t_su_sto_u32 设定结束信号建立时间 ns value 可根据需要修改
    t_hd_sto_u32 设定结束信号保持时间 ns value 可根据需要修改
    t_su_dat_u32 设定结束信号建立时间 ns value 可根据需要修改
    t_hd_dat_u32 设定起始信号建立时间 ns 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
    t-su-dat reg_lcnt - reg_sda_cnt
    t-hd-dat reg_sda_cnt

    如果节点中属性值为0,driver会按照既定计算得出ns值,如果有设定则按照设定值,设定的理论最大值为U32_MAX,单位为ns。

    5.4 PADMUX配置

    CONFIG配置:CONFIG_PADMUX_SUPPORT=TRUE

    如果chipname_xxx.sys文件配置了属性<padmux>,那么PADMUX的设定直接在此配置:

    <padmux>
        [schematic_u32_u32_u32]
            PAD_GPIOE_01    PINMUX_FOR_I2C0_MODE_1    MDRV_PUSE_I2C0_SDA,
            PAD_GPIOE_04    PINMUX_FOR_I2C1_MODE_1    MDRV_PUSE_I2C1_SCL,
            PAD_GPIOE_05    PINMUX_FOR_I2C1_MODE_1    MDRV_PUSE_I2C1_SDA,
            PAD_GPIOE_21    PINMUX_FOR_I2C2_MODE_1    MDRV_PUSE_I2C2_SCL,
            PAD_GPIOE_22    PINMUX_FOR_I2C2_MODE_1    MDRV_PUSE_I2C2_SDA,
            PAD_GPIOE_23    PINMUX_FOR_I2C3_MODE_1    MDRV_PUSE_I2C3_SCL,
            PAD_GPIOE_24    PINMUX_FOR_I2C3_MODE_1    MDRV_PUSE_I2C3_SDA,
            PAD_GPIOD_01    PINMUX_FOR_I2C4_MODE_1    MDRV_PUSE_I2C4_SCL,
            PAD_GPIOD_02    PINMUX_FOR_I2C4_MODE_1    MDRV_PUSE_I2C4_SDA,
        [status_u8] 1;
    

    否则通过使能PADMUX驱动,在chipname-xxx-padmux.c文件配置引脚复用功能,该文件位于sc/driver/sysdriver/padmux/hal/chipname/src,只需要在对应的schematic属性添加如下内容中设定:

    pad_info_t schematic[] =
    {
        {PAD_GPIOE_01    PINMUX_FOR_I2C0_MODE_1    MDRV_PUSE_I2C0_SDA},
        {PAD_GPIOE_04    PINMUX_FOR_I2C1_MODE_1    MDRV_PUSE_I2C1_SCL},
        {PAD_GPIOE_05    PINMUX_FOR_I2C1_MODE_1    MDRV_PUSE_I2C1_SDA},
        {PAD_GPIOE_21    PINMUX_FOR_I2C2_MODE_1    MDRV_PUSE_I2C2_SCL},
        {PAD_GPIOE_22    PINMUX_FOR_I2C2_MODE_1    MDRV_PUSE_I2C2_SDA},
        {PAD_GPIOE_23    PINMUX_FOR_I2C3_MODE_1    MDRV_PUSE_I2C3_SCL},
        {PAD_GPIOE_24    PINMUX_FOR_I2C3_MODE_1    MDRV_PUSE_I2C3_SDA},
        {PAD_GPIOD_01    PINMUX_FOR_I2C4_MODE_1    MDRV_PUSE_I2C4_SCL},
        {PAD_GPIOD_02    PINMUX_FOR_I2C4_MODE_1    MDRV_PUSE_I2C4_SDA},
    };
    

    第一列为引脚索引号,可以在sc/drivers/sysdriver/gpio/hal/chipname/pub/gpio.h中查询;

    第二列为模式定义,可以在sc/drivers/sysdriver/gpio/hal/chipname/pub/padmux.h中查询;

    第三列为引脚及搭配模式的索引名称,可以在sc/drivers/sysdriver/padmux/drv/pub/drv_puse.h中查询;

    5.5 Sample code

    头文件位于 sc/driver/sysdriver/i2c/drv/pub/drv_iic.h

    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. API 参考

    该功能模块提供以下接口:

    API名 功能
    drv_i2c_master_xfer 进行iic传输(同步)
    drv_i2c_set_speed 设置iic传输速度

    头文件位于sc/driver/sysdriver/i2c/drv/pub/drv_iic.h

    typedef struct i2c_msg {
        u16 addr;   /*slave address*/
        u16 flags;
    #define CAM_I2C_RD                   0x0001
    #define CAM_I2C_STOP_BEFORE_RESTART  0x0002
    #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
    #define CAM_I2C_STOP                 0x8000
        u16 len;        /*msg length*/
        u8 *buf;        /*pointer to msg data*/
    }tI2cMsg;
    

    6.1. drv_i2c_master_xfer

    • 功能

      与 para_group 对应iic channel进行一次同步的iic传输,使用 para_num 个 para_msg 存储传输信息。

    • 声明

      s32 drv_i2c_master_xfer(u8 para_group, struct i2c_msg *para_msg, s32 para_num);
      
    • 形参

      参数名称 描述
      para_group 设备组号
      para_msg 通信内容
      para_num 信息长度
    • 返回值

      返回值 描述
      0 失败
      正数 成功

    6.2 drv_i2c_set_speed

    • 功能

      设置 para_group 对应的bus速度为 speed

    • 声明

      s32 drv_i2c_set_speed(u8 para_group, u32 speed);
      
    • 形参

      参数名称 描述
      para_group i2c bus
      speed 通讯频率,单位HZ
    • 返回值

      返回值 描述
      0 成功
      other 失败

    7. 调试

    当出现通讯异常时,可以参考如下方面进行问题调试,提供了几种较为常见的排查方向,另调试过程建议抓取波形方便分析。

    排查方向
    常见问题
    备注
    外部上拉 1. 提示通讯超时错误信息;
    2. 抓取波形无变化,SCL始终为低电平;
    3. 波形读写的数据都为0x00
    padmux 1. 提示无ACK信号;
    2. 波形没有变化,电平一直维持
    参考PADMUX章节,或padmux模块说明
    通信速率 通讯失败,无ACK信号 可查找从设备手册,确认正常工作的通信速率范围
    时钟源 1. 提示通讯超时错误信息;
    2. 无波形
    参考CLKGEN模块说明
    时序 通讯无ACK信号 若从设备会对各部分详细时序有特定要求,可从设备手册查找确认
    从设备工作状态 通讯失败,无ACK 当从设备没有处在正常的工作状态的时候,无法对主设备发起的信号进行响应