1,概述

I2C(Inter-Integrated Circuit),中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,是由飞利浦公司在1980年代初设计的,方便了主板、嵌入式系统或手机与周边设备组件之间的通讯。由于其简单性,它被广泛用于微控制器与传感器阵列,显示器,IoT设备,EEPROM等之间的通信。本芯片提供了多条I2C总线,以及多种I2C工作模式可供使用。

2,API参考

2.1主要枚举、结构体介绍

2.1.1 I2C写入后停止或重新开启枚举

  1. enum HAL_I2C_ACTION_AFTER_WRITE_T {
  2. HAL_I2C_STOP_AFTER_WRITE = 0,
  3. HAL_I2C_RESTART_AFTER_WRITE,
  4. };

2.1.2 I2C ID枚举

enum HAL_I2C_ID_T {
    HAL_I2C_ID_0 = 0,
#if (CHIP_HAS_I2C >= 2)
    HAL_I2C_ID_1,
#endif
#if (CHIP_HAS_I2C >= 3)
    HAL_I2C_ID_2,
#endif
#if (CHIP_HAS_I2C >= 4)
    HAL_I2C_ID_3,
#endif

    HAL_I2C_ID_NUM,
};

注:CHIP_HAS_I2C宏可以根据不同芯片进行定义,便于移植

2.1.3 I2C 用户枚举

enum HAL_I2C_TASK_USER_T {
    HAL_I2C_TASK_USER_0 = 0,
    HAL_I2C_TASK_USER_1,
    HAL_I2C_TASK_USER_2,
    HAL_I2C_TASK_USER_3,
    HAL_I2C_TASK_USER_4,
    HAL_I2C_TASK_USER_5,
    HAL_I2C_TASK_USER_6,
    HAL_I2C_TASK_USER_7,

    HAL_I2C_TASK_USER_NUM, //用于标记最大USER数量
};

2.1.4 I2C 模式枚举

enum HAL_I2C_API_MODE_T {
    HAL_I2C_API_MODE_TASK = 0,
    HAL_I2C_API_MODE_SIMPLE,
    HAL_I2C_API_MODE_SENSOR_ENGINE,
    /* mode
     SIMPLE_MODE : pulling when reading or writing (always sync), or use dma external;master or slave;only enable slave related irq;
     TASK_MODE : task to driven reading or writing (sync or async, dma or non-dma);only master
     */
};

HAL_I2C_API_MODE_TASK是最常用的一种了,任务驱动的读写,可用于同步或异步,dma或非dma

2.1.5 I2C 配置结构体

struct HAL_I2C_CONFIG_T {

    enum HAL_I2C_API_MODE_T mode;

    uint8_t use_sync    : 1;
    uint8_t use_dma     : 1;
    uint8_t as_master   : 1;
    // Set rising_time_ns to tune I2C speed (0 to use default value)
    uint16_t rising_time_ns;
    uint32_t speed;
    uint32_t addr_as_slave;
};

2.1.6 GPIO模拟I2C配置结构体

struct HAL_GPIO_I2C_CONFIG_T {
    enum HAL_GPIO_PIN_T scl;  //模拟时钟线
    enum HAL_GPIO_PIN_T sda;  //模拟数据线
    uint32_t speed;
    uint8_t isOpen;  // initial value is 0
};

2.2 主要API介绍

