STM32嵌入式开发之——Timer中断及外部时钟的使用(标准库)

前言

以STM32F10x系列为例,Timer定时器分为基本定时器、通用定时器、高级定时器,不同级别的定时器提供的功能有所不同:

定时器类型功能模块说明与应用
基本定时器1. 时基单元由 PSC(预分频器)+ ARR(自动重装载值)组成,产生定时中断或触发DAC
2. 时基单元的工作流程CNT 从 0 计数到 ARR,溢出产生更新事件(UEV),用于中断或触发操作
3. 主模式触发 DAC 功能TIM6/TIM7 可配置 TRGO 输出,作为 DAC 的硬件触发源
通用定时器1. 与基本定时器的异同通用定时器支持通道输入输出、编码器接口,而基本定时器仅支持定时功能
2. 内外时钟源选择功能可选择内部时钟(CK_INT)或外部触发(ETR/TIx)作为计数时钟
3. 编码器接口功能配置 TIMx 为编码器模式,读取正交编码器的计数/方向信息
4. 主从触发模式功能多定时器同步启动、同步清零,通过触发控制器(TRGO/TS)连接
5. 输出比较功能到达设定时间输出翻转、置位、复位、强制电平等,用于 PWM、单脉冲输出等
6. 输入捕获电路捕捉输入信号上升/下降沿的 CNT 值,实现频率、周期、占空比测量
高级定时器1. 重复计数器RCR:控制更新事件每发生 N 次才触发一次真正更新(常用于周期控制)
2. 死区生成电路与电机控制提供互补 PWM 输出及死区时间插入,驱动三相无刷电机等功率应用
3. 刹车输入(Break)接收外部紧急信号,关闭 PWM 输出,保障电路和系统安全
中断结构定时中断基本结构配置时基 + 启用中断 + NVIC + 编写 ISR,执行周期性任务或控制逻辑
时序分析1. 缓冲(影子)寄存器PSC、ARR、CCR 等寄存器具有影子功能,更新需触发 UEV(更新事件)
2. 预分频器时序分析PSC 分频作用:实际 CNT 增加速率 = 时钟频率 / (PSC+1)
3. 计数器时序分析CNT 支持向上、向下、中心对齐计数模式,影响 PWM 波形对称性
4. RCC 时钟树简介TIM1 属于 APB2(72MHz),TIM2~TIM4 属于 APB1(36MHz ×2 = 72MHz)

这里主要对几种不同的定时器进行比较总结,更详细的解析参见 江协科技/江科大-STM32入门教程-11.TIM定时中断_通用定时器和基本定时器的区别-CSDN博客


标准库Timer相关库函数

STM32 标准外设库(StdPeriphLib)中 TIM 定时器相关函数用于配置和控制 TIM1~TIM17 等通用定时器、基本定时器和高级定时器的行为。

下面我按功能模块,简明分类讲解这些函数 是做什么的、什么时候用


1. 基本初始化相关

函数作用
TIM_DeInit()复位 TIM 外设寄存器(恢复默认值)
TIM_TimeBaseInit()设置定时器计数器基础参数,如 计数周期预分频计数方向
TIM_TimeBaseStructInit()初始化 TIM_TimeBaseInitTypeDef 结构体为默认值(常用于初始化前)

2. 计数器设置 / 启动控制

函数作用
TIM_Cmd()启动/停止定时器
TIM_SetCounter()设置当前计数值
TIM_GetCounter()读取当前计数值
TIM_SetAutoreload()设置自动重装载值(ARR)
TIM_GetPrescaler()获取当前预分频值
TIM_SetPrescaler()设置预分频器
TIM_PrescalerConfig()同上 + 可立即生效或等下次更新
TIM_CounterModeConfig()设置计数模式(向上/向下/中心对齐)
TIM_SetClockDivision()设置时钟分频

3. 输出比较(PWM)相关配置

