การใช้งานคำสั่งหน่วงเวลาและจับเวลา delay() และ millis() ใน arduino

การใช้งานคำสั่ง delay() millis() ใน arduino

การใช้งาน millis() กับ delay() ใน Arduino Uno

ที่มาและความสำคัญ การเขียนโปรแกรมไมโครคอนโทรลเลอร์ Arduino Uno จะมีฟังก์ชัน millis() และ delay() ให้ใช้สำหรับการจัดการเรื่องเวลา แต่ทั้งสองฟังก์ชันมีความแตกต่างกันในการใช้งานและความเหมาะสมในแต่ละสถานการณ์ หากเราเริ่มต้นศึกษาการเขียนไมโครคอนโทรลเลอร์ เรามักจะพบการเริ่มใช้งานคำสั่ง delay() เพื่อหน่วงเวลาบางอย่างเอาไว้ จนกระทั้งเราชินกับการใช้งาน delay() เพราะใช้งานง่ายไม่ซับซ้อนด้วยคุณสมบัติค้างสถานะนั้นๆไว้ขณะหนึ่งตามค่าที่กำหนด เมื่อเราสามารถเขียนไมโครคอนโทรลเลอร์ให้มีความซับซ้อนมากขึ้นเราจะพบว่า โปรแกรมของเราทำงานช้าลงอย่างเห็นได้ชัด หรืออาจจะอ่านค่าเซ็นเซอร์บ้างก็ไม่เป็นไปตามขอบเขตที่มันควรจะเป็นซึ่งอาจจะมาจากสาเหตของการใช้งานคำสั่ง delay() ก็เป็นได้เพราะเป็นการทำงานเป็นแบบ blocking

เราสามารถแก้ปัญหานี้ได้ด้วยการใช้งานคำสั่งอีกคำสั่งที่มีคุณสมบัติคล้ายๆกัน เป็นฟังก์ชันที่เกี่ยวข้องกับเวลาเหมือนกันแต่ทำงานกันคนละแบบนั้นคือ millis() แต่การใช้งาน milli() นั้นจะมียุ่งยากกว่า delay() เล็กน้อยเพราะต้องเขียนให้มีองค์ประกอบอื่นๆร่วมด้วย การทำงานของ millis() จะเป็นการเอาค่าเวลา millis() เริ่มขึ้นจากระบบ ที่ถูกนับอยู่ตลอดเวลาตั้งแต่เริ่มการทำงานของไมโครคอนโทรลเลอร์(มีกำหนดจุดสิ้นสุด และมีการรีเซ็ด) จากนั้นเราสามารถนำค่าเวลาเหล่านั้นมาทำการเทียบ ว่าเราต้องการให้มีการทำงานหรือเหตการณ์เกิดขึ้นที่ ช่วงเวลาใดๆการทำงานจะเป็นการตรวจเช็คและเปรียบเทียบต้องอาสัยคำสั่งการตัดสินใจร่วมด้วย และเป็นลักษณะของ non-blocking (ไม่มีการค้างสถานะใดๆเอาไว้) เราสามารถเขียนให้ผลลัพธ์ออกมาให้มีลักษณะที่เหมือนกันได้ต่อการใช้งานทั้งสองคำสั่ง delay() หรือ millis()

แต่การใช้งาน millis() นั้นมีข้อดีกว่ามากเพราะมันทำงานทันทีเปรียบเทียบแล้วได้ค่าที่ถูกต้องเป็นจริงก็จะทำงานใน //statement ทันทีซึ่งตรงจุดนี้จะเป็นข้อได้เปรียบในเรื่องของความเร็วเป็นอย่างมาก หากเรามีการกำหนดการทำงานที่เหมาะสมเราสามารถเขียรเพื่อให้การอ่านค่าต่างๆทำได้หลายๆงานในเวลาที่ไกล้เคียงกัน ทำให้บางคนอาจเข้าใจว่า การใช้งาน millis() เสมือนการทำงานแบบขนาน (thread)

1. millis()

millis() เป็นฟังก์ชันที่ใช้สำหรับการจับเวลาที่ผ่านไปตั้งแต่บอร์ด Arduino เริ่มทำงาน ซึ่งค่าที่ได้จะเป็นหน่วยมิลลิวินาที (1/1000 ของวินาที)

การใช้งาน

  • ตรวจสอบเวลา: millis() ใช้ในการตรวจสอบว่าเวลาผ่านไปมากน้อยแค่ไหนแล้ว ซึ่งสามารถนำไปใช้ในการทำงานหลายอย่างพร้อมกัน (เสมือน multitasking) โดยไม่หยุดการทำงานของโปรแกรม
  • การเปรียบเทียบเวลา: ใช้ millis() เพื่อตรวจสอบว่าเวลาผ่านไปตามที่ต้องการหรือยัง เช่น ต้องการให้ไฟ LED กระพริบทุกๆ 1 วินาที

ตัวอย่างการใช้งาน

unsigned long previousMillis = 0;  // เก็บเวลาที่ผ่านมา

const long interval = 1000;  // ตั้งค่าเป็น 1 วินาที (1000 มิลลิวินาที)
int ledPin = 13;  // ขาเชื่อมต่อของ LED

void setup() {
  pinMode(ledPin, OUTPUT);  // ตั้งค่าให้ขานี้เป็น OUTPUT
}
void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;  // อัพเดตเวลาครั้งล่าสุด

    digitalWrite(ledPin, !digitalRead(ledPin));  // เปลี่ยนสถานะของ LED
  }
}

