IR Receive - DMA Transfer
使用 TIM 发送 PWM 波形,模拟 IR 的发送端输出。
将 PWM 输出引脚与 IR 接收引脚连接,通过 DMA 接收数据,在 Debug Analyzer 内打印接收数据信息。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
更多信息请参考 快速入门。
硬件连线
连接 PWM 输出引脚 P2_6 和 IR 接收引脚 P2_5。
编译和下载
该示例的工程路径如下:
Project file: board\evb\io_sample\IR\Rx+GDMA\mdk
Project file: board\evb\io_sample\IR\Rx+GDMA\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
当 DMA 搬运 IR 接收的数据完成时,在 DMA 中断函数内打印搬运的数据长度和内容。
io_handle_gdma_msg: IR_GDMA_Rev_Data_Len = 80
io_handle_gdma_msg: GDMA_Recv_Buf[0] = 0x4e1e
io_handle_gdma_msg: GDMA_Recv_Buf[1] = 0x80004e1f
io_handle_gdma_msg: GDMA_Recv_Buf[2] = 0x4e1f
io_handle_gdma_msg: GDMA_Recv_Buf[3] = 0x80004e1f
...
io_handle_gdma_msg: GDMA_Recv_Buf[78] = 0x4e1f
io_handle_gdma_msg: GDMA_Recv_Buf[79] = 0x80004e1f
备注
0x4e1f 代表 IR 接收低电平的周期数,与 PWM 的 low count 值对应。0x80004e1f 代表 IR 接收高电平的周期数,其中 0x4e1f 与 PWM 的 high count 值对应, 0x80000000 代表高电平。
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\board\evb\io_sample\IR\Rx+GDMA
源码路径:
sdk\src\sample\io_sample\IR\Rx+GDMA
该工程的工程文件代码结构如下:
└── Project: rx+gdma
└── 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
└── adc.lib
├── peripheral includes all peripheral drivers and module code used by the application
├── rtl876x_rcc.c
├── rtl876x_pinmux.c
├── rtl876x_nvic.c
├── rtl876x_gdma.c
├── rtl876x_tim.c
└── rtl876x_ir.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();
ir_demo();
...
}
在 ir_demo
中,包含了 PAD/PINMUX 设置,TIM 外设初始化,IR 外设初始化和 DMA 外设初始化流程。
void ir_demo(void)
{
board_pwm_init();
driver_pwm_init();
TIM_Cmd(PWM_TIMER_NUM, ENABLE);
board_ir_init();
driver_ir_init();
driver_ir_gdma_init();
}
board_pwm_init
为 TIM 相关引脚的 PAD 与 PINMUX 设置,包含如下流程:
配置 PAD:设置引脚、PINMUX 模式、PowerOn、无内部上拉,输出高。
配置 PINMUX:分配引脚为 TIM_PWM5 功能。
board_ir_init
为 IR 相关引脚的 PAD 与 PINMUX 设置,包含如下流程:
配置 PAD:设置引脚、PINMUX 模式、PowerOn、内部上拉,输出失能。
执行
Pad_PullConfigValue()
,将引脚设置为强上拉模式。配置 PINMUX:分配引脚为 IRDA_RX 功能。
void board_ir_init(void) { Pad_Config(IR_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW); Pad_PullConfigValue(IR_RX_PIN, PAD_STRONG_PULL); Pinmux_Config(IR_RX_PIN, IRDA_RX); }
driver_pwm_init
为对 TIM 外设的初始化,包含如下流程:
使能 RCC 时钟。
使能 TIM PWM 输出功能。
设置 PWM 的高电平和低电平 Counter 值。
设置 TIM 模式为用户自定义模式。
初始化完毕后使能 TIM PWM 输出。
void driver_pwm_init(void) { RCC_PeriphClockCmd(APBPeriph_TIMER, APBPeriph_TIMER_CLOCK, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_StructInit(&TIM_InitStruct); TIM_InitStruct.TIM_PWM_En = PWM_ENABLE; TIM_InitStruct.TIM_PWM_High_Count = PWM_HIGH_COUNT; TIM_InitStruct.TIM_PWM_Low_Count = PWM_LOW_COUNT; TIM_InitStruct.TIM_Mode = TIM_Mode_UserDefine; TIM_TimeBaseInit(PWM_TIMER_NUM, &TIM_InitStruct); }
driver_ir_init
为对 IR 外设的初始化,包含如下流程:
使能 RCC 时钟。
设置 IR 接收频率为 40MHz。
设置 IR 为接收模式,启动方式为 IR 自动接收,数据接收方式为下降沿触发。
设置 IR 接收 FIFO 阈值,当 IR 接收 FIFO 满时丢弃最新数据,过滤低于 50ns 的杂波数据。
使能 DMA 接收功能,设置 waterLevel。
使能 IR 外设接收功能,清除 IR 接收 FIFO。
void driver_ir_init(void) { /* Enable IR clock */ RCC_PeriphClockCmd(APBPeriph_IR, APBPeriph_IR_CLOCK, ENABLE); /* Initialize IR */ IR_InitTypeDef IR_InitStruct; IR_StructInit(&IR_InitStruct); IR_InitStruct.IR_Clock = IR_CLOCK_40M; IR_InitStruct.IR_Freq = 40000000; IR_InitStruct.IR_Mode = IR_MODE_RX; IR_InitStruct.IR_RxStartMode = IR_RX_AUTO_MODE; IR_InitStruct.IR_RxFIFOThrLevel = IR_RX_FIFO_THR_LEVEL; IR_InitStruct.IR_RxFIFOFullCtrl = IR_RX_FIFO_FULL_DISCARD_NEWEST; IR_InitStruct.IR_RxFilterTime = IR_RX_FILTER_TIME_50ns; IR_InitStruct.IR_RxTriggerMode = IR_RX_FALL_EDGE; IR_InitStruct.IR_RxCntThrType = IR_RX_Count_High_Level; IR_InitStruct.IR_RxCntThr = 0x0ED8; IR_InitStruct.IR_RxDmaEn = ENABLE; IR_InitStruct.IR_RxWaterLevel = 4; IR_Init(&IR_InitStruct); IR_Cmd(IR_MODE_RX, ENABLE); IR_ClearRxFIFO(); }
driver_ir_gdma_init
为对 DMA 外设的初始化,包含如下流程:
使能 RCC 时钟。
使用 DMA 通道 1。
DMA 的传输方向为外设到内存传输。
源端地址为
&IR->RX_FIFO
,目的端地址为GDMA_Recv_Buf
。使能 DMA 通道 1 总传输完成中断
GDMA_INT_Transfer
。使能 DMA 传输。
void driver_ir_gdma_init(void) { RCC_PeriphClockCmd(APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE); GDMA_InitTypeDef GDMA_InitStruct; memset(GDMA_Recv_Buf, 0, sizeof(GDMA_Recv_Buf)); /*--------------GDMA init-----------------------------*/ GDMA_StructInit(&GDMA_InitStruct); GDMA_InitStruct.GDMA_ChannelNum = IO_TEST_GDMA_CHANNEL_MUM; GDMA_InitStruct.GDMA_BufferSize = IO_TEST_GDMA_TRANSFER_SIZE; GDMA_InitStruct.GDMA_DIR = GDMA_DIR_PeripheralToMemory; GDMA_InitStruct.GDMA_SourceDataSize = GDMA_DataSize_Word; GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Word; GDMA_InitStruct.GDMA_SourceMsize = GDMA_Msize_4; GDMA_InitStruct.GDMA_DestinationMsize = GDMA_Msize_4; GDMA_InitStruct.GDMA_SourceInc = DMA_SourceInc_Fix; GDMA_InitStruct.GDMA_DestinationInc = DMA_DestinationInc_Inc; GDMA_InitStruct.GDMA_SourceAddr = (uint32_t)(&IR->RX_FIFO); GDMA_InitStruct.GDMA_DestinationAddr = (uint32_t)(GDMA_Recv_Buf); GDMA_InitStruct.GDMA_SourceHandshake = GDMA_Handshake_IR_RX; GDMA_Init(IO_TEST_GDMA_Channel, &GDMA_InitStruct); GDMA_INTConfig(IO_TEST_GDMA_CHANNEL_MUM, GDMA_INT_Transfer, ENABLE); /*-----------------GDMA IRQ init-------------------*/ NVIC_InitTypeDef nvic_init_struct; nvic_init_struct.NVIC_IRQChannel = IO_TEST_GDMA_Channel_IRQn; nvic_init_struct.NVIC_IRQChannelCmd = (FunctionalState)ENABLE; nvic_init_struct.NVIC_IRQChannelPriority = 3; NVIC_Init(&nvic_init_struct); GDMA_Cmd(IO_TEST_GDMA_CHANNEL_MUM, ENABLE); }
功能实现
当 DMA 接收数据完成时,触发 GDMA_INT_Transfer
中断,进入 DMA 中断处理函数 IO_TEST_GDMA_Channel_Handler
。
失能 DMA 中断,失能 DMA 传输,清除中断标志位。
打印数据长度和接收数据内容。
void IO_TEST_GDMA_Channel_Handler(void) { GDMA_INTConfig(IO_TEST_GDMA_CHANNEL_MUM, GDMA_INT_Transfer, DISABLE); GDMA_Cmd(IO_TEST_GDMA_CHANNEL_MUM, DISABLE); IR_GDMA_Rev_Data_Len = IO_TEST_GDMA_TRANSFER_SIZE; io_handle_gdma_msg(); GDMA_ClearINTPendingBit(IO_TEST_GDMA_CHANNEL_MUM, GDMA_INT_Transfer); } void io_handle_gdma_msg(void) { DBG_DIRECT("io_handle_gdma_msg: IR_GDMA_Rev_Data_Len = %d \r\n", IR_GDMA_Rev_Data_Len); for (uint32_t i = 0; i < IR_GDMA_Rev_Data_Len; i++) { DBG_DIRECT("io_handle_gdma_msg: GDMA_Recv_Buf[%d] = 0x%x \r\n", i, GDMA_Recv_Buf[i]); } }