第四周 蓝桥杯 过渡模拟一

这是一个电压采集与计数系统,具有以下功能:

核心功能

  • 电压采集:输入4位数字表示电压值(0.001V~9.999V)
  • 数据显示:显示当前电压值
  • 参数设置:设置参考电压(1.0V~6.0V,步进0.5V)
  • 计数统计:统计电压下降沿次数(从高于参考值变为低于参考值)
  • LED指示:
    • LED0:电压低于参考值超过5秒点亮
    • LED1:计数值奇偶指示
    • LED2:无效按键指示(≥3次点亮)

四种显示模式

┌──────┬──────────────┬─────────────────────────┐
│ 模式 │ 功能 │ 显示格式 │
├──────┼──────────────┼─────────────────────────┤
│ 0 │ 电压采集界面 │ --XXXX(输入4位数字) │
├──────┼──────────────┼─────────────────────────┤
│ 1 │ 数据显示界面 │ A–X.XX(显示电压) │
├──────┼──────────────┼─────────────────────────┤
│ 2 │ 参数设置界面 │ C–X.XX(设置参考电压) │
├──────┼──────────────┼─────────────────────────┤
│ 3 │ 计数统计界面 │ PXXXXX(显示计数值) │
└──────┴──────────────┴─────────────────────────┘

二、严重BUG与解决方案 :warning::warning::warning:

BUG 1:数字输入逻辑错误(第40-48行) :red_circle:

问题代码:
if(Key_Down >=1 && Key_Down <= 10)
{
if(Seg_Disp_Mode==0 && Seg_Input_Index<4)
Seg_Input[Seg_Input_Index]=Key_Down - 1; // :cross_mark: 缺少大括号
Seg_Input_Index++; // :cross_mark: 这行总是执行!
Key_Error_Count=0; // :cross_mark: 这行总是执行!
}
else // :cross_mark: 这个else对应的是外层if
Key_Error_Count++;

问题分析:

  • 第43行的if语句后面没有大括号
  • 导致第44-45行(Seg_Input_Index++和Key_Error_Count=0)总是执行
  • 即使不在采集界面或数组已满,指针也会增加
  • 会导致数组越界和逻辑混乱

后果:
场景1:在非采集界面按数字键
→ Seg_Input_Index++ 仍然执行
→ 指针不断增加,可能超过3
→ 下次进入采集界面时,数组越界!

场景2:输入满4位后继续按数字键
→ Seg_Input_Index继续增加(4, 5, 6…)
→ 数组越界访问!

正确写法:
if(Key_Down >= 1 && Key_Down <= 10)
{
if(Seg_Disp_Mode == 0 && Seg_Input_Index < 4)
{
Seg_Input[Seg_Input_Index] = Key_Down - 1;
Seg_Input_Index++;
Key_Error_Count = 0;
}
else
{
Key_Error_Count++;
}
}


BUG 2:界面切换逻辑错误(第91-93行) :red_circle:

问题代码:
case 12: // 界面切换按键
if(Seg_Disp_Mode != 0)
{
Key_Error_Count = 0;
if(Seg_Disp_Mode == 2)
Voltage_Parameter_Ctrol = Voltage_Parameter; // 保存参数
Seg_Disp_Mode++; // :cross_mark: 第一次加1
if(++Seg_Disp_Mode == 4) // :cross_mark: 又加1!
Seg_Disp_Mode = 1;
}

问题分析:

  • 第91行:Seg_Disp_Mode++(加1)
  • 第92行:++Seg_Disp_Mode(又加1)
  • 导致每次按S12,模式会跳过一个!

实际效果:
初始:模式1(数据显示)
按S12 → 91行:1+1=2,92行:2+1=3 → 跳到模式3(计数统计)
→ 跳过了模式2(参数设置)!

初始:模式2(参数设置)
按S12 → 91行:2+1=3,92行:3+1=4,判断==4,设为1
→ 跳到模式1
→ 跳过了模式3!

正确写法:
case 12:
if(Seg_Disp_Mode != 0)
{
Key_Error_Count = 0;
if(Seg_Disp_Mode == 2)
Voltage_Parameter_Ctrol = Voltage_Parameter;

      // ✓ 方法1:只用一次++
      if(++Seg_Disp_Mode == 4)
          Seg_Disp_Mode = 1;

      // ✓ 方法2:更清晰的写法
      Seg_Disp_Mode++;
      if(Seg_Disp_Mode >= 4)
          Seg_Disp_Mode = 1;
  }
  else
      Key_Error_Count++;

break;


BUG 3:变量j未初始化(第193行) :red_circle:

问题代码:
case 3: // 计数统计界面
// …
unsigned char j; // :cross_mark: 第143行声明但未初始化
// …
while(Seg_Buf[j] == 0) // :cross_mark: 第193行:j的值是随机的!
{
Seg_Buf[j] = 10;
if(++j == 5)
break;
}
break;

问题分析:

  • 变量j在第143行声明,但没有初始化
  • 第193行直接使用j,其值是随机的(可能是0,也可能是255)
  • 如果j的初始值很大,会导致数组越界

后果:
场景1:j初始值为0(运气好)
→ 正常工作,去除前导零

场景2:j初始值为100(运气不好)
→ Seg_Buf[100]访问越界!
→ 程序崩溃或显示异常

正确写法:
case 3:
Seg_Point[3+(int)Voltage/10] = 0;
Seg_Buf[0] = 14;
Seg_Buf[1] = Count / 10000 % 10;
Seg_Buf[2] = Count / 1000 % 10;
Seg_Buf[3] = Count / 100 % 10;
Seg_Buf[4] = Count / 10 % 10;
Seg_Buf[5] = Count % 10;

  // ✓ 初始化j
  j = 1;  // 从第1位开始去除前导零
  while(j < 5 && Seg_Buf[j] == 0)  // ✓ 添加边界检查
  {
      Seg_Buf[j] = 10;
      j++;
  }

break;


BUG 4:Led_Pos未更新(第243行) :red_circle:

问题代码:
void Timer0Server() interrupt 1
{
// …
if(++Seg_Pos == 6) Seg_Pos = 0; // ✓ 数码管位置更新
Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos], Seg_Point[Seg_Pos]);

  Led_Disp(Led_Pos, ucLed[Led_Pos]);  // ❌ Led_Pos没有更新!
  // ...

}

问题分析:

  • Seg_Pos在中断中每1ms加1,实现数码管扫描
  • 但Led_Pos没有更新,一直是0
  • 导致只有第0个LED会被刷新,其他LED不工作

后果:
LED0:正常工作(一直被刷新)
LED1:不工作(从不被刷新)
LED2:不工作(从不被刷新)
LED3-7:不工作

正确写法:
void Timer0Server() interrupt 1
{
TL0 = 0x18;
TH0 = 0xFC;

  if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
  if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;

  // 数码管扫描
  if(++Seg_Pos == 6) Seg_Pos = 0;
  Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos], Seg_Point[Seg_Pos]);

  // ✓ LED扫描
  if(++Led_Pos == 8) Led_Pos = 0;
  Led_Disp(Led_Pos, ucLed[Led_Pos]);

  if(Voltage < Voltage_Parameter_Ctrol)
      Sys_Tick++;

  if(++Timer_500Ms == 500)
  {
      Timer_500Ms = 0;
      Seg_Flag ^= 1;
  }

}