第四周 蓝桥杯 过渡模拟二

一、代码功能概述 ( ̄▽ ̄)ゞ

核心功能

  • 温度采集:输入3位数字+小数点表示温度(如2.57°C)
  • 数据显示:显示当前温度值
  • 参数设置:设置温度上下限(10°C~70°C)
  • LED PWM控制:根据温度范围调整LED亮度(3档)
  • 长按支持:S14/S15支持短按和长按调整参数

三种显示模式

┌──────┬──────────────┬────────────────────────────┐
│ 模式 │ 功能 │ 显示格式 │
├──────┼──────────────┼────────────────────────────┤
│ 0 │ 温度采集界面 │ A–XXX(输入3位+小数点) │
├──────┼──────────────┼────────────────────────────┤
│ 1 │ 数据显示界面 │ C—XX(显示温度整数部分) │
├──────┼──────────────┼────────────────────────────┤
│ 2 │ 参数设置界面 │ P-XXYY(上限XX,下限YY) │
└──────┴──────────────┴────────────────────────────┘

二、需要注意的问题与解决方案 :warning:

问题1:长按检测代码重复 :warning:

问题代码:
// S14的长按检测(第51-74行)
if(Seg_Disp_Mode == 2)
{
if(Key_Down == 14)
Time_Flag = 1;
}
if(Count_500Ms < 500) // 短按
{
if(Key_Up == 14)
{
Time_Flag = Count_500Ms = 0;
if(++Parameter[Parameter_Index] > 70)
Parameter[Parameter_Index] = 10;
}
}
else // 长按
{
if(Key_Old == 14)
{
if(++Parameter[Parameter_Index] > 70)
Parameter[Parameter_Index] = 10;
}
if(Key_Up == 14)
Time_Flag = Count_500Ms = 0;
}

// S15的长按检测(第77-100行)
// 几乎完全相同的代码!

问题分析:

  • S14和S15的长按检测代码几乎完全重复
  • 代码冗余,不利于维护
  • 如果要修改逻辑,需要改两处

改进方案:
// ✓ 封装成函数
void Handle_Long_Press(unsigned char key_code, char increment)
{
if(Seg_Disp_Mode != 2) return; // 只在参数设置界面有效

  // 按下时启动计时
  if(Key_Down == key_code)
      Time_Flag = 1;

  // 短按处理
  if(Count_500Ms < 500)
  {
      if(Key_Up == key_code)
      {
          Time_Flag = Count_500Ms = 0;
          Parameter[Parameter_Index] += increment;
          if(Parameter[Parameter_Index] > 70 || Parameter[Parameter_Index] < 10)
              Parameter[Parameter_Index] = (increment > 0) ? 10 : 70;
      }
  }
  // 长按处理
  else
  {
      if(Key_Old == key_code)
      {
          Parameter[Parameter_Index] += increment;
          if(Parameter[Parameter_Index] > 70 || Parameter[Parameter_Index] < 10)
              Parameter[Parameter_Index] = (increment > 0) ? 10 : 70;
      }
      if(Key_Up == key_code)
          Time_Flag = Count_500Ms = 0;
  }

}

// 使用时
Handle_Long_Press(14, 1); // S14:参数+1
Handle_Long_Press(15, -1); // S15:参数-1


问题2:复杂的位运算表达式 :warning:

问题代码:
// 第247行
ucLed[1] = (!((int)Temperature / Parameter_Ctrol[0])) & ((int)Temperature / Parameter_Ctrol[1]);

问题分析:

  • 表达式过于复杂,难以理解
  • 使用了多重类型转换和位运算
  • 可读性极差

表达式含义:
Temperature < Parameter_Ctrol[0] 且 Temperature >= Parameter_Ctrol[1]
即:温度在下限和上限之间时,LED1点亮

改进方案:
// ✓ 方法1:拆分成清晰的逻辑
unsigned char is_below_upper = (Temperature < Parameter_Ctrol[0]);
unsigned char is_above_lower = (Temperature >= Parameter_Ctrol[1]);
ucLed[1] = is_below_upper && is_above_lower;

// ✓ 方法2:直接用条件判断
if(Temperature < Parameter_Ctrol[0] && Temperature >= Parameter_Ctrol[1])
ucLed[1] = 1;
else
ucLed[1] = 0;

// ✓ 方法3:三元运算符
ucLed[1] = (Temperature < Parameter_Ctrol[0] && Temperature >= Parameter_Ctrol[1]) ? 1 : 0;


问题3:除零风险 :warning::warning:

