การเขียนโปรแกรม Arduino เพื่ออ่านค่าอินพุตจากปุ่มกดและปัญหา Switch Bouncing#

Keywords: Atmel AVR MCU, ATmega328P, Arduino Programming, Software-based Button Debouncing


I/O Follower#

บทความนี้นำเสนอตัวอย่างโจทย์พื้นฐานสำหรับการฝึกเขียนโค้ดด้วยภาษา C/C++ โดยใช้คำสั่งของ Arduino (หรือเรียกว่า Arduino API) และเจาะจงเลือกใช้บอร์ดไมโครคอนโทรลเลอร์ที่มีตัวประมวลผล 8 บิต ตระกูล AVR เช่น ATmega328P (สำหรับบอร์ด Arduino Uno หรือ Nano เป็นต้น)

โจทย์ตัวอย่างคือ การเขียนโปรแกรมเพื่อคอยอ่านค่าที่ขาอินพุตแบบดิจิทัล แล้วตรวจสอบดูค่าลอจิกที่ได้และนำไปกำหนดสถานะที่ขาเอาต์พุต ดังนั้นถ้ามีการเปลี่ยนแปลงลอจิกที่ขาอินพุต เอาต์พุตจะต้องเปลี่ยนแปลงตาม (จึงเรียกว่า Digital I/O Follower) โดยกำหนดให้มีความสัมพันธ์แบบ Logical Inverse (การกลับค่าลอจิก) ดังนั้นในกรณีนี้ โปรแกรมจะทำให้งานในลักษณะที่เป็นลอจิกเกตแบบ NOT (Software-based Logic Inverter)

เมื่อมีการเปลี่ยนแปลงที่ขาอินพุตจะส่งผลต่อการเปลี่ยนแปลงที่ขาเอาต์พุตตามมา และระยะเวลาในการตอบสนองต่อการเปลี่ยนแปลง (Response Latency) ก็ขึ้นอยู่กับความเร็วหรือความสามารถในการประมวลผลของซีพียู

 


โค้ดตัวอย่างที่ 1: I/O Polling Loop#

ในตัวอย่างแรก จะเห็นได้ว่า ในฟังก์ชัน setup(){...} มีการใช้คำสั่ง pinMode() เพื่อกำหนดทิศทางของขา D2 ให้เป็นขาดิจิทัลอินพุต (และเปิดใช้งานตัวต้านทานภายในแบบ Pullup) และ D13 เป็นขาเอาต์พุต ตามลำดับ

ในฟังก์ชัน loop(){...} มีคำสั่งที่จะต้องทำซ้ำ และมีเพียงคำสั่งเดียวในตัวอย่างนี้ คือ คำสั่ง digitalRead(...) อ่านอินพุตที่ขา D2 แล้วนำมากลับค่าลอจิก (ด้วยโอเปอร์เรเตอร์ ! หรือ Logical NOT) และนำไปใช้อัปเดตค่าที่ขาเอาต์พุต D13 โดยใช้คำสั่ง digitalWrite(...)

void setup() {
  // configure D2 pin as input with internal pullup
  pinMode( 2, INPUT_PULLUP );
  // configure D13 pin as output
  pinMode( 13, OUTPUT );
}

void loop() {
  int new_value = !digitalRead( 2 );
  digitalWrite( 13, new_value );
}

หากนำโค้ดตัวอย่างนี้ไปทดลองกับอุปกรณ์ฮาร์ดแวร์จริง ก็ต้องหาวิธีการกำหนดค่าอินพุต ซึ่งก็สามารถทำได้โดยการสร้างวงจรปุ่มกด (Push Button / Tactile Switch) นำมาต่อเพิ่มบนเบรดบอร์ด (เช่น ต่อปุ่มกดแบบ Active-Low) เพื่อสร้างสัญญาณอินพุตแล้วป้อนเข้าที่ขา D2 ในขณะที่ขา D13 มีวงจร LED ใส่ไว้บนบอร์ดแล้ว

