决赛试题笔记


决赛笔记:LED彩灯智能控制系统逻辑全解 (V2.0)

一、 LED 流转逻辑:位运算与状态切换

1. 核心函数与头文件

  • 头文件#include <intrins.h>

  • 循环移位_crol_ (左循环) 和 _cror_ (右循环)。

    • 本质区别:普通的 << 会导致位丢失并补0,而循环移位会让最高位绕回到最低位,保证灯光永远在8盏灯内循环,不会熄灭。

2. 模式切换的“哨兵”逻辑

  • 模式 0 (L1->L8):使用 0x7f 作为终点判断(L8点亮),一旦到达,立即切换模式。

  • 模式 2 & 3 (对称流转)

    • ucLed_Data[4] = {0x7e, 0xbd, 0xdb, 0xe7}

      • 0x7e (0111 1110): L1 & L8 亮

      • 0xbd (1011 1101): L2 & L7 亮

      • 0xdb (1101 1011): L3 & L6 亮

      • 0xe7 (1110 0111): L4 & L5 亮

3. 指针溢出处理 (重点)

  • 正向溢出 (++Index == 4):当索引到达4时,超出了数组范围(0-3),重置回 0 或 3。

  • 反向溢出 (--Index == 255)

    • 底层原理unsigned char 是无符号型。当 0 再减 1 时,由于没有符号位,它会发生**“下溢”**,直接跳到该类型的最大值 255

    • 应用:利用这个特性,我们不需要写 if(Index < 0),而是写 if(Index == 255),这是单片机开发中最高效的边界处理手段。


二、 数码管显示:多界面状态机

1. 界面逻辑架构

  • Seg_Disp_Mode:大切换(运行 vs 设置)。

  • Data_Flag:小切换(在运行界面内,通过长按S4切换显示“状态”或“LED实时数据”)。

2. 段码与特殊字符安排

为了满足题目需求,Seg_Dula 数组必须包含非数字字符:

  • 下标 10: 0x00 (全灭,用于消影或闪烁)

  • 下标 11: 0x6d (字母 S - Stop)

  • 下标 12: 0x77 (字母 R - Run)

  • 下标 13: 0x40 (符号 - - 中横杠)

3. 闪烁逻辑 (Seg_Star_Flag)

  • 节拍生成:在定时器中断中,每 400ms 执行一次 Seg_Star_Flag ^= 1

  • 视觉效果:亮 400ms + 灭 400ms = 0.8秒周期,符合题目要求。

  • 实现手段Seg_Buf[i] = Seg_Star_Flag ? 数据 : 10;。当 Flag 为 0 时,送入段码 10(熄灭),从而实现闪烁。


三、 按键处理:交互与数据保护

1. 为什么必须用 else if 切换界面?

  • 逻辑防瞬移:如果使用连续的 if,在 Seg_Disp_Mode 从 0 变为 1 的瞬间,第二个 if 会立即触发。

  • ATM 机制else if 确保了“按下一次按键,只执行一个层级的动作”。

2. 数据缓冲机制 (数据保护)

  • 进入设置:先用 for 循环将 Led_Time_Data (运行值) 拷贝到 Led_Time_Set (临时值)。

  • 退出设置:只有再次按下 S6 并确认后,才将 Led_Time_Set 覆盖回 Led_Time_Data

  • 意义:用户在设置过程中如果后悔了,或者没按确认键,原本运行的参数不会被误修改。

3. 长按功能的实现

  • 原理:利用 Key_Old(上一时刻的状态)。

  • 逻辑if(Key_Old == 4) 意味着按键一直被按住没松开。此时开启 Data_Flag,数码管显示数据;一旦松开,回到状态界面。


四、 LED 状态逆向读取 (算法难点)

题目要求在数码管上显示当前是“哪盏灯”亮,但我们手里只有 ucLed 这个二进制数。

1. 寻找亮灯位置

C

 while((~ucLed & (0x01 << i)) != (0x01 << i)) i++;
  • ~ucLed:取反。因为我们的 LED 是低电平点亮(0代表亮),取反后 1 代表亮。

  • 0x01 << i:构造一个只有第 i 位是 1 的“探测器”。

  • & 运算:当探测器对准了那个 1 时,循环停止,此时的 i+1 就是亮灯的编号。

2. 对称模式的数据合并

对于模式 2 和 3,同时亮两盏灯,我们需要显示两个编号(如 18, 27)。

  • 算法Led_Data = (i+1)*10 + (8-(i+1))

  • 实例:如果 L1 亮(i=0),则 1*10 + (8-1) = 17(代表 1 和 8 亮,注:此处根据实际电路微调,通常为18)。


五、 底层节拍:定时器中断 (脉搏)

1. 任务分发 (减速变量)

  • 1ms:数码管扫描(Seg_Pos),保证显示不闪烁。

  • 10ms:按键扫描(Key_Slow_Down),避开机械抖动。

  • 400ms:闪烁标志位翻转(Timer_400Ms)。

  • Led_Time_Data[Led_Mode]:LED 流转的步进计时。

2. 优先级意识

中断服务函数(Timer0Server)里的代码越短越好,只做“计时”和“硬件刷新”,复杂的“逻辑运算”必须丢回 main 函数的 while(1) 中处理。


总结:给决赛选手的避坑指南

  1. 消影:数码管显示前必须先送 0x00 或者关断位选,防止鬼影。

  2. 变量范围:修改流转时间时,务必检查 3001200 的边界,防止数据越界导致程序崩溃。

  3. 异或的神级应用Flag ^= 1 是处理所有“开关型”逻辑的最佳方案。

适用赛道:蓝桥杯单片机设计与开发 / 彩灯控制系统专题