问题代码:
// 第246-248行
ucLed[0] = (int)Temperature / Parameter_Ctrol[0];
ucLed[1] = (!((int)Temperature / Parameter_Ctrol[0])) & ((int)Temperature / Parameter_Ctrol[1]);
ucLed[2] = !((int)Temperature / Parameter_Ctrol[1]);

风险分析:

  • 如果Parameter_Ctrol[0]或Parameter_Ctrol[1]为0,会导致除零错误
  • 虽然初始值是30和20,但如果代码有BUG导致参数被错误修改,就会崩溃

可能导致除零的场景:
场景1:参数设置时出现BUG
→ Parameter[0]或Parameter[1]被设为0
→ 保存到Parameter_Ctrol时,值为0
→ 除零崩溃!

场景2:内存被意外修改
→ 野指针或数组越界
→ Parameter_Ctrol被改为0
→ 除零崩溃!

改进方案:
// ✓ 添加除零保护
void Led_Proc()
{
// 参数有效性检查
if(Parameter_Ctrol[0] == 0) Parameter_Ctrol[0] = 30;
if(Parameter_Ctrol[1] == 0) Parameter_Ctrol[1] = 20;

  // LED亮度控制
  if(Temperature > Parameter_Ctrol[0])
      Led_Pwm = 3;
  else if(Temperature < Parameter_Ctrol[0] && Temperature > Parameter_Ctrol[1])
      Led_Pwm = 6;
  else
      Led_Pwm = 9;

  // LED状态控制(添加除零保护)
  ucLed[0] = (Parameter_Ctrol[0] > 0) ? ((int)Temperature / Parameter_Ctrol[0]) : 0;
  ucLed[1] = (Temperature < Parameter_Ctrol[0] && Temperature >= Parameter_Ctrol[1]) ? 1 : 0;
  ucLed[2] = (Parameter_Ctrol[1] > 0) ? (!((int)Temperature / Parameter_Ctrol[1])) : 0;
  ucLed[3] = Error_Flag;

}


问题4:参数边界判断不完整 :warning:

问题代码:
// 第168行
if(Parameter[0] >= Parameter[1] && Parameter[0] <= 70 && Parameter[1] >= 10)
{
Error_Flag = 0;
Parameter_Ctrol[0] = Parameter[0];
Parameter_Ctrol[1] = Parameter[1];
}
else
Error_Flag = 1;

问题分析:

  • 只检查了Parameter[0] <= 70和Parameter[1] >= 10
  • 没有检查Parameter[0] >= 10(上限可能小于10)
  • 没有检查Parameter[1] <= 70(下限可能大于70)

可能的问题场景:
场景1:上限设为5°C
→ Parameter[0] = 5
→ 5 >= 20(下限)? 否
→ Error_Flag = 1 ✓(正确拒绝)

场景2:下限设为75°C
→ Parameter[1] = 75
→ 30 >= 75? 否
→ Error_Flag = 1 ✓(正确拒绝)

但是:如果上限是8°C,下限是5°C
→ 8 >= 5 ✓, 8 <= 70 ✓, 5 >= 10 ✗
→ Error_Flag = 1 ✓(正确拒绝)

看起来逻辑是对的,但不够清晰

改进方案:
// ✓ 更清晰的边界检查
if(Parameter[0] >= 10 && Parameter[0] <= 70 && // 上限范围检查
Parameter[1] >= 10 && Parameter[1] <= 70 && // 下限范围检查
Parameter[0] >= Parameter[1]) // 上限≥下限
{
Error_Flag = 0;
Parameter_Ctrol[0] = Parameter[0];
Parameter_Ctrol[1] = Parameter[1];
}
else
{
Error_Flag = 1;
}


问题5:长按时参数调整速度问题 :warning:

问题代码:
// 第67-71行
if(Key_Old == 14) // S14长按
{
if(++Parameter[Parameter_Index] > 70)
Parameter[Parameter_Index] = 10;
}

问题分析:

  • 长按时,每10ms(按键扫描周期)就会执行一次参数加1
  • 调整速度太快,用户难以精确控制
  • 从10调到70需要60次,按住0.6秒就完成了

改进方案:
// ✓ 方法1:添加长按减速
static unsigned char long_press_slow_down = 0;

if(Key_Old == 14)
{
if(++long_press_slow_down >= 10) // 每100ms调整一次
{
long_press_slow_down = 0;
if(++Parameter[Parameter_Index] > 70)
Parameter[Parameter_Index] = 10;
}
}
else
{
long_press_slow_down = 0; // 松开时复位
}

