You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
15 KiB
455 lines
15 KiB
/*
|
|
* Copyright (c) 2019, Erich Styger
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#if PL_CONFIG_USE_GUI
|
|
#include "lv.h"
|
|
#include "LittlevGL/lvgl/lvgl.h"
|
|
#include "McuGDisplaySSD1306.h"
|
|
#include "McuSSD1306.h"
|
|
#include "McuRB.h"
|
|
#include <string.h> /* for memset() */
|
|
#include "McuShell.h"
|
|
#include "McuRTOS.h"
|
|
#include "lcd.h"
|
|
#if PL_CONFIG_USE_TOASTER
|
|
#include "toaster.h"
|
|
#endif
|
|
|
|
#if PL_CONFIG_USE_GUI_SCREENSAVER
|
|
static TimerHandle_t timerHndlLcdTimeout;
|
|
#endif
|
|
|
|
static McuRB_Handle_t ringBufferHndl;
|
|
|
|
#if PL_CONFIG_USE_GUI_SCREENSAVER
|
|
static void vTimerCallbackLCDExpired(TimerHandle_t pxTimer) {
|
|
/* timer callback to turn off LCD backlight */
|
|
(void)pxTimer; /* not used */
|
|
#if PL_CONFIG_USE_TOASTER
|
|
TOASTER_Show();
|
|
#else
|
|
McuSSD1306_DisplayOn(false);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static void KeyPressForLCD(void) {
|
|
#if PL_CONFIG_USE_GUI_SCREENSAVER
|
|
|
|
/* called for each key press. It turns on the LCD if it is dormant or resets the LCD backlight timeout timer */
|
|
if (xTimerIsTimerActive(timerHndlLcdTimeout)==pdFALSE) {
|
|
/* timer is not active: start it */
|
|
if (xTimerStart(timerHndlLcdTimeout, 0)!=pdPASS) {
|
|
for(;;); /* failure!?! */
|
|
}
|
|
#if PL_CONFIG_USE_TOASTER
|
|
TOASTER_StopScreenSaver();
|
|
/* and turn LCD on */
|
|
#else
|
|
McuSSD1306_DisplayOn(true);
|
|
#endif
|
|
} else {
|
|
if (xTimerReset(timerHndlLcdTimeout, 0)!=pdPASS) { /* reset timer, e.g. after key press or user input */
|
|
for(;;); /* failure?!? */
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static lv_indev_t *inputDevicePtr;
|
|
|
|
lv_indev_t *LV_GetInputDevice(void) {
|
|
return inputDevicePtr;
|
|
}
|
|
|
|
/* Flush the content of the internal buffer the specific area on the display
|
|
* You can use DMA or any hardware acceleration to do this operation in the background but
|
|
* 'lv_flush_ready()' has to be called when finished
|
|
* This function is required only when LV_VDB_SIZE != 0 in lv_conf.h*/
|
|
static void myDispFlush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
|
|
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
|
|
int32_t x, y;
|
|
int32_t x1, y1, x2, y2;
|
|
|
|
x1 = area->x1;
|
|
y1 = area->y1;
|
|
x2 = area->x2;
|
|
y2 = area->y2;
|
|
for(y = y1; y <= y2; y++) {
|
|
for(x = x1; x <= x2; x++) {
|
|
/* Put a pixel to the display. */
|
|
LCD_SetPixel(x, y, color_p->full);
|
|
color_p++;
|
|
}
|
|
}
|
|
LCD_UpdateRegion(x1, y1, x2-x1+1, y2-y1+1);
|
|
/* IMPORTANT!!!
|
|
* Inform the graphics library that you are ready with the flushing*/
|
|
lv_disp_flush_ready(disp_drv);
|
|
}
|
|
|
|
#if 0
|
|
/* Read the touchpad and store it in 'data'
|
|
* Return false if no more data read; true for ready again */
|
|
static bool ex_tp_read(lv_indev_data_t * data) {
|
|
/* Read the touchpad */
|
|
int x=0, y=0, res;
|
|
bool pressed;
|
|
|
|
res = TOUCH_Poll(&pressed, &x, &y);
|
|
if (res==1 && pressed) {
|
|
data->state = LV_INDEV_STATE_PR;
|
|
} else {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
}
|
|
data->point.x = x;
|
|
data->point.y = y;
|
|
return false; /*false: no more data to read because we are no buffering*/
|
|
}
|
|
#endif
|
|
|
|
void LV_Task(void) {
|
|
/* Periodically call this function.
|
|
* The timing is not critical but should be between 1..10 ms */
|
|
lv_task_handler();
|
|
}
|
|
|
|
/* called for push button events from Events.c */
|
|
void LV_ButtonEvent(uint8_t keys, uint16_t eventMask) {
|
|
uint16_t buttonInfo;
|
|
|
|
if (keys&LV_BTN_MASK_CENTER) {
|
|
buttonInfo = LV_BTN_MASK_CENTER | eventMask;
|
|
McuRB_Put(ringBufferHndl, &buttonInfo);
|
|
} else if (keys&LV_BTN_MASK_RIGHT) {
|
|
buttonInfo = LV_BTN_MASK_RIGHT | eventMask;
|
|
McuRB_Put(ringBufferHndl, &buttonInfo);
|
|
} else if (keys&LV_BTN_MASK_DOWN) {
|
|
buttonInfo = LV_BTN_MASK_DOWN | eventMask;
|
|
McuRB_Put(ringBufferHndl, &buttonInfo);
|
|
} else if (keys&LV_BTN_MASK_UP) {
|
|
buttonInfo = LV_BTN_MASK_UP | eventMask;
|
|
McuRB_Put(ringBufferHndl, &buttonInfo);
|
|
} else if (keys&LV_BTN_MASK_LEFT) {
|
|
buttonInfo = LV_BTN_MASK_LEFT | eventMask;
|
|
McuRB_Put(ringBufferHndl, &buttonInfo);
|
|
}
|
|
}
|
|
|
|
static uint8_t MapKeyOrientation(uint8_t key) {
|
|
switch(McuGDisplaySSD1306_GetDisplayOrientation()) {
|
|
case McuSSD1306_CONFIG_ORIENTATION_PORTRAIT:
|
|
switch(key) {
|
|
case LV_BTN_MASK_LEFT:
|
|
key = LV_BTN_MASK_UP;
|
|
break;
|
|
case LV_BTN_MASK_RIGHT:
|
|
key = LV_BTN_MASK_DOWN;
|
|
break;
|
|
case LV_BTN_MASK_UP:
|
|
key = LV_BTN_MASK_RIGHT;
|
|
break;
|
|
case LV_BTN_MASK_DOWN:
|
|
key = LV_BTN_MASK_LEFT;
|
|
break;
|
|
case LV_BTN_MASK_CENTER:
|
|
key = LV_BTN_MASK_CENTER;
|
|
break;
|
|
default:
|
|
key = 0; /* error case? */
|
|
break;
|
|
} /* switch */
|
|
break;
|
|
case McuSSD1306_CONFIG_ORIENTATION_PORTRAIT180:
|
|
switch(key) {
|
|
case LV_BTN_MASK_LEFT:
|
|
key = LV_BTN_MASK_DOWN;
|
|
break;
|
|
case LV_BTN_MASK_RIGHT:
|
|
key = LV_BTN_MASK_UP;
|
|
break;
|
|
case LV_BTN_MASK_UP:
|
|
key = LV_BTN_MASK_LEFT;
|
|
break;
|
|
case LV_BTN_MASK_DOWN:
|
|
key = LV_BTN_MASK_RIGHT;
|
|
break;
|
|
case LV_BTN_MASK_CENTER:
|
|
key = LV_BTN_MASK_CENTER;
|
|
break;
|
|
default:
|
|
key = 0; /* error case? */
|
|
break;
|
|
} /* switch */
|
|
break;
|
|
case McuSSD1306_CONFIG_ORIENTATION_LANDSCAPE180:
|
|
switch(key) {
|
|
case LV_BTN_MASK_LEFT:
|
|
key = LV_BTN_MASK_RIGHT;
|
|
break;
|
|
case LV_BTN_MASK_RIGHT:
|
|
key = LV_BTN_MASK_LEFT;
|
|
break;
|
|
case LV_BTN_MASK_UP:
|
|
key = LV_BTN_MASK_DOWN;
|
|
break;
|
|
case LV_BTN_MASK_DOWN:
|
|
key = LV_BTN_MASK_UP;
|
|
break;
|
|
case LV_BTN_MASK_CENTER:
|
|
key = LV_BTN_MASK_CENTER;
|
|
break;
|
|
default:
|
|
key = 0; /* error case? */
|
|
break;
|
|
} /* switch */
|
|
break;
|
|
case McuSSD1306_CONFIG_ORIENTATION_LANDSCAPE:
|
|
switch(key) {
|
|
case LV_BTN_MASK_LEFT:
|
|
key = LV_BTN_MASK_LEFT;
|
|
break;
|
|
case LV_BTN_MASK_RIGHT:
|
|
key = LV_BTN_MASK_RIGHT;
|
|
break;
|
|
case LV_BTN_MASK_UP:
|
|
key = LV_BTN_MASK_UP;
|
|
break;
|
|
case LV_BTN_MASK_DOWN:
|
|
key = LV_BTN_MASK_DOWN;
|
|
break;
|
|
case LV_BTN_MASK_CENTER:
|
|
key = LV_BTN_MASK_CENTER;
|
|
break;
|
|
default:
|
|
key = 0; /* error case? */
|
|
break;
|
|
} /* switch */
|
|
break;
|
|
default:
|
|
key = 0; /* error case? */
|
|
break;
|
|
} /* switch */
|
|
return key;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* To use a keyboard:
|
|
USE_LV_GROUP has to be enabled in lv_conf.h
|
|
An object group has to be created: lv_group_create() and objects have to be added: lv_group_add_obj()
|
|
The created group has to be assigned to an input device: lv_indev_set_group(my_indev, group1);
|
|
Use LV_GROUP_KEY_... to navigate among the objects in the group
|
|
*/
|
|
static bool keyboard_read(lv_indev_data_t *data) {
|
|
uint16_t keyData;
|
|
|
|
memset(data, 0, sizeof(lv_indev_data_t)); /* initialize all fields */
|
|
data->state = LV_INDEV_STATE_REL; /* by default, not pressed */
|
|
if (McuRB_NofElements(ringBufferHndl)==0) {
|
|
return false; /* no data present */
|
|
}
|
|
if (McuRB_Get(ringBufferHndl, &keyData)!=ERR_OK) {
|
|
return false; /* we had data in the buffer, but now not anymore? something went wrong! */
|
|
}
|
|
KeyPressForLCD();
|
|
if (keyData&(LV_MASK_PRESSED|LV_MASK_PRESSED_LONG)) {
|
|
data->state = LV_INDEV_STATE_PR;
|
|
} else if (keyData&(LV_MASK_RELEASED|LV_MASK_RELEASED_LONG)) {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
}
|
|
data->key = MapKeyOrientation(keyData&0xff);
|
|
return McuRB_NofElements(ringBufferHndl)!=0; /* return true if we have more data */
|
|
}
|
|
#endif
|
|
static bool encoder_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) {
|
|
uint16_t keyData;
|
|
|
|
(void)indev_drv; /* not used */
|
|
memset(data, 0, sizeof(lv_indev_data_t)); /* initialize all fields */
|
|
data->state = LV_INDEV_STATE_REL; /* by default, not pressed */
|
|
if (McuRB_NofElements(ringBufferHndl)==0) {
|
|
return false; /* no data present */
|
|
}
|
|
if (McuRB_Get(ringBufferHndl, &keyData)!=ERR_OK) {
|
|
return false; /* we had data in the buffer, but now not anymore? something went wrong! */
|
|
}
|
|
KeyPressForLCD(); /* inform LCD timer that there is a user action */
|
|
data->state = LV_INDEV_STATE_REL; /* default state */
|
|
keyData = (keyData&0xff00) | MapKeyOrientation(keyData&0xff);
|
|
/* keys are changing only enc_diff, except ENTER/CENTER/PUSH which sets the LV_INDEV_STATE_PR state */
|
|
switch(keyData&0xff) {
|
|
case LV_BTN_MASK_LEFT:
|
|
if (keyData&(LV_MASK_PRESSED)) {
|
|
data->enc_diff = -1;
|
|
McuShell_SendStr((uint8_t*)"left pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->enc_diff = -1;
|
|
McuShell_SendStr((uint8_t*)"left long pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED_LONG)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"left long released\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"left released\r\n", McuShell_GetStdio()->stdOut);
|
|
}
|
|
break;
|
|
case LV_BTN_MASK_RIGHT:
|
|
if (keyData&(LV_MASK_PRESSED)) {
|
|
data->enc_diff = 1;
|
|
McuShell_SendStr((uint8_t*)"right pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->enc_diff = 1;
|
|
McuShell_SendStr((uint8_t*)"right long pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED_LONG)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"right long released\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"right released\r\n", McuShell_GetStdio()->stdOut);
|
|
}
|
|
break;
|
|
case LV_BTN_MASK_UP:
|
|
if (keyData&(LV_MASK_PRESSED)) {
|
|
data->enc_diff = -1;
|
|
McuShell_SendStr((uint8_t*)"up pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->enc_diff = -1;
|
|
McuShell_SendStr((uint8_t*)"up long pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED_LONG)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"up long released\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"up released\r\n", McuShell_GetStdio()->stdOut);
|
|
}
|
|
break;
|
|
case LV_BTN_MASK_DOWN:
|
|
if (keyData&(LV_MASK_PRESSED)) {
|
|
data->enc_diff = 1;
|
|
McuShell_SendStr((uint8_t*)"down pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->enc_diff = 1;
|
|
McuShell_SendStr((uint8_t*)"down long pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED_LONG)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"down long released\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED)) {
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"down released\r\n", McuShell_GetStdio()->stdOut);
|
|
}
|
|
break;
|
|
case LV_BTN_MASK_CENTER:
|
|
if (keyData&(LV_MASK_PRESSED)) {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"center pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->state = LV_INDEV_STATE_PR;
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"center long pressed\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED_LONG)) {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"center long released\r\n", McuShell_GetStdio()->stdOut);
|
|
} else if (keyData&(LV_MASK_RELEASED)) {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
data->enc_diff = 0;
|
|
McuShell_SendStr((uint8_t*)"center released\r\n", McuShell_GetStdio()->stdOut);
|
|
}
|
|
break;
|
|
#if 0
|
|
case LV_BTN_MASK_NEXT:
|
|
if (keyData&(LV_MASK_PRESSED)) {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
data->enc_diff = 1;
|
|
} else if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->state = LV_INDEV_STATE_PR;
|
|
data->enc_diff = 0;
|
|
} else {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
data->enc_diff = 0;
|
|
}
|
|
break;
|
|
case LV_BTN_MASK_PREV:
|
|
if (keyData&(LV_MASK_PRESSED_LONG)) {
|
|
data->state = LV_INDEV_STATE_PR;
|
|
data->enc_diff = 0;
|
|
} else {
|
|
data->state = LV_INDEV_STATE_REL;
|
|
data->enc_diff = -1;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return false; /* error case? */
|
|
} /* switch */
|
|
return McuRB_NofElements(ringBufferHndl)!=0; /* return true if we have more data */
|
|
}
|
|
|
|
|
|
/* A static or global variable to store the buffers*/
|
|
static lv_disp_buf_t disp_buf;
|
|
|
|
/*Static or global buffer(s). The second buffer is optional*/
|
|
static lv_color_t buf_1[LV_CONFIG_DISPLAY_WIDTH * 10];
|
|
static lv_color_t buf_2[LV_CONFIG_DISPLAY_WIDTH * 10];
|
|
|
|
void LV_Init(void) {
|
|
lv_disp_drv_t disp_drv;
|
|
|
|
lv_init();
|
|
lv_disp_drv_init(&disp_drv);
|
|
|
|
lv_disp_buf_init(&disp_buf, buf_1, buf_2, LV_CONFIG_DISPLAY_WIDTH*10);
|
|
disp_drv.buffer = &disp_buf;
|
|
/*Set up the functions to access to your display*/
|
|
disp_drv.flush_cb = myDispFlush; /*Used in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
|
|
|
|
/*Finally register the driver*/
|
|
lv_disp_drv_register(&disp_drv);
|
|
|
|
/*************************
|
|
* Input device interface
|
|
*************************/
|
|
lv_indev_drv_t indev_drv; /*Descriptor of an input device driver*/
|
|
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
|
|
#if 0 /* touch pad */
|
|
indev_drv.type = LV_INDEV_TYPE_POINTER; /*The touchpad is pointer type device*/
|
|
indev_drv.read = ex_tp_read; /*Library ready your touchpad via this function*/
|
|
#elif 1
|
|
indev_drv.type = LV_INDEV_TYPE_ENCODER;
|
|
indev_drv.read_cb = encoder_read;
|
|
#else /* keyboard input */
|
|
indev_drv.type = LV_INDEV_TYPE_KEYPAD;
|
|
indev_drv.read = keyboard_read;
|
|
#endif
|
|
inputDevicePtr = lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
|
|
|
|
#if PL_CONFIG_USE_GUI_SCREENSAVER
|
|
timerHndlLcdTimeout = xTimerCreate(
|
|
"timerLCD", /* name */
|
|
pdMS_TO_TICKS(15*1000), /* period/time */
|
|
pdFALSE, /* auto reload */
|
|
(void*)1, /* timer ID */
|
|
vTimerCallbackLCDExpired); /* callback */
|
|
if (timerHndlLcdTimeout==NULL) {
|
|
for(;;); /* failure! */
|
|
}
|
|
if (xTimerStart(timerHndlLcdTimeout, 0)!=pdPASS) {
|
|
for(;;); /* failure!?! */
|
|
}
|
|
#endif
|
|
|
|
McuRB_Config_t config;
|
|
|
|
McuRB_GetDefaultconfig(&config);
|
|
config.elementSize = 2;
|
|
config.nofElements = 16;
|
|
ringBufferHndl = McuRB_InitRB(&config);
|
|
}
|
|
#endif /* PL_CONFIG_USE_GUI */
|
|
|