ตัวอย่างการต่อวงจรบนเบรดบอร์ดมีดังนี้

รูป: การต่อวงจรปุ่มกดบนเบรดบอร์ดร่วมกับบอร์ด Arduino Uno แบบ Active-High (Source: Arduino.cc)

 

รูป: ตัวอย่างผังวงจรปุ่มกดที่ทำงานแบบ Active-High และมีตัวต้านทานภายนอก 10kΩ แบบ Pulldown (Source: Arduino.cc)

 

รูป: ตัวอย่างการต่อวงจรทดลองจริงโดยใช้บอร์ด Arduino Nano 3.0 (วงจรปุ่มกดที่ทำงานแบบ Active-Low โดยใช้ตัวต้านทานแบบ Pull-up ของขา I/O ที่อยู่ภายในชิป)

 

อย่างไรก็ตามเพื่อให้เห็นการเปลี่ยนแปลงที่เกิดขึ้นที่ขาอินพุตและขาเอาต์พุตตามลำดับ การใช้เครื่องมือวัดอย่างเช่น ออสซิลโลโสโคปแบบดิจิทัล (Digital Oscillscope) หรือ เครื่องมือวิเคราะห์สัญญาณดิจิทัล (Logic Analyzer) จึงเป็นสิ่งสำคัญในการทดลอง

นอกจากนั้นแล้ว การใช้วงจรปุ่มกดและวัดสัญญาณการเปลี่ยนแปลงเชิงเวลาโดยใช้ออสซิลลโสโคป จะทำให้เห็นลักษณะของสัญญาณอินพุตจริงที่ถูกป้อนเข้าสู่วงจรภายในไมโครคอนโทรลเลอร์ผ่านทางขาดิจิทัล

การกดปุ่มแล้วปล่อยโดยทั่วไปแล้ว อาจมีการกระเด้งของปุ่มกดเกิดขึ้น (Button Bouncing) ทำให้เกิดการเปลี่ยนแปลงของระดับแรงดันไฟฟ้าหรือค่าลอจิกของสัญญาณอินพุตหลายครั้งในช่วงเวลาสั้น ๆ

 

ลองมาดูตัวอย่างสัญญาณที่วัดได้จริง (วัดสัญญาณโดยใช้เครื่องออสซิลโลสโคปแบบดิจิทัล Rigol DS1054Z)

รูป: แสดงรูปคลื่นสัญญาณช่วงที่อินพุตเปลี่ยนจากลอจิก LOW เป็น HIGH ช่อง (1) เป็นสัญญาณอินพุตที่ขา D2 รับมาจากวงจรปุ่มกด และช่อง (2) เป็นสัญญาณเอาต์พุตที่ขา D13

รูป: เปลี่ยน Time Resolution จาก 100us /div เป็น 5us /div

หากขยายแกนเวลาของรูปคลื่นสัญญาณ จะเห็นได้ชัดเจนมากขึ้น เมื่ออินพุตเปลี่ยนแปลงระดับจาก 10% ไปยัง 90% ของ VCC=+5V จะมีช่วงเวลาขาขึ้น (Rise Time) และเมื่ออินพุตเปลี่ยนจากลอจิก 0 เป็น 1 แล้ว จะทำให้เอาต์พุตเปลี่ยนแปลงตาม (เปลี่ยนจากลอจิก 1 เป็น 0) ทั้งสองเหตุการณ์มีระยะเวลาห่างกันประมาณ 5 us (ไมโครวินาที)

สัญญาณจากวงจรอินพุต หากไม่มีการกดปุ่ม จะได้ค่าลอจิกเป็น HIGH (1) หรือมีแรงดันไฟฟ้าประมาณ 5V สำหรับกรณีตัวอย่างนี้ แต่ถ้ามีการกดปุ่มในช่วงเวลาดังกล่าว ค่าลอจิกที่ได้จะเปลี่ยนเป็น LOW (0) และเมื่อปล่อยปุ่ม ก็จะกลับไปเป็นลอจิก HIGH เหมือนเดิม หากพิจารณาดูความสัมพันธ์เชิงลอจิกระหว่างสัญญาณอินพุตและเอาต์พุต ก็พบว่า เป็นแบบกลับค่าตามที่กำหนดไว้โดยฟังก์ชันการทำงานของโค้ดตัวอย่าง

