IR Transmit - DMA Transfer

该示例演示红外发送功能的使用方法。

该实例使用 IR 外设利用 DMA 搬运进行数据发送,实现红外发射功能。

使用逻辑分析仪观察 IR 发送波形。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

连接 IR 发送引脚 P2_5 至逻辑分析仪。

编译和下载

该示例的工程路径如下:

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

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

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

  1. 打开工程文件。

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

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

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

  5. 按下 reset 按键,开始运行。

测试验证

在逻辑分析仪内,观察 IR 发送波形。波形如下图所示:

这里应该是IR发送波形的图片

IR 发送波形

代码介绍

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

  1. 源码路径

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

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

源码路径

  • 工程路径: sdk\board\evb\io_sample\IR\Tx+GDMA

  • 源码路径: sdk\src\sample\io_sample\IR\Tx+GDMA

该工程的工程文件代码结构如下:

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

初始化

当 EVB 复位启动时,执行 main 函数,执行以下流程:

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

    ir_demo();

    ...
}

ir_demo 中,包含了 PAD/PINMUX 设置,IR 外设初始化和 DMA 外设初始化流程。

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

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

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

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

driver_ir_init 为对 IR 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 设置 IR 发送频率为 38kHz。

  3. 设置 IR 载波占空比为 1/3。

  4. 设置 IR 为发送模式。

  5. 设置不反转 IR 发送数据。

  6. 设置 IR 发送 FIFO 阈值为 2。

  7. 使能 IR DMA 发送,设置 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 为对 DMA 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 使用 DMA 通道 1。

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

  4. 源端地址为 GDMA_Send_Buf ,目的端地址为 &IR->TX_FIFO

  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;

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

功能实现

  1. 定义 IR 发送数据数组:有载波数据用载波个数与 0x80000000 进行或运算表示,无载波数据用载波个数与 0x00000000 进行或运算表示。

  2. 执行 IR_Cmd() ,使能 IR 外设发送功能。

    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. 当 DMA 搬运数据完成时,触发 GDMA_INT_Transfer 中断,进入 DMA 中断处理函数 IO_TEST_GDMA_Channel_Handler

    1. 失能 DMA 通道 1 总传输完成中断 GDMA_INT_Transfer ,失能 DMA 通道 1 传输,清除中断挂起位。

    2. 待 IR 波形全部打出后,在逻辑分析仪内可观察到 IR 发送波形。

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