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);// 重置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) {}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/422270.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ruoyi Cloud 本地启动

参考 http://doc.ruoyi.vip/ https://gitee.com/y_project/RuoYi-Cloud https://blog.csdn.net/cs_dnzk/article/details/135289966 https://doc.ruoyi.vip/ruoyi-cloud/cloud/seata.html#%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D 拉取代码本地跑通 用 git 从 ruoyi 微服务版仓…

sqli-labs靶场自动化利用工具——第6关

文章目录 概要整体架构流程技术细节执行效果小结 概要 Sqli-Labs靶场对于网安专业的学生或正在学习网安的朋友来说并不陌生&#xff0c;或者说已经很熟悉。那有没有朋友想过自己开发一个测试脚本能实现自动化化测试sqli-labs呢&#xff1f;可能有些人会说不是有sqlmap&#…

已成功入职华为,总结精选50个大模型高频面试题(附答案)

觉得中大厂面试太难的&#xff0c;完全就是自己没准备充分&#xff0c;技术不到位&#xff0c;没准备的面试完全是浪费时间&#xff0c;更是对自己的不负责! . 今天我给大家分享一下我整理的**《精选50个大模型高频面试题》** 大模型面试专题和答案&#xff0c;其中大部分都是面…

做统计(蓝桥杯初级)

系列文章目录 e&#xff0c;新系列没有目录&#xff09; 文章目录 系列文章目录前言一、个人名片二、描述三、输入输出以及代码示例1.输入输入样例&#xff1a; 2.输出输出样例&#xff1a; 3.代码示例 四、思路总结 前言 今天我们来做《做统计》 一、个人名片 个人主页&…

GPT-4论文阅读

GPT-4 Technical Report论文阅读 文章目录 GPT-4 Technical Report论文阅读 Abstract训练的稳定性Training processPredictable scaling训练的稳定性多么难能可贵 Capabilities考试成绩传统的benchmark语言方面的能力Visual inputsSteerability LimitationsRisks & mitigat…

架构模式:MVC

引言 MVC&#xff0c;即 Model&#xff08;模型&#xff09;-View&#xff08;视图&#xff09;-Controller&#xff08;控制器&#xff09;&#xff0c;是广泛应用于交互式系统中的典型架构模式&#xff0c;尤其在 GUI 和 Web 应用中。 MVC 的概念源自 GOF&#xff08;Gang …

Java设计模式之命令模式介绍和案例示范

一、命令模式简介 命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使你可以用不同的请求对客户端进行参数化、对请求排队或记录日志&#xff0c;以及支持可撤销的操作。命令模式的核心思想是将发出请…

学习Vue3的第四天

目录 pinia 安装 Pinia 存储读取数据 修改数据(三种方式) storeToRefs getters $subscribe store组合式写法 组件通信 props 自定义事件 mitt v-model $attrs $refs、$parent provide、inject slot pinia Pinia 是一个用于 Vue.js 的状态管理库&#xff0c;作…

vue-i18n 使用 $t 导致的 Typescript 报错问题

目录 1&#xff0c;问题2&#xff0c;解决 1&#xff0c;问题 在 Vue3 项目中使用 vue-i18n v9.14.0 时&#xff0c;可以&#xff1a; <template><div>{{ t(xxx) }}</div> </template><script setup lang"ts"> import { useI18n } f…

顶踩Emlog插件源码

源码介绍 顶踩Emlog插件源码 前些天看到小刀娱乐网的文章页面有了一些变化&#xff0c;那就是增加了一个有价值/无价值的顶踩按钮。 样式也是非常的好看 再加上两个表情包是非常的有趣。 写到了Emlog系统&#xff0c;效果如上图。 如何使用&#xff1a; 需要在echo_log.…

Datasheet SHT20芯片的数据手册