รูป: แสดงรูปคลื่นสัญญาณอินพุตและเอาต์พุต เมื่อมีการกดแล้วปล่อยปุ่ม

แต่หากสังเกตดูคลื่นสัญญาณอินพุตที่วัดได้จริงโดยใช้เครื่องออสซิลโสโคป จะพบว่า มีช่วงเวลาสั้น ๆ ที่มีการเปลี่ยนแปลงสัญญาณอย่างรวดเร็ว เหมือนมีสัญญาณรบกวน การเปลี่ยนแปลงในลักษณะนี้ เกิดจากการกระเด้งของปุ่มกดเมื่อถูกกดแล้วปล่อย

รูป: กดปุ่มครั้งที่ 1 เกิดการกระเด้งของปุ่มกดหลายครั้ง ช่วงที่มีการปล่อยปุ่ม (ช่วงเปลี่ยนจาก LOW เป็น HIGH หรือขอบขาขึ้น)

รูป: กดปุ่มครั้งที่ 2 เกิดการกระเด้งของปุ่มกดหลายครั้ง ช่วงที่มีการปล่อยปุ่ม

รูป: กดปุ่มครั้งที่ 3 เกิดการกระเด้งของปุ่มกดหลายครั้ง ช่วงที่มีการปล่อยปุ่ม

จากรูปสัญญาณที่วัดได้จริงและนำมาเป็นตัวอย่าง จะเห็นได้ว่า การกดปุ่มแล้วปล่อยในแต่ละครั้ง อาจให้ผลแตกต่างกัน ดังนั้นจากตัวอย่างนี้ การต่อวงจรปุ่มกดและอ่านค่าอินพุต ควรจะต้องพิจารณาปัญหาที่เกิดจากการกระเด้งของปุ่มกดหลายครั้งด้วย

บนเว็บไซต์ของ Arduino มีตัวอย่างการเขียนโค้ดเป็นแนวทางเพื่อรับมือกับปัญหานี้ (Button Debounce)

 


โค้ดตัวอย่างที่ 2: Interrupt-Driven Callback#

จากตัวอย่างที่ 1 เราสามารถเขียนโปรแกรมให้แตกต่างจากเดิมได้ เช่น การเปิดใช้งานส่วนที่เรียกว่า อินเทอร์รัพท์ (Interrupt) ซึ่งเกิดจากเหตุการณ์ภายนอก (External Interrupt) ในกรณีนี้คือ การเปลี่ยนแปลงลอจิกที่ขาอินพุต

ชิป ATmega328P มีขา I/O ที่ใช้งานร่วมกับอินเทอร์รัพท์จากเหตุการณ์ภายนอกได้ 2 ขา คือ Arduino D2 Pin (PD2 pin) และ Arduino D3 Pin (PD3 pin) ซึ่งตรงกับอินเทอร์รัพท์ภายนอกหมายเลข 0 และ 1 ตามลำดับ

โค้ดตัวอย่างที่ 2:

#define BTN_PIN      (2)  // D2  pin
#define LED_PIN      (13) // D13 pin

void setup() {
  pinMode( BTN_PIN, INPUT_PULLUP );
  pinMode( LED_PIN, OUTPUT );
  // use an anonymous function for callback
  attachInterrupt( 
     digitalPinToInterrupt(BTN_PIN), 
     [](){
        digitalWrite( LED_PIN, !digitalRead(BTN_PIN) );
     }, CHANGE );
}

void loop() {
}

