I2C Development Guide_Rev1.3

中文

Revision History

Version

Date

Author

Revision Content

Rev1.0

2023-09-11

TL

Initial document creation

Rev1.1

2024-03-25

SXX

Changed document name

Rev1.2

24-11-07

YMX

Added common issues section

Rev1.3

26-01-27

LJZ

Added new interfaces

1 Introduction

This document is designed for engineers using Lierda LTE-EC71X series (EC716 / EC718 / EC718M) modules for OpenCPU secondary development. It systematically introduces the functional features, interface definitions, and usage methods of I2C APIs. The document covers I2C basic concepts, port descriptions, receive/transmit mechanisms, detailed API explanations, and complete code examples to help developers quickly get started and correctly integrate serial communication functions.

The LTE-EC71X series modules support 2 I2C channels. For detailed IO multiplexing references, please refer to the hardware documentation.

2 Basic Concepts

I2C is a multi-master, two-wire, low-speed serial communication bus widely used for communication between microcontrollers and various peripheral devices. It uses two lines: Serial Data Line (SDA) and Serial Clock Line (SCL) for bidirectional transmission.

SCL: Serial Clock Line - clock generated by the master

SDA: Serial Data Line - bidirectional data transmission

I2C Data Frame Structure Diagram:

_images/i2c-guide/image_1.png

2.1 Receive/Transmit Mechanism

The I2C in the module is implemented based on I2C hardware controller, not software I2C. Users pass data to DMA through the interface, and DMA is responsible for actual data transmission.

I2C Write (Master Output): Master sends address + register + data, slave replies with ACK

I2C Read (Master Input): Slave actively drives SDA to send data, master replies with ACK/NACK

2.2 Hardware Wiring Instructions

  1. Do not connect SCL/SDA directly to VCC or GND, as this will damage the pins. Pull-up resistors are required, recommended value is 3.3KΩ.

  2. Multiple devices share the bus and are distinguished by device addresses, so there will be no conflicts.

  3. Keep traces as short as possible to reduce interference. For long wiring, it is recommended to increase the pull-up resistor value.

            VCC
             │
            3.3KΩ
             │
MODEM_SCL ───┼─── Slave SCL
             │
            3.3KΩ
             │
MODEM_SDA ───┼─── Slave SDA

MODEM_GND ─────── Slave GND

3 API Function Overview

Function

Description

liot_I2cInit()

Initialize I2C bus.

liot_I2cWrite()

Write data to I2C bus, slave device register address length is 8 bits.

liot_I2cRead()

Read data from I2C bus, slave device register address length is 8 bits.

liot_I2cRelease()

Release I2C bus.

liot_I2cWrite_16bit_addr()

Write data to I2C bus, slave device register address length is 16 bits.

liot_I2cRead_16bit_addr()

Read data from I2C bus, slave device register address length is 16 bits.

Liot_I2cWriteReg()

Write data to I2C bus, slave device register address length controlled by parameter, can be 8-bit or 16-bit.

Liot_I2cReadReg()

Read data from I2C bus, slave device register address length controlled by parameter, can be 8-bit or 16-bit.

4 Type Descriptions

4.1 liot_errcode_i2c_e

I2C API execution result error codes.

  1. Definition

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. Parameters

  • LIOT_I2C_SUCCESS: Function executed successfully.

  • LIOT_I2C_INIT_ERR: Invalid input parameter.

  • LIOT_I2C_NOT_INIT_ERR: I2C not initialized.

  • LIOT_I2C_INVALID_PARAM_ERR: Invalid parameter.

  • LIOT_I2C_WRITE_ERR: Failed to write data to I2C.

  • LIOT_I2C_READ_ERR: Failed to read data from I2C.

  • LIOT_I2C_RELEASE_ERR: Failed to release I2C bus.

4.2 liot_i2c_channel_e

I2C channel enumeration.

  1. Definition

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. Parameters

  • liot_i2c_1: I2C bus number 1, corresponds to I2C0 in pin multiplexing table.

  • liot_i2c_2: I2C bus number 2, corresponds to I2C1 in pin multiplexing table.

  • liot_i2c_3: I2C bus number 3, currently not supported by the module.

4.3 liot_i2c_mode_e

I2C working mode enumeration is defined as follows:

  1. Definition

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

