为什么 delay() 会破坏课堂上的 Arduino 项目(以及如何向孩子们教授 millis())

使用 delay() 会让微控制器进入休眠,从而导致传感器失明并锁死电机。本文介绍了如何利用手表思维模型向高年级学段(Senior Phase)学生教授非阻塞计时。
delay() 函数会破坏课堂上的 Arduino 项目,因为它会使微控制器完全停摆。在整个延迟期间,处理器什么都做不了:无法读取碰撞传感器,无法检测超声波测距读数,也无法对停止按钮做出响应。如果一辆轮式机器人在向前行驶时遇到了两秒的延迟,它就会在盲开两整秒撞上墙壁后,才会执行下一条指令。
要针对 7 至 9 年级学生解决这个问题,你不需要教授线程、硬件定时器中断或面向对象的有限状态机。你只需要一个具体的思维模型:看手表,而不是睡大觉。
课堂故障剖析
在几乎所有入门级机器人教学大纲中,第 1 课都是标准的 LED 闪烁:
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);这能跑通,能给学生带来即时满足感,但也埋下了一颗在三周后引爆的认知地雷。到了第 4 课,当要求学生制作行人过街红绿灯(闪烁车道灯同时检测按钮按下)或避障小车(驱动电机同时读取距离)时,他们的代码就彻底崩溃了。
学生写了一个循环:检测按钮、开启蜂鸣器,然后调用 delay(1000)。当他们按下按钮时,开发板通常毫无反应。为什么?因为处理器正忙着在 delay 内部消磨死循环周期。人手指按下并释放开关的瞬间,正好撞上了芯片在 99% 的循环周期中处于休眠状态的时候。
在一块运行频率为 16 MHz 的 ATmega328P 芯片上,调用一次 delay(1000) 就会白白浪费 1600 万个时钟周期去执行空汇编指令。机器人不是在多任务并行,而是卡死了。
看手表思维模型
在向学生展示代码之前,先让他们离开屏幕,进行一次五分钟的实操演示。请一位志愿者完成一项任务:“每三秒击一次掌,但如果我把这支笔扔下,你要在它掉到地上之前接住它。”
首先,让他们模拟 delay():告诉他们为了计时三秒,他们必须闭上眼睛,在脑海里默默数到三。在第二秒时扔下笔。他们每一次都会接空。因为他们在计时期间关闭了自己的感官。
接下来,给他们非阻塞(non-blocking)模型:告诉他们睁大双眼,看着教室墙上的挂钟(或数字手表),同时紧盯你的手。他们查看时间:如果距离上次击掌已经过去了三秒,他们就击掌。如果笔掉落,他们能立即做出反应。
这便将计时的概念从一个动作(“等待两秒”)转变为一种比较(“时间到了没有?”)。
核心模式:三个问题
一旦学生理解了墙上的挂钟,就可以引入 millis()。解释 millis() 就像一个内部秒表,在 Arduino 接通电源的那一毫秒就开始走动。它永不停歇,而且绝不会暂停代码。
每一个非阻塞事件都可以简化为持续循环中的三个通俗问题:
- 现在是几点?(读取秒表:
currentTime = millis()) - 我上次做这个动作是什么时候?(查看记录的便签:
previousTime) - 是否已经过去了规定的时长?(进行减法运算:
currentTime - previousTime >= interval)
如果第三个问题的答案是“是”,则执行动作并更新便签记录:设置 previousTime = currentTime。如果答案是“否”,则跳过该动作并立即继续往下走。由于循环在一毫秒的一小部分时间内就能跑完一轮,开发板因此能够每秒成千上万次地自由读取超声波传感器、检测限位开关并轮询巡线传感器。
如何教学语法而不让初学者望而生畏
对于 7 年级和 8 年级学生来说,围绕 unsigned long 的 C++ 样本代码往往会造成认知超载。在依照官方颁布的高年级学段(Senior Phase)编程与机器人课程标准教学的学校中,学生通常使用图形化编程环境或混合编辑器。
如果你使用的是图形化积木教学(例如基于 Scratch 的 Arduino 环境),转换过程非常直观:引入一个“计时器”积木和一个名为 last_time_checked 的变量。一旦引入了基础输入元件,就彻底杜绝使用“等待 1 秒”积木。
如果你教授的是基于文本的 Arduino C++,不要在没有任何教学支架的情况下把官方自带的 BlinkWithoutDelay 示例直接甩在他们的屏幕上。Arduino 官方示例同时融合了两个不同的概念:非阻塞计时以及状态翻转(反转 LED 状态变量)。这就是孩子们会感到困惑的原因。
将时间检查与执行逻辑剥离开来。改用以下教学顺序:
- 展示故障:让他们运行一个使用
delay()的 LED 闪烁程序,同时尝试触发紧急停止按钮。观察按钮无法触发时学生的挫败感。 - 引入计时器变量:向他们展示如何将
millis()打印到串口监视器(Serial Monitor),让他们看到数值持续不断地增加。这消除了该函数的神秘感——它不过是一个时间里程表。 - 编写定时包装结构:提供一个脚手架模板,将减法逻辑与机器人的行为逻辑分离开来。
| 方法 | 处理器状态 | 传感器响应速度 | 适用场景 |
|---|---|---|---|
delay(1000) | 停摆在忙等待(busy-wait)循环中 | 在延迟期间完全失明 | 单任务演示,4–6 年级入门 |
millis() 比较 | 以全时钟速度自由运行 | 即时(亚毫秒级轮询) | 机器人学、巡线小车、交互系统 |
让技能在课堂项目中扎根
当学生从小车“盲开”过渡到持续轮询传感器时,他们的调试体验将发生翻天覆地的变化。他们不必再苦思冥想为什么碰撞开关没能停住直流电机,而是能够清晰地追踪代码的执行流。
如果你的学校正在规划跨学期的递进式课程,准备从图形化逻辑过渡到实体机器人硬件,那么拥有成体系的教学步骤和经过验证的硬件方案将能消除摸索成本。我们在 Sheen Robotics for Schools 的团队致力于协助教师将这些嵌入式系统概念与课堂课时相衔接,确保学生在不良编程习惯形成之前,就建立起稳固可靠的思维模型。
一旦学生明白计算机绝不应该闭着眼睛数秒数,他们就已经跨越了门槛——从单纯地跑跑脚本,迈向了编写响应灵敏的真实软件的世界。



