查看原文
其他

STM32定时器DMA BURST传输解读

MilerShao 茶话MCU 2022-09-11


经常有人问起关于STM32定时器DMA BURST传输的话题,加上刚好最近有人问起关于STM32F303芯片的定时器BURST传输,这里就稍作整理一起聊聊。

 

DMA传输一般分为单次传输与突发传输。

所谓DMA突发传输,就是指每次的DMA触发事件会导致DMA连续传输指定个数的数据。我们常把每次的连续多个DMA数据传输,称为几拍。类似于音乐里的节拍【beat】,每节分为几拍,比如4拍、8拍等。DMA控制器会根据是单次传输还是突发传输模式来决定每次触发事件产生时传输的数据个数。假设配置DMA BURST模式,BURST size=8,则每来一个DMA触发事件,则实现8个数据的连续传输。【注意是数据个数,具体数据类型视具体定义】

 

这里要聊的定时器DMA BURST传输就是DMA BURST传输的一个具体应用。通过利用TIMER DMA BURST传输实现对定时器多个寄存器的编程访问而节省CPU开销。

定时器的DMA BURST传输中的外设端显然为定时器,其地址固定指向一个专用的虚拟寄存器TIMx_DMAR,即TIMx_DMAR寄存器的地址为DMA传输的外设地址。

当发生某定时器事件时,定时器会发起一串DMA请求序列,即BURST请求。每次访问TIMx_DMAR虚拟寄存器实际上是访问的经过重定向了的某定时器寄存器。其中有两个与定时器BURST传输有关的寄存器TIMx_DCRTIMx_DMAR,一起来看下。


其中DBL[4:0] 设置每次BURST传输的长度,即每次触发事件后DMA传输的数据个数。也就是上面说的节拍数。

DBA[4:0]设置DMA基地址,具体是指DMA burst传输要访问的某定时器的一串连续寄存器的首个寄存器相对于TIMx_CR1寄存器的地址位置偏移量。比如:【细节须阅读STM32相关参考手册定时器描述部分】

00000:TIMx_CR1,

00001:TIMx_CR2,

00010:TIMx_SMCR,

。。。。。。


假设DMA BURST传输要访问的是TIMx_ARRTIMx_RCRTIMx_CCR1 三个连续的寄存器,DBA则是根据TIMx_ARR距离TIMX_CR1的地址位置偏移量来计算,经查看参考手册,它为十进制数11.


 

DBL的值便是每次的突发传输个数减1,这里为2,即00010B.


结合上面的内容我们继续看看TIMx_DMAR寄存器的相关描述。


从上面寄存器描述可以得知DMATIMx_DMAR的访问的实际地址由下面算式确定:(TIMx_CR1的地址)+DBA+DMA index)x 4

其中DMA index DMA控制器自动控制,取值从0DBL. 因为在计算DBADBL时只是按寄存器存储空间顺序和传输个数进行自然计数,换算到4字节寄存器地址时还得乘以4.

注意别把TIMx_DMAR自身的地址和实际访问的寄存器地址弄混了。初次接触时比较容易出错,或者我们可以把TIMx_DMAR看成一个索引寄存器。

STM32F303的芯片的TIM1为例,TIM1_DMAR自身的物理地址为0x40012C4CDMA通过访问它而实际访问的地址由上面的算式决定,相关参数由TIM1_DCR的内容确定。

 

关于定时器的DMA BURST传输,在STM32的标准固件库有相关的例程。这里不妨以STM32F3的例程大致解读下关键代码。

固件库解压后在相关目录可以看到子目录\TIM_DMABurst\


这里有TIM BURST DMA的例程,COPYTEMPLATE目录下即可运行。

例程里每次TIM1更新事件DMA传输3个数据到TIM1_ARR, TIM1_RCR, TIM1_CCR1. 它的DMA mode配置为normal。相关代码如下:


#define TIM1_DMAR_ADDRESS  ((uint32_t)0x40012C4C) /* DMARaddress */

uint16_tSRC_Buffer[3] = {0x0FFF, 0x0000, 0x0555};

DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)TIM1_DMAR_ADDRESS;//设置DMA传输的目的地址

DMA_InitStructure.DMA_MemoryBaseAddr =(uint32_t)SRC_Buffer;//设置DMA传输的源地址

 DMA_InitStructure.DMA_BufferSize = 3;//设置DMA传输的数据个数

 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

  。。。。。。

TIM_DMAConfig(TIM1, TIM_DMABase_ARR,TIM_DMABurstLength_3Transfers);//配置TIMx_DCR寄存器

上面蓝色语句代码对TIM1_DCR进行配置,确定DMA BURST传输的长度、待访问的起始寄存器。


为了观察效果,可以对上面代码稍作改动调整。比如修改RCRCCR1的值,传输模式从NORMAL改为CIRCULAR.比如:

SRC_Buffer[9]= {0x0FFF, 0x3, 0x0222,0x0FFF, 0x4, 0x0555,0x0FFF, 0x5,0x0AAA};

DMA_InitStructure.DMA_BufferSize= 9;

DMA_InitStructure.DMA_Mode= DMA_Mode_Circular;


这样改的话,可以看到先有4个同一占空比的脉冲,然后5个另外占空比的脉冲最后6个又不一样的占空比的脉冲。就这样循环下去。


当你搞清了原理后,你可以根据你实际需求做出不同的应用。比方控制多个通道的占空比,控制指定个数的脉冲输出等等。顺便提醒下,各个定时器触发DMA的事件可能不一样,比方更新事件、通道捕捉事件,相应的DMA通道可能也不一样。所以不同的STM32系列间或不同的定时器间移植时要注意。


关于STM32定时器的常规功能应用,官方有个应用笔记AN4776值得阅读,可以点击下方“阅读原文”下载。


============================

往期链接:

1、关于读取STM32 RTC 日历值不更新的话题

2、利用STM32定时器输出指定脉冲个数的一种方法

3、一个关于STM32定时器的CCR清零话题

4、一个关于SD卡读写超时的话题

5、聊聊STM32芯片的DFU编程及相关话题


扫描或长按二维码可关注公众号

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存