IR Transmit - DMA Transfer

This example demonstrates the use of the infrared transmission function.

This instance uses the IR peripheral to send data via DMA, achieving the infrared transmission function.

A logic analyzer is used to observe the IR transmission waveform.

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 IR transmit pin P2_5 to the logic analyzer.

Building and Downloading

This sample can be found in the SDK folder:

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

Project file: board\evb\io_sample\IR\Tx+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

Observe the IR transmit waveform with a logic analyzer.

Here should be a picture of the IR sending waveforms

IR Transmit Waveform

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\Tx+GDMA

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

Source files are currently categorized into several groups as below.

└── Project: tx_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_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, IR peripheral initialization, and DMA peripheral initialization processes.

void ir_demo(void)
{
    ...
    board_ir_init();
    driver_ir_init(IR_Send_Data.CarrierFreq);
    driver_ir_gdma_init();
    ...
}

board_ir_init is for PAD and PINMUX settings related to IR pins, including the following steps:

  1. Configure PAD: Set pin, PINMUX mode, PowerOn, internal pull-none, and output low.

  2. Configure PINMUX: Assign pin to IRDA_TX function.

driver_ir_init is for initializing the IR peripheral, including the following steps:

  1. Enable RCC clock.

  2. Set IR transmission frequency to 38kHz.

  3. Set IR carrier duty cycle to 1/3.

  4. Set IR to transmission mode.

  5. Set IR transmission data to non-inverted.

  6. Set IR transmission FIFO threshold to 2.

  7. Enable IR DMA transmission and set waterLevel.

void driver_ir_init(uint32_t vFreq)
{
    /* Enable ir clock */
    RCC_PeriphClockCmd(APBPeriph_IR, APBPeriph_IR_CLOCK, ENABLE);

    /* Initialize ir */
    IR_InitTypeDef IR_InitStruct;
    IR_StructInit(&IR_InitStruct);
    IR_InitStruct.IR_Freq           = vFreq;//vFreq;
    IR_InitStruct.IR_DutyCycle      = 3; /* !< 1/3 duty cycle */
    IR_InitStruct.IR_Mode           = IR_MODE_TX;
    IR_InitStruct.IR_TxInverse      = IR_TX_DATA_NORMAL;
    IR_InitStruct.IR_TxFIFOThrLevel = IR_TX_FIFO_THR_LEVEL;
    IR_InitStruct.IR_TxDmaEn        = ENABLE;
    IR_InitStruct.IR_TxWaterLevel   = 15;

    IR_Init(&IR_InitStruct);
}

driver_ir_gdma_init is the initialization for the DMA peripheral, including the following processes:

  1. Enable the RCC clock.

  2. Use DMA channel 1.

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

  4. Set the source address to GDMA_Send_Buf and destination address to &IR->TX_FIFO.

  5. Enable the DMA channel 1 total 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;

    /*--------------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_MemoryToPeripheral;
    GDMA_InitStruct.GDMA_SourceInc          = DMA_SourceInc_Inc;
    GDMA_InitStruct.GDMA_DestinationInc     = DMA_DestinationInc_Fix;
    GDMA_InitStruct.GDMA_SourceDataSize         = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_DestinationDataSize    = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_SourceMsize            = GDMA_Msize_1;
    GDMA_InitStruct.GDMA_DestinationMsize       = GDMA_Msize_1;
    GDMA_InitStruct.GDMA_SourceAddr         = (uint32_t)(GDMA_Send_Buf);
    GDMA_InitStruct.GDMA_DestinationAddr    = (uint32_t)(&IR->TX_FIFO);
    GDMA_InitStruct.GDMA_DestHandshake      = GDMA_Handshake_IR_TX;
    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

  1. Define the IR transmission data array: carrier data is represented by performing an OR operation between the number of carriers and 0x80000000, while non-carrier data is represented by performing an OR operation between the number of carriers and 0x00000000.

  2. Execute IR_Cmd() to enable the IR peripheral transmission function.

    void ir_demo(void)
    {
        /* Data to send */
        IR_Send_Data.CarrierFreq = 38000;
        IR_Send_Data.DataLen = IO_TEST_GDMA_TRANSFER_SIZE;
        IR_Send_Data.DataBuf[0] =  0x80000000 | 0x200;
        IR_Send_Data.DataBuf[1] =  0x00000000 | 0x100;
        for (uint16_t i = 2; i < IR_Send_Data.DataLen - 1;)
        {
            IR_Send_Data.DataBuf[i] =  0x80000000 | (0x0A + i * 5);
            IR_Send_Data.DataBuf[i + 1] =  0x00000000 | (0x14 + i * 5);
            i += 2;
        }
        IR_Send_Data.DataBuf[IR_Send_Data.DataLen - 1] =  0x80000000 | 0x800;
    
        /* Test data buffer */
        for (uint32_t i = 0; i < IO_TEST_GDMA_TRANSFER_SIZE; i++)
        {
            GDMA_Send_Buf[i] = IR_Send_Data.DataBuf[i];
        }
    
        ...
        IR_Cmd(IR_MODE_TX, ENABLE);
    }
    
  3. When the DMA has completed transferring the data, it triggers the GDMA_INT_Transfer interrupt and enters the DMA interrupt handler function IO_TEST_GDMA_Channel_Handler.

    1. Disable the DMA channel 1 total transfer completion interrupt GDMA_INT_Transfer, disable DMA channel 1 transfer, and clear the interrupt pending bit.

    2. After all IR waveforms have been transmitted, the IR transmission waveform can be observed on the logic analyzer.

    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);
        DBG_DIRECT("IO_TEST_GDMA_Channel_Handler\r\n");
        GDMA_ClearINTPendingBit(IO_TEST_GDMA_CHANNEL_MUM, GDMA_INT_Transfer);
    }