准备
基于样例的c工程进行
git clone https://github.com/lvgl/lvgl.git
下载代码到RP2040-LCD-1.28\c\lib目录下
移植过程
配置文件lv_conf.h
复制
lvgl/lv_conf_template.h
改名字为lv_conf.h
将#if 0改为1,即直接#include “lv_conf.h”
将lv_conf.h添加到工程头文件包含路径。
底层驱动模板
lvgl/examples/porting
下有对应的模板文件,分别是显示,文件系统和输入设备的驱动模板。
lv_port_disp_template.c/h
lv_port_fs_template.c/h
lv_port_indev_template.c/h
暂时只移植显示,所以复制lv_port_disp_template.c/h到RP2040-LCD-1.28\c
改名字为
lv_port_disp.c/h
将.c和.h里面的#if 0改为1
.c中#include "lv_port_disp_template.h"改为#include "lv_port_disp.h"
HAL层模板
lvgl/src/hal
下
我们直接使用不修改
lv_hal_disp.c/h
lv_hal_indev.c/h
lv_hal_tick.c/h
lv_hal.h
添加文件
如下
RP2040-LCD-1.28/c/CMakeLists.txt中
add_subdirectory(./lib/lvgl)
添加lvgl的源码
然后调用RP2040-LCD-1.28/c/lib/lvgl/CMakeLists.txt
最终调用
RP2040-LCD-1.28/c/lib/lvgl/env_support/cmake/custom.cmake
需要移植的代码
移植比较简单,直接使用底层驱动模板根据实际实现修改lv_port_disp.c,并配置lv_conf.h即可。
头文件包含模式
需要在工程中定义宏
#define LV_LVGL_H_INCLUDE_SIMPLE 1
这样需要将lvgl.h所在路径配置为工程头文件包含路径。
代码中直接#include "lvgl.h"即可
否则是#include "../../lvgl.h"
lib/lvgl/env_support/cmake/custom.cmake中
该宏默认是使能的
option(LV_LVGL_H_INCLUDE_SIMPLE
"Use #include \"lvgl.h\" instead of #include \"../../lvgl.h\"" ON)
分辨率配置
lv_port_disp.h中
#define MY_DISP_HOR_RES 240
#define MY_DISP_VER_RES 240
初始化
lv_port_disp.c中
实现disp_init
即调用自己的初始化函数,如果在其他地方初始化了,该函数实现为空函数体即可。
缓冲区
lv_port_disp.c中
lv_port_disp_init
注释掉/* Example for 2) */
/* Example for 3) also set disp_drv.full_refresh = 1 below*/对应的代码
使用/* Example for 1) */
lv_port_disp_init
该函数调用disp_init实现初始化
刷新显示
lv_port_disp.c中包含#include "LCD_1in28.h"
disp_flush
/*put_px(x, y, color_p)/改为
lcd_draw_point(x,y,color_p->full);
并调用LcdPush();
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*/
volatile int32_t x;
volatile int32_t y;
printf("%d %d %d %d\r\n",area->y1,area->y2,area->x1,area->x2);
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++;
}
}
LcdPush();
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
lib/LCD/LCD_1in28.c中实现
static uint16_t s_disbuffer_au16[LCD_1IN28_WIDTH][LCD_1IN28_HEIGHT];
void lcd_draw_point(int32_t x,int32_t y,uint16_t color)
{
s_disbuffer_au16[y][x] = (color>>8) | ((color&0xFF)<<8);
}
void LcdPush(void)
{
LCD_1IN28_SetWindows(0, 0, LCD_1IN28_WIDTH, LCD_1IN28_HEIGHT);
DEV_Digital_Write(LCD_DC_PIN, 1);
spi_write_blocking(SPI_PORT, (uint8_t *)&s_disbuffer_au16[0][0], LCD_1IN28_HEIGHT*LCD_1IN28_WIDTH*2);
///UWORD j;
///for(j = 0; j < LCD_1IN28_HEIGHT; j++){
// DEV_SPI_Write_nByte((uint8_t *)&s_disbuffer_au16[j*LCD_1IN28_WIDTH], LCD_1IN28_WIDTH*2);
//}
}
lib/LCD/LCD_1in28.h中申明
void lcd_draw_point(int32_t x,int32_t y,uint16_t color);
void LcdPush(void);
颜色深度
lv_conf.h中
#define LV_COLOR_DEPTH 16
typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t;
则lv_color_t类型为lv_color_16_t
堆大小配置
lv_conf.h中
#define LV_MEM_SIZE (10U * 1024U) /* [bytes] */
按需提供堆大小,过大可能编译不过,过小可能影响创建对象。
时间滴答
如果在lv_conf.h中指定LV_TICK_CUSTOM为1则需要用户提供相关接口
LV_TICK_CUSTOM_SYS_TIME_EXPR用于获取当前毫秒值
和头文件LV_TICK_CUSTOM_INCLUDE
否则使用lvgl/src/hal/lv_hal_tick.c的实现
每隔x毫秒调用lv_tick_inc(x),使用内部计数器定时。
周期调用lv_tick_inc更新时间滴答,比如专门在某个定时线程中
bool timer_callback(repeating_timer_t *rt)
{
lv_tick_inc(5);
}
add_repeating_timer_ms(5,timer_callback,0,0);
日志
lv_conf.h中
#define LV_USE_LOG 0
改为
#define LV_USE_LOG 1使能日志
#define LV_LOG_LEVEL LV_LOG_LEVEL_TRACE设置日志,等级
LV_LOG_LEVEL_TRACE表示所有信息都打印
如果#define LV_LOG_PRINTF 0
则需要调用设置lv_log_register_print_cb()打印函数
否则使用printf
#define LV_LOG_USE_TIMESTAMP 1
使能打印时间
其他的模块打印使能
字体配置
lv_conf.h
中按需使能对应的字体,如果有对应编译错误信息根据提示使能
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
工程配置
RP2040-LCD-1.28\c\CMakeLists.txt中
添加
add_subdirectory(./lib/lvgl)
include_directories(./lib/lvgl)
include_directories(.)
add_definitions(-DLV_LVGL_H_INCLUDE_SIMPLE=1)
add_executable(main
main.c lv_port_disp.c
)
target_link_libraries(main examples lvgl LCD QMI8658 GUI Fonts Config pico_stdlib hardware_spi hardware_i2c )
examples/CMakeLists.txt中
include_directories(../lib/lvgl)
add_definitions(-DLV_LVGL_H_INCLUDE_SIMPLE=1)
测试
创建一个按钮
examples/LCD_1in28_test.c
#include "LCD_Test.h"
#include "LCD_1in28.h"
#include "QMI8658.h"
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/adc.h"
#include "draw.h"
#include "lvgl.h"
#include "lv_port_disp.h"
bool timer_callback(repeating_timer_t *rt)
{
lv_tick_inc(5);
///printf("%lld\r\n",time_us_64());
}
static void btn_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;
/*Get the first child of the button which is the label and change its text*/
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
/**
* Create a button with a label and react on click event.
*/
void lv_example_get_started_1(void)
{
lv_color_t color;
color.full=(uint16_t)0xF800;
lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/
lv_obj_set_style_text_font(btn, &lv_font_montserrat_24 ,0);
lv_obj_set_style_text_color(btn,color,0);
lv_obj_set_pos(btn, 120-100, 120-50); /*Set its position*/
lv_obj_set_size(btn, 200, 100); /*Set its size*/
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/
lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/
lv_label_set_text(label, "Hello LVGL"); /*Set the labels text*/
lv_obj_center(label);
}
struct repeating_timer timer;
int LCD_1in28_test(void)
{
if (DEV_Module_Init() != 0)
{
return -1;
}
adc_init();
adc_gpio_init(29);
adc_select_input(3);
LCD_1IN28_Init(VERTICAL);
LCD_1IN28_Clear(WHITE);
DEV_SET_PWM(60);
add_repeating_timer_ms(5,timer_callback,0,&timer);
///sleep_ms(5000);
lv_init();
lv_port_disp_init();
lv_example_get_started_1();
while(1)
{
sleep_us(1000); /* 必须又sleep 否则定时器不会回调 */
lv_task_handler();
}
DEV_Module_Exit();
return 0;
}
效果如下
镜像处理
写点函数交换xy即可
颜色处理
16位565模式,高低字节需要交换下
void lcd_draw_point(int32_t x,int32_t y,uint16_t color)
{
s_disbuffer_au16[y][x] = (color>>8) | ((color&0xFF)<<8);
}
编译
cd build
export PICO_SDK_PATH="/home/lhj/pico-setup/pico/pico-sdk" && cmake ..
Make
不编译demo,example
export PICO_SDK_PATH="/home/lhj/pico-setup/pico/pico-sdk" && cmake -DLV_CONF_BUILD_DISABLE_EXAMPLES=1 -DLV_CONF_BUILD_DISABLE_DEMOS=1 ..
坑
不要用dor循环书信屏幕,直接调用spi_write_blocking
for循环编译器会优化导致逻辑错误,可能并不会按照循环次数循环,而是卡死。
void LcdPush(void)
{
LCD_1IN28_SetWindows(0, 0, LCD_1IN28_WIDTH, LCD_1IN28_HEIGHT);
DEV_Digital_Write(LCD_DC_PIN, 1);
spi_write_blocking(SPI_PORT, (uint8_t *)&s_disbuffer_au16[0][0], LCD_1IN28_HEIGHT*LCD_1IN28_WIDTH*2);
///UWORD j;
///for(j = 0; j < LCD_1IN28_HEIGHT; j++){
// DEV_SPI_Write_nByte((uint8_t *)&s_disbuffer_au16[j*LCD_1IN28_WIDTH], LCD_1IN28_WIDTH*2);
//}
}
参考
https://lvgl.io/