目的

Timer是单片机中非常常见的一种外设组件,可以实现很多常用的功能,这篇文章就将对STM32中Timer的基础内容做个说明。

Timer基础说明

单片机中的Timer很多时候完整的表达是 定时/计数器 ,某种意义上来说Timer最基本的功能就仅仅只是计数而已,定时这个功能也只是在计数这个基础功能之上实现的。

下图是定时器基本功能的流程:
在这里插入图片描述
上图中最核心的部分就是计数器,计数器一端是信号的输出,另一端是计数值满足一定条件后的输入,围绕这这个我们就可以实现很多功能。比如从输入端来说我们可以用来统计外部信号的频率脉宽等;从输出端来说我们可以用来触发计数溢出中断、ADC采样、DAC输出、DMA传输等。在计数过程中放置一个中间值进行比较我们就可以以此来输出PWM信号。

Timer作为定时使用时信号来源通常使用内部时钟,这里的内部时钟指的是APB1、APB2这些。

下图是STM32F405/407 Datasheet 2.2节Device overview中的block diagram
在这里插入图片描述
从这个图中可以看到该系列芯片的Timer分别使用了APB1和APB2时钟

确定了时钟来源我们接着使用定时功能主要处理下面几项参数:

  • Prescaler 预分频系数
    输入给计数器的信号频率 = 输入到预分频器的信号频率 / (预分频系数 + 1)
    该值为0相当于对输入信号1分频,也就是不分频;该值为1相当于对输入信号2分频,依此类推;
    在STM32系列中该值常见取值范围为0~65535;
  • CounterMode 计数模式
    计数模式常见的就是 Up(向上计数模式),这个模式下计数器初始值为0,计数到下面的 Period+1 算作一个周期;
    其它可选值 Down(和UP反一反),Up/down(第一个周期是UP、第二个周期是Down,反复进行);
  • Period 计数周期
    以 Up模式 为例,在此模式下计数器从0开始计数,每一个信号计数值+1, 当计数Period次之后计数器值为Period,当再有一个信号进入后就算计数满一个周期 ,可以触发溢出中断或是其它动作;
    在STM32系列中该值常见取值范围为0~65535;
  • AutoReloadPreload 预装载
    计数器再计满一个周期之后会自动重新计数,也就是默认会连续运行。这连续运行过程中如果你修改了Period,那么根据当前状态的不同有可能发生超出预料的过程。如果使能了AutoReloadPreload,那么你对Period的修改将会在完成当前计数周期后才更新;

Timer作为定时使用时信号来源通常使用内部时钟,当我们确定Timer的时钟信号频率后根据此设定实现定时某一时间周期所需要的参数了,这里主要涉及Prescaler和Period两个参数。Prescaler是对输入定时器的时钟信号进行分频,Period为一个周期中的计数值。

根据上面的内容Timer每计数一次的时间为 1秒 ÷ (时钟频率 ÷ (Prescaler + 1)) ,定时器计数满一个周期的时间为 计数一次时间 × (Period + 1) 秒 。所以定时时间计算公式如下:
定时时间 = (Prescaler + 1) × (Period + 1) ÷ 时钟频率 单位:秒

STM32单片机中有很多个Timer,通常TIM6和TIM7称为基础定时器、TIM1和TIM8称为高级定时器、其余的被称为通用定时器。基础定时器基本上只有定时功能;通用定时器在定时基础上还支持外部输入捕获、比较、PWM输出等功能;高级定时器在通用定时器的基础上只要增加了用于电机控制等功能。

定时功能使用

Timer作为定时功能使用时通常用于在定时时间到的时候触发中断、触发ADC转换、触发数据传输等功能,这里以触发中断作为演示
下面图片看不清的话可以点击查看大图
在这里插入图片描述
上面演示中我使用了TIM7,启用了它的溢出中断,中断函数中翻转了PA2管脚的输出电平,最后使用示波器抓取该针脚电平作为演示。

根据上一章节的图表可知TIM7是在APB1总线上的,在上图中可以看到我的APB1给Timer的时钟频率为84MHz,这时如果设定Prescaler为83,即对时钟84分频,相当于给计数器的时钟为1MHz,每次计数时间为1us。上图中我设定Prescaler为8399,即对时钟8400分频,相当于给计数器的时钟为10KHz,每次计数时间为100us。

上图中我设定Period为9999,即每个计数周期为10000次计数,再根据时钟和Prescaler的设定,最终上图中一个计数周期为1秒中,中断为1秒触发一次。

在使用STM32CubeIDE配置TIM生成代码后,我们还需要手动添加中断回调处理:

void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef *htim)
{
	if(htim == &htim7) // 判断触发溢出中断的定时器
	{
		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
	}
}

有了中断回调处理后我们启用TIM只要使用下面方法即可:

HAL_TIM_Base_Start_IT(&htim7);

有时候定时器启用后会立即进入中断,可以在启用TIM前使用下面方法清除中断:

// __HAL_TIM_CLEAR_FLAG(__HANDLE__, TIM_FLAG_UPDATE)
__HAL_TIM_CLEAR_IT(__HANDLE__, TIM_IT_UPDATE)

如果想要停止定时器运行可以用下面方法:

HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim)

如果想要在程序中动态修改Period可以使用下面方面:

__HAL_TIM_SET_AUTORELOAD(__HANDLE__, __AUTORELOAD__)

如果要在定时器已经启用的情况下修改Period的话建议使能AutoReloadPreload。下面是一个修改Period的演示:
在这里插入图片描述


对于基础定时器、通用定时器、高级定时器而言因为三者功能不同所以在使用定时功能时配置界面选项也有所差异。下面是通用定时器TIM4的演示:

在这里插入图片描述
上面演示中最大的改动就是TIM4是选取了Internal Clock才是使用定时功能,代码几乎没有改动。


下面是高级定时器TIM1的演示:

在这里插入图片描述
要注意的是根据上一章节图表TIM1是在APB2下的,上图中APB2给Timer的时钟频率为168MHz,所以根据设定的参数计算出来一个定时周期为500ms,所以演示中每500ms会触发一次中断,翻转IO口电平。

总结

Timer作为定时器使用是比较简单的,更多内容可以查看官方文档和例程。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