2.2.1 开启I2C

  • 函数原型:

    uint32_t hal_i2c_open(enum HAL_I2C_ID_T id, const struct HAL_I2C_CONFIG_T *cfg);
    
  • 函数功能:开启指定I2C,并配置I2C

  • 函数参数:
    • id:I2C id
    • *cfg:I2C配置结构体
  • 返回值:

    • HAL_I2C_ERRCODE_IN_USE:I2C使用中
    • HAL_I2C_ERRCODE_INV_PARAM:参数错误
    • 0:开启成功

      2.2.2 关闭I2C

  • 函数原型:

    uint32_t hal_i2c_close(enum HAL_I2C_ID_T id);
    
  • 函数功能:关闭指定I2C

  • 函数参数:
    • id:I2C id
  • 返回值:

    • Priority Mask value

      2.2.3 I2C发送数据(master)

  • 函数原型:

    uint32_t hal_i2c_send(enum HAL_I2C_ID_T id, uint32_t device_addr, 
                        uint8_t *buf, uint32_t reg_len, uint32_t value_len,
                        uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler);
    
  • 函数功能:I2C作为主机发送数据

  • 函数参数:
    • id:I2C id
    • device_addr:从机地址
    • *buf:发送数据buffer指针
    • reg_len:寄存器地址长度
    • value_len:数据长度
    • transfer_id:默认为0
    • handler:默认为NULL
  • 返回值:

    • 0:发送成功
    • errcode:错误代码

      2.2.4 I2C接收数据(master)

  • 函数原型:

    uint32_t hal_i2c_recv(enum HAL_I2C_ID_T id, uint32_t device_addr, 
                        uint8_t *buf, uint32_t reg_len, uint32_t value_len,
                        uint8_t restart_after_write, uint32_t transfer_id,
                        HAL_I2C_TRANSFER_HANDLER_T handler);
    
  • 函数功能:I2C作为主机接收数据

  • 函数参数:
    • id:I2C id
    • device_addr:从机地址
    • *buf:发送数据buffer指针
    • reg_len:寄存器地址长度
    • value_len:数据长度
    • restart_after_write:写入完成是否重启
    • transfer_id:默认为0
    • handler:默认为NULL
  • 返回值:

    • 0:接收成功
    • errcode:错误代码

      2.2.5 I2C发送数据(master task mode)

  • 函数原型:

    uint32_t hal_i2c_task_send(enum HAL_I2C_ID_T id, uint16_t device_addr, 
                             const uint8_t *tx_buf, uint16_t tx_len,
                             uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler);
    
  • 函数功能:I2C在master task mode下发送数据

  • 函数参数:
    • id:I2C id
    • device_addr:从机地址(7位的,八位地址右移一位,丢掉读写位)
    • *tx_buf:要发送的数据,其中应该包含寄存器地址和寄存器值,如果寄存器地址是8位的,tx_buf[0]就是你应该填充的八位地址值,而后面的则为你要发送的数据;如果寄存器地址是16位的,tx_buf[0]和tx_buf[1]为你需要填充的寄存器地址,其后的则为数据。这里需要特别注意的是:I2C是可以自动累加寄存器地址发送发的,也就是说如果你的寄存器地址是连续的,如0x00,0x01,0x02等,就可以通过寄存器0x00发送3个byte的数据,他会将多余的值自动发送到后续的8位寄存器地址,所以可以调一个接口来接续写。
    • tx_len:数据的长度,为寄存器地址的长度加寄存器值的长度
    • transfer_id:默认为0
    • handler:默认为NULL
  • 返回值:

    • 0:发送成功
    • errcode:错误代码

      2.2.6 I2C接收数据(master task mode)

  • 函数原型:

    uint32_t hal_i2c_task_recv(enum HAL_I2C_ID_T id, uint16_t device_addr, 
                             const uint8_t *tx_buf, uint16_t tx_len,
                             uint8_t *rx_buf, uint16_t rx_len,
                             uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler);
    
  • 函数功能:I2C在master task mode下接收数据

  • 函数参数:
    • id:I2C id
    • device_addr:从机地址(7位的,八位地址右移一位,丢掉读写位)
    • *tx_buf:寄存器地址
    • tx_len:寄存器地址长度,8位的时候此数值为1;16位的时候此数值为2
    • *rx_buf:读数据的缓存buff
    • rx_len:需要读取的数据长度
    • transfer_id:默认为0
    • handler:默认为NULL
  • 返回值:

    • 0:发送成功
    • errcode:错误代码

      2.2.7 I2C写数据(从机模式)

  • 函数原型:

    uint32_t hal_i2c_slv_write(enum HAL_I2C_ID_T id, const uint8_t *buf, 
                             uint32_t buf_len, uint32_t *act_len);
    
  • 函数功能:I2C从机发送数据

  • 函数参数:

    • id:I2C id
    • *buf:待发送数据地址
    • buf_len:待发送数据长度
    • *act_len:用于记录当前实际发送的数据长度

      2.2.8 I2C读数据(从机模式)

  • 函数原型:

    uint32_t hal_i2c_slv_read(enum HAL_I2C_ID_T id, uint8_t *buf, 
                            uint32_t buf_len, uint32_t *act_len);
    
  • 函数功能:I2C从机接收数据

  • 函数参数:
    • id:I2C id
    • *buf:接收数据存放地址
    • buf_len:接收数据长度
    • *act_len:用于记录当前实际接收的数据长度
  • 返回值:

    • 0:接收成功

      2.2.9 模拟I2C开启

  • 函数原型:

    int hal_gpio_i2c_open(struct HAL_GPIO_I2C_CONFIG_T *cfg);
    
  • 函数功能:开启模拟I2C

  • 函数参数:
    • *cfg:模拟I2C配置结构体指针
  • 返回值

    • 0:开启成功
    • -1:开启失败

      2.2.10 模拟I2C关闭

  • 函数原型:

    int hal_gpio_i2c_close(struct HAL_GPIO_I2C_CONFIG_T *cfg);
    
  • 函数功能:关闭模拟I2C

  • 函数参数:
    • *cfg:模拟I2C配置结构体指针
  • 返回值

    • 0:关闭成功
    • -1:关闭失败

      2.2.11 模拟I2C发送数据

  • 函数原型:

    uint32_t hal_gpio_i2c_simple_send(struct HAL_GPIO_I2C_CONFIG_T *cfg, uint32_t device_addr, 
                                    const uint8_t *tx_buf, uint16_t tx_len);
    
  • 函数功能:模拟I2C发送数据

  • 函数参数:

    • *cfg:模拟I2C配置结构体指针
    • device_addr:从机地址
    • *tx_buf:待发送数据
    • tx_len:待发送数据长度

      2.2.12 模拟I2C接收数据

  • 函数原型:

    uint32_t hal_gpio_i2c_simple_recv(struct HAL_GPIO_I2C_CONFIG_T *cfg, uint32_t device_addr, 
                                    const uint8_t *tx_buf, uint16_t tx_len, 
                                    uint8_t *rx_buf, uint16_t rx_len);
    
  • 函数功能:模拟I2C接收数据

  • 函数参数:
    • *cfg:模拟I2C配置结构体指针
    • device_addr:从机地址
    • *tx_buf:寄存器地址
    • tx_len:寄存器地址长度,8位的时候此数值为1;16位的时候此数值为2
    • *rx_buf:读数据的缓存buff
    • rx_len:需要读取的数据长度

      3,使用教程

      3.1 头文件引用

      ```c

      include “hal_iomux.h”

      include “hal_i2c.h”

<a name="LKm82"></a>
## 3.2 I2C模式选择与开启

- 本芯片支持硬件I2C与模拟I2C并且支持多种工作模式,不同的工作模式需要使用不同的函数进行开启
- 本次使用硬件I2C的master task模式进行演示
- 首先需要实例化HAL_I2C_CONFIG_T结构体
```c
struct HAL_I2C_CONFIG_T _i2c_cfg{
    .mode = HAL_I2C_API_MODE_TASK;     //task模式
    .use_dma  = 0;        //不启用dma
    .use_sync = 1;        //启用同步
    .speed = 400000;    //400k
    .as_master = 1;        //主机模式
}
  • 之后开启I2C
    hal_iomux_set_i2c0();
    hal_i2c_open(HAL_I2C_ID_0, &_i2c_cfg);
    

    注意开启I2C之前需要用IOMUX的函数对IO口进行配置

