第16届蓝桥杯单片机省赛第2部分

第16届蓝桥杯单片机省赛第2部分

这题复盘笔记(优化美化版)

一、先抓题目的“核心逻辑骨架”

这题本质上是 4 条主线同时跑:

1. 采集链路

  • 温度:DS18B20
  • 光敏:AD 采集
  • 距离:超声波 1 秒一次

2. 判定链路

  • 温度高于参数 Para_TepTep_Flag=1
  • 距离低于参数 Para_DsClose_Flag=1
  • 连续两次距离差判断运动状态:
    • <5:静止
    • <10:徘徊
    • >=10:跑动
      状态变化后锁定 3 秒

3. 输出链路

  • L1~L4:根据光照等级显示,但前提是“接近”成立
  • L8:根据运动状态显示
  • 继电器:接近 && 高温 时吸合。

4. 交互链路

  • S4:四个主界面循环切换
  • S5:参数界面两个子界面切换
  • S8/S9:加减参数
  • S8+S9:统计界面长按 2 秒清零。

二、我建议你以后复习时,优先盯这 8 个大坑

1)Distance_old 忘记更新

这是运动状态判断最经典的坑。

错误本质

运动状态要靠:

abs(Distance - Distance_old)

如果 Distance_old 不更新,那么后面每次比较的都是旧旧旧数据,状态判断会越来越偏。

正确思路

每次完成一次有效距离采样后,都要把当前值存成“上一帧”:

Distance_old = Distance;

你这里的重点

你已经发现这一点了,这个必须记死。你原始笔记里也特别提到了这个错误。


2)第一次进入距离判定时,必须用 F_Check 做首帧保护

为什么要这样做

第一次采样时,没有“上一帧距离”可比。
所以第一次不能直接做:

abs(Distance - Distance_old)

否则是拿垃圾旧值在比较。

正确做法

第一次只做初始化:

if(F_Check==1)
{
    Distance_old=Distance;
    sport_state=1;
    sport_state_old=1;
    F_Check=0;
}

你这里的易错点

你自己已经指出:F_Check=0 原本忘了。
这个一忘,程序会永远反复走“首次初始化分支”,后面根本进不了正常判定流程。


3)距离采样节拍不要写错,特别是比较符号

题目要求是设备间隔 1 秒采集一次距离数据

你这里写的是:

if(Ds_Slow_Down<=999)return;
Ds_Slow_Down=0;

这个写法的理解

这是“没到 1000ms 就返回,到点了才执行一次”。

易错点

很多人会乱改成:

if(Ds_Slow_Down>=999)return;

这会导致逻辑完全反掉,测距函数可能一直不执行。

记忆法

你要养成一句话:

慢任务函数里,前面这个 if 是“没到时间就退出”。

你原始笔记里已经专门提醒“不要写成 >=”。这个提醒很对。


4)Ds_Slow_Down 的类型不能太小

这是隐藏很深但很致命的错误。

如果 Ds_Slow_Down 定义成 u8,它最大只能到 255,永远到不了 999,那么:

if(Ds_Slow_Down<=999)return;

就会一直 return,Get_Distance() 根本进不去。
你原始记录里已经抓到了这一点:Ds_Slow_Down 类型 u8,导致进不去中断/逻辑

结论

凡是计到几百、几千毫秒的计数器,优先想:

  • unsigned int
  • 或更大的类型

不要顺手全写 u8


5)AD 通道别读错:0x01 是光敏,0x03 是滑动

你这里:

AD_1_Data_100x=(float)AD_Read(0x01)/51.0f*100;

你笔记里强调得很对:

  • 0x01:光敏
  • 0x03:滑动。

为什么这是高频坑

因为比赛里 AD 芯片常常一题多个通道,很多人逻辑写对了,结果读错通道,表现就全乱。

复习时这样记

不是背 0x010x03,而是背:

“先看原理图/题目接线,再看 AD 通道。”


6)参数界面里的按键逻辑,必须限制作用域

题目规定:

  • S5 切换参数子界面
  • S8/S9 调参数
    这些都只在参数界面有效。

所以你这里这个判断非常关键:

if(Seg_Show_Mode==2)
{
    ...
}

易错点 1

如果不加这个限制,那么你在别的界面按 S5/S8/S9,也会偷偷改参数。

易错点 2

你还特别提醒了:

Para_Mode 要在 Seg_Show_Mode==2 限制下修改,不然会被误修改。
这个非常对。


7)参数数值单位要想清楚:显示值和存储值别混

你自己这个提醒也很好:

Para_Tep 在数码管显示的值到底是 ×10 还是 ×100,要特别小心。

这类题最怕什么

最怕“显示单位”和“内部比较单位”不统一。

比如你代码里:

Temperature_10x = rd_temperture() * 10;
Para_Tep = 300;

这说明:

  • 温度内部比较单位是 0.1℃
  • 300 实际表示 30.0℃

但题目显示的是整数温度参数 20~80℃。

