LoopBack

该示例演示使用 I2C 进行自发自收的通信。 将I2C0作为主机读取I2C1传递的数据长度和数据信息,I2C1作为从机接收I2C0发送数据长度和数据信息,实现I2C的通信功能。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考 快速入门

硬件连线

连接P4_0(I2C0的SCL)和P4_2(I2C1的SCL),P4_1(I2C0的SDA)和P4_3(I2C1的SDA),接线时需要接2.2k上拉电阻。

配置选项

该示例可配置的宏如下:

  1. I2C_MASTER_SEND_SLAVE_RECEIVE :开启该宏可配置示例为主机发送从机接收。

  2. I2C_MASTER_RECEIVE_SLAVE_SEND :开启该宏可配置示例为主机接收从机发送。

  3. I2C_MASTER_REPEAT_READ :开启该宏可配置示例为主机连续发送和接收数据。

编译和下载

该示例的工程路径如下:

Project file: samples\peripheral\i2c\trx_loopback\proj\rtl87x2g\mdk

Project file: samples\peripheral\i2c\trx_loopback\proj\rtl87x2g\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MPTool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

开启I2C主机发送从机接收 I2C_MASTER_SEND_SLAVE_RECEIVE 时:

  1. 当SLAVE端接收的数据达到设定的threshold level时,触发 I2C_INT_RX_FULL 中断,打印LOG。

    I2C1 Rx Full detected
    
  2. 当MASTER端数据发送完毕时,SLAVE端也检测到停止信号,MASTER端和SLAVE端触发 I2C_INT_STOP_DET 中断,打印LOG。

    I2C0 Stop signal detected
    I2C1 Stop signal detected
    
  3. 在SLAVE端中断函数内,打印收到的数据长度和数据内容。

    Slave Recv Date Lenth = 24
    I2C1_Slave_ReceiveData=0
    ...
    I2C1_Slave_ReceiveData=23
    

开启I2C主机接收从机发送 I2C_MASTER_RECEIVE_SLAVE_SEND 时:

  1. SLAVE端收到MASTER端发来的读请求,触发 I2C_INT_RD_REQ 中断,打印LOG 。

    Enter I2C1 interrupt I2C1_INT_RD_REQ!
    
  2. 打印MASTER端接收到的数据。

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

开启I2C主机连续发送和接收数据 I2C_MASTER_REPEAT_READ 时:

  1. SLAVE端收到MASTER端发来的读请求,触发 I2C_INT_RD_REQ 中断,打印LOG。

    Enter I2C1 interrupt I2C1_INT_RD_REQ!
    
  2. SLAVE端收到MASTER端发来的数据,在SLAVE端中断函数内,打印收到的数据长度和数据内容。

    Slave Recv Date Lenth = 4
    I2C1_Slave_ReceiveData=1
    I2C1_Slave_ReceiveData=2
    I2C1_Slave_ReceiveData=3
    I2C1_Slave_ReceiveData=4
    
  3. SLAVE端接收完毕数据,打印LOG。

    I2C1 RX_DONE detected
    
  4. SLAVE端给MASTER端回数据。MASTER端收数据完成后,打印LOG。

    I2C0 Stop signal detected
    
  5. 打印MASTER端收到的数据。

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

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  • 工程路径: sdk\samples\peripheral\i2c\trx_loopback\proj

  • 源码路径: sdk\samples\peripheral\i2c\trx_loopback\src

该工程的工程文件代码结构如下:

└── Project: trx_loopback
    └── secure_only_app
        └── Device                   includes startup code
            ├── startup_rtl.c
            └── system_rtl.c
        ├── CMSIS                    includes CMSIS header files
        ├── CMSE Library             Non-secure callable lib
        ├── Lib                      includes all binary symbol files that user application is built on
            └── rtl87x2g_io.lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl_rcc.c
            ├── rtl_pinmux.c
            ├── rtl_nvic.c
            ├── rtl_gdma.c
            └── rtl_i2c.c
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c
            └── io_i2c.c

初始化

初始化流程包括了 board_i2c_master_initboard_i2c_slave_initdriver_i2c_master_initdriver_i2c_slave_initnvic_i2c_config


board_i2c_master_initboard_i2c_slave_init 中包含了对I2C0和I2C1的PAD与PINMUX设置:

  1. 配置PAD:设置引脚、PINMUX模式、PowerOn、内部上拉、输出使能、输出高

  2. 配置PINMUX:分配引脚分别为I2C0_CLK、I2C0_DAT、I2C1_CLK、I2C1_DAT功能。


