本文是根据STM32F207的用户手册翻译整理而来git
DMA(Direct memory access)直接内存访问,被用于内存和内存之间或内存和外设之间的高速数据传输。数据传输能够在没有CPU的干预下快速移动,这样能够保持CPU资源处理其余事情github
DMA 控制器基于复杂的总线矩阵架构,将功能强大的双 AHB 主总线架构与独立的 FIFO 结编程
合在一块儿,优化了系统带宽,下图①处,能够看出双 AHB 主总线架构与独立的 FIFO的结构架构
注意看英文备注:app
DMA1控制器AHB外设端口没有像DMA2同样链接到总线矩阵,因此只有DMA2数据流能够执行存储器到存储器的传输优化
咱们对上图的②处,(DMA1和DMA2结构同样,咱们就选择DMA1)详细看ui
①每一个数据流总共能够有多达 8 个通道(或称请求)spa
②DMA1共有8个数据数据流(两个DMA共有16个数据流)翻译
③每一个DMA都有数据流仲裁器,用于处理 DMA 请求间的优先级3d
④DMA的数据流又有独立的FIFO
⑤DMA采用双 AHB 主总线架构
备注:
①处是选择器,配置完成只能选择一个通道,而③处是仲裁器,也就是说,配置完成,可能8个数据流所有存在,由仲裁器判断优先级
DMA从传输事务包含一系列的给定数目的数据传输序列。传输的数目能够经过软件编程,8位,16位或32位。
每一次DMA传输包含3个操做
在产生事件后,外设会向 DMA 控制器发送请求信号。 DMA 控制器根据通道优先级处理该请求。只要 DMA 控制器访问外设, DMA 控制器就会向外设发送确认信号。外设得到 DMA 控制器的确认信号后,便会当即释放其请求。一旦外设使请求失效, DMA 控制器就会释放确认信号。若是有更多请求,外设能够启动下一个事务
每一个数据流能够有8个通道
经过上图①能够看出,通道选择仲裁器能够经过DMA_SxCR寄存器的CHSEL[2:0]配置
DMA的请求能够来自TIM,ADC,SPI等外设
DMA1的请求通道
DMA2的请求通道
仲裁器为两个 AHB 主端口(存储器和外设端口)提供基于请求优先级的 8 个 DMA 数据流请求管理,并启动外设/存储器访问序列。
优先级管理分为两个阶段
8个DMA控制器数据流都可以提供源和目标之间的单向传输链路
每一个数据流配置后均可以执行
要传输的数据量(多达 65535)能够编程,并与链接到外设 AHB 端口的外设(请求 DMA 传输)的源宽度相关。每一个事务完成后,包含要传输的数据项总量的寄存器都会递减。
源地址和目标地址能够在整个4G地址空间,在0x00000000和0xFFFFFFFF之间
在DNA_SxCR寄存求的DIR[1:0]配置DMA的传输方式
源地址和目标地址的关系
当数据宽度是半字或字时,外设地址或存储器地址必须是半字或字对齐
当配置成外设到存储器的DMA传输模式时,两种模式
传输开始条件,使能数据流(DMA_SxCR 寄存器中的位 EN 置 1),而后外设发出请求,再而后该请求赢得了数据流仲裁,才会开始传输。
传输中止条件,下列知足一条便可
FIFO模式
这种模式,只要使能数据流(DMA_SxCR 寄存器中的位 EN 置 1),存储器数据就会传输到FIFO中,发生外设请求,FIFO数据会移出并存储到目标地址。当FIFO小于阈值,存储器的数据会重载FIFO。
直连模式
使能数据流时,DMA传输存储器的第一个数据到内部FIFO,发生外设请求时,DMA把预装在值发送的目标地址,而后进行下一个数据的传输。预装载的数据大小为 DMA_SxCR 寄存器中 PSIZE 位字段的值
传输中止条件,下列知足一条便可
存储器到外设模式和外设到存储器模式同样,一样须要对应数据流赢得仲裁,才会启动传输
这种模式较为简单,没有外设请求
启动传输
DMA_SxCR 寄存器中的使能位 (EN) 置 1 来使能数据流时,数据就会从源地址传输到FIFO,到达FIFO阈值时,FIFO数据移出到目标地址
中止传输,下列知足一条便可
固然,一样该数据流须要赢得仲裁
外设和存储器指针在每次传输后自动向后递增或保持常量,根据DMA_SxCR寄存器的PINC和MINC位。
禁止递增模式时很是有用的,当外设源和目标数据是经过单个寄存器访问的
若是使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1(对于字节)、 2(对于半字)或 4(对于字)
为了优化封装操做,能够无论 AHB 外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。 DMA_SxCR 寄存器中的 PINCOS 位用于将增量偏移大小与外设 AHB 端口或32 位地址(此时地址递增 4)上的数据大小对齐。 PINCOS 位仅对 AHB 外设端口有影响。
若是将 PINCOS 位置 1,则不论 PSIZE 值是多少,下一次传输的地址老是前一次传输的地址递增 4(自动与 32 位地址对齐) 。可是, AHB 存储器端口不受此操做影响。
若是 AHB 外设端口或 AHB 存储器端口分别请求突发事务,为了知足 AMBA 协议(在固定地址模式下不容许突发事务),则须要将 PINC 或 MINC 位置 1。
循环模式可用于处理循环缓冲区和连续数据流(例如 ADC 扫描模式)。可使用 DMA_SxCR寄存器中的 CIRC 位使能此特性。
当激活循环模式时,要传输的数据项的数目在数据流配置阶段自动用设置的初始值进行加载,并继续响应 DMA 请求。
也就是说,好比咱们要从内存中采集 64 个字节发送到串口,若是设置为重复采集,那么它会在 64 个字节采集完成以后继续从内存的第一个地址采集,如此循环。这里咱们设置为一次连续采集完成以后不循环。因此设置值为 DMA_Mode_Normal。在咱们下面的实验中,若是设置此参数为循环采集,那么你会看到串口不停的打印数据,不会中断,
此模式可用于全部 DMA1 和 DMA2 数据流。
经过将 DMA_SxCR 寄存器中的 DBM 位置 1,便可使能双缓冲区模式。
除了有两个存储器指针以外,双缓冲区数据流的工做方式与常规(单缓冲区)数据流的同样。使能双缓冲区模式时,将自动使能循环模式( DMA_SxCR 中的 CIRC 位的状态是“无关”),并在每次事务结束时交换存储器指针。
在此模式下,每次事务结束时, DMA 控制器都从一个存储器目标交换为另外一个存储器目标。这样,软件在处理一个存储器区域的同时, DMA 传输还能够填充/使用第二个存储器区域
基于DMA双缓冲模式的的特色,应用中必须开辟两个存储区以及存放两个存储区首地址的存储寄存器,DMA_SxM0AR和DMA_SxM1AR。
1:DMA_SxM0AR:指向存储区0(DMA_Memory_0),单缓冲模式下默认使用该寄存器作存储区指针。
2:DMA_SxM1AR:指向存储区1(DMA_Memory_1),仅在DMA双缓冲模式下才能使用。
3:DMA正在访问的当前存储区由DMA_SxCR表示
CT:当前目标
CT = 0:DMA正在访问存储区0,CPU能够访问存储区1
CT = 1:DMA正在访问存储区1,CPU能够访问存储区0
优势:
使用DMA双缓冲传输,既能够减小CPU的负荷,又能最大程度地实现DMA数据传输和CPU数据处理互不打扰又互不耽搁,DMA双缓冲模式的循环特性,使用它对存储区的空间容量要求也会大大下降。尤为在大批量数据传送时,你只需开辟两个合适大小的存储区,能知足DMA在切换存储区时的当前新存储区空出来就好,并不必定要开辟多大多深的存储空间,单纯一味地加大双缓冲区的深度并不明显改善数据传输情况
要传输的数据项数目必须在使能数据流以前编程到 DMA_SxNDTR(要传输数据项数目位,NDT)中,当流控制器是外设且 DMA_SxCR 中的 PFCTRL 位置为 1 时除外。
当使用内部 FIFO 时,源和目标数据的数据宽度能够经过 DMA_SxCR 寄存器的 PSIZE 和MSIZE 位(能够是 八、 16 或32 位)编程
DMA控制器能够产生一个单次传输或者四、8或16节拍的突发传输
突发大小经过软件针对两个 AHB 端口独立配置,配置时使用 DMA_SxCR 寄存器中的MBURST[1:0] 和 PBURST[1:0] 位
突发大小指示突发中的节拍数,而不是传输的字节数。
为确保数据一致性,造成突发的每一组传输都不可分割:在突发传输序列期间, AHB 传输会锁定,而且 AHB 总线矩阵的仲裁器不解除对 DMA 主总线的受权。
根据单次或突发配置的状况,每一个 DMA 请求在 AHB 外设端口上相应地启动不一样数量的传输
对于须要配置 MBURST 和 MSIZE 位的 AHB 存储器端口,必须考虑与上述相同的内容。在直接模式下,数据流只能生成单次传输,而 MBURST[1:0] 和 PBURST[1:0] 位由硬件强制配置。
必须选择地址指针(DMA_SxPAR 或 DMA_SxM0AR 寄存器),以确保一个突发块内的全部传输在等于传输大小的地址边界对齐
选择突发配置必需要遵照 AHB 协议,即突发传输不得越过 1 KB 地址边界,由于能够分配给单个从设备的最小地址空间是 1 KB。这意味着突发块传输不该越过 1 KB 地址边界,不然就会产生一个 AHB 错误,而且 DMA 寄存器不会报告这个错误。
FIFO结构
FIFO是用来临时存储从源地址传来的数据,在这些数据被发送到目的地市以前。
每一个数据流都有独立的4字FIFO,他们能够被软件配置为1/四、1/二、3/4或满
使用FIFO阈值,必须禁止直接模式
下图是FIFO结构和数据源、阈值关系的示意图
FIFO阈值和突发设置
警告被要求,选择 FIFO 阈值(DMA_SxFCR 寄存器的位 FTH[1:0])和存储器突发大(DMA_SxCR 寄存器的 MBURST[1:0] 位):FIFO 阈值指向的内容必须与整数个存储器突发传输彻底匹配。若是不是这样,当使能数据流时将生成一个 FIFO 错误( DMA_HISR 或 DMA_LISR寄存器的标志 FEIFx),而后将自动禁止数据流
FIFO更新
FIFO能够被更新,当数据流被禁止经过写入DMA_SxCR寄存器的EN位和当数据被配置成外设到存储器或存储区到存储区模式:若是禁止数据流时仍有某些数据存留在FIFO 中, DMA 控制器会将剩余的数据继续传输到目标(即便已经有效禁止了数据流)。刷新完成时,会将 DMA_LISR 或 DMA_HISR 寄存器中的传输完成状态位 (TCIFx) 置 1。
在这种状况下,剩余数据计数器 DMA_SxNDTR 保持的值指示在目标存储器现有多少可用数据项。
直接模式
默认状况下, FIFO 以直接模式操做(将 DMA_SxFCR 中的 DMDIS 位置 1),不使用 FIFO阈值级别。若是在每次 DMA 请求以后,系统须要至/自存储器的当即和单独传输,这种模式很是有用。
当在直接模式(禁止 FIFO)下将 DMA 配置为以存储器到外设模式传输数据时, DMA 会将一个数据从存储器预加载到内部 FIFO,从而确保一旦外设触发 DMA 请求时则当即传输数据
如下各类事件都可以结束传输过程,并将 DMA_LISR 或 DMA_HISR 状态寄存器中的 TCIFx位置 1
能够随时暂停 DMA 传输以供稍后从新开始;也能够在 DMA 传输结束前明确禁止暂停功能,分两种状况
控制要传输的数据数目的实体称为流控制器。此流控制器使用 DMA_SxCR 寄存器中的PFCTRL 位针对每一个数据流独立配置
流控制器能够是:
配置 DMA 数据流 x(其中 x 是数据流编号)时应遵照下面的顺序
一旦使能了流,便可响应链接到数据流的外设发出的任何 DMA 请求。
一旦在 AHB 目标端口上传输了一半数据,传输一半标志 (HTIF) 便会置 1,若是传输一半中断使能位 (HTIE) 置 1,还会生成中断。传输结束时,传输完成标志 (TCIF) 便会置 1,若是传输完成中断使能位 (TCIE) 置 1,还会生成中断。
对于每一个 DMA 数据流,可在发生如下事件时产生中断
中断列表
配置代码
/* Configure DMA Stream */ DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DST_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory; DMA_InitStructure.DMA_BufferSize = (uint32_t)32; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure);
设置 DMA 数据流对应的通道,供每一个数据流选择的通道请求多达 8 个,取值有
#define DMA_Channel_0 ((uint32_t)0x00000000) #define DMA_Channel_1 ((uint32_t)0x02000000) #define DMA_Channel_2 ((uint32_t)0x04000000) #define DMA_Channel_3 ((uint32_t)0x06000000) #define DMA_Channel_4 ((uint32_t)0x08000000) #define DMA_Channel_5 ((uint32_t)0x0A000000) #define DMA_Channel_6 ((uint32_t)0x0C000000) #define DMA_Channel_7 ((uint32_t)0x0E000000)
DMA 传输的外设基地址,假设进行uart1串口DMA 传输,咱们能够按照寄存器的地址偏移直接设置地址:0x40011004,也能够直接使用ST提供库的表示方法:&USART1->DR
DMA_Memory0BaseAddr :
DMA 传输的内存基地址
DMA_DIR:
设置数据传输方向,有存储器到存储器,存储器到外设,外设到存储器三种选择,取值有:
#define DMA_DIR_PeripheralToMemory ((uint32_t)0x00000000) #define DMA_DIR_MemoryToPeripheral ((uint32_t)0x00000040) #define DMA_DIR_MemoryToMemory ((uint32_t)0x00000080)
DMA_BufferSize:
设置一次传输数据量的大小
DMA_PeripheralInc:
设置传输数据的时候外设地址是不变仍是递增,若是设置为递增,那么下一次传输的时候地址加 1,取值有:
#define DMA_PeripheralInc_Enable ((uint32_t)0x00000200) #define DMA_PeripheralInc_Disable ((uint32_t)0x00000000)
DMA_MemoryInc:
设置传输数据时 候内存地址 是否递增。 这个参数和DMA_PeripheralInc 意思接近,只不过针对的是内存(存储器),取值有:
#define DMA_MemoryInc_Enable ((uint32_t)0x00000400) #define DMA_MemoryInc_Disable ((uint32_t)0x00000000)
DMA_PeripheralDataSize:
设置外设的数据长度是为字节传输(8bits),半字 传 输 (16bits) 还 是 字 传 输 (32bits),取值有:
#define DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) #define DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000800) #define DMA_PeripheralDataSize_Word ((uint32_t)0x00001000)
DMA_MemoryDataSize:
用来设置内存的数据长度
DMA_Mode:
设置 DMA 模式是否循环采集,取值有:
#define DMA_Mode_Normal ((uint32_t)0x00000000) #define DMA_Mode_Circular ((uint32_t)0x00000100)
DMA_Priority:
设置 DMA 通道的优先级,有低,中,高,超高三种模式。就是仲裁器仲裁的时候用的,当多个数据流同时开启,DMA仲裁器优先处理优先级高的数据流,取值有:
#define DMA_Priority_Low ((uint32_t)0x00000000) #define DMA_Priority_Medium ((uint32_t)0x00010000) #define DMA_Priority_High ((uint32_t)0x00020000) #define DMA_Priority_VeryHigh ((uint32_t)0x00030000)
DMA_FIFOMode:
设置是否开启 FIFO 模式,取值有:
#define DMA_FIFOMode_Disable ((uint32_t)0x00000000) #define DMA_FIFOMode_Enable ((uint32_t)0x00000004)
DMA_FIFOThreshold:
选择 FIFO 阈值,只有上个参数选择使能FIFO,这个参数才有用。取值有:
#define DMA_FIFOThreshold_1QuarterFull ((uint32_t)0x00000000) #define DMA_FIFOThreshold_HalfFull ((uint32_t)0x00000001) #define DMA_FIFOThreshold_3QuartersFull ((uint32_t)0x00000002) #define DMA_FIFOThreshold_Full ((uint32_t)0x00000003)
DMA_MemoryBurst:
配置存储器突发传输配置。能够选择为 4 个节拍的增量突发传输 ,8 个节拍的增量突发传输 , 16 个街拍的增量突发传输以及单次传输。取值有:
#define DMA_MemoryBurst_Single ((uint32_t)0x00000000) #define DMA_MemoryBurst_INC4 ((uint32_t)0x00800000) #define DMA_MemoryBurst_INC8 ((uint32_t)0x01000000) #define DMA_MemoryBurst_INC16 ((uint32_t)0x01800000)
DMA_PeripheralBurst:
配置外设突发传输配置。跟前面一个参数DMA_MemoryBurst 做用相似,只不过一个针对的是存储器。取值有:
#define DMA_PeripheralBurst_Single ((uint32_t)0x00000000) #define DMA_PeripheralBurst_INC4 ((uint32_t)0x00200000) #define DMA_PeripheralBurst_INC8 ((uint32_t)0x00400000) #define DMA_PeripheralBurst_INC16 ((uint32_t)0x00600000)
开源地址:
https://github.com/strongercjd/STM32F207VCT6
点击查看本文所在的专辑,STM32F207教程