当前位置: 首页 > article >正文

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);
  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) {

    }
}

http://www.kler.cn/a/301181.html

相关文章:

  • 汽车安全再进化 - SemiDrive X9HP 与环景影像系统 AVM 的系统整合
  • SpringSecurity+jwt+captcha登录认证授权总结
  • 鸿蒙实现 web 传值
  • SQL注入注入方式(大纲)
  • Python数据分析NumPy和pandas(三十五、时间序列数据基础)
  • 针对股票评论的情感分类器
  • CSS实现前端布局更巧妙的方案!在 flex 布局中通过使用 margin 实现水平垂直居中以及其他常见的前端布局
  • html初体验标准标签
  • MDK平台 - 变量和函数定义绝对位置
  • RISC-V (十一)软件定时器
  • 巧用抖音关键词视频列表 API 和视频评论 API 深度解析用户互动
  • 在 Jenkins 上通过 SSH 控制 Windows 目标计算机时,出现中文乱码
  • Redis——通用命令
  • 【原创教程】自动化工程案例01:8工位插针装配机02
  • 校园管理|基于springboot+vue的校园管理系统(源码+数据库+文档)
  • Leetcode 188. 买卖股票的最佳时机 Ⅳ 状态机dp C++实现
  • MAC配置chromedriver
  • EasyExcel 学习之 导出 “类型及精度问题”
  • Tomact的基本使用
  • 中国大数据产业的融资热潮来袭,哪些领域最受资本青睐?
  • 设计模式】Listener模式和Visitor模式的区别
  • 在JavaScript中实现简单的发布/订阅模式
  • 《C++位域:在复杂数据结构中的精准驾驭与风险规避》
  • spark读取csv文件
  • 云计算第四阶段----CLOUD 01-03
  • MySQL:视图【详解】