历史上的今天

今天是:2024年09月02日(星期一)

正在发生

2021年09月02日 | STM32—DMA存储器到外设

发布者:tanjunhui 来源: eefocus关键字:STM32  DMA 手机看文章 扫描二维码
随时随地手机看文章

DMA简介

DMA(Direct Memory Access)——直接存储器存取,就像其名称一样,DMA的主要作用是搬数据,DMA可以把数据从存储器搬到外设、从外设搬到存储器、从存储器搬到存储器。DMA的特殊之处就是搬运数据不需要占用CPU,DMA控制器包含了DMA1和DMA2,其中DMA1由7个通道,DMA2有5个通道。


DMA框图

了解外设先要理解其工作框图:

在这里插入图片描述

功能框图主要分为三部分:

1.DMA请求

外设如果想要通过DMA传输数据,必先给DMA控制器发送DMA请求,DMA收到请求信号之后会传回给外设一个应答信号,当外设应答后且DMA控制器收到应答信号后,就会启动DMA的传输,直至传输完毕。但DMA有2个DMA控制器,15条通道,不同的通道对应着不同的外设请求,所以必须对通道和外设请求进行一一对口,各个通道对应的外设如下:

在这里插入图片描述

2.通道

要注意的是DMA共有12个独立可编程的通道,DMA1有7个、DMA2有5个,每个通道对应着不同的外设的请求,但同一时间只能接收一个。

3.仲裁器

当多个通道同时请求时,就要处理先后响应的问题,就像中断里的优先级分组一样。仲裁器管理DMA通道请求时主要依据俩点:

第一判断:在DMA_CCRx 寄存器中设置有 4 个等级:非常高、高、中和低,先根据此优先级判断响应的先后,如果优先级都一样,进行第二判断。

第二判断:比较通道的编号,编号越低优先权越高。


DMA传输数据分析

使用DMA,核心技术就是配置数据的传输,主要分为三点:

1.传输的方向

2.传输的数量

3.传输的模式


1.传输的方向

DMA有三种数据传输方向:


一:存储器——>外设

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

标地址。


二:外设——>存储器

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


三:存储器——>存储器

当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把 DMA_CCR 位 14: MEM2MEM:存储器到存储器模式配

置为 1,启动 M2M 模式。


2.传输的数量

以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由DMA_CNDTR 配置,这是一个 32 位的寄存器,一次最多只能传输 65535 个数据。而且源和目标的数据宽度必须一致,数据宽度可以设置为8/16/32位。

除此之外,还要设置源和目标俩边数据指针的增量模式 ,即传输完一个数据后数据指针的移动模式,是加一?还是不变?以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。


3.传输的模式

数据传输的情况可以通过查询标志位或者通过中断来鉴别,每个DMA通道在DMA传输过半、传输完成、传输错误时都会有相应的标志位,如果使能相关的中断后还会产生中断。一次数据传输完成后还分俩种模式:是一次传输还是循环传输。

一次传输: 传输一次后就停止,要想再传的话,必须关闭DMA使能后重新配置后方能继续传输。

循环传输: 一次传输完成后又恢复第一次传输时的配置循环传输,不断重复。


代码部分

编程要点:

1.配置USART通信功能

2.设置DMA工作参数

3.使能DMA


DMA初始化结构体

固件库编程中对一个外设的操作主要通过配置外设的初始化结构体来完成,虽然最终操作的是寄存器,但相比较寄存器,还是采用库函数比较方便。


 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;


下面利用《零死角玩转 STM32F103》的话介绍一下结构体成员:

1.DMA_PeripheralBaseAddr:外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址。


== 2.DMA_Memory0BaseAddr == : 存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自定义存储区的首地址。零死角玩转 STM32F103—MINI


3.DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设。它设定

DMA_CCR 寄存器的 DIR[1:0]位的值。这里并没有存储器到存储器的方向选择,

当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。


4. DMA_BufferSize:设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。


5. DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。


6. DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以要使能存储器地址自动递增功能。


7.DMA_PeripheralDataSize:外设数据宽度,可选字节(8 位)、半字(16 位)和字(32位),它设定 DMA_CCR 寄存器的 PSIZE[1:0]位的值。


8. DMA_MemoryDataSize:存储器数据宽度,可选字节(8 位)、半字(16 位)和字(32位),它设定 DMA_CCR 寄存器的 MSIZE[1:0]位的值。当外设和存储器之间传数据时,两边的数据宽度应该设置为一致大小。


9. DMA_Mode: DMA 传输模式选择,可选一次传输或者循环传输,它设定

DMA_CCR 寄存器的 CIRC 位的值。例程我们的 ADC 采集是持续循环进行的,所

以使用循环传输模式。


10.DMA_Priority:软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL[1:0]位的值。 DMA 通道优先级只有在多个 DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。


==11. DMA_M2M == :存 储器 到存 储器 模式 ,使 用存储 器到 存储 器时 用到, 设定DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。


USART配置函数

此代码结合串口通信章节内容理解更佳,此处不多于讲解。USART配置详解


void DEBUG_UART_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

/* 第一步:初始化GPIO */

// 打开串口GPIO的时钟

DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

// 将USART Tx的GPIO配置为推挽复用模式

GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);


  // 将USART Rx的GPIO配置为浮空输入模式

GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

/* 第二步:配置串口的初始化结构体 */

// 打开串口外设的时钟

DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

// 配置串口的工作参数

// 配置波特率

USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;

// 配置 针数据字长

USART_InitStructure.USART_WordLength = USART_WordLength_8b;

// 配置停止位

USART_InitStructure.USART_StopBits = USART_StopBits_1;

// 配置校验位

