5、DMA Demo(STM32F407)

news2024/9/23 23:31:50

DMA简介

DMA 全称Direct Memory Access,即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。 DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

作用:为CPU减负。

DMA原理

STM32F4最多有2个DMA控制器,2个DMA控制器总共有16个数据流(每个控制器8个)。每个DMA控制器都用于管理一个或者多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或请求),每个通道都有一个仲裁器,用于处理DMA请求间的优先级。

DMA框图

 

DMA配置参数

  1.  通道
  2. 优先级
  3. 数据传输方向
  4. 存储器/外设 数据宽度
  5. 存储器/外设 地址是否增量
  6. 循环模式
  7. 数据传输量

程序源码

dma.h

#ifndef __DMA_H
#define	__DMA_H	   
#include "sys.h"

void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr);//配置DMAx_CHx
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr);	//使能一次DMA传输		   
#endif

dma.c

#include "dma.h"
#include "delay.h"

// DMAx的各通道配置
// 这里的传输形式是固定的,这点要根据不同的情况来修改
// 从存储器->外设模式/8位数据宽度/存储器增量模式
// DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
// chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
// par:外设地址
// mar:存储器地址
// ndtr:数据传输量
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, u32 chx, u32 par, u32 mar, u16 ndtr)
{

  DMA_InitTypeDef DMA_InitStructure;

  if ((u32)DMA_Streamx > (u32)DMA2) // 得到当前stream是属于DMA2还是DMA1
  {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // DMA2时钟使能
  }
  else
  {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // DMA1时钟使能
  }
  DMA_DeInit(DMA_Streamx);

  while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
  {
  } // 等待DMA可配置

  /* 配置 DMA Stream */
  DMA_InitStructure.DMA_Channel = chx;                                    // 通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr = par;                         // DMA外设地址
  DMA_InitStructure.DMA_Memory0BaseAddr = mar;                            // DMA 存储器0地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                 // 存储器到外设模式
  DMA_InitStructure.DMA_BufferSize = ndtr;                                // 数据传输量
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设非增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 存储器增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据长度:8位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 存储器数据长度:8位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 使用普通模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   // 中等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;         // 存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 外设突发单次传输
  DMA_Init(DMA_Streamx, &DMA_InitStructure);                          // 初始化DMA Stream
}
// 开启一次DMA传输
// DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
// ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, u16 ndtr)
{

  DMA_Cmd(DMA_Streamx, DISABLE); // 关闭DMA传输

  while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
  {
  } // 确保DMA可以被设置

  DMA_SetCurrDataCounter(DMA_Streamx, ndtr); // 数据传输量

  DMA_Cmd(DMA_Streamx, ENABLE); // 开启DMA传输
}

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "dma.h"

#define SEND_BUF_SIZE 8200 // 发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.

u8 SendBuff[SEND_BUF_SIZE]; // 发送数据缓冲区
const u8 TEXT_TO_SEND[] = {"ALIENTEK Explorer STM32F4 DMA 串口实验"};

int main(void)
{
	u16 i;
	u8 t = 0;
	u8 j, mask = 0;
	float pro = 0;									// 进度
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置系统中断优先级分组2
	delay_init(168);								// 初始化延时函数
	uart_init(115200);								// 初始化串口波特率为115200
	LED_Init();										// 初始化LED
	LCD_Init();										// LCD初始化
	KEY_Init();										// 按键初始化
	// 配置DMA,使用DMA2的第7个数据流和第4个通道,将USART1的数据寄存器作为外设,SendBuff作为存储器,传输长度为SEND_BUF_SIZE
	MYDMA_Config(DMA2_Stream7, DMA_Channel_4, (u32)&USART1->DR, (u32)SendBuff, SEND_BUF_SIZE); // DMA2,STEAM7,CH4,外设为串口1,存储器为SendBuff,长度为:SEND_BUF_SIZE.
	POINT_COLOR = RED;
	LCD_ShowString(30, 50, 200, 16, 16, "Explorer STM32F4");
	LCD_ShowString(30, 70, 200, 16, 16, "DMA TEST");
	LCD_ShowString(30, 90, 200, 16, 16, "ATOM@ALIENTEK");
	LCD_ShowString(30, 110, 200, 16, 16, "2014/5/6");
	LCD_ShowString(30, 130, 200, 16, 16, "KEY0:Start");
	POINT_COLOR = BLUE; // 设置字体为蓝色
	// 显示提示信息
	j = sizeof(TEXT_TO_SEND);			// 获取TEXT_TO_SEND字符串的长度,并赋值给变量j
	for (i = 0; i < SEND_BUF_SIZE; i++) // 循环从0到SEND_BUF_SIZE-1,填充SendBuff数组
	{
		if (t >= j) // 加入换行符
		{
			if (mask)
			{
				SendBuff[i] = 0x0a; // 插入换行符
				t = 0;
			}
			else
			{
				SendBuff[i] = 0x0d; // 插入回车符
				mask++;
			}
		}
		else // 复制TEXT_TO_SEND语句
		{
			mask = 0;
			SendBuff[i] = TEXT_TO_SEND[t];
			t++;
		}
	}
	POINT_COLOR = BLUE; // 设置字体为蓝色
	i = 0;
	while (1)
	{
		t = KEY_Scan(0);	// 清除按键标记
		if (t == KEY0_PRES) // KEY0按下
		{
			printf("\r\nDMA DATA:\r\n");
			LCD_ShowString(30, 150, 200, 16, 16, "Start Transimit....");
			LCD_ShowString(30, 170, 200, 16, 16, "   %");  // 显示百分号
			USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 使能串口1的DMA发送
			MYDMA_Enable(DMA2_Stream7, SEND_BUF_SIZE);	   // 启动一次DMA传输
			// 等待DMA传输完成,此时我们来做另外一些事,点灯
			// 实际应用中,传输数据期间,可以执行另外的任务
			while (1)
			{
				// 通过DMA_GetCurrDataCounter函数获取当前还剩余多少个数据未传输,然后计算传输的百分比
				if (DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) != RESET) // 等待DMA2_Steam7传输完成
				{
					DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7); // 清除DMA2_Steam7传输完成标志
					break;
				}
				pro = DMA_GetCurrDataCounter(DMA2_Stream7); // 得到当前还剩余多少个数据
				pro = 1 - pro / SEND_BUF_SIZE;				// 得到百分比
				pro *= 100;									// 扩大100倍
				LCD_ShowNum(30, 170, pro, 3, 16);
			}
			LCD_ShowNum(30, 170, 100, 3, 16);							 // 显示100%
			LCD_ShowString(30, 150, 200, 16, 16, "Transimit Finished!"); // 提示传送完成
		}
		i++;
		delay_ms(10);
		if (i == 20)
		{
			LED0 = !LED0; // 提示系统正在运行
			i = 0;
		}
	}
}

