瑞萨单片机开发教程[二十六] DMAC 存储器数据传输实验

开发板官网https://www.edevkit.com/)

本系列教程目录:


DMAC 存储器数据传输实验

本章目标:

  • 了解 DMA 基本概念和 RA6M5 处理器的 DMAC 模块
  • 学会使用 RASC 配置 DMAC 在指定内存间搬移数据

硬件设计(略,参见配套硬件原理图)

软件设计

新建工程

复制一份"07 SCI-UART_Recv_send"并修改文件夹名为"08 DMAC-Memory",双击打开"EBF_RA6M5.uvprojx"工程,通过 Keil 的新建文件新建"dmac_memory.c"和"dmac_memory.h",保存到工程目录下的 \\src 文件夹下。

FSP 配置

Keil 界面选择"Tools"→"RA Smart Configurator"打开 RASC 界面。

在 FSP 配置界面里面点开"Stacks"界面,"New Stack"→"Transfer"→"r_dmac"新建两个 DMAC 模块。

添加后,点击模块,在左下角属性界面配置参数。

属性 描述
Name 名字 。上图配置为 g_transfer_dmac0, 按照自己命名习惯设置、并且与代码中的对应即可。
Channel 通道 :DMA 传输通道选择。这里选择通道 0,通道 0 也是优先级最高的通道。 RA6M5 有 8 个通道,0~7 可选。其他 MCU 型号可能不同。
Mode 模式:DMA 传输模式选择。默认选择正常模式即可。
Transfer Size 传输大小:传输数据单元的大小。默认设置为 2 字节即可。
Destination Address Mode 目标地址模式:默认设置为固定。
Source Address Mode 源地址模式:默认设置为固定。
Repeat Area (Unused in Normal Mode) 重复区域(正常模式下不使用):源地址。
Destination Pointer 目标指针:NULL(在这里不方便确定传输地址,后续在代码部分配置)。
Source Pointer 源指针:NULL(在这里不方便确定传输地址,后续在代码部分配置)。
Number of Transfers 传输次数:指定正常和重复模式的传输次数 或 重复-块传输模式的块大小。
Number of Blocks (Valid only in Repeat, Block or Repeat-Block Mode) 块数量:指定要在重复、块或重复-块模式下传输的块数。
Activation Source 触发源 :选择 DMAC 传输开始事件。 如果未选择 ELC 事件,则可以使用软件启动。
Callback 回调函数 :触发 DMAC 中断时调用的回调函数。 此处设置为 dmac0_callback
Context 上下文:指向通过回调函数传递的上下文结构的指针。
Transfer End Interrupt Priority 传输结束中断优先级:传输结束中断的优先级设置。
Interrupt Frequency 中断频率 (选择什么时候触发中断): 所有传输都完成后产生中断。 传输完每一个块或重复大小的数据时都产生中断。
Offset value 偏移 : 偏移值在每次传输后添加到传输地址。 (仅当地址模式为"偏移"时有效)
Source Buffer Size 源缓冲大小 : 指定整个源缓冲区的大小。 (仅适用于带有源地址更新模式的重复-块传输模式,不适用于地址偏移模式)

配置完成之后可以按下快捷键"Ctrl + S"保存,最后点击"Generate Project Content"按钮,让软件自动生成配置代码即可。

程序设计

实现两个数组内存间数据传输,配置 DMAC 的源地址和目标地址,开启传输后对比发送和接收的数据是否相同,相同则表明传输成功。

  1. 打开 DMAC 设备对象

    fsp_err_t R_DMAC_Open(transfer_ctrl_t * const p_ctrl,
    transfer_cfg_t const * const p_cfg);

p_ctrl:用来记录一些状态信息;

p_cfg:配置信息,实际上就是指向 RASC 生成的 g_dma0_cfg 结构体;

  1. 关闭 DMAC 设备对象

    fsp_err_t R_DMAC_Close(transfer_ctrl_t * const p_ctrl);

close 函数会将 DMAC 设备对象的 open 标志位设置为 CLOSED,并且关闭中断。

  1. 使能 DMAC 的数据传输

    fsp_err_t R_DMAC_Enable(transfer_ctrl_t * const p_ctrl);

可能会在其它的 API 中使能 DMAC 的数据传输功能,因而此函数并非一定要手动调用,例如 reset 函数内就会在最后使能 DMAC 的数据传输。

  1. 软件开启 DMAC 的数据传输

    fsp_err_t R_DMAC_SoftwareStart(transfer_ctrl_t * const p_ctrl,
    transfer_start_mode_t mode);

