基于STM32定时器与中断的精准秒表设计与实现

张开发
2026/4/5 1:47:59 15 分钟阅读

分享文章

基于STM32定时器与中断的精准秒表设计与实现
1. 项目背景与核心功能做嵌入式开发的朋友应该都接触过定时器这个神器尤其是STM32的定时器模块功能强大到让人爱不释手。今天我要分享的是一个基于STM32定时器和中断系统的精准秒表项目这个项目特别适合刚入门嵌入式开发的同学们练手。别看它功能简单但包含了定时器配置、中断优先级管理、按键消抖、状态机控制等嵌入式开发的经典知识点。这个秒表的核心功能其实很直观用两个数码管显示00-99秒的计时通过三个按键实现开始、暂停和复位功能。我在实际项目中发现很多初学者容易在定时器配置和中断优先级管理上栽跟头导致计时不准或者按键响应不及时。比如有个学员就遇到过按下开始键后要等半秒才有反应的情况这就是典型的优先级配置问题。2. 硬件设计要点2.1 主控芯片选型我选择了STM32F103C8T6这款芯片江湖人称蓝莓派性价比高得离谱。它内置了多达11个定时器包括2个高级定时器、4个通用定时器和2个基本定时器还有2个看门狗定时器和1个系统定时器。对于我们的秒表项目来说随便挑一个通用定时器就够用了。这里有个小技巧如果你手头有其他STM32系列开发板也没关系只要带基本定时器功能的都可以用。我试过用STM32F030和STM32G071做这个项目代码稍作修改就能跑起来。2.2 数码管驱动电路我用的是两个一位共阳数码管这种数码管每个数字由7个LED组成加上小数点就是8个公共端接VCC。控制原理很简单给对应的段低电平就会点亮。比如要显示数字1只需要点亮b和c段。实际接线时有个坑要注意数码管的限流电阻不能少我见过不少初学者直接接IO口把数码管烧了的案例。建议每个段都串联一个220Ω的电阻这样既保证亮度又安全。2.3 按键电路设计三个按键分别对应开始、暂停和复位功能。这里我采用了上拉输入模式按键另一端接地。当按键按下时IO口会检测到低电平。为了节省IO资源我把开始和暂停按键配置成了外部中断触发复位键则采用轮询方式检测。3. 软件实现详解3.1 定时器配置关键步骤定时器是秒表的核心配置时要特别注意时钟源和分频系数。我用的是TIM2通用定时器配置过程分为几个关键步骤开启定时器时钟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; TIM_TimeBaseInitStructure.TIM_Prescaler 7200 - 1; TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStructure);这里有个计算公式要记住定时时间 (Period 1) × (Prescaler 1) / 时钟频率。我用的72MHz主频通过7200分频得到10kHz然后计数到10000就是1秒。3.2 中断优先级管理秒表项目涉及到两种中断外部中断按键和定时器中断。合理的优先级设置非常重要否则会出现按键响应延迟的问题。我的经验是外部中断优先级高于定时器中断开始按键优先级高于暂停按键复位功能不需要中断用轮询即可具体配置代码如下// 外部中断配置 NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // 子优先级 // 定时器中断配置 NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;3.3 按键消抖实现机械按键最大的问题就是抖动如果不处理会导致多次触发中断。我采用了最简单的延时消抖法if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) 0) { Delay_ms(20); // 延时20ms跳过抖动期 while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) 0); // 等待按键释放 Delay_ms(20); // 再次消抖 Timer_Init(); // 启动定时器 }在实际测试中我发现20ms的延时对于大多数按键都够用了。如果遇到特别劣质的按键可以适当延长到30-50ms。3.4 数码管显示控制数码管显示我用了最直接的静态驱动方式虽然占用了较多IO口但胜在稳定可靠。每个数字对应一个switch-case分支switch(i) { case 0: GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7); GPIO_SetBits(GPIOA,GPIO_Pin_3); break; case 1: GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_7); GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5); break; // 其他数字类似... }这里有个优化技巧如果IO口紧张可以考虑用74HC595这类移位寄存器来驱动数码管只需要3个IO口就能控制多个数码管。4. 常见问题与调试技巧4.1 定时器不准怎么办遇到定时不准的问题首先要检查时钟树配置。确认系统时钟是否配置正确比如72MHz定时器时钟源是否正确分频系数和重装载值计算是否正确可以用示波器测量定时器中断引脚的电平变化看周期是否准确。如果没条件用示波器可以改成闪烁LED的方式粗略判断。4.2 按键反应迟钝按键反应慢通常有三个原因中断优先级设置不合理导致中断被阻塞消抖延时过长主循环中有耗时操作建议先用逻辑分析仪抓取按键波形确认是硬件问题还是软件问题。我曾经遇到过一个案例是因为在中断服务函数中做了浮点运算导致中断响应变慢。4.3 数码管显示乱码数码管显示不正常时先检查共阳/共阴类型是否匹配段码表是否正确IO口模式是否配置为推挽输出限流电阻是否合适有个快速排查的方法单独测试每个段是否能正常点亮。比如让所有数码管显示8看是否所有段都亮。5. 项目优化与扩展基础功能实现后可以考虑以下几个优化方向改用动态扫描方式驱动数码管节省IO资源增加毫秒级计时功能使用更高精度的定时器添加蜂鸣器提示音在计时完成时提醒通过串口将计时数据上传到PC增加多组计时记忆功能我在实际项目中尝试过增加无线传输模块把计时数据实时发送到手机APP上显示。这个扩展很有意思但需要额外学习蓝牙或Wi-Fi模块的使用。

更多文章