跳到主要内容

【十】定时器查询方式实验(TIM1/2/3)

1. 项目概述

本项目演示如何使用 STM32F103C8T6 微控制器的3个定时器(TIM1、TIM2、TIM3)通过查询方式实现LED闪烁功能。系统在主循环中不断查询定时器更新标志位,当定时器到达设定时间后,主动检测标志位并控制LED状态翻转,展示了定时器轮询工作模式。

技术要点

  • ✅ TIM1高级定时器配置
  • ✅ TIM2/TIM3通用定时器配置
  • ✅ 定时器标志位查询
  • ✅ 主循环轮询检测
  • ✅ 无需NVIC中断配置
  • ✅ LED状态控制
  • ✅ 软件标志位翻转
  • ✅ 查询方式编程模式

与中断方式对比

特性查询方式(本项目)中断方式(6C项目)
CPU占用高(一直查询)低(按需响应)
响应速度中(取决于循环周期)快(立即响应)
实时性一般
编程复杂度简单稍复杂
NVIC配置不需要需要
适用场景简单应用、单任务复杂应用、多任务

应用场景

  1. ⏱️ 简单定时任务
  2. ⏱️ LED闪烁指示
  3. ⏱️ 教学演示
  4. ⏱️ 单任务系统
  5. ⏱️ 低中断系统
  6. ⏱️ 时间测量

2. 硬件平台

主控芯片

  • 型号: STM32F103C8T6
  • 内核: ARM Cortex-M3
  • 主频: 72 MHz
  • Flash: 64 KB
  • SRAM: 20 KB
  • 定时器: TIM1-TIM4

外设资源

定时器配置

定时器类型定时时间查询方式控制LED
TIM1高级定时器500ms主循环查询LED6
TIM2通用定时器500ms主循环查询LED7
TIM3通用定时器1000ms (1s)主循环查询LED8

LED指示灯

LED引脚闪烁频率定时器工作方式
LED6PB51Hz (500ms开/500ms关)TIM1查询标志位
LED7PB61Hz (500ms开/500ms关)TIM2查询标志位
LED8PB70.5Hz (1s开/1s关)TIM3查询标志位

硬件连接

STM32F103C8T6              LED指示灯
PB5 ----------> LED6 (TIM1查询,500ms翻转)
PB6 ----------> LED7 (TIM2查询,500ms翻转)
PB7 ----------> LED8 (TIM3查询,1s翻转)

查询方式工作示意图:

主循环工作流程:
┌─────────────────────────────────────────┐
│ while(1) │
│ { │
│ 查询TIM1标志位 → 处理 → 清除标志位 │
│ ↓ │
│ 查询TIM2标志位 → 处理 → 清除标志位 │
│ ↓ │
│ 查询TIM3标志位 → 处理 → 清除标志位 │
│ ↓ │
│ 返回循环开始 │
│ } │
└─────────────────────────────────────────┘

时间线:
0s 0.5s 1s 1.5s 2s
↓ ↓ ↓ ↓ ↓
TIM1查询 ✓ ✓ ✓ ✓ ✓ (LED6翻转)
TIM2查询 ✓ ✓ ✓ ✓ ✓ (LED7翻转)
TIM3查询 ✓ ✓ ✓ (LED8翻转)

注意事项:

  1. 无需配置NVIC中断
  2. 主循环持续运行查询标志位
  3. CPU不能休眠或执行其他长时间任务
  4. PB5-PB7需要禁用JTAG功能

3. 项目结构

6B_TIM1_2_3(查询)/
├── Libraries/ # STM32标准外设库
│ ├── CMSIS/ # Cortex微控制器软件接口标准
│ └── STM32F10x_StdPeriph_Driver/ # STM32F10x标准外设驱动
├── MDK/ # Keil工程文件
│ ├── TEST_CODE.uvproj # Keil项目文件
│ ├── TEST_CODE.uvopt # 项目配置选项
│ ├── Output/ # 编译输出目录
│ └── list/ # 列表文件目录
├── Readme/ # 说明文档目录
└── User/ # 用户代码目录
├── main.c # 主程序入口(含查询逻辑)
├── stm32f10x_conf.h # 外设库配置文件
├── stm32f10x_it.c # 中断服务函数(本项目未使用)
├── stm32f10x_it.h # 中断服务函数头文件
└── TF_APP/ # 应用层驱动
├── TF_Timer.c # 定时器驱动实现
├── TF_Timer.h # 定时器驱动头文件
├── TF_LED.c # LED驱动实现
└── TF_LED.h # LED驱动头文件

