Arduino Tutorial 3 : Digital input ,Debounce

Posted by | April 27, 2011 | Tutorial | 18 Comments

สำหรับในตอนนี้ ผมจะเพิ่มเติมวงจรอิเล็กทรอนิกส์ลงไปบ้างครับ การเขียนโปรแกรมไมโครคอนโทรลเลอร์ มันจะต่างจากการเขียนโปรแกรมบนคอมพิวเตอร์ ตรงส่วนนี้ล่ะครับ คือเราจะได้เรียนรู้เรื่อง hardware อิเล็กทรอนิกส์ด้วยครับ ในโลกไมโครอิเล็กทรอนิกส์ เป็นการเรียนรู้ ไม่รู้จบจริง มันจะมีเทคโนโลยีใหม่ มาให้เราต้องไปเรียนรู้เรื่อยๆ (อยู่ใช้มาหลายปี ผมบอกได้ว่าตามได้ ไม่หมดครับ แต่สนุก)

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

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

  • Button หรือปุ่มกดแบบโรงงาน มันจะมีขนาดใหญ่ และ ทนทานมาก
  • Tact switch เป็นปุ่มกด พบได้บนแผงวงจรอิเล็กทรอนิกส์ บนบอร์ด arduino จะมีเป็นปุ่มสำหรับ Reset
  • Micro Switch หรือ บางที่เรียกว่า Limit Switch อันนี้จะเห็นมากที่ใช้ในเครื่องจักร

และ ยังมี อุปกรณ์อื่นๆ ที่ทำงานเหมือนเป็นสวิตซ์ ได้อีกด้วย เช่น

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

เอาสวิตซ์ไปใช้งานได้อย่างไง

  • สิ่งแรกที่เราต้องทำความเข้าใจ เราจะแบ่งสถานะทางดิจิตอล ได้เพียงสองสถานะ  คือ High หรือ Low หรือ ON – OFF , หรือ บางที่จะเรียกสัญญาณทาง ดิจิตอลว่า  “0” กับ “1”
  • ในทางกายภาพแล้ว เราจะแบ่งสถานะทางดิจิตอล ด้วยแรงดันไฟ
  • สำหรับ Arduino จะใช้แรงดัน คือ “HIGH” คือ ไฟระดับ 5 Volt และ “LOW”  คือ ไฟระดับ 0 Volt
  • ดังนั้น สิ่งที่เราต้องทำ คือ เปลี่ยนการกดสวิตซ์ ให้เป็นการเปลี่ยนแรงดันสักก่อน

วงจร Pull-up ,Pull-down

สถานะที่กำหนดให้ขาของอุปกรณ์อิเล็กทรอนิกส์ รอรับอินพุท (INPUT) ขาพอร์ตจะเป็น High Impedance คือมีความต้านทานสูงมากต่ออยู่ ทำให้ขาพอร์ตนั้น เสมือนถูกปล่อยลอย ค่าอินพุทที่อ่านกลับมาได้ มันไม่แน่นอน

ดังนั้นในงานขาพอร์ตอินพุท วงจรของสวิตซ์ จำเป็นมากที่ต้องมี Pull-up Resistor หรือ Pull-down Resistor เพื่อที่จะกำหนดสภาวะดิจิตอลที่แน่นอน ให้กับอุปกรณ์อิเล็กทรอนิกส์ (ไม่ได้ เฉพาะเจาะจงว่าเป็นแค่ arduino นะครับ ทุกอุปกรณ์อิเล็กทรอนิกส์เลยที่บอกว่าขาอินพุทเป็น High Impedance)

ตามปกติ ตัวต้านทานที่ใช้ในวงจร  Pull-up หรือ Pull-down จะใช้ประมาณ 5k Ohm -20k Ohm

Pull-up Resistor คือการนำตัวต้านทานต่อเข้ากับ Vcc (+5V) เพื่อให้แรงดันอยู่คงที่ ทำให้อยู่ในสถานะ “HIGH” หรือ “1” ตลอดเวลา และเมื่อกดสวิตซ์ กระแสไฟฟ้าจะไหลลง Ground ทันที ซึ่งทำให้สถานะเป็นลอจิก “LOW” หรือ “0” และ การทำงานลักษณะนี้ จะเรียกว่า Active Low เพราะว่าจะเขียนโปรแกรมที่ทำงาน เมื่อลอจิกเป็น “LOW” ส่วนใหญ่ เราจะเห็นต่อสวิตซ์ นิยมใช้แบบ Pull-up มากกว่า

