下述以STM32F407VGT6为例,记录下学习DAM的相关理解以及记录。
理论
DMA介绍摘要自官方手册介绍
该设备具有两个通用双端口dma (DMA1和DMA2),每个DMA1和DMA2有8个流。它们能够管理内存到内存、外设到内存和内存到外设的传输。它们具有用于APB/AHB外设的专用fifo,支持突发传输,旨在提供最大的外设带宽(AHB/APB)。这两个DMA控制器支持循环缓冲区管理,因此当控制器到达缓冲区的末端时不需要特定的代码。两个DMA控制器还具有双重缓冲功能,可以自动使用和切换两个内存缓冲区,而不需要任何特殊代码。每个流连接到专用硬件DMA请求,支持每个流上的软件触发。配置由软件完成,源和目标之间的传输大小是独立的。
此外我发现ST官网也有对DMA的相关介绍STM32F2 STM32F4 DMA,肯定是讲的嘎嘎好的。
模式
正常模式:是最基本的DMA传输模式,它执行一次性的数据传输。工作方式,在正常模式下,DMA控制器会在完成指定数量的数据传输后停止传输。应用场景,适合一次性传输固定数量的数据,例如,从一个缓冲区复制数据到另一个缓冲区,或者从外设读取一定数量的数据。
循环模式:是一种特殊的DMA传输模式,它允许DMA传输在一个指定的缓冲区内循环进行。工作方式,在循环模式下,当DMA控制器完成指定数量的数据传输后,它会重新回到缓冲区的起始位置,并继续传输。适合需要持续传输数据的应用场景,例如实时数据流处理、音频数据传输等。例如,从ADC读取连续的数据流,或者向DAC写入连续的数据流。
Fifo
相当于使用先进先出的队列结构和其下的结构,来控制DMA的每次传输的数据量,传输触发条件等
Mx配置
STM32CubeMX的配置框图中参数介绍:
在MenToMen
处添加的,只能是内存到内存的控制流,假如是内存到外设或者外设到内存,如串口,ADC等,就要对外设配置好后,在看那个DMA总线的连接到该外设上,就可以去对应的DMA1
或DMA2
的选项卡上添加,都是有专属的DMA请求名称的。
通常DMA请求
确定后,方向
也随之确定了,也就是无法修改了。至于流
的选择,可以点看查看还有那个空闲的流可以选的,通常部分外设与内存之间的流有唯一的映射流(可以在数据手册中查看到),是不可选的。优先级
:共四级可选,Low(低),Medium(中),High(高),Very High(非常高),用于管理多个DMA请求之间的优先顺序。
下框图,模式
:有正常模式和循环模式,内存到内存模式无法启用循环模式。地址是否自增
:源内存和目的内存都可以勾选,例如:想要把一个数组的数据复制到另一个空数组中,那源内存和目的内存都必须勾选了,这样才能达到一一对应。数据长读
:三种可选,Byte(8位),Half Word(16位),Word(32位)。
当勾选上Use Fifo
后,下述参数可配置:阀值
:One Quarter Full(四分之一),Half Full(一半),Three Quarters Full(三分之一), Full(全满),当到达预设阀值后便会开始释放。释放值
:Single(单次传输),4 Increment(4个节拍的突发增量传输),8 Increment(8个节拍的突发增量传输),16 Increment(16个节拍的突发增量传输)。
参考网上资料,对上述使用Fifo的突发增量传输,写下个人理解:DMA要进行传输,就要获得总线仲裁管理总线事务裁决后分配的控制权,像是单次触发,就是接收到数据后就向裁决器申请控制权,至于突发增量传输的释放量,则是一次什么控制权后要传输的数据量,相对应的也是占用总线的时间数。阀值:则是内部数据量达到配置量时,会触发一次DMA传输。以目前开放板STM32F4为例,Fifo区的大小为4字(16字节)。例如:数据长度配置为word,释放量为4 Increment,阀值就只能选Full。因为目标传输数量就是
数据长度*释放量=4*word=Fifo缓冲区大小
。狭义总结:数据长度*释放量=传输大小(以及占用总线的节拍时间),阀值为Fifo缓冲区的数据达到多少了,就触发一次DMA传输,因为启用Fifo,数据的走向是,函数提交数据–>Fifo缓冲区–>达到阀值,开始申请DMA传输。
相关代码
STM32CubeMX中DMA的相关函数声明:
/*** 操作 ***/
HAL_StatusTypeDef HAL_DMA_Start (DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength); // 开始DMA传输
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength); // 启用中断启动DMA传输
HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma); // 终止DMA传输
HAL_StatusTypeDef HAL_DMA_Abort_IT(DMA_HandleTypeDef *hdma); // 终止在中断模式下的DMA传输
HAL_StatusTypeDef HAL_DMA_PollForTransfer(DMA_HandleTypeDef *hdma, HAL_DMA_LevelCompleteTypeDef CompleteLevel, uint32_t Timeout); // 完成传输轮询
void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma); // 处理DMA中断请求
HAL_StatusTypeDef HAL_DMA_CleanCallbacks(DMA_HandleTypeDef *hdma); // (没有定义)
HAL_StatusTypeDef HAL_DMA_RegisterCallback(DMA_HandleTypeDef *hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void (* pCallback)(DMA_HandleTypeDef *_hdma)); // 注册回调函数
HAL_StatusTypeDef HAL_DMA_UnRegisterCallback(DMA_HandleTypeDef *hdma, HAL_DMA_CallbackIDTypeDef CallbackID); // 注销回调
/*** 状态 ***/
HAL_DMA_StateTypeDef HAL_DMA_GetState(DMA_HandleTypeDef *hdma);
uint32_t HAL_DMA_GetError(DMA_HandleTypeDef *hdma);
轮询模式
轮询模式,又称堵塞模式。基本的使用流程,这里验证用的是DMA2流4
,要预先在STM32CubeMX上定义,验证的是内存到内存(变量到变量)。
// 定义两个变量
uint32_t i = 0;uint32_t num = 0;
// DMA_MemToMen验证(堵塞模式)
HAL_DMA_Start(&hdma_memtomem_dma2_stream4,(uint32_t)&i,(uint32_t)&num,1);
HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_stream4, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
中断模式
中断模式,又称非堵塞模式。基本的使用流程,除了上述讲到的按定义好DMA2流2
外,还要在NVIC
中使能该控制流的全局中断
,才能使用下述函数。
// 定义两个变量
uint32_t i = 0;uint32_t num = 0;
// DMA_MemToMen验证(非堵塞模式)
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream4,(uint32_t)&i,(uint32_t)&num,1);