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