// ✓ 方法2:根据长按时间加速
if(Key_Old == 14)
{
if(Count_500Ms < 1000) // 前1秒:慢速
{
if(++long_press_slow_down >= 10)
{
long_press_slow_down = 0;
Parameter[Parameter_Index]++;
}
}
else // 1秒后:快速
{
if(++long_press_slow_down >= 3)
{
long_press_slow_down = 0;
Parameter[Parameter_Index]++;
}
}

  if(Parameter[Parameter_Index] > 70)
      Parameter[Parameter_Index] = 10;

}


问题6:注释掉的代码 :warning:

问题代码:
// 第182-195行
//\t\tcase 14://参数+1
//\t\t\tif(Seg_Disp_Mode == 2)//处于参数设置界面
//\t\t\t{
//\t\t\t\tif(++Parameter[Parameter_Index] > 70)//超过上限值
//\t\t\t\t\tParameter[Parameter_Index] = 10;
//\t\t\t}
//\t\tbreak;

问题分析:

  • 注释掉的代码说明这是旧版本的实现
  • 新版本改用了长按检测(第51-100行)
  • 但注释掉的代码没有删除,影响代码整洁度

建议:
// ✓ 删除注释掉的代码
// 如果需要保留历史记录,使用版本控制系统(Git)
// 不要在代码中保留大段注释掉的代码


三、代码优点 ✓

本小姐要表扬一下这个代码的优点!( ̄▽ ̄*)

  1. 编码正确:使用UTF-8编码,注释正常显示
  2. 长按支持:实现了短按和长按功能,用户体验好
  3. PWM调光:LED亮度分3档,根据温度自动调整
  4. 参数验证:切换界面时会验证参数合理性
  5. 代码优化:第239行注释掉的复杂表达式被简化了
  6. 边界检查:第45行正确判断了数组索引范围

四、改进建议总结 o( ̄▽ ̄)d

优先级1(必须修复)

  1. 添加除零保护:防止Parameter_Ctrol为0导致崩溃
  2. 封装长按检测:消除代码重复

优先级2(强烈建议)

  1. 简化位运算表达式:提高可读性
  2. 添加长按减速:改善用户体验
  3. 完善边界检查:使参数验证逻辑更清晰

优先级3(建议)

  1. 删除注释代码:保持代码整洁
  2. 添加宏定义:替换魔法数字

五、完整改进示例 (* ̄︶ ̄)

// 添加宏定义
#define TEMP_MIN 10
#define TEMP_MAX 70
#define TEMP_UPPER_DEFAULT 30
#define TEMP_LOWER_DEFAULT 20

#define LED_PWM_HIGH 3 // 高温:暗
#define LED_PWM_MID 6 // 中温:中
#define LED_PWM_LOW 9 // 低温:亮

// 改进的LED处理函数
void Led_Proc()
{
// 参数有效性检查(防止除零)
if(Parameter_Ctrol[0] == 0) Parameter_Ctrol[0] = TEMP_UPPER_DEFAULT;
if(Parameter_Ctrol[1] == 0) Parameter_Ctrol[1] = TEMP_LOWER_DEFAULT;

  // LED亮度控制(清晰的逻辑)
  if(Temperature > Parameter_Ctrol[0])
      Led_Pwm = LED_PWM_HIGH;
  else if(Temperature > Parameter_Ctrol[1])
      Led_Pwm = LED_PWM_MID;
  else
      Led_Pwm = LED_PWM_LOW;

  // LED状态控制(清晰的逻辑)
  ucLed[0] = (Temperature > Parameter_Ctrol[0]) ? 1 : 0;
  ucLed[1] = (Temperature <= Parameter_Ctrol[0] && Temperature > Parameter_Ctrol[1]) ? 1 : 0;
  ucLed[2] = (Temperature <= Parameter_Ctrol[1]) ? 1 : 0;
  ucLed[3] = Error_Flag;

}

// 改进的参数验证
if(Parameter[0] >= TEMP_MIN && Parameter[0] <= TEMP_MAX &&
Parameter[1] >= TEMP_MIN && Parameter[1] <= TEMP_MAX &&
Parameter[0] >= Parameter[1])
{
Error_Flag = 0;
Parameter_Ctrol[0] = Parameter[0];
Parameter_Ctrol[1] = Parameter[1];
}
else
{
Error_Flag = 1;
}


哥们,你用的啥ai

用的claude

那是不是每问一次都要花一点钱

1 个赞

是的,不过目前我根据的使用情况来看,没怎么花钱