1.简介
DMA介绍
DMA是Direct Memory Access(直接存储器访问)的首字母缩写:是一种完全由硬件执行数据交换的工作方式。DMA控制器由CPU接管对总线的控制,不经过CPU直接在存储器和外设之间进行批量数据交换。
在硬件系统中,主要有CPU(内核)、外设、内设(SRAM)、总线(如图1所示)等结构组成,数据经常要在存储器与外设直接转移,或是从外设A转移到外设B。在不使用DMA的情乱下,内核通过系统总线经过总线矩阵协调,使用AHB把外设例如ADC采集的数据读取到内核,然后内核再通过总线矩阵协调,把数据存放到内存SRAM中。而DMA可以取代这样的工作,由DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设的数据经由DMA通道存放到内存SRAM,好处是提高数据的搬运速度。比如,使用DMA为多通道采集、采样频率高、连续输出数据的AD采集提供了更高效的办法。
图1
这里的外设一般是指外设的数据寄存器,比如ADC、SPI、I2C等外设的数据寄存器。存储器一般是指片内SRAM、外部存储器、片内Flash等。一句话概括就是使用DMA传输不需要占用CPU的资源,让CPU有足够的时间处理其他事情。
DMA传输的三大要素
- 传输源:DMA控制器从传输源读出数据;
- 传输目标:DMA控制器将数据传输的目标地址;
- 触发信号:用于触发一次数据传输的动作,执行一个单位的传输源至传输目标的数据传输;可以用来控制传输的时机。
DMA的优缺点:
速度快是其主要有点,由于CPU根本不参加传送操作,省去了CPU取指令、取数、送数等操作。在数据传输过程中,没有保存现场、恢复现场之类的工作。存储器地址修改、传输字个数的计数等等,不需要有软件实现,直接由内部硬件线路实现。DMA方式能满足高速I/O设备的要求,传输数据量较大时,有利于CPU效率的发挥。
配置起来相对复杂。如果传输数据少,可以直接由软件来实现,不需要使用DMA。在DMA传输中,如果有中断或者其他操作访问读写地址,会造成CPU卡死。
2.STM32的DMA特色
-
两个DMA控制器DMA1和DMA2,每个DMA控制器有8个数据流,每个数据流有多达8个通道;
-
软件编程设置优先级:非常高、高、中或低;硬件优先级可避免由于软件等级相同而引发的冲突;低通道优先高。
-
每个数据流有单独的四级32位先进先出存储器缓冲区(FIFO),DMA有两种运行模式:
FIFO模式:可通过软件将该阈值级别选取位FIFO大小的1/4,1/2或3/4.当FIFO存储器存储的数据大小超过FIFO大小的1/4,1/2或3/4,DMA将会传输FIFO里的数据。
直接模式:每个DMA请求会立即启动对存储器的传输。当在直接模式(禁止FIFO)下将DMA请求配置为以存储器到外设模式传输数据时,DMA仅会将一个数据从存储器预加载到内部FIFO,从而确保一旦外设触发DMA请求时则立即传输数据。 -
可编程和独立的源和目标的传输数据宽度:字节(8bit)、半字(16bit)、字(32bit);
-
由内存到内存、外设到内存和内存到外设的传输;
-
每个数据流也支持通过软件触发存储器到存储器的传输,只有DMA2控制器才可以进行存储器到存储器传输数据。
-
可编程设置传输数目:最大可达65535;
-
支持循环缓冲(循环模式)管理:对源和目标的增量或非增量寻址;
-
每个通道有5个事件标志:DMA传输一般、DMA传输完成、DMA传输出错、DMA FIFO错误、直接模式错误。
DMA事务
DMA可以实现外设寄存器、存储器之间三种模式,主要是DMA控制器采用AHB主总线,它可以控制AHB总线矩阵来启动AHB事务。
DMA事务由给定数据的数据传输序列组成。要传输的数据项的数目及其宽度(8位、16位或32位)可用软件编程。
1.DMA控制器产生事件。
2.外设会向DMA控制器发送请求信号。
3.DMA控制器收到该请求信号后,根据通道优先级处理该请求。
4.DMA控制器访问外设,DMA控制器向外设发送确认信号。
5.外设获得DMA控制器的确认信号后,会立即释放其请求。一旦外设请求失效,DMA控制器就释放确认信号。如果有更多请求,外设可以启动下一个事务。
图2
图3
图4
基于STM32的DMA应用
HAL库
```c
typedef struct __DMA_HandleTypeDef
{
DMA_Stream_TypeDef *Instance; /*!< DMA寄存器基地址 */
DMA_InitTypeDef Init; /*!< DMA通信参数 */
HAL_LockTypeDef Lock; /*!< 对资源型操作增加操作锁 */
__IO HAL_DMA_StateTypeDef State; /*!< DMA传输状态,通过不同状态的设定,实现各种方式的传输 */
void *Parent; /*!< 指向DMA通道外设句柄 */
void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA 传输完成回调函数 */
void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA 传输完成一半回调函数 */
void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< Memory1传输完成回调函数 */
void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< Memort1传输完成一半回调函数 */
void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA 传输错误回调函数 */
void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA 传输终止回调函数 */
__IO uint32_t ErrorCode; /*!< DMA 错误代码 */
uint32_t StreamBaseAddress; /*!< DMA 流基地址 */
uint32_t StreamIndex; /*!< DMA 索引号 */
}DMA_HandleTypeDef;
typedef struct
{
uint32_t Channel; /*!< Specifies the channel used for the specified stream.
This parameter can be a value of @ref DMA_Channel_selection */
uint32_t Direction; /*!< Specifies if the data will be transferred from memory to peripheral,
from memory to memory or from peripheral to memory.
This parameter can be a value of @ref DMA_Data_transfer_direction */
uint32_t PeriphInc; /*!< Specifies whether the Peripheral address register should be incremented or not.
This parameter can be a value of @ref DMA_Peripheral_incremented_mode */
uint32_t MemInc; /*!< Specifies whether the memory address register should be incremented or not.
This parameter can be a value of @ref DMA_Memory_incremented_mode */
uint32_t PeriphDataAlignment; /*!< Specifies the Peripheral data width.
This parameter can be a value of @ref DMA_Peripheral_data_size */
uint32_t MemDataAlignment; /*!< Specifies the Memory data width.
This parameter can be a value of @ref DMA_Memory_data_size */
uint32_t Mode; /*!< Specifies the operation mode of the DMAy Streamx.
This parameter can be a value of @ref DMA_mode
@note The circular buffer mode cannot be used if the memory-to-memory
data transfer is configured on the selected Stream */
uint32_t Priority; /*!< Specifies the software priority for the DMAy Streamx.
This parameter can be a value of @ref DMA_Priority_level */
uint32_t FIFOMode; /*!< Specifies if the FIFO mode or Direct mode will be used for the specified stream.
This parameter can be a value of @ref DMA_FIFO_direct_mode
@note The Direct mode (FIFO mode disabled) cannot be used if the
memory-to-memory data transfer is configured on the selected stream */
uint32_t FIFOThreshold; /*!< Specifies the FIFO threshold level.
This parameter can be a value of @ref DMA_FIFO_threshold_level */
uint32_t MemBurst; /*!< Specifies the Burst transfer configuration for the memory transfers.
It specifies the amount of data to be transferred in a single non interruptible
transaction.
This parameter can be a value of @ref DMA_Memory_burst
@note The burst mode is possible only if the address Increment mode is enabled. */
uint32_t PeriphBurst; /*!< Specifies the Burst transfer configuration for the peripheral transfers.
It specifies the amount of data to be transferred in a single non interruptible
transaction.
This parameter can be a value of @ref DMA_Peripheral_burst
@note The burst mode is possible only if the address Increment mode is enabled. */
}DMA_InitTypeDef;