ARM32开发——DMA内存到内存

🎬 秋野酱:《个人主页》
🔥 个人专栏:《Java专栏》《Python专栏》

⛺️心若有所向往,何惧道阻且长

需求

#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);
  1. 配置时钟
  2. 初始化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) {

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋野酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值