ESP32-C系列为单核,ESP32的core0主要运行WI-FI和蓝牙
API:
xPortGetCoreID() 获取当前任务运行的核心
xTaskCreate() 有系统选择运行核心,优先选择0
xTaskCreatePinnedToCore() 指派任何给指定核心
Arduino的setup和loop默认运行在core1
#include void taskA(void *ptParam)
{while (1){Serial.println(xPortGetCoreID()); // 获取当前任务运行的核心}
}void setup()
{// put your setup code here, to run once:Serial.begin(115200);xTaskCreatePinnedToCore(taskA, "Task A", 1024 * 4, NULL, 1, NULL, 0); // 最后一个参数为核心0
}void loop()
{
}
创建任务时使用空指针的原因,加快速度,可以接收任何类型的数据
注意事项,把U8G2相关的配置(初始化)写进任务中,不要写道setup中
#include
#include void oledTask(void *pvParam)
{U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);u8g2.begin();for (;;){u8g2.clearBuffer(); // clear the internal memoryu8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable fontu8g2.drawStr(15, 10, "LONELY BINARY"); // write something to the internal memoryu8g2.sendBuffer(); // transfer internal memory to the displayvTaskDelay(1000);}
}void setup()
{ // loopBack , Priority 1, Core 1xTaskCreatePinnedToCore(oledTask, "OLED Task", 1024 * 6, NULL, 1, NULL, 1);vTaskDelete(NULL); // 删除setup任务,节省内存空间
}void loop()
{
}
实时操作系统要求必须在指定的时间内处理数据(比如每3秒显示一次数据,不能快,也不能慢)
vTaskDelayUntil函数比vTaskDelay函数定时精准,用处:周期性获取传感器数据(蓝牙发送数据)
vTaskDelayUntil函数比vTaskDelay函数多了一个记录任务本次被唤醒的时刻的变量,因此如果想要实现控制任务能够周期性运行的话,vTaskDelayUntil函数是一种比较简单的方法。
注意:LastWakeTime中保存的是上次唤醒时间,进入vTaskDelayUntil后会判断,如果上次唤醒时间大于当前时间,说明节拍计数器溢出了,就不在延时直接执行了。
API:
vTaskDelayUntil(&xLastWakeTime, xFrequency)
最后一次的唤醒时间是指针类型。
本函数会自动更新xLastWakeTime为最后一次唤醒的时间
所以无需手动在while循环内对其手动赋值
xTaskGetTickCount()
TickCount和 Arduino Millis一样
uint32_t类型 49天后overflow(溢出)
void showStockTask(void *ptParam)
{static float stockPrice = 99.57; //股票价格//最后一次唤醒的tick count,第一次使用需要赋值//以后此变量会由vTaskDelayUntil自动更新TickType_t xLastWakeTime = xTaskGetTickCount(); // 相当于millisconst TickType_t xFrequency = 3000; // 间隔 3000 ticks = 3 secondsfor (;;){//恰恰算算,经过思考,既然我们叫做LastWakeTime,那么 vTaskDelayUntil 应该放在循环的第一句话//如果放在循环的最后一句话,应该改为xLastSleepTime 才更加合适vTaskDelayUntil(&xLastWakeTime, xFrequency);//验证当前唤醒的时刻tick countSerial.println(xTaskGetTickCount());//验证xLastWake Time是否被vTaskDelayUntil更新// Serial.println(xLastWakeTime);// ------- 很复杂的交易股票计算,时间不定 ---------stockPrice = stockPrice * (1 + random(1, 20) / 100.0);vTaskDelay(random(500, 2000));Serial.print("Stock Price : $");Serial.println(stockPrice);//使用vTaskDelay试试看会如何// vTaskDelay(xFrequency); // 测试结果显示,差距很大,时间不精确}
}void setup()
{Serial.begin(115200);xTaskCreate(showStockTask, "Show Stock Price", 1024 * 6, NULL, 1, NULL);
}void loop()
{
}
变量在stack中
建立任务时会占用额外的内存空间,大概368 bytes
如果有很多数据需要串口输出,单独创建一个task(例如蓝牙发送所有数据,其他任务通过多任务传参的方式把需要输出的数据发送给,打印输出task)
可以设置0~24之间的任意数字,0最小,24优先级最大
防止死机,每一个CPU都有一只看门狗,ESP32有两个核心,所以有两只狗,看门狗是针对任务的,不是针对CPU,默认情况下在核心0有一只看门狗
FIFO:first in first out
队列:queue,先进先出
队列是一种数据结构,可以包含一组固定大小的数据,在创建队列的同时,队列的长度和所包含数据类型的大小就确认下来了,一个队列可以有多个写入数据的任务和多个读取数据的任务。当一个任务试图从队列读取 数据的时候,它可以设置一个堵塞时间(block time)。这是当队列数据为空时,任务会进入阻塞状态的时间。当有数据在队列或者到达阻塞时间的时候,任务都会进入就绪状态。如果有多个任务同时在阻塞状态等待队列数据,优先级高的任务会在数据到达时进入就绪状态;在优先级相同的时候,等待时间长的任务会进入就绪状态。同理可以推及多个任务写入数据时候的运行状态。
使用步骤:
API:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
BaseType_t xQueueReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait
);
案例:通过队列+结构体的方式,将DHT22的温度和湿度数据在不同的任务间传输
创建队列:长度(可以放几个数据)、大小(每个数据的大小)
编程技巧:可以创建一个结构体类型,然后创建多个对象进行调用。
可以采用这种方法给上位机发送数据(把LCD显示部分的程序改为自己需要的数据)
/*程序: 消息队列公众号:孤独的二进制说明:本实例使用结构体巧妙的通过当个队列传输多个设备多种数据类型在接收方,我们通过deviceID来判断数据来源和value的意义API:QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,UBaseType_t uxItemSize );BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait);BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);*/
#include "DHTesp.h"
#include
LiquidCrystal_I2C lcd(0x27, 20, 4);
/*设备ID,共有2个设备*/
#define DHT22_ID 0
#define LDR_ID 1typedef struct
{byte deviceID;float value1;float value2;
} SENSOR;QueueHandle_t queueSensor = xQueueCreate(8, sizeof(SENSOR)); // 长度8,采用sizeof自动计算需要的空间void dht22(void *ptParam)
{const byte dhtPin = 32;DHTesp dhtSensor;dhtSensor.setup(dhtPin, DHTesp::DHT22);SENSOR dht22Sensor; // 创建一个结构体对象dht22Sensor.deviceID = DHT22_ID;while (1){TempAndHumidity data = dhtSensor.getTempAndHumidity();// Serial.println("Temp: " + String(data.temperature, 2) + "°C");// Serial.println("Humidity: " + String(data.humidity, 1) + "%");dht22Sensor.value1 = data.temperature;dht22Sensor.value2 = data.humidity;/*往队列里发送数据,如果队列数据是满的,等待2s,如果2s之后,队列还是满的,就放弃写操作*/// TickType_t timeOut = portMAX_DELAY; 不要用这个,时间为49天TickType_t timeOut = 2000;if (xQueueSend(queueSensor, &dht22Sensor, timeOut) != pdPASS){Serial.println("DHT22: Queue is full.");}vTaskDelay(1000); // 这个时间如果比较短,则队列很快就满了}
}void ldr(void *ptParam)
{const float GAMMA = 0.7;const float RL10 = 50;const byte ldrPIN = 27;pinMode(ldrPIN, INPUT);SENSOR ldrSensor;ldrSensor.deviceID = LDR_ID;while (1){int analogValue = analogRead(ldrPIN);float voltage = analogValue / 4095. * 5;float resistance = 2000 * voltage / (1 - voltage / 5);float lux = pow(RL10 * 1e3 * pow(10, GAMMA) / resistance, (1 / GAMMA));// Serial.print("LDR Light Sensor lux : ");// Serial.println(lux);ldrSensor.value1 = lux;// ldrSensor.value2 = 0.0; 这条语句不要也行// TickType_t timeOut = portMAX_DELAY;TickType_t timeOut = 2000;if (xQueueSend(queueSensor, &ldrSensor, timeOut) != pdPASS){Serial.println("LDR: Queue is full.");}vTaskDelay(1000);}
}void lcdTask(void *ptParam)
{ // LCD任务主体lcd.init();lcd.backlight();lcd.setCursor(0, 0);lcd.print(" LONELY BINARY ");SENSOR data; // while (1){// TickType_t timeOut = portMAX_DELAY;TickType_t timeOut = 2000;if (xQueueReceive(queueSensor, &data, timeOut) == pdPASS){switch (data.deviceID){case DHT22_ID:lcd.setCursor(0, 1);lcd.print("Temp: " + String(data.value1, 2) + "c");lcd.setCursor(0, 2);lcd.print("Humidity: " + String(data.value2, 1) + "%");break;case LDR_ID:lcd.setCursor(0, 3);if (data.value1 > 50){lcd.print("Bright ");}else{lcd.print("Dark ");}// lcd.setCursor(0, 3);lcd.print(String(data.value1, 2) + " lux");break;default:Serial.println("LCD: Unkown Device");break;}}else{Serial.println("LCD: Message Queue is Empty");};vTaskDelay(2000);}
}void setup()
{Serial.begin(115200);xTaskCreate(dht22, "DHT22", 1024 * 4, NULL, 1, NULL);xTaskCreate(ldr, "LDR LIGHT", 1024 * 4, NULL, 1, NULL);xTaskCreate(lcdTask, "lcd", 1024 * 8, NULL, 1, NULL);
}void loop() {}