/*
ESP32 Composite Video Library
Copyright (C) 2022 aquaticus
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
x1); for(int x = area->x1; x <= area->x2; x+=4) { pixel_data = *((uint8_t*)color_p); color_p++; pixel_data |= *((uint8_t*)color_p) << 8; color_p++; pixel_data |= *((uint8_t*)color_p) << 16; color_p++; pixel_data |= *((uint8_t*)color_p) << 24; color_p++; *dest = pixel_data; dest++; } #elif LV_COLOR_DEPTH==16 uint32_t* dest = (uint32_t*)(video_get_frame_buffer_address()+y*video_get_width()*2+area->x1*2); for(int x = area->x1; x <= area->x2; x+=2) { pixel_data = *((uint16_t*)color_p); color_p++; pixel_data |= *((uint16_t*)color_p) << 16; color_p++; *dest = pixel_data; dest++; } #endif } lv_disp_flush_ready(disp_drv); } #if CONFIG_LVGL_STATS void composite_monitor_cb(lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px) { static uint32_t min=0xFFFFFFFF, max, count, avg; if( time < min ) min = time; if( time > max ) max = time; avg += time; count++; const int64_t period = 1000*1000; //us static int64_t prev = 0; int64_t now = esp_timer_get_time(); if( now - prev > period ) { prev = now; ESP_LOGD(TAG, "LVGL AVG: %u, MIN: %u, MAX: %u [ms]", avg/count, min, max); count = avg = max = 0; min = 0xFFFFFFFF; } } #endif static void lv_tick_task(void* arg) { lv_tick_inc(LV_TICK_PERIOD_MS); } /** * @brief Initializes LVGL to work directly with framebuffer. * This mode does not use an extra memory for buffer but may degrade animations. * Tearing effect may be visible. * * @param mode graphics mode, resolution and TV system PAL or NTSC. * * @note This function calls \a lv_video_disp_init_buf() * * @sa lv_video_disp_init_buf() */ void lv_video_disp_init(GRAPHICS_MODE mode) { lv_video_disp_init_buf(mode, NULL, 0); } /** * @brief Initialize LVGL to use auxilary buffer. * Using additional buffer improves quality of animations. * If \a pixel_buffer is NULL LVGL will access framebuffer directly. * * @param mode graphics mode, resolution and TV system PAL or NTSC. * @param pixel_buffer pointer to auxilary buffer. LVGL uses it to refresh a screen. If set to NULL LVGL access directly framebuffer. * @param buffer_pixel_count size of \a pixel_buffer for in pixels. * * @sa lv_video_disp_init() */ void lv_video_disp_init_buf(GRAPHICS_MODE mode, lv_color_t* pixel_buffer, uint32_t buffer_pixel_count) { ESP_LOGI(TAG, "LVGL Init. Color depth %d", LV_COLOR_DEPTH); FRAME_BUFFER_FORMAT fb_format; #if LV_COLOR_DEPTH==1 if( NULL == g_lvgl_aux_buf ) { // Framebuffer compatible with LVGL 1 pixel/byte fb_format = FB_FORMAT_LVGL_1BPP; } else { // More memory efficient format 8 pixels/byte but needs buffers fb_format = FB_FORMAT_GREY_1BPP; } #elif LV_COLOR_DEPTH==8 fb_format = FB_FORMAT_RGB_8BPP; #elif LV_COLOR_DEPTH==16 fb_format = FB_FORMAT_RGB_16BPP; #else #pragma GCC error "Not supported LVGL color depth" #endif video_graphics(mode, fb_format); lv_init(); lv_disp_drv_init(&disp_drv); if( NULL == pixel_buffer) { ESP_LOGD(TAG, "LVGL updates framebuffer directly"); lv_disp_draw_buf_init(&disp_buf, video_get_frame_buffer_address(), NULL, video_get_width()*video_get_height()); disp_drv.direct_mode = 1; // Draw directly to framebuffer disp_drv.flush_cb = composite_dummy_flush_cb; g_lvgl_aux_buf = NULL; } else { ESP_LOGD(TAG, "LVGL updates using buffer"); lv_disp_draw_buf_init(&disp_buf, pixel_buffer, NULL, buffer_pixel_count); disp_drv.flush_cb = composite_buffer_flush_cb; disp_drv.rounder_cb = composite_rounder_cb; g_lvgl_aux_buf = pixel_buffer; } disp_drv.draw_buf = &disp_buf; assert(disp_drv.draw_buf); disp_drv.draw_buf = &disp_buf; disp_drv.hor_res = video_get_width(); disp_drv.ver_res = video_get_height(); #if CONFIG_LVGL_STATS disp_drv.monitor_cb = composite_monitor_cb; #endif lv_disp_drv_register(&disp_drv); const esp_timer_create_args_t periodic_timer_args = { .callback = &lv_tick_task, .name = "lvgl_gui" }; 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, LV_TICK_PERIOD_MS * 1000)); } #endif