[SOLVED] Best practice to avoid wdt trigger

filo_gr
Posts: 110
Joined: Wed Jul 28, 2021 12:25 pm
Location: Italy

[SOLVED] Best practice to avoid wdt trigger

Postby filo_gr » Sun Dec 18, 2022 3:14 pm

Hello! I'm working with a simple piece of code.

Pressing a button I'm able to toggle a led, with task_led().
The pressure of the button is made by polling the status of the pin made by task_debounce (), and there is also a debounce strategy.

Code: Select all

#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/task.h>
#include <stdint.h>
#include "driver/gpio.h"

#define GPIO_LED    15
#define GPIO_BUTTON 21

static QueueHandle_t queue = NULL;

static void
task_debounce (void * argp)
{
    uint32_t level = 0;
    uint32_t state = 0;
    uint32_t last = 0xFFFFFFFF;
    uint32_t mask = 0x7FFFFFFF;
    bool event = false;

    for (;;)
    {
        level = gpio_get_level(GPIO_BUTTON);
        state = (state << 1) | level;

        if (((state & mask) == mask) || ((state & mask) == 0))
        {
            if (level != last)
            {
                event = (bool) level;

                if (pdPASS == xQueueSendToBack(queue, &event, 1))
                {
                    last = level;
                }
            }
        }

        taskYIELD();
    }
}

static void
task_led (void * argp)
{
    BaseType_t st;
    bool event;
    bool led = false;

    (void) gpio_set_level(GPIO_LED, led);

    for (;;)
    {
        st = xQueueReceive(queue, &event, portMAX_DELAY);
        assert(pdPASS == st);

        if (true == event)
        {
            led ^= true;
            gpio_set_level(GPIO_LED, led);
        }
    }
}

void
app_main (void)
{
    int app_cpu = xPortGetCoreID();
    TaskHandle_t h_task = NULL;
    BaseType_t rc = 0;

    vTaskDelay(pdMS_TO_TICKS(2000));

    queue = xQueueCreate(40, sizeof(bool));
    assert(queue);
    
    gpio_pad_select_gpio(GPIO_LED);
    (void) gpio_set_direction(GPIO_LED, GPIO_MODE_OUTPUT);

    gpio_pad_select_gpio(GPIO_BUTTON);
    (void) gpio_set_direction(GPIO_BUTTON, GPIO_MODE_INPUT);
    (void) gpio_set_pull_mode(GPIO_BUTTON, GPIO_PULLUP_ONLY);

    rc = xTaskCreatePinnedToCore(task_debounce, "debounce", 2048, NULL, 1, &h_task, app_cpu);
    assert(pdPASS == rc);
    assert(h_task);

    rc = xTaskCreatePinnedToCore(task_led, "led", 2048, NULL, 1, &h_task, app_cpu);
    assert(pdPASS == rc);
    assert(h_task);
}
Running this piece of code on an ESP32, I get this error message:

Code: Select all

E (269269) task_wdt: Print CPU 1 backtrace


Backtrace:0x40084031:0x3FFB11800x40082665:0x3FFB11A0 0x40085174:0x3FFB7960 0x400D4BF9:0x3FFB7980 0x40087ACD:0x3FFB79B0 
0x40084031: esp_crosscore_isr at C:/esp/esp-idf/components/esp_system/crosscore_int.c:92

0x40082665: _xt_lowint1 at C:/esp/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1111

0x40085174: _frxt_dispatch at C:/esp/esp-idf/components/freertos/port/xtensa/portasm.S:465

0x400d4bf9: task_debounce at C:\Users\Filippo\Documents\Espressif\ch3_queue_press\build/../main/main.c:39

0x40087acd: vPortTaskWrapper at C:/esp/esp-idf/components/freertos/port/xtensa/port.c:131


E (274269) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (274269) task_wdt:  - IDLE (CPU 1)
E (274269) task_wdt: Tasks currently running:
E (274269) task_wdt: CPU 0: IDLE
E (274269) task_wdt: CPU 1: debounce
E (274269) task_wdt: Print CPU 0 (current core) backtrace


Backtrace:0x400D74AF:0x3FFB0B800x40082665:0x3FFB0BA0 0x400E5087:0x3FFB69C0 0x400D1923:0x3FFB69E0 0x40086566:0x3FFB6A00 0x40087ACD:0x3FFB6A20
0x400d74af: task_wdt_isr at C:/esp/esp-idf/components/esp_system/task_wdt.c:183 (discriminator 3)

0x40082665: _xt_lowint1 at C:/esp/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1111

0x400e5087: cpu_ll_waiti at C:/esp/esp-idf/components/hal/esp32/include/hal/cpu_ll.h:183
 (inlined by) esp_pm_impl_waiti at C:/esp/esp-idf/components/esp_pm/pm_impl.c:837

0x400d1923: esp_vApplicationIdleHook at C:/esp/esp-idf/components/esp_system/freertos_hooks.c:63

