Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP32C3 - non-LVGL task (temp reading) causes TFT display to go blank and stay blank on second run of task #340

Open
u8915055 opened this issue Feb 24, 2024 · 1 comment

Comments

@u8915055
Copy link

We use GitHub issues for development related discussions.
Please use the forum to ask questions.

Describe the issue
I have used a basic example to create a hello world message on an ST7735S display. I am using this on an ESP32C3 single core board. I am using LVGL 8.3 and ESP-IDF 5.1.2. When i load up the basic hello world example, the example runs and the screen displays the message. However, on this board my intention is to read a temperature from an SHT3X device (I2C) and display it on the screen. I create a task for the temperature reading that is created before the task to run lv_task_handler. Now if i completely disable the temperature code (task), then the display will show the hello world. if I disable the LVGL task then the system will keep reading and printf the results to the console. So i know all code seems to be working.

The problem starts when i create both of these tasks and run them. Because im creating the temperature task first, i get one temp reading out of it initially. Then the lv_task_handler task runs and the display shows 'hello world'. However the second the next iteration of temperature measurement happens (task fired every 10 seconds), the screen immediately goes blank and never comes back.

So its like the temperature task is somehow throwing of the lv_task_handler task. I realize that im on a single core device so i cant assign lvgl code to one core and temperature reading to another but i would think things are fast enough to handle both of these.

Either there seems to be some sort of memory corruption here or maybe there's an lv_task_handler timing issue?
Code to reproduce the issue

#include "freertos/FreeRTOS.h"
//#include "freertos/queue.h"
#include "freertos/task.h"
//#include "freertos/event_groups.h"
#include "freertos/semphr.h"

#include "nvs_flash.h"
#include "esp_flash.h"
#include "esp_chip_info.h"
#include "esp_err.h"
#include "esp_timer.h"
#include "esp_log.h"

#include "driver/i2c.h"
#include "sht3x.h"

#include "lvgl/lvgl.h"
//#include "lvgl/examples/lv_examples.h"
#include "lvgl_esp32_drivers/lvgl_helpers.h"

#define I2C_MASTER_NUM 0 
#define I2C_MASTER_SCL_IO 10
#define I2C_MASTER_SDA_IO 9
#define I2C_MASTER_FREQ_HZ 100000
#define I2C_MASTER_TX_BUF_DISABLE 0 
#define I2C_MASTER_RX_BUF_DISABLE 0  
#define SHT3X_ADDRESS 0x44

#define DISP_HOR_RES 128
#define DISP_VER_RES 160
#define LVGL_TICK_PERIOD_MS 1

static sht3x_t sht3x_dev;
float temperature;
float humidity;
SemaphoreHandle_t xGuiSemaphore = NULL;

static esp_err_t i2c_master_init(void)
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };
    esp_err_t err = i2c_param_config(0, &conf);
    if (err != ESP_OK) {
        return err;
    }
    return i2c_driver_install(0, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

static void scan_i2c(void)
{    
    printf("I2C:Starting I2C Scan\n");
    uint8_t address;
    printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\r\n");
    for (int i = 0; i < 128; i += 16) {
        printf("%02x: ", i);
        for (int j = 0; j < 16; j++) {
            fflush(stdout);
            address = i + j;
            i2c_cmd_handle_t cmd = i2c_cmd_link_create();
            i2c_master_start(cmd);
            i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, 0x1);
            i2c_master_stop(cmd);
            esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 50 / portTICK_PERIOD_MS);
            i2c_cmd_link_delete(cmd);
            if (ret == ESP_OK) {
                printf("%02x ", address);
            } else if (ret == ESP_ERR_TIMEOUT) {
                printf("UU ");
            } else {
                printf("-- ");
            }
        }
        printf("\r\n");
    }
}

static void measure_temphum(void *pvParameters)
{
    TickType_t last_wakeup = xTaskGetTickCount();

    uint8_t duration = sht3x_get_measurement_duration(SHT3X_HIGH);

    while (1)
    {
        //if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
            sht3x_start_measurement(&sht3x_dev, SHT3X_SINGLE_SHOT, SHT3X_HIGH);
            printf("MARKER: measure temp\n");
            // Wait until measurement is ready (constant time of at least 30 ms
            // or the duration returned from *sht3x_get_measurement_duration*).
            vTaskDelay(30/portTICK_PERIOD_MS);

            sht3x_get_results(&sht3x_dev, &temperature, &humidity);
            printf("SHT3x Sensor: %.2f °C, %.2f %%\n", temperature, humidity);
            // perform one measurement and do something with the results

            vTaskDelayUntil(&last_wakeup, 10000/portTICK_PERIOD_MS);
            printf( "Task Name\tStatus\tPrio\tHWM\tTask\tAffinity\n");
            char stats_buffer[1024];
            vTaskList(stats_buffer);
            printf("%s\n", stats_buffer);
            printf("MARKER: end of main\n");
            //xSemaphoreGive(xGuiSemaphore);
        //}
    }
}

