241 lines
8.2 KiB
C
241 lines
8.2 KiB
C
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
/* FreeRTOS includes */
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/semphr.h"
|
||
#include "esp_log.h"
|
||
#include "esp_timer.h"
|
||
#include "lvgl_gui.h"
|
||
|
||
static const char *TAG = "lvgl_gui";
|
||
|
||
#define ESP_GOTO_ON_FALSE(a, err_code, goto_tag, log_tag, format, ...) do { \
|
||
if (unlikely(!(a))) { \
|
||
ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
|
||
ret = err_code; \
|
||
goto goto_tag; \
|
||
} \
|
||
} while (0)
|
||
|
||
/* Creates a semaphore to handle concurrent call to lvgl stuff
|
||
* If you wish to call *any* lvgl function from other threads/tasks
|
||
* you should lock on the very same semaphore! */
|
||
static SemaphoreHandle_t xGuiSemaphore = NULL;
|
||
static TaskHandle_t g_lvgl_task_handle;
|
||
|
||
#define LVGL_TICK_MS 5
|
||
|
||
static scr_driver_t lcd_obj;
|
||
static touch_panel_driver_t touch_obj;
|
||
|
||
static uint16_t g_screen_width = 240;
|
||
static uint16_t g_screen_height = 320;
|
||
|
||
/*Write the internal buffer (VDB) to the display. 'lv_flush_ready()' has to be called when finished*/
|
||
static void ex_disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||
{
|
||
lcd_obj.draw_bitmap(area->x1, area->y1, (uint16_t)(area->x2 - area->x1 + 1), (uint16_t)(area->y2 - area->y1 + 1), (uint16_t *)color_map);
|
||
|
||
/* IMPORTANT!!!
|
||
* Inform the graphics library that you are ready with the flushing*/
|
||
lv_disp_flush_ready(drv);
|
||
}
|
||
|
||
#define DISP_BUF_SIZE (g_screen_width * 64)
|
||
#define SIZE_TO_PIXEL(v) ((v) / sizeof(lv_color_t))
|
||
#define PIXEL_TO_SIZE(v) ((v) * sizeof(lv_color_t))
|
||
#define BUFFER_NUMBER (2)
|
||
|
||
static esp_err_t lvgl_display_init(scr_driver_t *driver)
|
||
{
|
||
if (NULL == driver) {
|
||
ESP_LOGE(TAG, "Pointer of lcd driver is invalid");
|
||
return ESP_ERR_INVALID_ARG;
|
||
}
|
||
|
||
lcd_obj = *driver;
|
||
scr_info_t info;
|
||
lcd_obj.get_info(&info);
|
||
g_screen_width = info.width;
|
||
g_screen_height = info.height;
|
||
|
||
lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
|
||
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
|
||
disp_drv.hor_res = g_screen_width;
|
||
disp_drv.ver_res = g_screen_height;
|
||
|
||
disp_drv.flush_cb = ex_disp_flush; /*Used in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
|
||
|
||
size_t free_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||
const size_t remain_size = 60 * 1024; /**< Remain for other functions */
|
||
size_t alloc_pixel = DISP_BUF_SIZE;
|
||
if (((BUFFER_NUMBER * PIXEL_TO_SIZE(alloc_pixel)) + remain_size) > free_size) {
|
||
size_t allow_size = (free_size - remain_size) & 0xfffffffc;
|
||
alloc_pixel = SIZE_TO_PIXEL(allow_size / BUFFER_NUMBER);
|
||
ESP_LOGW(TAG, "Exceeded max free size, force shrink to %u Byte", allow_size);
|
||
}
|
||
|
||
lv_color_t *buf1 = heap_caps_malloc(PIXEL_TO_SIZE(alloc_pixel), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||
if (NULL == buf1) {
|
||
ESP_LOGE(TAG, "Display buffer memory not enough");
|
||
return ESP_ERR_NO_MEM;
|
||
}
|
||
#if (BUFFER_NUMBER == 2)
|
||
lv_color_t *buf2 = heap_caps_malloc(PIXEL_TO_SIZE(alloc_pixel), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||
if (NULL == buf2) {
|
||
heap_caps_free(buf1);
|
||
ESP_LOGE(TAG, "Display buffer memory not enough");
|
||
return ESP_ERR_NO_MEM;
|
||
}
|
||
#endif
|
||
|
||
ESP_LOGI(TAG, "Alloc memory total size: %u Byte", BUFFER_NUMBER * PIXEL_TO_SIZE(alloc_pixel));
|
||
|
||
static lv_disp_buf_t disp_buf;
|
||
|
||
#if (BUFFER_NUMBER == 2)
|
||
lv_disp_buf_init(&disp_buf, buf1, buf2, alloc_pixel);
|
||
#else
|
||
lv_disp_buf_init(&disp_buf, buf1, NULL, alloc_pixel);
|
||
#endif
|
||
|
||
disp_drv.buffer = &disp_buf;
|
||
|
||
/* Finally register the driver */
|
||
lv_disp_drv_register(&disp_drv);
|
||
return ESP_OK;
|
||
}
|
||
|
||
/*Function pointer to read data. Return 'true' if there is still data to be read (buffered)*/
|
||
static bool ex_tp_read(struct _lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||
{
|
||
data->state = LV_INDEV_STATE_REL;
|
||
touch_panel_points_t points;
|
||
touch_obj.read_point_data(&points);
|
||
// please be sure that your touch driver every time return old (last clcked) value.
|
||
if (TOUCH_EVT_PRESS == points.event) {
|
||
int32_t x = points.curx[0];
|
||
int32_t y = points.cury[0];
|
||
data->point.x = x;
|
||
data->point.y = y;
|
||
data->state = LV_INDEV_STATE_PR;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Input device interface,Initialize your touchpad */
|
||
static esp_err_t lvgl_indev_init(touch_panel_driver_t *driver)
|
||
{
|
||
if (NULL == driver) {
|
||
ESP_LOGE(TAG, "Pointer of touch driver is invalid");
|
||
return ESP_ERR_INVALID_ARG;
|
||
}
|
||
|
||
touch_obj = *driver;
|
||
|
||
lv_indev_drv_t indev_drv; /*Descriptor of an input device driver*/
|
||
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
|
||
|
||
indev_drv.type = LV_INDEV_TYPE_POINTER; /*The touchpad is pointer type device*/
|
||
indev_drv.read_cb = ex_tp_read; /*Library ready your touchpad via this function*/
|
||
|
||
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
|
||
return ESP_OK;
|
||
}
|
||
|
||
static void lv_tick_timercb(void *timer)
|
||
{
|
||
lv_tick_inc(LVGL_TICK_MS);
|
||
}
|
||
|
||
static void gui_task(void *args)
|
||
{
|
||
ESP_LOGI(TAG, "Start to run LVGL");
|
||
while (1) {
|
||
vTaskDelay(pdMS_TO_TICKS(10));
|
||
|
||
/* Try to take the semaphore, call lvgl related function on success */
|
||
if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
|
||
lv_task_handler();
|
||
xSemaphoreGive(xGuiSemaphore);
|
||
}
|
||
}
|
||
}
|
||
|
||
esp_err_t lvgl_init(scr_driver_t *lcd_drv, touch_panel_driver_t *touch_drv)
|
||
{
|
||
esp_err_t ret;
|
||
esp_timer_handle_t tick_timer = NULL;
|
||
|
||
/* Initialize LittlevGL */
|
||
lv_init();
|
||
|
||
/* Display interface */
|
||
ret = lvgl_display_init(lcd_drv);
|
||
ESP_GOTO_ON_FALSE(ESP_OK == ret, ESP_FAIL, err, TAG, "lvgl display initialize failed");
|
||
|
||
#if defined(CONFIG_LVGL_DRIVER_TOUCH_SCREEN_ENABLE)
|
||
if (NULL != touch_drv) {
|
||
/* Input device interface */
|
||
ret = lvgl_indev_init(touch_drv);
|
||
ESP_GOTO_ON_FALSE(ESP_OK == ret, ESP_FAIL, err, TAG, "lvgl indev initialize failed");
|
||
}
|
||
#endif
|
||
|
||
esp_timer_create_args_t timer_conf = {
|
||
.callback = lv_tick_timercb,
|
||
.name = "lv_tick_timer"
|
||
};
|
||
ret = esp_timer_create(&timer_conf, &tick_timer);
|
||
ESP_GOTO_ON_FALSE(ESP_OK == ret, ESP_FAIL, err, TAG, "lvgl tick timer initialize failed");
|
||
|
||
xGuiSemaphore = xSemaphoreCreateMutex();
|
||
ESP_GOTO_ON_FALSE(NULL != xGuiSemaphore, ESP_FAIL, err, TAG, "Create mutex for LVGL failed");
|
||
|
||
#if CONFIG_FREERTOS_UNICORE == 0
|
||
int err = xTaskCreatePinnedToCore(gui_task, "lv gui", 1024 * 8, NULL, 5, &g_lvgl_task_handle, 1);
|
||
#else
|
||
int err = xTaskCreatePinnedToCore(gui_task, "lv gui", 1024 * 8, NULL, 5, &g_lvgl_task_handle, 0);
|
||
#endif
|
||
ESP_GOTO_ON_FALSE(pdPASS == err, ESP_FAIL, err, TAG, "Create task for LVGL failed");
|
||
|
||
esp_timer_start_periodic(tick_timer, LVGL_TICK_MS * 1000U);
|
||
return ESP_OK;
|
||
err:
|
||
if (xGuiSemaphore) {
|
||
vSemaphoreDelete(xGuiSemaphore);
|
||
}
|
||
if (tick_timer) {
|
||
esp_timer_delete(tick_timer);
|
||
}
|
||
|
||
return ESP_FAIL;
|
||
}
|
||
|
||
void lvgl_acquire(void)
|
||
{
|
||
TaskHandle_t task = xTaskGetCurrentTaskHandle();
|
||
if (g_lvgl_task_handle != task) {
|
||
xSemaphoreTake(xGuiSemaphore, portMAX_DELAY);
|
||
}
|
||
}
|
||
|
||
void lvgl_release(void)
|
||
{
|
||
TaskHandle_t task = xTaskGetCurrentTaskHandle();
|
||
if (g_lvgl_task_handle != task) {
|
||
xSemaphoreGive(xGuiSemaphore);
|
||
}
|
||
}
|