【前言】
LVGL是一个开源的、免费的图形库,旨在为嵌入式系统提供轻量级且功能丰富的图形用户界面(GUI)解决方案。他的移植有很多文章都有介绍,但是一些对接LCD的显示优化的文章较小,今天我在移植完lvgl正常显示后,对显示的刷新方法进行了优化。现记录并分享如下:
1、最原始的方法,就是直接调用LCD的画点函数,比如我的工程里面就是lcd_draw_point(x, y, color_p->full);其刷新代码如下:
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { if(disp_flush_enabled) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/ int32_t x; int32_t y; for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { /*Put a pixel to the display. For example:*/ /*put_px(x, y, *color_p)*/ lcd_draw_point(x, y, color_p->full); color_p++; } } } /*IMPORTANT!!! *Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); }
这样,功能是可以实现,但是显示速度是非常慢的。
2、为了改进刷新速度,我们可以先获取disp_flush函数中,传入的一个显示块,即获取起始x、y坐标,结束x、y坐标。在disp_flush函数的参数中的指针const lv_area_t * area,即包含了他的这些全部信息,他的结构化原型为:
/** Represents an area of the screen.*/ typedef struct { lv_coord_t x1; lv_coord_t y1; lv_coord_t x2; lv_coord_t y2; } lv_area_t;
这样我就可以得知他需要显示的区域是多少,使用LCD的设置坐标函数来设置他的显示区域即:
lcd_set_address(startX, startY, endX, endY);
3、颜色参数信息是在lv_color_t * color_p 中传递过来的,我们找到他的原型如下:
他的颜色始为full这里。
常规的方法是,把full拆开两个8bit,然后再使用spi发送出去,由于我们的spi是可以一次传输16bit数据的,因此我们可以使用uint16_t来组织数据,并使用spi 的16bit方式来传递出去,这样速度就会大大的增加。
这个在我的另一篇帖子里【分享开发笔记,赚取电动螺丝刀】STM32F769驱动ST7789以及显示优化-电子产品世界论坛有详细的记录。
根据这样,我对代码进行了改进:
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { if(disp_flush_enabled) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/ // int32_t x; // int32_t y; // for(y = area->y1; y <= area->y2; y++) { // for(x = area->x1; x <= area->x2; x++) { // /*Put a pixel to the display. For example:*/ // /*put_px(x, y, *color_p)*/ // lcd_draw_point(x, y, color_p->full); // color_p++; // } // } uint32_t width = area->x2 - area->x1 + 1; uint32_t height = area->y2 - area->y1 + 1; uint32_t pixel_count = width * height; // 确保缓冲区足够大 printf(" pixel_count = %ld\r\n", pixel_count); uint16_t *buffer = (uint16_t *)malloc(pixel_count); if (buffer == NULL) { free(buffer); printf("malloc failed\r\n"); return; } // 将像素数据复制到缓冲区 for (uint32_t i = 0; i < pixel_count; i++){ buffer[i] = color_p[i].full; } lcd_draw_block(area->x1, area->y1, area->x2, area->y2, buffer, pixel_count); free(buffer); } /*IMPORTANT!!! *Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); }
在最初,我对通过x、y的起始坐标,计算出这一次刷新数据的大小,并申请内存。然后对color数据进行组装,最后通过lcd_draw_block转发出去。
在lcd中,我组织函数代码如下:
void lcd_draw_block(uint16_t startX, uint16_t startY, uint16_t endX, uint16_t endY, uint16_t *color_buffer, uint32_t size) { lcd_set_address(startX, startY, endX, endY); lcd_write_ram(); lcd_write_data_dma(color_buffer, size*2); //长度需要*2 不然dma写入不完整 }
对应的lcd_write_data_dma原型如下:
void lcd_write_data_dma(uint16_t *data, uint16_t size) { // 配置 SPI 为 16 位数据大小 hspi2.Init.DataSize = SPI_DATASIZE_16BIT; if (HAL_SPI_Init(&hspi2) != HAL_OK) { printf("SPI re-init failed for 16-bit mode\r\n"); return; } LCD_CS(0); LCD_WR(1); // 使用 DMA 发送数据 if (HAL_SPI_Transmit_DMA(&hspi2, (uint8_t *)data, size) != HAL_OK) { printf("DMA transmission failed\r\n"); } // 等待DMA传输完成 // while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY) // ; while(spi2_dma_tx_flag == 0) ; spi2_dma_tx_flag = 0; //重置标志 // 配置 SPI 为 8 位数据大小 hspi2.Init.DataSize = SPI_DATASIZE_8BIT; if (HAL_SPI_Init(&hspi2) != HAL_OK) { printf("SPI re-init failed for 8-bit mode\r\n"); } }
在这里,由先把数据传输的宽度修改为16bit,传送结束后,再换成8bit。
【总结】
通过上述代码的优化,提高了数据的传速速度,从而提高了刷新速度。