定时器介绍
软件定时
还记得以前在开发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= ((arr+1)*(psc+1))/Tclk公式理解汇总-CSDN博客
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连续不断的翻转状态,而不是翻转一次就结束)
STM32CubeMX----基本定时器(TIM6、TIM7)_counter period (autoreload register - 16 bits valu-CSDN博客
进行如下设置:(目前可以只关心 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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);
#else
HAL_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);