【十】定时器查询方式实验(TIM1/2/3)
1. 项目概述
本项目演示如何使用 STM32F103C8T6 微控制器的3个定时器(TIM1、TIM2、TIM3)通过查询方式实现LED闪烁功能。系统在主循环中不断查询定时器更新标志位,当定时器到达设定时间后,主动检测标志位并控制LED状态翻转,展示了定时器轮询工作模式。
技术要点
- ✅ TIM1高级定时器配置
- ✅ TIM2/TIM3通用定时器配置
- ✅ 定时器标志位查询
- ✅ 主循环轮询检测
- ✅ 无需NVIC中断配置
- ✅ LED状态控制
- ✅ 软件标志位翻转
- ✅ 查询方式编程模式
与中断方式对比
| 特性 | 查询方式(本项目) | 中断方式(6C项目) |
|---|---|---|
| CPU占用 | 高(一直查询) | 低(按需响应) |
| 响应速度 | 中(取决于循环周期) | 快(立即响应) |
| 实时性 | 一般 | 好 |
| 编程复杂度 | 简单 | 稍复杂 |
| NVIC配置 | 不需要 | 需要 |
| 适用场景 | 简单应用、单任务 | 复杂应用、多任务 |
应用场景
- ⏱️ 简单定时任务
- ⏱️ LED闪烁指示
- ⏱️ 教学演示
- ⏱️ 单任务系统
- ⏱️ 低中断系统
- ⏱️ 时间测量
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 | 引脚 | 闪烁频率 | 定时器 | 工作方式 |
|---|---|---|---|---|
| LED6 | PB5 | 1Hz (500ms开/500ms关) | TIM1 | 查询标志位 |
| LED7 | PB6 | 1Hz (500ms开/500ms关) | TIM2 | 查询标志位 |
| LED8 | PB7 | 0.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翻转)
注意事项:
- 无需配置NVIC中断
- 主循环持续运行查询标志位
- CPU不能休眠或执行其他长时间任务
- 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 │
│ │ └─ 否:继续 │
└─────────┬───────────────┘
│
└─ 返回循环开始
功能特点
-
查询方式工作:
- 主循环持续运行
- 主动查询标志位
- 无中断触发
- CPU一直忙碌
-
定时器配置:
- TIM1/TIM2:500ms
- TIM3:1000ms
- 使能更新中断(仅用于标志位)
- 不配置NVIC
-
LED控制逻辑:
每次定时器到达:
1. 检测到标志位置位
2. 清除标志位
3. 翻转软件标志(flag)
4. 根据flag控制LED开关 -
简单实现:
- 无需NVIC配置
- 无需中断服务函数
- 主循环逻辑简单
- 适合初学者
-
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);
}
}
}
}
主函数要点:
-
初始化顺序:
LED_Init(); // 先初始化LED
TIM1_Configuration(); // 再初始化定时器
TIM2_Configuration();
TIM3_Configuration();
注意:
- 无需NVIC_Configuration()
- 不配置中断优先级 -
主循环查询:
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一直在查询
- 不能休眠 -
标志位处理:
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 -
代码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配置要点:
-
使能更新中断的作用:
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
说明:
- 虽然使能了更新中断
- 但没有配置NVIC
- 不会触发中断服务函数
- 仅用于设置更新标志位(UIF) -
查询方式关键点:
// 配置定时器时:
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);
}
定时器配置要点:
- 与中断方式的区别:
查询方式:
- 配置定时器 ✓
- 使能更新中断 ✓(仅用于标志位)
- 配置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 硬件连接
连接步骤
-
连接LED
LED6 → PB5(TIM1查询,500ms闪烁)
LED7 → PB6(TIM2查询,500ms闪烁)
LED8 → PB7(TIM3查询,1s闪烁) -
连接调试器
SWDIO → PA13
SWCLK → PA14
9.2 编译与下载
编译工程
- 打开
MDK/TEST_CODE.uvproj - 编译:Project → Build Target (F7)
- 检查编译结果
下载程序
- 配置调试器
- 下载程序:Flash → Download (F8)
- 复位运行
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:标志位是否清除
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(); // 等待中断
}