สวัสดีครับ หลังจากที่เราสามารถติดต่อสื่อสารกับ API ได้แล้วนั้น ต่อมาเราจะมาเรียนรู้เรื่อง Multitasking เพื่อรองรับลักษณะการทำงานที่ซับซ้อนมากขึ้นกัน
What is Multitasking?
Multitasking คือการทำให้ระบบสามารถทำงานหลายงานได้พร้อมๆกัน (concurrently) โดยงานในที่นี้อาจจะหมายถึงโปรแกรม ฟังก์ชัน หรือในระดับเล็กที่สุดอาจหมายถึงโปรเซส โดยการทำงานพร้อมกันนั้นมีได้หลายลักษณะ อาจทำงานพร้อมกันจริงๆ แยกกันทำชัดเจน(อาจจะเรียกได้ว่าทำงานแบบขนาน) หรือใช้วิธีการสลับไปมาระหว่างหลายๆงานให้ดูเหมือนทำงานพร้อมกัน (โดยทั่วไปแล้วระบบมักจะทำงานในลักษณะแบบหลัง)

Why multitasking?
เนื่องจากความเร็วของหน่วยประมวลผลปัจจุบันค่อนข้างสูงมาก ทำให้การเขียนแบบทำงานเรียงไปตามลำดับบางครั้งไม่เห็นข้อเสียอะไร ประสิทธิภาพก็ได้ตามที่ต้องการ แต่มีงานหลายประเภทที่ต้องการผลลัพธ์ของงานหลายๆงานอย่างรวดเร็วหรือในทันทีเพื่อให้ระบบทำงานได้ตามความต้องการ ตัวอย่างหนึ่งที่เราพบเจอในชีวิตประจำวันคือการเปิดหลายโปรแกรมทำงานพร้อมกันบนคอมพิวเตอร์หรือมือถือ อย่างน้อยๆก็คิดว่าคงเคยเปิดเพลงฟัง ในขณะที่ทำงาน ถ้าตอนที่พิมเอกสารอยู่ เพลงดันสดุดก็คงหงุดหงิดน่าดู อีกตัวอย่างนึงที่ดูเหมือนจะไกลตัวแต่ก็สำคัญกับเรามากๆคือระบบ WebServer ของ Web ที่มีผู้ใช้นับล้านใช้งานพร้อมๆกัน ถ้าทำงานตอบสนองทุกๆคนพร้อมๆกันไม่ได้ ผู้ใช้ก็คงจะไม่พอใจเอามากๆ ในระบบเล็กๆอย่างใน ระบบ IOT ต่างๆก็ต้องทั้งติดต่อผ่าน Network พร้อมๆกับต้องตรวจจับสิ่งแวดล้อมผ่านเซ็นเซอร์ จะเห็นว่าการทำงานแบบ Multitasking เป็นสิ่งที่เราหลีกเลี่ยงไม่ได้เลยไม่ว่าหน่วยประมวลผลที่มีจะเร็วแรงแค่ไหน

