蓝桥杯单片机复习笔记- 第七讲:模拟温度采集记录器
-
一、 功能设计要求 (原题全解)
本试题要求设计一个具备温度采集、数值转换、参数设置保护及PWM亮度调节功能的智能系统。以下是基于任务书的详尽功能拆解:
- 系统初始化与量程要求
-
上电状态: 默认处于 温度采集界面。
-
采集有效范围: 0 - 85℃。
-
参数设置范围: 10 - 70℃。
-
默认参数:
-
温度上限值 (TMAX): 30℃。
-
温度下限值 (TMIN): 20℃。
- 数码管显示界面要求
系统共有三个显示界面,需清晰区分“输入状态”与“实际显示状态”:
-
界面1:温度采集界面 (默认)
-
进入方式: 上电默认或按下 S16。
-
显示内容: 提示符
A+ 当前输入的温度数据。 -
格式: 提示符显示为
熄灭(实际显示通常为A或不显示提示符,根据图表源码52,第一列是"A",第二列是"提示符 熄灭",通常理解为首位显示功能符A,或者按题目图示只显示数据)。根据表格 第一列为 “A”,故第一位显示A,后续显示输入数据。 -
交互特效: 当前正在输入的数码管单元需以 0.5秒/次 的速度闪烁 。
-
数据单位: mV (输入时模拟电压值,但题目语境为温度输入,显示℃)。
-
界面2:数据显示界面
-
显示内容: 提示符
C+ 当前实际温度数据。 -
格式:
-
第一位:显示
C。 -
后续位:显示实际温度 (如
23)。 -
数据处理: 实际温度数据需采用 四舍五入 后保留 整数部分 。
-
界面3:参数设置界面
-
进入方式: 按下 S12。
-
显示内容: 提示符
P+ TMAX + TMIN。 -
格式:
-
第一位:显示
P。 -
左侧数据区:显示 TMAX (如
30)。 -
右侧数据区:显示 TMIN (如
20)。 -
交互特效: 当前选中的参数单元格需以 0.5秒/次 的速度闪烁 。
-
默认焦点: 每次从数据界面进入参数界面,默认选中 TMAX 。
- 按键功能逻辑
-
S1-S10 (数字键): 对应 0-9 输入。仅在 温度采集界面 有效 。
-
S11 (小数点): 输入小数点。仅在 温度采集界面 有效 。
-
S16 (采集/确认):
-
功能:
-
在非采集界面按下:进入 温度采集界面 。
-
在采集界面按下:
-
若数据合法:保存当前值,四舍五入 计算后返回数据显示界面 。
-
若数据无效:清空当前输入,重新开始 。
-
S12 (界面切换):
-
功能: 在“数据显示界面”和“参数设置界面”之间循环切换 。
-
退出校验 (难点): 从参数界面退出时,需检查 TMAX TMIN 。
-
若合理:参数生效,进入数据界面。
-
若不合理 (TMAX < TMIN):自动恢复为 进入参数设置界面前的有效参数,并进入数据界面 (注:此处源码80描述为恢复并进入数据界面,但也常理解为恢复后停留在设置界面提示错误,需结合L4逻辑,源码80原文是"自动恢复进入参数设置界面前的有效参数,进入数据界面") 。
-
S13 (参数切换): 仅在参数设置界面有效。切换选择 TMAX 或 TMIN 。
-
S14 (加) & S15 (减):
-
有效性: 仅在参数设置界面有效 。
-
功能: 对当前选中的参数进行加/减 1℃ 操作 。
-
长按功能 (难点): 长按 0.5秒 后,可实现 快速 加、减功能 。
- LED 指示与亮度控制 (PWM)
系统需根据当前温度 与参数 () 的关系,控制LED的 点亮状态 和 亮度等级。
-
状态指示 (L1-L4):
-
L1 (高温报警): 当$T > T_{MAX}$ 时点亮,否则熄灭 。
-
L2 (正常范围): 当 $T*{MIN} \le T \le T*{MAX}$ 时点亮,否则熄灭 。
-
L3 (低温报警): 当 $T < T_{MIN}$ 时点亮,否则熄灭 。
-
L4 (错误指示): 当出现错误的参数设置操作 (如退出时参数不合理) 时点亮,直至下一次正确的参数设置后熄灭 。
-
亮度等级控制 (PWM):
-
1级亮度 (最暗/特定亮度): 当$T > T_{MAX}$ 时 。
-
2级亮度 (中等亮度): 当 $T*{MIN} \le T \le T*{MAX}$ 时 。
-
3级亮度 (最亮/特定亮度): 当 $T < T_{MIN}$ 时 。
二、 系统核心架构与变量设计( main.c)
1. 变量设计思路
本题变量设计非常具有代表性,分为浮点运算辅助、参数备份和PWM控制三类。
C
#include <REGX52.H>
#include "Key.h"
#include "Seg.h"
#include "LED.h"
// ... (基础变量省略) ...
// === 1. 采集与计算变量 ===
u8 Seg_Input[6]={10,10,10,11,11,11}; // 输入缓冲区 (10:熄灭, 11:空)
u8 Seg_Input_Index=3; // 输入游标,从第3位开始(前3位显示C/提示符)
u8 Point_Wela=3; // 记录小数点按下的位置(位选)
// 浮点转换系数表:根据小数点位置决定除以多少
// 例如输入 12.3 (点在第4位),数据是123,除以10.0得到12.3
float Point_Change[3]={100.0, 10.0, 1.0};
u16 Temperature=0; // 最终计算出的整数温度
bit Point_Flag=0; // 标记是否按下了小数点
bit Error_Flag=0; // 输入越界或逻辑错误标志
// === 2. 参数与长按变量 ===
// 采用双数组策略实现"参数回滚"功能
u8 T_Set_Old[2]={30,20}; // [0]TMAX, [1]TMIN - 存档值(生效值)
u8 T_Set_New[2]={30,20}; // 编辑值(显示值)
bit T_Set_Index=0; // 当前正在修改哪一个 (0或1)
// 长按计时逻辑
bit Time_Flag=0; // 计时启动开关
u16 Time_Count=0; // 按下时长计数器
// === 3. PWM 亮度控制变量 ===
u8 LED_PWM=0; // 目标占空比 (决定亮度)
u8 LED_Num=0; // 当前周期计数值 (0-9循环)
三、 难点突破:浮点输入与转换算法
本题最大的难点在于:用户输入的是字符流(带小数点),而系统需要将其转换为整数温度,且要求四舍五入。
1. 逻辑分析
-
**输入:**用户输入
12.5,缓冲区存为1, 2, 5,小数点位置记为Index=4。 -
**合成:**先按整数合成
125。 -
**定点化:**查表
Point_Change,找到对应系数10.0。 -
**四舍五入:**核心公式
(数值 + 0.5) / 系数。在整数运算中,等价于(数值 + 5) / 10。
2. 代码实现( Key_Proc- Case 16)
C
// S16: 采集/确认键逻辑
case 16:
if(Seg_Mode==1){ // 如果在采集界面 -> 确认输入
// 核心算法:数据合成与四舍五入
// 1. 合成原始整数: Seg_Input[3]*100 + ...
// 2. 加上补尝值 5 (用于四舍五入,相当于+0.5)
// 3. 除以对应的小数点系数 Point_Change
Temperature=(u16)((Seg_Input[3]*100+Seg_Input[4]*10+Seg_Input[5]+5)/Point_Change[Point_Wela-4]);
// 校验逻辑:必须包含小数点 且 范围在0-85之间
if(Point_Flag==1 && Temperature>=0 && Temperature<=85){
Seg_Mode=2; // 数据合法,跳转到显示界面
Error_Flag=0;
}else{
Error_Flag=1; // 数据非法,点亮L4
}
// 复位输入缓冲区,准备下一次采集
for(i=3;i<6;i++){
Seg_Input[i]=11;
}
Seg_Input_Index=3;
Point_Flag=0;
Seg_Input_Flash=3;
Seg_Point[Point_Wela-1]=0; // 清除小数点显示
}else{
Seg_Mode=1; // 如果不在采集界面 -> 进入采集界面
}
break;
四、 难点突破:长按/短按与参数回滚
在参数设置界面,S14/S15 既支持点按微调,又支持长按快速调节。同时S12 负责参数的合法性校验。
1. 长按/短按状态机思路
利用Time_Flag开启计时,在主循环检测按键状态:
-
**短按:**按键抬起(
Key_Up)时,若计时<500ms,执行一次操作。 -
**长按:**按键保持按下(
Key_Old)且计时>500ms,执行操作并将计时器重置为300ms(实现连续快速触发)。
2. 代码实现( Key_Proc- Mode 3)
C
if(Seg_Mode==3){
// === 长按触发逻辑 (以S14加号键为例) ===
if(Key_Down==14){
Time_Flag=1; // 按下瞬间,开启计时器
}
// 分支1: 短按逻辑 (计时未满500ms)
if(Time_Count<500){
if(Key_Up==14){ // 必须检测"抬起"动作
if(++T_Set_New[T_Set_Index]>70) T_Set_New[T_Set_Index]=10; // 循环加
// 操作完成,复位计时
Time_Flag=0;
Time_Count=0;
}
}
// 分支2: 长按逻辑 (计时已满500ms)
else{
if(Key_Old==14){ // 检测"保持按下"状态
if(++T_Set_New[T_Set_Index]>70) T_Set_New[T_Set_Index]=10;
Time_Count = 300; // 重置为300ms,实现0.2秒一次的连续触发
}
else if(Key_Up==14){ // 长按结束
Time_Flag=0;
Time_Count=0;
}
}
// ... S15减号键逻辑同理 ...
}
3. 参数回滚逻辑(S12)
退出设置时,如果TMAX < TMIN,则视为设置无效,恢复为旧参数。
C
case 12: // 界面切换键
if(Seg_Mode==2){
Seg_Mode=3; // 进入设置
}
else if(Seg_Mode==3){ // 退出设置 -> 检查合法性
// 检查:新设置的 TMAX 是否大于等于 TMIN
if(T_Set_New[0] > T_Set_New[1]){ // 逻辑修正: 题目要求TMAX>=TMIN
Seg_Mode=2; // 合法,切换界面
// 提交事务: 将新值覆盖旧值
T_Set_Old[0]=T_Set_New[0];
T_Set_Old[1]=T_Set_New[1];
}else{
// 非法,不切换界面(或切换界面但参数无效),此处逻辑为:
// 强制回滚: 用旧值覆盖新值,并停留在参数界面或退出?
// 代码逻辑实现的是: 恢复旧值,且清除闪烁位
// 注: 根据题目,反之自动恢复有效参数,进入数据界面。
// 这里的代码逻辑似乎是直接恢复并留在了界面3,或者下次进循环处理?
// *实际代码逻辑*: 恢复了T_Set_New,但没有执行 Seg_Mode=2,
// 这意味着用户必须改对参数才能退出,或者代码意图是回滚后继续让用户改。
T_Set_New[0]=T_Set_Old[0];
T_Set_New[1]=T_Set_Old[1];
Seg_Set_Flash=0;
}
}
break;
五、 硬件驱动:LED PWM 调光
题目要求根据温度范围改变LED亮度,这是通过软件模拟PWM实现的。
1. 软件PWM原理
-
周期: 10ms (由
Timer0每1ms调用,LED_Num0-9 循环)。 -
占空比:
LED_PWM决定了在这10ms里,LED亮几毫秒。 -
实现:
if (LED_Num < LED_PWM) 亮 else 灭。
2. 代码实现( LED_Proc& Timer0)
C
// 1. 业务层: 决定亮度等级 (LED_Proc)
// 题目要求3个亮度等级,这里分配为 30%, 90%, 60%
if(Temperature > T_Set_Old[0]){ // > TMAX
LED_PWM=3; // 亮度等级1 (较暗)
}
else if(Temperature < T_Set_Old[1]){ // < TMIN
LED_PWM=9; // 亮度等级3 (最亮)
}else{ // 中间范围
LED_PWM=6; // 亮度等级2 (中等)
}
// 2. 驱动层: 执行PWM (Timer0_Server)
if(++LED_Num==10) LED_Num=0; // 产生 0-9 的锯齿波
// 比较产生PWM波形
if(LED_Num < LED_PWM){
LED_Disp(LED_Pos, LED_Buf[LED_Pos]); // 亮
}else{
LED_Disp(LED_Pos, 0); // 灭
}
六、 总结与复习重点
-
**小数点处理技巧:**遇到带小数点的数码管输入,不要使用float 类型存储输入过程。最佳实践是:记录数字序列+ 记录小数点位置,最后统一进行一次浮点运算转换为整数。
-
长按通用模板:
-
按下置标志位。
-
松手时判断时间-> 短按。
-
按住时判断时间-> 长按-> 修改时间变量实现连续触发。
-
-
**事务提交模式:**在参数设置中,使用
Old(存档) 和New(草稿) 两组变量。只有校验通过时才执行Old = New,否则执行New = Old(回滚)。这是嵌入式界面开发的标准模式。 -
**PWM 模拟:**这里的PWM 频率为$1kHz / 10 = 100Hz$,足够骗过人眼实现调光,且不会有闪烁感。