esp-iot-solution/components/gui/lvgl_gui/lvgl_gui.c

241 lines
8.2 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 interfaceInitialize 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);
}
}