RISCV_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
    HW MIIC group5 3EH

    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,
            PAD_PM_I2C_SDA    PINMUX_FOR_PM_I2CM0_MODE_1    MDRV_PUSE_I2C5_SCL,
            PAD_PM_I2C_SDA    PINMUX_FOR_PM_I2CM0_MODE_1    MDRV_PUSE_I2C5_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},
        {PAD_PM_I2C_SDA    PINMUX_FOR_PM_I2CM0_MODE_1    MDRV_PUSE_I2C5_SCL},
        {PAD_PM_I2C_SDA    PINMUX_FOR_PM_I2CM0_MODE_1    MDRV_PUSE_I2C5_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 个 pMsg 存储传输信息。

    • 声明

      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 当从设备没有处在正常的工作状态的时候,无法对主设备发起的信号进行响应