4. 功能说明

系统工作流程

系统上电

LED初始化
(PB5-PB7配置为推挽输出)

TIM1初始化(500ms定时)
├─ 使能TIM1时钟
├─ 配置预分频器:7200-1
├─ 配置自动重载值:5000-1
├─ 使能更新中断(仅标志位)
└─ 启动TIM1

TIM2初始化(500ms定时)
├─ 使能TIM2时钟
├─ 配置预分频器:7200-1
├─ 配置自动重载值:5000-1
├─ 使能更新中断(仅标志位)
└─ 启动TIM2

TIM3初始化(1s定时)
├─ 使能TIM3时钟
├─ 配置预分频器:7200-1
├─ 配置自动重载值:1000-1
├─ 使能更新中断(仅标志位)
└─ 启动TIM3

主循环开始

┌─────────────────────────┐
│ 查询TIM1更新标志位 │
│ ├─ 标志位置位? │
│ │ ├─ 是:清除标志位 │
│ │ │ 翻转flag1 │
│ │ │ 控制LED6 │
│ │ └─ 否:继续 │
│ │
│ 查询TIM2更新标志位 │
│ ├─ 标志位置位? │
│ │ ├─ 是:清除标志位 │
│ │ │ 翻转flag2 │
│ │ │ 控制LED7 │
│ │ └─ 否:继续 │
│ │
│ 查询TIM3更新标志位 │
│ ├─ 标志位置位? │
│ │ ├─ 是:清除标志位 │
│ │ │ 翻转flag3 │
│ │ │ 控制LED8 │
│ │ └─ 否:继续 │
└─────────┬───────────────┘

└─ 返回循环开始

功能特点

  1. 查询方式工作:

    • 主循环持续运行
    • 主动查询标志位
    • 无中断触发
    • CPU一直忙碌
  2. 定时器配置:

    • TIM1/TIM2:500ms
    • TIM3:1000ms
    • 使能更新中断(仅用于标志位)
    • 不配置NVIC
  3. LED控制逻辑:

    每次定时器到达:
    1. 检测到标志位置位
    2. 清除标志位
    3. 翻转软件标志(flag)
    4. 根据flag控制LED开关
  4. 简单实现:

    • 无需NVIC配置
    • 无需中断服务函数
    • 主循环逻辑简单
    • 适合初学者
  5. LED闪烁效果:

    LED6: ━━━━━━━━        ━━━━━━━━        ━━━━━━━━  (1Hz)
    500ms off 500ms on 500ms off

    LED7: ━━━━━━━━ ━━━━━━━━ ━━━━━━━━ (1Hz)
    500ms off 500ms on 500ms off

    LED8: ━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━ (0.5Hz)
    1s off 1s on

5. 查询方式与中断方式对比

详细对比表

特性查询方式(本项目)中断方式(6C项目)
工作原理主循环主动查询标志位硬件触发中断
CPU占用率100%(一直查询)低(仅中断时)
响应时间取决于循环周期微秒级(立即)
实时性差(可能漏检)好(硬件保证)
功耗高(CPU不能休眠)低(可休眠)
编程复杂度简单(无中断)中等(需NVIC)
NVIC配置不需要必须配置
ISR函数不需要必须编写
适用场景单任务、简单应用多任务、实时应用
漏检风险有(循环慢时)无(硬件保证)
代码量稍多
调试难度简单稍难

代码对比

// 查询方式(本项目)
int main(void)
{
// 初始化
LED_Init();
TIM1_Configuration();

while(1)
{
// 主循环查询
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);
// 处理LED逻辑
flag1 = ~flag1;
if(flag1 == 0) LED_On(6);
else LED_Off(6);
}
}
}

// 中断方式(6C项目)
int main(void)
{
// 初始化
NVIC_Configuration(); // 需要配置NVIC
LED_Init();
TIM1_Configuration();

while(1)
{
// 主循环空转,中断自动处理
;
}
}

void TIM1_UP_IRQHandler(void) // 需要中断服务函数
{
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
flag1 = ~flag1;
if(flag1 == 0) LED_On(6);
else LED_Off(6);
}
}

CPU占用对比

