espressif / ESP8266_RTOS_SDK

Latest ESP8266 SDK based on FreeRTOS, esp-idf style.
http://bbs.espressif.com
Apache License 2.0
3.33k stars 1.56k forks source link

Idle task causing strange behaviour (GIT8266O-627) #1050

Open timkoers opened 3 years ago

timkoers commented 3 years ago

----------------------------- Delete below -----------------------------

If your issue is a general question, starts similar to "How do I..", or is related to 3rd party development kits/libs, please discuss this on our community forum at bbs.espressif.com instead.

INSTRUCTIONS

Before submitting a new issue, please follow the checklist and try to find the answer.

----------------------------- Delete above -----------------------------

Environment

Problem Description

The tasks are not working as the IDLE task is messing up the two other led blink tasks.

Expected Behavior

Task 1 and 2 to run each 500ms and stay 500ms apart from each other and the idle task to run the remaining time.

Actual Behavior

Task 1 and 2 running (visually) directly after each other, not 500ms apart from each other, with the IDLE task taking the remaining time. The leds that task 1 and 2 toggle are not toggled, they stay on.

Steps to repropduce

  1. Upload the code
  2. Attach three LEDS, one to D1, D2 and D4.
  3. Watch the first schedule cycle work properly
  4. Then when the IDLE task kicks in, LED1 and LED2 stay on permanently

Code to reproduce this issue

extern "C"
{
#include <freertos/FreeRTOSConfig.h>

#ifdef configUSE_PREEMPTION
#undef configUSE_PREEMPTION
#endif

#define configUSE_PREEMPTION 0 // Disable pre-emption and use round robbin

#include "esp_common.h"
#include "esp_wifi.h"
#include "esp_system.h"
#include "freertos/task.h"
#include "gpio.h"
#include "uart.h"
}

void task1_function(void *ignore)
{
    printf("Starting task1\n");
    fflush(stdout);

    bool on = false;
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO4);
    while (true)
    {           
        printf("Task 1 %d\n", on);  
        portENTER_CRITICAL();
        on = !on;
        GPIO_OUTPUT_SET(4, on);
        portEXIT_CRITICAL();
        vTaskDelay(LED_BLINK_DELAY / portTICK_RATE_MS);
    }

    vTaskDelete(NULL);
}

void task2_function(void *ignore)
{
    printf("Starting task2\n");
    fflush(stdout);

    bool on = false;
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO5);
    while (true)
    {           
        printf("Task 2 %d\n", on);
        portENTER_CRITICAL();
        on = !on;
        GPIO_OUTPUT_SET(5, on);
        portEXIT_CRITICAL();
        vTaskDelay(LED_BLINK_DELAY / portTICK_RATE_MS);
    }

    vTaskDelete(NULL);
}

void idle_function(void *ignore)
{
    printf("Starting idle\n");
    fflush(stdout);

    uint8_t on = 0x0;
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO2);
    for (;;)
    {
        portENTER_CRITICAL();
        on = ~on;
        GPIO_OUTPUT_SET(2, on);
        on = ~on;
        GPIO_OUTPUT_SET(2, on);
        portEXIT_CRITICAL();
    }

    vTaskDelete(NULL);
}