Datasheet SHT20芯片的数据手册 I2C读取湿度传感器返回的16位数据。SCL SDA 14位有效&#xff0c;我以为是将后二位删除&#xff0c;实际上看完手册才知道是后二位值无用&#xff0c;不是删除&#xff0c;而是清0&#xff0c;实际上还是16为&#xff0c;知识后二位是0还是1&…

Flume 日志采集系统

Flume 日志采集系统 一、Flume 概述二、Flume 架构设计2.1 架构图2.2 Flume Source 类型2.3 Flume Channel 类型2.4 Flume Sink 类型 三、Flume 安装部署3.1 下载解压3.2 上传解压3.3 修改配置文件2.4 启动 Flume Agent 四、案例实践&#xff1a;Flume 分布式集群搭建4.1 Flume…

基于SpringBoot的准妈妈孕期交流平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;IDEA/Eclipse、Navicat 系统展示 首页 管理员登录 用户管理 早教…

利用CubeMX复现正点原子TFTLCD驱动例程

来源&#xff1a;正点原子 FMC的工作原理暂时先欠着&#xff0c;先记录一下CRUD的过程。 第一步准备一个us级别延时函数&#xff0c;不会的参考拙作&#xff1a;STM32的定时器简介-CSDN博客 第二部开启FMC外设&#xff1a; ①进入 Pinout->FMC 配置栏&#xff0c;配置 …

如何做好网络安全

随着互联网技术的飞速发展&#xff0c;网站已成为企业对外展示、交流和服务的重要窗口。然而&#xff0c;随之而来的网站安全问题也日益凸显&#xff0c;给企业的业务发展和用户数据安全带来了巨大威胁。因此&#xff0c;高度重视网站安全已成为网络安全的首要任务。今天我们就…

使用ffmpeg在视频中绘制矩形区域

由于项目需要对视频中的人脸做定位跟踪&#xff0c; 我先使用了人脸识别算法&#xff0c;对视频中的每个帧识别人脸、通过人脸库比对&#xff0c;最终记录坐标等信息。 然后使用ffmpeg中的 drawbox 滤镜功能&#xff0c;选择性的绘制区域。从而实现人脸定位跟踪 1、drawbox …

用于协作代码开发的 10 大 GitHub 集成

GitHub 是开发人员的天堂。开发人员在分布式 GitHub 存储库中存储和管理其源代码,允许多个贡献者同时处理项目。这种协作行动将生产力提高了 22%,将修复漏洞的速度提高了 7 倍,并将入职时间缩短了 80%。 作为一个版本控制系统,它允许开发人员跟踪和审查更改、管理分支和合…

【Sceneform-EQR】通过sceneform-eqr实现一个视频播放器(使用安卓MediaPlayer实现视频播放)

在前一篇文档中介绍了如何在AR\三维场景创建几种背景 【Sceneform-EQR】scenefrom-eqr中的几种背景实现(不仅用于AR、三维场景&#xff0c;在图片、视频播放器中也适用) 本文将侧重介绍如何使用安卓MediaPlayer实现视频播放。 ↓↓↓↓↓↓↓↓↓↓↓↓ 以下正文 ↓↓↓↓↓↓…

李彦宏内部讲话曝光,谈大模型三大认知误区:智能体还是非共识

“外界对大模型有相当多的误解&#xff0c;”近日据媒体报道&#xff0c;李彦宏的一则内部讲话曝光。在最近一次和员工交流中&#xff0c;李彦宏谈及三个大模型认知误区&#xff0c;涵盖了大模型竞争、开源模型效率、智能体趋势等热点话题。 李彦宏认为未来大模型之间的差距可…

DWI扩散磁共振成像和结构连接组学指南

扩散磁共振成像和结构连接组学指南 引言流程概述扩散磁共振成像(dMRI)dMRI基础ADC&#xff08; apparent diffusion coefficient, 表观扩散系数&#xff09;MD&#xff08;mean diffusivity, 平均扩散率&#xff09;FA&#xff08; fractional anisotropy, 分数各向异性&#x…