【DMA】DMA入门:外设数据到内存,以串口DMA接收为例,解析底层实现
目录
DMA实战操作(DMA搬运外设数据到内存)
串口相关功能
串口DMA配置
代码编写与底层实现
HAL库代码复用小结
DMA实战操作(DMA搬运外设数据到内存)
下文以简单的使用DMA从串口接收数据到内部SRAM,Polling|中断方法 讲解
串口相关功能
串口功能框图:
配置好串口DMA后,如果串口接收到数据,就会发送一个 DMA_request ,通知DMA取走RDR寄存器中的数据(一个字节)。
串口接收过程中有关DMA的寄存器:
串口DMA配置
代码编写与底层实现
编写代码使用串口DMA接收20个字节:
用很简单,但是我们现在关注的是他的底层实现。
DMA不论和谁配合,都是搬运数据,并且给你扔 3 个回调出来:搬运一半回调(半满回调)、搬运完成回调(全满回调)、error回调
上一个实验我们还需要注册回调,但是这里运行了一句话好像就已经什么都帮我们完成了?
进入 HAL_UART_Receive_DMA() 函数,它实际调用的是 UART_Start_Receive_DMA():
我们进入 UART_Start_Receive_DMA() 函数,可以发现这个函数就在注册回调函数:
也就是说,它会把 UART_DMAReceiveCplt() 函数挂载到 DMA 传输完成回调函数上,当DMA传输完成,会自动调用 UART_DMAReceiveCplt() 。
但是 UART_DMAReceiveCplt() 就是串口传输完成回调函数吗?其实不是的,串口传输完成回调函数是 HAL_UART_RxCpltCallback() 。我们将 UART_DMAReceiveCplt() 函数往下翻,会发现它其实最终也是调用 HAL_UART_RxCpltCallback() 串口传输完成回调函数:
在注册完回调函数后,UART_Start_Receive_DMA() 就会开始启动 DMA 搬运数据:
还有一个比较重要的是 DMAR 寄存器,只有这个寄存器被置位为一,UART 才能启用DMA模式接收。
(该语句同样在UART_Start_Receive_DMA()函数体内)
HAL库代码复用小结
通过上面的代码截图,我们可以知道挂载在DMA全满回调上的函数最终调用的就是串口传输完成回调,而DMA错误回调也是,最终会调用串口传输错误回调,相当于所有错误信息在串口传输错误回调函数中汇总了。
这就是HAL库架构与代码复用的冰山一角。