stm32定时器误区

在用到STM32定时器的更新中断时,发现有些情形下只要开启定时器就立即进入一次中断。准确说,只要使能更新中断允许位就立即响应一次更新中断【当然前提是相关NVIC也已经配置好】。换言之,只要使能了相关定时器更新中断,不管你定时间隔多长甚至不在乎你是否启动了相关定时器,它都会立即进入一次定时器更新中断服务程序。
以STM32F051芯片为例,做了几种不同顺序的组合测试。根据测试发现,的确有些情况下一运行TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); 【即使能更新中断】就立即进入更新中断服务程序。当然后面的中断都是正常的。
老实说,这个问题比较容易忽视,有些情况下也无关紧要,但有些情况可能会给应用带来困扰。从ST MCU相关技术手册似乎并不能明显地找到关于这个问题的很合适或者逻辑性很强的前因后果。
经过验证测试,如果注意一下相关指令代码顺序是可以回避这个问题的。
这里写图片描述
先做更新中断标志的清除操作,即清除TIMx->SR寄存器里的UIF标志,然后做定时器更新中断的使能操作。至于开启相关定时器的指令摆放位置并不严格。下面是相关动作的操作顺序及结果,可以参考、验证之。这里共罗列了6种写法,其中有3种情形是会立即进入中断的,另外3种不会。
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //使能定时器1更新中断
TIM_Cmd(TIM1, ENABLE); //启动定时器
(1)。。。。。。不会立即进入更新中断程序。

TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中断请求位
TIM_Cmd(TIM1, ENABLE);
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定时器1更新中断
(2)。。。。。。不会立即进入更新中断程序。

TIM_Cmd(TIM1, ENABLE);
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中断请求位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定时器1更新中断
(3)。。。。。。不会立即进入更新中断程序。

TIM_Cmd(TIM1, ENABLE);
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定时器1更新中断
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中断请求位
   (4)。。。。。。立即进入更新中断程序。

 TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定时器1更新中断

TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中断请求位
TIM_Cmd(TIM1, ENABLE);
(5)。。。。。。立即进入更新中断程序。

 TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定时器1更新中断
 TIM_Cmd(TIM1, ENABLE); 
 TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中断请求位

(6)。。。。。。立即进入更新中断程序。

顺便提下关于定时器里UG位和URS位的使用,分别在TIMx->EGR和TIMx->CR1寄存器里。对UG位置1可以产生更新事件并对相关计数器和寄存器重新初始化,如果URS位为0的话,同时会产生更新中断。如果不希望对UG位置1的同时产生更新中断,得置URS位为1,否则会立即进入更新中断。
这里写图片描述
这里写图片描述
这里写图片描述
另外
我们平时使用定时器的时候多数都是处于开启状态,平时的定时中断书写格式一般是:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{

TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//要处理的事件内容。。。。
}
}

但是,项目的实验过程中,我使用的定时器处理事件稍微有点特殊,即,定时器不是一直处于开启状态, 而且关闭时候也是在中断里关闭。大概形式这样:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{

TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//要处理的事件内容。。。。
TIM_Cmd(TIM3, DISABLE); //失能(函数外使能)
}
}

看似没错,而且也看似正常。但是,处理的事件内容出现了很多未知错误(由于我的这个处理事件有很强的时序性,开始和结束都比较严格),无法正常执行。通过后来的调试中发现(把处理时间改为点灯或者打印输出方式),发现是:TIM_Cmd(TIM3, DISABLE); 扰乱了时序关系。当失能后,其实中断并没有真正失能,还会再进入一次中断,因此事件又被执行了一次,对于时序比较严格的事件,就产生了问题!
找到了原因,因此,我猜测虽然定时器失能并且关闭了定时器,但是可能中断标志位并没真正清除,虽然中断开始已经清除过一次,但估计因为失能使得标志位又被置位了,因此,我在失能前面加了句清除中断更新标志位,如下:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{

TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//要处理的事件内容。。。。
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);//再清除标志位
TIM_Cmd(TIM3, DISABLE); //失能(函数外使能)
}
}

果然,程序可以正常的时序运行。
比较纳闷关定时器前又得清下标志位,因此引起了另一个好奇心,是不是在其他地方关闭定时器(如主函数),也得这样做才可以。所以对这个好奇心进行了下测试。发现:如果把关闭定时器放到了主函数后,不用再清中断标志位。能正常把定时器关闭,并不会进入中断。
通过这次的问题,浪费了很多时间解决,不过也吸取到了点经验,但对于内在真正原因:在中断里失能和中断外失能效果为什么不一样,暂时还没搞清楚。。。但这个可以作为以后的前车之鉴,以及大家的前车之鉴,少走弯路。

猜你喜欢

转载自blog.csdn.net/qq_34888036/article/details/80872284