mode 参数是 transfer_start_mode_t 枚举类型,可以选择每次软件触发传输时,是只传输一个数据,还是传输多个数据直到所有数据传输完成,此枚举的原型如下:

复制代码
typedef enum e_transfer_start_mode
{
    TRANSFER_START_MODE_SINGLE = 0,
    TRANSFER_START_MODE_REPEAT = 1
} transfer_start_mode_t;
  1. 软件停止 DMAC 的数据传输

    fsp_err_t R_DMAC_SoftwareStop(transfer_ctrl_t * const p_ctrl);

只有在使用 softwareStart(..., TRANSFER_START_MODE_REPEAT) 模式的软件触发 DMAC 时,才能使用 softwareStop 函数中断 DMAC 的传输。

程序编写

dmac_memory.h 头文件里定义初始化函数,设置数据长度,设置数据地址函数。

复制代码
#ifndef __BSP_DMAC_MEMORY_H
#define __BSP_DMAC_MEMORY_H
#include "hal_data.h"

//(1)   //正常模式 
//(2)   //重复模式
//(3)   //块模式
//(4)   //重复-块模式
#define DMAC_TRANSFER_MODE 2        

// 用户要发送的数据大小
#define BUFFER_SIZE         32

void set_transfer_dst_src_address( transfer_cfg_t const * const p_config,
                                   void const * volatile p_src,
                                   void const * volatile p_dest );
void dmac_Init(void);
void dmac_transfer_test(void);
#endif

dmac_memory.c 里实现初始化函数、中断回调函数,设置数据长度,设置数据地址函数。

复制代码
#include "dmac_memory.h"
#include "led.h"

/**
 * 定义 SRC_Buffer 数组作为 DMAC 传输数据源
 * const 关键字将 SRC_Buffer 数组变量定义为常量类型,数据存储在内部的FLASH中
 */
const uint32_t SRC_Buffer[BUFFER_SIZE] = {
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                    0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                    0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80 };
/**
 * 定义 DMAC 传输目标存储器
 * 存储在内部的SRAM中
 */
uint32_t DST_Buffer[BUFFER_SIZE] = {0};

#if DMAC_TRANSFER_MODE == 1

/* 正常传输模式 */
transfer_info_t my_transfer_info_normal =
{
    .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,目标地址指针都会增加
    .transfer_settings_word_b.repeat_area    = TRANSFER_REPEAT_AREA_SOURCE,     //源区域重复 (正常模式下无效)
    .transfer_settings_word_b.irq            = TRANSFER_IRQ_END,                //传输完成后中断
    .transfer_settings_word_b.chain_mode     = TRANSFER_CHAIN_MODE_DISABLED,    //不使能(DMAC没有该功能,仅DTC有)
    .transfer_settings_word_b.src_addr_mode  = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,源地址指针都会增加
    .transfer_settings_word_b.size           = TRANSFER_SIZE_4_BYTE,            //每次传输4字节
    .transfer_settings_word_b.mode           = TRANSFER_MODE_NORMAL,            //正常传输模式
    .p_dest                                  = (void *) DST_Buffer,             //目标地址
    .p_src                                   = (void const *) SRC_Buffer,       //源地址
    .num_blocks = 0,            //指定传输的块数(正常模式下无效,仅在重复、块或重复-块模式下有效)
    .length     = BUFFER_SIZE,  //指定传输的长度(即正常和重复模式下的传输次数 或 块和重复-块模式下传输的块大小)
};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                    0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                    0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80 };

#elif DMAC_TRANSFER_MODE == 2

/* 重复传输模式 */
transfer_info_t my_transfer_info_repeat =
{
    .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,目标地址指针都会增加
    .transfer_settings_word_b.repeat_area    = TRANSFER_REPEAT_AREA_SOURCE,     //源区域重复 (正常模式下无效)
    .transfer_settings_word_b.irq            = TRANSFER_IRQ_END,                //传输完成后中断
    .transfer_settings_word_b.chain_mode     = TRANSFER_CHAIN_MODE_DISABLED,    //不使能(DMAC没有该功能,仅DTC有)
    .transfer_settings_word_b.src_addr_mode  = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,源地址指针都会增加
    .transfer_settings_word_b.size           = TRANSFER_SIZE_4_BYTE,            //每次传输4字节
    .transfer_settings_word_b.mode           = TRANSFER_MODE_REPEAT,            //重复传输模式
    .p_dest                                  = (void *) DST_Buffer,             //目标地址
    .p_src                                   = (void const *) SRC_Buffer,       //源地址
    .num_blocks = 8,            //指定传输的块数(正常模式下无效,仅在重复、块或重复-块模式下有效)
    .length     = 4,            //指定传输的长度(即正常的传输次数或重复模式下重复大小 或 块和重复-块模式下传输的块大小)
};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10 };