查询方式CPU占用:
CPU ████████████████████████████████ 100%
↑主循环一直在查询,CPU满载

中断方式CPU占用:
CPU ██─────────────────██────────── <10%
↑只在定时器到达时占用CPU,其他时间空闲

响应时间对比

查询方式响应时间:
定时器到达 → 等待主循环查询到 → 处理
↑ 延迟(取决于循环周期)

中断方式响应时间:
定时器到达 → 立即中断 → 处理
↑ 延迟(微秒级)

6. 代码详解

6.1 主程序 (main.c)

完整代码

#include "TF_LED.h"
#include "TF_Timer.h"

unsigned char flag1 = 0;
unsigned char flag2 = 0;
unsigned char flag3 = 0;

int main (void)
{
LED_Init(); // 初始化LED所对应IO口
TIM1_Configuration(); // 初始化定时器1,设定定时500ms
TIM2_Configuration(); // 初始化定时器2,设定定时500ms
TIM3_Configuration(); // 初始化定时器3,设定定时500ms

while(1) // 程序循环
{
// 查询TIM1更新标志位
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); // 必须清除标志位

flag1 = ~flag1; // 翻转标志1

if(flag1 == 0)
{
LED_On(6); // 点亮LED6
}
else
{
LED_Off(6); // 熄灭LED6
}
}

// 查询TIM2更新标志位
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);

flag2 = ~flag2; // 翻转标志2

if(flag2 == 0)
{
LED_On(7);
}
else
{
LED_Off(7);
}
}

// 查询TIM3更新标志位
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_FLAG_Update);

flag3 = ~flag3; // 翻转标志3

if(flag2 == 0) // Bug:应该是flag3
{
LED_On(8);
}
else
{
LED_Off(8);
}
}
}
}

主函数要点:

  1. 初始化顺序:

    LED_Init();           // 先初始化LED
    TIM1_Configuration(); // 再初始化定时器
    TIM2_Configuration();
    TIM3_Configuration();

    注意:
    - 无需NVIC_Configuration()
    - 不配置中断优先级
  2. 主循环查询:

    while(1)
    {
    // 持续查询TIM1
    if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
    {
    // 处理...
    }

    // 持续查询TIM2
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
    // 处理...
    }

    // 持续查询TIM3
    if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
    // 处理...
    }
    }

    特点:
    - 三个if语句顺序执行
    - CPU一直在查询
    - 不能休眠
  3. 标志位处理:

    if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
    {
    TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); // 必须清除

    flag1 = ~flag1; // 翻转标志

    if(flag1 == 0)
    LED_On(6);
    else
    LED_Off(6);
    }

    流程:
    1. 检查标志位是否置位
    2. 清除标志位(防止重复处理)
    3. 翻转软件标志
    4. 根据标志控制LED
  4. 代码Bug:

    // TIM3查询逻辑中
    if(flag2 == 0) // ❌ 错误:应该是flag3

    // 应该修改为:
    if(flag3 == 0) // ✅ 正确

6.2 定时器驱动 (TF_Timer)

TF_Timer.h - 头文件

#ifndef  __TF_TIMER_H__
#define __TF_TIMER_H__

#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
#include "misc.h"

// 函数声明
extern void TIM1_Configuration(void); // 初始化TIM1--设定定时500ms
extern void TIM2_Configuration(void); // 初始化TIM2--设定定时500ms
extern void TIM3_Configuration(void); // 初始化TIM3--设定定时1s

#endif

TIM1定时器配置

void TIM1_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

// 使能TIM1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

// 定时时间计算:
// T = ((1+TIM_Prescaler)/72M) * (1+TIM_Period)
// T = ((1+7199)/72M) * (1+4999) = 0.5秒
TIM_TimeBaseStructure.TIM_Period = (5000-1); // 自动重载值
TIM_TimeBaseStructure.TIM_Prescaler = (7200-1); // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // 重复计数器(TIM1特有)
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

TIM_ClearITPendingBit(TIM1, TIM_IT_Update); // 预先清除所有中断位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); // 使能TIM1更新中断
TIM_Cmd(TIM1, ENABLE); // 允许TIM1开始计数
}

TIM1配置要点:

  1. 使能更新中断的作用:

    TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);

    说明:
    - 虽然使能了更新中断
    - 但没有配置NVIC
    - 不会触发中断服务函数
    - 仅用于设置更新标志位(UIF)
  2. 查询方式关键点:

    // 配置定时器时:
    TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); // 使能更新事件

    // 但不配置NVIC:
    // NVIC_Configuration(); ← 不调用这个函数

    // 主循环中查询:
    if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
    {
    // 检测到定时器更新
    }

