1,概述
I2C(Inter-Integrated Circuit),中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,是由飞利浦公司在1980年代初设计的,方便了主板、嵌入式系统或手机与周边设备组件之间的通讯。由于其简单性,它被广泛用于微控制器与传感器阵列,显示器,IoT设备,EEPROM等之间的通信。本芯片提供了多条I2C总线,以及多种I2C工作模式可供使用。
2,API参考
2.1主要枚举、结构体介绍
2.1.1 I2C写入后停止或重新开启枚举
enum HAL_I2C_ACTION_AFTER_WRITE_T {
HAL_I2C_STOP_AFTER_WRITE = 0,
HAL_I2C_RESTART_AFTER_WRITE,
};
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配置结构体
返回值:
函数原型:
uint32_t hal_i2c_close(enum HAL_I2C_ID_T id);
函数功能:关闭指定I2C
- 函数参数:
- id:I2C id
返回值:
函数原型:
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
返回值:
函数原型:
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
返回值:
函数原型:
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
返回值:
函数原型:
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
返回值:
函数原型:
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从机发送数据
函数参数:
函数原型:
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:用于记录当前实际接收的数据长度
返回值:
函数原型:
int hal_gpio_i2c_open(struct HAL_GPIO_I2C_CONFIG_T *cfg);
函数功能:开启模拟I2C
- 函数参数:
- *cfg:模拟I2C配置结构体指针
返回值
函数原型:
int hal_gpio_i2c_close(struct HAL_GPIO_I2C_CONFIG_T *cfg);
函数功能:关闭模拟I2C
- 函数参数:
- *cfg:模拟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发送数据
函数参数:
函数原型:
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接收数据
- 函数参数:
<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文件夹中查看更多例程