Pull-down Resistor โดยใน Pull-down จะมีลักษณะคล้ายกับ Pull-up Resistor แตกต่าง ตรงที่ สภาวะปกติของ Pull-down จะเป็นลอจิก “LOW” หรือ “0”  เมื่อมีการกดปุ่ม กระแสไฟจะไหลเข้าขาอินพุท ทำให้ ลอจิกเป็น “HIGH” หรือ “1” ได้ การทำงานในลักษณะนี้ จะเรียกว่า Active High

ทำการทดลองกันครับ

<Lab-7> สวิตซ์เบื้องต้น

Sketch แรกสำหรับตอนนี้ เราจะลองทำสวิตซ์ไฟแบบ กดติดปล่อยดับ ง่ายก่อน โดยวงจรสวิตซ์ เราจะต่อแบบ Pull Up resistor เอาไว้ ดังรูป

โดยวงจรที่ใช้ จะมีการ Pull-Up ตัวต้านทาน เอาไว้ ระดับแรงดัน ในสภาวะปกติ จะอยู่ที่ 5 โวลต์ หรือ ลอจิก “1” แต่ถ้ามีการกดปุ่ม แรงดันจะอยู่ที่ 0 โวลต์ หรือ ลอจิก “0” หรือ เรียกว่า active Low ครับ

เราสามารถจากสถานะการกดสวิตซ์นี้ เราสามารถเอาไปทำโปรแกรม ได้ดังตัวอย่างนี้ครับ

int buttonPin = 2;
int ledPin =  13;
boolean buttonState = 0;

void setup() {
      pinMode(ledPin, OUTPUT);
      pinMode(buttonPin, INPUT);
}

void loop()
      buttonState = digitalRead(buttonPin);

      if (buttonState == HIGH) {
         digitalWrite(ledPin, LOW);
      } else {
         digitalWrite(ledPin, HIGH);
      }
}

ตามตัวอย่าง ผมได้เขียน การปกติเมื่อไม่มี การกดปุ่ม ไฟ LED จะไม่สว่าง แต่ถ้ามีการปุ่มกด LED จะติดขึ้นครับ แต่เมื่อปล่อยมันก็ดับ

เราต้องกำหนดขาพอร์ต buttonPin ให้เป็น อินพุท (INPUT) ด้วย (ตามปกติ ตอนเริ่มต้น ถ้าไม่มีการกำหนดใดๆ ในโปรแกรม บอร์ด Arduino จะกำหนดให้ทุกขา อินพุท(INPUT) อยู่แล้ว เราจะใส่หรือไม่ใส่คำสั่งนี้ก็ได้ครับ)

โดยใช้คำสั่ง pinMode ();

[1] pinMode(pin) ปกติพอร์ตจะถูกกำหนดให้เป็น INPUT อ่านค่าสถานะ จาก pin ส่งค่ากลับมาเป็น HIGH หรือ LOW

การอ่านสถานะดิจิตอล จะใช้คำสั่ง

[ 2] int digitalRead(pin) อ่านค่าสถานะ จาก pin ส่งค่ากลับมาเป็น HIGH หรือ LOW

คำสั่ง digitalRead จะส่งค่ากลับมาครับ (หรือเรียกว่า จะ Return ค่ากลับมา) เวลาใช้งานเราก้อควรจะสร้างตัวแปร ไว้รับค่าด้วย ตามตัวอย่าง ผมจะสร้าง ตัวแปรชื่อ buttonState ไว้รับค่า(return)

buttonState = digitalRead(buttonPin);

เราจะได้สถานะการกดหรือปล่อย สวิตซ์มาแล้วอยู่ใน ตัวแปร buttonState ซึ่งในคราวนี้ก้ออยู่ที่ว่าเราสถานะนี้เอาทำงานอะไรต่อไป

<Lab-8> สวิตซ์ควมคุมความเร็วไฟกระพริบ

หลายคนคงคิดถึงวงจรไฟกระพริบ รอบนี้ผมเลยเอาตัวอย่างไฟกระพริบมาประยุกต์ เพิ่มเติมกับสวิตซ์ ในครั้งนี้ เราจะเอาค่าสถานะการกด-ปล่อย มาใช้ในควบคุมความเร็วของไฟกระพริบ กดปุ่มจะกระพริบเร็วขึ้น ปล่อยจะกระพริบด้วยความเร็วปกติ สำหรับวงจร เราใช้ต่อเนื่องจากแล๊ปทดลองที่ 7 ต่อได้เลยครับ

int ledPin =  13;
int buttonPin = 2;