TIM2定时器配置

void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

// 使能TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

// T = ((1+7199)/72M) * (1+4999) = 0.5秒
TIM_TimeBaseStructure.TIM_Period = (5000-1);
TIM_TimeBaseStructure.TIM_Prescaler = (7200-1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_ARRPreloadConfig(TIM2, ENABLE); // 使能预装载
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}

TIM3定时器配置

void TIM3_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

// 使能TIM3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

// T = ((1+7199)/72M) * (1+999) = 0.1秒(注释错误,应为1秒需ARR=9999)
TIM_TimeBaseStructure.TIM_Period = (1000-1); // 实际100ms
TIM_TimeBaseStructure.TIM_Prescaler = (7200-1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}

定时器配置要点:

  1. 与中断方式的区别:
    查询方式:
    - 配置定时器 ✓
    - 使能更新中断 ✓(仅用于标志位)
    - 配置NVIC ✗(不需要)
    - 编写ISR ✗(不需要)
    - 主循环查询 ✓

    中断方式:
    - 配置定时器 ✓
    - 使能更新中断 ✓
    - 配置NVIC ✓(必须)
    - 编写ISR ✓(必须)
    - 主循环空转 ✓

6.3 LED驱动 (TF_LED)

LED驱动代码与其他项目相同,参见LED初始化和控制函数。


7. 定时器标志位查询原理

更新标志位(UIF)

定时器更新标志位工作原理:
┌─────────────────────────────────────────┐
│ 定时器计数过程 │
│ │
│ CNT=0 → CNT++ → ... → CNT=ARR │
│ ↓ │
│ 产生更新事件(UEV) │
│ ↓ │
│ 设置UIF标志位=1 │
│ ↓ │
│ ┌───────────────┴────────┐ │
│ │ │ │
│ NVIC使能? │ │
│ ├─ 是 → 触发中断(中断方式) │ │
│ └─ 否 → 仅置位标志(查询方式) │← 本项目
│ │
└─────────────────────────────────────────┘

标志位检查与清除:
主循环:
while(1)
{
// 1. 检查标志位
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
// 2. 清除标志位(必须!)
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);

// 3. 执行处理逻辑
// ...
}
}

标志位查询函数

// 检查标志位
FlagStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
// 读取状态寄存器(SR)
// 返回指定标志位状态(SET或RESET)
}

// 清除标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
// 清除状态寄存器(SR)中的标志位
// 防止重复检测
}

使用示例:
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); // 立即清除
// 处理逻辑...
}

查询时序图

定时器查询时序:
时间 →
主循环 TIM1到达 主循环查询 处理 主循环 TIM1到达
│ │ │ │ │ │
├───────┤ │ │ │ │
│ 检查 │ │ │ │ │
│ 标志 │ │ │ │ │
│ 位=0 │ │ │ │ │
│ ├─────────┤ │ │ │
│ │UIF=1 │ │ │ │
│ │ ├────────┤ │ │
│ │ │检测到 │ │ │
│ │ │UIF=1 │ │ │
│ │ │ ├──────┤ │
│ │ │ │清除 │ │
│ │ │ │UIF=0 │ │
│ │ │ │处理 │ │
│ │ │ │ │ │
├───────┴─────────┴────────┴──────┴───────┤

延迟:
- 定时器到达 → 主循环查询到
- 延迟时间取决于主循环周期
- 查询方式的主要缺点

8. 查询方式的优缺点

优点

✅ 优点:
┌────────────────────────────────────────┐
│ 1. 编程简单 │
│ - 无需配置NVIC │
│ - 无需编写中断服务函数 │
│ - 主循环逻辑清晰 │
│ │
│ 2. 容易理解 │
│ - 适合初学者 │
│ - 代码流程直观 │
│ - 调试简单 │
│ │
│ 3. 无中断嵌套 │
│ - 避免中断优先级问题 │
│ - 无中断冲突 │
│ - 时序可预测 │
│ │
│ 4. 灵活控制 │
│ - 可随时改变查询顺序 │
│ - 可动态enable/disable查询 │
└────────────────────────────────────────┘

缺点