实验效果

说明:

串口首先打印TFT屏的驱动IC(按下RESET键复位)

按下KEY0后,TFT屏显示“Transimit Finished!”并显示传输进度,串口重复打印“ALIENTEK Explorer STM32F4 DMA 串口实验”直至达到SEND_BUF_SIZE的值

DMA实验效果

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

倒计时(JS计时器)

<script>function countDown() {document.body.innerHTML ;//清空页面内容var nowTimer new Date(); //现在时间的毫秒数var valueTimer new Date("2024-1-1 12:00"); //用户输入年份倒计时时间毫秒数var timer (valueTimer - nowTimer) / 1000; //倒计时秒…

网工内推 | 云计算运维,云相关认证优先,最高30K,带薪年假

01 安畅网络 招聘岗位&#xff1a;云计算运维工程师 职责描述&#xff1a; 1、负责对公有云平台的计算、存储、网络资源等IAAS/SAAS/PAAS层产品组件日常交付部署运维工作&#xff0c;包括调试、配置、维护、监控、优化等工作&#xff1b; 2、负责对操作系统及应用日常运行维护…

springboot+jsp+java人才招聘网站4f21r

本基于springboot的人才招聘网站主要满足3种类型用户的需求&#xff0c;这3种类型用户分别为求职者、企业和管理员&#xff0c;他们分别实现的功能如下。 &#xff08;1&#xff09;求职者进入网站后可查看职位信息、企业信息以及职位新闻等&#xff0c;注册登录后可实现申请职…

获取焦点后,样式异常的处理方法

问题 在使用monaco-editor 设置代码提示未正常显示&#xff0c;提示框出现&#xff0c;看不到内容&#xff0c;如图 看不到内容&#xff0c;有两种情况&#xff1a; 情况一&#xff1a;没有得到数据&#xff0c;所以没有展示&#xff1b; 情况二&#xff1a;得到了数据&#x…

用户注册这样玩,保你平安

前言 基本上每个系统系统都包含用户注册、发送验证码等基本操作。在前些年&#xff0c;我还记得我在逛 csdn、贴吧、网易新闻等网站的时候是可以不登陆也能浏览完网页内容的&#xff0c;但是近几年这些网站已经改成了不登陆不让用&#xff0c;浏览网页时不时提醒你要进行登录&…

基于B/S架构的医院一体化电子病历编辑器源码

电子病历在线制作、管理和使用的一体化电子病历解决方案&#xff0c;通过一体化的设计&#xff0c;提供对住院病人的电子病历书写、保存、修改、打印等功能。电子病历系统将临床医护需要的诊疗资料以符合临床思维的方法展示。建立以病人为中心&#xff0c;以临床诊疗信息为主线…

【前端开发】Next.js与Nest.js之间的差异2023

在快节奏的网络开发领域&#xff0c;JavaScript已成为构建可靠且引人入胜的在线应用程序的标准语言。然而&#xff0c;随着对适应性强、高效的在线服务的需求不断增加&#xff0c;开发人员通常不得不从广泛的库和框架中进行选择&#xff0c;以满足其项目的要求。Next.js和Nest.…

Electron+Ts+Vue+Vite桌面应用系列:TypeScript常用语法详解

文章目录 1️⃣ TypeScript常用讲解1.1 使用1.2 字符串1.3 数字1.3 布尔1.4 数组1.5 元组1.6 枚举1.7 any1.8 void1.9 object1.10 函数指定返回值的类型1.11 联合类型1.12 类型断言1.13 接口1.14 函数类型1.15 类类型1.16 泛型 2️⃣ 类2.1 类的基本写法2.2 类的继承2.3 类的修…

指数退避和抖动

目录 引入 OCC 添加退避机制 添加抖动机制 小结 引入 OCC 乐观并发控制&#xff08;Optimistic Concurrency Control&#xff0c;OCC&#xff09;是一种既能保证多个写入者安全地修改单个对象又能避免丢失写入的古老方法OCC具有三个优点&#xff1a;只要底层存储可用&#…

windows配置服务开机自启和保活

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、下载WinSW&#xff1f;二、使用步骤1.解压2.配置3.安装服务4.服务启停5.服务卸载6.开机自启7.保活 总结 前言 写了一个程序或者是exe&#xff0c;或者是ba…

压缩包批量处理工具中文免费版1.7

软件功能&#xff1a; 压缩文件批量处理工具中文免费版是一款可以帮助用户来进行批量修改压缩文件的软件&#xff0c;支持的压缩格式有rar/zip/7z/tar 1、可批量向压缩包添加文件&#xff08;包括url快捷方式&#xff09; 2、可批量从压缩包删除文件&#xff08;支持删除二级目…

mysql中字符串截取与拆分

按分隔符把字符串拆成多行 引言截取字符串一、left(str,length)二、right(str,length)三、截取特定长度的字符串四、按分隔符截取 分割字符串一、分割成多列二、分割成多行 总结 引言 截取和拆分字符串在编程生涯中是普遍存在的&#xff0c;在sql中也不例外&#xff0c;下面就…

Linux常用命令----rmdir命令

文章目录 1. 简介2. 参数含义3. 常见用法及实例4. 注意事项5. 结语 1. 简介 rmdir命令在Linux系统中用于删除空的目录。它是一个基本的命令行工具&#xff0c;用于维护文件系统的组织结构。与rm命令不同&#xff0c;rmdir只能删除空目录&#xff0c;这提供了一种安全机制&…

小米的算法部署岗对新手是真的友好

大家好啊&#xff0c;我是董董灿。 自从开始写一些AI行业的岗位介绍&#xff0c;就养成了一个习惯&#xff0c;在上下班的路上经常就会打开某聘瞧一瞧。 导致之前一年不看的某聘认为我要看机会换工作&#xff0c;疯狂给我推猎头&#xff0c;然后电话就进来了。 不堪骚扰的我…

全网最牛最全面的Jmeter接口测试:jmeter_逻辑控制器_事务控制器

事务&#xff1a; 性能测试中&#xff0c;事务指的是从端到端&#xff0c;一个完整的操作过程&#xff0c;比如一次登录、一次 筛选条件查询&#xff0c;一次支付等&#xff1b;技术上讲&#xff1a;事务就是由1个或多个请求组成的 事务控制器 事务控制器类似简单控制器&…

【数据结构】源码角度剖析PriorityQueue

目录 认识 Queue 认识 PriorityQueue PriorityQueue为什么要用二叉堆&#xff1f; PriorityQueue构造方法源码分析 PriorityQueue 的属性 构造方法 JDK1.8传入不可比较的对象 JDK17传入不可比较的对象 传入带有Collection接口的对象 instanceof 关键字 Offer方法分析…

finebi 新手入门案例

finebi 新手入门案例 连锁超市销售数据分析 步骤&#xff1a; 准备公共数据新建分析主题处理数据在数据中分析在图形中分析数据大屏 准备公共数据 点击公共数据 点击新建文件夹 修改文件夹名称 上传数据 鼠标悬停在文件夹上&#xff0c;右侧出现 鼠标悬停在文件夹上&#x…

matplotlib与opencv图像读取与显示的问题

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 最近在用opencv和matplotlib展示图片,但是遇到了一些问题,这里展开说说 首先需要明确的是,opencv和matplotlib读取图片都是通道在最后,而前者默认可见光图像是BGR,后者是RGB.此外还有PIL以及imageio等读取图像的工具…

python——第十五天

面向对象和面向对象编程 面向对象编程&#xff1a; C语言是一门面向过程的编程语言&#xff01;&#xff01;&#xff01; 面向对象的编程思想 就是分门别类的一种能力 面向对象的概念 类&#xff1a; 对一类事物的统称 对象&#xff1a; 一类事物中的具体案例 面向对象的…

【论文阅读】1 SkyChain:一个深度强化学习的动态区块链分片系统

SkyChain 一、文献简介二、引言及重要信息2.1 研究背景2.2 研究目的和意义2.3 文献的创新点 三、研究内容3.1模型3.2自适应分类账协议3.2.1状态块创建3.2.2合并过程3.2.3拆分过程 3.3评价框架3.3.1性能3.3.1.1共识延迟3.3.1.2重新分片延迟3.3.1.3处理事务数3.3.1.4 约束 3.3.2 …