การใช้งาน 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()