免责声明:我珍惜每个人的时间,欣赏这个社区,并且不期望人们为我编写代码。我正在寻找有关根据我的申请最好做什么的指导。
我正在处理的项目遇到问题。主要问题是如何充分利用 FreeRTOS。
我在 FireBeetle 2 ESP32-E 上使用 PlatformIO。
附件是我认为对于我寻求帮助的代码中最有用的部分。如果有帮助的话,我可以分享所有 .h 和 .cpp 文件。
我想要实现的是:
ReadSensorsTaskcode 在计时器上运行
FanCleaningSEN55代码在 ISR(ESP32 上的触摸针)触发时运行
UpdateRGBLEDTaskcode 在计时器上运行。
ControlDisplayTaskcode 在 ISR(ESP32 上的触摸针)触发时运行。
我没有使用互斥量、信号量或任何类型的队列,因为经过多次尝试,我还没有弄清楚哪一个是正确的方法。二进制信号量? 任务固定在核心上。这个可以吗?使用两个核心有什么好处吗?我计划实现 WiFi 功能:与之相关的任何东西会“阻止”核心 0 吗?
非常感谢任何类型的反馈。
/************************************************************************************
******** FreeRTOS tasks
**********************************************************************************/
#include "global_defs.h"
#include "leds.h"
#include "display.h"
#include "sensirionSCD41.h"
#include "sensirionSEN55.h"
#include "time.h"
#include "gsheet.h"
#include "tasks.h"
TaskHandle_t ReadSensorsTask;
TaskHandle_t FanCleaningSEN55Task;
TaskHandle_t UpdateRGBLEDTask;
TaskHandle_t ControlDisplayTask;
// Settings
static const TickType_t dim_delay = 5000 / portTICK_PERIOD_MS;
// Pins (change this if your Arduino board does not have LED_BUILTIN defined)
static const int led_pin = LED_BUILTIN;
// Globals
static TimerHandle_t one_shot_timer = NULL;
// ISR function
void IRAM_ATTR gotTouchSEN55cleaning(){
// Resume tasks
vTaskResume(FanCleaningSEN55Task);
}
// ISR function
void IRAM_ATTR gotTouchDisplayCarousel(){
// Resume tasks
buttonDisplayCount++;
vTaskResume(ControlDisplayTask);
}
// Triggered by TIMER
void ReadSensorsTaskcode( void * pvParameters ){
/* DEBUG Inspect our own high water mark on entering the task. */
//uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
for (;;) {
// Run rightaway on first boot
if (readingCounter == 0){
const TickType_t xFrequency = (1000 / portTICK_PERIOD_MS);
vTaskDelay(xFrequency );
}
else{
const TickType_t xFrequency = (delayMinutesReadSensors * 60000 / portTICK_PERIOD_MS);
vTaskDelay(xFrequency );
}
//vTaskSuspend(UpdateRGBLEDTask);
Serial.println("** SCD41 and SEN55 Get Readings **");
Serial.print("running on core ");
Serial.println(xPortGetCoreID());
lightFadeInPurpleLED();
displayReading();
getSCD41readings();
getSEN55readings();
if (readingSensorsDataflagOK == true)
{
displayOKcircleThick();
}
else
{
displayNGcircleThick();
}
readingSensorsDataflagOK = false;
lightFadeOutPurpleLED();
readingCounter++;
// Show CO2 value if above dangerous levels
if (co2 > dangerousValueCO2)
{
displayCO2();
}
// Show latest sensors readings
else
{
displayCO2();
delay(3000);
displayTempHumid();
}
Serial.print("Value set in memory for dangerous CO2: ");
Serial.println(dangerousValueCO2);
Serial.print("Value set in memory for high CO2: ");
Serial.println(highValueCO2);
Serial.print("Value set in memory for max CO2 gauge: ");
Serial.println(maxco2);
Serial.print("Value set in memory for max PM2.5 gauge: ");
Serial.println(maxpm25);
Serial.print("Value set in memory for max VOC gauge: ");
Serial.println(maxVOC);
readingCounter++;
/* debug */
//uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
}
}
// Called by ISR
void FanCleaningSEN55code( void * pvParameters ){
for(;;){
Serial.print("FanCleaningSEN55 running on core ");
Serial.println(xPortGetCoreID());
//---------------------------------------------------------------------
//--- USER ACTION ----- SEN55 fan cleaning -----------------
int touchValue1 = touchRead(touchPinSEN55cleaning);
if (sen55initflagOK == true && touchValue1 < threshold);
if (sen55initflagOK == true && touchValue1 < threshold)
{
displaySEN55Cleaning();
sen55FanCleaning();
lightFadeInOutYellowLEDfast();
}
else if(sen55initflagOK == false && touchValue1 < threshold)
{
displaySEN55InitError();
displayNGcircleThick();
delay(2000);
}
// Suspend tasks
vTaskSuspend(FanCleaningSEN55Task);
}
}
// Triggered by TIMER
void UpdateRGBLEDTaskcode( void * pvParameters ){
for(;;){
const TickType_t xFrequency = (delaySecondsCO2LED * 1000 / portTICK_PERIOD_MS);
vTaskDelay(xFrequency );
//Serial.println("** Update RGB LED color based on CO2 **");
//Serial.print("running on core ");
//Serial.println(xPortGetCoreID());
//---------------------------------------------------------------------
//--- ONBOARD LED #2 ----- Update based on CO2 readings -----------------
if (useOnboardLEDUser == true)
{
if (co2 >= dangerousValueCO2)
{
lightFadeInOutRedLED();
}
else if (co2 >= highValueCO2)
{
lightFadeInOutOrangeLED();
}
else
{
lightFadeInOutGreenLED();
}
}
}
}
// Called by ISR
void ControlDisplayTaskcode( void * pvParameters ){
for(;;){
Serial.print("ControlDisplay running on core ");
Serial.println(xPortGetCoreID());
Serial.println(buttonDisplayCount);
int touchValue2 = touchRead(touchPinDisplayCarousel);
if (buttonDisplayCount > 1 && touchValue2 < threshold)
{
buttonDisplayCount = 0;
}
if (buttonDisplayCount == 0 && touchValue2 < threshold)
{
displayTempHumid();
}
else if (buttonDisplayCount == 1 && touchValue2 < threshold)
{
displayCO2();
}
// Suspend tasks
vTaskSuspend(ControlDisplayTask);
}
}
void initializeFreeRTOStasks() {
xTaskCreatePinnedToCore(
ReadSensorsTaskcode, /* Task function. */
"ReadSensorsTask", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
10, /* priority of the task */
&ReadSensorsTask, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
delay(500);
touchAttachInterrupt(touchPinSEN55cleaning, gotTouchSEN55cleaning, threshold);
xTaskCreatePinnedToCore(
FanCleaningSEN55code, /* Task function. */
"FanCleaningSEN55Task", /* name of task. */
5000, /* Stack size of task */
NULL, /* parameter of the task */
3, /* priority of the task */
&FanCleaningSEN55Task, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
vTaskSuspend(FanCleaningSEN55Task);
delay(500);
xTaskCreatePinnedToCore(
UpdateRGBLEDTaskcode, /* Task function. */
"UpdateRGBLEDTask", /* name of task. */
5000, /* Stack size of task */
NULL, /* parameter of the task */
8, /* priority of the task */
&UpdateRGBLEDTask, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
delay(500);
touchAttachInterrupt(touchPinDisplayCarousel, gotTouchDisplayCarousel, threshold);
xTaskCreatePinnedToCore(
ControlDisplayTaskcode, /* Task function. */
"ControlDisplayTask", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
3, /* priority of the task */
&ControlDisplayTask, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
vTaskSuspend(ControlDisplayTask);
delay(500);
}
我尝试为每个任务使用优先级,但这不起作用,并且 MCU 有时仍然会恐慌。一个简单的yield()对我的应用程序有用吗?
如果我们暂时忽略要求 4.2,那么您的愿望基本上相当于简单的协作多线程(即任务不能被抢占,除非它自愿让步)。 FreeRTOS 具有抢占式多线程功能,这意味着调度程序将在每次更新后重新评估正在运行的任务,以查看某个具有相同或更高优先级的线程是否已准备好运行。我会推荐优秀的 FreeRTOS 书,第 4 章作为参考。
不管怎样,你可以轻松模拟协作多线程。如果我想到解决您的问题的最简单的解决方案,那就是处理系统中出现的事件(中断或计时器滴答)的单个线程。第 1-4 步中的每一步都只是创建一个事件并将其推送到处理线程的队列中。
概念性解决方案,未经编译或测试:
QueueHandle_t ev_queue; // Queue for sending events to worker thread
TimerHandle_t timer; // Timer for generating periodic events
// Event definitions
typedef enum {
EV_READ_SENSORS,
EV_FAN_CLEANING,
EV_UPDATE_RGB_LED,
EV_CONTROL_DISPLAY,
} event_t;
// ISR generates some event
void IRAM_ATTR gotTouchSEN55cleaning() {
event_t ev = EV_FAN_CLEANING;
xQueueSendFromISR(ev_queue, &ev, NULL);
}
// ISR generates some other event
void IRAM_ATTR gotTouchDisplayCarousel() {
event_t ev = EV_CONTROL_DISPLAY;
xQueueSendFromISR(ev_queue, &ev, NULL);
}
// Timer generates the periodic events
void on_timer(TimerHandle_t timer_h) {
event_t ev;
if (TIME_TO_READ_SENSORS()) {
ev = EV_READ_SENSORS;
xQueueSend(ev_queue, &ev, 0);
}
if (TIME_TO_UPDATE_RGB_LED()) {
ev = EV_UPDATE_RGB_LED;
xQueueSend(ev_queue, &ev, 0);
}
}
// Single task handles all events in order of appearance
void worker(void* arg) {
while (true) {
event_t ev;
if (xQueueReceive(ev_queue, &ev, portMAX_DELAY)) {
switch (ev) {
case EV_READ_SENSORS:
ReadSensors(); // Your code in here, no FreeRTOS task management needed
break;
case EV_FAN_CLEANING:
FanCleaningSEN55();
break;
case EV_UPDATE_RGB_LED:
UpdateRGBLED();
break;
case EV_CONTROL_DISPLAY:
ControlDisplay();
break;
}
}
}
}
void app_main() {
ev_queue = xQueueCreate(10, sizeof(event_t));
assert(ev_queue);
BaseType_t ret = xTaskCreatePinnedToCore(worker, "Worker", 10000, NULL, 10, NULL, 1);
assert(ret == pdPASS);
timer = xTimerCreate("MyTimer", pdMS_TO_TICKS(100), pdTRUE, NULL, on_timer);
assert(timer);
}
要求 4.2“UpdateRGBLED 除外”并不完全适合这个简单模型。我会把这个问题留给你去解决,因为我在 10 秒内没有想出一个明确的解决方案:)
如果同一核心上没有其他优先级为 10 或更高的任务运行,它应该或多或少是实时工作的。如果您计划使用 WiFi 或 BLE,那么它们往往会在核心 0 上创建高优先级任务,在这种情况下,在核心 1 上运行任务并不是一个坏主意。否则核心钉住没有什么区别。