函数作用
TIM_OCxInit()初始化 OC 通道(x=1~4),配置 PWM 模式、比较值等
TIM_OCStructInit()初始化 TIM_OCInitTypeDef 结构体
TIM_CCxCmd()控制通道输出使能(TIMx_CHx)
TIM_CCxNCmd()控制互补通道输出(高级定时器才有)
TIM_OCxPreloadConfig()设置通道预装载(更新事件时才更新寄存器)
TIM_OCxFastConfig()快速比较模式
TIM_OCxPolarityConfig()设置输出极性(高电平有效或低电平有效)
TIM_OCxNPolarityConfig()设置互补输出极性(高级定时器)
TIM_SetCompareX()设置通道的比较值(决定 PWM 占空比)
TIM_ForcedOCxConfig()强制输出高/低电平
TIM_ClearOCxRef()清除比较参考输出
TIM_SelectOCxM()设置 OCx 模式(PWM模式1/2、强制高低等)

4. 输入捕获 / 编码器 / PWM输入

函数作用
TIM_ICInit()初始化输入捕获通道(测量信号频率、周期等)
TIM_ICStructInit()初始化 TIM_ICInitTypeDef
TIM_GetCaptureX()获取捕获值
TIM_SetICxPrescaler()设置输入捕获分频(每N个边沿触发)
TIM_PWMIConfig()用输入捕获测量 PWM 信号(周期+高电平时间)
TIM_EncoderInterfaceConfig()配置为编码器接口模式
TIM_SelectHallSensor()用于电机霍尔传感器接口

5. 时钟源选择——触发 / 同步 / 外部时钟

函数作用
TIM_SelectInputTrigger()选择触发源(内部/外部/其他通道)
TIM_SelectOutputTrigger()设置 TRGO 输出源
TIM_SelectSlaveMode()设置为从模式(响应外部触发)
TIM_SelectMasterSlaveMode()设置主从同步
TIM_ETRClockMode1Config()使用 ETR 外部时钟模式1
TIM_ETRClockMode2Config()外部时钟模式2
TIM_TIxExternalClockConfig()使用 TIx 通道作为外部时钟
TIM_ITRxExternalClockConfig()使用内部触发源作为时钟
TIM_ETRConfig()配置 ETR 极性、滤波器等

6. 更新控制 / 中断 / DMA

函数作用
TIM_UpdateDisableConfig()禁用更新事件(UG)
TIM_UpdateRequestConfig()设置是否在计数溢出时才生成更新事件
TIM_ITConfig()启用中断(如更新中断)
TIM_GetITStatus() / TIM_ClearITPendingBit()获取/清除中断状态
TIM_GenerateEvent()手动触发事件(UG、CCxG等)
TIM_DMAConfig()配置 DMA 的起始基地址和突发长度
TIM_DMACmd()启用/禁用某个 DMA 请求

7. 高级定时器专属功能(如 TIM1)

函数作用
TIM_BDTRConfig()设置刹车、死区、输出使能(用于电机控制)
TIM_BDTRStructInit()初始化 BDTR 结构体
TIM_CtrlPWMOutputs()控制高级定时器 PWM 输出总开关
TIM_SelectCOM()开启 COM 事件(互补 PWM 相关)
TIM_SelectCCDMA()允许使用 DMA 传输比较寄存器


Timer中断

定时中断基本结构框图如下:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 定时中断初始化流程

    第一步,RCC开启时钟,定时器的基准时钟和整个外设的工作时钟就都打开了
    第二步,选择时基单元的时钟源,对于定时中断就选择内部时钟源
    第三步,配置时基单元,包括预分频器、自动重装载器、计数模式等,参数用结构体配置
    第四步,配置输出中断控制,允许更新中断输出到NVIC
    第五步,配置NVIC,在NVIC中打开定时器中断通道并分配一个优先级
    第六步,运行控制,使能计数器,当定时器使能后,计数器就开始计数了,当计数器更新时,触发中断
    最后再写一个中断函数,中断函数每隔一段时间就能自动执行一次
    

