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_MASTER_SEND_SLAVE_RECEIVE
:开启该宏可配置示例为主机发送从机接收。I2C_MASTER_RECEIVE_SLAVE_SEND
:开启该宏可配置示例为主机接收从机发送。I2C_MASTER_REPEAT_READ
:开启该宏可配置示例为主机连续发送和接收数据。
编译和下载
该示例的工程路径如下:
Project file: board\evb\io_sample\I2C\Self_Test\mdk
Project file: board\evb\io_sample\I2C\Self_Test\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
开启宏 I2C_MASTER_SEND_SLAVE_RECEIVE
时,此时示例为 I2C 主机发送从机接收。
当 Slave 端接收的数据达到设定的 threshold level 时,触发
I2C_INT_RX_FULL
中断,打印 log。I2C1 Rx Full detected
当 Master 端数据发送完毕时,Slave 端检测到停止信号,Master 端和 Slave 端触发
I2C_INT_STOP_DET
中断,打印 log。I2C0 Stop signal detected I2C1 Stop signal detected
在 Slave 端中断函数内,打印收到的数据长度和数据内容。
Slave Recv Date Lenth = 24 I2C1_Slave_ReceiveData=0 ... I2C1_Slave_ReceiveData=23
开启宏 I2C_MASTER_RECEIVE_SLAVE_SEND
时,此时示例为 I2C 主机接收从机发送。
Slave 端收到 Master 端发来的读请求,触发
I2C_INT_RD_REQ
中断,打印 log。Enter I2C1 interrupt I2C1_INT_RD_REQ!
打印 Master 端接收到的数据。
Master Read data = 10 ... Master Read data = 33
开启宏 I2C_MASTER_REPEAT_READ
时,此时示例为 I2C 主机连续发送和接收数据。
Slave 端收到 Master 端发来的读请求,触发
I2C_INT_RD_REQ
中断,打印 log。Enter I2C1 interrupt I2C1_INT_RD_REQ!
Slave 端收到 Master 端发来的数据,在 Slave 端中断函数内,打印收到的数据长度和数据内容。
Slave Recv Date Lenth = 4 I2C1_Slave_ReceiveData=1 I2C1_Slave_ReceiveData=2 I2C1_Slave_ReceiveData=3 I2C1_Slave_ReceiveData=4
Slave 端接收完毕数据,打印 log。
I2C1 RX_DONE detected
Slave 端给 Master 端回数据。Master 端收数据完成后,打印 log。
I2C0 Stop signal detected
打印 Master 端收到的数据。
Master Repeat Read data = 10 ... Master Repeat Read data = 33
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
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_init
和 board_i2c_slave_init
为对 I2C0 和 I2C1 的 PAD/PINMUX 设置,包含如下流程:
配置 PAD:设置引脚、PINMUX 模式、PowerOn、内部上拉、输出使能、输出高。
配置 PINMUX:分配引脚分别为 I2C0_CLK、I2C0_DAT、I2C1_CLK、I2C1_DAT 功能。
driver_i2c_master_init
为 I2C0 外设的初始化,包含如下流程:
使能 RCC 时钟源。
设置 I2C 时钟频率为 100kHz。
设置设备模式为主机模式。
设置 I2C 地址为 7 位地址模式。
设置从机地址位 0x50。
使能 ACK 功能。
使能 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 外设的初始化,包含如下流程:
使能 RCC 时钟。
设置 I2C 时钟频率为 100kHz。
设置设备模式为从机模式。
设置 I2C 地址为 7 位地址模式。
设置从机地址位 0x50。
使能 ACK 功能。
使能 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
为对于不同宏配置下的中断配置,包含如下流程:
清除各中断挂起位。
若开启宏
I2C_MASTER_SEND_SLAVE_RECEIVE
:
配置 Master 端
I2C_INT_STOP_DET
中断。配置 Slave 端
I2C_INT_RX_FULL
,I2C_INT_STOP_DET
和I2C_INT_RX_DONE
中断。若开启宏
I2C_MASTER_RECEIVE_SLAVE_SEND
,配置 Slave 端I2C_INT_RD_REQ
中断。若开启宏
I2C_MASTER_REPEAT_READ
:
配置 Master 端
I2C_INT_STOP_DET
中断。配置 Slave 端
I2C_INT_RX_FULL
,I2C_INT_STOP_DET
,I2C_INT_RX_DONE
和I2C_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 主机发送从机接收。
初始化阶段完成之后,执行
I2C_MasterWrite()
,主机发送数据给从机,从机接收主机发送的数据。当从机接收的数据量达到设定阈值时触发
I2C_INT_RX_FULL
中断,进入中断处理函数I2C1_Handler
。获取 RX FIFO 中接收数据的长度以及保存接收数据。
记录已经接收的数据长度,清除中断标志位。
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); } }
当从机接收数据,接收到停止信号时,触发
I2C_INT_STOP_DET
中断,进入中断处理函数I2C1_Handler
。获取 RX FIFO 中接收数据的长度以及保存接收数据。
记录已经接收的数据长度,清除中断标志位。
打印接收到的所有数据。
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 主机接收从机发送。
初始化阶段完成后,执行
I2C_MasterRead()
,向 Slave 端发送读请求指令,并等待接收数据。Slave 端检测到读请求指令时触发
I2C_INT_RD_REQ
中断,进入中断处理函数I2C1_Handler
。执行
I2C_SendCmd()
命令发送数据给 Slave 端。发送数据完成后,清除
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); } ... }
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 主机连续发送和接收数据。
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 }
Slave 端接收 Master 端发送的数据,当 Slave 接收 FIFO 满时触发
I2C_INT_RX_FULL
,或者检测到停止信号时触发I2C_INT_STOP_DET
中断,接收 Master 发送的数据。Slave 检测到 RD_REQ 申请时触发
I2C_INT_RD_REQ
中断,将数据发送回 Master 端。