I2C 开发指导_Rev1.3

English

修订记录

版本

日期

作者

修订内容

Rev1.0

2023-09-11

TL

创建文档

Rev1.1

2024-03-25

SXX

更改文档名称

Rev1.2

24-11-07

YMX

增加常见问题章节

Rev1.3

26-01-27

LJZ

新增接口

1 简介

本文档面向使用 Lierda LTE-EC71X 系列(EC716 / EC718 / EC718M)模组进行 OpenCPU 二次开发的工程师,系统介绍了 I2C API 的功能特性、接口定义及使用方法。文档涵盖 I2C 基础概念、端口说明、接收/发送机制、API 详解以及完整代码示例,帮助开发者快速上手并正确集成串口通信功能。

LTE-EC71X 系列模组支持 2 路 I2C,详细 IO 复用参考请参考硬件资料。

2 基础概念

I2C是一种多主机、两线制、低速串行通信总线,广泛用于微控制器和各种外围设备之间的通信。它使用两条线路:串行数据线(SDA)和串行时钟线(SCL)进行双向传输。

SCL:串行时钟线 —— 由主机产生时钟

SDA:串行数据线 —— 双向传输数据

I2C 数据帧结构图

_images/i2c-guide/image_1.png

2.1 接收/发送机制

模组中的 I2C 是基于 I2C 硬件控制器 实现的,并非软件 I2C。用户通过接口将数据传给 DMA,由 DMA 负责数据实际传输。

I2C 写(主机输出):主机发地址 + 寄存器 + 数据,从机回复 ACK

I2C 读(主机输入):从机主动驱动 SDA 发数据,主机回复 ACK/NACK

2.2 硬件接线说明

  1. 禁止 SCL/SDA 直接接 VCC 或 GND,会烧坏引脚,需要上拉电阻,推荐 3.3KΩ。

  2. 多设备共用总线,依靠设备地址区分,不会冲突

  3. 走线尽量短,减少干扰,长布线建议加大上拉电阻

            VCC
             │
            3.3KΩ
             │
MODEM_SCL ───┼─── 从机 SCL
             │
            3.3KΩ
             │
MODEM_SDA ───┼─── 从机 SDA

MODEM_GND ─────── 从机 GND

3 API 函数概览

函数

说明

liot_I2cInit()

初始化 I2C 总线。

liot_I2cWrite()

向 I2C 总线写入数据,从设备的寄存器地址长度为 8 位。

liot_I2cRead()

从 I2C 总线读取数据,从设备的寄存器地址长度为 8 位。

liot_I2cRelease()

释放 I2C 总线。

liot_I2cWrite_16bit_addr()

向 I2C 总线写入数据,从设备的寄存器地址长度为 16 位。

liot_I2cRead_16bit_addr()

从 I2C 总线读取数据,从设备的寄存器地址长度为 16 位。

Liot_I2cWriteReg()

向 I2C 总线写入数据,从设备的寄存器地址长度通过参数控制,可选8位或16位。

Liot_I2cReadReg()

从 I2C 总线读取数据,从设备的寄存器地址长度通过参数控制,可选8位或16位。

4 类型说明

4.1 liot_errcode_i2c_e

I2C API 执行结果错误码。

  1. 定义

typedef enum
{
    LIOT_I2C_SUCCESS           = LIOT_SUCCESS,
    LIOT_I2C_INIT_ERR          = 1 | LIOT_I2C_ERRCODE_BASE,
    LIOT_I2C_NOT_INIT_ERR,
    LIOT_I2C_INVALID_PARAM_ERR,
    LIOT_I2C_WRITE_ERR         = 5 | LIOT_I2C_ERRCODE_BASE,
    LIOT_I2C_READ_ERR,
    LIOT_I2C_RELEASE_ERR,
} liot_errcode_i2c_e;
  1. 参数

  • LIOT_I2C_SUCCESS:函数执行成功。

  • LIOT_I2C_INIT_ERR:传入参数错误。

  • LIOT_I2C_NOT_INIT_ERR:I2C 未初始化。

  • LIOT_I2C_INVALID_PARAM_ERR:参数无效。

  • LIOT_I2C_WRITE_ERR:向 I2C 写入数据失败。

  • LIOT_I2C_READ_ERR:从 I2C 读取数据失败。

  • LIOT_I2C_RELEASE_ERR:释放 I2C 总线失败。

4.2 liot_i2c_channel_e

I2C 通道枚举。

  1. 定义

