STM32学习笔记:定时器(TIM)原理与应用(详解篇)
前言
定时器是STM32微控制器中最重要且最常用的外设之一,它不仅能提供精确的定时功能,还能实现PWM输出、输入捕获、编码器接口等多种功能。本文将全面介绍STM32的通用定时器,包括其工作原理、配置方法和典型应用。
一、STM32定时器概述
定时器时钟系统的设计和实现对于嵌入式系统的正常运作至关重要。STM32系列微控制器具有复杂的时钟系统架构,能够为不同外设提供灵活的时钟源选择。
STM32包含多种时钟源,满足不同应用场景需求:
时钟源 | 类型 | 频率 | 主要用途 |
---|---|---|---|
HSI | 高速内部RC振荡器 | 8MHz | 系统时钟备用源 |
HSE | 高速外部晶振 | 4-26MHz | 主系统时钟源 |
LSI | 低速内部RC振荡器 | ~32kHz | 独立看门狗、RTC |
LSE | 低速外部晶振 | 32.768kHz | 精确RTC时钟 |
PLL | 锁相环倍频 | 可配置 | 生成高频系统时钟 |
1. 定时器分类
STM32F1系列包含丰富的定时器资源,主要分为:
- 高级定时器(TIM1/TIM8):功能最全,支持PWM互补输出等高级功能
- 通用定时器(TIM2-TIM5):平衡功能与复杂度,适合大多数应用
- 基本定时器(TIM6/TIM7):功能简单,主要用于定时和DAC触发
2. 通用定时器主要功能
- 16/32位向上、向下、中央对齐的自动重装载计数器
- 多种时钟源选择:
- 内部时钟(CK_INT)
- 外部时钟模式1(外部引脚)
- 外部时钟模式2(外部触发输入ETR)
- 内部触发输入(ITRx,用于定时器级联)
- 4个独立通道,每个通道可用于:
- 输入捕获
- 输出比较
- PWM生成
- 单脉冲模式输出
- 丰富的触发源:可用于ADC启动、DAC触发等
- 编码器接口:可直接连接正交编码器
二、定时器工作原理
1. 定时器时钟系统
定时器时钟主要来自:
- 内部时钟(CK_INT):APB总线时钟经倍频后得到
- 当APB预分频器为1时,CK_INT = APB时钟
- 否则CK_INT = 2×APB时钟
- 外部时钟:
- 模式1:外部引脚(TIx)输入
- 模式2:外部触发输入(ETR)
2. 定时器基本组成
- 计数器寄存器(TIMx_CNT):核心计数单元
- 预分频器(TIMx_PSC):对输入时钟进行分频
- 16位可编程,分频系数1~65536
- 自动重装载寄存器(TIMx_ARR):决定计数周期
- 比较/捕获寄存器(TIMx_CCRx):用于比较或捕获
3. 计数模式
- 向上计数模式:从0计数到ARR值,然后重新从0开始
- 向下计数模式:从ARR值向下计数到0,然后重新从ARR开始
- 中央对齐模式:先向上计数到ARR,再向下计数到0
三、定时器应用开发
1. 基本定时功能实现
配置步骤:
- 使能定时器时钟
- 配置时基单元
- 使能定时器中断(如需)
- 配置NVIC
- 编写中断服务函数
代码实现:
// 定时器3初始化
void TIM3_Init(u16 arr, u16 psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;// 1. 使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);// 2. 配置时基TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装载值TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频系数TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);// 3. 使能中断TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);// 4. 配置NVICNVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 5. 启动定时器TIM_Cmd(TIM3, ENABLE);
}// 定时器3中断服务函数
void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){TIM_ClearITPendingBit(TIM3, TIM_IT_Update);// 用户处理代码LED_Toggle();}
}
2. PWM输出配置
PWM原理:通过调节占空比(高电平时间与周期之比)来控制输出
配置步骤:
- 使能定时器和GPIO时钟
- 配置GPIO为复用推挽输出
- 初始化时基单元
- 配置PWM模式
- 设置占空比
- 使能输出和定时器
代码实现:
void TIM2_PWM_Init(u16 arr, u16 psc)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;// 1. 使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 2. 配置GPIOGPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // TIM2_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 初始化时基TIM_TimeBaseStructure.TIM_Period = arr;TIM_TimeBaseStructure.TIM_Prescaler = psc;TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);// 4. 配置PWM模式TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比TIM_OC2Init(TIM2, &TIM_OCInitStructure);// 5. 使能预装载TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);// 6. 启动定时器TIM_Cmd(TIM2, ENABLE);
}// 设置PWM占空比
void PWM_SetDuty(TIM_TypeDef* TIMx, u32 ch, u16 duty)
{switch(ch){case 1: TIMx->CCR1 = duty; break;case 2: TIMx->CCR2 = duty; break;case 3: TIMx->CCR3 = duty; break;case 4: TIMx->CCR4 = duty; break;}
}
3. 输入捕获功能
应用场景:测量脉冲宽度、频率或周期
配置步骤:
- 使能时钟和GPIO
- 配置GPIO为输入
- 初始化时基单元
- 配置输入捕获参数
- 使能中断
- 编写中断服务函数
代码实现:
// 定时器5通道2输入捕获初始化
void TIM5_Cap_Init(u16 arr, u16 psc)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM5_ICInitStructure;NVIC_InitTypeDef NVIC_InitStructure;// 1. 使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 2. 配置GPIOGPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // TIM5_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 初始化时基TIM_TimeBaseStructure.TIM_Period = arr;TIM_TimeBaseStructure.TIM_Prescaler = psc;TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);// 4. 配置输入捕获TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频TIM5_ICInitStructure.TIM_ICFilter = 0x00; // 不滤波TIM_ICInit(TIM5, &TIM5_ICInitStructure);// 5. 使能中断TIM_ITConfig(TIM5, TIM_IT_CC2, ENABLE);// 6. 配置NVICNVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 7. 启动定时器TIM_Cmd(TIM5, ENABLE);
}// 定时器5中断服务函数
u32 capture_val = 0;
void TIM5_IRQHandler(void)
{if(TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET){capture_val = TIM_GetCapture2(TIM5); // 获取捕获值TIM_ClearITPendingBit(TIM5, TIM_IT_CC2);}
}
四、定时器高级应用
1. 定时器级联
将两个定时器级联可以扩展定时范围:
- 主定时器配置为定时模式
- 从定时器配置为外部时钟模式1,时钟源来自主定时器
// 定时器2作为主定时器,定时器3作为从定时器
void TIM23_Cascade_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 1. 主定时器(TIM2)配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_TimeBaseStructure.TIM_Period = 1000-1;TIM_TimeBaseStructure.TIM_Prescaler = 7200-1; // 10KHzTIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);// 配置TIM2触发输出TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);// 2. 从定时器(TIM3)配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);TIM_TimeBaseStructure.TIM_Period = 0xFFFF;TIM_TimeBaseStructure.TIM_Prescaler = 0;TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);// 配置TIM3为外部时钟模式1TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1); // TIM2作为TIM3的触发源TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);// 启动定时器TIM_Cmd(TIM2, ENABLE);TIM_Cmd(TIM3, ENABLE);
}
2. 编码器接口模式
用于连接正交编码器,可自动识别旋转方向和计数:
void TIM4_Encoder_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure;NVIC_InitTypeDef NVIC_InitStructure;// 1. 使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);// 2. 配置GPIOGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // TIM4_CH1/CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOB, &GPIO_InitStructure);// 3. 初始化时基TIM_TimeBaseStructure.TIM_Period = 0xFFFF;TIM_TimeBaseStructure.TIM_Prescaler = 0;TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);// 4. 配置编码器接口TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;TIM_ICInitStructure.TIM_ICFilter = 6; // 适当滤波TIM_ICInit(TIM4, &TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;TIM_ICInit(TIM4, &TIM_ICInitStructure);// 5. 启动定时器TIM_Cmd(TIM4, ENABLE);
}
五、总结
STM32的定时器外设功能强大且灵活,本文介绍了:
- 定时器的基本结构和时钟系统
- 三种基本应用模式:定时、PWM输出和输入捕获
- 两种高级应用:定时器级联和编码器接口
实际应用中需要注意:
- 时钟配置和分频计算
- 中断优先级的合理设置
- PWM输出时的死区时间控制(高级定时器)
- 输入捕获时的滤波设置
通过合理利用STM32的定时器资源,可以实现精确的定时控制、电机调速、信号测量等多种功能,是嵌入式系统开发中不可或缺的重要外设。
相关文章:
STM32学习笔记:定时器(TIM)原理与应用(详解篇)
前言 定时器是STM32微控制器中最重要且最常用的外设之一,它不仅能提供精确的定时功能,还能实现PWM输出、输入捕获、编码器接口等多种功能。本文将全面介绍STM32的通用定时器,包括其工作原理、配置方法和典型应用。 一、STM32定时器概述 定…...
JAVA获取ES连接并查询所有数据
我们的项目要获取es连接,新版本和旧版本有不小的区别,在8.17.0版本使用的是 ElasticsearchClient <dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.17…...

