定时器介绍
软件定时
还记得以前在开发C51的时候,经常使用stc助手生成的定时代码,形如:
void Delay500ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 4;j = 129;k = 119;do{do{while (--k);} while (--j);} while (--i);
}
定时器分类
基本定时器(TIM6,TIM7)没有输入输出通道,常用作时基,即定时功能
通用定时器(TIM2~TIM5)具有多路独立通道,可用于输入捕获/输出比较,也可用作时基
高级定时器(TIM1,TIM8)除具备通用定时器所有功能外,还具备带死区控制的互补信号输出刹车输入等功能(可用于电机控制、数字电源设计等)
可见,和51/52相比,stm32的定时器复杂得多
定时器计数模式
定时器时钟源
下图是手册P56,主要关注红圈部分
根据上图的原理,可以推出下面的公式,十分重要!!!
Tout = 设定时间 (单位秒s)
Tclk = 通过预分频后输出的TIMxCLK(上图右侧红线)
PSC = 预分频系数 (+1是因为计算机是从0开始的)
ARR = 自动重装载值(+1是因为计算机是从0开始的)
例如:要定时500ms,则可以在配置定时器时使用: PSC = 7199,ARR = 4999,TClk = 72M(72 000 000)(有多种组合)
((7199+1)/72000000)/(4999+1)=0.5s
使用定时器中断实现LED灯的状态反转
打开CubeMX,先进行惯例配置
配置时钟(上图)最右侧一列的“APB1 Timer clocks”和“APB2 Timer clocks”就是刚刚提到的计算公式中的Tclk,单位是MHz,此处就是Tclk = 72MHz,具体详情查看上图的时钟原理图
配置PB8(LED1)为GPIO_out模式且初始值为HIGH
Timer 配置
1. 在左侧选择Timers选项,此处选择通用定时器Timer2(Timer1是高级定时器,故不选用)
第一个参数就是预分频器,第三个参数就是ARR:
回顾刚刚的计算公式:
要定时500ms,则可以在配置定时器时使用: PSC = 7199,ARR = 4999,TClk = 72M(72 000 000)(可选其他多种组合)
同时打开第五个参数的自动重载!(因为希望LED连续不断的翻转状态,而不是翻转一次就结束)
进行如下设置:(目前可以只关心 Clock Source),在设置为Internal Clock后,下方会自动弹出NVIC界面,选择打开中断。
打开Keil
此时自动弹出Keil工程,记得先编译一下,养成良好编程习惯!
通过 stm32f1xx_it.c --> TIM2_IRQHandler() --> HAL_TIM_IRQHandler() --> 然后就和之前不大一样了,之前进行到这里就会出现一个 weak 类型的可重写中断处理函数,但此时是一个巨长无比的函数:
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{/* Capture compare 1 event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET){{__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;/* Input capture event */if ((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U){
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->IC_CaptureCallback(htim);
#elseHAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}/* Output compare event */else{
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->OC_DelayElapsedCallback(htim);htim->PWM_PulseFinishedCallback(htim);
#elseHAL_TIM_OC_DelayElapsedCallback(htim);HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;}}}/* Capture compare 2 event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;/* Input capture event */if ((htim->Instance->CCMR1 & TIM_CCMR1_CC2S) != 0x00U){
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->IC_CaptureCallback(htim);
#elseHAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}/* Output compare event */else{
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->OC_DelayElapsedCallback(htim);htim->PWM_PulseFinishedCallback(htim);
#elseHAL_TIM_OC_DelayElapsedCallback(htim);HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;}}/* Capture compare 3 event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3);htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;/* Input capture event */if ((htim->Instance->CCMR2 & TIM_CCMR2_CC3S) != 0x00U){
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->IC_CaptureCallback(htim);
#elseHAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}/* Output compare event */else{
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->OC_DelayElapsedCallback(htim);htim->PWM_PulseFinishedCallback(htim);
#elseHAL_TIM_OC_DelayElapsedCallback(htim);HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;}}/* Capture compare 4 event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4);htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;/* Input capture event */if ((htim->Instance->CCMR2 & TIM_CCMR2_CC4S) != 0x00U){
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->IC_CaptureCallback(htim);
#elseHAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}/* Output compare event */else{
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->OC_DelayElapsedCallback(htim);htim->PWM_PulseFinishedCallback(htim);
#elseHAL_TIM_OC_DelayElapsedCallback(htim);HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;}}/* TIM Update event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->PeriodElapsedCallback(htim);
#elseHAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}}/* TIM Break input event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_BREAK);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->BreakCallback(htim);
#elseHAL_TIMEx_BreakCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}}/* TIM Trigger detection event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_TRIGGER) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->TriggerCallback(htim);
#elseHAL_TIM_TriggerCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}}/* TIM commutation event */if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_COM) != RESET){if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_COM) != RESET){__HAL_TIM_CLEAR_IT(htim, TIM_FLAG_COM);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)htim->CommutationCallback(htim);
#elseHAL_TIMEx_CommutCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}}
}
但是,我们现在只关注:如果定时时间到了之后,定时器会做什么的函数
因此,可以找到这样一个函数:HAL_TIM_PeriodElapsedCallback(htim);
对于这个函数进行跳转,终于出现了可以重写的中断处理函数:
然后在main.c中重写这个函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){ //注意,htim是一个结构体指针,所以对于结构体中成员变量的访问要用“->”HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);}
}
经查找,要在main函数中添加一句,启动定时器:(当然,也是在生成的初始化之后)
HAL_TIM_Base_Start_IT(&htim2);