STM32学习和实践笔记(37):DMA实验

1.DMA简介

  DMA,全称是Direct Memory Access,中文意思为直接存储器访问。DMA可用于实现外设与存储器之间或者存储器与存储器之间数据传输的高效性。

之所以高效,是因为DMA传输数据移动过程无需CPU直接操作,这样节省的 CPU 资源就可供其它操作使用。同时,因为没有经过CPU进行中间操作,所以效率更高!

从硬件层面来理解,DMA就好像是RAMI/O设备间数据传输的通路,外设与存储器之间或者存储器与存储器之间可以直接在这条通路上进行数据传输。

这里说的外设一般指外设的数据寄存器,比如 ADC、 SPI、 I2C、 DCMI等外设的数据寄存器,存储器一般是指片内SRAM、外部存储器、片内 Flash 等。

   STM32F1 最多有 2 个 DMA 控制器, DMA1 有 7 个通道, DMA2 有 5个通道(DMA2 仅存在STM系列的大容量产品中),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。(很明显,不通CPU来管理,那就需要另外有一个控制器来控制,这就是DMA控制器和仲裁器的作用)

2.DMA结构框图

  DMA 控制器在整个芯片中,它独立于内核,是一个单独的外设,结构比较简单,从编程的角度来看,我们只需掌握结构框图中的三部分内容即可。如图:

1)标号1:DMA请求

  如果外设要想通过 DMA 的方式来传输数据,必须先给 DMA 控制器发送 DMA 请求, DMA收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕。

2)标号2:DAM通道

DMA含有DMA1和DMA2两个控制器,其中DMA1含有7个通道,DMA2含有5个通道,不同的 DMA 控制器的通道对应着不同的外设请求,如下图所示:

每个通道对应不同的外设的 DMA 请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。

DMA1:

(例如:外设ADC1只能通过DMA1的通道1与存储器互相传输数据。)

DMA2:

3)标号3:仲裁器

  当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器管理。

仲裁器管理 DMA 通道请求分为两个阶段。

第一阶段属于软件阶段,可以在DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。

第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的软件优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级

3.DMA数据具体传递方式

1外设到存储器

  当我们使用从外设到存储器传输时,以 ADC 采集为例DMA 外设寄存器的地址对应的就是 ADC 数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址。简单来说,就是从ADC数据寄存器地址往存储器对应的地址存放数据!因此首先将DMA外设寄存器地址对应到ADC数据寄存器地址。

2)存储器到外设

  当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例DMA 外设寄存器的地址对应的就是串口数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。

3)存储器到存储器

  当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。

跟上面两个不一样的是,这里需要把 DMA_CCR 位 14: MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式。

4.STM32F1 DMA配置步骤

具体步骤如下:(DMA相关库函数在stm32f10x_dma.c和stm32f10x_dma.h文件中)

(1)使能DMA控制器(DMA1或DMA2)时钟

void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

(2)初始化DMA通道,包括配置通道、外设和内存地址、传输数据量等

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef* DMA_InitStruct);

typedef struct

{

  uint32_t DMA_PeripheralBaseAddr; // 外设地址

  uint32_t DMA_MemoryBaseAddr; // 存储器地址

  uint32_t DMA_DIR; // 传输方向

  uint32_t DMA_BufferSize; // 传输数目

  uint32_t DMA_PeripheralInc; // 外设地址增量模式

  uint32_t DMA_MemoryInc; // 存储器地址增量模式

  uint32_t DMA_PeripheralDataSize; // 外设数据宽度

  uint32_t DMA_MemoryDataSize; // 存储器数据宽度

  uint32_t DMA_Mode; // 模式选择

  uint32_t DMA_Priority; // 通道优先级

  uint32_t DMA_M2M; // 存储器到存储器模式

} DMA_InitTypeDef;

该结构体各成员的作用介绍如下:

