蓝桥杯第九届省赛代码错误总结
错误列表
1.
系统流转计时器未累加
错误代码:
void Led_Proc()
{
unsigned char i;
ucLed[0]=1; // 多余的语句
// ❌ 没有 Sys_Tick++ 语句
if(Sys_Tick == Led_Time_Data[Led_Mode]) // Sys_Tick永远是0
{
Sys_Tick = 0;
switch(Led_Mode)
{
// ...
}
}
}
错误原因:
-
计时器变量
Sys_Tick必须累加才能计时 -
忘记累加导致条件永远不满足,LED不会流转
-
多余的
ucLed[0]=1会导致L1一直亮着
正确代码:
void Led_Proc()
{
unsigned char i;
if(Led_flag==0)
{
for(i=0;i<8;i++)
ucLed[i]=0;
}
else
{
Sys_Tick++; // ✓ 每次调用累加计时器
if(Sys_Tick == Led_Time_Data[Led_Mode])
{
Sys_Tick = 0;
switch(Led_Mode)
{
// ...
}
}
}
}
2.
亮度控制变量名混淆
错误代码:
void AD_DA()
{
Voltage = Ad_Read(0x83) / 51.0;
if((Voltage >= 0) && (Voltage < 1))
pwm_period = 0; // ❌ 修改了PWM周期计数器
else if((Voltage >= 1) && (Voltage < 2))
pwm_period = 1; // ❌ 错误
// ...
}
// 定时器中断中:
void Timer1_Isr(void) interrupt 3
{
// ...
pwm_period = (++pwm_period) % 5; // pwm_period在中断中自动累加
if(pwm_period < pwm_compare) // 用pwm_compare控制占空比
Led_Disp(ucLed);
else
Led_Off();
}
错误原因:
-
pwm_period:PWM周期计数器,在中断中自动累加(0→1→2→3→4→0循环) -
pwm_compare:PWM比较值,决定占空比/亮度等级 -
在
AD_DA()中修改pwm_period会破坏PWM循环,导致亮度控制失效
正确代码:
void AD_DA()
{
Voltage = Ad_Read(0x83) / 51.0;
if((Voltage >= 0) && (Voltage < 1.25))
pwm_compare = 1; // ✓ 修改PWM比较值(亮度等级1)
else if((Voltage >= 1.25) && (Voltage < 2.5))
pwm_compare = 2; // ✓ 亮度等级2
else if((Voltage >= 2.5) && (Voltage < 3.75))
pwm_compare = 3; // ✓ 亮度等级3
else
pwm_compare = 4; // ✓ 亮度等级4
}
同样的错误也出现在:
- 数码管显示亮度等级(第177-178行)
3.
流转间隔调整使用错误的索引
错误代码:
case 5: // S5加键
if(Seg_Mode == 0)
{
if(Seg_Set_Index == 0)
{
Led_Mode++;
if(Led_Mode == 4)
Led_Mode = 0;
}
else
{
Led_Time_Data[ucLed_index] += 100; // ❌ 使用了LED指针索引
if(Led_Time_Data[ucLed_index] >= 1200)
Led_Time_Data[ucLed_index] = 1200;
}
}
break;
错误原因:
-
ucLed_index:LED流转指针(0-7),指示当前点亮哪个LED -
Led_Mode:当前运行模式(0-3),对应模式1-4 -
应该调整当前运行模式的流转间隔,而不是LED指针位置的间隔
-
导致调整的参数不是正在显示的模式
正确代码:
else
{
Led_Time_Data[Led_Mode] += 100; // ✓ 使用当前模式索引
if(Led_Time_Data[Led_Mode] >= 1200)
Led_Time_Data[Led_Mode] = 1200;
}
同样的错误也出现在:
-
S4减键调整(第115行)
-
数码管显示流转间隔(第151-153行、第163-165行)
4.
模式切换条件过于复杂(可优化)
原始代码:
case 0:
// 模式1 L1-L8
for(i=0; i<8; i++)
ucLed[i] = 0;
ucLed[ucLed_index] = 1;
ucLed_index++;
if(ucLed_index >= 8)
ucLed_index = 0;
// ❌ 判断是否是L8点亮(过于复杂)
if((ucLed[7]==1) && (ucLed[0]==0) && (ucLed[1]==0) && ...)
Led_Mode = 1;
break;
优化原因:
-
题目要求固定顺序循环运行,不需要判断具体LED状态
-
索引归零时就是一轮结束,可以直接切换模式
-
简化逻辑,减少出错可能
优化后的代码:
case 0:
// 模式1 L1-L8
for(i=0; i<8; i++)
ucLed[i] = 0;
ucLed[ucLed_index] = 1;
ucLed_index++;
if(ucLed_index >= 8)
{
ucLed_index = 0;
Led_Mode = 1; // ✓ 一轮结束直接切换
}
break;
注意: 虽然不影响功能,但简化逻辑可以避免出错。同样的优化也适用于case 1。
5.
数码管显示高位为0时未熄灭
错误代码:
// 长亮时:
Seg_Buf[4] = Led_Time_Data[Led_Mode]/1000%10; // ❌ 千位为0时显示0,不美观
// 闪烁时:
Seg_Buf[4] = Seg_Star_Flag ? Led_Time_Data[Led_Mode]/1000%10 : 10;
// ❌ 虽然闪烁时熄灭,但当千位为0时,亮起时会显示0
错误原因:
-
数码管显示多位数字时,高位为0应该熄灭(显示为空),而不是显示"0"
-
例如:400ms应该显示为"_400"(千位熄灭),而不是"0400"
-
这是数码管显示的常规做法,提升显示美观度
正确代码:
// 长亮时:千位为0则熄灭
Seg_Buf[4] = ((Led_Time_Data[Led_Mode]/1000%10) == 0) ? 10 : (Led_Time_Data[Led_Mode]/1000%10);
// 闪烁时:先判断千位是否为0,再判断是否闪烁
Seg_Buf[4] = ((Led_Time_Data[Led_Mode]/1000%10) == 0) ? 10 :
(Seg_Star_Flag ? Led_Time_Data[Led_Mode]/1000%10 : 10);
代码解析:
长亮写法:
// 单层三元运算符
(条件) ? 真值 : 假值
// 展开为:
if((Led_Time_Data[Led_Mode]/1000%10) == 0)
Seg_Buf[4] = 10; // 千位为0,显示熄灭
else
Seg_Buf[4] = Led_Time_Data[Led_Mode]/1000%10; // 千位不为0,显示数字
闪烁写法(嵌套三元运算符):
// 外层:判断千位是否为0
// 内层:根据闪烁标志决定显示还是熄灭
(千位==0) ? 熄灭 : (闪烁标志 ? 显示数字 : 熄灭)
// 展开为:
if((Led_Time_Data[Led_Mode]/1000%10) == 0)
{
Seg_Buf[4] = 10; // 千位为0,一直熄灭
}
else
{
if(Seg_Star_Flag)
Seg_Buf[4] = Led_Time_Data[Led_Mode]/1000%10; // 闪烁亮起时显示数字
else
Seg_Buf[4] = 10; // 闪烁熄灭时显示熄灭
}
6.
S4按键短长按判断存在的问题
当前代码实现:
void Key_Proc()
{
// ... 按键扫描代码 ...
if(Key_Down == 4) // S4按下
Time_Flag = 1; // 启动计时
if(Count_2000Ms < 2000) // ⚠️ 短按判断(外层)
{
if(Key_Up == 4) // S4抬起
{
Time_Flag = Count_2000Ms = 0; // 状态复位
if(Seg_Mode==0)
{
// 执行减操作...
}
}
}
else // ⚠️ 长按判断(外层)
{
if(Key_Old == 4) // S4长按中
{
if(Seg_Mode==0)
Seg_Mode=Seg_flag=1; // ⚠️ 会重复执行
}
if(Key_Up == 4) // S4抬起
Time_Flag = Count_2000Ms = Seg_Mode=Seg_flag=0;
}
}
// 定时器中断中累加计时器
void Timer1_Isr(void) interrupt 3
{
// ...
if(Time_Flag == 1)
{
if(++Count_2000Ms >= 2000)
Count_2000Ms = 2000; // 防止溢出
}
}
存在的问题:
-
长按触发重复执行

