- 在刚开始学习 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); }