boolean buttonState;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop() {
  int timeDelay;
  buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH)  {
      timeDelay =1000;
   }  else  {
      timeDelay = 100;
   }
   blinky(timeDelay);

}

void Blinky(int time) {
   digitalWrite(ledPin ,HIGH);
   delay(time);
   digitalWrite(ledPin ,LOW);
   delay(time);
}

จากเห็นว่าโครงสร้างโปรแกรมจะคล้ายกับแล๊ป 7 นะครับ คืออ่านค่าสถานะสวิตซ์กลับมา แต่ในรอบนี้เราเอาสถานะสวิตซ์ไป เปลี่ยนค่าเวลาที่ใช้ delay ในโปรแกรมไฟกระพริบครับ หวังว่าจะเห็นภาพการอ่านค่าสถานะของการกดสวิตซ์ไปใช้งานกันแล้วนะครับ

<Lab-9> Toggle Switch

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

Toggle เป็นสวิตซ์ที่จำค่าสถานะการกด ไว้นะครับ กดหนึ่งครั้งเปลี่ยนจากดับเป็นติด และ กดอีกหนึ่งครั้งจะเปลี่ยนจากติดเป็นดับ วิธีการเขียนตามตัวอย่างเลย

int ledPin = 13;
int buttonPin = 2;

boolean buttonState;
boolean lastState;
boolean state = HIGH;

void setup() {
  pinMode (buttonPin,INPUT);
  pinMode (ledPin,OUTPUT);
}

void loop() {
  buttonState = digitalRead(buttonPin);

  if ( ( buttonState == LOW) && (lastState == HIGH) ) state = !state;

  digitalWrite(ledPin,state);

  lastState = buttonState;
 }

เห็นว่า Sketch ไม่ได้ Advance อะไรมากครับ เพียงแค่เปลี่ยนจากการกดแล้วเอาไปเปิด-ปิด ไฟ LED เอาไป เก็บค่าสถานะ ของหลอดไฟ

โดยใน Sketch นี้ ผมจะสร้างตัวแปรที่ชื่อ state เพิ่มมาอีกหนึ่งตัว เมื่อใดที่มีการกดปุ่ม state จะถูกกลับเป็น state ตรงข้าม จาก High เปลี่ยนเป็น Low หรือ จาก Low เปลี่ยนเป็น High โดยคำสั่ง

state = !state;

และเก็บค่า buttonState สุดท้ายไว้ใน lastState ด้วย เพื่อใช้ในการตรวจสอบว่า ค่าที่เกิดจากการกดในรอบนี้ ที่เป็นสัญญาณลอจิก “LOW” เกิดจากสภาวะครั้งที่แล้ว ที่ยังไม่มีการกดปุ่ม สถานะจะเป็น “HIGH” อย่างแน่นอน เพื่อให้อ่านค่าการกดปุ่มเพียงครั้งเดียวครับ

หลังจากที่ upload sketch เข้าไปทดลอง จะเห็นว่า เราสามารถทำสวิตซ์แบบ toggle ได้แล้ว คือกดติด กดดับ

แต่ช้าก่อนนะครับ หลังจากทดลองจะเห็นว่า มันไม่ได้ทำงานเหมือน sketch ที่เราเขียนเลย หรือเราว่าเขียนโปรแกรมผิค

ความจริงการทำงานโค๊ดนี้ เขียนถูกต้องครับ แต่เกิดสิ่งที่เราไม่ได้คาดไว้คือ สวิตซ์ทุกชนิด ที่มีหน้าสัมผัส (contact) เป็นโลหะ มักจะมีปัญหาการ bounce ของสัญญาณ คืออาการไกว่ของสัญญาณ ซึ่งจะเกิดในสั้นๆ 5-50 นาโนวินาที สาเหตุหนึ่งที่ทำให้เกิด ในช่วงที่กดปุ่ม หน้า contact จะสัมผัสไม่แนบสนิท จะเกิดสัญญาณ bounce คืออาการที่สัญญาณจะสลับ เป็น High หรือ Low อย่างรวดเร็ว สังเกตจากกราฟ  ก่อนที่จะเข้าสู่สภาวะเสถียร

sketch นี้จึงทำงานเห็น กดติดบ้าง ไม่ติดบ้าง เนื่องจากไม่ได้ เขียนส่วนป้องกันการ bounce ไว้ จะเห็นได้ว่าการเขียนโปรแกรมบนไมโครฯ จะไม่เหมือนเขียนใช้งานคอมพิวเตอร์ ในส่วนนี้ล่ะครับ เพราะว่าจะมีส่วนของ Hardware มาเกี่ยวข้องด้วย

