当前位置: 首页 > article >正文

STM32标准库-TIM定时器

文章目录

  • 一、TIM定时器
    • 1.1定时器
    • 1.2定时器类型
      • 1.1.1 高级定时器
      • 1.1.2通用定时器
      • 1.1.3基本定时器
  • 二、定时中断基本结构
    • 预分频器时器
    • 计时器时序
    • 计数器无预装时序
    • 计数器有预装时序
    • RCC时钟树
  • 三、定时器定时中断
    • 3.1 接线图
    • 3.2代码
    • 3.3效果:
  • 四、定时器外部中断
    • 4.1接线图
    • 4.2代码
    • 4.3效果:

一、TIM定时器

1.1定时器

  • TIM(Timer)定时器
  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

1.2定时器类型

在这里插入图片描述
== STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4==

1.1.1 高级定时器

在这里插入图片描述

1.1.2通用定时器

在这里插入图片描述

1.1.3基本定时器

在这里插入图片描述

二、定时中断基本结构

在这里插入图片描述

预分频器时器

在这里插入图片描述

预分频器作用:通过分频降低定时器时钟频率,公式为 CK_CNT = CK_PSC / (PSC + 1)(PSC 是预分频器寄存器值)

  • 时序关键节点:

CK_PSC:输入时钟(高频);
CNT_EN:计数器使能信号;
CK_CNT:分频后的计数器时钟(低频,驱动CNT计数);
更新事件(UEV):预分频器参数变更后,触发定时器更新,使新分频值生效。
流程:修改TIMx_PSC寄存器值→预分频缓冲器暂存→更新事件触发时,新值加载到实际计数器,改变CK_CNT频率。

计时器时序

在这里插入图片描述

计数器无预装时序

在这里插入图片描述

计数器有预装时序

在这里插入图片描述

RCC时钟树

在这里插入图片描述

三、定时器定时中断

3.1 接线图

在这里插入图片描述

3.2代码

main.c

#include "stm32f10x.h"                  // 包含STM32F10x系列设备头文件,访问寄存器和外设
#include "Delay.h"                       // 延时功能(未使用,可忽略)
#include "OLED.h"                        // OLED显示驱动,实现屏幕初始化和显示
#include "CountSensor.h"                 // 计数传感器(未使用,可忽略)
#include "Timer.h"                       // TIM2定时器初始化(内部时钟,1秒中断一次)uint16_t Num = 0;                        // 记录定时器中断次数(每秒+1)int main(void)
{		OLED_Init();                          // 初始化OLED(配置通信,如I2C/SPI)Timer_Init();                         // 初始化TIM2(内部时钟,周期1秒)OLED_ShowString(1, 1, "Num:");        // 第1行显示"Num:",标识计数while(1) // 主循环,持续更新显示{ OLED_ShowNum(1, 5, Num, 5);       // 显示中断次数(右对齐,5位)OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5); // 显示TIM2当前计数值(0~9999循环)}
}// TIM2更新中断服务函数(每秒触发一次)
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { // 检查中断标志Num++;                                // 计数+1(每秒更新)TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除标志}
}

Timer.

#include "stm32f10x.h"                  // 包含STM32F10x系列设备头文件,访问寄存器和外设功能extern uint16_t Num;  // 外部声明计数变量(主程序中定义)// 定时器TIM2初始化(内部时钟,1秒中断一次)
void Timer_Init(void)
{// 1. 使能TIM2时钟(APB1总线)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 2. 配置内部时钟源TIM_InternalClockConfig(TIM2);// 3. 时基单元配置(72MHz系统时钟下,1秒周期)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;          // 时钟分频(无分频)TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;      // 向上计数TIM_TimeBaseInitStruct.TIM_Period = 9999;                         // 自动重装值(0~9999,共10000次计数)TIM_TimeBaseInitStruct.TIM_Prescaler = 7199;                      // 预分频(7200分频,72MHz/7200=10kHz)TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;                 // 重复计数(通用定时器不用)TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);                  // 初始化时基// 4. 清除初始更新标志(避免误触发)TIM_ClearFlag(TIM2, TIM_FLAG_Update);// 5. 使能更新中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 6. NVIC配置(中断优先级)NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                   // 分组:2位抢占,2位子优先级NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;                      // TIM2中断通道NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;                      // 使能中断NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;            // 抢占优先级2NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;                   // 子优先级1NVIC_Init(&NVIC_InitStruct);                                      // 初始化NVIC// 7. 启动定时器TIM_Cmd(TIM2, ENABLE);
}/*
// TIM2中断服务函数(需取消注释以更新Num)
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {                // 检查更新中断标志Num++;                                                       // 计数变量递增(每秒+1)TIM_ClearITPendingBit(TIM2, TIM_IT_Update);                   // 清除中断标志}
}
*/

