| | 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 |