🧩 1. ความหมายของ Interrupt

Interrupt (การขัดจังหวะ) คือ “เหตุการณ์ที่ทำให้ไมโครคอนโทรลเลอร์หยุดทำงานปัจจุบันชั่วคราว เพื่อไปทำสิ่งที่สำคัญกว่า”

เช่น

  • เมื่อกดปุ่ม → ให้ขัดจังหวะไปทำฟังก์ชัน “toggle LED”
  • เมื่อเซนเซอร์จับสัญญาณ → ให้ขัดจังหวะเพื่ออ่านค่า
  • เมื่อมีข้อมูล Serial เข้า → ให้ขัดจังหวะเพื่อรับทันที

หลังจากทำเสร็จ ระบบจะ กลับมาทำงานต่อจากจุดเดิมอัตโนมัติ


⚙️ 2. ประเภทของ Interrupt

ประเภทแหล่งที่มาตัวอย่าง
Hardware Interruptสัญญาณจากภายนอก MCUปุ่มกด, เซนเซอร์, Timer
Software Interruptคำสั่งในโปรแกรมใช้ yield(), attachInterrupt() เรียกเอง

Arduino และ ESP32 ใช้แบบ Hardware Interrupt เป็นหลัก


🔹 3. โครงสร้างการใช้งาน Interrupt (Arduino / ESP32)

C++
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 เลย!

C++
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:

C++
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 ไหน)

📘 ตัวอย่าง:

C++
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)

C++
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)

C++
#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 ชั่วคราว:

C++
detachInterrupt(digitalPinToInterrupt(pin));

📘 ตัวอย่าง:

C++
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
ModeRISING, FALLING, CHANGE
volatileตัวแปรที่อาจถูกเปลี่ยนโดย ISR
Debounceป้องกันการนับซ้ำจากการเด้งของสัญญาณ
ESP32รองรับ Interrupt ทุกขา และหลาย Core
ISR ต้องสั้นห้ามใช้ delay(), Serial.print() ภายใน
ใช้กับ Sensor / ปุ่มตรวจจับเหตุการณ์แบบเรียลไทม์

⚡ แนวคิดสรุปแบบภาพ (จำง่าย)

┌──────────────────────────────────────────┐
│               LOOP()                                                                                               │
│ ทำงานปกติ → ทำซ้ำ → ทำซ้ำ → ...                                                                │
└──────────────────────────────────────────┘
          ↑
          │  (Interrupt signal)
          │
┌──────────────────────────────────────────┐
│               ISR()                                                                                                   │
│ ฟังก์ชันขัดจังหวะ (เช่น ปุ่มกด, Sensor)                                                          │
│ ทำงานทันที แล้วกลับไปที่ LOOP()                                                                  │
└──────────────────────────────────────────┘