تا اینجا در تمام برنامهها از loop() برای بررسی مداوم وضعیت پایهها استفاده کردهایم (به این روش Polling میگویند). اما این روش برای رویدادهای سریع یا نامنظم بهینه نیست. راهحل بهتر، استفاده از وقفهها (Interrupts) است: تابعی که فقط دقیقاً در لحظهٔ وقوع یک رویداد (مثل فشردن دکمه) اجرا میشود.
ثبت یک وقفهٔ سختافزاری
const int LED_PIN = 2;
const int BUTTON_PIN = 4;
volatile bool ledState = false;
void IRAM_ATTR onButtonPress() {
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, FALLING);
}
void loop() {
Serial.println("برنامه اصلی همچنان در حال اجراست...");
delay(1000);
}تابع وقفه (ISR) باید بسیار کوتاه و سریع باشد و حتماً با عبارت IRAM_ATTR علامتگذاری شود (تا از حافظهٔ RAM سریعتر اجرا شود، نه فلش). از عملیات سنگین مثل delay طولانی، Serial.print زیاد، یا شبکه درون آن خودداری کنید؛ متغیرهایی که هم در ISR و هم در loop() استفاده میشوند باید volatile تعریف شوند.
تایمرهای نرمافزاری با کتابخانهٔ Ticker
کتابخانهٔ استاندارد Ticker (بخشی از هستهٔ ESP32-Arduino) به شما اجازه میدهد یک تابع را در بازههای زمانی مشخص، بدون مسدود کردن برنامهٔ اصلی، اجرا کنید — معادل کلاس Timer در میکروپایتون.
#include <Ticker.h>
const int LED_PIN = 2;
Ticker blinker;
bool ledState = false;
void toggleLed() {
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
}
void setup() {
pinMode(LED_PIN, OUTPUT);
blinker.attach(0.5, toggleLed); // هر ۰٫۵ ثانیه صدا زده میشود
}
void loop() {
// برنامهٔ اصلی آزاد است کارهای دیگری انجام دهد
}کارگاه عملی: با استفاده از RISING بهجای FALLING، رفتار متفاوت را مشاهده کنید و توضیح دهید چرا فرق میکند.