1、DMA概念
“Direct Memory Access(存储器直接访问)。这是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据。整个数据传输操作在一个称为"DMA控制器"的控制下进行的。CPU除了在数据传输开始和结束时做一点处理外(开始和结束时候要做中断处理),在传输过程中CPU可以进行其他的工作(前提是未设置停止CPU访问)。这样,在大部分时间里,CPU和输入输出都处于并行操作。因此,使整个计算机系统的效率大大提高”。
DMA传送方式是让存储器与外设、或外设与外设之间直接交换数据,不需要经过CPU的累加器中转,减少了这个中间环节,并且内存地址的修改、传送完毕的结束报告都是由硬件电路实现的,因此大大地提高了数据的传输速度。一个DMA传送只需要执行一个DMA周期,相当于一个总线读写周期。
DMA是在专门的硬件( DMA)控制下,实现高速外设和主存储器之间自动成批交换数据尽量减少CPU干预的输入/输出操作方式。
2、DMA适用场景
DMA方式主要适用于一些高速的I/O设备。这些设备传输字节或字的速度非常快。对于这类高速I/O设备,如果用输入输出指令或采用中断的方法来传输字节信息,会大量占用CPU的时间,同时也容易造成数据的丢失。而DMA方式能使I/O设备直接和存储器进行成批数据的快速传送。
DMA传送主要用于需要高速大批量数据传送的系统中,以提高数据的吞吐量。如磁盘存取、图像处理、高速数据采集系统、同步通信中的收/发信号等方面应用甚广。通常只有数据流量较大(kBps或者更高)的外设才需要支持DMA能力,这些应用方面典型的例子包括视频、音频和网络接口。
3、STM32f10x的DMA功能框图
-
12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
-
每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过 软件来配置。
-
在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、 中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
-
独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目 标地址必须按数据传输宽度对齐。
-
支持循环的缓冲器管理
-
每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志 逻辑或成为一个单独的中断请求。
-
存储器和存储器间的传输
-
外设和存储器、存储器和外设之间的传输
-
闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
-
可编程的数据传输数目:最大为65535 。
4、DMA通道配置
1、在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将 是数据传输的源或目标
2、在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数 据将从这个地址读出或写入这个地址
3、在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
4、在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。
5、在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外 设和存储器的数据宽度、传输一半产生中断或传输完成产生中断
6、设置DMA_CCRx寄存器的ENABLE位,启动该通道。
5、DAM中断
6、DMA通道
7、DMA寄存器
- DMA中断状态寄存器(DMA_ISR)
- DMA中断标志清除寄存器(DMA_IFCR)
- DMA通道x配置寄存器(DMA_CCRx)(x = 1…7)
- DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1…7)
- DMA通道x外设地址寄存器(DMA_CPARx)(x = 1…7)
- DMA通道x存储器地址寄存器(DMA_CMARx)(x = 1…7)
8、DMA标准库
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; // 指定DMAy Channelx的外设基地址
uint32_t DMA_MemoryBaseAddr; // 指定DMAy Channelx的内存基址
uint32_t DMA_DIR; // 指定外围设备是源还是目标(值见DMA_data_transfer_direction)
uint32_t DMA_BufferSize; // 指定指定通道的缓冲区大小(以数据为单位)
// 数据单位等于DMA_PeripheralDataSize或DMA_MemoryDataSize成员中设置的配置,具体取决于传输方向
uint32_t DMA_PeripheralInc; // 指定外设地址寄存器是否递增(值见DMA_peripheral_incremented_mode)
uint32_t DMA_MemoryInc; // 指定内存地址寄存器是否递增(值见DMA_memory_incremented_mode)
uint32_t DMA_PeripheralDataSize; // 指定外设数据宽度(值见DMA_peripheral_data_size)
uint32_t DMA_MemoryDataSize; // 指定内存数据宽度(值见DMA_memory_data_size)
uint32_t DMA_Mode; // 指定DMAy Channelx的操作模式(值见DMA_circular_normal_mode)
// 如果在所选通道上配置了内存到内存的数据传输,则无法使用循环缓冲区模式
uint32_t DMA_Priority; // 指定DMAy Channelx的软件优先级(值见DMA_priority_level)
uint32_t DMA_M2M; // 指定是否在内存到内存传输中使用DMAy Channelx(值见DMA_memory_to_memory)
}DMA_InitTypeDef;
DMA_data_transfer_direction:
#define DMA_DIR_PeripheralDST ((uint32_t)0x00000010) // 传输方向为内存到外设
#define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000) // 传输方向外设到内存
DMA_peripheral_incremented_mode:
#define DMA_PeripheralInc_Enable ((uint32_t)0x00000040) // 外设地址递增
#define DMA_PeripheralInc_Disable ((uint32_t)0x00000000) // 外设地址非递增
DMA_memory_incremented_mode:
#define DMA_MemoryInc_Enable ((uint32_t)0x00000080) // 存储地址递增
#define DMA_MemoryInc_Disable ((uint32_t)0x00000000) // 存储地址非递增
DMA_peripheral_data_size:
#define DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) // 外设数据宽度-字节
#define DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000100) // 外设数据宽度-半字
#define DMA_PeripheralDataSize_Word ((uint32_t)0x00000200) // 外设数据宽度-字
DMA_memory_data_size:
#define DMA_MemoryDataSize_Byte ((uint32_t)0x00000000) // 外设数据宽度-字节
#define DMA_MemoryDataSize_HalfWord ((uint32_t)0x00000400) // 外设数据宽度-半字
#define DMA_MemoryDataSize_Word ((uint32_t)0x00000800) // 外设数据宽度-字
DMA_circular_normal_mode:
#define DMA_Mode_Circular ((uint32_t)0x00000020) // 循环模式
#define DMA_Mode_Normal ((uint32_t)0x00000000) // 非循环模式
DMA_priority_level:
#define DMA_Priority_VeryHigh ((uint32_t)0x00003000) // 非常高
#define DMA_Priority_High ((uint32_t)0x00002000) // 高
#define DMA_Priority_Medium ((uint32_t)0x00001000) // 中
#define DMA_Priority_Low ((uint32_t)0x00000000) // 低
DMA_memory_to_memory:
#define DMA_M2M_Enable ((uint32_t)0x00004000) // 开启内存到内存
#define DMA_M2M_Disable ((uint32_t)0x00000000) // 关闭内存到内存