蓝桥杯单片机第五周

蓝桥杯单片机第五周

过度模拟二

模拟温度采集记录器

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); // 否则强行灭灯,产生“暗”的效果
 }

功能:处理三种模式下的按键逻辑,特别是参数检查和小数点输入。

代码亮点与避坑

  1. 纯净的数据采集 (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
             
         // ...后续跳转逻辑...
     }
  1. 参数检查门神 (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. 核心知识点总结

  1. 分时复用:定时器就像一个尽职的“时间管理大师”,每 1ms 叫醒一次中断服务函数,轮流处理扫描、计数、PWM。
  2. 四舍五入技巧(int)(float_val + 0.5) 是 C 语言中最简单的四舍五入写法。
  3. 状态机思维:在 key_procseg_proc 中,大量使用 if(mode==0)... else if(mode==1)...,这就是状态机。在写代码前,先想清楚系统有几个状态,每个状态下按键和屏幕分别该干什么。