#elif DMAC_TRANSFER_MODE == 3

/* 块传输模式 */
transfer_info_t my_transfer_info_block =
{
    .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,目标地址指针都会增加
    .transfer_settings_word_b.repeat_area    = TRANSFER_REPEAT_AREA_SOURCE,     //源区域重复 (正常模式下无效)
    .transfer_settings_word_b.irq            = TRANSFER_IRQ_END,                //传输完成后中断
    .transfer_settings_word_b.chain_mode     = TRANSFER_CHAIN_MODE_DISABLED,    //不使能(DMAC没有该功能,仅DTC有)
    .transfer_settings_word_b.src_addr_mode  = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,源地址指针都会增加
    .transfer_settings_word_b.size           = TRANSFER_SIZE_4_BYTE,            //每次传输4字节
    .transfer_settings_word_b.mode           = TRANSFER_MODE_BLOCK,             //块传输模式
    .p_dest                                  = (void *) DST_Buffer,             //目标地址
    .p_src                                   = (void const *) SRC_Buffer,       //源地址
    .num_blocks = 4,            //指定传输的块数(正常模式下无效,仅在重复、块或重复-块模式下有效)
    .length     = 8,            //指定传输的长度(即正常的传输次数或重复模式下重复大小 或 块和重复-块模式下传输的块大小)
};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20 };

#elif DMAC_TRANSFER_MODE == 4

/* 重复-块传输模式 */
transfer_info_t my_transfer_info_repeat_block =
{
    .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,目标地址指针都会增加
    .transfer_settings_word_b.repeat_area    = TRANSFER_REPEAT_AREA_SOURCE,     //源区域重复 (正常模式下无效)
    .transfer_settings_word_b.irq            = TRANSFER_IRQ_EACH,               //每次传输完成后都触发中断
    .transfer_settings_word_b.chain_mode     = TRANSFER_CHAIN_MODE_DISABLED,    //不使能(DMAC没有该功能,仅DTC有)
    .transfer_settings_word_b.src_addr_mode  = TRANSFER_ADDR_MODE_INCREMENTED,  //每次传输后,源地址指针都会增加
    .transfer_settings_word_b.size           = TRANSFER_SIZE_4_BYTE,            //每次传输4字节
    .transfer_settings_word_b.mode           = TRANSFER_MODE_REPEAT_BLOCK,      //重复-块传输模式
    .p_dest                                  = (void *) DST_Buffer,             //目标地址
    .p_src                                   = (void const *) SRC_Buffer,       //源地址
    .num_blocks = 4,            //指定传输的块数(正常模式下无效,仅在重复、块或重复-块模式下有效)
    .length     = 2,           //指定传输的长度(即正常的传输次数或重复模式下重复大小 或 块和重复-块模式下传输的块大小)
};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304,
                    0x01020304,0x01020304,0x01020304,0x01020304 };

#endif


//设置传输的源地址和目的地址
void set_transfer_dst_src_address( transfer_cfg_t const * const p_config,
                                   void const * volatile p_src,
                                   void const * volatile p_dest )
{
    p_config->p_info->p_src = (void *) p_src;
    p_config->p_info->p_dest = (void *) p_dest;
}


//DMAC 初始化函数
void dmac_Init(void)
{
    fsp_err_t err;

        //设置传输的源地址和目的地址
    set_transfer_dst_src_address(&g_transfer0_dmac0_cfg, SRC_Buffer, DST_Buffer);

    //打开 DMAC 模块
    err = R_DMAC_Open(&g_transfer0_dmac0_ctrl, &g_transfer0_dmac0_cfg);
    assert(FSP_SUCCESS == err);

#if (DMAC_TRANSFER_MODE == 1)   //正常模式
    err = R_DMAC_Reconfigure(&g_transfer0_dmac0_ctrl, &my_transfer_info_normal);
    assert(FSP_SUCCESS == err);

#elif (DMAC_TRANSFER_MODE == 2) //重复模式
    err = R_DMAC_Reconfigure(&g_transfer0_dmac0_ctrl, &my_transfer_info_repeat);
    assert(FSP_SUCCESS == err);

#elif (DMAC_TRANSFER_MODE == 3) //块模式
    err = R_DMAC_Reconfigure(&g_transfer0_dmac0_ctrl, &my_transfer_info_block);
    assert(FSP_SUCCESS == err);

#elif (DMAC_TRANSFER_MODE == 4) //重复-块模式
    err = R_DMAC_Reconfigure(&g_transfer0_dmac0_ctrl, &my_transfer_info_repeat_block);
    assert(FSP_SUCCESS == err);
#endif //DMAC_TRANSFER_MODE

}