/******************************************************************************
 * FunctionName : user_init
 * Description  : entry of user application, init user function here
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void user_init(void)
{
    UART_SetBaudrate(UART0, 115200);

    printf("\n\nSDK version:%s\n\n", system_get_sdk_version());    
    wifi_set_opmode_current(NULL_MODE);
    fflush(stdout);
    vTaskDelay(500 / portTICK_RATE_MS);

    xTaskCreate(task1_function, (signed char *)"task1", 4096, NULL, 3, &task1);
    vTaskDelay(LED_BLINK_DELAY / portTICK_RATE_MS);
    xTaskCreate(task2_function, (signed char *)"task2", 4096, NULL, 3, &task2);
    vTaskDelay(LED_BLINK_DELAY / portTICK_RATE_MS);
    xTaskCreate(idle_function, (signed char *)"idle", 4096, NULL, tskIDLE_PRIORITY, &idleTask);    
}
timkoers commented 3 years ago

Apparently, you can't use GPIO2. Using gpio16_output_set works flawelessly in the idle task

dsptech commented 3 years ago

Hi, I see that you forced "#define configUSE_PREEMPTION 0". Is this setting also global? (aka visible by task.c ?) In a such case, freertos is no longer preemptive (cooperative only) and your loop in the idle function will monopolize the cpu because there are not yield / wait calls inside. You can avoid that by adding a simple yield call OUTSIDE the critical section. Please note that this is not enough to avoid the watchdog reset. You also need to clear the watchdog or leave enough cpu time to the idle task (use a wait instead of the yield).

Regarding GPIO16, it seems to me a bit strange. Is this the only change you applied ? Regards.

dsptech commented 3 years ago

.. or leave the PREEMPTION active. Remember anyway that the idle task will not run (it will yield) even if another IDLE_PRIORITY task is in active state (not in wait) and the clear of the watchdog will not occur.

timkoers commented 3 years ago

Hi, I see that you forced "#define configUSE_PREEMPTION 0". Is this setting also global? (aka visible by task.c ?)

Yes I did, but even with a vTaskDelay it isn't working. Yield did also not solve the problem

Regarding GPIO16, it seems to me a bit strange. Is this the only change your applied ?

Yep, I changed it to use gpio16_set_output and it works properly now. Very strange

dsptech commented 3 years ago

Yes I did, but even with a vTaskDelay it isn't working.

do you mean vTaskDelay(1) (or higher value) after portEXIT_CRITICAL() ?

I've have not the IDF version 2.6.3 on my machine, so I'm not able to test your issue, but I can say that the hardware access to gpio16 is very different from other gpio pins (and the library management too). Have you checked if the problem persists on other GPIO pins?

dsptech commented 3 years ago

I noticed now that in idle function, you are using at not boolean value in GPIO_OUTPUT_SET (the value switch from 0x00 and 0xFF due the uint8_t declaration). Is there a reason for that ? (If I remember correctly, this can involve in a wrong bit mask). Can you try "on = !on;" as you already done in other functions ?

P.S.: I never mentioned that, but I presumed that you stripped something in your snippet. Even if it work, the idle loop will generate microseconds (or less) pulses only.

timkoers commented 3 years ago

Can you try "on = !on;" as you already done in other functions ?

Good one, I thought I had already fixed that. Might it be that the GPIO_OUTPUT_SET function uses the wrong bitmask for that or something?

P.S.: I never mentioned that, but I presumed that you stripped something in your snippet. Even if it work, the idle loop will generate microseconds (or less) pulses only.

Yes, that's exactly what I want 😉

timkoers commented 3 years ago

Yes I did, but even with a vTaskDelay it isn't working.

do you mean vTaskDelay(1) (or higher value) after portEXIT_CRITICAL() ?

Yep

I've have not the IDF version 2.6.3 on my machine, so I'm not able to test your issue, but I can say that the hardware access to gpio16 is very different from other gpio pins (and the library management too).

Strange, how's that possible? Internal chip layout or something?

Have you checked if the problem persists on other GPIO pins?

Yes, it does

dsptech commented 3 years ago

Good one, I thought I had already fixed that. Might it be that the GPIO_OUTPUT_SET function uses the wrong bitmask for that or something?

from gpio.h (branch 2.x.x) from the current repository: #define GPIO_OUTPUT_SET(gpio_no, bit_value) gpio_output_conf(bit_value<<gpio_no, ((~bit_value)&0x01)<<gpio_no, 1<<gpio_no, 0)

from gpio.c: `

void gpio_output_conf(uint32 set_mask, uint32 clear_mask, uint32 enable_mask, uint32 disable_mask) { GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, set_mask); GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, clear_mask); GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, enable_mask); GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, disable_mask); }`

Strange, how's that possible? Internal chip layout or something?

gpio16 is not a true gpio pin. It is a library abstraction only (on v3.x). The pin is directly managed from the rtc/sleep internal device and not from the gpio device. Have you checked about the presence of your pulses out of gpio16 ? I think that GPIO_OUTPUT_SET does not support it at all. EDIT: Some time ago I also saw an old post that claim gpio16 slower than other pins due its slower clock (I have never tested this). This pin may not be suitable for your pulses