Changes between Initial Version and Version 1 of ESP32


Ignore:
Timestamp:
06/12/21 08:21:30 (3 years ago)
Author:
krit
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ESP32

    v1 v1  
     1= ESP 32 RTOS =
     2
     3ช่วงนี้กลับมาเล่นพวกบอร์ด ESP32 อีกครั้ง หลังจากโดนทวงงานที่ดองค้างไว้นานน(น.หนู 32 ตัว) ซึ่งในงานนี้มีส่วนที่ต้องศึกษาการเขียนโปรแกรมแบบ Multi-tasking บน ESP32 จึงเป็นโอกาสที่จะเขียนเก็บไว้เป็น log ดูเล่นเผื่อวันหลังเรากลับมาดูโปรแกรมที่ตัวเองเขียนไว้จะได้ไม่งง
     4
     5เนื่องจากสเปกของ บอร์ด ESP32 มันมี 2 CPU ครับ (CPU0 และ CPU1) ด้วยเหตุนี้เราจึงอยากจะลองเขียนโปรแกรมเพื่อแบ่งงานให้ CPU 2 ตัวนี้ไปช่วยกันทำงานโดยใช้ RTOS ครับ
     6
     7Note : บทความนี้อธิบายบนพื้นฐานการใช้งานบอร์ด ESP32 และ Arduino IDE ครับ ใครที่ยังไม่เคยเล่น Arduino อาจจะต้องไปศึกษาพื้นฐานเพิ่มเติมครับ
     8
     9ESP32 รองรับการการเขียนโปรแกรมเพื่อใช้ FreeRTOS อยู่แล้วครับ เราจะมาเรียนรู้ตัวอย่างการใช้งานแบบง่ายๆกัน แต่ก่อนอื่นมาเข้าใจกันก่อนว่า …
     10RTOS คืออะไร ?
     11
     12เจ้า RTOS (Real-Time Operating System ) มันเป็น Kernel ที่ช่วยให้บอร์ด ESP32 สามารถเขียนโปรแกรมเพื่อแบ่งเวลาแบ่งทรัพยากรในการประมวลผลงานต่างๆ ในแต่ละ CPU ได้
     13
     14การเขียนโปรแกรมโดยใช้ RTOS เราจะต้องสร้างหน่วยทำงานที่เรียกว่า Task ซึ่งแต่ละ Task สามารถแยกกันทำงานแบบอิสระต่อกันได้
     15
     16RTOS มีวิธีจัดการ Task ต่างๆ โดยการดูระดับความสำคัญ ( Priority ) ซึ่ง Task ที่มีระดับความสำคัญสูงสุดจะได้ทำงานก่อน และใช้ Time slicing ในการแบ่งเวลากันทำงาน ซึ่งทำให้ ESP32 สามารถทำงานคล้ายแบบ Multi-tasking ได้
     17Time slicing
     18
     19RTOS มีฟังก์ชั่นคล้ายๆ 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 อื่นต่อเนื่องได้
     28Task
     29
     30การสร้าง Task ใน RTOS เราจะเขียน function ที่เราต้องการให้ทำงานไว้ก่อน แล้วก็สร้าง Task เพื่อไปเรียก function มาใช้งานอีกที
     31
     32พวก function ที่เราเขียนควรจะมี infinite loop ภายในด้วยนะครับ Task จะได้ทำงานวนไปเรื่อยๆ แต่ถ้าเราเขียนโปรแกรมให้ฟังก์ชั่นทำงานครั้งเดียวจบ เราต้องลบ Task ที่ไม่ใช้งานด้วย
     33
     34Task ถ้ามองง่ายๆ มันก็คือ กล่องๆหนึ่งที่เราต้องเอา Function ไปผูกไว้ และมีการกำหนด Parameter ต่างๆเพิ่มเติมเข้ามา เช่น Priority เพื่อไว้ตัดสินว่ากล่อง Task ใหนจะได้ทำงานก่อน
     35ส่วนประกอบเพื่อสร้าง Task
     36
     37เมื่อเราจะสร้าง Task ให้เราประกาศฟังก์ชันตามรูปแบบนี้
     38{{{
     39xTaskCreate(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{{{
     53xTaskCreatePinnedToCore(TaskFunction,TaskName,StackDepth,(void*)PassParameters,TaskPriority,TaskHandle,Core)
     54}}}
     557. Core : สำหรับ ESP เราสามารถกำหนดให้ Task ทำงานที่ CPU 0 หรือ 1
     56
     57ลองมาดู code ตัวอย่างการสร้าง Task เพื่อความเข้าใจกันครับ โดยตัวอย่างนี้ เราจะเขียนโปรแกรมสร้าง Task ในบอร์ด ESP32 เพื่อปริ้นดูการทำงานของแต่ละ Task ผ่าน Serial port
     58
     59อธิบาย code ทีละส่วนกัน
     60{{{
     61const 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{{{
     67TaskHandle_t Task1 = NULL;
     68TaskHandle_t Task2 = NULL;
     69TaskHandle_t Task3 = NULL;
     70}}}
     71สร้างตัวแปรมาเก็บค่า TaskHandle ของแต่ละ Task ไว้ก่อน เดี๋ยวเอาไปใช้ตอนประกาศ Created Task
     72{{{
     73int passValue = 0;
     74}}}
     75สร้างตัวแปรเก็บค่าตัวเลขไว้ เดี๋ยวจะเอาไปทดลองใช้ส่งค่าเข้าไปใน Task1 เพื่อนับจำนวนรอบที่ทำงาน
     76{{{
     77void 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{{{
     91void loop() { }
     92}}}
     93ไม่จำเป็นต้องเขียนโปรแกรมใน Loop()
     94{{{
     95void 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
     111void 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
     134STATE ของ TASK
     135
     136เพื่อความเข้าใจในการจัดการ Task เพิ่มขึ้น ควรจะมาดูเรื่อง State การทำงานของ Task กันต่อ ซึ่งมี 4 State ดังนี้
     137
     1381) Running state : Task ที่มี Priority สูงสุด จะได้ทำงานใน State นี้ และเป็น Task เดียวที่ได้ทำงานในเวลานั้นๆ
     139
     1402) Ready state : Task ที่รอเข้าไปทำงานใน Running state จะกองกันอยู่ที่นี่ ( เป็นพวก Task ที่มี Priority น้อยว่า Task ใน Running state)
     141
     1423) Blocked state : Task ที่อยู่ใน state นี้คือ Task ที่ถูก Blocked การทำงานชั่วคราว เช่น Task ที่ใช้คำสั่ง vTaskDelay()
     143
     1444) Suspended state : Task ที่อยู่ใน state นี้คือ Task ที่โดนสั่งให้พักการทำงาน โดยใช้คำสั่ง vTaskSuspend() และหาต้องการให้ Task นั้นๆ กลับมาทำงานตามปกติ เราต้องส่งคำสั่ง vTaskResume() เพื่อปลดล็อค
     145
     146RTOS 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
     160TaskHandle_t Task1 = NULL;
     161TaskHandle_t Task2 = NULL;
     162TaskHandle_t Task3 = NULL;
     163TaskHandle_t Task4 = NULL;
     164
     165เริ่มต้นด้วยการสร้างตัวแปรมาเก็บค่า TaskHandle ของแต่ละ Task
     166
     167void 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
     179void 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
     197void 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
     212void 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
     226void 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 ที่ผมไปอ่านแล้วลองทำตามดู ใครสนใจรายละเอียดต้นทาง ลองเข้าไปอ่านกันได้ครับ
     260Tasks: CreateTasks
     261Get started with creating a basic task in FreeRTOS with the ESP32 and ESP-IDF Before starting make sure that you have…
     262
     263coder137.github.io
     264FreeRTOS With Arduino 06 : Task Suspend and Resume
     265In earlier tutorials, we saw how to create, use and delete the tasks. In this tutorial, we will see how to Suspend and…
     266
     267www.instructables.com
     268การใช้งาน FreeRTOS ตอนที่ 1
     269FreeRTOS พัฒนาขึ้นมาโดยบริษัท Real Time Engineer โดย FreeRTOS…
     270
     271medium.com
     272Attaphon One
     273
     274let’s find the better way
     275
     276    Esp32
     277    Freertos
     278
     279More from Attaphon One
     280
     281let’s find the better way
     282
     283Mar 29, 2019
     284MQL4 : เริ่มต้นเขียน Expert Advisor
     285
     286สวัสดีครับทุกท่าน ผมคิดว่าใครที่เข้ามาอ่านบทความนี้น่าจะกำลังสนใจที่จะศึกษาการเขียน Expert Advisor เพื่อเป็นตัวช่วยในการเทรด Forex ( ตลาดซื้อขายคู่เงินตราต่างประเทศ ) ซึ่งใครที่เพิ่งจะเริ่มต้นเทรด ผมขอแนะนำให้ไปศึกษาหลักการและวิธีการเทรดจาก Blog อื่นๆ ก่อนนะครับ ข้อมูลมันเยอะมาก
     287โปรแกรม Meta Trader ( MT4 ) ซึ่งใช้ในการเทรดค่าเงินต่างประเทศ
     288
     289blog นี้จะเน้นการเขียนโปรแกรม EA ที่ได้สะสมความรู้มา ผมอยากเขียนเก็บเป็น Log เผื่อมีใครสนใจจะได้ศึกษาการเขียนโปรแกรมไปด้วยกันครับ
     290
     291    คำเตือน : ตลาด Forex มีความเสี่ยงสูงมาก + ก.ไก่ร้อยตัว ศึกษากันให้ดีก่อนเทรดด้วยนะ
     292
     293ขอคั่นโฆษณาสักนิดครับ ตอนนี้ผมเขียนบทความการสร้าง Expert Advisor แบบละเอียดแล้วนะครับ เข้าไปอ่านได้ใน link นี้
     294MQL4 : สารบัญ Expert Advisor 101
     295
     296attaphon.medium.com
     297
     298ในการเริ่มฝึกเทรดค่าเงิน ขั้นแรกเราต้องสมัครและเปิดบัญชีกับโบรกเกอร์ก่อน ( ขั้นตอนนี้ ผู้อ่านก็ต้องหาข้อมูลเพิ่มเอง ว่าจะสมัครโบรกเกอร์ใหน เพราะเราจะเน้นเขียนโปรแกรม ) เมื่อสมัครบัญชีได้แล้ว ให้เราดาวโหลดโปรแกรม MetaTrader 4 จากเวปของโบรกเกอร์มา install ในคอมพิวเตอร์ของเรา
     299
     300Read more · 4 min read
     301
     302More From Medium
     303Node.js streams by examples
     304Quynh Nguyen
     305Heard About Brain Computer Interfaces?
     306Nemath Ahmed in The Startup
     307Concurrency issue — A silent killer of your program
     308Tanapol Nearunchorn
     309Getting Started with Components & Icons in Denali
     310Chas Turansky in Denali Design
     311Deploy Strapi to Heroku Step by Step Tutorial
     312Teng Zhang
     313FreeRTOS in a nutshell
     314Florian Kromer
     315The Development of Node.JS Server to Communicate With KEPServerEX Via Configuration API
     316Zeno Chullamonthon in The Startup
     317How the iPhone made me break up with my first love (a biased UX Review)
     318Alan Ng. in Prototypr
     319
     320About
     321
     322Help
     323
     324Legal