所以复习时要强制问自己 3 个问题

  1. 这个变量是“真实值”还是“放大后的值”?
  2. 比较的时候单位统一了吗?
  3. 显示的时候有没有做对应换算?

8)继电器逻辑不是“写了变量就算完”,必须真的调用输出函数

你这里这一段很关键:

Relay_Flag=(Close_Flag==1&&Tep_Flag==1);
...
Relay(Relay_Flag);

你在笔记里提到:

Relay() 从未调用,以及这里的状态机,按键是事件触发不是状态触发。

这个坑的本质

很多人以为:

Relay_Flag = 1;

就等于继电器吸合了。
其实不是。
这只是“软件里的逻辑状态变了”,真正硬件动作还得靠:

Relay(Relay_Flag);

一句口诀

判定是判定,输出是输出。变量变化 ≠ 硬件动作。


三、你这题里一个特别容易忽略的硬件坑:P2 操作影响 I2C

这是你笔记里最有价值的一条之一。

你对比发现:

你的 Seg.c 某处写成了:

temp = P2 | 0x1f;

正确参考应是:

temp = P2 & 0x1f;
temp = temp | 0xc0;
P2 = temp;

为什么错得这么严重

因为 P2 | 0x1f 会把低 5 位全置 1。
而低位里又可能挂了 I2C 相关线,导致数码管扫描时不停干扰 I2C 通信,最终表现成:

  • AD 读数乱跳
  • PCF8591 数据异常
  • 看起来像“采样不稳”,但根因其实是端口操作污染了总线。

这类问题怎么记

以后只要遇到:

  • 数码管扫描异常
  • I2C 读数乱跳
  • 明明逻辑没问题但数据抖得离谱

就先检查:

  1. 端口复用
  2. 位操作是否写错了 | / &
  3. 定时器中断里有没有反复改共享口线

这个经验非常值钱。


四、按键部分,你要牢牢记一句话

按键是“事件触发”,不是“状态触发”。

你自己已经意识到这一点了,这说明你思路在往对的方向走。

什么叫事件触发

例如:

Key_Down = Key_Val & (Key_Old ^ Key_Val);

这个代表“刚按下那一下”。

为什么不能直接用 Key_Val

因为 Key_Val 表示“当前正在按着”,如果拿它直接改界面、加参数,就会出现:

  • 一按一下跳很多次
  • 长按疯狂连发
  • 状态错乱

所以考场上要这样分

  • Key_Down:适合切界面、加减一次参数
  • Key_Up:适合松手触发的动作
  • Key_Old==组合键值 + 计时:适合长按判定

五、你这题还要特别注意“参数界面锁定输出”

题目明确要求:

进入参数界面时,LED、继电器状态锁定(不可变化),退出参数界面后取消状态锁定。

你代码里是这样处理的:

if(Seg_Show_Mode==2) Para_Lock=1;
else Para_Lock=0;

然后在 Led_Proc()AD_DA() 等地方去判断 Para_Lock。这条主思路是对的。

复习时别只看“能不能切界面”

还要看:

  • 进入参数界面后,LED 是否保持原状
  • 继电器是否保持原状
  • 采集功能是否还在继续
  • 退出参数界面后是否恢复实时控制

因为题目要求的是“锁定输出状态”,不是“停止整个系统”。


六、给你整理成一版“考场速查版”

1. 运动状态判断四连查

  • 有没有每 1 秒采一次距离
  • Distance_old 有没有更新
  • 首次采样有没有 F_Check
  • 状态变化后有没有锁定 3 秒。

2. 参数界面三连查

  • S5/S8/S9 是否只在参数界面有效
  • 进入参数界面默认是不是温度参数界面
  • 参数范围有没有卡死在 20~80。

3. 继电器两连查

  • 条件是不是 Close_Flag && Tep_Flag
  • 有没有真正调用 Relay() 输出。

4. 数码管/I2C 一连查

  • P2 位操作是不是写错成 |0x1f,导致 I2C 被干扰。

5. 定时变量类型一连查

  • 需要计到几百/几千 ms 的变量,别用 u8

七、我帮你再压缩成“最终背诵版”

这题最容易死的点

  1. 距离旧值没更新
  2. 首次采样没做保护
  3. 1 秒采样节拍写反
  4. Ds_Slow_Down 类型太小
  5. AD 通道读错
  6. 参数界面按键没加限制
  7. 温度参数单位混乱
  8. 继电器只改变量没真输出
  9. P2 写错影响 I2C
  10. 把按键当状态触发,不是事件触发

八、给你一个更适合记在笔记本上的版本

本题一句话总纲

采集要准,判定要稳,输出要分离,按键要事件化,界面切换不能影响主功能。

本题调试顺序

先采集 → 再判定 → 再显示 → 再按键 → 最后查锁定与长按。

本题最值得反复提醒自己的三句话

1. 变量更新时机比公式本身更重要。
2. 输出函数没调用,前面全白写。
3. 端口操作错一位,外设全乱。