这是一个LED流水灯控制系统,具有以下功能:
核心功能
- 4种LED流转模式:左移 → 右移 → 正序 → 倒序(循环)
- 可调流转时间:每个模式的流转时间可独立设置(400ms~1200ms,步进100ms)
- 启动/暂停控制:S7键控制系统运行/暂停
- 数据显示功能:暂停时长按S4显示当前LED点亮数量
- 参数设置界面:可设置各模式的流转时间
按键功能
┌──────┬─────────────────────────┬─────────────────────┐
│ 按键 │ 功能 │ 使用场景 │
├──────┼─────────────────────────┼─────────────────────┤
│ S4 │ 参数减少 / 长按显示数据 │ 设置界面 / 运行界面 │
├──────┼─────────────────────────┼─────────────────────┤
│ S5 │ 参数增加 │ 设置界面 │
├──────┼─────────────────────────┼─────────────────────┤
│ S6 │ 界面切换 / 设置确认 │ 全局 │
├──────┼─────────────────────────┼─────────────────────┤
│ S7 │ 启动/暂停 │ 全局 │
└──────┴─────────────────────────┴─────────────────────┘
二、遇到的问题与解决方法 ( ̄へ ̄)
问题1:注释乱码问题 ![]()
现象:
/* ͷ�ļ������� */ // 应该是"头文件声明区"
unsigned char Key_Slow_Down;//���̼���ר�ñ��� // 应该是"按键减速专用变量"
原因:
- 文件使用了 GBK/GB2312 编码(中文Windows默认编码)
- 但编辑器或显示工具使用了 UTF-8 编码读取
- 导致中文字符显示为乱码
解决方法:
方法1:转换文件编码(推荐)
- 用记事本或VSCode打开文件
- 选择"另存为"
- 编码选择"UTF-8"
- 保存后重新打开
方法2:修改编辑器设置
- VSCode:右下角点击编码
- 选择"通过编码重新打开"
- 选择"GBK"或"GB2312"
- 文件正常显示后,再转换为UTF-8保存
方法3:使用iconv命令(Linux/Mac)
iconv -f GBK -t UTF-8 main.c > main_utf8.c
本小姐的建议:
- 统一使用 UTF-8 编码,这是国际标准!( ̄▽ ̄*)
- 避免使用中文路径和中文文件名
- 在文件开头添加编码声明注释
问题2:数组越界风险 ![]()
![]()
问题代码:
// 第132-137行
unsigned char i=2;
while(Seg_Buf[i] == 0)
{
Seg_Buf[i]=10;
i++; //
没有边界检查!
}
风险分析:
- Seg_Buf 数组长度为 6(索引 0~5)
- 如果从 i=2 开始,最多可以访问到 i=5
- 但如果 Seg_Buf[2]~Seg_Buf[5] 全是 0,i 会增加到 6,导致 数组越界!
可能后果:
轻则:读取到未知内存数据,显示异常
重则:程序崩溃,系统死机
解决方法:
// ✓ 添加边界检查
unsigned char i=2;
while(i < 6 && Seg_Buf[i] == 0) // 添加 i < 6 条件
{
Seg_Buf[i]=10;
i++;
}
更好的封装:
// 封装成函数,更安全
void Remove_Leading_Zero(unsigned char start_pos)
{
unsigned char i = start_pos;
while(i < 6 && Seg_Buf[i] == 0)
{
Seg_Buf[i] = 10;
i++;
}
}
// 使用时
Remove_Leading_Zero(2);
问题3:Led_Data变量未实现 ![]()
问题代码:
// 第28行:声明了变量
unsigned char Led_Data;
// 第145-146行:使用了变量
Seg_Buf[4] = Led_Data / 10;
Seg_Buf[5] = Led_Data % 10;
// 但是!整个代码中没有给Led_Data赋值!
后果:
- Led_Data 的值是未定义的(可能是随机值)
- 数据显示功能会显示错误的LED数量
解决方法:
// 在Led_Proc()函数中添加LED数量统计
void Led_Proc()
{
unsigned char i;
P1 = ucLed;
// ✓ 添加:统计当前点亮的LED数量
Led_Data = 0;
for(i = 0; i < 8; i++)
{
if((ucLed & (1 << i)) == 0) // 0表示LED亮
Led_Data++;
}
// 原有的流转逻辑...
if(Sys_Tick == Led_Time_Data[Led_Disp_Mode])
{
// ...
}
}
优化版本(位运算):
// 使用查表法,更高效
const unsigned char BitCount[256] = {
8,7,7,6,7,6,6,5,7,6,6,5,6,5,5,4, // 0x00-0x0F
// … 完整的256个值
};
Led_Data = BitCount[ucLed]; // 直接查表
问题4:Set_Flag逻辑混乱 ![]()
问题代码:
// 第156-161行
if(Set_Flag==1) // 注释说是"编号设置界面"
{
Seg_Buf[0]=Seg_Star_Flag?13:10;
Seg_Buf[1]=Seg_Star_Flag?Led_Time_Set_Index+1:10;
}
else // 注释说是"时间修改界面"
{
Seg_Buf[2]=Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index]/1000%10:10;
// …
}
问题分析:
- 第64-66行注释说:Set_Flag=0 是模式选择阶段,Set_Flag=1 是时间修改阶段
- 但第156行的逻辑是:Set_Flag==1 时闪烁模式编号(应该是模式选择阶段)
- 逻辑和注释矛盾!
正确理解:
实际逻辑(根据代码行为):
- Set_Flag=0:模式选择阶段(闪烁时间参数)
- Set_Flag=1:时间修改阶段(闪烁模式编号)
但这和注释说的相反!
解决方法:
// 方法1:修正注释(推荐)
if(Set_Flag==1) // 时间修改阶段(闪烁模式编号)
{
Seg_Buf[0]=Seg_Star_Flag?13:10;
Seg_Buf[1]=Seg_Star_Flag?Led_Time_Set_Index+1:10;
}
else // 模式选择阶段(闪烁时间参数)
{
Seg_Buf[2]=Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index]/1000%10:10;
// …
}
// 方法2:修正逻辑(如果注释是对的)
if(Set_Flag==0) // 模式选择阶段(闪烁模式编号)
{
Seg_Buf[0]=Seg_Star_Flag?13:10;
Seg_Buf[1]=Seg_Star_Flag?Led_Time_Set_Index+1:10;
}
else // 时间修改阶段(闪烁时间参数)
{
Seg_Buf[2]=Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index]/1000%10:10;
// …
}
本小姐的建议:
- 使用 枚举类型 代替 bit 类型,更清晰!
typedef enum {
MODE_SELECT = 0, // 模式选择阶段
TIME_EDIT = 1 // 时间修改阶段
} SetStage_t;
SetStage_t Set_Stage = MODE_SELECT;
// 使用时
if(Set_Stage == TIME_EDIT)
{
// 时间修改逻辑
}
问题5:魔法数字过多 ![]()
问题代码:
if(Key_Down == 7) // 7是什么?
if(Led_Time_Set[Led_Time_Set_Index] >= 1300) // 1300是什么?
if(++Key_Slow_Down == 10) // 10是什么?
问题:
- 代码可读性差
- 修改时容易出错
- 不利于维护
解决方法:
// ✓ 使用宏定义
#define KEY_START_STOP 7
#define KEY_SETTING 6
#define KEY_PARAM_UP 5
#define KEY_PARAM_DOWN 4
#define LED_TIME_MIN 400
#define LED_TIME_MAX 1200
#define LED_TIME_STEP 100
#define KEY_SCAN_PERIOD 10 // 按键扫描周期(ms)
#define SEG_UPDATE_PERIOD 500 // 数码管更新周期(ms)
#define BLINK_PERIOD 400 // 闪烁周期(ms)
// 使用时
if(Key_Down == KEY_START_STOP)
if(Led_Time_Set[Led_Time_Set_Index] >= LED_TIME_MAX)
if(++Key_Slow_Down == KEY_SCAN_PERIOD)
问题6:unsigned char下溢问题 ![]()
问题代码:
// 第100行
if(–Led_Time_Set_Index == 255)
Led_Time_Set_Index = 3;
// 第214行
if(–ucLed_Data_Index == 255)
{
Led_Disp_Mode = 0;
ucLed_Data_Index = 0;
}
问题分析:
- unsigned char 类型范围是 0~255
- 当值为 0 时再减 1,会下溢变为 255
- 这里利用了下溢特性来检测边界
风险:
- 代码不直观,容易误解
- 如果变量类型改变(如改为 int),逻辑会失效
更好的写法:
// ✓ 方法1:先判断再操作
if(Led_Time_Set_Index == 0)
Led_Time_Set_Index = 3;
else
Led_Time_Set_Index–;
// ✓ 方法2:使用模运算
Led_Time_Set_Index = (Led_Time_Set_Index + 3) % 4; // 循环递减
问题7:第167行的计算错误 ![]()
![]()
![]()
严重BUG!
// 第167行
Seg_Buf[5]=Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index]/1%10:10;
// ↑
// 除以1???
问题:
- 应该是 /10%10 或 %10
- 写成 /1%10 是笔误,但不影响结果(除以1等于本身)
- 但这是代码不规范的表现!
正确写法:
Seg_Buf[5]=Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index]%10:10;