Self Test

该示例演示使用 I2C 进行自发自收的通信。

将 I2C0 作为主机读取 I2C1 传递的数据长度和数据信息,I2C1 作为从机接收 I2C0 发送数据长度和数据信息,实现 I2C 的通信功能。

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

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

上拉电阻的连接示意图如下图所示:

这里应该是I2C上拉电阻示意图

I2C 上拉电阻连接示意图

配置选项

该示例可配置的宏如下:

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

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

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

编译和下载

该示例的工程路径如下:

Project file: board\evb\io_sample\I2C\Self_Test\mdk

Project file: board\evb\io_sample\I2C\Self_Test\gcc

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

  1. 打开工程文件。

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

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

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

  5. 按下 reset 按键,开始运行。

测试验证

开启宏 I2C_MASTER_SEND_SLAVE_RECEIVE 时,此时示例为 I2C 主机发送从机接收。

  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_MASTER_RECEIVE_SLAVE_SEND 时,此时示例为 I2C 主机接收从机发送。

  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_MASTER_REPEAT_READ 时,此时示例为 I2C 主机连续发送和接收数据。

  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\board\evb\io_sample\I2C\Self_Test

  • 源码路径: sdk\src\sample\io_sample\I2C\Self_Test

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

└── Project: self_test
    └── secure_only_app
        └── include
            ├── app_define.h
            └── rom_uuid.h
        ├── cmsis                    includes CMSIS header files and startup files
            ├── overlay_mgr.c
            ├── system_rtl876x.c
            └── startup_rtl876x.s
        ├── lib                      includes all binary symbol files that user application is built on
            ├── rtl8752h_sdk.lib
            ├── gap_utils.lib
            └── ROM.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_pinmux.c
            ├── rtl876x_nvic.c
            └── rtl876x_i2c.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            └── main.c

初始化

当 EVB 复位启动时,执行 main 函数,执行以下流程:

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    __enable_irq();
    i2c_demo();

    while (1)
    {
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
    }
}

i2c_demo 中,包含了 PAD/PINMUX 设置,I2C 外设的初始化,中断配置初始化等流程。

void i2c_demo(void)
{
    /* Configure pad and pinmux firstly! */
    board_i2c_master_init();
    board_i2c_slave_init();

    /* Initialize i2c peripheral */
    driver_i2c_master_init();
    driver_i2c_slave_init();

    /* Config i2c nvic */
    nvic_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. 使能 RCC 时钟源。

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

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

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

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

  6. 使能 ACK 功能。

  7. 使能 I2C0。

void driver_i2c_master_init(void)
{
    /* Initialize I2C peripheral */
    RCC_PeriphClockCmd(APBPeriph_I2C0, APBPeriph_I2C0_CLOCK, ENABLE);

    I2C_InitTypeDef  I2C_InitStruct;
    I2C_StructInit(&I2C_InitStruct);

    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              = I2C_Ack_Enable;

    I2C_Init(I2C0, &I2C_InitStruct);
    I2C_Cmd(I2C0, ENABLE);
}

driver_i2c_slave_init 为 I2C1 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

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

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

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

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

  6. 使能 ACK 功能。

  7. 使能 I2C1。

void driver_i2c_slave_init(void)
{
    /* Initialize I2C peripheral */
    RCC_PeriphClockCmd(APBPeriph_I2C1, APBPeriph_I2C1_CLOCK, ENABLE);

    I2C_InitTypeDef  I2C_InitStruct;
    I2C_StructInit(&I2C_InitStruct);

    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               = I2C_Ack_Enable;

    I2C_Init(I2C1, &I2C_InitStruct);
    I2C_Cmd(I2C1, ENABLE);
}

nvic_i2c_config 为对于不同宏配置下的中断配置,包含如下流程:

  1. 清除各中断挂起位。

  2. 若开启宏 I2C_MASTER_SEND_SLAVE_RECEIVE

    1. 配置 Master 端 I2C_INT_STOP_DET 中断。

    2. 配置 Slave 端 I2C_INT_RX_FULLI2C_INT_STOP_DETI2C_INT_RX_DONE 中断。

  3. 若开启宏 I2C_MASTER_RECEIVE_SLAVE_SEND ,配置 Slave 端 I2C_INT_RD_REQ 中断。

  4. 若开启宏 I2C_MASTER_REPEAT_READ

    1. 配置 Master 端 I2C_INT_STOP_DET 中断。

    2. 配置 Slave 端 I2C_INT_RX_FULLI2C_INT_STOP_DETI2C_INT_RX_DONEI2C_INT_RD_REQ 中断。

void nvic_i2c_config(void)
{
    /* Detect stop signal */
    I2C_ClearINTPendingBit(I2C0, I2C_INT_STOP_DET);
    I2C_ClearINTPendingBit(I2C1, I2C_INT_STOP_DET);
    /* Detect read require signal */
    I2C_ClearINTPendingBit(I2C1, I2C_INT_RD_REQ);
    /* Detect rx fifo full signal */
    I2C_ClearINTPendingBit(I2C1, I2C_INT_RX_FULL);
    /* Detect rx transmission finished signal */
    I2C_ClearINTPendingBit(I2C1, I2C_INT_RX_DONE);

#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

    /* Config I2C interrupt */
    NVIC_InitTypeDef NVIC_InitStruct;

    NVIC_InitStruct.NVIC_IRQChannel = I2C0_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = I2C1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

功能实现

开启宏 I2C_MASTER_SEND_SLAVE_RECEIVE 时,此时示例为 I2C 主机发送从机接收。

  1. 初始化阶段完成之后,执行 I2C_MasterWrite() ,主机发送数据给从机,从机接收主机发送的数据。

  2. 当从机接收的数据量达到设定阈值时触发 I2C_INT_RX_FULL 中断,进入中断处理函数 I2C1_Handler

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

    2. 记录已经接收的数据长度,清除中断标志位。

    void I2C1_Handler(void)
    {
        ...
        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);
        }
    
    }
    
  3. 当从机接收数据,接收到停止信号时,触发 I2C_INT_STOP_DET 中断,进入中断处理函数 I2C1_Handler

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

    2. 记录已经接收的数据长度,清除中断标志位。

    3. 打印接收到的所有数据。

    void I2C1_Handler(void)
    {
        ...
        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);
    
            DBG_DIRECT("Slave Recv Date Lenth = %d", I2C_Rev_Data_Lenth);
    
            for (uint32_t i = 0; i < I2C_Rev_Data_Lenth; i++)
            {
                DBG_DIRECT("I2C1_Slave_ReceiveData=%d\n", I2C_Rev_Data[i]);
            }
    
        }
    
    }
    

