使用HAL库开发STM32:Timer基础说明与定时功能使用

本文详细介绍了STM32单片机中Timer的基本概念,包括计数器工作原理、内部时钟选择、Prescaler预分频和Period计数周期设置。通过实例演示如何配置定时器以触发中断,并提供了配置代码和关键参数计算。
摘要由CSDN通过智能技术生成

目的

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作为定时器使用是比较简单的,更多内容可以查看官方文档和例程。

以下是FreeModbus STM32 HAL库中porttimer文件的示例代码,其中包含了中文注释: ``` /* * FreeModbus Libary: STM32Fxxx Port Timer For Modbus * * Copyright (C) 2013 Real Time Engineers ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * File: porttimer.c * * Description: STM32Fxxx port timer implementation for FreeModbus. */ /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- STM32 includes ----------------------------------*/ #include "stm32f10x.h" /* ----------------------- Defines ------------------------------------------*/ #define MB_TIMER_PRESCALER ( ( uint16_t )( ( SystemCoreClock / 2 ) / 1000000 ) - 1 ) // 定时器预分频器,用于将系统时钟分频到1MHz #define MB_TIMER_TICKS ( 20000UL ) // 定时器重装值,用于定时20ms /* ----------------------- Static variables ---------------------------------*/ static USHORT usTimerOCRADelta; /* ----------------------- Function prototypes ------------------------------*/ static void prvvTIMERExpiredISR( void ); /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; /* Enable the TIM2 global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); /* Time base configuration */ TIM_TimeBaseStructInit( &TIM_TimeBaseStructure ); TIM_TimeBaseStructure.TIM_Period = ( uint16_t )( MB_TIMER_TICKS - 1 ); TIM_TimeBaseStructure.TIM_Prescaler = MB_TIMER_PRESCALER; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit( TIM2, &TIM_TimeBaseStructure ); /* Clear TIM2 update flag */ TIM_ClearFlag( TIM2, TIM_FLAG_Update ); /* Enable TIM2 Update interrupt */ TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE ); /* Preload enable */ TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable ); TIM_ARRPreloadConfig( TIM2, ENABLE ); /* TIM2 enable counter */ TIM_Cmd( TIM2, ENABLE ); /* Timer delta value for Modbus tick */ usTimerOCRADelta = ( uint16_t )( ( ( uint32_t )usTim1Timerout50us * ( uint32_t )( MB_TIMER_TICKS ) ) / ( uint32_t )20000 ); return TRUE; } void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ TIM_SetAutoreload( TIM2, ( uint16_t )( MB_TIMER_TICKS - 1 ) ); TIM_SetCompare1( TIM2, TIM_GetCounter( TIM2 ) + usTimerOCRADelta ); TIM_Cmd( TIM2, ENABLE ); } void vMBPortTimersDisable( ) { /* Disable any pending timers. */ TIM_Cmd( TIM2, DISABLE ); TIM_SetCounter( TIM2, 0 ); } /* ----------------------- Timers functions ---------------------------------*/ void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void TIM2_IRQHandler( void ) { if( TIM_GetITStatus( TIM2, TIM_IT_Update ) != RESET ) { TIM_ClearITPendingBit( TIM2, TIM_IT_Update ); prvvTIMERExpiredISR( ); } } ``` 这段代码实现了FreeModbus STM32 HAL库定时器功能。其中,`MB_TIMER_PRESCALER`和`MB_TIMER_TICKS`分别是定时器的预分频器和重装值,用于将定时器时钟分频到1MHz并定时20ms。在`xMBPortTimersInit()`函数中,首先配置了定时器的各项参数,并设置了定时器中断的优先级和使能。在`vMBPortTimersEnable()`函数中,设置了定时器到期时间,并使能了定时器。而在`vMBPortTimersDisable()`函数中,则禁用了定时器。 此外,代码还实现了定时器到期时的中断处理函数`prvvTIMERExpiredISR()`和定时器中断服务函数`TIM2_IRQHandler()`,以便在定时器到期时触发Modbus超时处理。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naisu Xu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值