การเปิดใช้งานอินเทอร์รัพท์ภายนอกสำหรับบอร์ด Arduino ก็ทำได้โดยการใช้คำสั่งของ Arduino API: attachinterrupt(...) และให้เลือกขาดิจิทัล D2 (INT.0) หรือ D3 (INT.1) สำหรับ ATmega328P

การใช้คำสั่ง digitalPinToInterrupt(...) จะช่วยระบุว่า ขาที่เลือกใช้นั้นตรงกับอินเทอร์รัพท์ภายนอกที่หมายเลขใด (เรียกว่า External Interrupt Number)

อีกทั้งต้องระบุประเภทของเหตุการณ์ หรือ โหมดการทำงาน ได้แก่ ขอบขาขึ้น (RISING) ขอบขาลง (FALLING) หรือทั้งสองกรณีก็ได้ (CHANGE)

ฟังก์ชัน attachInterrupt(...) สำหรับเปิดใช้งาน และ detachInterrupt(...) สำหรับปิดอินเทอร์รัพท์ภายนอกที่ขาอินพุต ถูกสร้างไว้ในไฟล์ WInterrupts.c ของ Arduino Core for AVR

void attachInterrupt( 
       uint8_t interruptNum, 
       void (*userFunc)(void), 
       int mode );

void detachInterrupt( uint8_t interruptNum );

เมื่อเกิดเหตุการณ์อินเทอร์รัพท์ภายนอกในแต่ละครั้ง จะมีการเรียกฟังก์ชันที่เกี่ยวข้อง หรือเรียกว่า User-defined Callback Function ทำหน้าที่เป็น ISR (Interrupt Service Routine)

 

ข้อสังเกต: ชิปไมโครคอนโทรลเลอร์บนบอร์ด Arduino ที่แตกต่างกัน มีจำนวนหมายเลขอินเทอร์รัพท์ภายนอกแตกต่างกันได้

ตาราง: เปรียบเทียบความแตกต่างในการใช้งานขาอินพุต-ดิจิทัลของบอร์ด Arduino สำหรับคำสั่ง attachInterrupt() เช่น จำนวนขาและหมายเลขขาที่เลือกใช้ได้ (Source: Arduino.cc)

 


ตัวอย่างที่ 3: Software-based Button Debouncing#

ตัวอย่างถัดไปเป็นการสาธิตวิธีการลดปัญหาปุ่มกระเด้งที่ทำให้เกิดสัญญาณแบบพัลส์ช่วงสั้น ๆ เทคนิคที่ใช้คือ การวนลูปคอยอ่านค่าลอจิกที่ขาอินพุต (Input Sampling) เว้นระยะห่างคงที่ เช่น ทุก ๆ 2 มิลลิวินาที แล้วเก็บบันทึกค่าลอจิกเอาไว้ในตัวแปร sampled_bits โดยมีลักษณะเลื่อนบิตเข้าไปเก็บในตำแหน่ง LSB (Least Significant Bit) และเลื่อนบิตก่อนหน้านั้นไปทางซ้ายทีละหนึ่งตำแหน่ง ขนาดข้อมูลที่ใช้เก็บค่าบิตเหล่านี้ เท่ากับ 8 บิต (ใช้ชนิดข้อมูลเป็นแบบ uint8_t)

ถ้าค่าบิตในตัวแปร sampled_bits มีค่าเท่ากับ 0b0000000 (เลขฐานสอง) หรือ ทุกบิตเป็น 0 ก็หมายความว่า ช่วงเวลาที่ผ่านมาและมีการสุ่มอ่านค่าอินพุต 8 ครั้งล่าสุด สัญญาณอินพุตคงที่ (Stable) และเป็น LOW ดังนั้นจึงให้เอาต์พุตเปลี่ยนเป็น HIGH เพื่อเป็นการกลับค่าลอจิกของอินพุต

