有人使用STM32的定时器的输出比较功能,具体就是输出4个通道的PWM信号。不过 他需要不定时地调整4个通道的占空比,即调整他们的CCR值。但现在有个小问题,那就是新的CCR值的获得往往会能跨越多个目前定时器的计数周期,这样的话,即使开启各个通道CCR值的预装功能,似乎也很保证做到一次性修改。因为它希望新的CCR值被同时更新。换句话说,他担心不同通道新的CCR值分散在不同计数周期生效,可能给应用带来些麻烦。
我们知道,STM32定时器的预装寄存器的值到影子寄存器的更新往往离不开更新事件。一般来讲,只要启动了定时器,更新事件会随着计数器的溢出而自然产生。
既然这样,比方若是在下面四个时刻获得了新的CCR值,有没有办法让这几个值同时实施更新,而不是分布在不同时刻生效呢?
其实是可以的。有两种方法可以参考,但有个共同的前提,就是开始做新数据准备时,先把定时器更新事件的产生允许关闭,因为STM32定时器更新事件最终是否产生是可以软件控制的,我们可以等数据都准备好了再允许定时器更新事件的产生。通过对TIMER控制寄存器的UDIS位置1或清零达到关闭或允许更新事件的产生的目的。
在这个前提下,我们可以有两种做法。
第一种,在准备新数据时,先通过对UDIS写1禁止更新事件的产生,同时开启CCR的预装功能,然后就可以悠闲地给各通道的CCR寄存器写新值了,不必担心它会生效,此时我们只是写进了CCR的预装寄存器。等到4个通道的新值都写进各自CCR预装寄存器后,再将更新事件位UDIS清零以允许定时器更新事件产生,数据更新的事就交给后续更新事件来完成,不必担心生效先后问题。
还有一种做法,那就是用户根本不直接对CCR进行改写操作,而是先将4个通道的新数据放在连续的内存里,借助定时器事件的DMABurst传输功能一次性完成修改。
大致流程是,先通过对UDIS写1禁止更新事件产生,然后将4个通道的新的CCR数据按照CCR1、CCR2、CCR3、CCR4的顺序放在内存,配置基于定时器更新事件的DMA Burst传输功能。即一个定时器事件可以触发DMA完成多个定时器寄存器与内存间的一次性传输。
此时,4个通道的CCR寄存器的预装功能可以开启,也可以不开启,但是几个通道的配置要保持一致,不要有的通道开启、有的不开启,否则反而不能保证几个通道参数的同步修改。
针对第2种做法,这里简单示范下实现过程。这里以STM32G4系列的TIM4来设计的。
上图中红线配置提示开启了CCR寄存器的预装功能。前面说了也可以同时都不开启。
使用CubeMx配置后生成HAL库工程代码,添加用户代码。我定义一个4字大小的数组
CCR_Data【4】用来存放新的CCR数据。下面代码实际上是我模拟反复调用的,在别的地方动态修改NewDataOK的值。
if(NewDataOK)
{
//Prohibit Update event ’generation since now
TIM4->CR1|=(TIM_CR1_UDIS);
NewDataOK =0;
CCR_Data[0]=CCR1_Value;
CCR_Data[1]=CCR2_Value;
CCR_Data[2]=CCR3_Value;
CCR_Data[3]=CCR4_Value;
HAL_DMA_Abort(&hdma_tim4_up);
htim4.DMABurstState= HAL_DMA_BURST_STATE_READY;
HAL_TIM_DMABurst_WriteStart(&htim4,TIM_DMABASE_CCR1, TIM_DMA_UPDATE,&CCR_Data[0],TIM_DMABURSTLENGTH_4TRANSFERS);
__HAL_TIM_CLEAR_FLAG(&htim4,TIM_FLAG_UPDATE);
//Update event generation allowed now
TIM4->CR1 &=~(TIM_CR1_UDIS);
}
其中,HAL_TIM_DMABurst_WriteStart()函数是Cube库里现存的,它实现从内存到定时器寄存器的BURST传输。还有一个函数HAL_TIM_DMABurst_ReadStart()实现从定时器寄存器到内存的BURST传输。
下面截图是测试结果,各个通道同步变化:
我给的测试数据4个通道都是一样的,示波器只接了3个通道。
上面重点分享了定时器更新事件的产生可以通过软件关闭或开启的这个特性,我们可以在以后STM32开发应用中灵活运用。 不过,请不要把定时器更新事件是否可以产生跟更新事件源混为一谈。这里讨论的是更新事件产生的允许问题,而更新事件源则讨论的是更新事件怎么来的,它可以因计数器的溢出、工作在复位模式的从定时器收到触发信号或者由软件操作寄存器而产生。