LoopBack

This sample demonstrates communication between I2C master and slave using interrupt. Meanwhile, the master and the slave are interconnected to achieve lookback. Samples of its three communication modes are as follows:

I2C Communication Mode in Loopback Sample

Communication

Brief Introduction

Macros in Configuration

Master Write

The master sends data and the slave receives data.

Set I2C_MASTER_SEND_SLAVE_RECEIVE to 1 to enable Master Write function

Master Read

The master receives data and the slave sends data.

Set I2C_MASTER_RECEIVE_SLAVE_SEND to 1 to enable Master Read function

Master Repeat Read

The master sends data and then receives data.

Set I2C_MASTER_REPEAT_READ to 1 to enable Master Repeat Read function

In this sample, I2C0 is configured as the master, and I2C1 is configured as the slave.

Requirements

For requirements, please refer to the Requirements.

Wiring

Connect P4_0 (master SCL) to P4_2 (slave SCL), P4_1 (master SDA) to P4_3 (slave SDA) on the EVB.

Note

The frequency of SCL is influenced by both the pull-up resistor and the wire capacitance. Therefore, it is strongly recommended to choose appropriate pull-up resistors to make the frequency accurate. This sample need connect 2.2 kΩ pull-up resistors.

The connection schematic of the pull-up resistor is shown below:

Here should be the I2C pull-up resistor schematic

I2C pull-up resistor schematic

Configurations

  1. The following macros can be configured to modify the sample function. Note: Only one of the following three macros can be set to 1.

    #define I2C_MASTER_SEND_SLAVE_RECEIVE   0       /*< Set this macro to 1 to modify the sample for master send slave receive function. */
    #define I2C_MASTER_RECEIVE_SLAVE_SEND   0       /*< Set this macro to 1 to modify the sample for master receive slave send function. */
    #define I2C_MASTER_REPEAT_READ          1       /*< Set this macro to 1 to modify the sample for master repeat read function. */
    
  2. The following macros can be configured to modify pin definitions.

    #define I2C0_SCL_PIN                    P4_0
    #define I2C0_SDA_PIN                    P4_1
    
    #define I2C1_SCL_PIN                    P4_2
    #define I2C1_SDA_PIN                    P4_3
    
  3. The following macros can be configured to modify the I2C data transfer length.

    #define TransferLength                  24      /*< Set this macro to modify the transfer length. */
    

Building and Downloading

For building and downloading, please refer to the Building and Downloading.

Experimental Verification

Master Send Slave Receive

  1. After the EVB starts, the master sends 24 bytes of data ranging from 0 to 23. When the master completing sending data, the I2C_INT_STOP_DET interrupt is triggered and interrupt log information is printed.

    I2C0 Stop signal detected
    
  2. When the data received from the slave reaches the set threshold level(set to 8 in this sample), the I2C_INT_RX_FULL interrupt is triggered. The interrupt log information is printed and the data is received in the interrupt handler.

    I2C1 Rx Full detected
    
  3. When the master completes sending data, the slave detects the stop signal and triggers the I2C_INT_STOP_DET interrupt. The interrupt log information is printed, and the remaining data is received. Meanwhile, the data length and data is printed in the interrupt handler.

    I2C1 Stop signal detected
    Slave Recv Date Lenth = 24
    I2C1_Slave_ReceiveData=0
    ...
    I2C1_Slave_ReceiveData=23
    

Master Receive Slave Send

  1. After the EVB starts, the master device reads the expected 24 bytes data by sending a read request command to the slave device.

  2. When the slave receives the read request from the master, the I2C_INT_RD_REQ interrupt is triggered. The interrupt log information is printed, and the slave device sends 24 bytes of data, with content ranging from 10 to 33, to the master device in the interrupt handler.

    Enter I2C1 interrupt I2C1_INT_RD_REQ!
    
  3. After the master device completes receiving the data, it prints the received data.

    Master Read data = 10
    ...
    Master Read data = 33
    

Master Repeat Read

  1. After the EVB starts, the master device sends 4 bytes of data ranging from 1 to 4 to the slave device.

  2. Once the master device finishes sending data, the master device reads the expected 24 bytes data by sending a read request command to the slave device.

  3. When the slave receives the read request from the master, the I2C_INT_RD_REQ interrupt is triggered. The interrupt log information is printed, and the slave device sends 24 bytes of data, with content ranging from 10 to 33, to the master device in the interrupt handler.

    Enter I2C1 interrupt I2C1_INT_RD_REQ!
    
  4. After the slave device finishes sending data to the master device, the master device generates a stop signal. The slave device triggers I2C_INT_RX_DONE and I2C_INT_STOP_DET interrupt. And the data received in step 1 is printed in the interrupt handler.

    I2C1 RX_DONE detected
    I2C1 Stop signal detected
    Slave Recv Date Lenth = 4
    I2C1_Slave_ReceiveData=1
    I2C1_Slave_ReceiveData=2
    I2C1_Slave_ReceiveData=3
    I2C1_Slave_ReceiveData=4
    
  5. After the master device finishes receiving the data, it prints the received data.

    Master Repeat Read data = 10
    ...
    Master Repeat Read data = 33
    