-
当
Count_2000Ms >= 2000后,每次Key_Proc()(每10ms)都会进入else分支 -
如果按键一直按着,
Seg_Mode=Seg_flag=1会重复赋值很多次 -
虽然多次赋值1不影响功能,但逻辑不优雅
-
-
短按判断时机不够精确

-
外层if持续检查
Count_2000Ms < 2000,即使没按键也在判断 -
更好的做法是在按键松开时判断计时器值
-
优化建议(参考标准写法):
bit Long_Press_Triggered = 0; // 添加长按触发标志
void Key_Proc()
{
// ... 按键扫描代码 ...
// S4按下:启动计时
if(Key_Down == 4)
{
Time_Flag = 1;
Long_Press_Triggered = 0; // ✓ 重置长按标志
}
// 长按判断:达到2000ms且未触发过
if(Count_2000Ms >= 2000 && !Long_Press_Triggered && Key_Old == 4)
{
Long_Press_Triggered = 1; // ✓ 标记已触发,只执行一次
if(Seg_Mode == 0)
Seg_Mode = Seg_flag = 1;
}
// S4松开:根据计时器值判断短按/长按
if(Key_Up == 4)
{
if(Count_2000Ms < 2000) // 短按
{
if(Seg_Mode == 0)
{
// 执行减操作...
}
}
else // 长按松开
{
Seg_Mode = Seg_flag = 0; // 关闭显示
}
// 复位计时器
Time_Flag = Count_2000Ms = 0;
}
}
优化要点:
-
使用Long_Press_Triggered标志位防止重复触发 -
在按键松开时统一判断短按/长按,逻辑更清晰 -
计时器复位统一处理,不会遗漏
注意: 当前代码虽然可以工作,但优化后逻辑更清晰,更符合编程规范。
总结
最严重的错误(会导致功能完全无法工作):
-
系统计时器未累加 - LED不会流转,核心功能失效
-
PWM变量混淆 - 亮度控制失效
中等错误(功能异常或不完整):
-
流转间隔索引错误 - 调整的不是当前模式的参数
-
高位0未熄灭 - 显示不美观,不符合数码管显示规范
轻微问题(可优化但不影响基本功能):
-
模式切换条件复杂 - 可以简化
-
长短按逻辑不完善 - 可能重复执行赋值
学到的经验
-
计时器逻辑三部曲 - 累加 → 判断 → 复位,不能忘记累加
-
变量命名要区分 -
period是计数器,compare是阈值;index是位置,mode是模式 -
数码管高位处理 - 高位为0时应该熄灭,使用三元运算符优雅处理
-
嵌套三元运算符技巧 -
(外层条件) ? 值1 : (内层条件 ? 值2 : 值3)可以处理多层逻辑 -
长按判断防重复 - 使用标志位确保长按只触发一次
-
按键状态机要清晰 - 按下启动计时 → 中断累加 → 松开时判断,逻辑要分明
-
逻辑要追求简洁 - 能用简单判断就不用复杂条件(KISS原则)
-
优化不影响功能也要做 - 代码可读性和可维护性同样重要
生成时间:2026-02-03
蓝桥杯第九届省赛代码调试总结