简单的本节略过,详细的可以看视频:单片机ESP32上的FREERTOS这个作者讲的挺好的,通俗易懂
任务的状态
FreeRTOS中的任务有运行态、就绪态、阻塞态、挂起态四种状态,在任何时候都只处于其中一种状态。任务状态之间的转换如下图所示:
任务的优先级
每一个任务都会有一个任务优先级,其范围为 0 (configMAX_PRIORITIES - 1),0优先级最低,(configMAX_PRIORITIES - 1)优先级最大,通常空闲任务的优先级最低,为0。
创建任务
使用动态的方法创建一个任务:
void setup() {
xTaskCreate(task1,
"task1",
4096,
NULL,
5,
NULL);
}
void task1(void *pvParameters)
{
while(1)
{
vTaskDelay(1);
}
vTaskDelete(NULL);
}
更多的任务创建和FreeRTOS函数说明看这里: ESP32之FreeRTOS–任务的创建和运行
传递给任务函数的参数
byte LED1_PIN = 23;
byte LED2_PIN = 21;
void task1(void *pt) {
byte * pbLEDPIN;
pbLEDPIN = (byte *)pt;
byte LEDPIN;
LEDPIN = *pbLEDPIN;
pinMode(LEDPIN, OUTPUT);
while (1) {
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
vTaskDelay(1000);
}
}
void task2(void *pt) {
byte LEDPIN = *(byte *)pt;
pinMode(LEDPIN, OUTPUT);
while (1) {
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
vTaskDelay(3000);
}
}
void setup() {
Serial.begin(9600);
byte * pbLED1PIN;
pbLED1PIN = &LED1_PIN;
void * pvLED1PIN;
pvLED1PIN = (void *)pbLED1PIN;
if (xTaskCreate(task1,
"Blink 23",
1024,
pvLED1PIN,
1,
NULL) == pdPASS)
Serial.println("Task1 Created.");
if (xTaskCreate(task2,
"Blink 21",
1024,
(void *)&LED2_PIN,
1,
NULL) == pdPASS)
Serial.println("Task2 Created.");
}
void loop() {
}
程序出处 该程序task1创建一个字节指针 byte * pbLED1PIN,通过 pbLED1PIN =&LED1_PIN来指向LED1_PIN的地址,再创建pvLED1PIN这个void类型的指针来存储pbLED1PIN转换成void的值。
函数传递的是void类型的指针,所以参数需要转换成void类型的指针才能在任务函数传递。
task2则简单粗暴一点,(void *)&LED2_PIN,直接取地址,然后用空指针去指向,也一样完成传递。
结构体多参数传递
程序出处
typedef struct {
byte pin;
int delayTime;
} LEDFLASH;
LEDFLASH led1, led2;
if (xTaskCreate(ledFlash,
"FLASH LED",
1024,
(void *)&led1,
1,
NULL) == pdPASS)
Serial.println("led1 flash task Created.");
void ledFlash(void *pt) {
LEDFLASH * ptLedFlash = (LEDFLASH *)pt;
byte pin = ptLedFlash->pin;
int delayTime = ptLedFlash->delayTime;
pinMode(pin,OUTPUT);
while (1) {
digitalWrite(pin, !digitalRead(pin));
vTaskDelay(delayTime);
}
}
实例化结构体led1,然后传入结构体的指针地址,再创建结构体指针(类型为LEDFLASH )使其等于pt(类型转换为LEDFLASH *),这样子就可以获取到结构体led1的地址了。
互斥量Mutex
程序 多个任务一起用同一个变量就会出问题了,所以就要引入互斥量的概念,简单来说,就是互斥锁锁住一个变量之后,只有等这个锁解了,其他任务才能使用这个变量。
SemaphoreHandle_t xMutexInventory = NULL;
void retailTask(void *pvParam) {
while (1) {
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {
for (int i; i < random(10, 100); i++)
vTaskDelay(pdMS_TO_TICKS(i));
xSemaphoreGive(xMutexInventory);
} else {
}
};
vTaskDelay(100);
}
}
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS)
如果没有获取到互斥量,则执行以下内容,超时时间是timeOut
xSemaphoreGive(xMutexInventory);
ESP32的双核心
ESP32是具有两个核心的芯片(可以对应一下你手中的型号作对比),是一个40nm的芯片。
核心0主要负责WiFi和蓝牙,如果不是操作这两个,十分不建议将代码放在核心0中运行
void taskA(void *ptParam) {
while (1) {
Serial.println(xPortGetCoreID());
}
}
void setup() {
Serial.begin(115200);
xTaskCreatePinnedToCore(taskA, "Task A", 1024 * 4, NULL, 1, NULL,1);
}
void loop() {
Serial.println(xPortGetCoreID());
}
主要是这一句,修改最后一个参数0或1即可修改核心数,使用Arduino环境对esp进行开发时建议使用Application CPU(id=1),否则可能有bug,不建议使用xTaskCreate
xTaskCreatePinnedToCore(taskA, "Task A", 1024 * 4, NULL, 1, NULL,1);
固定频率运行任务
以固定频率运行任务,不多也不少
TickType_t xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastWakeTime, xFrequency);
vTaskDelayUntil(上一次唤醒的时间的结构体地址, 下一次运行的时间);
软件定时器
FreeRTOS中只要内存允许,想要多少个Timer 就有多少个Timer
lockHandle = xTimerCreate("Lock Car",
2000,
pdFALSE,
(void *)0,
lockCarCallback);
|