一、定时器概述
定时器是一种电子组件,主要用于定时控制,具备精确计时的能力。它可以在设定的时间间隔内触发特定的操作,如发送数据、采集传感器信息、检测输入信号或产生规律性输出波形。这种灵活性使定时器在多个行业中得到广泛应用,支持各种复杂功能的实现,是现代电子系统中不可或缺的一部分。
1.1 定时原理
首先,时钟信号(CLK)输入到预分频器(PSC),在此降低频率以适应不同的定时需求。降频后的时钟信号送入定时器时钟(TIM CLK),再驱动计数器(CNT)。计数器根据自动重装载寄存器(ARR)设定的值进行计数,一旦计数值达到ARR设定的值,定时器便完成一个周期,产生中断或其他预设的定时事件,如输出比较信号或重置计数器。通过调整PSC和ARR的值,可以精确控制定时周期和定时器的输出
1.2 定时器分类
定时器分为基本定时器、通用定时器和高级定时器,每个类型的定时器功能上都有区别,具体见下面的表格。
在STM32F103系列中,每个定时器的特性如下表。
二、基本定时器工作原理
STM32F103有两个基本定时器TIM6和TIM7,它们的功能完全相同,资源是完全独立的,可以同时使用。其主要特性如下:16位自动重载递增计数器,16位可编程预分频器,预分频系数1~65536,用于对计数器时钟进行分频,还可以触发DAC的同步电路,以及生成中断/DMA请求。
2.1 基本定时器框图
-
时钟源
定时器的计数能力依赖于提供给它的时钟源。这个时钟源起始于APB1总线,但并非直接供应,二十通过倍频器调节。当APB1总线的预分频器设置为1时,倍频器系数也是1,因此定时器的时钟频率与APB1总线相同。若APB1预分频器系数大于等于2,倍频器系数成为2,定时器时钟频率便是APB1总线频率的两倍。考虑到在sys_stm32_clock_init函数中APB1总线被设置为36MHz,并且预分频器的分频系数是2,挂在APB1总线上的定时器的时钟频率将是72MHz。 -
控制器
控制器除了控制定时器复位、使能、计数等功能之外,还可以用于触发DAC转换。 -
时基单元
时基单元包括:计数器寄存器(TIMx_CNT)、预分频寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR)。基本定时器的这三个寄存器都是16位雨哦小数字,即可设置的范围是0~65535.
时间单元中的预分频器PSC,它有一个输入和一个输出。输入CK_PSC来源于控制器部分,实际上就是来自于内部时钟(CK_INT),即2倍的APB1总线时钟频率(72MHz)。输出CK_CNT是分频后的时钟,它是计数器实际的计数时钟,通过设置预分频器寄存器(TIMx_PSC)的值可以得到不同频率CK_INT,计算公式如下:
f C K C N T = f C K P S C / ( P S C [ 15 : 0 ] + 1 ) f_{CK_CNT} = f_{CK_PSC} / (PSC[15:0] + 1) fCKCNT=fCKPSC/(PSC[15:0]+1)
PSC[15:0]是写入预分频器寄存器(TIMx_PSC)的值。注意:预分频器寄存器(TIMx_PSC)可以载运行过程中修改它的值,新的预分频数值将在下一个更新事件时起作用。因为更新事件发生时,会把TIMx_PSC寄存器值更新到其影子寄存器(间接更新,确保稳定)中。
2.2 更新事件
-
软件产生
将TIMx_EGR寄存器的位UG置1,产生更新事件后,硬件会自动将UG位清零。 -
硬件产生
基本定时器的核心是一个递增计数器(CNT)。启动定时器的条件是TIMx_CR1寄存器的GEN位被置为1。此后,每个CK_CNT时钟脉冲都会使CNT寄存器的值增加1。当CNT的值增加至自动重载寄存器(ARR)相等时,CNT会自动清零,并触发一个更新事件。这可能导致DMA请求产生中断或触发其他同步操作。每次CNT清零后,它会在接收到下一个脉冲时再次递增,循环继续。计时器的这种计数溢出行为,即CNT等于ARR时自动的清零,被称为定时器溢出或上溢。
2.3 定时器计数模式
定时器计数模式主要有三种:递增计数模式、递减计数模式、中心对齐模式,每种模式的溢出条件如下:
各计数模式的工作示意图如下:
-
递增计数模式
计数器从0开始,向上递增至ARR(自动重载寄存器)值然后溢出,触发更新事件后重新从0开始计数。 -
递减计数模式
计数器从ARR值开始,向下递减至0然后溢出,触发更新事件后重新从ARR值开始计数。 -
中心对齐模式
计数器从0开始向上递增至ARR值,然后改为向下计数至0,循环往复。
2.4 溢出事件计算方式
T o u t = ( A R R + 1 ) ∗ ( P S C + 1 ) F t T_{out} = \frac{(ARR + 1) * (PSC + 1)}{F_{t}} Tout=Ft(ARR+1)∗(PSC+1)
- Tout是定时器溢出时间
- Ft是定时器的时钟源频率
- ARR是自动重装载寄存器的值
- PSC是预分频器寄存器的值
2.5 定时器配置步骤
- 配置定时器基础工作参数:HAL_TIM_Base_Init()
- 定时器基础MSP初始化:HAL_TIM_Base_MspInit(). 配置NVIC、CLOCK
- 使能更新中断并启动计数器:HAL_TIM_Base_Start_IT()
- 设置优先级,使能中断:HAL_NVIC_SetPriority()、HAL_NVIC_EnableIRQ()
- 编写中断服务函数:TIMx_IRQHandler()
- 编写定时器更新中断回调函数:HAL_TIM_PeriodElapsedCallback()
三、程序示例
头文件定义如下:
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 基本定时器 定义 */
/* TIMX 中断定义
* 默认是针对TIM6/TIM7
* 注意: 通过修改这4个宏定义,可以支持TIM1~TIM8任意一个定时器.
*/
#define BTIM_TIMX_INT TIM6
#define BTIM_TIMX_INT_IRQn TIM6_DAC_IRQn
#define BTIM_TIMX_INT_IRQHandler TIM6_DAC_IRQHandler
#define BTIM_TIMX_INT_CLK_ENABLE() do{ __HAL_RCC_TIM6_CLK_ENABLE(); }while(0) /* TIM6 时钟使能 */
/******************************************************************************************/
void btim_timx_int_init(uint16_t arr, uint16_t psc); /* 基本定时器 定时中断初始化函数 */
定时器源文件代码如下:
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/btim.h"
TIM_HandleTypeDef g_timx_handle; /* 定时器句柄 */
/**
* @brief 基本定时器TIMX定时中断初始化函数
* @note
* 基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
* 基本定时器的时钟为APB1时钟的2倍, 而APB1为36M, 所以定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr: 自动重装值。
* @param psc: 时钟预分频数
* @retval 无
*/
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
g_timx_handle.Instance = BTIM_TIMX_INT; /* 通用定时器X */
g_timx_handle.Init.Prescaler = psc; /* 设置预分频系数 */
g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_handle.Init.Period = arr; /* 自动装载值 */
HAL_TIM_Base_Init(&g_timx_handle);
HAL_TIM_Base_Start_IT(&g_timx_handle); /* 使能定时器x及其更新中断 */
}
/**
* @brief 定时器底层驱动,开启时钟,设置中断优先级
此函数会被HAL_TIM_Base_Init()函数调用
* @param htim:定时器句柄
* @retval 无
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == BTIM_TIMX_INT)
{
BTIM_TIMX_INT_CLK_ENABLE(); /* 使能TIM时钟 */
HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 1, 3); /* 抢占1,子优先级3,组2 */
HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn); /* 开启ITM3中断 */
}
}
/**
* @brief 定时器TIMX中断服务函数
* @param 无
* @retval 无
*/
void BTIM_TIMX_INT_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_handle); /* 定时器中断公共处理函数 */
}
/**
* @brief 定时器更新中断回调函数
* @param htim:定时器句柄
* @retval 无
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == BTIM_TIMX_INT)
{
LED1_TOGGLE(); /* LED1反转 */
}
}
主函数代码如下:文章来源:https://www.toymoban.com/news/detail-834815.html
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/btim.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
btim_timx_int_init(5000 - 1, 7200 - 1); /* 10Khz的计数频率,计数5K次为500ms */
while (1)
{
LED0_TOGGLE();
delay_ms(200);
}
}
整体代码流程如下,先初始化串口、LED引脚还有基本定时器TIM6(时间为500ms),主函数循环中每200ms切换LED0的状态。每500ms执行定时器中断服务函数,切换LED1状态。文章来源地址https://www.toymoban.com/news/detail-834815.html
到了这里,关于STM32 MCU 定时器详解(1)--基本定时器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!