USART_InitStructure.USART_Parity = USART_Parity_No ;

// 配置硬件流控制

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

// 配置工作模式,收发一起

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

// 完成串口的初始化配置

USART_Init(DEBUG_USARTx, &USART_InitStructure);


/* 第三步:使能串口 */

// 使能串口

USART_Cmd(DEBUG_USARTx, ENABLE);

}


DMA配置函数

此函数主要是打开DMA外设时钟、配置DMA初始化结构体、使能DMA通道


// 串口对应的DMA请求通道

#define  USART_TX_DMA_CHANNEL     DMA1_Channel4

// 外设寄存器地址,USART1的数据寄存器地址(数据寄存器包含USART将发送或接收的数据)

#define  USART_DR_ADDRESS        (USART1_BASE+0x04)

// 一次发送的数据量

#define  SENDBUFF_SIZE            5000


uint8_t SendBuff[SENDBUFF_SIZE];

/* 定义的数组就存储在存储器SRAM中 */


void USARTx_DMA_Config(void)

{

DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

   // 设置DMA源地址:串口数据寄存器地址*/

       DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;

// 内存地址(要传输的变量的指针)

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;

// 方向:从内存到外设

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

// 传输大小

DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;

// 外设地址不增     

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

// 内存地址自增

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

// 外设数据单位

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

// 内存数据单位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  

// DMA模式,一次或者循环模式

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;

//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

// 优先级:中

DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 

// 禁止内存到内存的传输

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

// 配置DMA通道    

DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);

// 使能DMA

DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);

}


主函数

int main(void)

{

uint32_t i;

DEBUG_UART_Config();

USARTx_DMA_Config();

  /*填充将要发送的数据*/

  for(i=0;i  {

    SendBuff[i] = 'P';    

  }

   /* USART1向DMA发出请求 */

USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);


}

关键字:STM32  DMA 引用地址:STM32—DMA存储器到外设

上一篇:STM32—SysTick系统定时器
下一篇:STM32—SPI详解

推荐阅读

近年来,随着我国智慧物流业的快速发展,供应链端的自动化和信息化程度亦在不断提高,自动化立体仓库在很多行业得到了广泛应用,市场空间不断增大。据相关资料统计,近十年来我国自动化物流仓储系统市场规模保持平均约20%的增速,到2020年我国自动化立体库的市场规模将达到325亿元;而2016年,该项数据仅为149亿元。自动化立体仓库对仓库的存储量和仓储效...
上一篇文章讲了RTC,里面其实已经包含了时钟系统的介绍了。这篇文章将再详细的讲一下。一、时钟系统框图二、时钟系统STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。③、LSI是低速内部时钟,RC振荡器,频率...
提供领先的创新视频和显示处理解决方案提供商——Pixelworks, Inc.(纳斯达克股票代码:PXLW)今日发布了其第六代移动视觉处理器---i6,这是首款基于Pixelworks大量移动显示数据集和训练模型的低功耗人工智能(AI)技术的产品。Pixelworks i6处理器是业界首个具有集成AI处理单元的设备,该组件可自适应优化整体图像质量,从而在各种移动观看条件下提供始终...
大张伟是著名歌手、主持人。 他以其独特的幽默感和音乐才华受到大众的喜爱,在踏入主持圈后,很多人喜欢他的主持风格。他参加了许多不同的节目,给人们带来了很多欢乐。 在日常生活中,他也是一个非常快乐的人。他家的装修也流露出喜感,把家里装修的就像KTV一样。虽然不是所有人都能接受这种装修风格,但是现在很多普通人也会在家里建立一个家庭影音娱...

史海拾趣

问答坊 | AI 解惑

《32位MCU开发全攻略——上册》

《32位MCU开发全攻略——上册》…

查看全部问答∨

Protel DXP 2004实用教程CD版本

Protel DXP 2004实用教程CD版本…

查看全部问答∨

硬件化

哥哥们     我写了个程序,公司要求把这程序硬件化, 硬件化怎么做,?…

查看全部问答∨

谁有太阳能充电器的电路图?

要那种自动断电的,就是有阳光时给蓄电池充电,一旦没有阳光了,立刻停止充电,改为蓄电池供给一个LED发光二级管电,提示充电完毕 多谢大虾相助!…

查看全部问答∨

猎头职位:赴美国工程师!咨询13381085719 13381085771刘先生

猎头职位:赴美国工程师!咨询13381085719 13381085771刘先生…

查看全部问答∨

请问,现在常用的samsung nand flash 128MB是哪款?型号是什么,谢谢了

RT,可以加我QQ170473535,或者邮箱neulibo@sina.com.cn,再次感谢…

查看全部问答∨

Failed to attach to END device

我在烧写VxWorks镜像时,烧写完成后,启动就出现提示: muxDevload failed for device entry 0 Failed to attach to END device Attaching interface lo0...done wdb Config:error configuring WDB communication interface ... ... ... W ...…

查看全部问答∨

TMS2812 FFT库调用 问题求解

各位仁兄好!    现在我在调试FFT的时候遇见好多问题,前面都解决掉了,就是FFT转换成幅值mag的时候怎么都不正确。    我的调用方法如下(刚刚从网上直接修改的网友代码,和我的大同小异): #include \"DSP281x_De ...…

查看全部问答∨

有人用 cc2530 simplici TI协议么

要用cc2530做 点对点的透明传输。 上层是 一对多 的 轮询机制。 因为cc2530只管 把原来由485串口的有线传输 改成 无线传输。所以,2530本身不管任何 通信机制。 只是要求它可以 实现 任意时刻有一对cc2530设备点对点传输数据; 使用simpliciTI ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved