ESP32 RTOS基础开发笔记02
三、多任务
1.任务是竞争系统资源的最小运行单元。
2.一个系统能运行多少个任务,取决于系统的可以SRAM
3.FreeRTOS任务是采用抢占式调度机制。
1)高优先级任务可以低优先级任务。
2)低优先级任务必须在高优先级任务阻塞或者结束后才 能得到调度。
FreeRTOS任务是也支持时间片轮询调度机制。1)系统中无更高优先级任务存在
2)相同优先级的任务采用时间片轮询调度
4.任务的状态
多任务实例程序
/* Blink Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "sdkconfig.h"
/* Can use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
#define BLINK_GPIO GPIO_NUM_2
#define BLINK_GPIO22 GPIO_NUM_22
#define Key_GPIO GPIO_NUM_21
static TaskHandle_t APPTaskCreat_Handle = NULL;
static TaskHandle_t Led_Task_Handle = NULL;
static TaskHandle_t Led2_Task_Handle = NULL;
static TaskHandle_t KeyScan_Task_Handle=NULL;
void Led_Task_Function(void *arg)
{
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
while (1)
{
/* Blink off (output low) */
printf("Turning off the LED\n");
gpio_set_level(BLINK_GPIO, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
/* Blink on (output high) */
printf("Turning on the LED\n");
gpio_set_level(BLINK_GPIO, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void Led2_Task_Function(void *arg)
{
gpio_reset_pin(BLINK_GPIO22);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO22, GPIO_MODE_OUTPUT);
while (1)
{
/* Blink off (output low) */
printf("Turning off the LED22\n");
gpio_set_level(BLINK_GPIO22, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
/* Blink on (output high) */
printf("Turning on the LED22\n");
gpio_set_level(BLINK_GPIO22, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
//按键扫描
void KeyScan_Task_Function(void *arg)
{
gpio_reset_pin(Key_GPIO);
gpio_set_direction(Key_GPIO, GPIO_MODE_INPUT);
gpio_reset_pin(GPIO_NUM_23);
gpio_set_direction(GPIO_NUM_23, GPIO_MODE_OUTPUT);
while (1)
{
if(gpio_get_level(Key_GPIO)==0)
{
vTaskSuspend(Led2_Task_Handle);//挂起
gpio_set_level(BLINK_GPIO22, 0);
gpio_set_level(GPIO_NUM_23,1);
}
else
{
vTaskResume(Led2_Task_Handle);
gpio_set_level(GPIO_NUM_23,0);
}
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void APPTaskCreat_Function(void *arg)
{
BaseType_t xReturn = pdPASS;
//LED1任务程序
xReturn = xTaskCreate(Led_Task_Function, //任务函数
"LedTask", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&Led_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("LedTask任务创建成功\r\n");
}
//LED2任务程序
xReturn = xTaskCreate(Led2_Task_Function, //任务函数
"Led2Task", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&Led2_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("Led2Task任务创建成功\r\n");
}
//按键扫描程序
xReturn = xTaskCreate(KeyScan_Task_Function, //任务函数
"KeyScanTask", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&KeyScan_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("KeyScanTask任务创建成功\r\n");
}
printf("删除APPTask任务成功\r\n");
vTaskDelete(APPTaskCreat_Handle); //删除任务
}
// void CPU_Task(void* arg)
// {
// uint8_t CPU_RunInfo[400]; //保存任务运行时间信息
// while (1)
// {
// memset(CPU_RunInfo,0,400); //信息缓冲区清零
// vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息
// printf("---------------------------------------------\r\n");
// printf("task_name task_status priority stack task_id\r\n");
// printf("%s", CPU_RunInfo);
// printf("---------------------------------------------\r\n");
// memset(CPU_RunInfo,0,400); //信息缓冲区清零
// vTaskGetRunTimeStats((char *)&CPU_RunInfo);
// printf("task_name run_cnt usage_rate\r\n");
// printf("%s", CPU_RunInfo);
// printf("---------------------------------------------\r\n\n");
// vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500个tick */
// }
// }
void app_main(void)
{
//xTaskCreatePinnedToCore(Led_Task_Function,"LedTask",2048,NULL,1,NULL,tskNO_AFFINITY);
// BaseType_t xReturn = pdPASS;
// xTaskCreatePinnedToCore(CPU_Task,"CPU_Task",6114,NULL,1,NULL,tskNO_AFFINITY);
xTaskCreatePinnedToCore(APPTaskCreat_Function,//任务函数
"APPTaskCreat",//任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
1, //任务优先级
&APPTaskCreat_Handle, //任务句柄
tskNO_AFFINITY);//指定运行任务的CPU,使用这个宏表示不会固定到任何核上
// xReturn=xTaskCreate(APPTaskCreat_Function,//任务函数
// "APPTaskCreat",//任务名称
// 2048,//任务栈大小
// NULL,//任务入口函数参数
// 1,//任务优先级
// &APPTaskCreat_Handle);//指定运行任务的CPU,使用这个宏表示不会固定到任何核上
// configASSERT( APPTaskCreat_Handle );
// //启动任务调度器
// if (pdPASS==xReturn)
// {
// /* code */
// // vTaskStartScheduler();
// printf("启动任务,开启调度器成功\r\n");
// }
// else
// {
// return (-1);
// }
// while(1);
}
四、消息队列
回想一下,在裸机的编程中,我们是怎样使用全局数组的呢?
队列又称消息队列,是一种常用于任务间通信的数据结构。
典型流程
1.创建消息队列
xQueueCreate(Queue_Length,//消息队列的长度
Queue_Size);//消息的大小
2.写队列操作
xQueueSend(Creat_Queue_Handle,//消息队列句柄
&send_data1,//发送的消息内容
0); //等待时间为0
3.读队列操作
xQueueReceive(Creat_Queue_Handle,//消息队列的句柄
&rec_queue, //接收到发送的信息内容
portMAX_DELAY); //等待时间 一直等
备注:1)在指定的阻塞时间内(也就是设置的时间),当且仅当消息队列中有消息时,任务才能读取到消息,否则 该任务将一直保持阻塞状态
2)超过了指定的阻塞时间,即使队列中没有数据,任务也会由阻塞状态变为就绪状态
3)从队列中读取到消息(复制形式进行的)后并把消息从队列中删除
//不会把消息从队列中删除
xQueuePeeK(Creat_Queue_Handle,//消息队列的句柄
&rec_queue, //接收到发送的信息内容
portMAX_DELAY); //等待时间 一直等
4.删除队列
vQueueDelete(Creat_Queue_Handle); //删除消息队列
具体实例程序
/* Blink Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h" //消息队列
#include "freertos/event_groups.h"
#include "driver/gpio.h"
#include "driver/ledc.h" //LED PWM控制
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"
#include "sdkconfig.h"
/* Can use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
//GPIO定义
#define BLINK_GPIO GPIO_NUM_2
#define BLINK_GPIO22 GPIO_NUM_22
#define Key_GPIO GPIO_NUM_21
#define Key2_GPIO GPIO_NUM_19
//消息队列定义
#define Queue_Length 4 //队列的长度,最大可包含多少个消息
#define Queue_Size 4 //队列中每个消息的大小(字节)
QueueHandle_t Creat_Queue_Handle = NULL; //队列句柄
static TaskHandle_t Receive_Task_Handle = NULL; //任务句柄
static TaskHandle_t Send_Task_Handle = NULL; //任务句柄
//GPIO 任务的创建
static const char *LED1TAG = "LED1"; //Log使用,标志用
static const char *LED2TAG = "LED2"; //Log使用,标志用
static TaskHandle_t APPTaskCreat_Handle = NULL; //任务句柄
static TaskHandle_t Led_Task_Handle = NULL; //任务句柄
static TaskHandle_t Led2_Task_Handle = NULL; //任务句柄
static TaskHandle_t KeyScan_Task_Handle = NULL; //任务句柄
//GPIO初始化
void GPIO_init()
{
gpio_reset_pin(BLINK_GPIO);
gpio_reset_pin(BLINK_GPIO22);
gpio_reset_pin(GPIO_NUM_23);
gpio_reset_pin(Key_GPIO);
gpio_reset_pin(Key2_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
gpio_set_direction(BLINK_GPIO22, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_23, GPIO_MODE_OUTPUT);
gpio_set_direction(Key_GPIO, GPIO_MODE_INPUT);
gpio_set_direction(Key2_GPIO, GPIO_MODE_INPUT);
}
void Led_Task_Function(void *arg)
{
while (1)
{
/* Blink off (output low) */
ESP_LOGI(LED1TAG, "Turning off the LED\n");
gpio_set_level(BLINK_GPIO, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
/* Blink on (output high) */
ESP_LOGI(LED1TAG, "Turning on the LED\n");
gpio_set_level(BLINK_GPIO, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void Led2_Task_Function(void *arg)
{
while (1)
{
/* Blink off (output low) */
ESP_LOGI(LED2TAG, "Turning off the LED22\n");
gpio_set_level(BLINK_GPIO22, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
/* Blink on (output high) */
ESP_LOGI(LED2TAG, "Turning on the LED22\n");
gpio_set_level(BLINK_GPIO22, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
//按键扫描
void KeyScan_Task_Function(void *arg)
{
while (1)
{
if (gpio_get_level(Key_GPIO) == 0)
{
gpio_set_level(BLINK_GPIO22, 0);
gpio_set_level(GPIO_NUM_23, 1);
printf("Led2任务被挂起\r\n");
vTaskSuspend(Led2_Task_Handle); //挂起
}
else
{
gpio_set_level(GPIO_NUM_23, 0);
printf("Led2任务被释放\r\n");
vTaskResume(Led2_Task_Handle);
}
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
//消息队列初始化
void Creat_Queue_init()
{
Creat_Queue_Handle = xQueueCreate(Queue_Length,//消息队列的长度
Queue_Size);//消息的大小
if (NULL != Creat_Queue_Handle)
{
printf("创建消息队列成功\r\n");
}
else
{
printf("删除消息队列\r\n");
vQueueDelete(Creat_Queue_Handle); //删除消息队列
}
}
//消息队列接收任务的创建
void Receive_Task_Function(void *arg)
{
BaseType_t xReturn = pdTRUE; //定义一个创建信息返回值
uint32_t rec_queue; //定义一个接收消息的变量
while (1)
{
xReturn = xQueueReceive(Creat_Queue_Handle,//消息队列的句柄
&rec_queue, //接收到发送的信息内容
portMAX_DELAY); //等待时间 一直等
if(pdTRUE==xReturn)
{
printf("本次接收的数据是:%d\r\n",rec_queue);
}
else
{
printf("数据接收错误,错误代码:0x%1x\r\n",xReturn);
}
}
}
//消息队列接收任务的创建
void Send_Task_Function(void *arg)
{
BaseType_t xReturn = pdPASS; //定义一个创建信息返回值
uint32_t send_data1=1;
uint32_t send_data2=2;
while (1)
{
if (gpio_get_level(Key_GPIO) == 0)
{
printf("发送消息 senddata1\r\n");
xReturn=xQueueSend(Creat_Queue_Handle,//消息队列句柄
&send_data1,//发送的消息内容
0); //等待时间为0
if(pdPASS==xReturn)
{
printf("消息 senddata1 发送成功!\r\n");
}
}
if (gpio_get_level(Key2_GPIO) == 0)
{
printf("发送消息 senddata2\r\n");
xReturn=xQueueSend(Creat_Queue_Handle,//消息队列句柄
&send_data2,//发送的消息内容
0); //等待时间为0
if(pdPASS==xReturn)
{
printf("消息 senddata2 发送成功!\r\n");
}
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
//总任务的创建
void APPTaskCreat_Function(void *arg)
{
BaseType_t xReturn = pdPASS;
//LED1任务程序
xReturn = xTaskCreate(Led_Task_Function, //任务函数
"LedTask", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&Led_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("LedTask任务创建成功\r\n");
}
//LED2任务程序
xReturn = xTaskCreate(Led2_Task_Function, //任务函数
"Led2Task", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&Led2_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("Led2Task任务创建成功\r\n");
}
//按键扫描程序
xReturn = xTaskCreate(KeyScan_Task_Function, //任务函数
"KeyScanTask", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&KeyScan_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("KeyScanTask任务创建成功\r\n");
}
//接收消息队列任务程序
xReturn = xTaskCreate(Receive_Task_Function, //任务函数
"Receive_Task", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&Receive_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("Receive_Task任务创建成功\r\n");
}
//发送消息队列任务程序
xReturn = xTaskCreate(Send_Task_Function, //任务函数
"Send_Task", //任务名称
2048, //任务栈大小
NULL, //任务入口函数参数
2, //任务优先级
&Send_Task_Handle); //指定运行任务的CPU,使用这个宏表示不会固定到任何核上
if (pdPASS == xReturn)
{
printf("Send_Task任务创建成功\r\n");
}
printf("删除APPTask任务成功\r\n");
vTaskDelete(APPTaskCreat_Handle); //删除任务
}
// void CPU_Task(void* arg)
// {
// uint8_t CPU_RunInfo[400]; //保存任务运行时间信息
// while (1)
// {
// memset(CPU_RunInfo,0,400); //信息缓冲区清零
// vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息
// printf("---------------------------------------------\r\n");
// printf("task_name task_status priority stack task_id\r\n");
// printf("%s", CPU_RunInfo);
// printf("---------------------------------------------\r\n");
// memset(CPU_RunInfo,0,400); //信息缓冲区清零
// vTaskGetRunTimeStats((char *)&CPU_RunInfo);
// printf("task_name run_cnt usage_rate\r\n");
// printf("%s", CPU_RunInfo);
// printf("---------------------------------------------\r\n\n");
// vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500个tick */
// }
// }
void app_main(void)
{
//xTaskCreatePinnedToCore(Led_Task_Function,"LedTask",2048,NULL,1,NULL,tskNO_AFFINITY);
// BaseType_t xReturn = pdPASS;
// xTaskCreatePinnedToCore(CPU_Task,"CPU_Task",6114,NULL,1,NULL,tskNO_AFFINITY);
GPIO_init();
Creat_Queue_init();
xTaskCreatePinnedToCore(APPTaskCreat_Function, "APPTaskCreat", 2048, NULL, 1, &APPTaskCreat_Handle, tskNO_AFFINITY);
// xReturn=xTaskCreate(APPTaskCreat_Function,//任务函数
// "APPTaskCreat",//任务名称
// 2048,//任务栈大小
// NULL,//任务入口函数参数
// 1,//任务优先级
// &APPTaskCreat_Handle);//指定运行任务的CPU,使用这个宏表示不会固定到任何核上
// configASSERT( APPTaskCreat_Handle );
// //启动任务调度器
// if (pdPASS==xReturn)
// {
// /* code */
// // vTaskStartScheduler();
// printf("启动任务,开启调度器成功\r\n");
// }
// else
// {
// return (-1);
// }
// while(1);
} 作者:小梦小图 https://www.bilibili.com/read/cv13527386/ 出处:bilibili