แต่ถ้าตัวแปรมีค่าเท่ากับ 0b11111111 (เลขฐานสอง) หรือ ทุกบิตเป็น 1 ก็หมายความว่า ช่วงเวลาที่ผ่านมานั้น สัญญาณอินพุตคงที่ และเป็น HIGH ดังนั้นในกรณีนี้ จึงให้เอาต์พุตเปลี่ยนเป็น LOW

แต่ถ้าตัวแปร sampled_bits มีบิตทั้งเป็น 0 หรือ 1 แสดงว่า เกิดการเปลี่ยนแปลงชั่วขณะ ดังนั้นในช่วงเวลาดังกล่าว จึงไม่ต้องอัปเดตค่าลอจิกของเอาต์พุต เป็นวิธีกรองเหตุการณ์ (Event Filtering) ที่เกิดกระเด้งของปุ่มกดซึ่งไม่เป็นที่ต้องการ

#define BTN_PIN      (2)
#define LED_PIN      (13)

#define INTERVAL_MS  (2)

// global variable 
uint32_t ts; // used to save timestamp (in msec)

void setup() {
  pinMode( BTN_PIN, INPUT_PULLUP );
  pinMode( LED_PIN, OUTPUT );
  digitalWrite( LED_PIN, LOW );
  ts = millis();
}

void loop() {
  static uint8_t sampled_bits = 0xff;
  if ( millis() - ts >= INTERVAL_MS ) {
      ts += INTERVAL_MS;
      sampled_bits = (sampled_bits<<1) | (digitalRead(BTN_PIN) & 1);
      if (sampled_bits==0x00) {
        digitalWrite( LED_PIN, HIGH );
      } else if (sampled_bits==0xff) {
        digitalWrite( LED_PIN, LOW );
      }
  }
}

 

มาลองดูตัวอย่างสัญญาณอินพุตและเอาต์พุตจากการทดลองโดยใช้ฮาร์ดแวร์จริง

รูป: การกดปุ่มแล้วปล่อยหนึ่งครั้ง

รูป: สัญญาณอินพุตที่มีขอบขาลงซึ่งเกิดจากการกดปุ่ม แต่มีการกระเด้งของปุ่มและทำให้เกิดสัญญาณพัลส์ตามมา แต่สัญญาณเอาต์พุตยังคงระดับเดิม ยังไม่เปลี่ยนแปลง (พัลส์ที่เกิดขึ้นถูกกรองด้วยวิธีการซอฟต์แวร์)

รูป: สัญญาณอินพุตและเอาต์พุตในช่วงเวลาที่มีการกดปุ่มแล้วปล่อย สองครั้งถัดกัน (Double Click)

 

ข้อสังเกต: การแเก้ปัญหาที่เกิดจากปุ่มกระเด้งด้วยวิธีการซอฟต์แวร์ ก็ช่วยได้ระดับหนึ่ง นอกจากนั้นแล้วยังมีวิธีการแก้ปัญหาเชิงฮาร์ดแวร์ เช่น การใช้ลอจิกเกตประเภท Schmitt-Trigger หรือการใช้วงจร RC Filter (Passive Low-Pass Filter) หรือใช้ไอซีที่มีหน้าที่นี้โดยเฉพาะ (เรียกว่า Switch Debouncer IC) เช่น Maxim MAX681x (datasheet)

รูป: วงจร RC และลอจิกเกต Schmitt-Trigger Inverter (Source: Texas Instruments)

 


ตัวอย่างที่ 4: C++ Class for Button Debouncing#

จากโค้ดตัวอย่างที่แล้ว เราลองมาสร้างคลาส C++ (ใช้ชื่อคลาส Button) สำหรับเอาไว้ใช้งานกับอินพุตที่มาจากวงจรปุ่มกด เป็นตัวอย่างดังนี้

File: Button.h

/////////////////////////////////////////////////////
// File: Button.h (C++ Class: Button)
/////////////////////////////////////////////////////

class Button { // Input Button with Debouncing Logic
  public: 
    Button( int pin, bool pullup=false, int interval_ms=2 ) {
       _pin = pin;
       pinMode( _pin, pullup ? INPUT_PULLUP : INPUT );
       _sampled_bits = 0xff;
       clear();
       _interval_ms  = interval_ms;
       _ts = millis();
    }