Code Overview

This section introduces the code and process description for initialization and corresponding function implementation in the sample.

Source Code Directory

The directory for project file and source code are as follows:

  • Project directory: sdk\samples\peripheral\i2c\trx_loopback\proj

  • Source code directory: sdk\samples\peripheral\i2c\trx_loopback\src

Initialization

The initialization flow for peripherals can refer to Initialization Flow in General Introduction.

  1. Call Pad_Config() and Pinmux_Config() to configure the PAD and PINMUX of the corresponding pins.

    void board_i2c_master_init(void)
    {
       Pad_Config(I2C_MASTER_SCL_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(I2C_MASTER_SDA_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    
       Pinmux_Config(I2C_MASTER_SCL_PIN, I2C0_CLK);
       Pinmux_Config(I2C_MASTER_SDA_PIN, I2C0_DAT);
    }
    
    void board_i2c_slave_init(void)
    {
       Pad_Config(I2C_SLAVE_SCL_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(I2C_SLAVE_SDA_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    
       Pinmux_Config(I2C_SLAVE_SCL_PIN, I2C1_CLK);
       Pinmux_Config(I2C_SLAVE_SDA_PIN, I2C1_DAT);
    }
    
  2. Call RCC_PeriphClockCmd() to enable the I2C clock.

  3. Initialize the I2C peripheral:

    1. Define the I2C_InitTypeDef type I2C_InitStruct, and call I2C_StructInit() to pre-fill I2C_InitStruct with default values.

    2. Modify the I2C_InitStruct parameters as needed. The initialization parameter configurations for I2C0 and I2C1 are shown in the table below. Call I2C_Init() to initialize the I2C peripheral.

I2C Initialization Parameters

I2C Hardware Parameters

Setting in the I2C_InitStruct

I2C0(Master)

I2C1(Slave)

Clock(Hz)

I2C_InitTypeDef::I2C_ClockSpeed

100000

100000

Device role (I2C master or I2C slave)

I2C_InitTypeDef::I2C_DeviveMode

I2C_DeviveMode_Master

I2C_DeviveMode_Slave

Address mode (7bits/ 10bits mode)

I2C_InitTypeDef::I2C_AddressMode

I2C_AddressMode_7BIT

I2C_AddressMode_7BIT

Slave address

I2C_InitTypeDef::I2C_SlaveAddress

0x50

0x50

Rx threshold level

I2C_InitTypeDef::I2C_RxThresholdLevel

8

8

Auto ACK enable

I2C_InitTypeDef::I2C_Ack

ENABLE

ENABLE

  1. Call I2C_Cmd() to enable I2C peripheral.

Functional Implementation

Master Send Slave Receive

The flow for master send slave receive is shown in the figure:

Here should be I2C master send slave receive flow

I2C Master send slave receive flow

  1. Call I2C_INTConfig() to configure the corresponding I2C interrupt and NVIC. For NVIC configuration, refer to Interrupt Configuration.

    void nvic_i2c_config(void)
    {
       ...
    #if I2C_MASTER_SEND_SLAVE_RECEIVE
       I2C_INTConfig(I2C0, I2C_INT_STOP_DET, ENABLE);
       I2C_INTConfig(I2C1, I2C_INT_RX_FULL, ENABLE);
       I2C_INTConfig(I2C1,  I2C_INT_STOP_DET, ENABLE);
       I2C_INTConfig(I2C1,  I2C_INT_RX_DONE, ENABLE);
    #endif
       ...
       /* Config I2C interrupt */
       NVIC_InitTypeDef NVIC_InitStruct;
       ...
    }
    
  2. The master call I2C_MasterWrite() to send data to the slave.

  3. When the data received from the slave reaches the set threshold level, the I2C_INT_RX_FULL interrupt is triggered. In the slave interrupt handler, call I2C_ReceiveData() to receive data.

  4. When the master completes sending data, the slave detects the stop signal and triggers the slave I2C_INT_STOP_DET interrupt. In the slave interrupt function, call I2C_ReceiveData() to receive the remaining data.

    void I2C1_Handler(void)
    {
       ...
       if (I2C_GetINTStatus(I2C1, I2C_INT_STOP_DET) == SET)
       {
          ...
          lenth = I2C_GetRxFIFOLen(I2C1);
          for (uint32_t i = 0; i < lenth; i++)
          {
                I2C_Rev_Data[I2C_Rev_Index++] = I2C_ReceiveData(I2C1);
          }
          I2C_Rev_Data_Lenth += lenth;
          I2C_ClearINTPendingBit(I2C1, I2C_INT_STOP_DET);
          ...
       }
       ...
       if (I2C_GetINTStatus(I2C1, I2C_INT_RX_FULL) == SET)
       {
          lenth = I2C_GetRxFIFOLen(I2C1);
    
          /*read I2C data*/
          for (uint32_t i = 0; i < lenth; i++)
          {
                I2C_Rev_Data[I2C_Rev_Index++] = I2C_ReceiveData(I2C1);
          }
          I2C_Rev_Data_Lenth += lenth;
          I2C_ClearINTPendingBit(I2C1, I2C_INT_RX_FULL);
       }
    }
    

Master Receive Slave Send

The flow for master receive slave send is shown in the figure:

Here should be I2C master read flow

I2C master receive slave send flow

  1. Call I2C_INTConfig() to configure the corresponding I2C interrupt and NVIC. For NVIC configuration, refer to Interrupt Configuration.

    void nvic_i2c_config(void)
    {
       ...
    #if I2C_MASTER_RECEIVE_SLAVE_SEND
    I2C_INTConfig(I2C1, I2C_INT_RD_REQ, ENABLE);
    #endif
       ...
       /* Config I2C interrupt */
       NVIC_InitTypeDef NVIC_InitStruct;
       ...
    }
    
  2. The master calls I2C_MasterRead() to read the data by sending a read request command to the slave device.

  3. When the slave receives the read request from the master, the I2C_INT_RD_REQ interrupt is triggered. In the slave interrupt handler, call I2C_SendCmd() to send data to the master.

    void I2C1_Handler(void)
    {
       uint8_t send_data_buffer[100];
       for (uint32_t i = 0; i < TransferLength; i++)
       {
          send_data_buffer[i] = i + 10;
       }
       if (I2C_GetINTStatus(I2C1, I2C_INT_RD_REQ) == SET)
       {
          for (uint32_t i = 0; i < TransferLength; i++)
          {
                I2C_SendCmd(I2C1, I2C_WRITE_CMD, send_data_buffer[i], DISABLE);
          }
    
          I2C_ClearINTPendingBit(I2C1, I2C_INT_RD_REQ);
    
       }
       ...
    
    }
    

Master Repeat Read

The master device executes the process of sending first and then receiving, the flow is shown in the figure:

Here should be I2C master repeat read flow

I2C master repeat read flow

  1. Call I2C_INTConfig() to configure the corresponding I2C interrupt and NVIC. For NVIC configuration, refer to Interrupt Configuration.

    void nvic_i2c_config(void)
    {
       ...
    #if I2C_MASTER_REPEAT_READ
       I2C_INTConfig(I2C0, I2C_INT_STOP_DET, ENABLE);
       I2C_INTConfig(I2C1, I2C_INT_RX_FULL, ENABLE);
       I2C_INTConfig(I2C1,  I2C_INT_STOP_DET, ENABLE);
       I2C_INTConfig(I2C1,  I2C_INT_RX_DONE, ENABLE);
       I2C_INTConfig(I2C1, I2C_INT_RD_REQ, ENABLE);
    #endif
       ...
       /* Config I2C interrupt */
       NVIC_InitTypeDef NVIC_InitStruct;
       ...
    }
    
  2. The master calls I2C_RepeatRead(), sends tx_data to the slave, and receives rx_data from the slave .

  3. When the slave device receives a read request from the master device, the I2C_INT_RD_REQ interrupt is triggered. In the interrupt handler, calls I2C_SendCmd() to send data to the master device.

  4. After the slave device completes sending data to the master device, the master device generates a stop signal. The slave device triggers the I2C_INT_RX_DONE and I2C_INT_STOP_DET interrupts, and in the interrupt handler, calls I2C_ReceiveData() to receive data.”

    void I2C1_Handler(void)
    {
       uint16_t lenth;
    
       uint8_t send_data_buffer[100];
       for (uint32_t i = 0; i < TransferLength; i++)
       {
          send_data_buffer[i] = i + 10;
       }
       if (I2C_GetINTStatus(I2C1, I2C_INT_RD_REQ) == SET)
       {
          for (uint32_t i = 0; i < TransferLength; i++)
          {
                I2C_SendCmd(I2C1, I2C_WRITE_CMD, send_data_buffer[i], DISABLE);
          }
          I2C_ClearINTPendingBit(I2C1, I2C_INT_RD_REQ);
       }
    
       if (I2C_GetINTStatus(I2C1, I2C_INT_STOP_DET) == SET)
       {
          /*read I2C receive data*/
          lenth = I2C_GetRxFIFOLen(I2C1);
          for (uint32_t i = 0; i < lenth; i++)
          {
                I2C_Rev_Data[I2C_Rev_Index++] = I2C_ReceiveData(I2C1);
          }
          I2C_Rev_Data_Lenth += lenth;
          I2C_ClearINTPendingBit(I2C1, I2C_INT_STOP_DET);
          ...
       }
       if (I2C_GetINTStatus(I2C1, I2C_INT_RX_DONE) == SET)
       {
          DBG_DIRECT("I2C1 RX_DONE detected");
          I2C_ClearINTPendingBit(I2C1, I2C_INT_RX_DONE);
    
       }
       ...
    
    }