<Lab-10> Toggle Switch แบบปรับปรุง

เรามาหา วิธีการแก้ปัญหา bounce ของสัญญาณกันดีกว่า สำหรับ อาการ bounce คือการที่สัญญาณยังไม่นิ่ง เกิดการเหวี่ยงในช่วงสั่นๆ ระยะเวลาการเกิด 5-50 นาโนวินาที และ เกิดช่วงเริ่มต้นของการกดสวิตซ์

วิธีการแก้ไขที่ง่ายที่สุด คือ หลังจากหน้า contact หรือกดปุ่มในครั้งแรก arduino จะรับสัญญาณการกดปุ่มได้ แต่มันยังไม่แน่ใจว่า สวิตซ์โดนกดจริงหรือป่าว ให้รออีกสัก 5-50 นาโนวินาที แล้วตรวจสอบสถานะการกดปุ่มอีกรอบ ถ้ามีการกดจริง สัญญาณดิจิตอลที่อ่านได้ อยู่ในช่วงที่เสถียร ซึ่งค่าที่ออกมาจะบอกได้ว่ากด แต่ถ้าตรวจสอบอีกที่แล้วไม่พบ แสดงว่าสัญญาณที่ได้รับ เป็น noise ในระบบ ดังนั้น sketch ใหม่ จึงได้ดังนี้

int ledPin = 13;
int buttonPin = 2;

boolean buttonState;
boolean lastState;
boolean state = HIGH;

 void setup() {
  pinMode (buttonPin,INPUT);
  pinMode (ledPin,OUTPUT);
}

void loop() {
  reading = digitalRead(buttonPin);

  if ( reading == LOW && lastState == HIGH ) {
    delay (10);
    if (digitalRead(buttonPin) == LOW) state = !state;
  }

  digitalWrite(ledPin,state);
  lastState = reading;

}

เห็นว่า เมื่อเราเข้าใจปัญหา เราจะเขียนโปรแกรมได้แก้ง่ายมากๆ ผมน่าจะบอกตั้งแต่แรก ฮ่าๆ ผมเป็นพวกชอบแกล้งคนครับ ให้โดนเยอะๆ จะได้เอาไปโม้เพื่อนๆ ต่อได้
สำหรับรอบนี้ ก็คงพอจะเข้าใจวิธีการใช้ไมโคร และใช้งาน arduino เพิ่มขึ้นอีกนิดแล้ว ก็มีปัญหาไม่เข้าใจส่วนไหน ติชม หรือ comment เข้ามาได้นะครับ เจอกันโอกาสหน้า บายยย ^_^

About chang