DMA_PeripheralBaseAddr:外设地址,通过DMA_CPAR寄存器设置,一般设置为外设的数据寄存器地址,比如要进行串口DMA 传输,那么外设基地址为串口接受发送数据存储器USART1->DR 的地址,表示方法为&USART1->DR。如果是存储器到存储器模式则设置为其中一个存储区地址。

  DMA_Memory0BaseAddr:存储器地址,通过DMA_CMAR寄存器设置,一般设置为我们自定义存储区的首地址,即我们存放DMA传输数据的内存地址。比如我们定义一个u32类型数组,将数组首地址(直接使用数组名即可)赋值给DMA_Memory0BaseAddr,在DMA传输的时候就可以把数组内的数据发送或接收。

  DMA_DIR:数据传输方向选择,可选择外设到存储器、存储器到外设以及存储器到存储器。通过设定DMA_CCR寄存器的DIR[1:0]位的值决定。比如本章实验是从内存读取数据发送到串口,所以数据传输方向为存储器到外设,配置为DMA_DIR_MemoryToPeripheral。

  DMA_BufferSize:用来设置一次传输数据的大小,通过DMA_CNDTR寄存器设置。

  DMA_PeripheralInc:用来设置外设地址是递增还是不变,通过DMA_CCR寄存器的PINC位设置,如果设置为递增,那么下一次传输的时候地址加1。通常外设只有一个数据寄存器,所以一般不会使能该位,即配置为DMA_PeripheralInc_Disable。

  DMA_MemoryInc:用来设置内存地址是否递增,通过DMA_CCR寄存器的MINC位设置。我们自定义的存储区一般都是存放多个数据的,所以需要使能存储器地址自动递增功能,即配置为DMA_MemoryInc_Enable。

  DMA_PeripheralDataSize:外设数据宽度选择,可以为字节(8位)、半字(16位)、字(32位),通过DMA_CCR寄存器的PSIZE[1:0]位设置。例如本章实验数据是按照8位字节传输,所以配置为DMA_PeripheralDataSize_Byte。

  DMA_MemoryDataSize:存储器数据宽度选择,可以为字节(8位)、半字(16位)、字(32位),通过DMA_CCR寄存器的MSIZE[1:0]位设置。本章实验同样设置为8位字节传输,这个要和我们定义的数组对应,所以配置为DMA_MemoryDataSize_Byte。

  DMA_Mode:DMA传输模式选择,可选择一次传输或者循环传输,通过DMA_CCR寄存器的CIRC位来设定。比如我们要从内存(存储器)中传输64个字节到串口,如果设置为循环传输,那么它会在64个字节传输完成之后继续从内存的第一个地址传输,如此循环。这里我们设置为一次传输完成之后不循环。所以设置值为DMA_Mode_Normal。

  DMA_Priority:用来设置DMA通道的优先级,有低,中,高,超高四种级别,可通过DMA_CCR寄存器的PL[1:0]位来设定。DMA优先级只有在多个DMA数据流同时使用时才有意义,本章实验我们只使用了一个DMA数据流,所以可以任意设置DMA优先级,这里我们就设置为中等优先级,配置参数为DMA_Priority_Medium。

DMA_M2M:用来设置存储器到存储器模式,使用存储器到存储器时用到,设定DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式

了解结构体成员功能后,就可以进行配置,本章实验配置代码如下

  DMA_InitTypeDef  DMA_InitStructure;

  DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址

  DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存储器0地址

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存储器到外设模式

  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_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输

  DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA

(3)使能外设DMA功能(DMA请求映射图对应的外设)

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  //使能串口1的DMA发送

(4)开启DMA的通道传输

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

DMA_Cmd(DMA1_Channel4,ENABLE);

(5)查询DMA传输状态

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);

例如我们要查询DMA1通道4传输是否完成,方法是:

DMA_GetFlagStatus(DMA1_FLAG_TC4);

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);

5.硬件电路

  本实验使用到硬件资源如下

1)D1和D2指示灯

2)K_UP按键

3)串口1

4)DMA

D1指示灯用来提示系统运行状态,K_UP按键用来控制DMA发送,每按一次K_UP键,DMA就将内存(自定义的一个数组)内数据发送USART1,并通过串口1将发送的内容打印出来。在DMA数据传输的过程中让D2指示灯不断闪烁,直到数据传输完成。D2指示灯闪烁表CPU在执行其他的任务,说明DMA传输是不需要占用CPU的。

6.编写DMA控制程序

  本实验所要实现的功能是:通过K_UP按键控制DMA串口1数据的传送,在传送过程中让D2指示灯不断闪烁,直到数据传送完成。D1指示灯闪烁提示系统正常运行。程序框架如下:

(1)初始化USART1_TX对应的DMA通道相关参数

(2)编写主函数

main.c

