第四周决赛与过渡模拟一

:blue_book: 单片机竞赛备赛笔记:决赛试题与过渡模拟一

笔记说明:本笔记涵盖了《决赛一 Led彩灯控制系统》与《过渡一 电压采集器》两大核心模块的考点、代码逻辑及避坑指南。


:green_circle: 第一部分:决赛一 Led 彩灯控制系统

1. 核心算法:位移与循环

在流水灯控制中,位移是最基础的操作。

  • 指令函数

    • _crol_(变量, 位数):循环左移 (Circle Rotate Left)。特点:移出的位会补回到另一端(首尾相接)。

    • _cror_(变量, 位数):循环右移。

    • 注意:使用前必须包含头文件 #include <intrins.h>

2. 重要原则:寄存器保护机制

  • 规则严禁直接对 P0 寄存器进行位移操作。

  • 原因:P0 端口状态可能受到负载影响,或者直接操作会导致显示不稳定(鬼影)。

  • 正确做法(中间变量法)

     unsigned char Led_Temp = 0xFE; // 定义中间变量
     Led_Temp = _crol_(Led_Temp, 1); // 在变量上运算
     P0 = Led_Temp; // 最后赋值给硬件
    

3. 经典 Bug 修复:模式切换复位

  • 问题描述:当从 模式 4(或其他复杂模式)切换回 模式 1 时,流水灯往往不是从第一个灯(L1)开始亮,而是接着上一次的位置。

  • 解决方案“切换即复位”

     if (Key_Value == S_Mode_Switch) {
         Mode++;
         if (Mode > 4) Mode = 1;
     ​
         // 关键点:切换模式时,强制重置中间变量
         Led_Temp = 0xFE; 
         P0 = Led_Temp;
     }
    

4. 数码管闪烁控制

  • 全部闪烁:利用定时器产生的标志位(如 Seg_Star_Flag)控制所有位的显示。

  • 覆盖闪烁(指定位闪烁)

    • 利用 Set_Flag 判断当前是在设置哪一位。

    • 三目运算符逻辑

      C

       // 如果 Flag为1显示数据,为0显示“熄灭码(10)”
       Seg_Buf[Index] = (Seg_Star_Flag) ? Real_Data : 10; 
      

5. 按键逻辑优化

  • 防连跳 (else if)

    • 在状态切换逻辑中,必须使用 else if。如果不加 else,CPU 会在一次扫描中连续执行多个 if,导致模式直接从 1 跳到 3。
  • 一键开关 (^=)

    • 利用按位异或实现状态翻转(启动/停止)。

    • Run_Flag ^= 1; (0 变 1,1 变 0)。

  • 按键长按

    • 核心是计时器。按下开始计时,抬起时判断计时时长。

    • if (Time > 1000ms) → 执行长按功能;else → 执行短按功能。

6. 硬件常识

  • LED 逻辑01。(共阳极接法)

:red_circle: 第二部分:过渡一 电压采集器 (进阶逻辑)

1. 输入限制与处理

  • 输入位数限制:防止用户输入超过 4 位导致溢出。

     if (Input_Index < 4) { // 限制只能输4位
         Input_Val = Input_Val * 10 + Key_Value; // 移位累加算法
         Input_Index++; 
     }
    
  • 输入时闪烁:提示用户当前正在编辑。同样使用“面具法”(定时器标志位覆盖数据)。

2. 非阻塞闪烁 (避免卡死)

  • 严禁使用 Delay:在数码管刷新或主循环中,绝对不能使用 Delay_ms(500) 来做闪烁,这会卡死按键扫描。

  • 正确做法:利用定时器中断翻转标志位 Flash_Flag,主循环只根据这个 Flag 决定显示还是熄灭。

3. 数值处理技巧

  • 四舍五入保留两位

    • 单片机处理浮点数很慢,通常放大 100 倍用整数处理。

    • 核心公式Result = (Raw_Data + 5) / 10; (加 5 是为了实现四舍五入)。

  • Float 显示在数码管

    • 不要用 %f 打印。

    • 手动点亮小数点:控制 Seg_Point 数组,在特定位(如第 2 位)赋值 0 点亮小数点。

4. 界面美化:高位熄灭 (Leading Zero Suppression)

  • 需求:显示 25 而不是 0025

  • 算法逻辑:从最高位(千位)开始扫描。

     // 伪代码示例
     if (千位 == 0) {
         熄灭千位;
         if (百位 == 0) {
             熄灭百位;
             // ...以此类推,注意个位通常不灭
         }
     }
    

5. 业务逻辑:计数值增加条件

  • 边缘检测(下降沿):题目要求“当电压从大于 Vp 变为小于 Vp 时,计数加 1”。

    • 需要两个变量:Voltage_Old (旧值) 和 Voltage_New (新值)。

    • 判断公式

       if (Voltage_Old > Vp && Voltage_New < Vp) {
           Count++; // 只有在这个瞬间才计数
       }
      

6. 高阶 LED 状态指示

  • 根据电压范围点亮不同的 LED(如 L1, L2, L3)。

  • 注意逻辑互斥:使用 if ... else if ... else 结构,确保同一时间只有一个状态生效,避免 LED 乱闪。


:memo: 总结回顾 (Cheat Sheet)

  1. 位移_crol_,记得包含 intrins.h

  2. 切换模式记得复位中间变量。

  3. 闪烁不要用延时,要用定时器 Flag + 三目运算符

  4. 按键切换状态必加 else if

  5. 电压输入算法:Val = Val * 10 + Key

  6. 边缘检测要存旧值:if (Old > Ref && New < Ref)