17. STM32F103C8T6定时器3中断实现1秒LED闪烁实战

📅 发布时间:2026/7/4 12:25:27 👁️ 浏览次数:
17. STM32F103C8T6定时器3中断实现1秒LED闪烁实战
17. STM32F103C8T6定时器3中断实现1秒LED闪烁实战很多刚开始玩STM32的朋友一听到“定时器中断”就觉得头大寄存器、分频、重装载值这些名词听起来就复杂。其实啊定时器中断是嵌入式开发里最常用、也最实用的功能之一。今天咱们就以最经典的STM32F103C8T6为例手把手教你用定时器3TIM3的中断功能实现一个精准的1秒LED闪烁。学完这个你就能轻松搞定各种需要定时执行的任务了比如定时采集传感器数据、定时发送报文等等。我会把整个配置过程掰开揉碎了讲从时钟怎么来的到参数怎么算再到中断函数怎么写保证你跟着做一遍就能完全掌握。1. 定时器中断是干什么的为什么需要它在开始动手之前咱们先搞明白为什么要用定时器中断。想象一下你想让一个LED灯每隔1秒精确地亮灭一次。如果用最简单的Delay函数死等在延时期间单片机就卡在那里什么都干不了这显然很浪费CPU资源。定时器中断就是为了解决这个问题的。它就像一个独立工作的“闹钟”。你设置好1秒的“闹铃”然后CPU就可以去干别的主程序任务了。等1秒时间一到“闹钟”定时器就会“叮”的一声产生中断CPU暂时放下手头的工作赶紧去处理一下中断里的事情比如翻转一下LED处理完立刻又回去继续干原来的活。这样CPU的利用率就大大提高了程序也能实现更复杂的功能。STM32F103C8T6有好几个定时器功能有强有弱但实现基本的定时中断是所有定时器都具备的“基本功”。今天我们就用TIM3来练手。2. 第一步打开定时器的时钟开关要让定时器工作第一步就是给它供电也就是开启时钟。STM32的外设很多为了省电默认都是关着的用哪个才开哪个。TIM3挂载在APB1总线上。这里有个非常重要的知识点也是新手最容易搞错的地方TIM3的时钟源不是APB1总线时钟的36MHz而是72MHz为什么看下面的流程就明白了系统主时钟SYSCLK是72MHz。AHB总线时钟也是72MHz。APB1总线时钟是AHB的2分频所以是72MHz / 2 36MHz。但是STM32设计了一个“倍频器”如果APB1的分频系数是1即没分频则定时器时钟直接等于APB1时钟如果APB1的分频系数不是1比如我们这里的2分频那么定时器时钟会在APB1时钟基础上再×2。所以TIM3的实际工作时钟是36MHz × 2 72MHz。这个72MHz是我们后面计算定时参数的基础一定要记牢。用代码开启时钟非常简单就一行RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能TIM3的时钟3. 第二步配置定时器的心脏——时间参数时钟有了接下来就要告诉定时器你想让它隔多久“响一次铃”。这需要配置两个核心参数预分频器Prescaler和自动重装载值Period。你可以把它们理解成调整“闹钟”精度的两个旋钮预分频器PSC把72MHz的“高速时钟”进行分频得到一个适合计数的“慢速时钟”。比如分频7200倍得到10kHz的计数时钟。自动重装载值ARR计数器数多少个数产生一次中断。比如设置数10000个数中断一次。定时时间的计算公式是定时时间 (ARR 1) * (PSC 1) / 定时器时钟频率我们的目标是1秒时钟是72MHz72,000,000 Hz。为了计算方便我们可以这样设计先将72MHz分频PSC为10kHz。PSC 7200 - 1。因为分频系数 PSC 1所以7200分频后计数时钟 72,000,000 / 7200 10,000 Hz (10kHz)。也就是说计数器每1/10000秒0.1毫秒计一个数。然后让计数器数10000个数。ARR 10000 - 1。因为计数器从0数到ARR总共是ARR1个数。代入公式定时时间 (10000) * (7200) / 72,000,000 72,000,000 / 72,000,000 1秒。完美除了这两个参数我们还需要设置计数模式和时钟分频用于数字滤波基本定时用默认值即可。来看具体的配置代码TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 定义定时器初始化结构体 /* 配置定时器基础参数 */ TIM_TimeBaseStructure.TIM_Period 10000 - 1; // 自动重装载值决定计数的上限 TIM_TimeBaseStructure.TIM_Prescaler 7200 - 1; // 预分频值将72MHz分频为10kHz TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; // 时钟分频默认DIV1即可 TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; // 向上计数模式从0数到ARR TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // 将配置写入TIM3寄存器注意TIM_Period和TIM_Prescaler赋值时为什么要减1这是因为寄存器是从0开始计数的。设置Period9999表示计数器从0数到9999总共10000个数。预分频器同理。4. 第三步配置中断的“排队规则”优先级定时器参数设好了但它怎么通知CPU呢靠中断。STM32的中断有很多比如串口收到数据、按键按下、定时器时间到都会产生中断。当多个中断同时发生时谁先处理这就需要一个“排队规则”也就是中断优先级。STM32的中断优先级分为抢占优先级和子优先级抢占优先级高抢占优先级的中断可以打断正在执行的低抢占优先级的中断嵌套。就像急诊病人可以插队普通门诊。子优先级当两个中断的抢占优先级相同时比较子优先级高的先执行。如果连子优先级也相同那就比较它们的硬件中断号。我们首先需要设置整个系统的中断分组通常在主函数初始化时只设置一次这里假设我们已经设置成分组22位抢占优先级2位子优先级。然后配置TIM3自身的中断优先级。NVIC_InitTypeDef NVIC_InitStructure; // 定义NVIC初始化结构体 NVIC_InitStructure.NVIC_IRQChannel TIM3_IRQn; // 指定要配置的是TIM3的中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; // 抢占优先级设为0较高 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // 子优先级设为1 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; // 使能这个中断通道 NVIC_Init(NVIC_InitStructure); // 将配置写入NVIC寄存器5. 第四步打开中断开关并启动定时器配置好了“排队规则”还得把定时器的“中断输出”和“总开关”打开。使能更新中断告诉TIM3当计数器溢出更新事件时要产生中断信号。启动定时器让TIM3的计数器开始按照我们设定的参数运行。TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // 使能TIM3的“更新”中断 TIM_Cmd(TIM3, ENABLE); // 启动TIM3定时器计数器开始跑到这里硬件层面的配置就全部完成了。定时器已经开始默默计时每过1秒它就会向CPU申请一次中断。6. 第五步编写中断服务函数——中断来了干什么中断服务函数ISR是中断发生后CPU要去执行的函数。它的函数名是固定的由启动文件定义我们不能乱改。TIM3的中断服务函数名就是TIM3_IRQHandler。在这个函数里我们通常要做三件事检查中断源判断是不是我们关心的那个中断事件比如更新中断触发了。执行中断任务做我们想做的事比如翻转LED。清除中断标志非常重要告诉硬件这个中断我已经处理完了否则它会一直认为中断未处理导致程序不断跳进中断函数卡死在里面。警告中断服务函数要求执行速度越快越好避免影响其他中断和主程序。像printf这种耗时很长的函数绝对不要放在正式项目的中断里这里只是为了演示。假设我们的LED接在GPIOC的Pin13上像很多最小系统板上的用户LED一样。我们用一个全局变量flag来记录LED当前的状态。// 定义一个全局变量用于记录LED状态 uint8_t flag 0; void TIM3_IRQHandler(void) // 固定的中断函数名 { // 1. 检查是否是“更新”中断触发了 if (TIM_GetITStatus(TIM3, TIM_IT_Update) SET) { // 2. 执行中断任务翻转LED if(flag 0) { GPIO_SetBits(GPIOC, GPIO_Pin_13); // LED灭假设低电平点亮 flag 1; } else { GPIO_ResetBits(GPIOC, GPIO_Pin_13); // LED亮 flag 0; } // 可以在这里通过串口发送一个简单的字符如A来调试但不要用printf // USART_SendData(USART1, A); } // 3. 清除TIM3的更新中断标志位等待下一次中断 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); }7. 把代码跑起来看看效果将上面的代码片段整合到你的工程里别忘了在主函数初始化中配置好GPIOC Pin13为推挽输出模式并初始化串口如果你用了调试输出。编译、下载到地阔星STM32F103C8T6开发板。上电后你应该能看到板载的LED灯以非常精确的1秒间隔稳定地闪烁。几个常见的坑点时钟算错这是最大的坑一定要反复确认你的定时器实际时钟是多少。对于APB1总线上的定时器如果APB1预分频系数≠1时钟是要×2的。忘了清除中断标志后果很严重程序会不断进入中断主程序根本得不到执行。中断服务函数名写错名字必须和启动文件里定义的一模一样否则中断发生了也找不到这个函数。在中断里做耗时操作比如延时、复杂的计算、打印大量信息这会阻塞系统。搞定这个1秒定时闪烁你就掌握了STM32定时器中断最核心的流程。以后无论用定时器做PWM、输入捕获还是高级定时功能这套配置的骨架都是相通的。多动手试几次把这些步骤变成你的肌肉记忆嵌入式开发的路就走宽了。