STM32 —— DMA 发送与接收数据详解
STM32 —— DMA 发送与接收数据详解
简介
DMA(Direct Memory Access) :直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU ,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输
功能详解
功能框图
DMA 控制器基于复杂的总线矩阵架构,将功能强大的双 AHB 主总线架构与独立的 FIFO 结合在一起,优化了系统带宽
功能描述
DMA 控制器和 Cortex™-M3 核心共享系统数据总线,执行直接存储器数据传输。当 CPU 和 DMA 同时访问相同的目标( RAM 或外设)时, DMA 请求会暂停 CPU 访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证 CPU 至少可以得到一半的系统总线(存储器或外设)带宽
DMA 处理
在发生一个事件后,外设向 DMA 控制器发送一个请求信号。 DMA 控制器根据通道的优先权处理请求。当 DMA 控制器开始访问发出请求的外设时, DMA 控制器立即发送给它一个应答信号。当从 DMA 控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求, DMA 控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期
总之,每次 DMA 传送由 3 个操作组成:
· 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是 DMA_CPARx 或 DMA_CMARx 寄存器指定的外设基地址或存储器单元
· 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是 DMA_CPARx 或 DMA_CMARx 寄存器指定的外设基地址或存储器单元
· 执行一次 DMA_CNDTRx 寄存器的递减操作,该寄存器包含未完成的操作数目
简单来说就是先通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器两次寻址,从外设数据寄存器或存储器单元中加载数据,并讲加载的数据存储进去,最后 DMA_SxNDTR 计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数
DMA 通道
DMA 有 DMA1 和 DMA2 两个控制器, DMA1 有 7 个通道, DMA2 有 5 个通道,不同的 DMA 控制器的通道对应着不同的外设请求,虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个
这决定了我们在软件编程上该怎么设置,具体见 DMA 请求映像表:
各个通道的DMA1请求一览:
外设 | 通道1 | 通道2 | 通道3 | 通道4 | 通道5 | 通道6 | 通道7 |
---|---|---|---|---|---|---|---|
ADC1 | ADC1 | ||||||
SPI/I²S | SPI1_RX | SPI1_TX | SPI/I2S2_RX | SPI/I2S2_TX | |||
USART | USART3_TX | USART3_RX | USART1_TX | USART1_RX | USART2_RX | USART2_TX | |
I²C | I2C2_TX | I2C2_RX | I2C1_TX | I2C1_RX | |||
TIM1 | TIM1_CH1 | TIM1_CH2 | TIM1_TX4 TIM1_TRIG TIM1_COM |
TIM1_UP | TIM1_CH3 | ||
TIM2 | TIM2_CH3 | TIM2_UP | TIM2_CH1 | TIM2_CH2 TIM2_CH4 |
|||
TIM3 | TIM3_CH3 | TIM3_CH4 TIM3_UP |
TIM3_CH1 TIM3_TRIG |
||||
TIM4 | TIM4_CH1 | TIM4_CH2 | TIM4_CH3 | TIM4_UP |
各个通道的DMA2请求一览:
外设 | 通道1 | 通道2 | 通道3 | 通道4 | 通道5 |
---|---|---|---|---|---|
ADC3(2); | ADC3 | ||||
SPI/I2S3 | SPI/I2S3_RX | SPI/I2S3_TX | |||
UART4 | UART4_RX | UART4_TX | |||
SDIO(1) | SDIO | ||||
TIM5 | TIM5_CH4 TIM5_TRIG |
TIM5_CH3 TIM5_UP |
TIM5_CH2 | TIM5_CH1 | |
TIM6/DAC通道1 | TIM6_UP/DAC通道1 | ||||
TIM7/DAC通道2 | TIM7_UP/DAC通道2 | ||||
TIM8(1) | TIM8_CH3 TIM8_UP |
TIM8_CH4 TIM8_TRIG TIM8_COM |
TIM8_CH2 | TIM8_CH2 |
1. ADC3、 SDIO和TIM8的DMA请求只在大容量的产品中存在。
*注意: *ADC2 没有 DMA 功能,其中 ADC3、 SDIO 和 TIM8 的 DMA 请求只在大容量产品中存在,这个在具体项目时要注意
可编程的数据量
每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行 DMA 传输。 DMA 传输的数据量是可编程的,最大达到 65535 ,包含要传输的数据项数量的寄存器,在每次传输后递减
外设和存储器的传输数据量可以通过 DMA_CCRx 寄存器中的 PSIZE 和 MSIZE 位编程
可编程的数据传输宽度、对齐方式和数据大小端:当PSIZE和MSIZE不相同时, DMA模块按照下表进行数据对齐
可编程的数据传输宽度和大小端操作(当PINC = MINC = 1):
指针增量
通过设置 DMA_CCRx 寄存器中的 PINC 和 MINC 标志位,外设和存储器的指针在每次传输后可以有选择地完成自动增量
当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值,增量值取决与所选的数据宽度为 1、 2 或 4 。第一个传输的地址是存放在 DMA_CPARx/DMA_CMARx 寄存器中地址。在传输过程中,这些寄存器保持它们初始的数值,软件不能改变
和读出当前正在传输的地址(它在内部的当前外设/存储器地址寄存器中)
当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。要开始新的 DMA 传输,需要在关闭 DMA 通道的情况下,在 DMA_CNDTRx 寄存器中重新写入传输数目。在循环模式下,最后一次传输结束时,DMA_CNDTRx 寄存器的内容会自动地被重新加载为其初始数值,内部的当前外设/存储器地址寄存器也被重新加载为 DMA_CPARx/DMA_CMARx 寄存器设定的初始基地址
通道配置过程
下面是配置DMA通道x的过程(x代表通道号):
-
在 DMA_CPARx 寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标
-
在 DMA_CMARx 寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址
-
在 DMA_CNDTRx 寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减
-
在 DMA_CCRx 寄存器的 PL[1:0] 位中设置通道的优先级
-
在 DMA_CCRx 寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断
-
设置 DMA_CCRx 寄存器的 ENABLE 位,启动该通道。
一旦启动了 DMA 通道,它既可响应连到该通道上的外设的 DMA 请求。
当传输一半的数据后,半传输标志(HTIF)被置 1,当设置了允许半传输中断位(HTIE)时,将产生一个中断请求。在数据传输结束后,传输完成标志(TCIF)被置 1,当设置了允许传输完成中断位(TCIE)时,将产生一个中断请求
循环模式
循环模式用于处理循环缓冲区和连续的数据传输(如 ADC 的扫描模式)。在 DMA_CCRx 寄存器中的 CIRC 位用于开启这一功能。当启动了循环模式,数据传输的数目变为 0 时,将会自动地被恢复成配置通道时设置的初值,DMA 操作将会继续进行
存储器到存储器模式
DMA 通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。当设置了 DMA_CCRx 寄存器中的 MEM2MEM 位之后,在软件设置了 DMA_CCRx 寄存器中的 EN 位启动 DMA 通道时,DMA 传输将马上开始。当 DMA_CNDTRx 寄存器变为 0 时,DMA 传输结束。存储器到存储器模式不能与循环模式同时使用
仲裁器
仲裁器根据通道请求的优先级来启动外设/存储器的访问
当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器管理
优先权管理分2个阶段:
- 软件:每个通道的优先权可以在 DMA_CCRx 寄存器中设置,有4个等级:
· 最高优先级
· 高优先级
· 中等优先级
· 低优先级
2. 硬件:如果 2 个或以上请求有相同的软件优先级(即 DMA 通道请求设置的优先级一样),则他们优先级取决于通道编号,较低编号的通道比较高编号的通道有较高的优先权。举个例子,通道2优先于通道4
注意:在大容量产品和互联型产品中, DMA1控制器拥有高于DMA2控制器的优先级
DMA 请求映像
DMA1
从外设(TIMx[x=1、 2、 3、 4]、 ADC1、 SPI1、 SPI/I2S2、 I2Cx[x=1、 2]和USARTx[x=1、 2、 3])产生的 7 个请求,通过逻辑或输入到 DMA1 控制器,这意味着同时只能有一个请求有效
外设的 DMA 请求,可以通过设置相应外设寄存器中的 DMA 控制位,被独立地开启或关闭
具体细节看下面的 DMA1 请求影响图解:
DMA2
从外设(TIMx[5、 6、 7、 8]、 ADC3、 SPI/I2S3、 UART4、 DAC通道1、 2和SDIO)产生的 5 个请求,经逻辑或输入到 DMA2 控制器,这意味着同时只能有一个请求有效
外设的 DMA 请求,可以通过设置相应外设寄存器中的 DMA 控制位,被独立地开启或关闭
注意:DMA2控制器及相关请求仅存在于大容量产品和互联型产品
具体细节看下面的 DMA2 请求影响图解:
DMA 寄存器
寄存器地址映像如下:
注意:在以下列举的所有寄存器中,所有与通道 6 和通道 7 相关的位,对 DMA2 都不适用,因为 DMA2 只有 5 个通道
中断状态寄存器 DMA_ISR
中断标志清除寄存器 DMA_IFCR
通道 x 配置寄存器 DMA_CCRx (x = 1…7)
通道 x 传输数量寄存器 DMA_CNDTRx (x = 1…7)
通道 x 外设地址寄存器 DMA_CPARx (x = 1…7)
通道 x 存储器地址寄存器 DMA_CMARx (x = 1…7)
数据配置
使用 DMA,最核心就是配置要传输的数据,包括数据从哪里来,要到哪里去,传输的数据的单位是什么,要传多少数据,是一次传输还是循环传输等等
从哪里来到哪里去
我们知道 DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。具体的方向 DMA_CCR 位 4 DIR 配置: 0 表示从外设到存储器, 1 表示从存储器到外设。这里面涉及到的外设地址由 DMA_CPAR 配置,存储器地址由 DMA_CMAR 配置
外设到寄存器
当我们使用从外设到存储器传输时,以 ADC 采集为例
DMA 外设寄存器的地址对应的就是 ADC 数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址
存储器到外设
当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例
DMA 外设寄存器的地址对应的就是串口数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址
存储器到存储器
当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例
DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址
跟上面两个不一样的是,这里需要把 DMA_CCR 位 14: MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式
要传多少,单位是什么
当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多少,数据的单位是什么
以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由 DMA_CNDTR 配置,这是一个 32 位的寄存器,前16位为数据传输数量,一次最多只能传输 65535 个数据
要想数据传输正确,源和目标地址存储的数据宽度还必须一致,串口数据寄存器是 8 位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由 DMA_CCR 的 PSIZE[1:0] 配置,可以是 8/16/32 位,存储器的数据宽度由 DMA_CCR 的 MSIZE[1:0] 配置,可以是 8/16/32 位
在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。外设的地址指针由 DMA_CCRx 的 PINC 配置,存储器的地址指针由 MINC 配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。具体的数据指针的增量模式由实际情况决定
什么时候传输完成
数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个 DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。有关各个标志位的详细描述请参考 DMA 中断状态寄存器 DMA_ISR 的详细描述
传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由 DMA_CCR 寄存器的 CIRC 循环模式位控制
传输参数
我们知道,数据传输,首先需要的是1 数据的源地址 2 数据传输位置的目标地址 ,3 传递数据多少的数据传输量 ,4 进行多少次传输的传输模式 DMA所需要的核心参数,便是这四个
当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输
也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输