FreeRTOS หลักการทำงาน

task state

Running คือ task ที่กำลัง execute อยู่
Ready คือ task ที่สามารถ execute ได้แล้ว แต่รออยู่ เช่น รอ task ที่ priority สูงกว่าทำงานไปก่อน
Blocked คือ task ที่ถูก block ไม่ให้ทำงาน อาจมาจากการรอ event อื่นให้ทำงานไปก่อน เช่น รอ event delay หรือรอการทำงานของ queue, semaphore, event group โดยทั่วไปการ block จะมีเวลา timeout กำหนด แต่จะมีการรอไปเรื่อยๆโดยไม่กำหนดก็ได้ เช่น รอการทำงานของ event
Suspended เหมือนกับ Blocked แต่ไม่มีเวลา timeout กำหนด และขึ้นอยู่กับคำสั่ง  vTaskSuspended() และ vTaskResume()

Task Priorities
task priority ที่เริ่มทำงานมีค่าเริ่มที่ 0 ส่วน task ที่มี priority สูงกว่าจะเริ่มทำงานก่อน task ที่ที priority ต่ำกว่าจะอยู่ใน Ready state ค่าสูงสุดของ task priority กำหนดได้ในไฟล์ FreeRTOSconfig.h


การจัดการ tasks
สร้าง task ใช้ xTaskCreate(), xTaskCreateStatic() ลบ task ใช้ xTaskDelete()
สร้าง task ด้วย macro ใช้ portTASK_FUNCTION_PROTO( vATaskFunction, pvParameters )

FreeRTOS Queue
คือ queue ที่ใช้ในการสื่อสารระหว่าง task โดยเป็นแบบ first in first out (FIFO)  ตัว message ที่ถูกส่งไปใน queue จะใช้ pointer สำหรับการ blocked ของ queue นั้น เป็นดังนี้
ถ้า queue ว่าง แล้วมี task มา get message ตัว task นั้นจะถูก blocked
ถ้า queue เต็ม แลัวมี task ส่ง message เข้ามา task นั้นจะถูก blocked
ถ้ามีหลาย task เข้ามาใช้ queue พร้อมกัน task ที่ priority จะได้ใช้ก่อน

Binary semaphore
ใช้ในการสื่อสารระหว่าง task สื่อสารกับ task และ interrupt สื่อสารกับ task โดยสื่อสารกันในลักษณะที่ควบคุมการทำงาน  ให้เรามอง semaphore เป็นเหมือน queue ที่มี 1 ช่องใส่ข้อมูล
ให้เราพิจารณากรณี polling ที่ต้องมีการวนซ้ำมาเช็ค service หนึ่งว่าพร้อมใช้งานแล้วหรือยัง กรณีนี้มีการใช้งาน cpu และไม่สะดวกในการให้ task อื่นทำงาน แต่ถ้าใช้ semaphore จะกำหนดให้ task ที่ต้องการใช้ semaphore นั้นรออยู่ใน blocked state จากนั้นเมื่อมึ Interrupt ทำการใส่ข้อมูลไปใน semaphore ตัว task ที่รอเรียกใช้ ก็สามารถนำไปใช้งานได้ เวลานำไปใช้จะเช็คแค่ว่า semaphore นั้นว่างหรือไม่ ถ้าไม่ว่าง(มีข้อมูลอยู่) ก็นำไปใช้ได้

FreeRTOS counting Semaphone
xSemaphoreCreateCounting ใช้กำหนดจำนวนของ semaphore โดยการ give semaphore เป็นการเพิ่มค่า counting ส่วนการ take semaphore เป็นการลด counting ของ semaphore ถ้า semaphore ถูก give เกินจำนวนมันจะ limit จำนวนตามที่กำหนดไว้ แต่ถ้า semaphore counting = 0 จะไม่สามารถ take semaphore ได้ ตัวอย่างเช่น กำหนด counting semaphore = 3 นั่นคือถ้าเรา give semaphore ไป 3 ครั้ง เราก็จะ take semaphore ได้ 3 ครั้ง เหมือนเป็นการเพิ่ม length ของ semaphore

Mutexes
Mutexes คือ semaphore อีกแบบหนึ่งซึ่งมีการใช้งานที่แตกต่างออกไปคือถ้ามี 2 task ต้องการใช้งานฟังก์ชันที่ถูกจำกัดการเข้าถึงโดยใช้ Mutexes ถ้า task1 ทำการ take mutex ไปจะทำให้ task1 เข้าใช้งานฟังก์ชันได้ พอใช้เสร็จแล้วจะมีดาร give mutex คืน โดยที่ในระหว่างที่ task1 ยังไม่ give mutex คืน tasl2 จะไม่สามารถ take mutex ได้   หลังจากที่ task1 give mutex คืนเสร็จแล้ว task2 จึงจะสามารถ take mutex ได้  การใช้งานแบบนี้ถ้า task ที่ priotiry สูงจะไม่สามารถ take mutex ได้ ถ้า task ที่ priority ต่ำ ยังไม่ give mutex คืน

Recursive mutexes
คือการวนซ้ำทำการ task กับ give เช่น เมื่อทำการ take 5 ครั้ง ก็จะมีการ give คืน 5 ครั้ง
mutexes ไม่สามารถใช้งานกับ interrupt ได้เพราะ interrupt มีระบบ priority แต่ mutex มันมีการทำงานที่ไม่สน priority และระบบการ block ของ mutex ซึ่งการ interrupt ไม่ควรถูก blocked