1、开启时钟

  • 定时器上电后默认选择内部时钟,若需要内部时钟,下面的代码可省略:

    TIM_InternalClockConfig(TIM_TypeDef* TIMx);
    

2、时基单元初始化函数TIM_TimBaseInit参数配置

1. TIM_ClockDivision(采样时钟分频频率选择)

适用于滤波器采样频率设定,不直接影响时基单元。

  • 外部输入信号可能会抖动,通过滤波器采样判断是否稳定。
  • 采样频率越低、采样点越多,滤波效果越好,但延迟越大。
  • TIM_ClockDivision 设置分频倍数,用于采样滤波,常见选项:
    • TIM_CKD_DIV1:不分频
    • TIM_CKD_DIV2:2分频
    • TIM_CKD_DIV4:4分频

2. TIM_CounterMode(计数器模式)

  • 决定计数的方向或方式:
    • TIM_CounterMode_Up:向上计数
    • TIM_CounterMode_Down:向下计数(用于特殊情况)
    • TIM_CounterMode_CenterAligned1/2/3:中心对齐模式,PWM 更平滑

3. TIM_Period(自动重装载值 ARR)

  • 定义定时器计数周期,当计数器值 CNT 到达该值时溢出并触发中断。
  • 有效范围:0 ~ 65535

4. TIM_Prescaler(预分频器值 PSC)

  • 对输入时钟(如72MHz)进行预分频,降低计数频率。
  • 有效范围:0 ~ 65535

5. TIM_RepetitionCounter(重复计数器)

  • 仅高级定时器(如 TIM1、TIM8)有效,通用定时器无需设置,可置 0。

6. 定时时间计算

参考公式: CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1)
其中:

  • CK_PSC 是定时器时钟(一般为 72MHz)
  • PSC 是预分频值
  • ARR 是自动重装载值

举例:
若定时 1 秒,CK_PSC = 72MHz:
72,000,000 / (7200) / (10000) = 1Hz → 每秒溢出一次(定时 1 秒)

  • 即设置:
    • PSC = 7200 - 1
    • ARR = 10000 - 1

📝 提示: PSC 和 ARR 的组合并不唯一,只要乘积满足要求即可。


7. 更新事件行为

TIM_TimeBaseInit() 调用后,立即触发一次“更新事件”。

  • 原因:PSC 和 ARR 都是带缓冲寄存器的,写入值只有在更新事件发生后才生效。
  • 初始化时自动触发一次更新事件,让寄存器立即加载新值。
  • 注意:更新事件会同时触发更新中断,建议手动清除更新中断标志,防止初始化阶段误进中断。
TIM_ClearITPendingBit(TIMx, TIM_IT_Update); // 清除更新中断标志

3、使能中断函数TIM_ITConfig

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启更新中断到NVIC的通道

4、启动定时器

TIM_Cmd(TIM2,ENABLE);//当产生更新时,就会触发中断

程序代码

timer.h

#ifndef __TIME__H
#define __TIME_H
void Timer_Init(void);

#endif

time.c

#include "stm32f10x_tim.h"

/*
1.RCC开启时钟
2.选择时钟源
3.配置时钟源:PSC(预分频器)、ARR(自动重装器)、计数器(CNT)
4.配置输出中断控制,允许更新中断输出到NVIC
5.配置NVIC,再NVIC中打开定时器中断的通道,并分配优先级
6.运行控制,开启计数器
7.中断函数编写
*/