จากโค้ดด้านบนจะเป็นตัวอย่าง อย่างง่ายสำหรับใช้งานของ millis() ในลำดับถัดไปจะเป็นการประยุกก์เอาคำสั่งนี้มาลองเขียนโดยใช้งานร่วมกับ led จำนวน 4 ดวงหากเราต้องการเขียนโดยใช้ millis() จะต้องเขียนอย่างไรและมีความแตกต่างกับการใช้งาน delay() มากน้อยแค่ไหน.

จากรูปการต่อใช้งาน led จำนวน 4 ดวงเชื่อมต่อกับ arduino uno ในตำแหน่งขา 2,3,4,5 แล้วทำการเขียนโปรแกรมเพื่อให้งานคำสั่ง millis()

const int led_all[] = {2,3,4,5};
unsigned long period = 0;
const long last_time = 300;
unsigned int c = 0;
void setup() {
      Serial.begin(9600);
      for(int a1 = 0;a1<4;a1++){
        	pinMode(led_all[a1],OUTPUT);
      }
}
void loop() {
  if(millis()-period >= last_time){   // หนึ่งงานสำหรับการแสดงผลไฟกระพริบ
        period = millis();
        digitalWrite(led_all[c-1],LOW);  
        digitalWrite(led_all[c],HIGH);
        if(c>3)
        	c=0;
        else 
        	c++;
        Serial.print("print c: ");
        Serial.println(c);
  }
  // if(millis()-period2 >= last_time2) { // งานที่สอง
  //	period2 = millis();
  //	statement....
  //}
}

จากโค้ดข้างต้นแสดงไฟกระพริบติดเละดับสลับกันแบบดาวตกด้วยการใช้งานบอร์ด nano maker จาก cytron ซึ่งบอร์ดตัวนี้มี led built in ไว้แล้วทำให้สะดวกต่อการใช้งาน จากการเขียนเราจะเห็นได้ว่าเมื่อเราเรียกใช้งาน millis() จะมีความซับซ้อนกว่ามาก แต่การทำงานนี้จะไม่เกิดการ blocking ของโปรแกรมเลยเราสามารถเขียนให้มีการทำงานอื่นๆ ได้เลยในขณะที่ led กำลังกระพริบในแต่ละดวง หากเราใช้งานคำสั่ง delay() การทำงานจำเป็นจะต้องรอให้การแสดงผล led ทั้ง 4 ดวงเสร็จก่อนถึงจะเริ่มทำงานในส่วนอื่นๆ



2. delay()

delay() เป็นฟังก์ชันที่ใช้สำหรับหยุดการทำงานของโปรแกรมชั่วคราวตามเวลาที่กำหนด(blocking) ซึ่งเวลานั้นหน่วยเป็นมิลลิวินาที

การใช้งาน

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

ตัวอย่างการใช้งาน

int ledPin = 13;  // ขาเชื่อมต่อของ LED

void setup() {
  pinMode(ledPin, OUTPUT);  // ตั้งค่าให้ขานี้เป็น OUTPUT
}

void loop() {
  digitalWrite(ledPin, HIGH);  // เปิด LED
  delay(1000);  // หยุดโปรแกรม 1 วินาที (1000 มิลลิวินาที)
  
  digitalWrite(ledPin, LOW);  // ปิด LED
  delay(1000);  // หยุดโปรแกรม 1 วินาที (1000 มิลลิวินาที)
}


สรุปความแตกต่าง และข้อดีและข้อจำกัด

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

ข้อดีของ delay()

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

ข้อเสียของ delay()

  • หยุดการทำงานของโปรแกรมทั้งหมด: ไม่สามารถตอบสนองต่อเหตุการณ์อื่นขณะใช้งานได้
  • ไม่เหมาะสำหรับงานหลายหน้าที่: ทำให้ไม่สามารถทำงานพร้อมกันได้
  • ประสิทธิภาพต่ำ: อาจทำให้โปรแกรมล่าช้าในโครงการที่ซับซ้อน

ข้อดีของ millis()

  • การทำงานแบบไม่หยุดนิ่ง: ช่วยให้โปรแกรมทำงานหลายหน้าที่พร้อมกันได้
  • เหมาะสำหรับงานที่ต้องการตอบสนองแบบเรียลไทม์: เช่น โครงการที่ต้องการความแม่นยำด้านเวลา
  • ยืดหยุ่นและปรับแต่งได้ง่าย: สามารถใช้ในโครงการที่ซับซ้อน
  • ประสิทธิภาพดี: เหมาะสำหรับระบบที่ต้องทำงานหลายฟังก์ชัน

ข้อเสียของ millis()

  • มีความซับซ้อนสำหรับผู้เริ่มต้น: ต้องมีความเข้าใจในเรื่องตัวแปรและการคำนวณเวลา
  • มีข้อจำกัดเรื่องการรีเซ็ตค่า: ค่า millis() จะรีเซ็ตเมื่อเวลาผ่านไปประมาณ 50 วัน
  • ต้องเขียนโค้ดเพิ่มเติม: อาจทำให้โค้ดดูซับซ้อนกว่า

สุดท้ายนี้ การเลือกใช้งานฟังก์ชันระหว่าง millis() และ delay() ขึ้นอยู่กับความต้องการของโปรแกรมว่าต้องการให้โปรแกรมทำงานแบบไม่หยุดหรือไม่ ถ้าต้องการการทำงานที่ไม่หยุดให้ใช้ millis() แต่ถ้าต้องการหยุดการทำงานชั่วคราวให้ใช้ delay()



วีรภัทร : ผู้เขียน
(update 14/12/67)