    void update() {
       uint32_t now = millis();
       if ( millis() - _ts >= _interval_ms ) {
          _ts = now;
          _sampled_bits = (_sampled_bits<<1) | (digitalRead(_pin) & 1);
          if (_sampled_bits==0xf0) {
              _falling_edge = true;
          } 
          else if (_sampled_bits==0x0f) {
              _rising_edge = true;
          }
      }
    }
    bool has_changed_rising() {
      return _rising_edge;
    }
    bool has_changed_falling() {
      return _falling_edge;
    }
    void clear() {
      _rising_edge  = false;
      _falling_edge = false;  
    }

  private: 
    int _pin;
    uint8_t  _sampled_bits = 0xff;
    uint32_t _interval_ms, _ts;
    bool _rising_edge;
    bool _falling_edge;
};

File: button_debouncer_demo.ino

/////////////////////////////////////////////////////
// Demo code for the Button C++ class
/////////////////////////////////////////////////////

#include "Button.h"

#define BTN_PIN      (2)
#define LED_PIN      (13)

Button btn( BTN_PIN, true ); // create a Button instance

void setup() {
  pinMode( LED_PIN, OUTPUT );
  digitalWrite( LED_PIN, LOW );
}

void loop() {
  btn.update(); // must be called regularly.
  if (btn.has_changed_falling()) {
    btn.clear();
    digitalWrite( LED_PIN, HIGH );
  }
  if (btn.has_changed_rising()) {
    btn.clear();
    digitalWrite( LED_PIN, LOW );
  }
}

จากตัวอย่าง ตัวแปร btn อ้างอิงอ็อบเจกต์ที่ถูกสร้างขึ้นมาจากคลาส Button และจะต้องมีการทำคำสั่ง update() ของตัวแปร btn เพื่อตรวจสอบอินพุตสำหรับปุ่มกดเป็นระยะ ๆ

ถ้าหากต้องการตรวจดูว่า มีการเปลี่ยนแปลงเกิดขึ้นกับสถานะของอินพุต เช่น ขอบขาขึ้น หรือ ขอบขาลง ให้ใช้คำสั่ง has_changed_rising() หรือ has_changed_falling() และจากนั้นให้เคลียร์เหตุการณ์ที่ตรวจพบ โดยทำคำสั่ง clear() ของตัวแปร btn

รูป: ทดสอบการทำงานของโค้ดตัวอย่างโดยใช้ฮาร์ดแวร์จริง และวัดสัญญาณที่ได้

 

ถัดไปลองมาดัดแปลงโค้ดตัวอย่าง เพื่อสาธิตการนับจำนวนการกดปุ่มแล้วปล่อย และแสดงข้อความจำนวนครั้งที่นับได้ และมีการสลับสถานะของ LED เมื่อมีการกดปุ่มแล้วปล่อยในแต่ละครั้ง

File: button_click_counter.ino

#include "Button.h"

#define BTN_PIN      (2)
#define LED_PIN      (13)

Button btn( BTN_PIN, true ); // create a Button instance

void setup() {
  Serial.begin( 115200 );
  Serial.println( "Switch debouncer demo...\n" );
  pinMode( LED_PIN, OUTPUT );
  digitalWrite( LED_PIN, LOW );
}

String str;
uint8_t cnt = 0;

void loop() {
  btn.update(); // must be called regularly.
  if (btn.has_changed_falling()) {
    btn.clear();
    cnt++; // increment click counter by 1
    str = "Clicks: ";
    str += cnt;
    Serial.println( str.c_str() );
  }
  if (btn.has_changed_rising()) {
    btn.clear();
    // toggle LED
    digitalWrite( LED_PIN, !digitalRead(LED_PIN) );
  }
}