/**
* @brief 初始化定时器
* @param None
* @return None
*/
void Timer_Init(void){
  // 开启TIM2(通用定时器)
  // 使用APB1开启时钟,TIM2是APB1总线外设
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  
  // 选择时钟源
  TIM_InternalClockConfig(TIM2);
  
  // 配置时基单元
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //指定时钟分频
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数器模式为向上计数
  TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;  //ARR
  TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;  //PSC
  TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  //重复计数器的值
  
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
  
  TIM_ClearFlag(TIM2,TIM_FLAG_Update); // 在使能TIM之前,清除第一次中断标志位
  
  //使能中断
  TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启更新中断到NVIC的通道
	
  //NVIC中断
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC优先级分组
  NVIC_InitTypeDef NVIC_InitTyStructure;
  NVIC_InitTyStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitTyStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_InitTyStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
  NVIC_InitTyStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
  NVIC_Init(&NVIC_InitTyStructure); 
  
  //启动定时器
  TIM_Cmd(TIM2,ENABLE);//当产生更新时,就会触发中断
}


/*
中断函数模版
void TIM2_IRQHandler(void) //当定时器产生更新中断时,这个函数就会自动被执行
{
	//检查中断标志位
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
	{
	//执行相应的用户代码
		Num ++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除标志位
	}
}
*/

main.c

#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "Delay.h"
#include "OLED.h"
#include "time.h"
 
uint16_t Num;
 
int main(void)
{
	
	OLED_Init();	//初始化OLED
	Timer_Init();    //初始化定时器
	
	OLED_ShowString(1,1,"Num:");
 
	while(1)
	{
		OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);//CNT计数器值的变化情况(变化范围是ARR从0一直到自动重装值(10000-1))
	}
 
}
 
 
// 定时器2中断函数放在使用中断的main.c文件中;在startup文件中
// 当定时器产生更新中断时,这个函数就会自动被执行
void TIM2_IRQHandler(void) 
{
	//检查中断标志位
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
	{
	//执行相应的用户代码
		Num ++;//定时器每秒自动加一个Num全局变量
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除标志位
	}
 
}

外部输入模式

外部输入模式配置函数 TIM_ETRClockMode2Config

外部时钟可以通过定时器的 ETR(外部触发输入)引脚输入,调用 TIM_ETRClockMode2Config() 进行配置。

TIM_ETRClockMode2Config(TIMx, TIM_ExtTRGPrescaler_x, TIM_ExtTRGPolarity_x, ExtTRGFilter);

参数说明

  1. TIM_ExtTRGPrescaler:外部时钟预分频器
  • 可选值:
    • TIM_ExtTRGPSC_OFF:1 分频(不分频)
    • TIM_ExtTRGPSC_DIV2:2 分频
    • TIM_ExtTRGPSC_DIV4:4 分频
    • TIM_ExtTRGPSC_DIV8:8 分频
  1. TIM_ExtTRGPolarity:外部触发极性选择
  • TIM_ExtTRGPolarity_NonInverted:不反向,高电平或上升沿有效
  • TIM_ExtTRGPolarity_Inverted:反向,低电平或下降沿有效
  1. ExtTRGFilter:外部输入滤波器
  • 可选值范围:0x00 ~ 0x0F
  • 数值越大,滤波时间越长,抗干扰能力越强,但响应延迟增加
  • 每个值对应一个固定采样频率 f 和采样数 N,具体查阅芯片参考手册(RM)

GPIO 配置要求

由于外部时钟输入依赖于物理引脚的信号输入,需提前配置对应的 GPIO 引脚:

  • 推荐配置:
    • 默认:浮空输入模式(GPIO_Mode_IN_FLOATING
    • 若信号功率足够大:可选上拉或下拉输入,提升抗干扰能力
    • 若信号功率较小:建议使用浮空输入,避免内部上拉/下拉电阻影响外部信号
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 或使用 GPIO_Mode_IPU/IPD
GPIO_Init(GPIOx, &GPIO_InitStructure);

📌 外部时钟接入场景适用于编码器解码、脉冲计数器、红外测距等高精度信号处理。

程序代码


参考文章

江协科技/江科大-STM32入门教程-11.TIM定时中断_通用定时器和基本定时器的区别-CSDN博客

江协科技/江科大-STM32入门教程-12.示例程序(定时器定时中断&定时器外部时钟)_stm32 定时器代码-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FishNKernel

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

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

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

打赏作者

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

抵扣说明:

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

余额充值