static void check_chip(void) 
{
    /* Print chip information */
    esp_chip_info_t chip_info;
    uint32_t flash_size;
    esp_chip_info(&chip_info);
    printf("This is %s chip with %d CPU core(s), WiFi%s%s, ",
        CONFIG_IDF_TARGET,
        chip_info.cores,
        (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
        (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");

    unsigned major_rev = chip_info.revision / 100;
    unsigned minor_rev = chip_info.revision % 100;
    printf("silicon revision v%d.%d, ", major_rev, minor_rev);
    if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
        printf("Get flash size failed");
        return;
    }

    printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
        (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
}

static void gui_app(void)
{
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);
    lv_obj_t* label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Hello world!");
    lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0x00ff00), LV_PART_MAIN);
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}

// static void lv_taskhandler_task(void)
// {
//     xGuiSemaphore = xSemaphoreCreateMutex();

//     while (1) {

//         /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
//         vTaskDelay(20 / portTICK_PERIOD_MS);

//         /* Try to take the semaphore, call lvgl related function on success */
//         if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
//             lv_task_handler();
//             xSemaphoreGive(xGuiSemaphore);
//         }
//     }
// }

static void lvgl_tick_task(void *arg)
{
    (void) arg;
    lv_tick_inc(LVGL_TICK_PERIOD_MS);
}

static void lvgl_gui_init(void)
{
    /*Setup LVGL*/
    lv_init();
    lvgl_driver_init();

    static lv_disp_draw_buf_t draw_buf;

    /*Declare a buffer for 1/10 screen size*/
    static lv_color_t buf1[DISP_HOR_RES * DISP_VER_RES / 10];
    assert(buf1);
    static lv_color_t buf2[DISP_HOR_RES * DISP_VER_RES / 10];
    assert(buf2);

    /*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */
    lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_HOR_RES * DISP_VER_RES / 10);

    static lv_disp_drv_t disp_drv;  
    lv_disp_drv_init(&disp_drv);            /*Basic initialization*/
    disp_drv.flush_cb = st7735s_flush;      /*Set your driver function*/
    disp_drv.draw_buf = &draw_buf;          /*Assign the buffer to the display*/
    disp_drv.hor_res = DISP_HOR_RES;        /*Set the horizontal resolution of the display*/
    disp_drv.ver_res = DISP_VER_RES;        /*Set the vertical resolution of the display*/
    disp_drv.rotated = 1;
    lv_disp_drv_register(&disp_drv);        /*Finally register the driver*/

    /*Create timer to call lv_tick*/
    static const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lvgl_tick_task,
        .name = "periodic_gui"
    };

    static esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LVGL_TICK_PERIOD_MS * 1000)); //time in microseconds

    gui_app();

    xGuiSemaphore = xSemaphoreCreateMutex();

    while (1) {

        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(10 / portTICK_PERIOD_MS);

        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
            lv_task_handler();
            xSemaphoreGive(xGuiSemaphore);
        }
    }
    free(buf1);
    free(buf2);
    vTaskDelete(NULL);
}

void app_main(void)
{
    //Initialize NVS
    printf("MARKER: starting main\n");
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret); 

    //Check SOC Chip Specs
    printf("MARKER: check chip\n");
    check_chip();

    //Initialize I2C
    printf("MARKER: initialize I2C\n");
    ESP_ERROR_CHECK(i2c_master_init());
    printf("I2C:I2C Initialized SDA:%d SCL:%d CLK:%d\n",I2C_MASTER_SDA_IO,I2C_MASTER_SCL_IO,I2C_MASTER_FREQ_HZ);

    //Scan for I2C devices
    printf("MARKER: scan I2C\n");
    scan_i2c();
    i2c_driver_delete(I2C_NUM_0);

    //Initialize task for temphum measurement
    printf("MARKER: initialize I2C for temphum\n");
    ESP_ERROR_CHECK(i2cdev_init());
    memset(&sht3x_dev, 0, sizeof(sht3x_t));
    ESP_ERROR_CHECK(sht3x_init_desc(&sht3x_dev, SHT3X_ADDRESS, 0, I2C_MASTER_SDA_IO, I2C_MASTER_SCL_IO));
    ESP_ERROR_CHECK(sht3x_init(&sht3x_dev));
    vTaskDelay( 1000 / portTICK_PERIOD_MS);
    xTaskCreate(&measure_temphum, "measure_temphum", configMINIMAL_STACK_SIZE * 2, NULL, 0, NULL);


    xTaskCreate(&lvgl_gui_init, "lvgl_gui_init", 4096*4, NULL, 2, NULL);
}

Expected Results
I expect that both tasks wil continue to run properly. The temperature task keeps running but somethign is happening to the display. I actaully dont think the lvgl task is crashing because i can see it if i continually dump a task list. I see this:

MARKER: measure temp
SHT3x Sensor: 21.28 °C, 55.72 %
Task Name       Status  Prio    HWM     Task    Affinity
measure_temphum X       0       460     4
IDLE            R       0       1256    3
lvgl_gui_init   B       2       14376   5
esp_timer       B       22      3856    1

Actual Results
Display goes blank after second iteration of temperature measurement
ESP32 Chip version
ESP32C3
ESP-IDF version
5.1.2
Development kit used

Development machine OS
MAC OS with VSCODE
Compilation warnings/errors (if available)
None
If possible, copy the compilation log into a file and attach it here

@OscarMonedero
Copy link

hi, did you used the lvgl_port or did you create an other project? Because i am trying to create a project for an esp32c3 with a ssd1306 screen and im not able to turn on the screen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants