C++ freertos 在获取信号量失败后从 subclass 调用基 class 的重写函数

C++ freertos Call overriden function of base class from subclass after getting semaphore failes

我正在尝试创建一个包含信号量的基 class 并等待它可以获取信号量,然后调用派生 class 中的函数。 (我想有许多不同的基础 classes 来实现不同的功能,但它们都应该在获取信号量后才执行此功能。 我对freertos不是很熟悉,所以我想错误可能与此有关。

我做了一些尝试,但到目前为止没有成功。

基本代码如下:

基地class:

class CommandMode{
public:
    CommandMode(xSemaphoreHandle* smphr_calc_steps);
    virtual ~CommandMode(){};
    virtual int GetNextState()=0;

protected:
    static void TSK_CalcSteps(void* params);
    xSemaphoreHandle* smphr_calc_steps;
    virtual void CalculateSteps(){};
};

CommandMode::CommandMode(xSemaphoreHandle* smphr_calc_steps):smphr_calc_steps(smphr_calc_steps){
    xTaskCreate(&TSK_CalcSteps, "output data calc", 2048, this, 1, NULL);
}

void CommandMode::TSK_CalcSteps(void* params){
    CommandMode* cm = (CommandMode*) params;
    while(true){
        //force reevaluation at least every 60s
        xSemaphoreTake(cm->smphr_calc_steps, 60*1000/portTICK_PERIOD_MS); // i think the problem is related to that line
        cm->CalculateSteps();
    }
}

这里是派生的 class:

class Mode1:public CommandMode{
public:
    Mode1(xSemaphoreHandle* smphr_calc_steps);
    ~Mode1(){};
    int GetNextState() override;
    
protected:
    void CalculateSteps() override;
};

Mode1::Mode1(xSemaphoreHandle* smphr_calc_steps) : CommandMode(smphr_calc_steps){}

void Mode1::CalculateSteps(){
    Serial.println("Mode1::CalculateSteps");
}

int Mode1::GetNextState(){
    Serial.println("Mode1::GetNextState");
    return 5;
}

然后我尝试像这样调用它们:

CommandMode* current = nullptr;
xSemaphoreHandle smphr = xSemaphoreCreateBinary();
//xSemaphoreHandle smphr2 = xSemaphoreCreateBinary();

Serial.println("init with mode1");
delay(200);
Mode1* a = new Mode1(&smphr);
a->GetNextState(); //This will work
current = a;
current->GetNextState(); //This will work as well

xSemaphoreGive(smphr);  //This does not work as intended/causes the issue

我也不确定 cm->CalculateStepes() 行。由于我将 'cm' 作为 void* 传递,它是否仍会评估正确的 subclass 模块?我是 运行 在带有 Plattform IO 的 ESP32 上,以防这很重要。

到目前为止我有时会遇到看门狗错误,但大多数情况下我会遇到以下两个错误:

/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1443 (xQueueGenericReceive)- assert failed!
abort() was called at PC 0x40087f1d on core 0

assertion "res == coreID || res == portMUX_FREE_VAL" failed: file "/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/portmux_impl.inc.h", line 105, function: vPortCPUAcquireMutexIntsDisabledInternal
abort() was called at PC 0x400d8cf3 on core 0

我尝试使用任务本身的整个代码并尝试使用多个 delays(),但我从来没有让它工作过。

如果有人能告诉我问题出在哪里,或者建议如何以更好的方式实现此行为,我将很高兴? 我也想在基础 class 中包含其他函数和值。这没有问题,但是基于信号量的派生 class 函数的调用根本不起作用。

xSemaphoreTake希望句柄传值,你传指针,那肯定是错误的。

代码编译是因为 SemaphoreHandle_t 本身是一个隐藏的指针(因此这不是一个好的做法,但由于其他充分的原因经常出现在低级的东西中)。这也意味着您可以而且应该将 smphr_calc_steps 作为一个值传递并存储在您的 class 中,作为一个指针,它的复制成本很低。不需要额外的间接寻址,只会使事情复杂化。

FreeRTOS 在堆上分配信号量和 returns 一个指针(=句柄)给你,你唯一的责任是在将来的某个时候调用 vSemaphoreDelete 来释放内存。