3.3效果:

Num 变化:每秒递增 1(如:0 → 1 → 2 → …)
CNT 变化:每秒内从 0 递增到 9999,然后归零,Num+1。

四、定时器外部中断

4.1接线图

在这里插入图片描述

4.2代码

Timer.c

#include "stm32f10x.h"                  // 包含STM32F10x系列外设寄存器定义extern uint16_t Num;  // 外部声明的计数变量(需在主程序中定义)/*** @brief  定时器TIM2初始化(ETR外部时钟模式)* @note   存在3处关键问题:*         1. PA0被配置为上拉输入,与ETR复用功能冲突;*         2. 中断服务函数被注释,无法响应溢出中断;*         3. 时基参数过于激进(周期9、预分频0),计数过快。*/
void Timer_Init(void)
{// 1. 使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  // 使能TIM2时钟(APB1总线)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟(APB2总线)// 2. 配置PA0(冲突点:ETR模式下应自动复用,手动配置输入模式会冲突)GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         // 上拉输入(与ETR功能冲突,建议移除)GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     // 输入模式下速度设置无效,仅语法要求GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;             // 配置PA0引脚GPIO_Init(GPIOA, &GPIO_InitStructure);                // 初始化GPIOA// 3. 配置ETR外部时钟模式2//    参数:不分频、上升沿触发(NonInverted对应上升沿)、无滤波TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF,            // ETR预分频器关闭TIM_ExtTRGPolarity_NonInverted, // 极性:非反相(上升沿触发)0x00);                        // 滤波系数0(无滤波)// 4. 配置时基单元(参数问题:周期9 + 预分频0 → 每10个脉冲触发一次中断)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频(不影响计数频率)TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数TIM_TimeBaseInitStruct.TIM_Period = 10 - 1;             // 自动重装值:计数到9时溢出TIM_TimeBaseInitStruct.TIM_Prescaler = 1 - 1;           // 预分频值:0(不分频)TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;       // 重复计数器(通用定时器无效)TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);        // 初始化时基// 5. 清除更新标志(避免初始化时误触发中断)TIM_ClearFlag(TIM2, TIM_FLAG_Update);// 6. 使能更新中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);              // 允许TIM2的更新中断// 7. 配置NVIC中断优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);         // 分组:2位抢占优先级 + 2位子优先级NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;            // 中断通道:TIM2全局中断NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;            // 使能中断通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;  // 抢占优先级:2NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;         // 子优先级:1NVIC_Init(&NVIC_InitStruct);                            // 初始化NVIC// 8. 启动定时器TIM_Cmd(TIM2, ENABLE);                                  // 启动TIM2计数器
}/*** @brief  获取TIM2当前计数值* @retval TIM2的计数器值(0 ~ TIM_Period)*/
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2);  // 读取TIM2的CNT寄存器值
}/*
// 【关键缺失】TIM2中断服务函数(当前被注释,导致Num无法递增)
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { // 检查更新中断标志Num++;                                        // 计数变量递增TIM_ClearITPendingBit(TIM2, TIM_IT_Update);   // 清除中断标志(必须操作)}
}
*/

main.c

