Arduino - 时间
Arduino - 时间与并发
Section titled “Arduino - 时间与并发”管理时间是嵌入式编程最基本的一个方面。Arduino 提供了简单的函数来暂停执行,但要创建响应式和复杂的项目,你必须学会以非阻塞方式处理时间。
delay() 函数:简单但阻塞
Section titled “delay() 函数:简单但阻塞”delay() 函数通常是初学者学习的第一个时间函数。它会暂停程序指定数量的毫秒。
函数签名:
void delay(unsigned long ms); // 暂停 'ms' 毫秒void delayMicroseconds(unsigned int us); // 暂停 'us' 微秒delay() 的问题: 当你的程序处于 delay() 调用中时,它会阻塞。它无法读取传感器、检查按钮按下,或通过串口进行通信。这使得它不适用于任何需要同时处理多项任务的项目。
millis() 函数:非阻塞代码的关键
Section titled “millis() 函数:非阻塞代码的关键”millis() 函数是 Arduino 上多任务处理的基石。它返回自 Arduino 开发板开始运行当前程序以来经过的毫秒数。
函数签名:
unsigned long millis(); // 返回自启动以来的毫秒数核心思想: 不是暂停程序,而是记录事件发生的时间。然后,在你的主 loop() 函数中,你持续检查是否已过去足够的时间来触发下一个事件。这就像看钟表来判断是否该做某事,而不是启动秒表并一直盯着它。
示例:不使用 delay() 闪烁 LED
Section titled “示例:不使用 delay() 闪烁 LED”这是一个典型示例,演示了 millis() 的强大功能。它在闪烁 LED 的同时,让 loop() 函数可以自由地执行其他任务。
// LED 引脚和闪烁间隔的常量const int ledPin = LED_BUILTIN; // 使用内置 LEDconst long interval = 1000; // 闪烁的间隔(1000 毫秒 = 1 秒)
// 存储状态的变量int ledState = LOW; // ledState 用于设置 LED
// 将存储 LED 上次更新的时间unsigned long previousMillis = 0;
void setup() { pinMode(ledPin, OUTPUT);}
void loop() { // 获取当前时间 unsigned long currentMillis = millis();
// 检查是否是时候闪烁 LED 了 if (currentMillis - previousMillis >= interval) { // 保存上次闪烁 LED 的时间 previousMillis = currentMillis;
// 如果 LED 熄灭,则打开它,反之亦然 if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; }
// 用新的 ledState 设置 LED digitalWrite(ledPin, ledState); }
// --- 你可以在这里添加其他代码 --- // 例如,读取传感器或检查按钮。 // 这段代码将持续运行,不会被 LED 闪烁阻塞。}微秒精度:micros()
Section titled “微秒精度:micros()”对于需要更高时间分辨率的任务(例如控制舵机或读取某些传感器),可以使用 micros() 函数。它的工作方式与 millis() 完全相同,但返回自程序启动以来的微秒数。
函数签名:
unsigned long micros(); // 返回自启动以来的微秒数处理回绕(溢出)
Section titled “处理回绕(溢出)”millis() 函数使用 unsigned long 类型,它可以存储高达 4,294,967,295 的值。这意味着 millis() 将在大约 49.7 天后溢出(重置为零)。micros() 函数溢出速度快得多,大约 70 分钟后就会溢出。
幸运的是,“不使用 delay() 闪烁 LED”示例中使用的减法技术(currentMillis - previousMillis)旨在正确处理这种回绕,这得益于无符号整数算术的特性。只要你使用这种模式,对于短于 49 天的间隔,你无需担心回绕问题。
- 避免使用
delay():对于任何需要执行多个简单、顺序任务的项目,请避免使用delay()。尽早采用millis()模式。 - 使用
unsigned long:始终将millis()或micros()的结果存储在unsigned long变量中,以避免溢出问题和错误。 - 管理多个定时器:你可以通过为每个独立定时事件使用单独的
previousMillis和interval变量来管理它们。 - 选择正确的工具:对于大多数人类尺度的时间(秒、分钟),请使用
millis()。仅当你在快速硬件交互中需要非常高的精度时才使用micros()。