一、题目概述
基于 STC15F2K60S2 单片机(12MHz),实现一个 电压监测与统计系统,包含:
- AD 电压采集与实时显示
- 电压阈值设置(EEPROM 掉电保存)
- 电压下降沿检测与计数
- LED 状态指示
- 非法按键错误计数
二、功能模块实现
2.1 数码管显示(3个界面,S12 切换)
| 界面 | 首位标识 | 显示内容 | 格式 |
|---|---|---|---|
| 0 | U (11) |
实时采集电压 | U X.XX |
| 1 | P (12) |
电压阈值设置 | P X.XX |
| 2 | n (13) |
下降沿计数 | n XX |
|
|
2.2 按键功能
| 按键 | 功能 | 有效界面 | 非法操作 |
|---|---|---|---|
| S12 | 切换界面(0→1→2→0) | 全局 | 不计错误 |
| S13 | 清零下降沿计数 | 界面2 | Error++ |
| S16 | 阈值+0.5V(循环) | 界面1 | Error++ |
| S17 | 阈值-0.5V(循环) | 界面1 | Error++ |
关键逻辑:切换到界面2时自动保存阈值到 EEPROM。
|
|
2.3 电压下降沿检测
采用 Flag 标志位 实现边沿检测:
|
|
|
|
2.4 LED 指示
| LED | 条件 | 含义 |
|---|---|---|
| L1 | Time5000 >= 5000 |
电压持续低于阈值超过5秒 |
| L2 | Voltage_Count % 2 == 1 |
下降沿计数为奇数 |
| L3 | Error >= 3 |
非法按键操作累计≥3次 |
三、踩坑记录(重点)
踩坑1:Time5000 == 5000 只命中 1ms
原始代码:
|
|
问题: Time5000 由 1ms 中断持续递增,等于 5000 仅持续 1ms,之后变为 5001、5002…,条件几乎不可能被主循环捕获。
修复: 使用 >= 替代 ==:
|
|
教训:对中断中持续递增的计数器,判断时必须用
>=,不能用==。
踩坑2:Time5000 没有清零逻辑
原始代码: Time5000 从上电开始只增不减,无法正确表达"持续低于阈值5秒"的含义。
修复: 在电压 ≥ 阈值时清零,重新开始计时:
|
|
教训:定时计数器必须有明确的"重置时机",否则只是一个单调递增的计数器,无法表达时间区间。
踩坑3:LED 判断被困在下降沿代码块内
原始代码:
|
|
问题: LED 状态只在下降沿发生的瞬间才更新。如果没有新的下降沿,即使条件满足(如 Error ≥ 3),LED 也不会改变。
修复: 将 LED 判断移到下降沿块外部,使其每次 Led_Proc() 调用都执行:
|
|
教训:LED/显示等"状态输出"逻辑应独立于"事件检测"逻辑,确保状态能持续刷新,不要和事件触发耦合在一起。
踩坑4:计数界面高位未熄灭(前导零问题)
问题: 计数界面(界面2)固定显示两位数,当 Voltage_Count 为个位数(如 3)时,数码管显示 03 而非 3,不符合题目要求。
原始代码:
|
|
修复: 高位为0时赋值10(熄灭码),实现前导零消隐:
|
|
教训:数码管显示数值时,高位前导零应消隐(赋值为熄灭码10),这是蓝桥杯的常见扣分点。
四、通用经验总结
| 编号 | 经验 | 适用场景 |
|---|---|---|
| 1 | 中断计数器判断用 >=,不用 == |
所有基于中断递增的定时判断 |
| 2 | 定时计数器必须有清零时机 | 超时检测、周期计时 |
| 3 | 状态输出与事件检测分离 | LED、数码管等需持续刷新的外设 |
| 4 | 大括号作用域要仔细确认 | 缩进容易误导,需对照 {} 配对 |
| 5 | Flag 边沿检测模式:>=阈值→置1,<阈值且Flag→清0并计数 |
电压/信号的上升沿/下降沿检测 |
| 6 | 计数显示高位为0时应熄灭(显示10),不要显示前导零 | 数码管计数/数值显示 |