#include "stm32f10x.h"                  // 包含STM32F10x系列设备的寄存器定义和外设库函数#include "Delay.h"                      
#include "OLED.h"                       
#include "CountSensor.h"               
#include "Timer.h"                       // 定时器驱动头文件(包含TIM2初始化逻辑)uint16_t Num = 0;                        // 全局计数变量,记录定时器中断次数int main(void)
{		OLED_Init();                          // 初始化OLED显示屏(配置通信接口,如I2C/SPI)Timer_Init();                         // 初始化TIM2定时器(ETR外部时钟模式,需PA0输入外部信号)OLED_ShowString(1, 1, "Num:");  // 在OLED第1行第1列显示字符串"Num:",标识计数结果OLED_ShowString(2, 1, "CNT:");  // 在OLED第2行第1列显示字符串"CNT:",标识定时器当前计数值while(1) // 主循环,持续更新显示内容{ OLED_ShowNum(1, 5, Num, 5);     // 在OLED第1行第5列显示Num(右对齐,占5位)OLED_ShowNum(2, 5, Timer_GetCounter(), 5);   // 在OLED第2行第5列显示TIM2当前计数值(右对齐,占5位)}
}void TIM2_IRQHandler(void)// TIM2定时器中断服务函数(处理更新中断,每TIM_Period+1个脉冲触发一次)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)    // 检查TIM2的更新中断标志是否置位{Num++;                 // 计数变量递增(记录中断次数)TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志,避免重复触发}
}
  • 定时器配置(Timer.c):

使能 TIM2 和 GPIOA 时钟(PA0 为 ETR 引脚)。
配置 ETR 外部时钟模式(上升沿触发,不分频)。
时基参数:TIM_Period(自动重载值)和TIM_Prescaler(预分频值,需根据外部信号频率调整)。
使能更新中断,配置 NVIC 优先级。

  • 中断处理:

每次 TIM2 计数溢出(达到TIM_Period)时,Num加 1,用于统计中断次数。
OLED 实时显示Num(总计数)和TIM2->CNT(当前计数值,0~TIM_Period循环)。

4.3效果:

  • 当 PA0 接入外部脉冲信号(如方波)时,TIM2 计数器CNT随脉冲上升沿递增。
  • 每计数到 9(即收到 10 个脉冲)时,触发更新中断,Num加 1。
  • 计数器每触发一次CNT+1,加到9时,Num+1.
http://www.lryc.cn/news/2401247.html

相关文章:

  • 【算法训练营Day05】哈希表part1
  • CMap应用场景和例子
  • Kafka 如何保证顺序消费
  • 【算法题】算法一本通
  • Modbus转Ethernet IP赋能挤出吹塑机智能监控
  • C++中如何遍历map?
  • 什么是终端安全管理系统(终端安全管理软件2024科普)
  • 书籍转圈打印矩阵(8)0604
  • 【JVM】Java类加载机制
  • Elasticsearch中的自定义分析器(Custom Analyzer)介绍
  • 《C++初阶之入门基础》【C++的前世今生】
  • Apache APISIX
  • 如何在 git dev 中创建合并请求
  • 基于nlohmann/json 实现 从C++对象转换成JSON数据格式
  • Java枚举类映射MySQL的深度解析与实践指南
  • 代码训练LeetCode(21)跳跃游戏2
  • 【HarmonyOS 5】鸿蒙APP使用【团结引擎Unity】开发的案例教程
  • 《T/CI 404-2024 医疗大数据智能采集及管理技术规范》全面解读与实施分析
  • 国产三维CAD皇冠CAD在「金属压力容器制造」建模教程:蒸汽锅炉
  • Mysql避免索引失效
  • python爬虫:Ruia的详细使用(一个基于asyncio和aiohttp的异步爬虫框架)
  • C++中单例模式详解
  • 舆情监控系统爬虫技术解析
  • Windows上用FFmpeg采集摄像头推流 → MediaMTX服务器转发流 → WSL2上拉流播放
  • cpp多线程学习
  • Vue3中Ant-design-vue的使用-附完整代码
  • k8s热更新-subPath 不支持热更新
  • Redis Sorted Set 深度解析:从原理到实战应用
  • docker中组合这几个命令来排查 import 模块失败 的问题
  • 若依框架修改模板,添加通过excel导入数据功能