IR Receive - DMA Transfer

This example demonstrates how to use DMA to receive IR data.

Use TIM to send PWM waveforms, simulating the output of the IR transmitter.

Connect the PWM output pin to the IR receiving pin, receive data through DMA, and print the received data information in the Debug Analyzer.

Requirements

The sample supports the following development kits:

Development Kits

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

For more requirements, please refer to Quick Start.

Wiring

Connect the PWM output pin P2_6 and the IR receiving pin P2_5.

Building and Downloading

This sample can be found in the SDK folder:

Project file: board\evb\io_sample\IR\Rx+GDMA\mdk

Project file: board\evb\io_sample\IR\Rx+GDMA\gcc

Please follow these steps to build and run the example:

  1. Open sample project file.

  2. To build the target, follow the steps listed on the Generating App Image in Quick Start.

  3. After a successful compilation, the app bin app_MP_xxx.bin will be generated in the directory mdk\bin or gcc\bin.

  4. To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.

  5. Press reset button on EVB board and it will start running.

Experimental Verification

When DMA completes the transfer of IR received data, print the length and content of the transferred data within the DMA interrupt function.

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

Note

0x4e1f represents the number of cycles of the IR receiver’s low level, corresponding to the low count value of the PWM. 0x80004e1f represents the number of cycles of the IR receiver’s high level, where 0x4e1f corresponds to the high count value of the PWM, and 0x80000000 represents the high level.

Code Overview

This chapter will be introduced according to the following several parts:

  1. Source Code Directory.

  2. Peripheral initialization will be introduced in chapter Initialization.

  3. Functional implementation after initialization will be introduced in chapter Function Implementation.

Source Code Directory

  • Project directory: sdk\board\evb\io_sample\IR\Rx+GDMA

  • Source code directory: sdk\src\sample\io_sample\IR\Rx+GDMA

Source files are currently categorized into several groups as below.

└── 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

Initialization

When the EVB is reset, the main function is executed, following these steps:

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

    ir_demo();

    ...
}

In ir_demo, it includes the PAD/PINMUX settings, TIM peripheral initialization, IR peripheral initialization, and DMA peripheral initialization processes.

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 sets PAD and PINMUX for TIM related pins, including the following steps:

  1. Configure PAD: set pin, PINMUX mode, PowerOn, internal pull-none, output high.

  2. Configure PINMUX: assign pin to TIM_PWM5 function.

board_ir_init sets PAD and PINMUX for IR related pins, including the following steps:

  1. Configure PAD: set pin, PINMUX mode, PowerOn, internal pull-up, output disabled.

  2. Execute Pad_PullConfigValue() to set the pin to strong pull-up mode.

  3. Configure PINMUX: assign pin to IRDA_RX function.

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 is the initialization of the TIM peripheral and includes the following processes:

  1. Enable the RCC clock.

  2. Enable the TIM PWM output function.

  3. Set the Counter values for the PWM’s high and low levels.

  4. Set the TIM mode to user-defined mode.

  5. Enable the TIM PWM output.

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 is the initialization function for the IR peripheral, including the following steps:

  1. Enable the RCC clock.

  2. Set the IR receiving frequency to 40MHz.

  3. Set the IR to receiving mode, with the start mode as IR automatic reception, and data reception triggered by the falling edge.

  4. Set the IR receiving FIFO threshold, discard the latest data when the IR receiving FIFO is full, and filter out noise data below 50ns.

  5. Enable the DMA receiving function and set the waterLevel.

  6. Enable the IR peripheral receiving function and clear the IR receiving 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 is the initialization of the DMA peripheral, including the following steps:

  1. Enable the RCC clock.

  2. Use DMA channel 1.

  3. Set the DMA transfer direction to peripheral-to-memory.

  4. The source address is &IR->RX_FIFO and the destination address is GDMA_Recv_Buf.

  5. Enable the DMA channel 1 transfer complete interrupt GDMA_INT_Transfer.

  6. Enable DMA transfer.

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

Functional Implementation

When DMA data reception is complete, it triggers the GDMA_INT_Transfer interrupt and enters the DMA interrupt handler function IO_TEST_GDMA_Channel_Handler.

  1. Disable DMA interrupt, disable DMA transfer, and clear the interrupt flag.

  2. Print the data length and received data content.

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