寒假第八讲(彩灯控制系统更新版)

:light_bulb: 彩灯控制系统编程笔记

:bullseye: 项目概述

彩灯控制系统 ——实现LED灯的不同流转模式,配合按键控制、数码管显示和参数设置功能。


:radio_button: 一、按键控制技巧

1.1 避免按键重复执行问题

问题 :按键按下后默认会多执行一次
要求 :S6为"设置"键,按顺序切换:运行界面 → 模式设置 → 时间设置 → 保存退出

// 核心逻辑:if-else结构确保每次按键只执行一个分支
unsigned char Seg_Disp_Mode; // 0-运行界面 1-设置界面 2-数据界面

case 6: // S6设置键
    if(Seg_Disp_Mode == 0) // 第一次按:进入设置
    {
        for(i=0;i<4;i++)
            Led_Time_Set[i] = Led_Time_Data[i]; // 备份当前数据
        Seg_Disp_Mode = 1; // 进入设置界面
    }
    else if(Seg_Disp_Mode == 1) // 已在设置界面中
    {
        Set_Flag ^= 1; // 切换设置项:0=模式编号 1=流转间隔
        if(Set_Flag == 0) // 第三次按:保存并退出
        {
            for(i=0;i<4;i++)
                Led_Time_Data[i] = Led_Time_Set[i]; // 保存设置
            Seg_Disp_Mode = 0; // 返回运行界面
        }
    }
break;

:memo: 执行流程
按键1 → 进入设置(模式编号) → Set_Flag=0
按键2 → 切换项(流转间隔) → Set_Flag=1
按键3 → 保存退出 → Set_Flag=0 → 返回运行界面

1.2 按键长按功能

要求 :S4在系统停止时长按显示数据,松手恢复

bit Data_Disp_Flag; // 0=状态界面 1=数据界面

if(System_Flag == 0) // 系统暂停状态
{
    if(Key_Old == 4 && Seg_Disp_Mode == 0)
        Data_Disp_Flag = 1; // 长按S4显示数据
    else
        Data_Disp_Flag = 0; // 其他情况显示状态
}
else
    Data_Disp_Flag = 0; // 运行状态只显示状态界面

:1234: 二、数码管显示技巧

2.1 数码管闪烁效果

要求 :当前选中项以0.8秒间隔亮灭(0.4秒亮,0.4秒灭)

// 设置界面显示
case 1: // 流转时间设置界面
    // 正常显示所有内容
    Seg_Buf[0] = 13; // 特殊字符
    Seg_Buf[1] = Led_Time_Set_Index + 1; // 模式编号
    Seg_Buf[2-5] = 时间值的千/百/十/个位;
    
    // 根据选择项设置闪烁
    if(Set_Flag == 0) // 选中模式编号
    {
        Seg_Buf[0] = Seg_Star_Flag ? 13 : 10; // 10=熄灭
        Seg_Buf[1] = Seg_Star_Flag ? (Led_Time_Set_Index+1) : 10;
    }
    else // 选中流转间隔
    {
        // 四个时间位同时闪烁
        for(i=2; i<=5; i++)
            Seg_Buf[i] = Seg_Star_Flag ? 原值 : 10;
    }
break;

2.2 闪烁定时控制

// 400ms触发一次,0.8秒一个完整周期
if(++Timer_400Ms == 400)
{
    Timer_400Ms = 0;
    Seg_Star_Flag ^= 1; // 取反:0→1→0→1...
}

2.3 高位灭零处理

i = 0;
while(Seg_Buf[i] == 0) // 高位为0时熄灭
{
    Seg_Buf[i] = 10; // 10表示熄灭
    i++;
    if(i >= 数码管位数) break; // 防止越界
}

:light_bulb: 三、LED控制技巧

unsigned char Led_Pos; // LED位置变量
unsigned char Led_Mode; // 当前模式0-3

switch(Led_Mode)
{
    case 0: // 模式1:L1→L8顺序点亮
        if(++Led_Pos == 8) // 每次+1,到8时切换
        {
            Led_Pos = 7;   // 为模式2准备:从L8开始
            Led_Mode = 1;  // 切换到模式2
        }
        // LED显示:1<<Led_Pos 对应的LED亮
    break;
    
    case 1: // 模式2:L8→L1逆序点亮
        if(--Led_Pos == 255) // 每次-1,减到-1(255)时切换
        {
            Led_Pos = 7;   // 注意:7=0111(L8和L1的中间状态)
            Led_Mode = 2;  // 切换到模式3
        }
    break;
    
    case 2: // 模式3:对称外扩 07→16→25→34
        Led_Pos += 9; // 神奇的数字9!让两个灯对称移动
        if(Led_Pos > 34) // 超过最大对称位置
        {
            Led_Pos = 34;  // 模式4起始:34对称(L3,L4亮)
            Led_Mode = 3;  // 切换到模式4
        }
    break;
    
    case 3: // 模式4:对称内缩 34→25→16→07
        Led_Pos -= 9; // 反向减9
        if(Led_Pos > 200) // 无符号数减到小于0会变成255
        {
            Led_Pos = 0;   // 模式1起始:L1亮
            Led_Mode = 0;  // 切换回模式1,完成循环
        }
    break;
}

其中亮灯操作如下

if(Led_Mode<=1)//系统处于前两种流转模式
{
  for(i==0;i<8;i++)
  {
    ucLed[i]=(i==Led_Pos);
  }
}
else//处于后两种流转模式
{
  for(i==0;i<8;i++)
  {
    ucLed[i]=(i==(Led_Pos/10))||(i==(Led_Pos%10));
  }
}

:1234: 数字9的奥秘

为什么用±9 实现对称移动?

二进制思考: 原始位置 左移1位 结果(对称位置)
00000111 (7) → 00010000 (16) = 差9 
00010000 (16) → 00011001 (25) = 差9 
00011001 (25) → 00100010 (34) = 差9

:clipboard: 五、编程要点总结

  1. 按键防抖 :使用if-else 结构避免重复执行
  2. 状态管理 :合理使用标志位(bit 类型)
  3. 显示优化 :闪烁效果、高位灭零
  4. 数据安全 :设置时先备份,确认后再保存
  5. 模式循环 :注意模式切换时的状态初始化

:sparkles: 小提示 :调试时可以先固定时间间隔,测试单个模式,确保每个LED都能正确点亮,再组合成完整系统。