#include "system.h"
#include "led.h"
#include "SysTick.h"
#include "usart.h"
#include "key.h"
#include "dma.h"#define send_buf_len 5000 //5000个字节长度u8 send_buf[send_buf_len];void Send_Data(u8 *p)
{u16 i;for (i=0; i<send_buf_len; i++){*p='5';//将5000个字节里的内容全部设为字符5p++;}}int main()
{u8 i=0;u8 key=0;SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组LED_Init();USART1_Init(9600);key_Init();DMAx_Init(DMA1_Channel4,(u32)&USART1->DR,(u32)send_buf,send_buf_len);Send_Data(send_buf);//将5000个字节里的内容全部设为字符5while(1){key = KEY_Scan(0);if(key == KEY_UP){USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//使能串口1的DMA功能DMAx_Enable(DMA1_Channel4,send_buf_len);while(1){if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=0)//检查传输是否完成{DMA_ClearFlag(DMA1_FLAG_TC4);break;//完成则退出while,led2就不再闪}led2=!led2; //没完成就一直闪delay_ms(300);}}i++;if(i%20 ==0){led1=!led1;//LED1闪,用来指示主程序循环是否运行delay_ms(300);}}}

        dma.c

#include "dma.h"
#include "SysTick.h"void DMAx_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 par,u32 mar,u16 ndtr)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA1时钟DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//传输方向:存储器到外设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_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输DMA_Init(DMAy_Channelx, &DMA_InitStructure);  //初始化DMA通道}void DMAx_Enable(DMA_Channel_TypeDef* DMAy_Channelx,u16 ndtr)
{DMA_Cmd(DMAy_Channelx,DISABLE); //DMA_SetCurrDataCounter(DMAy_Channelx, ndtr);DMA_Cmd(DMAy_Channelx,ENABLE);}

       程序写到开发板上测试,结果显示如下,LED也正常闪 ,实验是成功的!

             

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

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

相关文章

小程序使用接口wx.getLocation配置

开通时需详细描述业务&#xff0c;否则可能审核不通过 可能需要绑定腾讯位置服务&#xff0c;新建应该&#xff0c;绑定到小程序 配置 权限声明&#xff1a;在使用wx.getLocation前&#xff0c;需要在app.json的permission字段中声明对用户位置信息的使用权限&#xff0c;并提…

大数据学习-大数据介绍

意义 从海量的数据中分析出海量数据背后的价值 需要分析海量的数据&#xff0c;就需要存储、计算和分析 那就需要分布式多台计算机合适的工具来处理数据 工具 特点 大数据的核心工作&#xff1a;从海量的、高增长的、多类别的、信息密度低的数据中挖掘出高质量的结果 数据存储…

【CS.AI】决策树算法介绍: 原理与案例实现

文章目录 1. 简介1.1 决策树的基本原理核心概念: 2. 决策树的应用案例2.1 金融行业中的信用评分2.2 医疗诊断中的应用2.3 零售行业中的市场营销 3. 决策树的优缺点优点:缺点: 4. Python代码示例5 结论 [toc] ![在这里插入图片描述 1. 简介 决策树是一种常见且强大的机器学习算…

基坑监测的内容及其重要性概述

随着城市建设的不断深入&#xff0c;基坑工程作为基础建设的重要组成部分&#xff0c;其安全性和稳定性成为了关注的重点。为了确保基坑施工过程中的安全&#xff0c;基坑监测显得尤为重要。本文将围绕基坑监测的内容展开&#xff0c;旨在帮助读者更好地理解其重要性及实施方法…

卫星导航与gazebo仿真

全球卫星导航系统(Global Navigation Satelite System,GNSS)&#xff0c;简称卫星导航&#xff0c;是室外机器人定位的一个主要信息来源。 卫星导航能给机器人提供什么信息&#xff1f; 正常工作时&#xff0c;实际上可以提供机器人所需的所有定位信息&#xff0c;包括&#x…

用了这么久的群晖NAS,它到底能干些什么?

从21年开始玩群晖也有几年了&#xff0c;除非面临断电或升级&#xff0c;这个小伙伴都任劳任怨的工作着 现在NAS也广泛应用于家庭和企业环境中了&#xff0c;今天盘点一下我用群晖NAS都干了些什么~ 1.文件存储与共享&#xff1a; 群晖NAS可以作为文件服务器&#xff0c;提供…

【Autoware】Autoware.universe安装过程与问题记录

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Autoware.universe安装过程与问题记录。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下…

七人拼团:互助共赢,电商新动力

在当前繁荣的电商领域中&#xff0c;七人互助拼团模式以其别具一格的激励机制和互助合作理念&#xff0c;成为了消费者和商家共同瞩目的焦点。接下来&#xff0c;我们将详细解读这一模式中的直推激励、滑落补偿以及团队成就奖&#xff0c;并探讨其如何体现互助合作的精神。 一、…

190.回溯算法:组合(力扣)

代码随想录 (programmercarl.com) 一、什么是回溯算法 回溯算法是一种通用的算法设计技巧&#xff0c;特别适用于解决组合、排列、子集等问题。它通过逐步构建解决方案&#xff0c;并在发现部分解决方案无效时撤销&#xff08;回溯&#xff09;部分计算&#xff0c;从而寻找所…

灵活的招聘管理系统有五种方法帮助成功招聘

还记得以前的时代吗&#xff1f;这取决于你的年龄&#xff0c;直到智能手机、流媒体电视和电子邮件出现。今天&#xff0c;任何活着的成年人都经历了技术上的巨大变化&#xff0c;这创造了一种新的行为方式。人才获取也是如此。 一个值得推荐的招聘管理系统 招聘团队被困在满足…

用于制作耳机壳的UV树脂耳机壳UV胶价格高不高?

用于制作耳机壳的UV树脂耳机壳UV胶价格高不高&#xff1f; 制作耳机壳的UV树脂价格相对于一些其他材料可能会略高&#xff0c;但具体的价格取决于多个因素&#xff0c;如品牌、型号、质量等。一些高端的UV树脂品牌和型号可能会价格较高&#xff0c;但它们也通常具有更好的性能…

nvidia历史版本驱动

打开官网 https://www.nvidia.cn windows GTX-1060为例 标准

【中学教资科目二】03中学教学

03中学教学 第一节 教学的任务1.1 教学的任务 第二节 教学过程2.1 教学过程的本质2.2 教学过程的基本规律 第三节 教学原则3.1 因材施教原则 第四节 教学方法4.1 我国中学常用的教学方法4.2 国外常用的教学方法 第五节 教学组织形式、教学工作基本环节和教学评价5.1 现代其他教…

OpenHarmony角落里的知识:WPA_Supplicant连接方式

WPA_Supplicant连接方式简析 /third_party/wpa_supplicant |-- CONTRIBUTIONS # 贡献说明 |-- COPYING # 版权说明 |-- wpa_supplicant-2.9 # 轻量级系统的wpa_supplicant | |-- hostapd # Wi-Fi热点相关功能&#xff08;Acces…

基于Web的图书管理系统运行教程

1.5、安装数据库和数据库管理工具 目前作者所有毕设源码数据库都用 MySQL5.7版本&#xff0c;强烈建议初学者跟作者保持一致&#xff0c;避免出现其他错误&#xff0c;待运行成功后再根据需要调整数据库版本。 MySQL5.7安装教程请看作者另一篇文章&#xff1a;《MySQL绿色版安…

设计模式(三)代理模式

目录 一、什么是代理模式 二、静态代理 1、定义 2、代码 2.1、接口 2.2、被代理对象 2.3、代理对象 2.4、测试 三、动态代理 1、定义 2、代码 2.1、接口 2.2、目标对象 2.3、代理对象 2.4、测试 一、什么是代理模式 代理模式(Proxy Pattern)是一种结构性模式。代理模…

【前端】[vue3] [uni-app] 组件样式击穿:deep

我是在开发uni-app时测试的思路&#xff0c;大家可以借鉴一下。 我这边测试的是uni组件&#xff0c;但是我觉得即便你用element-plus之类的&#xff0c;样式击穿的思路都相同。 我自定义了一个全局样式scss文件&#xff0c;并引入到了项目中。(如图) 利用vue3 中的 deep 方式…

人脸识别——可解释的人脸识别(XFR)人脸识别模型是根据什么来识别个人的

可解释性人脸识别&#xff08;XFR&#xff09;&#xff1f; 人脸识别有一个任务叫1:N&#xff08;识别&#xff09;。这个任务将一个人的照片与N张注册照片进行比较&#xff0c;找出相似度最高的人。 这项任务用于刑事调查和出入境点。在犯罪调查中&#xff0c;任务从监控摄像…

【论文笔记】Parameter-Effificient Transfer Learning for NLP

题目:Parameter-Effificient Transfer Learning for NLP 阅读 文章目录 0.摘要1.引言2 Adapter tuning for NLP3 实验3.1 参数/性能平衡3.2 讨论 4.相关工作 0.摘要 克服微调训练不高效的问题&#xff0c;增加一些adapter模块&#xff0c;思想就是固定原始的网络中的参数&…

mysql model_path longblob 类型文件操作

在 MySQL 中&#xff0c;如果你有一个表包含一个 LONG BLOB 类型的字段&#xff08;例如 model_path&#xff09;&#xff0c;你可以使用 SQL 查询来提取该字段中的二进制数据&#xff0c;并将其下载为文件。以下是一个完整的步骤来实现这个过程&#xff1a; 步骤1&#xff1a;…