I2C only supports two transmission modes: STANDARD and FAST.

  • LIOT_STANDARD_MODE: Standard mode, transmission speed is 100 kbps.

  • LIOT_FAST_MODE: Fast mode, transmission speed is 400 kbps.

5 API Function Details

5.1 liot_I2cInit

This function is used to initialize I2C bus.

  1. Declaration

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

  • i2c_no: [In] I2C bus number.

  • Mode: [In] I2C working mode.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.2 liot_I2cWrite

This function is used to write data to I2C bus, only for writing data when slave device register address length is 8 bits.

  1. Declaration

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. Parameters

  • i2c_no: [In] I2C bus number.

  • slave: [In] I2C slave device address.

  • addr: [in] I2C slave device register address.

  • data: [in] Data to be written.

  • length: [in] Length of data to be written, unit: bytes.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.3 liot_I2cRead

This function is used to read data from I2C bus, only for reading data when slave device register address length is 8 bits.

  1. Declaration

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. Parameters

  • i2c_no: [In] I2C bus number.

  • slave: [In] I2C slave device address.

  • addr: [in] I2C slave device register address.

  • buf: [out] Data read.

  • length: [in] Length of data to read, unit: bytes.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.4 liot_I2cRelease

This function is used to release I2C bus. If you need to reinitialize the same I2C bus, you must call this function to release the I2C bus first, then call liot_I2cInit() again for initialization.

  1. Declaration

liot_errcode_i2c_e liot_I2cRelease(liot_i2c_channel_e i2c_no);
  1. Parameters

  • i2c_no: [In] I2C bus number.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.5 liot_I2cWrite_16bit_addr

This function is used to write data to I2C bus, only for writing data when slave device register address length is 16 bits.

  1. Declaration

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. Parameters

  • i2c_no: [In] I2C bus number.

  • slave: [In] I2C slave device address.

  • addr: [in] I2C slave device register address.

  • data: [in] Data to be written.

  • length: [in] Length of data to be written, unit: bytes.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.6 liot_I2cRead_16bit_addr

This function is used to read data from I2C bus, only for reading data when slave device register address length is 16 bits.

  1. Declaration

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. Parameters

  • i2c_no: [In] I2C bus number.

  • slave: [In] I2C slave device address.

  • addr: [In] I2C slave device register address.

  • buf: [Out] Data read.

  • length: [In] Length of data to read, unit: bytes.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.7 Liot_I2cWriteReg

Write data to I2C bus, slave device register address length controlled by parameter, can be 8-bit or 16-bit.

  1. Declaration

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. Parameters

  • i2c_no: [In] I2C bus number.

  • devAddr: [In] I2C slave device address.

  • reg: [In] I2C slave device register address.

  • addr16: [In] Whether register length is 16-bit. 0: 8-bit, 1: 16-bit.

  • data: [In] Data to be written.

  • len: [In] Length of data to write, unit: bytes.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

5.8 Liot_I2cReadReg

Read data from I2C bus, slave device register address length controlled by parameter, can be 8-bit or 16-bit.

  1. Declaration

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. Parameters

  • i2c_no: [In] I2C bus number.

  • devAddr: [In] I2C slave device address.

  • reg: [In] I2C slave device register address.

  • addr16: [In] Whether register length is 16-bit. 0: 8-bit, 1: 16-bit.

  • data: [Out] Data read.

  • len: [In] Length of data to read, unit: bytes.

  1. Return Value

  • liot_errcode_i2c_e: Execution result code, see 4.1 for reference.

6 Code Example

The following code is located in examples/demo/src/demo_i2c.c, demonstrating how to initialize multiple I2C ports and perform timed read/write operations for 8-bit and 16-bit registers in a loop.


#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 itself
}

7 Common Issues

7.1 What is the Speed of I2C?

I2C supports 4 speed configurations:

  • Standard Speed (100kHz)

  • Fast Speed (400kHz)

  • Fast+ Speed (1MHz)

  • High Speed (3.4MHz) (currently not supported)

7.2 I2C Not Working Properly After Deep Sleep Wakeup?

It is recommended to deinitialize I2C before sleep to restore I2C status. After deep sleep wakeup, the code will run again, so there is no need to worry about initialization issues after wakeup.