- 在剛開始學習 arduino 時,當時想讓幾個燈以不同的頻率閃爍,找遍了網上,也沒找到可以實現的方法,後來學習 STM32 後,定時器操作勉強可以達到想要的多任務效果,但也不盡人意,直到了解到 STM32 可以跑系統,才知道單片機也可以這麼玩。後來從ESP8266到ESP32,瞭解到ESP32的超強內核,內嵌 FreeRTOS 操作系統,有了這一功能,我們可以輕易完成當初的想法。
- FreeRTOS 不僅可以在SDK編程中可以使用,Arduino 中也支持FreeRTOS 的一系列操作。
- 此博文主要記錄學習過程的心得體會和程序代碼,以供後續項目使用!
- 學習地址:DFROBOT官網
- ESP32-IDF 官方講解FreeRTOS:http://esp32.info/docs/esp_idf/html/dd/d3c/group__xTaskCreate.html
- FreeRTOS官網:https://www.freertos.org/a00125.html
- 創建 FreeRTOS 任務:
/* @功能:創建多任務 @時間:2020/3/5 @作者:劉澤文 @QQ:2822604962 */ #include <WiFi.h> #define LED1 19 #define LED2 22 #define LED1_OFF digitalWrite(LED1, HIGH)//關燈 #define LED1_ON digitalWrite(LED1, LOW)//開燈 #define LED1_PWM digitalWrite(LED1, !digitalRead(LED1))//燈閃爍 #define LED2_OFF digitalWrite(LED2, HIGH)//關燈 #define LED2_ON digitalWrite(LED2, LOW)//開燈 #define LED2_PWM digitalWrite(LED2, !digitalRead(LED2))//燈閃爍 void taskOne( void * parameter ){ while(1){ delay(200); LED1_PWM; } Serial.println("Ending task 1"); vTaskDelete( NULL ); } void taskTwo( void * parameter){ while(1){ delay(400); LED2_PWM; } Serial.println("Ending task 2"); vTaskDelete( NULL ); } void taskThree( void * parameter){ while(1){ delay(800); LED1_PWM; LED2_PWM; } Serial.println("Ending task 3"); vTaskDelete( NULL ); } void setup() { Serial.begin(115200); pinMode(LED1,OUTPUT); pinMode(LED2,OUTPUT); LED1_OFF; LED2_OFF; delay(1000); xTaskCreate( taskOne, /*任務函數*/ "TaskOne", /*帶任務名稱的字符串*/ 10000, /*堆棧大小,單位爲字節*/ NULL, /*作爲任務輸入傳遞的參數*/ 1, /*任務的優先級*/ NULL); /*任務句柄*/ xTaskCreate( taskTwo, /* Task function. */ "TaskTwo", /* String with name of task. */ 10000, /* Stack size in bytes. */ NULL, /* Parameter passed as input of the task */ 1, /* Priority of the task. */ NULL); /* Task handle. */ xTaskCreate( taskThree, /* Task function. */ "taskThree", /* String with name of task. */ 10000, /* Stack size in bytes. */ NULL, /* Parameter passed as input of the task */ 1, /* Priority of the task. */ NULL); /* Task handle. */ } void loop(){ delay(1000); }
- 查詢 FreeRTOS 任務優先級:
/* @功能:查詢任務優先級 @時間:2020/3/5 @作者:劉澤文 @QQ:2822604962 */ #include <Arduino.h> #define LED1 19 #define LED1_OFF digitalWrite(LED1, HIGH)//關燈 #define LED1_ON digitalWrite(LED1, LOW)//開燈 #define LED1_PWM digitalWrite(LED1, !digitalRead(LED1))//燈閃爍 void taskOne( void * parameter ){ while(1){ delay(200); LED1_PWM; } Serial.println("Ending task 1"); vTaskDelete( NULL ); } void setup(void) { Serial.begin(115200); pinMode(LED1,OUTPUT); LED1_OFF; delay(500); TaskHandle_t myTask;//聲明一個TaskHandle_t類型的變量,用於存儲將要新建的任務的句柄 xTaskCreate( taskOne, /*任務函數*/ "TaskOne", /*帶任務名稱的字符串*/ 10000, /*堆棧大小,單位爲字節*/ NULL, /*作爲任務輸入傳遞的參數*/ 6, /*任務的優先級*/ &myTask); /*任務句柄*/ Serial.print("taskOne任務的優先級 = "); Serial.println(uxTaskPriorityGet(myTask)); } void loop(void) { delay(1000); Serial.print("loop()任務的優先級 = "); Serial.println(uxTaskPriorityGet(NULL)); }
- FreeRTOS 隊列:
/* @功能: 隊列測試 @時間:2020/3/5 @作者:劉澤文 @QQ:2822604962 */ #include <Arduino.h> QueueHandle_t queue; void setup() { Serial.begin(115200); queue = xQueueCreate( 10, sizeof( int ) );//創建隊列 if(queue == NULL){ Serial.println("Error creating the queue"); } } void loop() { if(queue == NULL)return; for(int i = 0; i<10; i++){ xQueueSend(queue, &i, portMAX_DELAY);//向隊列尾部插入數值 } int element; Serial.println("xQueueReceive 函數讀取結果:"); for(int i = 0; i<10; i++){ xQueueReceive(queue, &element, portMAX_DELAY);//讀取隊列值,並從隊列中移除 Serial.print(element); Serial.print("|"); } Serial.println(""); delay(1000); }
- FreeRTOS 隊列性能測試:
/* @功能: 隊列性能測試 @時間:2020/3/5 @作者:劉澤文 @QQ:2822604962 */ #include <Arduino.h> QueueHandle_t queue;//新建隊列 int queueSize = 10000;//隊列大小 //被賦值系統時間的一些變量 unsigned long startProducing, endProducing, startConsuming, endConsuming, producingTime, consumingTime; void producerTask( void * parameter ) { startProducing = millis();//開始時間 for( int i = 0;i<queueSize;i++ ){ xQueueSend(queue, &i, portMAX_DELAY);//寫入隊列值 } endProducing = millis();//結束時間 vTaskDelete( NULL ); } void consumerTask( void * parameter) { startConsuming = millis();//開始時間 int element; for( int i = 0; i<queueSize; i++ ){ xQueueReceive(queue, &element, portMAX_DELAY);//讀取隊列值 } endConsuming = millis();//結束時間 vTaskDelete( NULL ); } void setup() { Serial.begin(115200); queue = xQueueCreate( queueSize, sizeof( int ) );//設置隊列大小 if(queue == NULL){ Serial.println("Error creating the queue"); } xTaskCreate( producerTask, /* Task function. */ "Producer", /* String with name of task. */ 10000, /* Stack size in words. */ NULL, /* Parameter passed as input of the task */ 10, /* Priority of the task. */ NULL); /* Task handle. */ xTaskCreate( consumerTask, /* Task function. */ "Consumer", /* String with name of task. */ 10000, /* Stack size in words. */ NULL, /* Parameter passed as input of the task */ 10, /* Priority of the task. */ NULL); /* Task handle. */ producingTime = endProducing - startProducing; Serial.print("Producing time: "); Serial.println(producingTime);//寫入耗費時間(ms) consumingTime = endConsuming - startConsuming; Serial.print("Consuming time: "); Serial.println(consumingTime);//讀取耗費時間(ms) } void loop() { delay(1000); }
- 使用 FreeRTOS 隊列實現任務之間的通信:(隊列的元素可以改爲結構體試試哦~)
/* @功能: 兩個不同的任務之間進行通信 @時間:2020/3/11 @作者:劉澤文 @QQ:2822604962 */ #include <WiFi.h> QueueHandle_t queue;//新建隊列 int queueSize = 40;//隊列大小 void producerTask( void * parameter ) { while(true){ for( int i = 0;i<queueSize;i++ ){ xQueueSend(queue, &i, portMAX_DELAY);//寫入隊列值 } delay(1000); } vTaskDelete(NULL); } void consumerTask( void * parameter) { int element; while(true){ for( int i = 0; i<queueSize; i++ ){ xQueueReceive(queue, &element, portMAX_DELAY);//讀取隊列值 Serial.print(element);//打印出來 Serial.print("|"); } Serial.println(""); delay(1000); } vTaskDelete( NULL ); } void setup() { Serial.begin(9600); queue = xQueueCreate( queueSize, sizeof( int ) );//設置隊列大小 if(queue == NULL){ Serial.println("Error creating the queue"); } xTaskCreate( producerTask, /* Task function. */ "Producer", /* String with name of task. */ 10000, /* Stack size in words. */ NULL, /* Parameter passed as input of the task */ 1, /* Priority of the task. */ NULL); /* Task handle. */ xTaskCreate( consumerTask, /* Task function. */ "Consumer", /* String with name of task. */ 10000, /* Stack size in words. */ NULL, /* Parameter passed as input of the task */ 1, /* Priority of the task. */ NULL); /* Task handle. */ } void loop() { delay(1000); }
- 在 FreeRTOS 隊列前/後插入數據:
/* @功能: 在隊列的前後插入數據 @時間:2020/3/11 @作者:劉澤文 @QQ:2822604962 */ #include <WiFi.h> //新建隊列,測試兩個插入函數 QueueHandle_t queueBack; QueueHandle_t queueFront; void setup() { Serial.begin(9600); queueBack = xQueueCreate( 10, sizeof( int ) );//設置隊列大小 queueFront = xQueueCreate( 10, sizeof( int ) );//設置隊列大小 if(queueBack == NULL || queueFront ==NULL){ Serial.println("Error creating one of the queues"); } } void loop() { if(queueBack == NULL || queueFront == NULL )return; for(int i = 0; i<10; i++){ xQueueSendToBack(queueBack, &i, 0);//從後面插入值 xQueueSendToFront(queueFront, &i, 0);//從前面插入值 } int element; Serial.println("queueBack隊列:"); //讀取queueBack隊列的值 for(int i = 0; i<10; i++){ xQueueReceive(queueBack, &element, 0); Serial.print(element); Serial.print("|"); } Serial.println(); Serial.println("queueFront隊列:"); //讀取queueFront隊列的值 for(int i = 0; i<10; i++){ xQueueReceive(queueFront, &element, 0); Serial.print(element); Serial.print("|"); } Serial.println(); Serial.println("-------完成-------"); delay(1000); }