0x40086566: prvIdleTask at C:/esp/esp-idf/components/freertos/tasks.c:3974 (discriminator 1)

0x40087acd: vPortTaskWrapper at C:/esp/esp-idf/components/freertos/port/xtensa/port.c:131


E (274269) task_wdt: Print CPU 1 backtrace


Backtrace:0x40084031:0x3FFB11800x40082665:0x3FFB11A0 0x40085174:0x3FFB7960 0x400D4BF9:0x3FFB7980 0x40087ACD:0x3FFB79B0 
0x40084031: esp_crosscore_isr at C:/esp/esp-idf/components/esp_system/crosscore_int.c:92

0x40082665: _xt_lowint1 at C:/esp/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1111

0x40085174: _frxt_dispatch at C:/esp/esp-idf/components/freertos/port/xtensa/portasm.S:465

0x400d4bf9: task_debounce at C:\Users\Filippo\Documents\Espressif\ch3_queue_press\build/../main/main.c:39

0x40087acd: vPortTaskWrapper at C:/esp/esp-idf/components/freertos/port/xtensa/port.c:131
What I ask is: what is the best practice to avoid this type of error? I don't want to increase the WDT time, neither insert delays or interrupts. I want simply polling the button.
I thought that taskYIELD() suffices, but it doesn't...

Thank you :)
Last edited by filo_gr on Tue Dec 20, 2022 9:06 am, edited 1 time in total.
Filippo

mikemoy
Posts: 627
Joined: Fri Jan 12, 2018 9:10 pm

Re: Best practice to avoid wdt trigger

Postby mikemoy » Sun Dec 18, 2022 3:31 pm

https://www.freertos.org/a00020.html

Did you try vTaskDelay(1 / portTICK_PERIOD_MS); instead of yield ?

A6ESPF
Posts: 28
Joined: Tue Dec 10, 2019 12:16 pm

Re: Best practice to avoid wdt trigger

Postby A6ESPF » Sun Dec 18, 2022 10:53 pm

When you yield the task with higher priority, it'll immediately come back to an active state, thus keeping the idle task blocked. You'll have to add a delay to your task, as @mikemoy suggested.

filo_gr
Posts: 110
Joined: Wed Jul 28, 2021 12:25 pm
Location: Italy

Re: Best practice to avoid wdt trigger

Postby filo_gr » Mon Dec 19, 2022 1:03 pm

mikemoy wrote:
Sun Dec 18, 2022 3:31 pm
https://www.freertos.org/a00020.html

Did you try vTaskDelay(1 / portTICK_PERIOD_MS); instead of yield ?
Do you mean I should remove taskYIELD() and use the vTaskDelay(1 / portTICK_PERIOD_MS)?
A6ESPF wrote: When you yield the task with higher priority, it'll immediately come back to an active state, thus keeping the idle task blocked. You'll have to add a delay to your task, as @mikemoy suggested.
So, to correctly unblock the idle task, what should I do?

This

Code: Select all

vTaskDelay(1 / portTICK_PERIOD_MS);
taskYIELD();
or this one

Code: Select all

taskYIELD();
vTaskDelay(1 / portTICK_PERIOD_MS);
or directly remove the taskYIELD()?

Code: Select all

vTaskDelay(1 / portTICK_PERIOD_MS);
:?:
Filippo

boarchuz
Posts: 606
Joined: Tue Aug 21, 2018 5:28 am

Re: Best practice to avoid wdt trigger

Postby boarchuz » Mon Dec 19, 2022 1:33 pm

In my opinion you should just disable the task watchdog.

It's there to ensure that tasks (usually only idleTask) aren't unexpectedly starved. If you know that lower priority tasks will always be starved, by design, then it doesn't make much sense for the watchdog to panic about it.

With a big disclaimer that there's rarely a good reason for 100% CPU, definitely not a button and LED, and some care needs to be taken if idleTask will never run (eg. with task deletion)...

filo_gr
Posts: 110
Joined: Wed Jul 28, 2021 12:25 pm
Location: Italy

Re: Best practice to avoid wdt trigger

Postby filo_gr » Mon Dec 19, 2022 7:27 pm

1) I tried to modify only the priority of the tasks, reducing it to 0.

Code: Select all

rc = xTaskCreatePinnedToCore(task_debounce, "debounce", 2048, NULL, 0, &h_task, app_cpu);

Code: Select all

rc = xTaskCreatePinnedToCore(task_led, "led", 2048, NULL, 0, &h_task, app_cpu);
Now the WDT doesn't trigger, however the system is very, very slowed down. I mean I must click the button and mantain it pressed for a while in order to toggle the led.

2) I restored the original task priority, however now I use vTaskDelay(1); instead of taskYIELD();.

I obtain something similar as the previous point. I'm not able to quickly toggle the led.

boarchuz wrote: In my opinion you should just disable the task watchdog.

It's there to ensure that tasks (usually only idleTask) aren't unexpectedly starved. If you know that lower priority tasks will always be starved, by design, then it doesn't make much sense for the watchdog to panic about it.
I think this option is a bit critical, because if WDT triggers means that my code needs some code optimization.