408第一季 - 数据结构 - 线性表
只能用C/C! 顺序表 闲聊 线性表的逻辑顺序和物理顺序相同 都是1234 顺序表的优点: 随机访问,随机访问的意思是访问的时间 和位置没有关系,访问下标1和100一样的,更深层就是直接计算 a100 * 数组大小,随便…...

第23讲、Odoo18 邮件系统整体架构
目录 Odoo 邮件系统整体架构邮件发送方式邮件模板配置SMTP 邮件服务器配置邮件发送过程开发中常见邮件发送需求常见问题排查提示与最佳实践完整示例:审批通过自动发邮件门户表单自动邮件通知案例邮件队列与异步发送邮件添加附件邮件日志与调试多语言邮件模板邮件安…...
【QT面试题】(三)
文章目录 Qt信号槽的优点及缺点Qt中的文件流和数据流区别?Qt中show和exec区别QT多线程使用的方法 (4种)QString与基本数据类型如何转换?QT保证多线程安全事件与信号的区别connect函数的连接方式?信号与槽的多种用法Qt的事件过滤器有哪些同步和…...
DeepSeek09-open-webui使用
Open WebUI 完全指南:从安装到知识库搭建与异常处理 最后更新:2025年6月7日 | 适用版本:Open WebUI v0.6.x 一、安装部署 1.1 系统要求 **Python 3.12 **(严格版本要求,更高版本3.13不兼容)Node.js 20.x内…...