What is RTOS and How Does it relates to Multitasking?
RTOS ย่อมาจาก Real Time Operating System ซึ่งหมายถึงระบบปฏิบัติการที่รองรับการคำนวณแบบตามเวลาจริง หรือเรียกอีกแบบว่าการทำงานที่ Deadline มีผลสำคัญ ซึ่งระบบปฏิบัติการรูปแบบนี้จะบริหารจัดการการใช้หน่วยประมวลผล การใช้งานหน่วยความจำเพื่อตอบสนองความต้องการที่รวดเร็ว มีเวลาและทรัพยากรจำกัด ระบบ RTOS ที่ออกแบบมาเพื่อตอบสนองอย่างรวดเร็วทันทีนี้จึงมักมาพร้อมกับ ความสามารถในการทำ Multitasking โดยมีระบบ Scheduler คอยบริหารจัดการเวลาที่แต่ละโปรเซสจะได้ทำในช่วงเวลาต่างๆ
FreeRTOS
FreeRTOS พัฒนาขึ้นมาโดยบริษัท Real Time Engineer โดย FreeRTOS เป็นระบบปฏิบัติการที่ออกแบบมาสำหรับไมโครคอนโทรลเลอร์หรือไมโครโพรเซสเซอร์เล็กๆ เพื่อการใช้งานแบบ Multitasking (ทำงานแบบหลายงานพร้อมกัน) ในรูปแบบที่เป็น Realtime OS โดยใน board lolin d32 Pro ของเรารองรับการใช้งาน FreeRTOS อยู่แล้ว
การใช้งาน Multitasking ของ FreeRTOS
ในต้นโค้ดอย่าลืมใส่
#include <Arduino_FreeRTOS.h>
FreeRTOS มีคำสั่งสำหรับ สร้าง Task ที่จะเราจะนำไปใช้ในงานที่จะ multitask 2 คำสั่งหลัก
xTaskCreatexTaskCreatePinnedToCoreโดยในค่ายนี้เราจะใช้xTaskCreatePinnedToCoreเป็นหลักเนื่องจาก Lolin D32 Pro ที่เรามีนี้ มี CPU ให้ใช้ ถึง 2 cores เราจึงสามารถกำหนดได้ว่าให้ Task ไหนทำงานบน Core ไหน
xTaskCreatePinnedToCore (
TaskFunction_t TaskFunction,
const char* TaskName,
const uint32_t Stack_Size,
void* Param,
UBaseType_t Priority,
TaskHandle_t* Task_Handle,
const BaseType_t coreID
)
คำอธิบายแต่ละ Parameters
TaskFunctionส่งชื่อ function ที่ต้องการให้ Task นั้นๆทำ โดย function ดังกล่าวไม่ควร return ค่าและต้องเป็น function ที่รันไม่รู้จับ (forever loop)TaskNameระบุชื่อ Task ซึ่งจะเป็นตัวช่วยในการ debug โดยขนาดไม่เกิน 16 ตัวอักษรStack_Sizesize ของ memory ที่ function ของ Task ใช้ในหน่วยBytesอาจใส่ไว้เผื่อๆซัก 1000 หรือลองคำนวนดูว่า function จะใช้เท่าไหร่ ถ้าน้อยเกินมักจะทำให้ตัว board restart ได้ ขณะเดียวกันก็ไม่ควรประกาศเยอะเกินอาจทำให้ RAM หมดและเกิดปัญหาตามมาParamค่าตัวแปรที่ส่งเพื่อเอาไปใช้ใน Task function ถ้าไม่ใช้ให้กำหนดเป็นNULLPriorityลำดับความสำคัญของ Task ที่จะมีผลกับการ schedule cpu โดย 0 คือ Priority ต่ำสุดTask_Handleตัวแปรของ Task ที่จะถูกนำไปใช้ Handle(suspend,delete,resume) ต่อไปcoreIDระบุ core ที่จะให้ Task ไปรัน (โดยใน Board นี้มี core หมายเลข 0 และ 1)
การ Delay ใน Task หนึ่งๆในระบบ Multitask ต้องไม่รบกวน Task อื่นๆ FreeRTOS มีคำสั่งที่ใช้ Delay Task โดยใช้ Timer โดยใช้คำสั่ง vTaskDelay ต่างจาก delay ที่เคยใช้มาซึ่งจะ เป็น Hardware Delay ที่หยุดการทำงานของ CPU ไปชั่วขณะ
vTaskDelay (
TickType_t Tick
)
โดยค่า Tick ที่ใส่ไม่ใช่เวลาที่ต้องการให้ Delay ค่า Tick จะต้องคำนวณจากตัว Timer ด้วย แต่สามารถกำหนดเวลาได้ง่ายๆ โดยการใช้ค่า time(ms)/portTICK_PERIOD_MS เช่นถ้าต้องการ delay 2 วิ ก็จะต้องพิมคำสั่ง vTaskDelay(2000/portTICK_PERIOD_MS);
ตัวอย่างการใช้งาน FreeRTOS Multitasking เพื่อสร้างระบบที่ทำให้ built-in LED(GPIO5) กระพริบทุก 0.2 วิ และ LED สีแดง ที่ GPIO12 กระพริบทุก 0.5 วิ
#include <Arduino.h>
#define BLUE 5
#define RED 12
TaskHandle_t TaskA = NULL;
TaskHandle_t TaskB = NULL;
void setup(){
pinMode(BLUE, OUTPUT);
pinMode(RED, OUTPUT);
xTaskCreatePinnedToCore(Lit_LED, "Builtin_LED", 1000, NULL, 1, &TaskA, 0);
xTaskCreatePinnedToCore(Blink_LED, "Blink_LED", 1000, NULL, 1, &TaskB, 1);
}
void loop(){
}
void Lit_LED(void *param){
while(1){
digitalWrite(BLUE, 0);
vTaskDelay(200/portTICK_PERIOD_MS);
digitalWrite(BLUE, 1);
vTaskDelay(200/portTICK_PERIOD_MS);
}
}
void Blink_LED(void *param){
while(1){
digitalWrite(RED, 1);
vTaskDelay(500/portTICK_PERIOD_MS);
digitalWrite(RED, 0);
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
โดย code ดังกล่าวจะให้ TaskA ซึ่งเป็นการทำให้ Builtin LED กระพริบ 0.2 วิ รันอยู่ที่ Core 0 และ TaskB ซึ่งเป็นการทำให้ Red LED ที่ GPIO12 กระพริบทุก 0.5 วิรันอยู่ที่ Core 1 จะเห็นว่าเกิดขึ้นได้พร้อมๆกันไม่ติดขัด และไม่สามารถเขียนได้ถ้าไม่มี multitask มาช่วย อาจลองให้ทั้งสอง Task รันบน Core เดียวกันก็จะเห็นว่าได้ผลลัพท์แทบไม่ต่างกัน ด้วยความสามารถในการ schedule ของ FreeRTOS
Note!!: ใน ESP32 void loop() อาจนับว่าเป็น Task ที่ทำงานใน core 1 ที่ priority 1
How FreeRTOS schedule tasks?
เราสามารถบริหารจัดการ Task ได้โดยการทำให้ Task มีสถานะ (state) ในรูปแบบต่างๆ ในระบบ RTOS จะมีการแบ่ง state ของ Task ออกเป็น 4 states
- Running state : Task ที่มี Priority สูงสุด จะได้ทำงานใน State นี้ และเป็น Task เดียวที่ได้ทำงานในเวลานั้นๆ
- Ready state : Task ที่รอเข้าไปทำงานใน Running state จะกองกันอยู่ที่นี่ ( เป็นพวก Task ที่มี Priority น้อยว่า Task ใน Running state)
- Blocked state : Task ที่อยู่ใน state นี้คือ Task ที่ถูก Blocked การทำงานชั่วคราว เช่น Task ที่ใช้คำสั่ง vTaskDelay()
- Suspended state : Task ที่อยู่ใน state นี้คือ Task ที่โดนสั่งให้พักการทำงาน โดยใช้คำสั่ง vTaskSuspend() และหาต้องการให้ Task นั้นๆ กลับมาทำงานตามปกติ เราต้องส่งคำสั่ง vTaskResume() เพื่อปลดล็อค
โดยการ Handle Task ต่างๆ ทำได้โดยการส่ง Task_Handle ไปที่ function handle ต่างๆเช่น
vTaskDelete (ลบ Task ที่สร้างไว้)
vTaskDelete(TaskHandle_t Task_Handle);
vTaskSuspend (สั่งให้ Task เข้าสู่ Suspend State)
vTaskSuspend(TaskHandle_t Task_Handle);
vTaskResume (สั่งให้ Task เข้าสู่ Ready State(ออกจาก Suspend) )
vTaskResume(TaskHandle_t Task_Handle);
vTaskPrioritySet (เปลี่ยน Priority ของ Task)
vTaskPrioritySet(TaskHandle_t Task_Handle, UBaseType_t NewPriority);
อ่านข้อมูลเพิ่มเติมเกี่ยวกับ FreeRTOS ได้ที่