ชื่อ “ช้าง” ส่วนมากเขาจะเรียกว่า “พี่ช้าง” แล้ว มีความสนใจทางเทคโนโลยีทางคอมพิวเตอร์ อิเล็กทรอนิกส์ และ หุ่นยนต์ เป็นทั้งนักคิด นักประดิษฐ์ ชอบทดลองเล่น จนเดี่ยวนี้รู้สึกว่าจะเล่นมากกว่ามืออาชีพไปสักแล้ว

  • อัดแน่นไปด้วยเนื้อหาสาระครับ พี่ แบบนี้ แหละที่อยากอ่านที่สุด

    • ขอบคุณครับ ดีใจมีคนเข้ามาอ่าน 😀

      จะพัฒนา เนื้อหาให้ดี ยิ่งๆ ขึ้นไปครับ

      • Wattana Nokngam

        เป็นกำลังใจให้ครับ

  • Bobswm

    อิๆ กว่าตอน 3 จะเกิด ก็ไปเรียนกับ Jeremy เรียบร้อยแล้ว มั่วอังกฤษตั้งนาน พอมาอ่านไทยถึงได้ อ๋อ

    • เราอัดแน่น ด้วยเนื้อหาครับ เลยนานนิดหน่อย
      แต่ปีนี้จะพยายามทำ tutorial เบื้องต้น ให้เสร็จครับ

  • Oamosed

    ขอบคุณที่ให้ความรู้

  • dome

    ผมทำตามแลปที่ 7 ผมพบอย่างนึงคือ เมื่อผมถอดสายไฟออกหมด ต่อแค่ Led ที่ขา 13 ลงกราว แล้วก็ จั้มสายไฟจาก port 2 มาจิ้มที่กราวไฟก็ติด สรุปคือ จิ้มติด ปล่อยดับ  ไม่ต้องต่อแหล่งจ่าย 5V กับ pull-up resistor
    อยากทราบว่า มันเกิดขึ้นได้อย่างไรครับ

  • Anonymous

    เรียนเรื่อง switch มาหลายตอนแล้ว เลยลองเขียนโค๊ดเรื่อง toggle switch เองมาหลายวันแล้ว ไม่สำเร็จ ต้องพึ่งผู้รู้ล่ะคราวนี้ คือ

    กดแล้วปล่อย switch >> ให้ LED ติดกระพริบ ติด-ดับ ติด-ดับ……….ไปเรื่อยๆ จนกว่าจะ
    กดแล้วปล่อย switch อีกที >> LED ดับ
    หน้าตาโปรแกรม จะเป็นอย่างไร ใครทราบ ช่วยหน่อยครับ

    • ตกลง ทำได้แล้วใช่ป่าวครับ 😀

  • koh

    ขอบคุณมากครับสำหรับข้อมูล

  • Artiiz IChu

    แล้วถ้าจะทำ แบบ กด-ปล่อย Switch ครั้งแรก LED 1 ติด (ดวงอื่นดับหมด) ครั้ง ที่ 2 LED2 ติด (ดวงอื่นดับหมด) ครั้งที่ 3 LED 3 ติด (ดวงอื่นดับหมด) ครั้ง ที่ 4 ก้ จะดับทุกดวง เป็นแบบนี้ ไป เรื่อยๆ พอจะมี แนวไหมคับ

  • Warut Somsong

    int button_pin2 = 2;
    int led_pin7=7;
    int button_state ;
    boolean state = true;

    void setup (){
    pinMode (led_pin7,OUTPUT);
    pinMode (button_pin2,INPUT);
    }

    void loop (){

    button_state = digitalRead (button_pin2);
    if(button_state == 1){
    state = ! state;
    }
    digitalWrite (led_pin7,state);

    }
    ผมผิดตรงไหนครับ กดติด กดดับ

    • thanya

      int button_pin2 = 2;
      int led_pin7=7;
      int button_state ;
      int lastbutton_state;
      boolean state = false;

      void setup (){
      pinMode (led_pin7,OUTPUT);
      pinMode (button_pin2,INPUT);
      }
      void loop (){
      button_state = digitalRead (button_pin2);
      delay(50);

      if(button_state == 0 && lastbutton_state==1){
      state = ! state;
      }
      digitalWrite (led_pin7,state);
      lastbutton_state = button_state;
      }

  • ต้อง

    ตัวต้านทาน ขาออก ต่อเข้าบอร์ด ขา เบอร์ 2 ใช่ไหมครับ

  • อิคคิว

    ถ้ากดครั้งแรกกะพริบ1ครั้ง,กดครั้งที่สองกะพริบสองครั้ง,กดครั้งที่3กะพริบสามครั้งละค่ะ ต้องเขียนโปรแกรมอะไร เขียนยังไง

    • Bankha Shapphaphabs

      #include

      int led = 13;

      SoftwareSerial mySerial (10,11);

      void setup() {
      pinMode (led , OUTPUT);

      Serial.begin(57600);
      mySerial.begin(57600);

      Serial.print(“Hardware & Software Serial !” );

      }

      void loop()
      {
      if(mySerial.available())
      {
      char j = mySerial.read();
      if (j>=’1’&& jj; i++ )
      {
      digitalWrite(led,HIGH);delay(100);
      digitalWrite(led,LOW);delay(100);
      }
      }
      }
      if (Serial.available())
      {
      char ch = Serial.read();
      Serial.println();
      if (ch >= ‘1’ && ch <= '9' )
      {
      Serial.print ("Tooggle: ");
      }else{
      Serial.print ("Unknown: ");
      }
      Serial.print(ch);
      }
      }

  • Lee Min

    สวัสดีค่ะ ถ้าเปลี่ยนจาก switch เป็น ir sensor ถ้าผ่าน ir ตัวที่ 1 ไฟจะติดหมด ผ่านตัวที่ 2 ไฟจะดับ เเละ ผ่าน ir ตัวที่ 2 ไฟติดหมด พอผ่าน ir ตัวที่ 1 ไฟดับทั้งหมด จะเขียนยังไงคะ

  • Kitphich Pan U Thai

    ได้สาระมากเรยครับ // ถามสักนิดงับพี่ ถ้าอยากกดสวิตช์ แล้วให้บอร์ด reset นี่ต้องมีโค้ด แล้วต่อพิเศษไรมั้ยครับ