🧩 1. ความหมายของ Interrupt
Interrupt (การขัดจังหวะ) คือ “เหตุการณ์ที่ทำให้ไมโครคอนโทรลเลอร์หยุดทำงานปัจจุบันชั่วคราว เพื่อไปทำสิ่งที่สำคัญกว่า”
เช่น
- เมื่อกดปุ่ม → ให้ขัดจังหวะไปทำฟังก์ชัน “toggle LED”
- เมื่อเซนเซอร์จับสัญญาณ → ให้ขัดจังหวะเพื่ออ่านค่า
- เมื่อมีข้อมูล Serial เข้า → ให้ขัดจังหวะเพื่อรับทันที
หลังจากทำเสร็จ ระบบจะ กลับมาทำงานต่อจากจุดเดิมอัตโนมัติ
⚙️ 2. ประเภทของ Interrupt
| ประเภท | แหล่งที่มา | ตัวอย่าง |
|---|---|---|
| Hardware Interrupt | สัญญาณจากภายนอก MCU | ปุ่มกด, เซนเซอร์, Timer |
| Software Interrupt | คำสั่งในโปรแกรม | ใช้ yield(), attachInterrupt() เรียกเอง |
Arduino และ ESP32 ใช้แบบ Hardware Interrupt เป็นหลัก
🔹 3. โครงสร้างการใช้งาน Interrupt (Arduino / ESP32)
attachInterrupt(digitalPinToInterrupt(pin), ISR_function, mode);
| พารามิเตอร์ | ความหมาย |
|---|---|
pin | ขาที่จะรับสัญญาณขัดจังหวะ |
ISR_function | ฟังก์ชันที่จะทำงานเมื่อเกิดเหตุการณ์ |
mode | เงื่อนไขการขัดจังหวะ เช่น RISING, FALLING, CHANGE, HIGH, LOW |
🔹 4. เงื่อนไขของ Interrupt Mode
| โหมด | คำอธิบาย | ตัวอย่าง |
|---|---|---|
RISING | ขาเปลี่ยนจาก LOW → HIGH | ปุ่มกดจาก 0 → 1 |
FALLING | ขาเปลี่ยนจาก HIGH → LOW | ปุ่มปล่อย |
CHANGE | เมื่อขาเปลี่ยนสถานะใด ๆ | กด/ปล่อยปุ่ม |
HIGH | เมื่อขาเป็น HIGH | สัญญาณค้างสูง |
LOW | เมื่อขาเป็น LOW | สัญญาณค้างต่ำ |
🔹 5. ตัวอย่าง: ปุ่มขัดจังหวะควบคุมไฟ LED
📘 ตัวอย่างนี้แสดงให้เห็นว่า LED จะเปลี่ยนสถานะทุกครั้งที่มีการ “กดปุ่ม”
ไม่ต้องใช้ digitalRead() ใน loop เลย!
const int button = 4;
const int led = 2;
volatile bool ledState = false;
void IRAM_ATTR toggleLED() { // ISR (Interrupt Service Routine)
ledState = !ledState;
digitalWrite(led, ledState);
}
void setup() {
pinMode(led, OUTPUT);
pinMode(button, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button), toggleLED, FALLING);
}
void loop() {
// MCU ทำงานอื่น ๆ ต่อได้เลย
delay(1000);
Serial.println("Main loop running...");
}
คำสั่ง
volatileใช้บอกคอมไพเลอร์ว่า ตัวแปรledStateอาจถูกเปลี่ยนค่าโดย Interrupt ได้ทุกเมื่อ
🔹 6. การอธิบายการทำงานของโค้ด
| ขั้นตอน | การทำงาน |
|---|---|
| 1 | เมื่อกดปุ่ม (LOW → FALLING) ระบบจะขัดจังหวะทันที |
| 2 | เรียกฟังก์ชัน toggleLED() |
| 3 | เปลี่ยนสถานะไฟ LED |
| 4 | กลับมาทำงานใน loop() ต่อ |
เหมือนมี “ผู้ช่วย” เฝ้าสัญญาณให้ตลอดเวลา MCU ไม่ต้องเสียเวลาเช็คค่าเองใน loop
🔹 7. ข้อควรระวังในการใช้ Interrupt
1️⃣ ISR ต้องสั้นมาก (ควรจบภายในไม่กี่ microseconds)
- ห้ามใช้
delay(),Serial.print(), หรือฟังก์ชันที่ช้า - ใช้แค่ “ตั้งธง (flag)” เพื่อให้
loop()ทำงานต่อภายหลัง
2️⃣ ใช้ตัวแปรแบบ volatile
เพื่อป้องกันไม่ให้ค่าถูกเก็บ cache โดย compiler
3️⃣ Debounce ปุ่มกด
เนื่องจากการกดปุ่มจริงจะมีสัญญาณกระเด้ง (bounce)
ควรหน่วงเวลาเล็กน้อยใน ISR หรือใช้ Timer ตรวจสอบภายหลัง
📘 ตัวอย่างการป้องกัน bounce:
volatile unsigned long lastTime = 0;
void IRAM_ATTR handleButton() {
unsigned long now = millis();
if (now - lastTime > 200) { // หน่วง 200ms
lastTime = now;
ledState = !ledState;
digitalWrite(led, ledState);
}
}
🔹 8. Interrupt หลายตัวใน ESP32
ESP32 รองรับ Interrupt ได้แทบทุกขา
และรองรับ “Priority” และ “Core Binding” (ขาใดอยู่ CPU core ไหน)
📘 ตัวอย่าง:
void setup() {
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(4), ISR_A, FALLING);
attachInterrupt(digitalPinToInterrupt(5), ISR_B, FALLING);
}
ทำให้ ESP32 เหมาะกับงาน Real-Time เช่น หุ่นยนต์, Encoder, Counter, และ Ultrasonic
🔹 9. ตัวอย่าง: นับจำนวนครั้งที่กดปุ่ม (Counter Interrupt)
const int button = 4;
volatile int count = 0;
void IRAM_ATTR countButton() {
count++;
}
void setup() {
Serial.begin(115200);
pinMode(button, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button), countButton, FALLING);
}
void loop() {
Serial.print("Count = ");
Serial.println(count);
delay(500);
}
กดปุ่มแต่ละครั้ง จะนับเพิ่มขึ้นทันที โดยไม่ต้องคอยเช็คใน loop
🔹 10. ตัวอย่าง: Interrupt + Sensor (เช่น IR หรือ Encoder)
#define SENSOR_PIN 27
volatile unsigned long pulseCount = 0;
void IRAM_ATTR countPulse() {
pulseCount++;
}
void setup() {
Serial.begin(115200);
pinMode(SENSOR_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), countPulse, RISING);
}
void loop() {
Serial.printf("Pulses: %lu\n", pulseCount);
delay(1000);
}
ใช้ได้กับ Sensor ที่ส่งสัญญาณพัลส์ เช่น IR Encoder, Speed Sensor
🔹 11. ตัวอย่าง: การสื่อสารผ่าน Interrupt
บางครั้งเราต้องการให้ MCU 2 ตัว คุยกันผ่านสัญญาณขัดจังหวะ
เช่น “ESP32 แจ้ง Arduino ว่าอ่านค่าจบแล้ว”
📘 ตัวอย่างแนวคิด:
- ESP32 → ส่งพัลส์ HIGH 1ms เพื่อแจ้ง Arduino
- Arduino → ใช้ Interrupt ขา RX เพื่อตรวจจับสัญญาณ
🔹 12. การปิดการใช้งาน Interrupt
ถ้าต้องการหยุด Interrupt ชั่วคราว:
detachInterrupt(digitalPinToInterrupt(pin));
📘 ตัวอย่าง:
detachInterrupt(digitalPinToInterrupt(4)); // ปิดการขัดจังหวะขา 4
📚 13. แบบฝึกหัด
1️⃣ เขียนโปรแกรมให้กดปุ่ม 1 ครั้ง = เปิด LED, กดอีกครั้ง = ปิด
2️⃣ เขียนโปรแกรมนับจำนวนครั้งที่กดปุ่มโดยใช้ Interrupt
3️⃣ เขียนโปรแกรมตรวจจับสัญญาณ Encoder (เพิ่ม count เมื่อสัญญาณขึ้น)
4️⃣ เพิ่มระบบ Debounce ป้องกันการนับผิดพลาด
5️⃣ เขียนโปรแกรม ESP32 ที่มี Interrupt 2 ตัวทำงานคนละฟังก์ชัน (เช่น ปุ่ม A / ปุ่ม B)
🧾 14. สรุปแนวคิดสำคัญ
| หัวข้อ | อธิบาย |
|---|---|
| Interrupt | การขัดจังหวะการทำงานปกติเพื่อจัดการเหตุการณ์ด่วน |
| ISR (Interrupt Service Routine) | ฟังก์ชันที่ถูกเรียกเมื่อเกิด Interrupt |
| attachInterrupt() | ใช้กำหนดขาและฟังก์ชัน ISR |
| detachInterrupt() | ปิดการใช้งาน Interrupt |
| Mode | RISING, FALLING, CHANGE |
| volatile | ตัวแปรที่อาจถูกเปลี่ยนโดย ISR |
| Debounce | ป้องกันการนับซ้ำจากการเด้งของสัญญาณ |
| ESP32 | รองรับ Interrupt ทุกขา และหลาย Core |
| ISR ต้องสั้น | ห้ามใช้ delay(), Serial.print() ภายใน |
| ใช้กับ Sensor / ปุ่ม | ตรวจจับเหตุการณ์แบบเรียลไทม์ |
⚡ แนวคิดสรุปแบบภาพ (จำง่าย)
┌──────────────────────────────────────────┐
│ LOOP() │
│ ทำงานปกติ → ทำซ้ำ → ทำซ้ำ → ... │
└──────────────────────────────────────────┘
↑
│ (Interrupt signal)
│
┌──────────────────────────────────────────┐
│ ISR() │
│ ฟังก์ชันขัดจังหวะ (เช่น ปุ่มกด, Sensor) │
│ ทำงานทันที แล้วกลับไปที่ LOOP() │
└──────────────────────────────────────────┘
