Memory To Memory - Single Block

该示例使用 DMA 的 Single Block 功能,实现 memory 到 memory 搬运数据。

单 Block 最大传输数据为 65535,如果超过该数据量,需要用到 Multi Block 传输,详情参考 Memory to Memory - Multi Block 示例。

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

编译和下载

该示例的工程路径如下:

Project file: board\evb\io_sample\GDMA\Mem2Mem_single_block\mdk

Project file: board\evb\io_sample\GDMA\Mem2Mem_single_block\gcc

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

  1. 打开工程文件。

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

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

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

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

测试验证

EVB 启动复位后,DMA 开始搬运数据。数据搬运完成后,在 Debug Analyzer 工具内会显示传输完成信息。

[io_gdma] io_handle_gdma_msg: GDMA transfer data completion!

备注

如果检测到搬运的数据有误,会在 Debug Analyzer 工具上显示错误数据信息。

代码介绍

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

  1. 源码路径

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

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

源码路径

  • 工程路径: sdk\board\evb\io_sample\GDMA\Mem2Mem_single_block

  • 源码路径: sdk\src\sample\io_sample\GDMA\Mem2Mem_single_block

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

└── Project: adc_continuous_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
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_nvic.c
            └── rtl876x_gdma.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            ├── app_task.c
            └── io_gdma.c

初始化

当 EVB 复位启动时,调用 main() 函数,将执行以下流程:

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    board_init();
    le_gap_init(APP_MAX_LINKS);
    gap_lib_init();
    app_le_gap_init();
    app_le_profile_init();
    pwr_mgr_init();
    task_init();
    os_sched_start();

    return 0;
}

备注

le_gap_init()gap_lib_init()app_le_gap_initapp_le_profile_init 等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。

与外设相关的初始化流程具体如下:

  1. 在执行 os_sched_start() 开启任务调度后,在 app_main_task 主任务内,执行 driver_init 对外设驱动进行初始化配置。

  2. driver_init 中执行 driver_gdma_init,该函数为 DMA 外设的初始化,包含如下流程:

    1. 初始化 DMA 发送数组 GDMA_Send_Buf 与接收数组 GDMA_Recv_Buf

    2. 使能 RCC 时钟。

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

    4. 设置完成一次 DMA 传输的数据个数为 200。

    5. 设置源端地址自增,目的端地址自增。

    6. 设置源端和目的端的数据宽度和单次传输的数据个数。

    7. 设置源端地址为 GDMA_Send_Buf,目的端地址为 GDMA_Recv_Buf

    8. 使能 DMA 传输完成中断 GDMA_INT_Transfer

    void driver_gdma_init(void)
    {
        uint32_t i = 0;
    
        /* Test data buffer */
        for (i = 0; i < GDMA_TRANSFER_SIZE; i++)
        {
            GDMA_Send_Buf[i] = i + 1;
            GDMA_Recv_Buf[i] = 0;
        }
    
        /* Turn on gdma clock */
        RCC_PeriphClockCmd(APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE);
    
        GDMA_InitTypeDef GDMA_InitStruct;
        GDMA_StructInit(&GDMA_InitStruct);
        /* GDMA initial*/
        GDMA_InitStruct.GDMA_ChannelNum          = GDMA_CHANNEL_NUM;
        GDMA_InitStruct.GDMA_DIR                 = GDMA_DIR_MemoryToMemory;
        GDMA_InitStruct.GDMA_BufferSize          = GDMA_TRANSFER_SIZE;
        GDMA_InitStruct.GDMA_SourceInc           = DMA_SourceInc_Inc;
        GDMA_InitStruct.GDMA_DestinationInc      = DMA_DestinationInc_Inc;
        GDMA_InitStruct.GDMA_SourceDataSize      = GDMA_DataSize_Word;
        GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Word;
        GDMA_InitStruct.GDMA_SourceMsize         = GDMA_Msize_8;
        GDMA_InitStruct.GDMA_DestinationMsize    = GDMA_Msize_8;
        GDMA_InitStruct.GDMA_SourceAddr          = (uint32_t)GDMA_Send_Buf;
        GDMA_InitStruct.GDMA_DestinationAddr     = (uint32_t)GDMA_Recv_Buf;
    
        GDMA_Init(GDMA_Channel, &GDMA_InitStruct);
    
        GDMA_INTConfig(GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE);
    
        /* GDMA irq init */
        NVIC_InitTypeDef NVIC_InitStruct;
        NVIC_InitStruct.NVIC_IRQChannel         = GDMA_Channel_IRQn;
        NVIC_InitStruct.NVIC_IRQChannelCmd      = (FunctionalState)ENABLE;
        NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
        NVIC_Init(&NVIC_InitStruct);
    }
    

功能实现

  1. 在主函数中执行 os_sched_start() ,开启任务调度。当 stack 准备好时,在 app_handle_dev_state_evt 函数中执行 GDMA_Cmd() 开启 DMA 搬运。

    void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
    {
        ...
        if (gap_dev_state.gap_init_state != new_state.gap_init_state)
        {
            if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
            {
                APP_PRINT_INFO0("GAP stack ready");
                /*stack ready*/
                GDMA_Cmd(GDMA_CHANNEL_NUM, ENABLE);
            }
        }
        ...
    }
    
  2. DMA 搬运数据完成后,触发 GDMA_INT_Transfer 中断,进入 DMA 中断处理函数。

    1. 关闭 DMA GDMA_INT_Transfer 中断。

    2. 定义消息类型 IO_MSG_TYPE_GDMA ,发送 msg 给 task。在主 task 中,对发送的消息数据进行处理。

    3. 清除 DMA GDMA_INT_Transfer 中断标志位。

    4. 执行 io_handle_gdma_msg ,打印数据传输完成信息,判断 GDMA_Recv_BufGDMA_Send_Buf 是否相同,不相同则打印错误数据。

    void GDMA_Channel_Handler(void)
    {
        GDMA_INTConfig(GDMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE);
    
        T_IO_MSG int_gdma_msg;
    
        int_gdma_msg.type = IO_MSG_TYPE_GDMA;
        int_gdma_msg.subtype = 0;
        if (false == app_send_msg_to_apptask(&int_gdma_msg))
        {
            APP_PRINT_ERROR0("[io_gdma] GDMA_Channel_Handler: Send int_gdma_msg failed!");
            //Add user code here!
            GDMA_ClearINTPendingBit(GDMA_CHANNEL_NUM, GDMA_INT_Transfer);
            return;
        }
    
        GDMA_ClearINTPendingBit(GDMA_CHANNEL_NUM, GDMA_INT_Transfer);
    }
    
    void io_handle_gdma_msg(T_IO_MSG *io_gdma_msg)
    {
        APP_PRINT_INFO0("[io_gdma] io_handle_gdma_msg: GDMA transfer data completion!");
        for (uint32_t i = 0; i < GDMA_TRANSFER_SIZE; i++)
        {
            if (GDMA_Send_Buf[i] != GDMA_Recv_Buf[i])
            {
                APP_PRINT_INFO2("[io_gdma] io_handle_gdma_msg: Data transmission error! GDMA_Send_Buf = %d, GDMA_Recv_Buf = %d",
                                GDMA_Send_Buf[i], GDMA_Recv_Buf[i]);
            }
        }
    }