driver_i2c_master_init 包含了对I2C0外设的初始化:

  1. 使能PCC时钟源。

  2. 设置I2C时钟频率为100k。

  3. 设置设备模式为主机模式。

  4. 设置I2C地址为7位地址模式。

  5. 设置从机地址位0x50。

  6. 使能ACK功能。

  7. 使能I2C0。

RCC_PeriphClockCmd(APBPeriph_I2C0, APBPeriph_I2C0_CLOCK, ENABLE);
...
I2C_InitStruct.I2C_ClockSpeed       = 100000;
I2C_InitStruct.I2C_DeviveMode       = I2C_DeviveMode_Master;
I2C_InitStruct.I2C_AddressMode      = I2C_AddressMode_7BIT;
I2C_InitStruct.I2C_SlaveAddress     = 0x50;
I2C_InitStruct.I2C_RxThresholdLevel = 8;
I2C_InitStruct.I2C_Ack              = ENABLE;

driver_i2c_slave_init 包含了对I2C1外设的初始化:

  1. 使能PCC时钟。

  2. 设置I2C时钟频率为100k。

  3. 设置设备模式为从机模式。

  4. 设置I2C地址为7位地址模式。

  5. 设置从机地址位0x50。

  6. 使能ACK功能。

  7. 使能I2C1。

RCC_PeriphClockCmd(APBPeriph_I2C1, APBPeriph_I2C1_CLOCK, ENABLE);
...
I2C_InitStruct.I2C_ClockSpeed        = 100000;
I2C_InitStruct.I2C_DeviveMode        = I2C_DeviveMode_Slave;
I2C_InitStruct.I2C_AddressMode       = I2C_AddressMode_7BIT;
I2C_InitStruct.I2C_SlaveAddress      = 0x50;
I2C_InitStruct.I2C_RxThresholdLevel  = 8;
I2C_InitStruct.I2C_Ack               = ENABLE;

nvic_i2c_config 包含了对中断的配置

  1. 清除中断挂起位

  2. 根据程序中具体发送或接收设定使能I2C0和I2C1相关中断。

  I2C_ClearINTPendingBit(I2C0, I2C_INT_STOP_DET);
  ...

#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

#if I2C_MASTER_RECEIVE_SLAVE_SEND
  I2C_INTConfig(I2C1, I2C_INT_RD_REQ, ENABLE);
#endif

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

功能实现

开启I2C主机发送从机接收 I2C_MASTER_SEND_SLAVE_RECEIVE 时:

  1. 执行 I2C_MasterWrite() ,主机发送write_data数组中的数据给从机。

  2. 从机接收主机发送的数据,当从机接收达到threshold时触发 I2C_INT_RX_FULL ,或者检测到停止信号时触发 I2C_INT_STOP_DET 中断,进入中断处理函数 I2C1_Handler

  3. 判断检测到接收满状态为SET时:

    1. 获取RX FIFO中接收数据的长度以及保存接收数据。

    2. 清除 I2C_INT_RX_FULL 中断挂起位。

  4. 判断从机检测到停止信号状态为SET时:

    1. 获取RX FIFO中接收数据的长度以及保存接收数据。

    2. 打印接收数据总长度以及接收数据信息。

    3. 清除 I2C_INT_STOP_DET 中断挂起位。

if (I2C_GetINTStatus(I2C1, I2C_INT_STOP_DET) == SET)
{
   DBG_DIRECT("I2C1 Stop signal detected");

   /*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_FULL) == SET)
{
   DBG_DIRECT("I2C1 Rx Full detected");
   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);
}

开启I2C主机接收从机发送 I2C_MASTER_RECEIVE_SLAVE_SEND 时:

  1. 主机通过 I2C_MasterRead() 发送命令给从机。

  2. 从机检测到RD_REQ申请时触发 I2C_INT_RD_REQ 中断,进入中断处理函数 I2C1_Handler

    1. 执行 I2C_SendCmd() 命令发送数据给主机。

    2. 发送数据完成后,清除 I2C_INT_RD_REQ 中断挂起位。

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);
  1. 打印主机收到的数据。


开启I2C主机连续发送和接收数据 I2C_MASTER_REPEAT_READ 时:

  1. 主机发送 I2C_RepeatRead() 命令给从机,发送tx_data中的数据,接收数据存储在rx_data中。

uint8_t tx_data[10] = {01, 02, 03, 04};
uint8_t rx_data[TransferLength] = {0};
I2C_RepeatRead(I2C0, tx_data, 4, rx_data, TransferLength);
  1. 从机接收主机发送的数据,当从机接收FIFO满时触发I2C_INT_RX_FULL,或者检测到停止信号时触发 I2C_INT_STOP_DET 中断;从机检测到RD_REQ申请时触发 I2C_INT_RD_REQ 中断,进入中断处理函数 I2C1_Handler (同上)。