typedef enum
{
    liot_i2c_1 = 0, // i2c channel 1
    liot_i2c_2,     // i2c channel 2
    liot_i2c_3,     // i2c channel 3, not support
} liot_i2c_channel_e;
  1. 参数

  • liot_i2c_1:I2C 总线编号为 1,对应引脚复用表I2C0。

  • liot_i2c_2:I2C 总线编号为 2,对应引脚复用表I2C1。。

  • liot_i2c_3:I2C 总线编号为 3,当前模组不支持,

4.3 liot_i2c_mode_e

I2C 工作模式枚举定义如下:

  1. 定义

typedef enum
{
    LIOT_STANDARD_MODE = 0, // Standard mode (100K)
    LIOT_FAST_MODE     = 1, // Fast mode (400K)
} liot_i2c_mode_e;
  1. 参数

I2C 仅支持两种传输模式 STANDARD 和 FAST。

  • LIOT_STANDARD_MODE: 标准模式,传输速度为 100 kbps。

  • LIOT_FAST_MODE:快速模式,传输速度为 400 kbps。

5 API 函数详解

5.1 liot_I2cInit

该函数用于初始化 I2C 总线。

  1. 声明

liot_errcode_i2c_e liot_I2cInit(liot_i2c_channel_e i2c_no, liot_i2c_mode_e Mode);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • Mode:[In] I2C 的工作模式。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.2 liot_I2cWrite

该函数用于向 I2C 总线写入数据,仅用于从设备的寄存器地址长度为 8 位的数据写入。

  1. 声明

liot_errcode_i2c_e liot_I2cWrite(liot_i2c_channel_e i2c_no, uint8_t slave, uint8_t addr, uint8_t *data, uint32_t length);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • slave:[In] I2C 从设备地址。

  • addr:[in] I2C 从设备的寄存器地址。

  • data: [in] 写入的数据。

  • length: [in] 写入的数据,单位:字节。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.3 liot_I2cRead

该函数用于从 I2C 总线读取数据,,仅用于从设备的寄存器地址长度为 8 位的数据读取。

  1. 声明

liot_errcode_i2c_e liot_I2cRead(liot_i2c_channel_e i2c_no, uint8_t slave, uint8_t addr, uint8_t *buf, uint32_t length)
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • slave:[In] I2C 从设备地址。

  • addr:[in] I2C 从设备的寄存器地址。

  • buf: [out] 读取的数据。

  • length: [in] 读取数据的长度,单位:字节。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.4 liot_I2cRelease

该函数用于释放 I2C 总线。若需要重新初始化同一个 I2C 总线,须调用该函数释放 I2C 总线,再重新调用 liot_I2cInit()进行初始化。

  1. 声明

liot_errcode_i2c_e liot_I2cRelease(liot_i2c_channel_e i2c_no);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.5 liot_I2cWrite_16bit_addr

该函数用于向 I2C 总线写入数据,仅用于从设备的寄存器地址长度为 16 位的数据写入。

  1. 声明

liot_errcode_i2c_e liot_I2cWrite_16bit_addr(liot_i2c_channel_e i2c_no, uint8_t slave, uint16_t addr, uint8_t *data, uint32_t length);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • slave:[In] I2C 从设备地址。

  • addr:[in] I2C 从设备的寄存器地址。

  • data: [in] 写入的数据。

  • length: [in] 写入数据的长度,单位:字节。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.6 liot_I2cRead_16bit_addr

该函数用于从 I2C 总线读取数据,,仅用于从设备的寄存器地址长度为 16 位的数据读取。

  1. 声明

liot_errcode_i2c_e liot_I2cRead_16bit_addr(liot_i2c_channel_e i2c_no, uint8_t slave, uint16_t addr, uint8_t *buf, uint32_t length);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • slave:[In] I2C 从设备地址。

  • addr:[In] I2C 从设备的寄存器地址。

  • buf: [Out] 读取的数据。

  • length: [In] 读取数据的长度,单位:字节。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.7 Liot_I2cWriteReg

从 I2C 总线写入数据,从设备的寄存器地址长度通过参数控制,可选8位或16位。

  1. 声明

liot_errcode_i2c_e Liot_I2cWriteReg(liot_i2c_channel_e i2c_no, uint8_t devAddr, uint16_t reg, BOOL addr16, const uint8_t *data, uint32_t len);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • devAddr:[In] I2C 从设备地址。

  • reg:[In] I2C 从设备的寄存器地址。

  • addr16:[In] 寄存器长度是否为16位。0: 8位     1: 16位。

  • data: [In] 读取的数据。

  • len: [In] 读取数据的长度,单位:字节。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

5.8 Liot_I2cReadReg

从 I2C 总线读取数据,从设备的寄存器地址长度通过参数控制,可选8位或16位。

  1. 声明

