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

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

การใช้งานคำสั่ง 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)