❌ 缺点:
┌────────────────────────────────────────┐
│ 1. CPU占用高 │
│ - 主循环一直运行 │
│ - CPU不能休眠 │
│ - 功耗高 │
│ │
│ 2. 响应延迟 │
│ - 取决于主循环周期 │
│ - 可能漏检快速事件 │
│ - 实时性差 │
│ │
│ 3. 资源浪费 │
│ - 大量时间在查询标志位 │
│ - CPU利用率低 │
│ - 不适合多任务 │
│ │
│ 4. 扩展性差 │
│ - 查询点越多,循环越慢 │
│ - 难以处理复杂逻辑 │
│ - 不适合复杂系统 │
└────────────────────────────────────────┘

适用场景

适合查询方式:
✓ 简单应用(单一任务)
✓ 教学演示
✓ 低中断系统
✓ 对实时性要求不高
✓ LED闪烁等简单指示

适合中断方式:
✓ 复杂应用(多任务)
✓ 实时系统
✓ 低功耗应用
✓ 需要快速响应
✓ 数据采集、通信等

9. 使用说明

9.1 硬件连接

连接步骤

  1. 连接LED

    LED6 → PB5(TIM1查询,500ms闪烁)
    LED7 → PB6(TIM2查询,500ms闪烁)
    LED8 → PB7(TIM3查询,1s闪烁)
  2. 连接调试器

    SWDIO → PA13
    SWCLK → PA14

9.2 编译与下载

编译工程

  1. 打开 MDK/TEST_CODE.uvproj
  2. 编译:Project → Build Target (F7)
  3. 检查编译结果

下载程序

  1. 配置调试器
  2. 下载程序:Flash → Download (F8)
  3. 复位运行

9.3 运行效果

预期现象:

  • LED6闪烁:500ms亮/500ms灭(1Hz)
  • LED7闪烁:500ms亮/500ms灭(1Hz)
  • LED8闪烁:100ms亮/100ms灭(实际,代码bug)
  • 三个LED独立闪烁

9.4 测试方法

测试1:查询响应

// 在主循环中添加延时测试
while(1)
{
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);
// 记录检测到标志位的时间
uint32_t detect_time = GetTick();
// 观察延迟
}

// 添加延时模拟其他任务
Delay_ms(10); // 延迟会影响查询频率
}

测试2:对比中断方式

1. 运行本项目(查询方式)
2. 运行6C项目(中断方式)
3. 对比CPU占用率和响应速度

观察:
- 查询方式:CPU一直忙碌
- 中断方式:CPU大部分时间空闲

10. 常见问题

问题1:LED不闪烁

可能原因:

  1. ❌ 标志位未清除
  2. ❌ 主循环被阻塞
  3. ❌ 定时器未启动

解决方案:

// 检查1:标志位是否清除
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); // 必须清除
// ...
}

// 检查2:主循环是否被阻塞
while(1)
{
// 避免长时间延时
// Delay_ms(10000); ← 这会阻塞查询

// 查询逻辑
if(TIM_GetITStatus(...))
{
// ...
}
}

// 检查3:定时器是否启动
TIM_Cmd(TIM1, ENABLE); // 确保启动

问题2:响应延迟

原因: 主循环周期过长

解决方案:

// 优化主循环,减少执行时间
while(1)
{
// 快速查询
if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);
flag1 = ~flag1;
if(flag1 == 0) LED_On(6);
else LED_Off(6);
}

// 避免在主循环中做耗时操作
// 如果必须,考虑使用中断方式
}

问题3:flag3的LED不正常

原因: 代码bug,判断了错误的标志位

解决方案:

// ❌ 错误代码
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_FLAG_Update);
flag3 = ~flag3;
if(flag2 == 0) // 错误:应该是flag3
{
LED_On(8);
}
}

// ✅ 正确代码
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_FLAG_Update);
flag3 = ~flag3;
if(flag3 == 0) // 修正
{
LED_On(8);
}
}

问题4:CPU占用过高

原因: 查询方式本身特点

解决方案:

如果需要降低CPU占用:
1. 改用中断方式(6C项目)
2. 使用RTOS进行任务管理
3. 添加CPU休眠(查询间隙)

// 示例:查询间隙休眠(不推荐,影响响应)
while(1)
{
// 查询所有定时器
// ...

// 短暂休眠(会延长响应时间)
__WFI(); // 等待中断
}