ARM32开发——DMA内存到内存
🎬 秋野酱:《个人主页》
🔥 个人专栏:《Java专栏》《Python专栏》
⛺️心若有所向往,何惧道阻且长
文章目录
- 需求
- 数据交互流程
- 开发流程
- 依赖引入
- DMA初始
- DMA传输请求
- 完整代码
- 关心的内容
- DMA初始化
- DMA初始化
- DMA数据传输请求
- 完整代码
- DMA中断
- 开启中断
- 中断函数实现
- 完整代码
需求
#define ARR_LEN 1024
char src[ARR_LEN] = "hello\0";
char dst[ARR_LEN] = {0};
将src这个数组的值,赋值到dst这个数组中,不可以采取直接赋值的方式,需要通过DMA将数据进行传递。
数据交互流程
开发流程
依赖引入
添加标准库中的gd32f4xx_dma.c文件
DMA初始
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(RCU_DMA1);
// 重置dma
dma_deinit(DMA1, DMA_CH0);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = DMA_MEMORY_TO_MEMORY;
// 外设(作为内存到内存拷贝的源)
dsdps.periph_addr = (uint32_t)src;
dsdps.periph_inc = DMA_PERIPH_INCREASE_ENABLE;
// 内存
dsdps.memory0_addr = (uint32_t)dst;
dsdps.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 数据长度
dsdps.number = ARR_LEN;
// 数据宽度
dsdps.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_single_data_mode_init(DMA1, DMA_CH0, &dsdps);
- 配置时钟
- 初始化dma通道
DMA传输请求
dma_channel_enable(DMA1, DMA_CH0);
while(RESET == dma_flag_get(DMA1, DMA_CH0, DMA_FLAG_FTF));
dma_flag_clear(DMA1, DMA_CH0, DMA_FLAG_FTF);
● dma_channel_enable: 请求dma数据传输
● DMA_FLAG_FTF:为传输完成标记
完整代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "Usart0.h"
#define ARR_LEN 1024
char src[ARR_LEN] = "hello\0";
char dst[ARR_LEN] = {0};
void Usart0_recv(uint8_t* data, uint32_t len) {
printf("recv: %s\r\n", data);
dma_channel_enable(DMA1, DMA_CH0);
while(RESET == dma_flag_get(DMA1, DMA_CH0, DMA_FLAG_FTF));
dma_flag_clear(DMA1, DMA_CH0, DMA_FLAG_FTF);
printf("dst: %s\n", dst);
}
static void DMA_config() {
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(RCU_DMA1);
// 重置dma
dma_deinit(DMA1, DMA_CH0);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = DMA_MEMORY_TO_MEMORY;
// 外设(作为内存到内存拷贝的源)
dsdps.periph_addr = (uint32_t)src;
dsdps.periph_inc = DMA_PERIPH_INCREASE_ENABLE;
// 内存
dsdps.memory0_addr = (uint32_t)dst;
dsdps.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 数据长度
dsdps.number = ARR_LEN;
// 数据宽度
dsdps.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_single_data_mode_init(DMA1, DMA_CH0, &dsdps);
// // 中断配置
// nvic_irq_enable(DMA1_Channel0_IRQn, 2, 2);
// // 中断使能
// dma_interrupt_enable(DMA1, DMA_CH0, DMA_CHXCTL_FTFIE);
// // 开启DMA通道
// dma_channel_enable(DMA1, DMA_CH0);
}
//void DMA1_Channel0_IRQHandler() {
// // 判断中断标记
// if(SET == dma_interrupt_flag_get(DMA1, DMA_CH0, DMA_INT_FLAG_FTF)) {
// printf("dst: %s\n", dst);
// }
// // 清理标记位
// dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INT_FLAG_FTF);
//}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
systick_config();
Usart0_init();
DMA_config();
while(1) {
}
}
关心的内容
uint32_t dmax = DMA1;
uint32_t dmax_rcu = RCU_DMA1;
uint32_t dmax_ch = DMA_CH0;
uint32_t dmax_dirction = DMA_MEMORY_TO_MEMORY;
uint32_t dmax_src = (uint32_t)src;
uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_ENABLE;
uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;
uint32_t dmax_src_len = ARR_LEN;
uint32_t dmax_dst = (uint32_t)dst;
uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(dmax_rcu);
// 重置dma
dma_deinit(dmax, dmax_ch);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = dmax_dirction;
// 内存
dsdps.memory0_addr = dmax_dst;
dsdps.memory_inc = dmax_dst_inc;
// 外设
dsdps.periph_addr = dmax_src;
dsdps.periph_inc = dmax_src_inc;
// 数据长度
dsdps.number = dmax_src_len;
// 数据宽度
dsdps.periph_memory_width = dmax_src_width;
dma_single_data_mode_init(dmax, dmax_ch, &dsdps);
DMA初始化
uint32_t dmax = DMA1;
uint32_t dmax_rcu = RCU_DMA1;
uint32_t dmax_ch = DMA_CH0;
uint32_t dmax_dirction = DMA_MEMORY_TO_MEMORY;
//uint32_t dmax_src = (uint32_t)src;
uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_ENABLE;
uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;
//uint32_t dmax_src_len = ARR_LEN;
uint32_t dmax_dst = (uint32_t)dst;
uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(dmax_rcu);
// 重置dma
dma_deinit(dmax, dmax_ch);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = dmax_dirction;
// 内存
dsdps.memory0_addr = dmax_dst;
dsdps.memory_inc = dmax_dst_inc;
// 外设
//dsdps.periph_addr = dmax_src;
dsdps.periph_inc = dmax_src_inc;
// 数据长度
//dsdps.number = dmax_src_len;
// 数据宽度
dsdps.periph_memory_width = dmax_src_width;
dma_single_data_mode_init(dmax, dmax_ch, &dsdps);
DMA初始化
初始化时,不再初始化源地址和要传输的长度。
DMA数据传输请求
uint32_t dmax = DMA1;
uint32_t dmax_rcu = RCU_DMA1;
uint32_t dmax_ch = DMA_CH0;
uint32_t dmax_dirction = DMA_MEMORY_TO_MEMORY;
//uint32_t dmax_src = (uint32_t)src;
uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_ENABLE;
uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;
//uint32_t dmax_src_len = ARR_LEN;
uint32_t dmax_dst = (uint32_t)dst;
uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(dmax_rcu);
// 重置dma
dma_deinit(dmax, dmax_ch);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = dmax_dirction;
// 内存
dsdps.memory0_addr = dmax_dst;
dsdps.memory_inc = dmax_dst_inc;
// 外设
//dsdps.periph_addr = dmax_src;
dsdps.periph_inc = dmax_src_inc;
// 数据长度
//dsdps.number = dmax_src_len;
// 数据宽度
dsdps.periph_memory_width = dmax_src_width;
dma_single_data_mode_init(dmax, dmax_ch, &dsdps);
DMA数据传输请求
dma_periph_address_config(DMA1, DMA_CH0,(uint32_t)data);
dma_transfer_number_config(DMA1, DMA_CH0, len);
dma_channel_enable(DMA1, DMA_CH0);
while(RESET == dma_flag_get(DMA1, DMA_CH0, DMA_FLAG_FTF));
dma_flag_clear(DMA1, DMA_CH0, DMA_FLAG_FTF);
请求数据传输前,进行动态配置:
● 在此需要注意的是,要分清楚当前是否是调用源的地址配置
完整代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "Usart0.h"
#define ARR_LEN 1024
char dst[ARR_LEN] = {0};
void Usart0_recv(uint8_t* data, uint32_t len) {
printf("recv: %s\r\n", data);
dma_periph_address_config(DMA1, DMA_CH0,(uint32_t)data);
dma_transfer_number_config(DMA1, DMA_CH0, len);
dma_channel_enable(DMA1, DMA_CH0);
while(RESET == dma_flag_get(DMA1, DMA_CH0, DMA_FLAG_FTF));
dma_flag_clear(DMA1, DMA_CH0, DMA_FLAG_FTF);
dst[len] = '\0';
printf("dst: %s\n", dst);
}
static void DMA_config() {
uint32_t dmax = DMA1;
uint32_t dmax_rcu = RCU_DMA1;
uint32_t dmax_ch = DMA_CH0;
uint32_t dmax_dirction = DMA_MEMORY_TO_MEMORY;
//uint32_t dmax_src = (uint32_t)src;
uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_ENABLE;
uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;
//uint32_t dmax_src_len = ARR_LEN;
uint32_t dmax_dst = (uint32_t)dst;
uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(dmax_rcu);
// 重置dma
dma_deinit(dmax, dmax_ch);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = dmax_dirction;
// 内存
dsdps.memory0_addr = dmax_dst;
dsdps.memory_inc = dmax_dst_inc;
// 外设
//dsdps.periph_addr = dmax_src;
dsdps.periph_inc = dmax_src_inc;
// 数据长度
//dsdps.number = dmax_src_len;
// 数据宽度
dsdps.periph_memory_width = dmax_src_width;
dma_single_data_mode_init(dmax, dmax_ch, &dsdps);
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
systick_config();
Usart0_init();
DMA_config();
while(1) {
}
}
DMA中断
通常,dma数据传输完成,我们也是可以知道的。可以通过中断的方式获取。
开启中断
// 中断配置
nvic_irq_enable(DMA1_Channel0_IRQn, 2, 2);
// 中断使能
dma_interrupt_enable(DMA1, DMA_CH0, DMA_INT_FTF);
// 开启DMA通道
dma_channel_enable(DMA1, DMA_CH0);
● DMA_CHXCTL_FTFIE: 表示中断数据传输完成标记
中断函数实现
void DMA1_Channel0_IRQHandler() {
// 判断中断标记
if(SET == dma_interrupt_flag_get(DMA1, DMA_CH0, DMA_INT_FLAG_FTF)) {
printf("INT dst: %s\n", dst);
// 清理标记位
dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INT_FLAG_FTF);
}
}
● DMA_INT_FLAG_FTF: 表示中断传输完成标记,需要在标记触发时做业务逻辑
● 同时,需要配合寄存器,清除标记
完整代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "Usart0.h"
#define ARR_LEN 1024
char src[ARR_LEN] = "hello\0";
char dst[ARR_LEN] = {0};
void Usart0_recv(uint8_t* data, uint32_t len) {
printf("recv: %s\r\n", data);
memcpy(src, data, len);
src[len] = '\0';
dma_channel_enable(DMA1, DMA_CH0);
printf("dst: %s\n", dst);
}
static void DMA_config() {
uint32_t dmax = DMA1;
uint32_t dmax_rcu = RCU_DMA1;
uint32_t dmax_ch = DMA_CH0;
uint32_t dmax_dirction = DMA_MEMORY_TO_MEMORY;
uint32_t dmax_src = (uint32_t)src;
uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_ENABLE;
uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;
uint32_t dmax_src_len = ARR_LEN;
uint32_t dmax_dst = (uint32_t)dst;
uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(dmax_rcu);
// 重置dma
dma_deinit(dmax, dmax_ch);
dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = dmax_dirction;
// 内存
dsdps.memory0_addr = dmax_dst;
dsdps.memory_inc = dmax_dst_inc;
// 外设
dsdps.periph_addr = dmax_src;
dsdps.periph_inc = dmax_src_inc;
// 数据长度
dsdps.number = dmax_src_len;
// 数据宽度
dsdps.periph_memory_width = dmax_src_width;
dma_single_data_mode_init(dmax, dmax_ch, &dsdps);
// 配置中断
nvic_irq_enable(DMA1_Channel0_IRQn, 2, 2);
dma_interrupt_enable(dmax, dmax_ch, DMA_INT_FTF);
}
void DMA1_Channel0_IRQHandler() {
// 判断中断标记
if(SET == dma_interrupt_flag_get(DMA1, DMA_CH0, DMA_INT_FLAG_FTF)) {
printf("INT dst: %s\n", dst);
// 清理标记位
dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INT_FLAG_FTF);
}
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
systick_config();
Usart0_init();
DMA_config();
while(1) {
}
}