🎬 秋野酱:《个人主页》
🔥 个人专栏:《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);// 重置dmadma_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);// 重置dmadma_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);// 重置dmadma_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) {}
}