378 lines
11 KiB
C
378 lines
11 KiB
C
/**
|
||
* @file lv_port_disp.c
|
||
*
|
||
*/
|
||
|
||
#if 1
|
||
|
||
/*********************
|
||
* INCLUDES
|
||
*********************/
|
||
#include "lv_port_disp.h"
|
||
#include "lisa_display.h"
|
||
#include <stdlib.h>
|
||
/*********************
|
||
* DEFINES
|
||
*********************/
|
||
|
||
/**********************
|
||
* TYPEDEFS
|
||
**********************/
|
||
|
||
/**********************
|
||
* STATIC PROTOTYPES
|
||
**********************/
|
||
static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||
|
||
/**********************
|
||
* STATIC VARIABLES
|
||
**********************/
|
||
lisa_device_t *lv_display_device = NULL;
|
||
|
||
/* Display buffers */
|
||
static lv_disp_draw_buf_t disp_buf;
|
||
static lv_color_t *buf_1;
|
||
|
||
#if CONFIG_LV_DOUBLE_VDB
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
#include "queue.h"
|
||
#include "semphr.h"
|
||
|
||
static lv_color_t *buf_2;
|
||
static QueueHandle_t flush_queue;
|
||
static TaskHandle_t flush_task_handle;
|
||
static SemaphoreHandle_t flush_sem; // Add semaphore handle
|
||
|
||
typedef struct {
|
||
lv_area_t area;
|
||
lv_color_t *color_p;
|
||
lv_disp_drv_t *disp_drv;
|
||
} flush_msg_t;
|
||
|
||
static void flush_thread(void *arg)
|
||
{
|
||
flush_msg_t msg;
|
||
lisa_display_buffer_desc_t desc;
|
||
|
||
while (1) {
|
||
if (xQueueReceive(flush_queue, &msg, portMAX_DELAY) == pdTRUE) {
|
||
desc.width = msg.area.x2 - msg.area.x1 + 1;
|
||
desc.height = msg.area.y2 - msg.area.y1 + 1;
|
||
desc.pitch = desc.width * sizeof(lv_color_t);
|
||
desc.buf_size = desc.height * desc.width * sizeof(lv_color_t);
|
||
|
||
lisa_display_write(lv_display_device, msg.area.x1, msg.area.y1, &desc, msg.color_p);
|
||
|
||
xSemaphoreGive(flush_sem);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#if CONFIG_LV_CUSTOM_MONITOR
|
||
#include "lisa_log.h"
|
||
#include "csk_monitor_lv.h"
|
||
|
||
volatile uint64_t g_draw_letter_time;
|
||
volatile uint32_t g_draw_letter_call_cnt;
|
||
|
||
volatile uint64_t g_draw_image_time;
|
||
volatile uint32_t g_draw_image_call_cnt;
|
||
|
||
volatile uint64_t g_draw_rect_time;
|
||
volatile uint32_t g_draw_rect_call_cnt;
|
||
|
||
static void monitor_cb(lv_disp_drv_t * drv, uint32_t time, uint32_t px)
|
||
{
|
||
LOGI("[%s] time:%dms px:%d", __func__, time, px);
|
||
|
||
LOGI("draw letter: cnt = %d, time = %dus", g_draw_letter_call_cnt, clk_2_us(g_draw_letter_time));
|
||
g_draw_letter_time = 0;
|
||
g_draw_letter_call_cnt = 0;
|
||
|
||
LOGI("draw image: cnt = %d, time = %dus", g_draw_image_call_cnt, clk_2_us(g_draw_image_time));
|
||
g_draw_image_time = 0;
|
||
g_draw_image_call_cnt = 0;
|
||
|
||
LOGI("draw rect: cnt = %d, time = %dus", g_draw_rect_call_cnt, clk_2_us(g_draw_rect_time));
|
||
g_draw_rect_time = 0;
|
||
g_draw_rect_call_cnt = 0;
|
||
|
||
#ifdef CONFIG_LV_USE_GPU_CSK_DMA2D
|
||
LOGI("dam2d fill: cnt = %d, time = %dus, dma_time = %dus", g_dma2d_fill_call_cnt, clk_2_us(g_dma2d_fill_total_time), clk_2_us(g_dma2d_fill_dma_time));
|
||
g_dma2d_fill_total_time = 0;
|
||
g_dma2d_fill_dma_time = 0;
|
||
g_dma2d_fill_call_cnt = 0;
|
||
|
||
LOGI("dam2d copy: cnt = %d, time = %dus, dma_time = %dus", g_dma2d_copy_call_cnt, clk_2_us(g_dma2d_copy_total_time), clk_2_us(g_dma2d_copy_dma_time));
|
||
g_dma2d_copy_total_time = 0;
|
||
g_dma2d_copy_dma_time = 0;
|
||
g_dma2d_copy_call_cnt = 0;
|
||
#endif
|
||
|
||
#ifdef CONFIG_LV_USE_RPC
|
||
LOGI("rpc fill normal : cnt = %d, time = %dus, proxy_time = %dus", g_rpc_fill_normal_call_cnt, clk_2_us(g_rpc_fill_normal_total_time), clk_2_us(g_rpc_fill_normal_proxy_time));
|
||
g_rpc_fill_normal_total_time = 0;
|
||
g_rpc_fill_normal_proxy_time = 0;
|
||
g_rpc_fill_normal_call_cnt = 0;
|
||
|
||
LOGI("rpc map normal: cnt = %d, time = %dus, proxy_time = %dus", g_rpc_map_normal_call_cnt, clk_2_us(g_rpc_map_normal_total_time), clk_2_us(g_rpc_map_normal_proxy_time));
|
||
g_rpc_map_normal_total_time = 0;
|
||
g_rpc_map_normal_proxy_time = 0;
|
||
g_rpc_map_normal_call_cnt = 0;
|
||
|
||
LOGI("rpc fill blended: cnt = %d, time = %dus, proxy_time = %dus", g_rpc_fill_blended_call_cnt, clk_2_us(g_rpc_fill_blended_total_time), clk_2_us(g_rpc_fill_blended_proxy_time));
|
||
g_rpc_fill_blended_total_time = 0;
|
||
g_rpc_fill_blended_proxy_time = 0;
|
||
g_rpc_fill_blended_call_cnt = 0;
|
||
|
||
LOGI("rpc map blended: cnt = %d, time = %dus, proxy_time = %dus", g_rpc_map_blended_call_cnt, clk_2_us(g_rpc_map_blended_total_time), clk_2_us(g_rpc_map_blended_proxy_time));
|
||
g_rpc_map_blended_total_time = 0;
|
||
g_rpc_map_blended_proxy_time = 0;
|
||
g_rpc_map_blended_call_cnt = 0;
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
#if CONFIG_LV_DRIVER_PIXEL_ALIGN_SIZE > 1
|
||
static void disp_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area)
|
||
{
|
||
area->y1 &= ~(CONFIG_LV_DRIVER_PIXEL_ALIGN_SIZE - 1);
|
||
area->y2 |= (CONFIG_LV_DRIVER_PIXEL_ALIGN_SIZE - 1);
|
||
area->x1 &= ~(CONFIG_LV_DRIVER_PIXEL_ALIGN_SIZE - 1);
|
||
area->x2 |= (CONFIG_LV_DRIVER_PIXEL_ALIGN_SIZE - 1);
|
||
|
||
/* Clamp to screen boundaries to prevent overflow */
|
||
lv_coord_t max_x, max_y;
|
||
if(disp_drv->rotated == LV_DISP_ROT_90 || disp_drv->rotated == LV_DISP_ROT_270) {
|
||
max_x = disp_drv->ver_res - 1;
|
||
max_y = disp_drv->hor_res - 1;
|
||
} else {
|
||
max_x = disp_drv->hor_res - 1;
|
||
max_y = disp_drv->ver_res - 1;
|
||
}
|
||
|
||
if(area->x2 > max_x) area->x2 = max_x;
|
||
if(area->y2 > max_y) area->y2 = max_y;
|
||
}
|
||
#endif
|
||
lisa_device_t *lv_port_get_display_device(void)
|
||
{
|
||
return lv_display_device;
|
||
}
|
||
|
||
void lv_port_disp_init(lisa_device_t *display_dev)
|
||
{
|
||
lisa_display_capabilities_t caps = {0};
|
||
|
||
/*-------------------------
|
||
* Initialize your display
|
||
* -----------------------*/
|
||
lv_display_device = display_dev;
|
||
if (lv_display_device == NULL) {
|
||
LV_LOG_ERROR("[%s] display device create faild", __FUNCTION__);
|
||
return;
|
||
}
|
||
|
||
lisa_display_get_capabilities(lv_display_device, &caps);
|
||
lisa_display_blanking_on(lv_display_device);
|
||
|
||
/*-----------------------------
|
||
* Create a buffer for drawing
|
||
*----------------------------*/
|
||
uint32_t size = caps.width * caps.height * sizeof(lv_color_t);
|
||
buf_1 = (lv_color_t *)lv_mem_alloc(size);
|
||
if (buf_1 == NULL) {
|
||
LV_LOG_ERROR("[%s] buf_1 malloc faild", __FUNCTION__);
|
||
return;
|
||
}
|
||
|
||
#if CONFIG_LV_DOUBLE_VDB
|
||
buf_2 = lv_mem_alloc(size);
|
||
if(buf_2 == NULL) {
|
||
LV_LOG_ERROR("[%s] lv_mem malloc faild", __FUNCTION__);
|
||
lv_mem_free(buf_1);
|
||
return;
|
||
}
|
||
|
||
/* Create message queue for flush commands */
|
||
flush_queue = xQueueCreate(2, sizeof(flush_msg_t));
|
||
if (flush_queue == NULL) {
|
||
LV_LOG_ERROR("[%s] Failed to create flush queue", __FUNCTION__);
|
||
lv_mem_free(buf_1);
|
||
lv_mem_free(buf_2);
|
||
return;
|
||
}
|
||
|
||
/* Create semaphore for flush synchronization */
|
||
flush_sem = xSemaphoreCreateBinary();
|
||
if (flush_sem == NULL) {
|
||
LV_LOG_ERROR("[%s] Failed to create flush semaphore", __FUNCTION__);
|
||
vQueueDelete(flush_queue);
|
||
lv_mem_free(buf_1);
|
||
lv_mem_free(buf_2);
|
||
return;
|
||
}
|
||
/* Initialize semaphore as available */
|
||
xSemaphoreGive(flush_sem);
|
||
|
||
/* Create flush thread */
|
||
BaseType_t ret = xTaskCreate(flush_thread, "lvgl_flush", 2048, NULL,
|
||
configMAX_PRIORITIES - 1, &flush_task_handle);
|
||
if (ret != pdPASS) {
|
||
LV_LOG_ERROR("[%s] Failed to create flush thread", __FUNCTION__);
|
||
vQueueDelete(flush_queue);
|
||
vSemaphoreDelete(flush_sem);
|
||
lv_mem_free(buf_1);
|
||
lv_mem_free(buf_2);
|
||
return;
|
||
}
|
||
|
||
lv_disp_draw_buf_init(&disp_buf, buf_1, buf_2, size / sizeof(lv_color_t));
|
||
#else
|
||
lv_disp_draw_buf_init(&disp_buf, buf_1, NULL, size / sizeof(lv_color_t));
|
||
#endif
|
||
|
||
#if 1
|
||
/*-----------------------------------
|
||
* Register the display in LVGL
|
||
*----------------------------------*/
|
||
static lv_disp_drv_t disp_drv;
|
||
lv_disp_drv_init(&disp_drv);
|
||
|
||
/*Set up the functions to access to your display*/
|
||
disp_drv.flush_cb = disp_flush;
|
||
|
||
#if CONFIG_LV_DRIVER_PIXEL_ALIGN_SIZE > 1
|
||
disp_drv.rounder_cb = disp_rounder;
|
||
#endif
|
||
|
||
#if CONFIG_LV_DRIVER_ROTATE_NORMAL
|
||
disp_drv.rotated = LV_DISP_ROT_NONE;
|
||
lisa_display_set_orientation(lv_display_device, LISA_DISPLAY_ORIENTATION_0);
|
||
#elif CONFIG_LV_DRIVER_ROTATE_90
|
||
disp_drv.rotated = LV_DISP_ROT_90;
|
||
lisa_display_set_orientation(lv_display_device, LISA_DISPLAY_ORIENTATION_90);
|
||
#elif CONFIG_LV_DRIVER_ROTATE_270
|
||
disp_drv.rotated = LV_DISP_ROT_270;
|
||
lisa_display_set_orientation(lv_display_device, LISA_DISPLAY_ORIENTATION_270);
|
||
#endif
|
||
|
||
#if CONFIG_LV_DRIVER_FULL_REFRESH
|
||
disp_drv.full_refresh = 1;
|
||
#endif
|
||
|
||
#if CONFIG_LV_CUSTOM_MONITOR
|
||
disp_drv.monitor_cb = monitor_cb;
|
||
#endif
|
||
|
||
/*Set the resolution of the display*/
|
||
disp_drv.hor_res = caps.width;
|
||
disp_drv.ver_res = caps.height;
|
||
|
||
/*Used to copy the buffer's content to the display*/
|
||
disp_drv.draw_buf = &disp_buf;
|
||
#if CONFIG_LV_COLOR_DEPTH_1
|
||
uint8_t *fb_buffer_mono = lv_mem_alloc(caps.width * caps.height / 8);
|
||
if (fb_buffer_mono == NULL) {
|
||
LV_LOG_ERROR("[%s] lv_mem malloc faild", __FUNCTION__);
|
||
} else {
|
||
disp_drv.user_data = fb_buffer_mono;
|
||
}
|
||
#endif
|
||
|
||
/*Finally register the driver*/
|
||
lv_disp_drv_register(&disp_drv);
|
||
#endif
|
||
}
|
||
|
||
#if CONFIG_LV_COLOR_DEPTH_1
|
||
|
||
int framebuffer_convert_to_mono(const void *src, void *dst, uint32_t width, uint32_t height)
|
||
{
|
||
const uint8_t *src_buf = (const uint8_t *)src;
|
||
uint8_t *dst_buf = (uint8_t *)dst;
|
||
|
||
if (src == NULL || dst == NULL || width == 0 || height == 0) {
|
||
return -1;
|
||
}
|
||
|
||
uint32_t src_pitch = width * sizeof(lv_color_t);
|
||
uint32_t dst_pitch = (width + 7) / 8; // 1bpp: 8个像素占1字节,向上取整
|
||
|
||
for (uint32_t y = 0; y < height; y++) {
|
||
for (uint32_t x = 0; x < width; x++) {
|
||
uint32_t src_idx = y * src_pitch + x * sizeof(lv_color_t);
|
||
uint32_t dst_idx = y * dst_pitch + x / 8;
|
||
uint8_t bit_pos = 7 - (x % 8);
|
||
|
||
lv_color_t color = *((lv_color_t *)(src_buf + src_idx));
|
||
if (lv_color_to1(color) == 0) {
|
||
dst_buf[dst_idx] &= ~(1 << bit_pos);
|
||
} else {
|
||
dst_buf[dst_idx] |= (1 << bit_pos);
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
#endif
|
||
|
||
/*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_disp_flush_ready()' has to be called when finished.*/
|
||
static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
|
||
{
|
||
#if CONFIG_LV_DOUBLE_VDB
|
||
flush_msg_t msg;
|
||
msg.area.x1 = area->x1;
|
||
msg.area.y1 = area->y1;
|
||
msg.area.x2 = area->x2;
|
||
msg.area.y2 = area->y2;
|
||
msg.disp_drv = disp_drv;
|
||
msg.color_p = color_p;
|
||
|
||
if (xSemaphoreTake(flush_sem, portMAX_DELAY) != pdTRUE) {
|
||
LV_LOG_ERROR("[%s] Failed to take flush semaphore", __FUNCTION__);
|
||
}
|
||
|
||
if (xQueueSend(flush_queue, &msg, portMAX_DELAY) != pdPASS) {
|
||
LV_LOG_ERROR("[%s] Failed to send flush message", __FUNCTION__);
|
||
}
|
||
|
||
lv_disp_flush_ready(msg.disp_drv);
|
||
#else
|
||
lisa_display_buffer_desc_t desc = {0};
|
||
/* Update descriptor for the current area */
|
||
desc.width = area->x2 - area->x1 + 1;
|
||
desc.height = area->y2 - area->y1 + 1;
|
||
desc.pitch = desc.width * sizeof(lv_color_t);
|
||
desc.buf_size = desc.height * desc.width * sizeof(lv_color_t);
|
||
|
||
#if CONFIG_LV_COLOR_DEPTH_1
|
||
uint8_t *fb_buffer_mono = (uint8_t *)disp_drv->user_data;
|
||
if (fb_buffer_mono == NULL) {
|
||
LV_LOG_ERROR("[%s] fb_buffer_mono is NULL", __FUNCTION__);
|
||
lv_disp_flush_ready(disp_drv);
|
||
return;
|
||
}
|
||
if (framebuffer_convert_to_mono(color_p, fb_buffer_mono, desc.width, desc.height) != 0) {
|
||
LV_LOG_ERROR("[%s] framebuffer_convert_to_mono faild", __FUNCTION__);
|
||
lv_disp_flush_ready(disp_drv);
|
||
return;
|
||
}
|
||
color_p = (lv_color_t *)fb_buffer_mono;
|
||
#endif
|
||
/* Write data to display */
|
||
lisa_display_write(lv_display_device, area->x1, area->y1, &desc, color_p);
|
||
|
||
/* IMPORTANT!!!
|
||
* Inform the graphics library that you are ready with the flushing*/
|
||
lv_disp_flush_ready(disp_drv);
|
||
#endif
|
||
}
|
||
#endif
|