3.3 I2C发送与接收

  • 直接使用头文件中的各个函数比较复杂,一般将读写的接口进行封装后使用
  • I2C device封装:

    struct i2c_dev {
      const char *name;
      uint32_t addr;                    //设备地址
      enum HAL_I2C_ID_T bus;            //I2C总线
      struct HAL_I2C_CONFIG_T cfg;    
      int inited;                        //是否初始化
    };
    
  • 写封装:

    uint32_t i2c_write(struct i2c_dev *dev, const uint8_t *txbuf, uint16_t txlen)
    {
      uint32_t ret;
      if (dev->cfg.mode == HAL_I2C_API_MODE_SIMPLE)  //模式判断,选用不同的发送函数
          ret = hal_i2c_simple_send(dev->bus, dev->addr, txbuf, txlen);
      else
          ret = hal_i2c_task_send(dev->bus, dev->addr, txbuf, txlen, 0, NULL);
    
      if (ret) {
          printf("[%s] failed, dev_addr 0x%x, ret %u\r\n", __func__, dev->addr, ret);
      }
      return ret;
    }
    
  • 读封装:

    uint32_t i2c_read(struct i2c_dev *dev, uint8_t *rxbuf, uint16_t rxlen)
    {
      uint32_t ret;
      if (dev->cfg.mode == HAL_I2C_API_MODE_SIMPLE)
          ret = hal_i2c_simple_recv(dev->bus, dev->addr, NULL, 0, rxbuf, rxlen);
      else
          ret = hal_i2c_task_recv(dev->bus, dev->addr, NULL, 0, rxbuf, rxlen, 0, NULL);
    
      if (ret) {
          printf("[%s] failed, dev_addr 0x%x, ret %d\r\n", __func__, dev->addr, ret);
      }
      return ret;
    }
    

    可在wifi_app/app/bes_test/peripheral_modules/i2c文件夹中查看更多例程