RTOS Task Notifications
ทุก task จะประกอบด้วย 32-bits notifications  ตัว RTOS Task notifications คือ event ที่ทำการส่งข้อมูลหนึ่งไป unblock อีก task ได้ และสามารถ update ข้อมูล notification ได้ด้วย
การ update ข้อมูล notification ที่ทำได้ ได้แก่
- ตั้งค่า notification value ด้วยไม่ overwrite ตัวก่อนหน้า
- overwrite notification value
- ตั้งค่าจำนวน bit ของ notification value ตั้งแต่ 1 หรือมากกว่า
- เพิ่มค่า notification value ที่ task รับเข้ามา
notification ใช้งาน unblock  task ได้เร็วกว่า semaphore 45% และใช้ memory น้อยกว่า ทำให้มีการไปใช้ทำ lightweight semaphore, lightweight semaphore counting, lightweight event group, lightweight mailbox

ข้อจำกัด คือใช้งานส่ง notification ไปได้ task เดียวเท่านั้น และในกรณีที่นำ notification มาใช้แทน queue ตัว sending task จะเข้าไปเป็น block state ไม่ได้จนกว่าจะส่ง notification ไปยัง receiving task จนเสร็จ( receiving task อยู่ใน block state ระหว่างรอ notification )

task notifications มี open issue https://github.com/espressif/esp-idf/issues/872

co-routine state
co-routine ออกแบบมาให้ใช้กับ micro controller ที่มีข้อจำกัดด้านหน่วยความจำ(RAM) แต่โดยทั่วไปแล้ว micro controller 32-bits มักไม่ใช้ co-routine
Ready คือ co-routine ที่กำลัง execute
Running คือ co-routine ที่พร้อม execute แต่รออยู่ มีสาเหตุมาจาก routine ที่ priority สูงกว่ากำลัง run อยู่ หรือ task กำลัง run อยู่
Blocked คือรอการทำงานของ event รอได้นานตามเวลาที่กำหนด(time period expired)

co-routine priorities
กำหนดค่าได้ตั้งแต่ 0 ถึง  configMAX_CO_ROUTINE_PRIORITIES - 1  ค่าที่สูงกว่ามี priority มากกว่า

การสร้าง co-routine
void vACoRoutineFunction( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
ต้องเริ่มด้วย crSTART() และจบด้วย crEND()
ไม่มีการ return ค่ากลับ
co-routine สามารถสร้างจากอีก co-routine หนึ่งได้  จึงมี uxIndex(ตัวอย่างรับค่าเป็น int) ใช้เป็นค่าบอกความแตกต่างของแต่ละ co-routine

scheduling co-routine
ถ้ามีแต่ co-routine ในโปรแกรมจะสั่ง co-routine ที่ priority สูงสุดมา run

mixing task and co-routines
ถ้าสร้าง co-routine ไว้ใน task เมื่อ task นั้นทำงาน co-routine ก็จะทำงานด้วย โดยจัด priority ตาม task ที่ co-routine นั้นไปสิงอยู่

Limitations and Restrictions of co-routine
sharing stack คือ stack ของ co-routine ไม่ดูแล co-routine ตอนที่มัน block ทำให้ค่าของตัวแปรมีการเปลี่ยนได้ถ้ามีการเรียกใช้คำสั่ง crDelay แม้จะประการตัวแปร static ไว้ก็ตาม
ฟังก์ชันที่ถูกเรียกใช้ใน co-routine ไม่สามารถ run คำสั่ง block( เช่น crDelay) ได้
use of switch statements คือไม่สามารใช้คำสั่ง block ใน switch case ได้

Event bit and Event group
Event bit คือการกำหนดค่าของบิตเป็น flag เพื่อสื่อความหมายอะไรสักอย่าง เช่น กำหนดให้ bit =1 เมื่อส่งข้อมูล กำหนดให้บิตเป็น 0 เพื่อรับข้อมูล เป็นต้น

Event group คือ event bit ที่มีหลาย digit เรากำหนดค่าแต่ละ digit ได้

ความท้าทายในการใช้งาน event group
race condition
    เพราะ event group ใครจะเข้ามาเขียนก็ได้ ดังนั้น ฟังก์ชันที่เข้ามาก่อนทำการเปลี่ยนค่า จะทำให้อีกฟังก์ชันหนึ่งรับค่าที่เปลี่ยนไป และมันไม่มีการกำหนดว่าฟังก์ชันไหนสามารถ set bit, clear bit ได้
avoid non-determinism
    มันทำมาให้ใช้งานแบบ non-determisim เพราะมันไม่รู้ว่าจะมีกี่ task ที่ถูก block โดย event group เมื่อมี bit ถูก set ทำให้ไม่รู้เงื่อนไขที่ใช้ test กับเงื่อนไขที่จะ unblock task นั้น
FreeRTOS quality standard เน้น deterministic action การใช้งาน event group จะเป็นการไม่ทำตาม standard
The RTOS scheduler's locking mechanism ทำให้ interrupt ยังคงทำงานได้ เมื่อ task set event bit
The centralised deferred interrupt mechanism ทำการ set bit ได้เมื่อทำการ set bit จาก interrupt service routine

esp32 idf information
http://esp32.info/docs/esp_idf/html/modules.html