DMA配置和使用如此简单(超详细)
DMA传输
看过我这篇博文《 STM32 | 基于NRF24L01串口透传(不定长数据无线串口双向传输) 》的应该就清楚我为什么要写DMA传输,最终目的就是为了让nRF24L01采用串口DMA+SPI DMA方式实现串口的透传,所以本文来讲讲DMA传输。还是老样子,我认真写好本文,希望更多人了解并且会用并且了解DMA传输,希望大家多多支持,可以参与评论或点个赞,谢谢! 那么就进入正题!
测试平台:STM32F103C8T6
库版本:官方库3.5版本
关键词:DMA
一、查询和中断不爽吗?为什么要使用串口DMA?(借串口DMA引入)
如果真的是刚刚接触单片机或嵌入式,肯定会问,因为按照难易程度:DMA>中断>查询。这里我提几个反问句回答。
- 查询方式是不是要在一个循环里反复执行判断?如果串口传输数据频率快于循环频率,请问能及时收到数据吗?
- 中断过于频繁主程序还要不要运行?如果我只接收数据,但不用接收一个字节数据就处理一次,那么请问,有必要频繁中断吗?
- 数据传输过程需不需要时间?需要的话,那我们是不是还要等待?
其实我们要求的很简单,就是高效,传输数据的时候我们不在等待的时间浪费CPU资源,而且数据是一个字节一个字节传送的,接收的时候只要一个数据包最后一个字节数据接收到再处理即可,发送的时候让串口自己一个字节一个字节把数据发出去即可,不用在等待一个字节发送完再发下一字节数据这样。为了提高CPU使用效率,于是就使用DMA方式。
二、DMA介绍
学习只有积跬步,才能至千里。可能废话有点多,当然这是写给还不知道DMA的同学看的。已经了解的,只想学怎么配置的可以直接跳转到后面的内容。
1、什么是DMA?
不太官方的理解:DMA只是个搬运工,帮助老板(CPU)搬运东西(数据),可以帮老板(CPU)把东西(数据)从家搬运到家门外(存储器→外设);可以帮老板(CPU)把到东西(数据)从家门外搬运到家里(外设→存储器);还可以帮老板(CPU)把东西(数据)从家里卧室1搬到卧室2(存储器→存储器)。
官方一点的表达:DMA,全称为:Direct Memory Access,即直接存储器访问。直接存储器存取( DMA )用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须 CPU 干预,数据可以通过 DMA 快速地移动,这就节省了 CPU 的资源来做其他操作。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能嵌入式系统算法和网络是很重要的。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
2、STM32上的DMA资源
STM32 最多有 2 个 DMA 控制器( DMA2 仅存在大容量产品中),12个独立的可配置的通道(请求), DMA1 有 7 个通道。DMA2 有 5 个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。
小容量产品是指闪存存储器容量在16K至32K字节之间的微控制器。
中容量产品是指闪存存储器容量在64K至128K字节之间的微控制器。
大容量产品是指闪存存储器容量在256K至512K字节之间的微控制器。
互联型产品是指STM32F105xx和STM32F107xx微控制器。
3、DMA主要特征
- 每个通道都直接连接专用的硬件 DMA 请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
- 在同一个 DMA 模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求 0 优先于请求 1 ,依此类推,可以参考STM32数据手册)。
- 独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
- 支持循环的缓冲器管理(会把原来的数据覆盖)。
- 每个通道都有 3 个事件标志(DMA 半传输, DMA 传输完成和 DMA 传输出错),这 3 个事件标志逻辑或成为一个单独的中断请求。
- 存储器和存储器间的传输(仅 DMA2 可以)。
- 外设和存储器、存储器和外设之间的传输。
- 闪存、SRAM 、外设的 SRAM 、APB1 、APB2 和 AHB 外设均可作为访问的源和目标。
- 可编程的数据传输数目:最大为65535(216-1)。
4、DMA请求映像
后续要以串口2的接收举例,由以下内容可以知道USART2_RX请求在DMA1通道6。
(1)DMA1控制器
从外设(TIMx[x=1、2 、3、4] 、ADC1 、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3])产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。参见下图的DMA1请求映像。
外设的DMA请求,可以通过设置相应外设寄存器中的控制位,被独立地开启或关闭。
DMA1 请求映像
各个通道的DMA1请求一览
(1)DMA2控制器
从外设(TIMx[5、6、7、8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO)产生的5个请求,经逻辑或输入到DMA2控制器,这意味着同时只能有一个请求有效。参见下图的DMA2请求映像。
外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭。
注意: DMA2控制器及相关请求仅存在于大容量产品和互联型产品中。
DMA2 请求映像
各个通道的DMA2请求一览
注意:ADC3、SDIO和TIM8的DMA请求只在大容量的产品中存在。
5、DMA寄存器介绍
DMA寄存器的介绍主要参照参考手册,这里列出来方便大家阅读,使用DMA的时候可以使用库函数,方便阅读;也可直接配置寄存器,方便使用(快捷)。
注意: 在以下列举的所有寄存器中,所有与通道6和通道7相关的位,对DMA2都不适用,因为DMA2只 有5个通道。
(1)DMA中断状态寄存器(DMA_ISR)
数据位 |
|
---|---|
位31:28 | 保留,始终读为0。 |
位27,23,19,15,11,7,3 | TEIFx:通道x的传输错误标志(x = 1 … 7) (Channel x transfer error flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’可以清除这里对应的标志位。 0:在通道x没有传输错误(TE); 1:在通道x发生了传输错误(TE)。 |
位26,22,18,14,10,6,2 | HTIFx:通道x的半传输标志(x = 1 … 7) (Channel x half transfer flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’可以清除这里对应的标志位。 0:在通道x没有半传输事件(HT); 1:在通道x产生了半传输事件(HT)。 |
位25,21,17,13, 9,5,1 | TCIFx:通道x的传输完成标志(x = 1 … 7) (Channel x transfer complete flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’可以清除这里对应的标志位。 0:在通道x没有传输完成事件(TC); 1:在通道x产生了传输完成事件(TC)。 |
位24,20,16,12, 8,4,0 | GIFx:通道x的全局中断标志(x = 1 … 7) (Channel x global interrupt flag) 硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’可以清除这里对应的标志位。 0:在通道x没有TE、HT或TC事件; 1:在通道x产生了TE、HT或TC事件。 |
(2)DMA中断标志清除寄存器(DMA_IFCR)
数据位 |
|
---|---|
位31:28 | 保留,始终读为0。 |
位27,23,19,15,11,7,3 | CTEIFx:清除通道x的传输错误标志(x = 1 … 7) (Channel x transfer error clear) 这些位由软件设置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的对应TEIF标志。 |
位26,22,18,14,10,6,2 | CHTIFx:清除通道x的半传输标志(x = 1 … 7) (Channel x half transfer clear) 这些位由软件设置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的对应HTIF标志。 |
位25,21,17,13,9,5,1 | CTCIFx:清除通道x的传输完成标志(x = 1 … 7) (Channel x transfer complete clear) 这些位由软件设置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的对应TCIF标志。 |
位24,20,16,12,8,4,0 | CGIFx:清除通道x的全局中断标志(x = 1 … 7) (Channel x global interrupt clear) 这些位由软件设置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的对应的GIF、TEIF、HTIF和TCIF标志。 |
(3)DMA通道x配置寄存器(DMA_CCRx)(x = 1…7)
数据位 |
|
---|---|
位31:15 | 保 |