Summary:
  • Task delay becomes critical
  • Lower task priority becomes critical
  • I'd like to avoid to disable the WDT
  • Interrupt on the button pin to wake up the task could be an option, however I want to avoid interrups and I want to use polling
Is it possible that I can't polling a pin? On all microcontrollers (without FreeRTOS) I think it is easy to implement!
Maybe I misinterpreted the use of FreeRTOS on microcontrollers...
Is there a way to solve this? :shock:
Filippo

ESP_Sprite
Posts: 9749
Joined: Thu Nov 26, 2015 4:08 am

Re: Best practice to avoid wdt trigger

Postby ESP_Sprite » Tue Dec 20, 2022 3:14 am

Generally, you're better off not polling stuff under a RTOS; as you saw, spinning in a loop means that other tasks can't really get any CPU time.

The best way would be to use interrupts to not have the CPU do anything while no button is pressed, but as that's not what you want, here's some other things.

One of the issues you have here is that a vTaskDelay(1) will delay for at least the inverse of the FreeRTOS tick frequency, which by default is 100mS. Given that your debounce strategy takes 31 cycles to decide a button is pressed, that means that you need to keep the button pressed for 3.1 seconds before something happens.

The simplest thing would be to change your strategy. If you change 'mask' to 0x3 for instance, the button only needs to be pressed for 200 ms before it triggers.

The alternative is to find a way to delay less time. The dirtiest way would be to increase the FreeRTOS tick frequency (which you can do in menuconfig), but a cleaner way would be to use e.g. esp_timer. Both these methods have the downside that it simply costs a bunch of CPU time to switch tasks, so you'd be wasting CPU power that way, but it'd work.

filo_gr
Posts: 110
Joined: Wed Jul 28, 2021 12:25 pm
Location: Italy

Re: Best practice to avoid wdt trigger

Postby filo_gr » Tue Dec 20, 2022 7:39 am

ESP_Sprite wrote:
Tue Dec 20, 2022 3:14 am
One of the issues you have here is that a vTaskDelay(1) will delay for at least the inverse of the FreeRTOS tick frequency, which by default is 100mS. Given that your debounce strategy takes 31 cycles to decide a button is pressed, that means that you need to keep the button pressed for 3.1 seconds before something happens.

The simplest thing would be to change your strategy. If you change 'mask' to 0x3 for instance, the button only needs to be pressed for 200 ms before it triggers.

The alternative is to find a way to delay less time. The dirtiest way would be to increase the FreeRTOS tick frequency (which you can do in menuconfig), but a cleaner way would be to use e.g. esp_timer. Both these methods have the downside that it simply costs a bunch of CPU time to switch tasks, so you'd be wasting CPU power that way, but it'd work.
Thank you ESP_Sprite! I knew there were something missing about my understanding of FreeRTOS :mrgreen:

This means that tasks aren't intended for spinning in a loop (that is inefficient) but they must be triggered through events in order to optimize their behaviour
Last edited by filo_gr on Tue Dec 20, 2022 9:05 am, edited 1 time in total.
Filippo

ESP_Sprite
Posts: 9749
Joined: Thu Nov 26, 2015 4:08 am

Re: Best practice to avoid wdt trigger

Postby ESP_Sprite » Tue Dec 20, 2022 9:03 am

filo_gr wrote:
Tue Dec 20, 2022 7:39 am
This means that tasks aren't intended for spinning in a loop (that is inefficient) but the must be triggered through events in order to optimize their behaviour
Yes, indeed. Having a loop in a task per se is not bad (even mandatory for lots of use cases) but you want something that blocks the task inside that loop until something 'interesting' happens, e.g. something that waits on a semaphore, queue, task notification etc.

A6ESPF
Posts: 28
Joined: Tue Dec 10, 2019 12:16 pm

Re: [SOLVED] Best practice to avoid wdt trigger

Postby A6ESPF » Fri Dec 23, 2022 9:06 am

filo_gr wrote: So, to correctly unblock the idle task, what should I do?

This

Code: Select all

vTaskDelay(1 / portTICK_PERIOD_MS);
taskYIELD();
or this one

Code: Select all

taskYIELD();
vTaskDelay(1 / portTICK_PERIOD_MS);
or directly remove the taskYIELD()?

Code: Select all

vTaskDelay(1 / portTICK_PERIOD_MS);
:?:

The last one, remove taskYIELD() and add vTaskDelay(1 / portTICK_PERIOD_MS).

Also, if you know your main task will be active longer than CONFIG_ESP_TASK_WDT_TIMEOUT_S seconds (5 by default), you should increase the watchdog timer period (to 30s for example). You can do that by putting CONFIG_ESP_TASK_WDT_TIMEOUT_S=30 in sdkconfig.defaults file.

Who is online

Users browsing this forum: benrank, Majestic-12 [Bot], MicroController and 111 guests