// DMA 传输完成标志位
volatile bool dmac0_complete_transfer_flag = false;
// 传输次数计数(中断次数)
volatile uint16_t dmac0_transfer_count = 0;

//DMAC 中断回调函数
void dmac0_callback(dmac_callback_args_t *p_args)
{
    FSP_PARAMETER_NOT_USED(p_args);

    dmac0_complete_transfer_flag = true;

    dmac0_transfer_count ++;
}

//比较数组内容
uint8_t BufferCompare(const uint32_t *pBuffer1, const uint32_t *pBuffer2, uint16_t BufferLength)
{
    while(BufferLength--)
    {
        if(*pBuffer1 != *pBuffer2)
        {
            return 1;
        }
        pBuffer1++;
        pBuffer2++;
    }

    return 0;
}

//打印缓冲区数据:十六进制格式
void BufferShow_HexData(const uint32_t *pBuffer, uint16_t BufferLength)
{
    while(BufferLength)
    {
        if((BufferLength % 4) == 0)
            printf("\r\n\t");

        printf("0x%08X ", (unsigned int)*pBuffer);

        pBuffer++;
        BufferLength--;
    }

    printf("\r\n");
}

void dmac_transfer_test(void)
{
    fsp_err_t err;
    int ret;

    //使能 DMAC
  err = R_DMAC_Enable(&g_transfer0_dmac0_ctrl);
  assert(FSP_SUCCESS == err);

    R_DMAC_SoftwareStart(&g_transfer0_dmac0_ctrl, TRANSFER_START_MODE_REPEAT);

    //判断传输完成中断(需至少触发过1次
  while (false == dmac0_complete_transfer_flag);

  R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS);   //加上延时确保所有传输都已完成
  printf("\r\ndmac0_transfer_count = %d\r\n", dmac0_transfer_count);

    printf("\r\nSRC:");
  BufferShow_HexData(SRC_Buffer, BUFFER_SIZE);
  printf("\r\nDST:recv"); 
  BufferShow_HexData(DST_Buffer, BUFFER_SIZE);
  printf("\r\nExpected_DST:");
  BufferShow_HexData(Expected_DST_Buffer, BUFFER_SIZE);

    ret = BufferCompare(DST_Buffer, Expected_DST_Buffer, BUFFER_SIZE);
    if(ret !=0)
    {
        LED1_ON();
        printf("failed:The source data is not equal to the data after transmission.");
        R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
        LED1_OFF();
    }
    else
    {
        LED1_ON();
        printf("succeed:The source data is equal to the data after transmission.");
    }
}

hal_entry.c 文件中添加头文件 dmac_memory.h

复制代码
void hal_entry(void)
{
    /* TODO: add your own code here */

    led_Init(); // LED 初始化
  uart0_Init();
    dmac_Init();

    printf("start dmac test\r\n");

    dmac_transfer_test();

    while(1)
    {
        LED1_TOGGLE();
        R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
    }


#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

下载验证

连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完。

正常模式发送和接收的数据都相同。

修改"DMAC_TRANSFER_MODE"= 2 重复模式下查看数据,

相关推荐
求知喻1 小时前
串口中断模式
单片机·嵌入式硬件
ACP广源盛139246256731 小时前
GSV6155@ACP# Type-C 视频信号转换芯片:技术架构、工程痛点适配与落地场景分析
大数据·c语言·开发语言·人工智能·分布式·嵌入式硬件·架构
光子物联单片机2 小时前
STM32传感器模块编程实践(二十一)DIY语音控制小车套件模型
c语言·stm32·单片机·嵌入式硬件·mcu
csdn杰哥2 小时前
瑞萨单片机开发教程[二十八] DTC 串口传输实验
单片机·嵌入式硬件·瑞萨·ra6m5
义嘉泰2 小时前
【硬核拆解】540×540时代的显示核心:集创北方 CO6300 AMOLED驱动芯片深度解析
嵌入式硬件·芯片
铅笔小新z2 小时前
【stm32】GPIO
stm32·嵌入式硬件·mongodb
csdn杰哥3 小时前
瑞萨单片机开发教程[三十] IIC 触摸驱动实验
单片机·嵌入式硬件·瑞萨·ra6m5
振南的单片机世界3 小时前
高优先级“插队”,低优先级让路:中断嵌套的核心规则
arm开发·stm32·单片机·嵌入式硬件
ahccqw5 小时前
电机入门学习(电机认识,分类)
嵌入式硬件