蓝桥杯单片机第五周
过度模拟二
模拟温度采集记录器
1. 总体设计:分时操作系统的思想
在这个项目中,我们不需要复杂的操作系统,而是利用 定时器中断 来给每个模块分配时间片。
核心代码结构
主函数 main() 非常干净,只负责一直在那里“空转”或者等待标志位,真正的脏活累活都在具体的模块函数里。
C
void main(){
Timer0Init(); // 1. 启动心脏(定时器)
while(1){
led_proc(); // 2. 处理LED逻辑
seg_proc(); // 3. 处理显示逻辑
key_proc(); // 4. 处理按键逻辑
}
}
2. 核心模块详解
功能:为系统提供 1ms 的时间基准,并实现 LED 的亮度调节(PWM)。
通俗原理:
- 减速变量:单片机跑得太快了,按键和数码管不需要 1ms 扫描一次,所以用
key_slow_down这种变量让它们每 10ms 才工作一次。 - PWM(调光):把 10 毫秒看作一个周期,切成 10 份。通过控制亮几份、灭几份,来欺骗眼睛,实现亮度分级。
代码结合:
C
void timer0_server() interrupt 1 {
// ...重装载初始值...
// 1. 减速逻辑:让外设不要跑太快
if(++seg_slow_down==10) seg_slow_down=0;
if(++key_slow_down==10) key_slow_down=0;
// 2. 扫描逻辑:切换数码管位选,让人眼看到完整的字
if(++seg_pos == 6) seg_pos = 0;
if(++led_pos == 8) led_pos = 0;
seg_disp(seg_pos,seg_buf[seg_pos],seg_point[seg_pos]);
// 3. 闪烁逻辑:每500ms翻转一次状态
if(++timer_flash==500){
flash_flag^=1;
timer_flash=0;
}
// 4. 【核心】PWM 调光逻辑
// 必须让计数器跑起来!(0 -> 9 循环)
if(++led_num == 10) led_num = 0;
// 只有在计数器小于设定亮度值(led_pwm)时才亮灯
if(led_num < led_pwm)
led_disp(led_pos,ucled[led_pos]);
else
led_disp(led_pos,0); // 否则强行灭灯,产生“暗”的效果
}
功能:处理三种模式下的按键逻辑,特别是参数检查和小数点输入。
代码亮点与避坑:
- 纯净的数据采集 (S1-S10, S16)
在采集时,不要做任何处理,用户输什么就是什么。千万别在这里 +5。
C
case 16: // 采集确认键
if(seg_disp_mode==0){
// 计算原始温度:百位*100 + 十位*10 + 个位
// 【修正】去掉 +5,保持数据纯净
temperature = input[0]*100 + input[1]*10 + input[2];
// 根据小数点位置还原真实浮点数
if(point_wela==1)
temperature = temperature/100.0; // 例如输入 1.23
else if(point_wela==2)
temperature = temperature/10.0; // 例如输入 12.3
// ...后续跳转逻辑...
}
- 参数检查门神 (S12)
退出设置界面时,必须检查逻辑合理性。
C
case 12: // 界面切换
if(seg_disp_mode==2){
// 【修正】题目要求 TMAX >= TMIN (包含等于)
if(parameter[0] >= parameter[1]){
// 合理:保存并生效
parameter_ctrol[0]=parameter[0];
parameter_ctrol[1]=parameter[1];
err_flag=0;
}
else{
// 不合理:报错,并回滚到上一次的有效参数
parameter[0]=parameter_ctrol[0];
parameter[1]=parameter_ctrol[1];
err_flag=1; // 点亮 L4 报错灯
}
}
功能:根据 seg_disp_mode 显示不同的内容。
代码结合:
1. 模式 0:采集输入(带闪烁)
利用三目运算符 ? : 实现闪烁效果。
C
case 0: // 采集界面
seg_buf[0]=12; // 显示 'A'
// ...
// 如果当前位是正在输入的位(input_index),且 flag 为 1 则显示数字,否则显示灭(10)
seg_buf[3+input_index] = flash_flag ? input[input_index] : 10;
break;
2. 模式 1:数据显示(四舍五入)
这是题目的一个考点。四舍五入要在显示环节做。
C
case 1: // 数据显示界面
seg_buf[0]=13; // 显示 'C'
// 【核心算法】四舍五入取整
// 原理:浮点数 + 0.5 后转整数,会自动向下取整实现四舍五入
// 例:20.5 + 0.5 = 21.0 -> 21
// 例:20.4 + 0.5 = 20.9 -> 20
{
unsigned int temp_int = (unsigned int)(temperature + 0.5);
seg_buf[4] = temp_int / 10; // 十位
seg_buf[5] = temp_int % 10; // 个位
}
seg_point[4]=0; // 此模式下不显示小数点
break;
模块四:红绿灯系统(LED 逻辑)**
功能:根据温度判断“冷、适中、热”,并设置对应的 PWM 亮度等级。
代码结合:
这里最重要的是边界条件,不能漏掉等于号。
C
void led_proc(){
// 1. 设置 PWM 亮度等级 (及格线)
if(temperature > parameter_ctrol[0])
led_pwm = 3; // 高温:最暗 (30%占空比)
// 【修正】必须包含等于号 (>= 和 <=)
else if(temperature >= parameter_ctrol[1] && temperature <= parameter_ctrol[0])
led_pwm = 6; // 适中:中亮 (60%占空比)
else
led_pwm = 9; // 低温:最亮 (90%占空比)
// 2. 设置 LED 开关状态 (逻辑同上)
ucled[0] = temperature > parameter_ctrol[0]; // L1: 高温灯
ucled[1] = temperature >= parameter_ctrol[1] && temperature <= parameter_ctrol[0]; // L2: 正常灯
ucled[2] = temperature < parameter_ctrol[1]; // L3: 低温灯
ucled[3] = err_flag; // L4: 错误指示灯
}
3. 核心知识点总结
- 分时复用:定时器就像一个尽职的“时间管理大师”,每 1ms 叫醒一次中断服务函数,轮流处理扫描、计数、PWM。
- 四舍五入技巧:
(int)(float_val + 0.5)是 C 语言中最简单的四舍五入写法。 - 状态机思维:在
key_proc和seg_proc中,大量使用if(mode==0)... else if(mode==1)...,这就是状态机。在写代码前,先想清楚系统有几个状态,每个状态下按键和屏幕分别该干什么。