รูป: ตัวอย่างข้อความเอาต์พุตที่ได้รับมาจากบอร์ดและปรากฎใน Arduino Serial Monitor

 


ตัวอย่างที่ 5: Wokwi-based Arduino Simulator#

เราลองมาใช้โปรแกรมแบบออนไลน์ที่มีชื่อว่า Wokwi AVR Simulator จำลองการทำงานหรือทดสอบการทำงานของโค้ดตัวอย่าง จุดเด่นข้อหนึ่งของ Wokwi คือ เราสามารถเขียนโค้ด Arduino Sketch และจำลองการทำงานได้ และในกรณีที่ใช้วงจรปุ่มกดเป็นอินพุต (wokwi-pushbutton) เราสามารถตั้งค่าได้ว่า จะให้จำลองสถานการณ์ที่มีการกระเด้งของปุ่มด้วย หรือไม่ (bounce เป็น 0 หรือ 1)

รูป: ตัวอย่างการต่อวงจรเสมือนจริงโดยใช้บอร์ด Arduino Nano และปุ่มกดบนเบรดบอร์ด

ในการจำลองการทำงานของ Arduino โดยใช้ Wokwi Simulator เราสามารถนำอุปกรณ์ที่เรียกว่า Virtual Logic Analyzer มาใช้สำหรับตรวจสอบและบันทึกการเปลี่ยนแปลงของสัญญาณในระบบได้ บันทึกเป็นไฟล์ .vcd โดยอัตโนมัติ เมื่อจบขั้นตอนการจำลองการทำงาน แต่มีข้อจำกัดคือ ยังไม่สามารถแสดงรูปคลื่นสัญญาณได้ และต้องใช้โปรแกรมภายนอกสำหรับไฟล์ .vcd ที่ได้

จากการจำลองการทำงานและมีการกดปุ่มหลาย ๆ ครั้ง ลองมาดูตัวอย่างรูปคลื่นสัญญาณอินพุตและเอาต์พุตที่ได้

รูป: แสดงข้อมูลจากไฟล์ .vcd ที่ได้จากการจำลองการทำงาน โดยใช้โปรแกรม GTKwave และสังเกตได้ว่า มีการกดปุ่มแล้วปล่อยหลายครั้ง

รูป: ขยายแกนเวลา (Zoom-In) จะเห็นได้ว่า มีการเกิดเหตุการณ์การกระเด้งของปุ่มกด มีสัญญาณพัลส์หลายครั้งเกิดขึ้น

รูป: ทดลองโค้ดตัวอย่างที่มีการแก้ปัญหาการกระเด้งของปุ่มกด

รูป: สังเกตเห็นได้ว่า เมื่อมีการกดปุ่มและสัญญาณเปลี่ยนจาก HIGH เป็น LOW มีเหตุการณ์การกระเด้งของปุ่มกด แต่สัญญาณเอาต์พุตจะไม่เปลี่ยนแปลงตามทันที มีการกรองเหตุการณ์ดังกล่าวเอาไว้ และเกิดการเปลี่ยนแปลงของเอาต์พุต ประมาณ 10 ms หลังจากนั้น


กล่าวสรุป#

บทความนี้นำเสนอตัวอย่างการเขียนโค้ดสำหรับใช้งานขา GPIO ของบอร์ด Arduino เป็นอินพุตและเอาต์พุตแบบดิจิทัล และกำหนดให้โปรแกรมตัวอย่างมีหน้าที่คอยตรวจสอบการเปลี่ยนแปลงสถานะลอจิกที่ขาอินพุตเพื่อนำไปอัปเดตสถานะลอจิกที่ขาเอาต์พุต โดยใช้สองวิธี คือ I/O Polling Loop และ External Interrupt Handling อีกทั้งได้ยกตัวอย่างปัญหาที่เกิดจากการกระเด้งของปุ่มกดที่ใช้เป็นอินพุต และตัวอย่างการแก้ปัญหา

 


This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Created: 2022-01-20 | Last Updated: 2022-11-07