IR Receive - DMA Transfer

该示例演示利用 DMA 接收 IR 数据的使用方法。

使用 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

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

  1. 打开工程文件。

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

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

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

  5. 按下 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 代表高电平。

代码介绍

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

  1. 源码路径

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

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

源码路径

  • 工程路径: 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 设置,包含如下流程:

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

  2. 配置 PINMUX:分配引脚为 TIM_PWM5 功能。

board_ir_init 为 IR 相关引脚的 PAD 与 PINMUX 设置,包含如下流程:

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

  2. 执行 Pad_PullConfigValue() ,将引脚设置为强上拉模式。

  3. 配置 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 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 使能 TIM PWM 输出功能。

  3. 设置 PWM 的高电平和低电平 Counter 值。

  4. 设置 TIM 模式为用户自定义模式。

  5. 初始化完毕后使能 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 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 设置 IR 接收频率为 40MHz。

  3. 设置 IR 为接收模式,启动方式为 IR 自动接收,数据接收方式为下降沿触发。

  4. 设置 IR 接收 FIFO 阈值,当 IR 接收 FIFO 满时丢弃最新数据,过滤低于 50ns 的杂波数据。

  5. 使能 DMA 接收功能,设置 waterLevel。

  6. 使能 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 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 使用 DMA 通道 1。

  3. DMA 的传输方向为外设到内存传输。

  4. 源端地址为 &IR->RX_FIFO ,目的端地址为 GDMA_Recv_Buf

  5. 使能 DMA 通道 1 总传输完成中断 GDMA_INT_Transfer

  6. 使能 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

  1. 失能 DMA 中断,失能 DMA 传输,清除中断标志位。

  2. 打印数据长度和接收数据内容。

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]);
    }
}