liot_errcode_i2c_e Liot_I2cReadReg(liot_i2c_channel_e i2c_no, uint8_t devAddr, uint16_t reg, BOOL addr16, uint8_t *data, uint32_t len);
  1. 参数

  • i2c_no:[In] I2C 总线编号。

  • devAddr:[In] I2C 从设备地址。

  • reg:[In] I2C 从设备的寄存器地址。

  • addr16:[In] 寄存器长度是否为16位。0: 8位     1: 16位。

  • data: [Out] 读取的数据。

  • len: [In] 读取数据的长度,单位:字节。

  1. 返回值

  • liot_errcode_i2c_e:执行结果码,请参考 4.1。

6 代码示例

以下代码位于 examples/demo/src/demo_i2c.c,演示了如何初始化多个 I2C 端口并在循环中定时针对 8 为和 16 位寄存器读写的示例。


#include "lierda_app_main.h"
#include "liot_gpio2.h"
#include "liot_i2c.h"
#include "liot_os.h"

#define LIOT_I2C_SCL_BIT  67
#define LIOT_I2C_SCL_FUNC (2)

#define LIOT_I2C_SDA_BIT  66
#define LIOT_I2C_SDA_FUNC (2)


/ @brief 8-bit slave write address */
#define SalveAddr_w_8bit   (0x42 >> 1)
/ @brief 8-bit slave read address */
#define SalveAddr_r_8bit   (0x43 >> 1)

/ @brief 16-bit slave write address */
#define SalveAddr_w_16bit (0xa0 >> 1)
/ @brief 16-bit slave read address */
#define SalveAddr_r_16bit (0xa1 >> 1)

/ @brief Test mode selection: 1-8bit register address; 0-16bit register address */
#define demo_for_8bit_or_16bit  (0)

/
 * @brief I2C function demonstration thread
 * @details Initialize I2C interface and perform read-write operations in a loop. Select 8-bit or 16-bit register address mode based on demo_for_8bit_or_16bit
 * @param[in] arvg Thread parameters (unused)
 */
void liot_i2c_demo_thread(void *arvg)
{
    int i2c_no = 0;
    int ret;
    int fastmode = 0;
    uint8_t read_data = 0;
#if demo_for_8bit_or_16bit
    uint8_t data = 0xaa;  /< Data to be written in 8-bit mode */
#else
    uint8_t data = 0xab;  /< Data to be written in 16-bit mode */
#endif

    liot_rtos_task_sleep_ms(200);

    liot_trace("I2C DEMO TEXT !!!");

    // Configure I2C pin functions
    Liot_SetPinFunc(LIOT_I2C_SCL_BIT, LIOT_I2C_SCL_FUNC);
    Liot_SetPinFunc(LIOT_I2C_SDA_BIT, LIOT_I2C_SDA_FUNC);

    // Initialize I2C interface
    ret = liot_I2cInit(i2c_no, fastmode);
    if (ret != LIOT_I2C_SUCCESS)
    {
        liot_trace("I2C INIT FAILED[%d]", ret);
    }

    while (1)
    {
#if demo_for_8bit_or_16bit
        // 8-bit register address mode: read operation
        liot_I2cRead(i2c_no, SalveAddr_r_8bit, 0xf0, &read_data, 1);
        liot_trace("< read i2c value=0x%x, ret=%d >\n", read_data, ret);

        // 8-bit register address mode: write operation
        liot_I2cWrite(i2c_no, SalveAddr_w_8bit, 0x55, &data, 1);
        liot_trace("< write i2c value=0x%x, ret=%d >\n", data, ret);
#else
        // 16-bit register address mode: read operation
        ret = liot_I2cRead_16bit_addr(i2c_no, SalveAddr_r_16bit, 0x00ff, &read_data, 1);
        liot_trace("< read i2c value=0x%x, ret=%d >\n", read_data, ret);

        // 16-bit register address mode: write operation
        ret = liot_I2cWrite_16bit_addr(i2c_no, SalveAddr_w_16bit, 0x00ff, &data, 1);
        liot_trace("< write i2c value=0x%x, ret=%d >\n", data, ret);
#endif
        read_data = 0;
        liot_rtos_task_sleep_ms(200);  /< Delay 200ms after each operation */
    }

    liot_rtos_task_delete(NULL); // kill itsel
}

7 常见问题

7.1 IIC的速度是多少?

IIC支持4种速度配置:

  • Standard Speed (100kHz)

  • Fast Speed (400kHz)

  • Fast+ Speed ( 1MHz)

  • High Speed (3.4MHz)(暂不支持)

7.2 深度休眠唤醒后 I2C 工作不正常?

建议在休眠前将 I2C 去初始化掉,将 I2C 的状态恢复,深度休眠唤醒后代码会重新跑,不用担心唤醒后初始化的问题