There is an interesting bug in LVGL which involves timing with button presses and screen change animations. Basically if a button is double pressed as a screen change is occurring, the screen changes to an undefined blank screen. What needs to happen is the button have a “debounce” routine to ignore multiple presses while screen change animations are happening. Attached is source which one can replicate the issue including the “hack” which ignores the button press for a second.
I think the real fix is to ignore button presses while screen change animations occur. I’d have to study the code more to see how to implement this. Possibly push the calls on a stack at the start of the animation then pop them off at the end?
#include "FS.h"
#include <TFT_eSPI.h>
#include <Ticker.h>
#include <lvgl.h>
#define HACKFIX false
#define HOR_RES 320
#define VER_RES 240
#define CALIBRATION_FILE "/TouchCalData"
#define SETTINGS_FILE "/SettingsData"
#define REPEAT_CAL false
#define LVGL_TICK_PERIOD 20
#define M1PIND1 16
#define M1PIND2 17
Ticker tick;
volatile int m1Ctr = 0;
lv_obj_t *mainScr, *startScr;
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
lv_disp_draw_buf_t disp_buf;
lv_disp_drv_t disp_drv;
lv_indev_drv_t indev_drv;
lv_color_t buf[HOR_RES * 10];
void touch_calibrate()
{
uint16_t calData[5];
uint8_t calDataOK = 0;
// check if calibration file exists and size is correct
if (SPIFFS.exists(CALIBRATION_FILE))
{
if (REPEAT_CAL)
{
// Delete if we want to re-calibrate
SPIFFS.remove(CALIBRATION_FILE);
}
else
{
File f = SPIFFS.open(CALIBRATION_FILE, "r");
if (f)
{
if (f.readBytes((char *)calData, 14) == 14)
calDataOK = 1;
f.close();
}
}
}
if (calDataOK && !REPEAT_CAL)
{
// calibration data valid
tft.setTouch(calData);
}
else
{
// data not valid so recalibrate
tft.fillScreen(TFT_BLACK);
tft.setCursor(20, 0);
tft.setTextFont(2);
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.println("Touch corners as indicated");
tft.setTextFont(1);
tft.println();
if (REPEAT_CAL)
{
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println("Set REPEAT_CAL to false to stop this running again!");
}
tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println("Calibration complete!");
// store data
File f = SPIFFS.open(CALIBRATION_FILE, "w");
if (f)
{
f.write((const unsigned char *)calData, 14);
f.close();
}
}
}
#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char *file, uint32_t line, const char *fn_name, const char *dsc)
{
Serial.printf("%s@%d->%s\r\n", file, line, dsc);
Serial.flush();
}
#endif
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors(&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
uint16_t touchX, touchY;
bool touched = tft.getTouch(&touchX, &touchY, 600);
if (!touched)
{
data->state = LV_INDEV_STATE_REL;
}
else
{
data->state = LV_INDEV_STATE_PR;
}
if (touchX > HOR_RES || touchY > VER_RES)
{
}
else
{
if (3 == tft.getRotation())
{
// Shift coordinates
data->point.x = HOR_RES - touchX;
data->point.y = VER_RES - touchY;
}
else if (1 == tft.getRotation())
{
data->point.x = touchX;
data->point.y = touchY;
}
}
}
static void lv_tick_handler(void)
{
lv_tick_inc(LVGL_TICK_PERIOD);
}
lv_obj_t * buildButton(lv_obj_t *scr,
lv_coord_t x, lv_coord_t y,
lv_coord_t w, lv_coord_t h,
const char *lab, lv_event_cb_t cb)
{
lv_obj_t *btn1 = lv_btn_create(scr);
lv_obj_set_pos(btn1, x, y);
lv_obj_set_size(btn1, w, h);
lv_obj_add_event_cb(btn1, cb, LV_EVENT_ALL, NULL);
lv_obj_t *label = lv_label_create(btn1);
lv_label_set_text(label, lab);
lv_obj_center(label);
return btn1;
}
uint32_t lstTm = 0;
void startbtn_event_cb(lv_event_t *e)
{
if (lv_event_get_code(e) == LV_EVENT_CLICKED)
{
#if HACKFIX
uint32_t thisTm = lv_tick_elaps(lstTm);
lstTm = lv_tick_get();
if(thisTm < 1000) {
return;
}
#endif
lv_scr_load_anim(startScr, LV_SCR_LOAD_ANIM_MOVE_LEFT, 300, 300, false);
}
}
void cancel_event_cb(lv_event_t *e)
{
if (lv_event_get_code(e) == LV_EVENT_CLICKED)
{
#if HACKFIX
uint32_t thisTm = lv_tick_elaps(lstTm);
lstTm = lv_tick_get();
if(thisTm < 1000) {
return;
}
#endif
lv_scr_load_anim(mainScr, LV_SCR_LOAD_ANIM_MOVE_LEFT, 300, 300, false);
}
}
void setup()
{
Serial.begin(115200); /* prepare for possible serial debug */
lv_init();
tick.attach_ms(LVGL_TICK_PERIOD, lv_tick_handler);
tft.begin(); /* TFT init */
tft.setRotation(1); /* Portrait orientation */
// check file system exists
if (!SPIFFS.begin())
{
Serial.println("Formating file system");
SPIFFS.format();
SPIFFS.begin();
}
touch_calibrate();
lv_disp_draw_buf_init(&disp_buf, buf, NULL, HOR_RES * 10);
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.hor_res = HOR_RES;
disp_drv.ver_res = VER_RES;
disp_drv.flush_cb = my_disp_flush;
lv_disp_drv_register(&disp_drv);
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
mainScr = lv_scr_act();
buildButton(mainScr, 10, 100, 100, 75, "Start", startbtn_event_cb);
startScr = lv_obj_create(NULL);
buildButton(startScr, HOR_RES - 10 - 100, VER_RES - 10 - 75,
100, 75, "Cancel", cancel_event_cb);
}
void loop()
{
lv_task_handler();
}