STM32的守时器常识适当杂乱,这儿罗列一些基础常识,在之后的文章我会写一下它的各种运用。
通用守时器是一个经过可编程预分频器驱动的16位主动装载计数器构成。它适用于多种场合,包含守时中止、丈量输入信号的脉冲长度(输入捕获)或许发生输出波形(输出比较和PWM)。每个守时器都是彻底独立的,没有相互同享任何资源,它们能够一同同步操作。
STM32F103RC系列有4个通用守时器,2个高档守时器和两个根本守时器:
咱们最常运用通用守时器,功用包含:
1、16位向上 、向下、向上/向下主动装载计数器(TIMx_CNT),例如向上是指:计数器从0计数到主动加载值(TIMx_ARR),然后从头从0开端计数而且发生一个计数器溢出事情。
2、16 位可编程(能够实时修正)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的恣意数值。
3、4 个独立通道(TIMx_CH1~4)。
4、支撑多种中止,如溢出中止等。
这儿咱们叙述运用通用守时器中止试验:
一、守时器时钟挑选
咱们常运用内部时钟(CK_INT),经过装备TIMx_SMCR的SMS[2:0],装备为000。该时钟是ABP1时钟的的1倍(APB1不分频)或2倍(APB1分频)。
二、计数器方法
可挑选向上 、向下或许向上/向下。
三、守时时刻核算
溢出时刻 = ( 主动加载值(ARR)+ 1 )( 预分频系数(PSC)+ 1 ) / 守时器时钟(Tclk)
四、库函数装备
1、使能能守时器时钟。
2、初始化守时器,装备ARR,PSC(在stm32f10x_tim.c)
TIM_TimeBaseInit();
其间的结构体:typedef struct
{
uint16_t TIM_Prescaler; //设置分频系数
uint16_t TIM_CounterMode; //计数方法
uint16_t TIM_Period; //主动重装载值
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
3、敞开守时器中止,装备中止优先级分组NVIC。
void TIM_ITConfig();
NVIC_Init();
4、 使能守时器。
TIM_Cmd();
5、编写中止服务函数。
TIMx_IRQHandler();
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_ClearITPendingBit(TIM1, TIM_IT_Update);//铲除更新中止请求位
TIM_Cmd(TIM1, ENABLE);
(5)。。。。。。当即进入更新中止程序。
(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); //失能(函数外使能)
}
}
公然,程序能够正常的时序运转。
比较疑惑关守时器前又得清下标志位,因而引起了另一个好奇心,是不是在其他当地封闭守时器(如主函数),也得这样做才能够。所以对这个好奇心进行了下测验。发现:假如把封闭守时器放到了主函数后,不必再清中止标志位。能正常把守时器封闭,并不会进入中止。
经过这次的问题,浪费了许多时刻处理,不过也汲取到了点经历,但关于内涵真实原因:在中止里失能和中止外失能作用为什么不一样,暂时还没搞清楚。。。但这个能够作为今后的前车之鉴,以及我们的前车之鉴,少走弯路。