开启宏 I2C_MASTER_RECEIVE_SLAVE_SEND 时,此时示例为 I2C 主机接收从机发送。

  1. 初始化阶段完成后,执行 I2C_MasterRead() ,向 Slave 端发送读请求指令,并等待接收数据。

  2. Slave 端检测到读请求指令时触发 I2C_INT_RD_REQ 中断,进入中断处理函数 I2C1_Handler

    1. 执行 I2C_SendCmd() 命令发送数据给 Slave 端。

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

    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)
        {
            DBG_DIRECT("Enter I2C1 interrupt I2C1_INT_RD_REQ!");
    
            for (uint32_t i = 0; i < TransferLength; i++)
            {
                I2C_SendCmd(I2C1, I2C_WRITE_CMD, send_data_buffer[i], I2C_STOP_DISABLE);
            }
    
            I2C_ClearINTPendingBit(I2C1, I2C_INT_RD_REQ);
    
        }
        ...
    }
    
  3. Master 端接收完毕数据后打印数据。

    void i2c_demo(void)
    {
        ...
    #if I2C_MASTER_RECEIVE_SLAVE_SEND
        /* I2C master read */
        uint8_t data[TransferLength] = {0};
        I2C_MasterRead(I2C0, data, TransferLength);
    
        for (uint32_t i = 0; i < TransferLength ; i++)
        {
            DBG_DIRECT("Master Read data = %d\n", data[i]);
        }
    #endif
        ...
    }
    

开启宏 I2C_MASTER_REPEAT_READ 时,此时示例为 I2C 主机连续发送和接收数据。

  1. Master 端执行 I2C_RepeatRead() 发送数据给 Slave 端,并等待 Slave 端回复数据。

    void i2c_demo(void)
    {
        ...
    #if I2C_MASTER_REPEAT_READ
        /* I2C master repeat read */
        uint8_t tx_data[10] = {01, 02, 03, 04};
        uint8_t rx_data[TransferLength] = {0};
        I2C_RepeatRead(I2C0, tx_data, 4, rx_data, TransferLength);
    
        for (uint32_t i = 0; i < TransferLength ; i++)
        {
            DBG_DIRECT("Master Repeat Read data = %d\n", rx_data[i]);
        }
    #endif
    }
    
  2. Slave 端接收 Master 端发送的数据,当 Slave 接收 FIFO 满时触发 I2C_INT_RX_FULL ,或者检测到停止信号时触发 I2C_INT_STOP_DET 中断,接收 Master 发送的数据。

  3. Slave 检测到 RD_REQ 申请时触发 I2C_INT_RD_REQ 中断,将数据发送回 Master 端。