文章目录
- 一、DMAC存储器到存储器传输
- 二、DTC外部中断触发传输
一、DMAC存储器到存储器传输
1. FSP配置
打开该工程的 FSP 配置界面。然后按如图步骤加入 DMAC。
加入 DMAC 后如下图所示。
单击上图中新添加的 r_dmac 框,然后在左下角的“属性”窗口配置 DMAC 模块的各个属性参数。 按照如下图所示来配置即可。
在上图中,实际上只需配置框中的那部分属性,其他的属性均按照默认即可。
DMAC 的配置项(与上图相对应):
2. 定义传输源和目标存储器
首先,要使用 DMA 传输,就肯定要有一个源地址和一个目标地址, 这里定义 SRC_Buffer 数组的首地址作为源地址, DST_Buffer 数组的首地址作为DMAC传输的目标地址。 SRC_Buffer 数组由于有 const 声明为常量,因此其数据存储在内部 Flash 中, DST_Buffer 为普通的全局变量,其数据存储在 RAM 中。这两个数组的大小由宏定义 BUFFER_SIZE 来决定。
// 用户要发送的数据大小
#define BUFFER_SIZE 32
/**
* 定义 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};
3. 设置源地址和目标地址函数
DMAC 传输的配置,可以选择在 FSP 配置界面中进行配置, 但是要注意,在 FSP 配置界面中进行配置的话是不方便配置传输地址的, 所以此时我们其实还没有配置传输的源地址和目标地址,这部分需要在代码里面配置。
/* 设置传输的源地址和目的地址 */
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;
}
4. 使用FSP配置器生成的配置
前面在 FSP 配置界面上的配置其实是保存在 hal_data.c 文件中。如下图所示。
完全可以在 hal_data.c 文件中找到定义好的数据,重新复制一份,再改个变量名,在此基础上重新配置各个参数。 为了使用宏去方便切换不同模式的配置代码,在代码中配置 DMAC。 在头文件 “bsp_dmac_m2m.h” 中,默认定义了宏 USE_MY_TRANSFER_INFOR_CONFIG 来选择 使用在代码里自定义的传输信息配置,但是现在让先注释掉这个宏定义,从而选择使用在 FSP 配置界面上的配置。
下面的是与之前在 FSP 配置界面上的配置等效的配置代码:
/* FSP配置界面的传输信息配置(正常传输模式)等效于下面 fsp_transfer_info_normal 里的配置(除了源地址和目标地址)
源地址和目标地址在FSP配置界面设置的话不太方便,我们会在 DMAC_Init 函数里设置。
关于偏移值(Offset value)和源缓冲大小(Source Buffer Size),在 transfer_info_t 里没有这两项设置,建议在FSP配置界面设置
- 偏移值只有在地址模式是偏移模式的情况下才会用到;
- 而源缓冲大小与之相关的功能本例程不会涉及到,所以暂不考虑。
下面的 fsp_transfer_info_normal 仅作为对比参考,在本例程中是没有用到的。
*/
//transfer_info_t fsp_transfer_info_normal =
//{
// .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED, //每次传输后,目标地址指针固定不变
// .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_FIXED, //每次传输后,源地址指针固定不变
// .transfer_settings_word_b.size = TRANSFER_SIZE_2_BYTE, //每次传输2字节
// .transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL, //正常传输模式
// .p_dest = (void *) DST_Buffer, //目标地址
// .p_src = (void const *) SRC_Buffer, //源地址
// .num_blocks = 0, //指定传输的块数(正常模式下无效,仅在重复、块或重复-块模式下有效)
// .length = 1, //指定传输的长度(即正常和重复模式下的传输次数 或 块和重复-块模式下传输的块大小)
//};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
0x00000304,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000 };
可以看到,上述代码中的传输信息结构体变量 fsp_transfer_info_normal 被完全注释掉了。
需要用到的是 Expected_DST_Buffer 数组, 其中保存的数据是根据源数据 SRC_Buffer 和当前的传输配置得出的正确结果, 在进行实际上的传输结束后也应该在 DST_Buffer 数组中获得这样的数据。 换句话说,SRC_Buffer 保存了要传输的源数据,DST_Buffer 保存了传输后的实际结果, Expected_DST_Buffer 则保存的是传输后的正确结果, 后面还会通过 DST_Buffer 与 Expected_DST_Buffer 数组中数据的对比,来判断传输是否成功。
5. 使用代码配置:正常传输模式
下面是 DMAC 在正常模式下传输的配置代码:
/* 正常传输模式 */
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 };
注:使用以上代码需在头文件 “bsp_dmac_m2m.h” 中定义宏 USE_MY_TRANSFER_INFOR_CONFIG, 并且定义宏 DMAC_TRANSFER_MODE 等于 DMAC_TRANSFER_NORMAL_MODE 来选择传输模式为: 正常传输模式。
传输信息结构体变量 my_transfer_info_normal 用于对 DMAC 的传输进行重配置。
6. 使用代码配置:重复传输模式
上面使用完正常传输模式,下面来试一下重复传输模式, 其实无非就是在正常模式下多了 可以指定重复传输的次数 的功能, 就变成了重复传输模式。可以结合地址递增模式、重复区域,来实现环形队列。
下面是 DMAC 在重复传输模式下传输的配置代码:
/* 重复传输模式 */
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 };
注:使用以上代码需在头文件 “bsp_dmac_m2m.h” 中定义宏 USE_MY_TRANSFER_INFOR_CONFIG, 并且定义宏 DMAC_TRANSFER_MODE 等于 DMAC_TRANSFER_REPEAT_MODE 来选择传输模式为: 重复传输模式。
传输信息结构体变量 my_transfer_info_repeat 用于对 DMAC 的传输进行重配置。
7. 使用代码配置:块传输模式
下面是 DMAC 在块传输模式下传输的配置代码:
/* 块传输模式 */
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 };
注:使用以上代码需在头文件 “bsp_dmac_m2m.h” 中定义宏 USE_MY_TRANSFER_INFOR_CONFIG, 并且定义宏 DMAC_TRANSFER_MODE 等于 DMAC_TRANSFER_BLOCK_MODE 来选择传输模式为: 块传输模式。
传输信息结构体变量 my_transfer_info_block 用于对 DMAC 的传输进行重配置。
8. 使用代码配置:重复-块传输模式
下面是 DMAC 在重复-块传输模式下传输的配置代码:
/* 重复-块传输模式 */
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 };
注:使用以上代码需在头文件 “bsp_dmac_m2m.h” 中定义宏 USE_MY_TRANSFER_INFOR_CONFIG, 并且定义宏 DMAC_TRANSFER_MODE 等于 DMAC_TRANSFER_REPEAT_BLOCK_MODE 来选择传输模式为: 重复-块传输模式。
传输信息结构体变量 my_transfer_info_repeat_block 用于对 DMAC 的传输进行重配置。
9. DMAC初始化函数
若是使用 FSP 配置,需要设置传输地址, 通过调用 set_transfer_dst_src_address 函数来设置。
然后,调用 R_DMAC_Open 函数打开 DMAC。 需要注意,R_DMAC_Open 函数需要在 set_transfer_dst_src_address 函数之后调用, 因为在 R_DMAC_Open 函数内部会根据传输信息(包括传输地址等)来配置底层寄存器。
若是使用代码配置 DMAC 的传输信息(即定义了宏 USE_MY_TRANSFER_INFOR_CONFIG), 则还需要对 DMAC 进行重配置,方法是调用 R_DMAC_Reconfigure 函数, 并传入用于 DMAC 的传输信息配置的结构体变量(比如 my_transfer_info_normal)的首地址。
使用宏 DMAC_TRANSFER_MODE 来判断传输模式, 并根据不同的传输模式选择使用以下的传输信息变量来进行重配置: my_transfer_info_normal / my_transfer_info_repeat / my_transfer_info_repeat / my_transfer_info_block / my_transfer_info_repeat_block。
DMAC 初始化函数如下所示:
/* DMAC 初始化函数 */
void DMAC_Init(void)
{
fsp_err_t err;
/* 使用 FSP 界面的配置:需要先重新设置传输的源地址和目的地址 */
#ifndef USE_MY_TRANSFER_INFOR_CONFIG
set_transfer_dst_src_address(&g_transfer_dmac0_cfg, SRC_Buffer, DST_Buffer);
#endif
/* 打开 DMAC 模块 */
err = R_DMAC_Open(&g_transfer_dmac0_ctrl, &g_transfer_dmac0_cfg);
assert(FSP_SUCCESS == err);
/* 使用我们新的自定义的传输信息:重新配置传输 */
#ifdef USE_MY_TRANSFER_INFOR_CONFIG
// 根据我们要使用的传输模式进行选择配置:
#if (DMAC_TRANSFER_MODE == DMAC_TRANSFER_NORMAL_MODE) //正常模式
err = R_DMAC_Reconfigure(&g_transfer_dmac0_ctrl, &my_transfer_info_normal);
assert(FSP_SUCCESS == err);
#elif (DMAC_TRANSFER_MODE == DMAC_TRANSFER_REPEAT_MODE) //重复模式
err = R_DMAC_Reconfigure(&g_transfer_dmac0_ctrl, &my_transfer_info_repeat);
assert(FSP_SUCCESS == err);
#elif (DMAC_TRANSFER_MODE == DMAC_TRANSFER_BLOCK_MODE) //块模式
err = R_DMAC_Reconfigure(&g_transfer_dmac0_ctrl, &my_transfer_info_block);
assert(FSP_SUCCESS == err);
#elif (DMAC_TRANSFER_MODE == DMAC_TRANSFER_REPEAT_BLOCK_MODE) //重复-块模式
err = R_DMAC_Reconfigure(&g_transfer_dmac0_ctrl, &my_transfer_info_repeat_block);
assert(FSP_SUCCESS == err);
#endif //DMAC_TRANSFER_MODE
#endif //USE_MY_TRANSFER_INFOR_CONFIG
}
10. DMAC中断回调函数
DMAC 中断回调函数如下所示:
// DMA 传输完成标志位
volatile bool dmac0_complete_transmission_sign = false;
// 传输次数计数(中断次数)
volatile uint16_t dmac0_transfer_count;
/* DMAC 中断回调函数 */
void dmac0_callback(dmac_callback_args_t *p_args)
{
(void)(p_args);
dmac0_complete_transmission_sign = true;
dmac0_transfer_count ++;
}
dmac0_transfer_count 用于记录 DMAC 产生中断的次数。
11. hal_entry入口函数
在 hal_entry 函数中,程序遵循以下步骤来执行:
-
调用 DMAC_Init 函数初始化 DMAC。
-
调用 R_DMAC_Enable 函数使能 DMAC 使之可以响应传输请求。
-
调用 R_DMAC_SoftwareStart 函数来发起软件触发请求信号来启动 DMAC 传输。
-
通过一定的延时等待所有传输完成,因为在产生多次的中断的情况下,仅通过中断标志位判断可能出错。
-
在传输完成之后,比较传输目标地址的数据(DST_Buffer)和期待的正确数据(Expected_DST_Buffer)是否一致。
传入 R_DMAC_SoftwareStart 函数的参数 TRANSFER_START_MODE_SINGLE 和 TRANSFER_START_MODE_REPEAT 的区别如下:
- 传入参数为 TRANSFER_START_MODE_SINGLE 时, 正常模式和重复模式下每发起一次软件请求只会传输一个数据单元大小(transfer_size_t size)的数据。 块模式下则是只会传输一个块大小(transfer_info_t::length)的数据。
- 传入参数为 TRANSFER_START_MODE_REPEAT 时, 会自动重复地触发传输,期间可能会产生多次中断,直至所有数据都传输完成为止。
hal_entry 入口函数如下所示。
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "dmac/bsp_dmac_m2m.h"
extern const uint32_t SRC_Buffer[BUFFER_SIZE];
extern uint32_t DST_Buffer[BUFFER_SIZE];
extern uint32_t Expected_DST_Buffer[BUFFER_SIZE];
extern volatile bool dmac0_complete_transmission_sign;
extern volatile uint16_t dmac0_transfer_count;
uint8_t BufferCompare(const uint32_t *pBuffer1, const uint32_t *pBuffer2, uint16_t BufferLength);
void BufferShow_HexData(const uint32_t *pBuffer, uint16_t BufferLength);
void hal_entry(void)
{
/* TODO: add your own code here */
fsp_err_t err = FSP_SUCCESS;
uint8_t res;
LED_Init(); // LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
/* 初始化 DMAC */
DMAC_Init();
dmac0_complete_transmission_sign = false; //传输完成标志位清零
printf("这是一个 DMAC 存储器到存储器的传输实验例程\r\n");
printf("打开串口助手,查看接收窗口打印的相关提示信息\r\n");
printf("观察板载LED灯,本实验使用两个LED灯来指示 DMAC 传输结果\r\n");
printf("- DMA 数据传输失败,则 LED1 亮(红色)\r\n");
printf("- DMA 数据传输成功,则 LED2 亮(蓝色)\r\n");
printf("--------------------------------------------\r\n");
/* 使能 DMAC 使之可以响应传输请求 */
R_DMAC_Enable(&g_transfer_dmac0_ctrl);
/************************************/
/* 使用软件触发的方式启动 DMAC 传输 */
/************************************/
#ifndef USE_MY_TRANSFER_INFOR_CONFIG
/* 根据 FSP 配置界面的传输信息配置进行传输 */
//可以用下面这种方式:
R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_REPEAT);
//也可以用这种方式:
//for (uint16_t i = 0; i < 1; i++)
//{
// err = R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_SINGLE);
// assert(FSP_SUCCESS == err);
//}
#else // 下面的这些是使用自定义的传输配置信息配置
#if (DMAC_TRANSFER_MODE == DMAC_TRANSFER_NORMAL_MODE) //正常模式(相当于重复次数为1的重复模式)
//可以用下面这种方式:
R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_REPEAT);
//也可以用这种方式:
//for (uint16_t i = 0; i < BUFFER_SIZE; i++)
//{
// err = R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_SINGLE);
// assert(FSP_SUCCESS == err);
//}
#elif (DMAC_TRANSFER_MODE == DMAC_TRANSFER_REPEAT_MODE) //重复模式
//可以用下面这种方式:
R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_REPEAT);
//也可以用这种方式:
//for (uint16_t i = 0; i < BUFFER_SIZE; i++)
//{
// err = R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_SINGLE);
// assert(FSP_SUCCESS == err);
//}
#elif (DMAC_TRANSFER_MODE == DMAC_TRANSFER_BLOCK_MODE) //块模式
//可以用下面这种方式:
R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_REPEAT);
//也可以用这种方式:
//for (uint16_t i = 0; i < 4; i++)
//{
// err = R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_SINGLE);
// assert(FSP_SUCCESS == err);
//
// //加个小延时,确保DMAC通道0传输完成之后才再次软件触发启动,否则传输可能出错
// R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
//}
#elif (DMAC_TRANSFER_MODE == DMAC_TRANSFER_REPEAT_BLOCK_MODE) //重复-块模式
//可以用下面这种方式:
R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_REPEAT);
//也可以用这种方式:
//for (uint16_t i = 0; i < 4; i++)
//{
// err = R_DMAC_SoftwareStart(&g_transfer_dmac0_ctrl, TRANSFER_START_MODE_SINGLE);
// assert(FSP_SUCCESS == err);
//
// //加个小延时,确保DMAC通道0传输完成之后才再次软件触发启动,否则传输可能出错
// R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
//}
#endif //DMAC_TRANSFER_MODE
#endif //USE_MY_TRANSFER_INFOR_CONFIG
/* 判断传输完成中断(需至少触发过1次) */
while (false == dmac0_complete_transmission_sign);
/* 等待所有传输完成(如果是TRANSFER_IRQ_EACH模式,传输过程中可能会触发多次中断) */
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS); //加上延时确保所有传输都已完成
printf("\r\n传输计数(中断次数):dmac0_transfer_count = %d\r\n", dmac0_transfer_count);
/* 将传输后的数据与我们所期待的结果相比较 */
res = BufferCompare(DST_Buffer, Expected_DST_Buffer, BUFFER_SIZE);
printf("传输结果:");
/* 根据两者数据的比较结果进行判断 */
if( res != 0)
{
/* 源数据与传输后数据不相等时,LED1 亮(红色),表示传输失败 */
LED1_ON;
printf("<传输失败>\r\n");
}
else
{
/* 源数据与传输后数据相等时,LED1 亮(蓝色),表示传输成功 */
LED2_ON;
printf("<传输成功>\r\n");
}
printf("\r\nSRC:");
BufferShow_HexData(SRC_Buffer, BUFFER_SIZE);
printf("\r\nDST:(应与 Expected_DST 一致)");
BufferShow_HexData(DST_Buffer, BUFFER_SIZE);
printf("\r\nExpected_DST:");
BufferShow_HexData(Expected_DST_Buffer, BUFFER_SIZE);
while(1)
{
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
12. 缓冲区数据比较函数
/* 缓冲区数据比较函数
返回 0 表示两个缓冲区数据一致
*/
uint8_t BufferCompare(const uint32_t *pBuffer1, const uint32_t *pBuffer2, uint16_t BufferLength)
{
/* 数据长度递减 */
while(BufferLength--)
{
/* 判断两个数据源是否对应相等 */
if(*pBuffer1 != *pBuffer2)
{
/* 对应数据源不相等马上退出函数,并返回1 */
return 1;
}
/* 递增两个数据源的地址指针 */
pBuffer1++;
pBuffer2++;
}
/* 完成判断并且两组数据完全一致 */
return 0;
}
13. 打印缓冲区数据函数
/* 打印缓冲区数据函数
打印缓冲区数据:十六进制格式
*/
void BufferShow_HexData(const uint32_t *pBuffer, uint16_t BufferLength)
{
while(BufferLength)
{
if((BufferLength % 4) == 0)
printf("\r\n\t");
printf("0x%08X ", *pBuffer);
pBuffer++;
BufferLength--;
}
printf("\r\n");
}
二、DTC外部中断触发传输
1. FSP配置
先加入外部中断。 点到“Pins”页面,在 ICU0 里面找到 IRQ09,这里选用 P004 引脚(SW2按键连接的引脚)连接到外部中断 IRQ09。
然后点到“Stacks”页面,按照“New Stack”→“Input”→“External IRQ”的步骤添加一个ICU模块来配置外部中断。 ICU 模块的配置可按照如下图所示进行配置,中断触发方式默认选择上升沿触发, 中断优先级设置比 UART 中断大一点,设置为优先级 10。
接着在“Stacks”页面继续加入 DTC 模块,按如下图所示步骤添加。
按如下图配置一下 DTC:
DTC 配置项(与上图相对应):
配置完成后,直接右上角点击生成代码。
2. 使用FSP配置器生成的配置
下面的是与之前在 FSP 配置界面上的配置等效的配置代码:
/* FSP配置界面的传输信息配置(正常传输模式)等效于下面 fsp_transfer_info_normal 里的配置(除了源地址和目标地址)
源地址和目标地址在FSP配置界面设置的话不太方便,我们会在 DTC_Init 函数里设置。
关于偏移值(Offset value)和源缓冲大小(Source Buffer Size),在 transfer_info_t 里没有这两项设置,建议在FSP配置界面设置
- 偏移值只有在地址模式是偏移模式的情况下才会用到;
- 而源缓冲大小与之相关的功能本例程不会涉及到,所以暂不考虑。
下面的 fsp_transfer_info_normal 仅作为对比参考,在本例程中是没有用到的。
*/
//transfer_info_t fsp_transfer_info_normal =
//{
// .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED, //每次传输后,目标地址指针固定不变
// .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_FIXED, //每次传输后,源地址指针固定不变
// .transfer_settings_word_b.size = TRANSFER_SIZE_2_BYTE, //每次传输2字节
// .transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL, //正常传输模式
// .p_dest = (void *) DST_Buffer, //目标地址
// .p_src = (void const *) SRC_Buffer, //源地址
// .num_blocks = 0, //指定传输的块数(正常模式和重复模式下均无效,仅块模式下有效)
// .length = 1, //指定传输的长度(即正常的传输次数或重复模式下重复大小 或 块模式下传输的块大小)
//};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
0x00000304,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000 };
3. 使用代码配置:正常传输模式
下面是 DTC 在正常模式下传输的配置代码:
/* 正常传输模式 */
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 = 1, //指定传输的长度(即正常的传输次数或重复模式下重复大小 或 块模式下传输的块大小)
};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
0x01020304,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000 };
4. 使用代码配置:重复传输模式
下面是 DTC 在重复传输模式下传输的配置代码:
/* 重复传输模式 */
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_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, //重复传输模式
.p_dest = (void *) DST_Buffer, //目标地址
.p_src = (void const *) SRC_Buffer, //源地址
.num_blocks = 0, //指定传输的块数(正常模式和重复模式下均无效,仅块模式下有效)
.length = 2, //指定传输的长度(即正常的传输次数或重复模式下重复大小 或 块模式下传输的块大小)
};
// 按照上述传输信息配置,期待的正确传输结果为:
uint32_t Expected_DST_Buffer[BUFFER_SIZE] = {
0x01020304,0x05060708,0x01020304,0x05060708,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000 };
5. 使用代码配置:块传输模式
下面是 DTC 在块传输模式下传输的配置代码:
/* 块传输模式 */
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 = 1, //指定传输的块数(正常模式和重复模式下均无效,仅块模式下有效)
.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 };
6. DTC初始化函数
DTC 初始化函数如下所示:
/* DTC 初始化函数 */
void DTC_Init(void)
{
fsp_err_t err;
/* 使用 FSP 界面的配置:需要先重新设置传输的源地址和目的地址 */
#ifndef USE_MY_TRANSFER_INFOR_CONFIG
set_transfer_dst_src_address(&g_transfer_dtc_cfg, SRC_Buffer, DST_Buffer);
#endif
err = R_DTC_Open(&g_transfer_dtc_ctrl, &g_transfer_dtc_cfg);
assert(FSP_SUCCESS == err);
/* 使用我们新的自定义的传输信息:重新配置传输 */
#ifdef USE_MY_TRANSFER_INFOR_CONFIG
// 根据我们要使用的传输模式进行选择配置:
#if (DTC_TRANSFER_MODE == DTC_TRANSFER_NORMAL_MODE) //正常模式
err = R_DTC_Reconfigure(&g_transfer_dtc_ctrl, &my_transfer_info_normal);
assert(FSP_SUCCESS == err);
#elif (DTC_TRANSFER_MODE == DTC_TRANSFER_REPEAT_MODE) //重复模式
err = R_DTC_Reconfigure(&g_transfer_dtc_ctrl, &my_transfer_info_repeat);
assert(FSP_SUCCESS == err);
#elif (DTC_TRANSFER_MODE == DTC_TRANSFER_BLOCK_MODE) //块模式
err = R_DTC_Reconfigure(&g_transfer_dtc_ctrl, &my_transfer_info_block);
assert(FSP_SUCCESS == err);
#endif //DTC_TRANSFER_MODE
#endif //USE_MY_TRANSFER_INFOR_CONFIG
}
7. 按键外部中断回调函数
按键外部中断回调函数如下所示:
/* 按键按下标志 */
volatile bool key_sw2_press = false;
/* 按键中断回调函数 */
void icu_external_irq_callback(external_irq_callback_args_t *p_args)
{
/* 判断中断通道 */
if (9 == p_args->channel)
{
key_sw2_press = true; // 按键SW2按下
}
}
8. hal_entry入口函数
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
#include "dtc/bsp_dtc.h"
extern const uint32_t SRC_Buffer[BUFFER_SIZE];
extern uint32_t DST_Buffer[BUFFER_SIZE];
extern uint32_t Expected_DST_Buffer[BUFFER_SIZE];
extern volatile bool dtc_complete_transmission_sign;
uint8_t BufferCompare(const uint32_t *pBuffer1, const uint32_t *pBuffer2, uint16_t BufferLength);
void BufferShow_HexData(const uint32_t *pBuffer, uint16_t BufferLength);
void hal_entry(void)
{
/* TODO: add your own code here */
uint8_t res;
LED_Init(); // LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
/* 初始化 DTC */
DTC_Init();
printf("这是一个 DTC 存储器到存储器的传输实验例程\r\n");
printf("打开串口助手,查看接收窗口打印的相关提示信息\r\n");
printf("按下按键 SW2 激活 DTC 传输\r\n");
printf("观察板载LED灯,本实验使用两个LED灯来指示 DTC 传输结果\r\n");
printf("- DTC 数据传输失败,则 LED1 亮(红色)\r\n");
printf("- DTC 数据传输成功,则 LED2 亮(蓝色)\r\n");
printf("--------------------------------------------\r\n");
/* Open ICU module */
R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg);
/* 允许中断 */
R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl);
/* 使能 DTC 模块 */
R_DTC_Enable(&g_transfer_dtc_ctrl);
/*************************************/
/* 使用按键外部中断触发激活 DTC 传输 */
/*************************************/
#ifndef USE_MY_TRANSFER_INFOR_CONFIG
/* 根据 FSP 配置界面的传输信息进行传输 */
/* 等待激活 DTC 传输 */
for (uint16_t i = 0; i < 1; i++)
{
/* 等待按键按下,按键按下一次即激活一次 DTC 传输 */
while (false == key_sw2_press);
key_sw2_press = false;
/* 等待本次传输完成 */
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS); //加上延时确保传输完成
}
#else // 下面的这些是使用自定义的传输配置信息配置
#if (DTC_TRANSFER_MODE == DTC_TRANSFER_NORMAL_MODE) //正常模式
/* 等待激活 DTC 传输 */
for (uint16_t i = 0; i < 1; i++)
{
/* 等待按键按下,按键按下一次即激活一次 DTC 传输 */
while (false == key_sw2_press);
key_sw2_press = false;
/* 等待本次传输完成 */
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS); //加上延时确保传输完成
}
#elif (DTC_TRANSFER_MODE == DTC_TRANSFER_REPEAT_MODE) //重复模式
/* 等待激活 DTC 传输 */
for (uint16_t i = 0; i < 4; i++)
{
/* 等待按键按下,按键按下一次即激活一次 DTC 传输 */
while (false == key_sw2_press);
key_sw2_press = false;
/* 等待本次传输完成 */
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS); //加上延时确保传输完成
}
#elif (DTC_TRANSFER_MODE == DTC_TRANSFER_BLOCK_MODE) //块模式
/* 等待激活 DTC 传输 */
for (uint16_t i = 0; i < 1; i++)
{
/* 等待按键按下,按键按下一次即激活一次 DTC 传输 */
while (false == key_sw2_press);
key_sw2_press = false;
/* 等待本次传输完成 */
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS); //加上延时确保传输完成
}
#endif //DTC_TRANSFER_MODE
#endif //USE_MY_TRANSFER_INFOR_CONFIG
/* 将传输后的数据与我们所期待的结果相比较 */
res = BufferCompare(DST_Buffer, Expected_DST_Buffer, BUFFER_SIZE);
printf("传输结果:");
/* 根据两者数据的比较结果进行判断 */
if( res != 0)
{
/* 源数据与传输后数据不相等时,LED1 亮(红色),表示传输失败 */
LED1_ON;
printf("<传输失败>\r\n");
}
else
{
/* 源数据与传输后数据相等时,LED1 亮(蓝色),表示传输成功 */
LED2_ON;
printf("<传输成功>\r\n");
}
printf("\r\nSRC:");
BufferShow_HexData(SRC_Buffer, BUFFER_SIZE);
printf("\r\nDST:(应与 Expected_DST 一致)");
BufferShow_HexData(DST_Buffer, BUFFER_SIZE);
printf("\r\nExpected_DST:");
BufferShow_HexData(Expected_DST_Buffer, BUFFER_SIZE);
while(1)
{
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
实验现象
首先通过宏 USE_MY_TRANSFER_INFOR_CONFIG (在 bsp_dtc.h 文件中) 来选择是使用自定义的传输信息,还是使用在 FSP 配置界面配置的传输信息。
其次通过宏 DMAC_TRANSFER_MODE (在 bsp_dtc.h 文件中)来选择不同的传输模式。
最后编译工程并下载到开发板上,打开串口助手可以查看程序运行打印的提示信息。 按下启明6M5开发板上的按键 SW2,即触发 DTC 传输。 如果 DTC 传输成功,则蓝色 LED2 亮起,如果传输失败则红色 LED1 亮起。