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

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

การใช้งาน 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....
  //}
  
}


จากโค้ดข้างต้นแสดงไฟกระพริบติดเละดับสลับกันแบบดาวตก จากการเขียนเราจะเห็นได้ว่าเมื่อเราเรียกใช้งาน 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 มิลลิวินาที)
}

    

สรุปความแตกต่าง

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

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



วีรภัทร : ผู้เขียน