| 1 | = ESP 32 RTOS = |
| 2 | |
| 3 | ช่วงนี้กลับมาเล่นพวกบอร์ด ESP32 อีกครั้ง หลังจากโดนทวงงานที่ดองค้างไว้นานน(น.หนู 32 ตัว) ซึ่งในงานนี้มีส่วนที่ต้องศึกษาการเขียนโปรแกรมแบบ Multi-tasking บน ESP32 จึงเป็นโอกาสที่จะเขียนเก็บไว้เป็น log ดูเล่นเผื่อวันหลังเรากลับมาดูโปรแกรมที่ตัวเองเขียนไว้จะได้ไม่งง |
| 4 | |
| 5 | เนื่องจากสเปกของ บอร์ด ESP32 มันมี 2 CPU ครับ (CPU0 และ CPU1) ด้วยเหตุนี้เราจึงอยากจะลองเขียนโปรแกรมเพื่อแบ่งงานให้ CPU 2 ตัวนี้ไปช่วยกันทำงานโดยใช้ RTOS ครับ |
| 6 | |
| 7 | Note : บทความนี้อธิบายบนพื้นฐานการใช้งานบอร์ด ESP32 และ Arduino IDE ครับ ใครที่ยังไม่เคยเล่น Arduino อาจจะต้องไปศึกษาพื้นฐานเพิ่มเติมครับ |
| 8 | |
| 9 | ESP32 รองรับการการเขียนโปรแกรมเพื่อใช้ FreeRTOS อยู่แล้วครับ เราจะมาเรียนรู้ตัวอย่างการใช้งานแบบง่ายๆกัน แต่ก่อนอื่นมาเข้าใจกันก่อนว่า … |
| 10 | RTOS คืออะไร ? |
| 11 | |
| 12 | เจ้า RTOS (Real-Time Operating System ) มันเป็น Kernel ที่ช่วยให้บอร์ด ESP32 สามารถเขียนโปรแกรมเพื่อแบ่งเวลาแบ่งทรัพยากรในการประมวลผลงานต่างๆ ในแต่ละ CPU ได้ |
| 13 | |
| 14 | การเขียนโปรแกรมโดยใช้ RTOS เราจะต้องสร้างหน่วยทำงานที่เรียกว่า Task ซึ่งแต่ละ Task สามารถแยกกันทำงานแบบอิสระต่อกันได้ |
| 15 | |
| 16 | RTOS มีวิธีจัดการ Task ต่างๆ โดยการดูระดับความสำคัญ ( Priority ) ซึ่ง Task ที่มีระดับความสำคัญสูงสุดจะได้ทำงานก่อน และใช้ Time slicing ในการแบ่งเวลากันทำงาน ซึ่งทำให้ ESP32 สามารถทำงานคล้ายแบบ Multi-tasking ได้ |
| 17 | Time slicing |
| 18 | |
| 19 | RTOS มีฟังก์ชั่นคล้ายๆ Delay() แต่เป็นรูปแบบ Software Timer ชื่อว่า vDelayTask() ด้วยฟังก์ชันนี้ทำให้เราสามารถสั่งให้ Task หยุดพักการทำงาน โดยไม่ทำให้ CPU หยุดพักการทำงานไปด้วย ( Delay() เป็น Hardware Timer ) ทำให้แต่ละ Task แบ่งเวลากันทำงานเป็นรูปแบบ Time Slicing คือ ถ้ายังมี Task ที่รอทำงานต่อก็ จับมา Run ได้เลยครับ |
| 20 | |
| 21 | ขอยกตัวอย่างครับ เช่นว่า เราต้องการเขียนโปรแกรมสั่ง ESP32 ให้ทำงานอยู่ 2 งาน คือ [ 1 – สั่งให้ไปเก็บค่าจาก Sensor ] และ [ 2 - สั่งให้ส่ง SMS รายงานผล] ตามรูปนี้… |
| 22 | |
| 23 | ทั่วไป เราจะเขียนโปรแกรมแบบข้อ 1 เริ่มต้นโดยการสั่งให้ทำฟังก์ชั่นอ่านค่าจาก Analog sensor ก่อน แล้วใส่ Delay() เพื่อรออ่านค่าจาก sensor หลังจากนั้นจึงทำฟังก์ชันส่ง SMS ต่อ ซึ่งก็ใส่ Delay() อีกครั้งเพื่อรอ GSM module ทำงานให้เสร็จ |
| 24 | |
| 25 | ในช่วงที่ฟังก์ชัน Delay() ทำงาน ช่วงนั้น CPU จะหยุดการประมวลผล ซึ่งหาก Delay มีค่าที่ตั้งไว้นาน จะเกิดช่วงเวลาที่สูญเปล่าเพราะไม่ได้ทำงานฟังก์ชันอื่นต่อเนื่องเลย |
| 26 | |
| 27 | แต่ถ้าเราเปลี่ยนไปเขียนโปรแกรมแบบข้อ 2 ( เขียนเป็น Task โดยใช้ RTOS ) และใช้ vTaskDelay() แทน เมื่อ Taskอ่านค่าSensor ทำงานเสร็จ Taskส่งSMS ก็จะทำงานต่อทันที และระหว่างช่วงที่ Taskส่งSMS ถูก Blocked รอทำงานให้เสร็จ Taskอ่านค่าsensor ก็สามารถทำงานวนรอไปเรื่อยๆได้ เนื่องจาก vTaskDelay() จะหยุดการทำงานของ Task ที่ระดับ Software จึงทำให้ CPU ประมวลผล Task อื่นต่อเนื่องได้ |
| 28 | Task |
| 29 | |
| 30 | การสร้าง Task ใน RTOS เราจะเขียน function ที่เราต้องการให้ทำงานไว้ก่อน แล้วก็สร้าง Task เพื่อไปเรียก function มาใช้งานอีกที |
| 31 | |
| 32 | พวก function ที่เราเขียนควรจะมี infinite loop ภายในด้วยนะครับ Task จะได้ทำงานวนไปเรื่อยๆ แต่ถ้าเราเขียนโปรแกรมให้ฟังก์ชั่นทำงานครั้งเดียวจบ เราต้องลบ Task ที่ไม่ใช้งานด้วย |
| 33 | |
| 34 | Task ถ้ามองง่ายๆ มันก็คือ กล่องๆหนึ่งที่เราต้องเอา Function ไปผูกไว้ และมีการกำหนด Parameter ต่างๆเพิ่มเติมเข้ามา เช่น Priority เพื่อไว้ตัดสินว่ากล่อง Task ใหนจะได้ทำงานก่อน |
| 35 | ส่วนประกอบเพื่อสร้าง Task |
| 36 | |
| 37 | เมื่อเราจะสร้าง Task ให้เราประกาศฟังก์ชันตามรูปแบบนี้ |
| 38 | {{{ |
| 39 | xTaskCreate(TaskFunction,TaskName,StackDepth,(void*)PassParameters,TaskPriority,TaskHandle) |
| 40 | }}} |
| 41 | |
| 42 | มาดูตัวแปรที่ใช้ตอนสร้าง Task ทีละตัวกัน |
| 43 | {{{ |
| 44 | TaskFunction : ชื่อฟังก์ชันที่จะให้ทำงานเป็น Task ซึ่งในฟังก์ชันนั้นควรจะเขียนโปรแกรมแบบทำงานวนลูป( infinite loop ) |
| 45 | TaskName : ชื่อของ Task ( ข้อมูลตรงส่วนนี้จะใช้ตอน Debug ไม่มีผลต่อการทำงานของโปรแกรมโดยตรง ) |
| 46 | StackDepth : ขนาด Stack ของ Task (เพื่อจอง memory) การกำหนดขนาดของ Task ถ้ากำหนดไว้น้อยเกินไปจะทำให้ ESP32 Restart ตัวเองตลอดเวลา แต่ถ้ากำหนดไว้มากไปก็ทำให้เสีย memory ทิ้งเปล่าๆ การกำหนดค่านี้แบบคร่าวๆ คือ ลอง complie ดูขนาดของ function ที่ผูกกับ Task นี้ ลองดูจำนวน Byte ที่ใช้ไปครับ แล้วเอามาใส่ในตัวแปรนี้ |
| 47 | PassParameter : ชื่อตัวแปรที่จะส่งค่าเข้ามาทำงานต่อใน Task ( ดูวิธีการทำงานใน code ตัวอย่างเพิ่มเติมครับ ) |
| 48 | TaskPriority : กำหนดเลข Priority ให้ Task ซึ่งค่า 0 คือ Priority ที่ต่ำที่สุด |
| 49 | TaskHandle : ชื่อตัวแปรของ Task ที่จะนำไปใช้ในการ Handle ทำงานอื่นๆต่อ ( ดูวิธีการทำงานใน code ตัวอย่างเพิ่มเติมครับ ) |
| 50 | }}} |
| 51 | แต่ถ้าอยากกำหนดให้ลึกว่า CPU ใหนจะทำงาน Task นั้นๆ ให้ประกาศฟังก์ชันตามรูปแบบนี้ |
| 52 | {{{ |
| 53 | xTaskCreatePinnedToCore(TaskFunction,TaskName,StackDepth,(void*)PassParameters,TaskPriority,TaskHandle,Core) |
| 54 | }}} |
| 55 | 7. Core : สำหรับ ESP เราสามารถกำหนดให้ Task ทำงานที่ CPU 0 หรือ 1 |
| 56 | |
| 57 | ลองมาดู code ตัวอย่างการสร้าง Task เพื่อความเข้าใจกันครับ โดยตัวอย่างนี้ เราจะเขียนโปรแกรมสร้าง Task ในบอร์ด ESP32 เพื่อปริ้นดูการทำงานของแต่ละ Task ผ่าน Serial port |
| 58 | |
| 59 | อธิบาย code ทีละส่วนกัน |
| 60 | {{{ |
| 61 | const TickType_t xDelay2000ms = pdMS_TO_TICKS(2000); |
| 62 | }}} |
| 63 | ฟังก์ชัน vTaskDelay() จะรับค่า Tick ซึ่งเป็นค่า Timer ของ CPU ดังนั้นเราจึงต้องแปลงค่า เวลา(หน่วยมิลลิวินาที)ให้เป็นค่า Tick ก่อน จึงนำมาใช้งานได้ |
| 64 | |
| 65 | เริ่มต้น เราจึงสร้างตัวแปรมาเก็บค่า Tick เพื่อใช้ในฟังก์ชั้น vTaskDelay(…) โดยเรียกใช้ฟังก์ชัน pdMS_TO_TICKS(…) ที่จะแปลงตัวเลขหน่วยมิลลิวินาทีเป็นจำนวน Tick (ในตัวอย่างนี้คือ xDelay2000ms เก็บค่า Tick ของ 2 วินาทีไว้ ) |
| 66 | {{{ |
| 67 | TaskHandle_t Task1 = NULL; |
| 68 | TaskHandle_t Task2 = NULL; |
| 69 | TaskHandle_t Task3 = NULL; |
| 70 | }}} |
| 71 | สร้างตัวแปรมาเก็บค่า TaskHandle ของแต่ละ Task ไว้ก่อน เดี๋ยวเอาไปใช้ตอนประกาศ Created Task |
| 72 | {{{ |
| 73 | int passValue = 0; |
| 74 | }}} |
| 75 | สร้างตัวแปรเก็บค่าตัวเลขไว้ เดี๋ยวจะเอาไปทดลองใช้ส่งค่าเข้าไปใน Task1 เพื่อนับจำนวนรอบที่ทำงาน |
| 76 | {{{ |
| 77 | void setup() { |
| 78 | Serial.begin(115200); |
| 79 | delay(1000); |
| 80 | // Created 3 Task here |
| 81 | xTaskCreatePinnedToCore(func1_Task,"func1_Task",1000,(void*) passValue,1,&Task1,0); |
| 82 | xTaskCreatePinnedToCore(func2_Task,"func2_Task",1000,NULL,1,&Task2,0); |
| 83 | xTaskCreatePinnedToCore(func3_Task,"func3_Task",1000,NULL,1,&Task3,0); |
| 84 | } |
| 85 | }}} |
| 86 | มาถึงส่วนของฟังก์ชั่น setup(…) เราจะประกาศ Created Task กันในฟังก์ชันนี้ โดยใช้คำสั่ง xTaskCreatePinnedToCore(…) |
| 87 | |
| 88 | Task ที่ 1 : ผูกกับฟังก์ชั่น func1_Task() , ตั้งชื่อ Task ว่า “func1_Task”,DepthStack = 1000 ,ส่งตัวแปร PassValue เข้าไปใน Task นี้ด้วย , มีค่า Priority = 1 , ผูกกับ TaskHandle ชื่อว่า Task1 และให้ Task นี้ทำงานอยู่บน Core 0 |
| 89 | Task ที่ 2 และ 3 : ผูกกับ func2_Task() และ func3_Task() ตามลำดับ โดยไม่มีการส่งตัวแปรเข้าไปใน Task , มี Priority = 1 และให้ Task ทำงานอยู่บน Core 0 เหมือนกัน |
| 90 | {{{ |
| 91 | void loop() { } |
| 92 | }}} |
| 93 | ไม่จำเป็นต้องเขียนโปรแกรมใน Loop() |
| 94 | {{{ |
| 95 | void func1_Task(void *pvvalue){ |
| 96 | int f1param = (int)pvvalue ; |
| 97 | while(1){ |
| 98 | Serial.println(String("hello from Task1 : count >> ") + f1param ); |
| 99 | f1param++; |
| 100 | vTaskDelay(xDelay2000ms); |
| 101 | } |
| 102 | } |
| 103 | }}} |
| 104 | ในส่วนของ func1_Task() ที่ผูกกับ Task1 ไว้ |
| 105 | |
| 106 | สร้างตัวแปร int ชื่อ f1param ไว้เก็บค่า pvvalue ที่ส่งเข้ามา(แปลง type ให้เป็น int ไว้แล้ว ) |
| 107 | ปริ้นค่า “Hello from Task1” พร้อมกับจำนวนรอบที่ทำงาน ( ตัวแปร f1param ) ผ่าน Serial port |
| 108 | อัพเดทค่า f1param +1 เข้าไป ซึ่งตัวแปร passValue ก็จะถูกอัพเดทค่าใหม่นี้ด้วย เพราะเป็น pointer ของ f1param |
| 109 | ทำการ Delay Task1 ไว้ 2 วินาที แล้วจะวนมาทำงานใหม่ |
| 110 | |
| 111 | void func2_Task(void *pvParam){ |
| 112 | while(1){ |
| 113 | Serial.println(String("hello from Task2")); |
| 114 | vTaskDelay(xDelay2000ms); |
| 115 | } |
| 116 | }void func3_Task(void *pvParam){ |
| 117 | while(1){ |
| 118 | Serial.println(String("hello from Task3")); |
| 119 | vTaskDelay(xDelay2000ms); |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | ส่วนใน func2_Task() และ func3_Task() ก็จะสร้าง infinite loop ไว้ปริ้นค่า Hello from task ไว้เช่นกัน และ มี Delay ไว้ 2 วินาทีค่อยทำงานใหม่อีกครั้ง |
| 124 | |
| 125 | การทำงานของ code นี้ |
| 126 | |
| 127 | Task ถูกสร้างไล่เรียงลำดับเป็น Task1 , Task2 และ Task3 ภายใน Setup() |
| 128 | เนื่องจากทุกTask มีค่า Priority เท่ากัน Task 1 ที่สร้างขึ้นมาก่อนจึงได้ทำงานก่อน และ Task2 และ Task3 จะทำงานเรียงต่อกันไป |
| 129 | Task1 เริ่มทำงาน โดยการปริ้น “Hello from Task1” พร้อมกับแสดงจำนวนรอบที่ทำงาน โดยนำตัวแปรชื่อ passValue ที่ส่งผ่านเข้ามาใน Task มาแสดงค่า หลังจากนั้น เพิ่มค่า +1 ให้กับตัวแปรดังกล่าว แล้วทำการ Blocked Task1 ให้หยุดทำงานชั่วคราว 2 วินาที ด้วยคำสั่ง vTaskDelay() |
| 130 | หลังจาก Task1 ถูก Blocked แล้ว Task2 จะเริ่มทำงาน โดยการปริ้น “Hello from Task2” แล้วทำการ blocked Task2 ให้หยุดทำงานชั่วคราว 2 วินาที |
| 131 | หลังจาก Task2 ถูก Blocked แล้ว Task3 จะเริ่มทำงาน โดยการปริ้น “Hello from Task3” แล้วทำการ blocked Task3 ให้หยุดทำงานชั่วคราว 2 วินาที |
| 132 | เมื่อ ครบ 2 วินาที Task1,Task2 และ Task3 จะกลับมาทำงานปกติ ทำงานวนไปเรื่อยๆ |
| 133 | |
| 134 | STATE ของ TASK |
| 135 | |
| 136 | เพื่อความเข้าใจในการจัดการ Task เพิ่มขึ้น ควรจะมาดูเรื่อง State การทำงานของ Task กันต่อ ซึ่งมี 4 State ดังนี้ |
| 137 | |
| 138 | 1) Running state : Task ที่มี Priority สูงสุด จะได้ทำงานใน State นี้ และเป็น Task เดียวที่ได้ทำงานในเวลานั้นๆ |
| 139 | |
| 140 | 2) Ready state : Task ที่รอเข้าไปทำงานใน Running state จะกองกันอยู่ที่นี่ ( เป็นพวก Task ที่มี Priority น้อยว่า Task ใน Running state) |
| 141 | |
| 142 | 3) Blocked state : Task ที่อยู่ใน state นี้คือ Task ที่ถูก Blocked การทำงานชั่วคราว เช่น Task ที่ใช้คำสั่ง vTaskDelay() |
| 143 | |
| 144 | 4) Suspended state : Task ที่อยู่ใน state นี้คือ Task ที่โดนสั่งให้พักการทำงาน โดยใช้คำสั่ง vTaskSuspend() และหาต้องการให้ Task นั้นๆ กลับมาทำงานตามปกติ เราต้องส่งคำสั่ง vTaskResume() เพื่อปลดล็อค |
| 145 | |
| 146 | RTOS API บางส่วนที่เราจะมาดูกันเพิ่ม เพื่อลองเขียนโปรแกรมเปลี่ยน State ของ Task มีดังนี้ |
| 147 | |
| 148 | xTaskCreated(…) : สร้าง Task ใหม่ |
| 149 | vTaskDelete(…) : ลบ Task ที่เคยสร้างไว้ ( เลิกใช้งาน Task ) |
| 150 | vTaskDelay(…) : สั่งให้ Task หยุดพการทำงานตามเวลาที่กำหนด |
| 151 | vTaskSuspend(…) : สั่งให้ Task เข้าสู่ Suspend state ( หยุดทำงาน ) |
| 152 | vTaskResume(…) : สั่งให้ Task เข้าสู่ Ready state ( ออกจาก Suspend state ) |
| 153 | vTaskPriorityGet(…) : อ่านค่า Priority ของ Task |
| 154 | vTaskPrioritySet(…) : เปลี่ยนค่า Priority ของ Task |
| 155 | |
| 156 | มาลองดู code ตัวอย่างกันครับ |
| 157 | |
| 158 | คร่าวๆการทำงานของ code ตัวอย่าง เราจะสร้าง Task มาลองทำงานแบบสลับ State เมื่อทำงานจบ ก็จะลบ Task ทิ้งไป |
| 159 | |
| 160 | TaskHandle_t Task1 = NULL; |
| 161 | TaskHandle_t Task2 = NULL; |
| 162 | TaskHandle_t Task3 = NULL; |
| 163 | TaskHandle_t Task4 = NULL; |
| 164 | |
| 165 | เริ่มต้นด้วยการสร้างตัวแปรมาเก็บค่า TaskHandle ของแต่ละ Task |
| 166 | |
| 167 | void setup() { |
| 168 | Serial.begin(115200); |
| 169 | delay(3000); |
| 170 | // Created 4 Task here |
| 171 | xTaskCreatePinnedToCore(f4_Task,"func4_Task",1000,NULL,4,&Task3,0); |
| 172 | xTaskCreatePinnedToCore(f3_Task,"func3_Task",1000,NULL,3,&Task3,0); |
| 173 | xTaskCreatePinnedToCore(f2_Task,"func2_Task",1000,NULL,2,&Task2,0); |
| 174 | xTaskCreatePinnedToCore(f1_Task,"func1_Task",1000,NULL,1,&Task1,0); |
| 175 | } |
| 176 | |
| 177 | ใน Setup() เราประกาศสร้าง Task จำนวน 4 Task โดยแต่ละ Task มี Priority ต่างกัน |
| 178 | |
| 179 | void f4_Task(void *pvParam){ |
| 180 | |
| 181 | Serial.println(String("Hello from TASK4 , Priority is : ") + uxTaskPriorityGet(Task4)); |
| 182 | int newPriority = uxTaskPriorityGet(Task4) - 4; |
| 183 | Serial.println(String("NOW WE CHANGE PRIORITY OF TASK4 TO BE : ") + newPriority ); |
| 184 | vTaskPrioritySet(Task4,newPriority); |
| 185 | |
| 186 | Serial.println(String("Hello again from TASK4 , then delete TASK4 !")); |
| 187 | vTaskDelete(NULL); |
| 188 | |
| 189 | } |
| 190 | |
| 191 | ฟังก์ชั่น f4_Task() ที่ผูกกับ Task4 |
| 192 | |
| 193 | ปริ้น “Hello from Task4” ผ่าน Serial Port พร้อมทั้งปริ้นค่า Priority ของ Task ด้วยฟังก์ชั้น uxTaskPriorityGet(…) |
| 194 | เปลี่ยน Priority ของ Task4 จาก 4 ให้เป็น 0 ( ต่ำสุด ) ด้วยฟังก์ชัน vTaskPrioritySet(…) |
| 195 | ลบ Task4 ทิ้ง โดยคำสั่ง vTaskDelete(…) |
| 196 | |
| 197 | void f3_Task(void *pvParam){ |
| 198 | |
| 199 | Serial.println(String("Hello from TASK3 , then Suspend TASK2 & TASK3 !")); |
| 200 | vTaskSuspend(Task2); |
| 201 | vTaskSuspend(NULL);Serial.println(String("Hello again from TASK3 , then delete TASK3 !")); |
| 202 | vTaskDelete(NULL); |
| 203 | |
| 204 | } |
| 205 | |
| 206 | ฟังก์ชั่น f3_Task() ที่ผูกกับ Task3 |
| 207 | |
| 208 | ปริ้น “Hello from Task3” ผ่าน Serial Port |
| 209 | Suspend Task2 และ Task3 ด้วยคำสั่ง vTaskSuspend(…) |
| 210 | ลบ Task3 ทิ้ง โดยคำสั่ง vTaskDelete(…) |
| 211 | |
| 212 | void f2_Task(void *pvParam){ |
| 213 | |
| 214 | Serial.println(String("Hello from TASK2 , then Resume TASK3 !")); |
| 215 | vTaskResume(Task3);Serial.println(String("Hello again from TASK2 , then delete TASK2 !")); |
| 216 | vTaskDelete(Task2); |
| 217 | |
| 218 | } |
| 219 | |
| 220 | ฟังก์ชั่น f2_Task() ที่ผูกกับ Task2 |
| 221 | |
| 222 | ปริ้น “Hello from Task2” ผ่าน Serial Port |
| 223 | Resume Task3 ด้วยคำสั่ง vTaskResume(…) |
| 224 | ลบ Task2 ทิ้ง โดยคำสั่ง vTaskDelete(…) |
| 225 | |
| 226 | void f1_Task(void *pvParam){ |
| 227 | |
| 228 | Serial.println(String("Hello from TASK1 , then Resume TASK2 !")); |
| 229 | vTaskResume(Task2);Serial.println(String("Hello again from TASK1 , then delete TASK1 !")); |
| 230 | vTaskDelete(NULL); |
| 231 | |
| 232 | }x |
| 233 | |
| 234 | ฟังก์ชั่น f1_Task() ที่ผูกกับ Task1 |
| 235 | |
| 236 | ปริ้น “Hello from Task1” ผ่าน Serial Port |
| 237 | Resume Task2 ด้วยคำสั่ง vTaskResume(…) |
| 238 | ลบ Task1 ทิ้ง โดยคำสั่ง vTaskDelete(…) |
| 239 | |
| 240 | การทำงานของตัวอย่าง code นี้ |
| 241 | |
| 242 | เริ่มต้น Task4 จะเริ่มทำงานก่อน โดยการปริ้น “Hello from Task4” พร้อมทั้งค่า Priority ของ Task4 เอง |
| 243 | Task4 ทำการเปลี่ยนค่า Priority ของตัวเอง จาก 4 เป็น 0 ( ค่าต่ำสุด ) |
| 244 | Task3 กลายเป็น Task ที่มี Priority สูงสุด จึงเลื่อนมาทำงานใน Running state. ส่วน Task4 ก็ถูกโยนกลับไปใน Ready state |
| 245 | Task3เริ่มทำงานโดยการปริ้น “Hello from Task3” หลังจากนั้น ก็ suspend Task2 และ Task3(ตัวมันเอง) ทำให้ Task2&Task3 ถูกโยนไปอยู่ใน Suspended state |
| 246 | ตอนนี้ Task1 ซึ่งมี Priority สูงสุด จะเริ่มทำงาน โดยการปริ้น “Hello from Task1” แล้วก็ทำการ resume Task2 กลับมา |
| 247 | Task2 จะกลับมาเป็น Task ที่มี Priority สูงสุดอีกครั้ง จึงทำให้ Task1 ถูกโยนกลับไปที่ Ready state อีกครั้ง |
| 248 | Task2 ก็เริ่มทำงานโดยการปริ้น “Hello from Task2” แล้วก็ทำการ resume Task3 กลับมาจาก suspended state |
| 249 | Task3 กลับมาทำงานอีกครั้งเนื่องจาก Priority สูงสุด และทำงานต่อจากการทำงานเดิม โดยการ delete ตัวเอง พร้อมกับปริ้น “Hello again from Task3 , then delete Task3” |
| 250 | Task2 กลับมาทำงานอีกครั้งเนื่องจาก Priority สูงสุด และทำงานต่อจากการทำงานเดิม โดยการ delete ตัวเอง พร้อมกับปริ้น “Hello again from Task2 , then delete Task2” |
| 251 | Task1 กลับมาทำงานอีกครั้งเนื่องจาก Priority สูงสุด และทำงานต่อจากการทำงานเดิม โดยการ delete ตัวเอง พร้อมกับปริ้น “Hello again from Task1 , then delete Task1” |
| 252 | Task4 กลับมาทำงานอีกครั้งเนื่องจาก Priority สูงสุด และทำงานต่อจากการทำงานเดิม โดยการ delete ตัวเอง พร้อมกับปริ้น “Hello again from Task4 , then delete Task4” |
| 253 | ขั้นตอนนี้นี้ไม่เหลือ Task ให้ทำงานแล้ว จึงจบการทำงาน |
| 254 | |
| 255 | เป็นไงครับ เห็นการทำงานโยนไปโยนมาของ Task ที่ไปอยู่ใน State ต่างๆ หวังว่าผู้อ่านคงเข้าใจเพิ่มขึ้นเกี่ยวกับการทำงานของ Task และเผื่อจะเป็นไอเดียเอาไปประยุกต์ใช้ในโปรเจ็คตัวเองนะครับ |
| 256 | |
| 257 | ถ้าโปรเจ็คของท่านไม่ได้มีความซับซ้อนมาก วิธีการเขียนโปรแกรมแบบเดิมอาจจะเป็นคำตอบที่ดีอยู่แล้วก็ได้ แต่ถ้าโปรแกรมที่เราเขียนมีความซับซ้อนและเกี่ยวเนื่องกับการทำงานหลายๆอย่างพร้อมกันกัน RTOS น่าจะเป็นคำตอบที่ดีสำหรับโปรเจ็คแบบนั้นครับ เพราะจะทำให้เราเขียน code ได้เข้าใจง่าย |
| 258 | |
| 259 | สุดท้ายนี้ ขอลง Reference แหล่งที่มาที่ศึกษาการใช้งาน RTOS ที่ผมไปอ่านแล้วลองทำตามดู ใครสนใจรายละเอียดต้นทาง ลองเข้าไปอ่านกันได้ครับ |
| 260 | Tasks: CreateTasks |
| 261 | Get started with creating a basic task in FreeRTOS with the ESP32 and ESP-IDF Before starting make sure that you have… |
| 262 | |
| 263 | coder137.github.io |
| 264 | FreeRTOS With Arduino 06 : Task Suspend and Resume |
| 265 | In earlier tutorials, we saw how to create, use and delete the tasks. In this tutorial, we will see how to Suspend and… |
| 266 | |
| 267 | www.instructables.com |
| 268 | การใช้งาน FreeRTOS ตอนที่ 1 |
| 269 | FreeRTOS พัฒนาขึ้นมาโดยบริษัท Real Time Engineer โดย FreeRTOS… |
| 270 | |
| 271 | medium.com |
| 272 | Attaphon One |
| 273 | |
| 274 | let’s find the better way |
| 275 | |
| 276 | Esp32 |
| 277 | Freertos |
| 278 | |
| 279 | More from Attaphon One |
| 280 | |
| 281 | let’s find the better way |
| 282 | |
| 283 | Mar 29, 2019 |
| 284 | MQL4 : เริ่มต้นเขียน Expert Advisor |
| 285 | |
| 286 | สวัสดีครับทุกท่าน ผมคิดว่าใครที่เข้ามาอ่านบทความนี้น่าจะกำลังสนใจที่จะศึกษาการเขียน Expert Advisor เพื่อเป็นตัวช่วยในการเทรด Forex ( ตลาดซื้อขายคู่เงินตราต่างประเทศ ) ซึ่งใครที่เพิ่งจะเริ่มต้นเทรด ผมขอแนะนำให้ไปศึกษาหลักการและวิธีการเทรดจาก Blog อื่นๆ ก่อนนะครับ ข้อมูลมันเยอะมาก |
| 287 | โปรแกรม Meta Trader ( MT4 ) ซึ่งใช้ในการเทรดค่าเงินต่างประเทศ |
| 288 | |
| 289 | blog นี้จะเน้นการเขียนโปรแกรม EA ที่ได้สะสมความรู้มา ผมอยากเขียนเก็บเป็น Log เผื่อมีใครสนใจจะได้ศึกษาการเขียนโปรแกรมไปด้วยกันครับ |
| 290 | |
| 291 | คำเตือน : ตลาด Forex มีความเสี่ยงสูงมาก + ก.ไก่ร้อยตัว ศึกษากันให้ดีก่อนเทรดด้วยนะ |
| 292 | |
| 293 | ขอคั่นโฆษณาสักนิดครับ ตอนนี้ผมเขียนบทความการสร้าง Expert Advisor แบบละเอียดแล้วนะครับ เข้าไปอ่านได้ใน link นี้ |
| 294 | MQL4 : สารบัญ Expert Advisor 101 |
| 295 | |
| 296 | attaphon.medium.com |
| 297 | |
| 298 | ในการเริ่มฝึกเทรดค่าเงิน ขั้นแรกเราต้องสมัครและเปิดบัญชีกับโบรกเกอร์ก่อน ( ขั้นตอนนี้ ผู้อ่านก็ต้องหาข้อมูลเพิ่มเอง ว่าจะสมัครโบรกเกอร์ใหน เพราะเราจะเน้นเขียนโปรแกรม ) เมื่อสมัครบัญชีได้แล้ว ให้เราดาวโหลดโปรแกรม MetaTrader 4 จากเวปของโบรกเกอร์มา install ในคอมพิวเตอร์ของเรา |
| 299 | |
| 300 | Read more · 4 min read |
| 301 | |
| 302 | More From Medium |
| 303 | Node.js streams by examples |
| 304 | Quynh Nguyen |
| 305 | Heard About Brain Computer Interfaces? |
| 306 | Nemath Ahmed in The Startup |
| 307 | Concurrency issue — A silent killer of your program |
| 308 | Tanapol Nearunchorn |
| 309 | Getting Started with Components & Icons in Denali |
| 310 | Chas Turansky in Denali Design |
| 311 | Deploy Strapi to Heroku Step by Step Tutorial |
| 312 | Teng Zhang |
| 313 | FreeRTOS in a nutshell |
| 314 | Florian Kromer |
| 315 | The Development of Node.JS Server to Communicate With KEPServerEX Via Configuration API |
| 316 | Zeno Chullamonthon in The Startup |
| 317 | How the iPhone made me break up with my first love (a biased UX Review) |
| 318 | Alan Ng. in Prototypr |
| 319 | |
| 320 | About |
| 321 | |
| 322 | Help |
| 323 | |
| 324 | Legal |