HarmonyOS:Counter计数器组件
一、概述 计数器组件,提供相应的增加或者减少的计数操作。 说明 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 二、属性 除支持通用属性外,还支持以下属性。 enableInc enableInc(value: b…...
数据类型 -- 字符
在C中,字符型(char)用于存储单个字符,如字母、数字、符号等。字符型是最基本的数据类型之一,常用于处理文本、字符数组(字符串)等场景。 1. 基本类型 • char:标准字符类型&#x…...
WordZero:让Markdown与Word文档自由转换的Golang利器
在日常工作中,我们经常需要在Markdown和Word文档之间进行转换。Markdown方便编写和版本控制,而Word文档更适合正式的商务环境。作为一名Golang开发者,我开发了WordZero这个库,专门解决这个痛点。 项目背景 GitHub仓库࿱…...

sqlsugar WhereIF条件的大于等于和等于查出来的坑
一、如下图所示,当我用 .WhereIF(input.Plancontroltype > 0, u > u.Plancontroltype (DnjqPlancontroltype)input.Plancontroltype) 这里面用等于的时候,返回结果一条数据都没有。 上图中生成的SQL如下: SELECT id AS Id ,code AS …...

Pandas 技术解析:从数据结构到应用场景的深度探索
序 我最早用Python做大数据项目时,接触最早的就是Pandas了。觉得对于IT技术人员而言,它是可以属于多场景的存在,因为它的本身就是数据驱动的技术生态中,对于软件工程师而言,它是快速构建数据处理管道的基石࿱…...

数据库系统概论(十七)超详细讲解数据库规范化与五大范式(从函数依赖到多值依赖,再到五大范式,附带例题,表格,知识图谱对比带你一步步掌握)
数据库系统概论(十七)超详细讲解数据库规范化与五大范式(从函数依赖到多值依赖,再到五大范式,附带例题,表格,知识图谱对比带你一步步掌握) 前言一、为什么需要规范化1. 我们先想一个…...
[c#]判定当前软件是否用管理员权限打开
有时一些软件的逻辑中需要使用管理员权限对某些文件进行修改时,那么该软件在执行或者打开的场合,就需要用使用管理员身份运行才能达到效果。那么在c#里,如何判定该软件是否是对管理员身份运的呢? 1.取得当前的windows用户。 2.取得…...

并发编程实战(生产者消费者模型)
在并发编程中使用生产者和消费者模式能够解决绝大多数的并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。 生产者和消费者模式: 在线程的世界中生产者就是产生数据的线程,而消费者则是消费数据的线程。在多线程开…...
分布式微服务系统架构第144集:FastAPI全栈开发教育系统
加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ 使用docker搭建常用开发环境 docker安装mysql docker ru…...
el-tabs 切换时数据不更新的问题
最近业务需求,需要在页面中使用tabs,使用过程中出现tabs切换,数据不更新的问题,以下是思路和解决办法。 Vue 会追踪你在模板中绑定的数据,并在数据发生变化时重新渲染相应的部分。但在使用 el-tabs 时,有时…...

git小乌龟不显示图标状态解决方案
第一步 在开始菜单的搜索处,输入regedit命令,打开注册表。 第二步 在注册表编辑器中,找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers 这一项。 第三步 让Tortoise相关的项目排在前…...

获取 OpenAI API Key
你可以按照以下步骤来获取 openai.api_key,用于调用 OpenAI 的 GPT-4、DALLE、Whisper 等 API 服务: 🧭 获取 OpenAI API Key 的步骤: ✅ 1. 注册或登录 OpenAI 账号 打开 https://platform.openai.com/ 使用你的邮箱或 Google/…...

【Android基础回顾】五:AMS(Activity Manager Service)
Android 的 AMS(Activity Manager Service)是 Android 系统中的核心服务之一,负责管理整个应用生命周期、任务栈、进程和四大组件(Activity、Service、BroadcastReceiver、ContentProvider)的运行。它运行在系统进程 s…...

pycharm中提示C++ compiler not found -- please install a compiler
1.最近用pycharm编译一个开源库,编译的依赖c compiler 2.单单使用pycharm编译,编译器报错C compiler not found – please install a compiler 3.需要在配置环境中引入对应库 4.从新编译后没有提示:C compiler not found – please install a compiler错误。...
类型别名与类型自动推导
类型别名与类型的自动推导 类型别名 为什么要引入类型别名? 为了给类型赋予特殊含义或便于使用 典型用途 (1)增强代码可移植性 例如:size_t (在不同系统中可能是unsigned int 或 unsigned long) 首先是…...

一站式直播工具:助力内容创作者高效开启直播新时代
近年来,随着互联网技术的不断进步和短视频、直播行业的爆发式增长,越来越多的企业和个人投入到直播电商、互动娱乐、在线教育等场景。直播运营过程中,涉及到数据统计、弹幕互动、流程自动化、内容同步等诸多环节。如何提升运营效率、减少人工…...
【学习笔记】Lamba表达式[匿名函数]
【学习笔记】Lamba表达式[匿名函数] Lamba表达式格式函数模板Lamba表达式例子 Lamba表达式格式 格式: [捕获列表](参数列表) -> 返回类型 { 函数体 }1、捕获列表:指定如何访问外部变量(如 [&x] 引用捕获,[x] 值捕获&#…...
学习笔记(26):线性代数-张量的降维求和,简单示例
学习笔记(26):线性代数-张量的降维求和,简单示例 1.先理解 “轴(Axis)” 的含义 张量的 “轴” 可以理解为 维度的方向索引 。对于形状为 (2, 3, 4) 的张量,3 个轴的含义是: 轴 0(axis0&…...

以智能管理为基础,楼宇自控打造建筑碳中和新路径
在全球气候变化的严峻形势下,“碳中和”已成为各国发展的重要战略目标。建筑行业作为能源消耗与碳排放的“大户”,其运行阶段的能耗占全社会总能耗近40%,碳排放占比与之相当,实现建筑碳中和迫在眉睫。传统建筑管理模式下ÿ…...
81 实战一:给root目录扩容
添加一块100G硬盘 vgextend centos /dev/sdb1 /dev/sdc lvextend -L +120G /dev/centos/root xfs_growfs /dev/centos/root df -h 看是否扩容成功 82 实战二:给swap空间扩容 添加一块20G硬盘 fdisk -l 可以看到新添加的硬盘 vgextend centos /dev/sdd …...
1130 - Host ‘xxx.x.xx.xxx‘is not allowed to connect to this MySQL server
以下为本次问题的解决办法: 1、暂停mysql容器: docker stop mysql 2、删除mysql容器:docker rm mysql 3、查看mysql容器是否被删除:docker ps -a #没有mysql容器就是删除成功 4、run mysql容器: docker run -d --…...
HttpURLConnection实现
我有一个接口 http://ip:port/Others/airportnew/,采用post方法调用,采用body方式传值,其body内容为{"data": {"data": {"image": ""}} },现在我需要在java中调用这个接口,帮…...

day029-Shell自动化编程-计算与while循环
文章目录 1. read 交互式初始化变量1.1 案例-安装不同的软件1.2 案例-比较大小 2. 计算2.1 bc2.2 awk2.3 expr2.4 let2.5 案例-计算内存的空闲率2.6 案例-检查域名过期时间和https整数过期时间 3. 循环3.1 循环控制语句3.2 for循环-c语言格式3.3 while循环3.3.1 案例-猜数字3.3…...

Linux命令基础(2)
su和exit命令 可以通过su命令切换到root账户 语法:su [-] 用户名 -符号是可选的,表示是否在切换用户后加载环境变量,建议带上 参数:用户名,表示要切换的用户,用户名可以省略,省略表示切换到ro…...