-
Notifications
You must be signed in to change notification settings - Fork 0
sdk例程:st7789屏移植lvgl_v82,以及按键模拟touch功能
minichao9901 edited this page Jun 4, 2024
·
5 revisions
- 这个例程,重点讲解lvgl_v82的裁剪和移植。例程基本可以参考前面的一个例程。
- lvgl_v82,与前面版还是有些差别的。
- 所需资源:lvgl到官网下载,定时器采用的是小梅哥的库,按键key_scan()采用的原子的程序,gpio读写采用的是小梅哥的库
- 登录官网,点击github,下载lvgl v8.2
https://lvgl.io/
https://github.com/lvgl/lvgl
-
裁剪:这保留这5样
-
拷贝到项目下,将lv_conf_template.h放到上层,与main.c一个层,改名为lv_conf.h
-
相关的头文件添加到main.c中
#include "ACZ702_Lib/COMMON.h"
#include "lvgl/lvgl.h"
#include "lvgl/examples/porting/lv_port_disp_template.h"
#include "lvgl/examples/porting/lv_port_indev_template.h"
#include "lvgl/demos/lv_demos.h"
-
开启宏 lv_port_disp_template.c/.h的启用宏=1 lv_port_indev_template.c/.h的启用宏=1
-
配置lv_conf.h文件,颜色深度要确认
-
移植lv_port_disp_template.c文件。
-
调用一个demo(注意刚开始移植的时候,不要把所有的demo都拷贝过来,那样编译太慢了。只拷贝一个过来)。注意demo的宏编译开关在lv_conf.h的最末尾。
-
然后编译跑通,把显示调好。
-
移植lv_port_indev_template.c文件。
-
然后编译跑通,把触摸调好。
-
拷贝更多的demo过来,一个个的demo看效果。
#include "ACZ702_Lib/COMMON.h"
#include "lvgl/lvgl.h"
#include "lvgl/examples/porting/lv_port_disp_template.h"
#include "lvgl/examples/porting/lv_port_indev_template.h"
#include "lvgl/demos/lv_demos.h"
//GPIO2, V1扩展板
#define PS_DCX (54+0)
#define PS_RSTN (54+1)
#define PL_DCX (54+2)
#define PL_RSTN (54+3)
//GPIO1,小梅哥扩展板
#define PS_KEY1 (54+4)
#define PS_KEY2 (54+5)
#define PS_KEY3 (54+6)
#define PS_KEY4 (54+7)
#define PS_KEY5 (54+8)
int main_ps_emio(void)
{
PS_GPIO_Init(); //初始化PS端MIO和EMIO
PS_GPIO_SetMode(PS_DCX, OUTPUT, 1);
PS_GPIO_SetMode(PS_RSTN, OUTPUT, 1);
PS_GPIO_SetMode(PL_DCX, OUTPUT, 1);
PS_GPIO_SetMode(PL_RSTN, OUTPUT, 1);
PS_GPIO_SetMode(PS_KEY1, INPUT, 1);
PS_GPIO_SetMode(PS_KEY2, INPUT, 1);
PS_GPIO_SetMode(PS_KEY3, INPUT, 1);
PS_GPIO_SetMode(PS_KEY4, INPUT, 1);
PS_GPIO_SetMode(PS_KEY5, INPUT, 1);
}
#define KEY1_PRES 1 //KEY1按下后返回值
#define KEY2_PRES 2 //KEY2按下后返回值
#define KEY3_PRES 3 //KEY3按下后返回值
#define KEY4_PRES 4 //KEY4按下后返回值
#define KEY5_PRES 5 //KEY5按下后返回值
#define KEY1 PS_GPIO_GetPort(PS_KEY1)
#define KEY2 PS_GPIO_GetPort(PS_KEY2)
#define KEY3 PS_GPIO_GetPort(PS_KEY3)
#define KEY4 PS_GPIO_GetPort(PS_KEY4)
#define KEY5 PS_GPIO_GetPort(PS_KEY5)
u8 KEY_Scan(u8 mode)
{
static u8 key_up = 1; // 按键松开标志
if (mode == 1)
key_up = 1; // 支持连按
if (key_up && (KEY1 == 0 || KEY2 == 0 || KEY3 == 0 || KEY4 == 0 || KEY5 == 0))
{
usleep(50 * 1000);
key_up = 0;
if (KEY1 == 0)
return KEY1_PRES;
else if (KEY2 == 0)
return KEY2_PRES;
else if (KEY3 == 0)
return KEY3_PRES;
else if (KEY4 == 0)
return KEY4_PRES;
else if (KEY5 == 0)
return KEY5_PRES;
}
else if (KEY1 == 1 && KEY2 == 1 && KEY3 == 1 && KEY4 == 1 && KEY5 == 1)
key_up = 1;
return 0; // 无按键按下
}
void st7789_rst()
{
PS_GPIO_SetPort(PL_RSTN,0);
usleep(100*1000);
PS_GPIO_SetPort(PL_RSTN,1);
usleep(100*1000);
}
void write_command(u8 cmd)
{
PS_GPIO_SetPort(PL_DCX,0);
AXI_SPI_Transfer(&AXI_SPI0, 0, NULL, &cmd, 1);
}
void write_command_data(u8 cmd, u8 *pdata, u32 length)
{
PS_GPIO_SetPort(PL_DCX,0);
AXI_SPI_Transfer(&AXI_SPI0, 0, NULL, &cmd, 1);
usleep(1);
PS_GPIO_SetPort(PL_DCX,1);
AXI_SPI_Transfer(&AXI_SPI0, 0, NULL, pdata, length);
}
void write_data(u8 *pdata, u32 length)
{
PS_GPIO_SetPort(PL_DCX,1);
AXI_SPI_Transfer(&AXI_SPI0, 0, NULL, pdata, length);
}
void lcd_init()
{
write_command(0x11);
usleep(100*1000);
write_command(0x21);
u8 data=0x55;
write_command_data(0x3a, &data, 1);
// data=0x8;
// write_command_data(0x36, &data, 1);
write_command(0x29);
}
#define LCD_X_SIZE 240
#define LCD_Y_SIZE 240
void lcdqspi_fill_block(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t color)
{
u8 xwin_list[]={(x0>>8)&0xff, x0&0xff, (x1>>8)&0xff, x1&0xff};
u8 ywin_list[]={(y0>>8)&0xff, y0&0xff, (y1>>8)&0xff, y1&0xff};
write_command_data(0x2a, xwin_list, 4);
write_command_data(0x2b, ywin_list, 4);
u32 length=(x1-x0)*(y1-y0);
write_command(0x2c);
while(length--){
u8 color_list[]={(color>>8)&0xff, color&0xff};
write_data(color_list,2);
}
}
void lcdqspi_clear(uint32_t color)
{
lcdqspi_fill_block(0, 0, LCD_X_SIZE - 1, LCD_Y_SIZE - 1, color);
}
u8 frame_cache[240*240*2];
void lcdqspi_fill_pcolor(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint16_t *pcolor)
{
u8 xwin_list[]={(x0>>8)&0xff, x0&0xff, (x1>>8)&0xff, x1&0xff};
u8 ywin_list[]={(y0>>8)&0xff, y0&0xff, (y1>>8)&0xff, y1&0xff};
write_command_data(0x2a, xwin_list, 4);
write_command_data(0x2b, ywin_list, 4);
u32 length=(x1-x0)*(y1-y0);
write_command(0x2c);
while(length--){
u8 color_list[]={(*pcolor>>8)&0xff, *pcolor&0xff};
write_data(color_list,2);
pcolor++;
}
// u32 remain=length;
// u32 BLOCK_SIZE=2;
// while(remain>=BLOCK_SIZE){
// u32 index=0;
// for(int i=0; i<BLOCK_SIZE; i++){
// frame_cache[index++]=(*pcolor>>8)&0xff;
// frame_cache[index++]=*pcolor&0xff;
// pcolor++;
//
// write_data(frame_cache,BLOCK_SIZE*2);
// }
// remain-=BLOCK_SIZE;
// index+=BLOCK_SIZE*2;
// }
//
// if(remain>0){
// u32 index=0;
// for(int i=0; i<remain; i++){
// frame_cache[index++]=(*pcolor>>8)&0xff;
// frame_cache[index++]=*pcolor&0xff;
// pcolor++;
//
// write_data(frame_cache,remain*2);
// }
// }
}
u16 frame_buff[240][240];
void fill_color(u16 color){
for(int i=0; i<240; i++){
for(int j=0; j<240; j++){
frame_buff[i][j]=color;
}
}
}
void ScuTimer_IRQ_Handler(void *CallBackRef)
{
/* ↓↓↓用户处理↓↓↓ */
lv_tick_inc(1);
/* ↑↑↑结束处理↑↑↑ */
XScuTimer_ClearInterruptStatus(&ScuTimer);
}
int main(void)
{
Xil_DCacheDisable();
/* 私有定时器初始化 */
ScuGic_Init();
ScuTimer_Int_Init(1000);
//初始化SPI0,设为主机模式,64分频
AXI_SPI_Init(&AXI_SPI0, XPAR_SPI_0_DEVICE_ID, XSP_MASTER_OPTION|XSP_CLK_ACTIVE_LOW_OPTION|XSP_CLK_PHASE_1_OPTION);
main_ps_emio();
st7789_rst();
lcd_init();
/* lvgl初始化 */
lv_init();
lv_port_disp_init();
lv_port_indev_init();
/* 创建demo */
//lv_demo_printer();
//lv_demo_keypad_encoder();
lv_demo_widgets();
//lv_demo_stress();
//lv_demo_benchmark();
/* 死循环 */
for ( ; ; ) {
lv_task_handler();
usleep(5 * 1000);
}
return 0;
}
/**
* @file lv_port_disp_templ.c
*
*/
/*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_disp_template.h"
#include "../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void disp_init(void);
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
// const lv_area_t * fill_area, lv_color_t color);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define MY_DISP_HOR_RES 240
#define MY_DISP_VER_RES 240
#define LV_VER_RES_MAX 240
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/* Example for 1) */
// static lv_disp_draw_buf_t draw_buf_dsc_1;
// static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
// /* Example for 2) */
// static lv_disp_draw_buf_t draw_buf_dsc_2;
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 3) also set disp_drv.full_refresh = 1 below*/
static lv_disp_draw_buf_t draw_buf_dsc_3;
static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = 240;
disp_drv.ver_res = 240;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_3;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
/*You code here*/
}
/*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)
{
/*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)*/
// color_p++;
// }
// }
xil_printf("x0=%d, x1=%d, y0=%d, y1=%d\r\n", area->x1,area->x2,area->y1,area->y2);
extern void lcdqspi_fill_pcolor(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint16_t *pcolor);
lcdqspi_fill_pcolor(area->x1,area->y1,area->x2,area->y2, color_p);
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
/*OPTIONAL: GPU INTERFACE*/
/*If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color*/
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
// const lv_area_t * fill_area, lv_color_t color)
//{
// /*It's an example code which should be done by your GPU*/
// int32_t x, y;
// dest_buf += dest_width * fill_area->y1; /*Go to the first line*/
//
// for(y = fill_area->y1; y <= fill_area->y2; y++) {
// for(x = fill_area->x1; x <= fill_area->x2; x++) {
// dest_buf[x] = color;
// }
// dest_buf+=dest_width; /*Go to the next line*/
// }
//}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
/**
* @file lv_port_indev_templ.c
*
*/
/*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_indev_template.h"
#include "../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
/**********************
* STATIC VARIABLES
**********************/
lv_indev_t * indev_touchpad;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_indev_init(void)
{
/**
* Here you will find example implementation of input devices supported by LittelvGL:
* - Touchpad
* - Mouse (with cursor support)
* - Keypad (supports GUI usage only with key)
* - Encoder (supports GUI usage only with: left, right, push)
* - Button (external buttons to press points on the screen)
*
* The `..._read()` function are only examples.
* You should shape them according to your hardware
*/
static lv_indev_drv_t indev_drv;
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad if you have*/
touchpad_init();
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad*/
static void touchpad_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*Save the pressed coordinates and the state*/
if(touchpad_is_pressed()) {
touchpad_get_xy(&last_x, &last_y);
data->state = LV_INDEV_STATE_PR;
} else {
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
//xil_printf("touch state=%d, x=%d, y=%d\r\n", data->state, last_x, last_y);
data->point.x = last_x;
data->point.y = last_y;
}
static uint8_t key;
extern uint8_t KEY_Scan(uint8_t mode);
/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
key = KEY_Scan(0);
if (key != 0)
{
return true;
}
return false;
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
switch (key)
{
case 1:
(*x) = 20, (*y) = 20; break;
case 2:
(*x) = 60, (*y) = 20; break;
case 3:
(*x) = 120, (*y) = 20; break;
case 4:
(*x) = 180, (*y) = 20; break;
case 5:
(*x) = 230, (*y) = 20; break;
}
}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
对于spi这种接口,rgb565数据需要msb/lsb交换后才能发送,很麻烦。对此lvgl支持LV_COLOR_16_SWAP,非常贴心。当开启后,可以大大简化lcdqspi_fill_pcolor函数的实现,不需要软件手动交换了。这样可以直接用spi直接发送。不过令人遗憾的是,由于AXI QuadSpi block传输性能实在太差(每2个block传输之间的等待时间,与block的长度成正比,导致实际几乎没有提示速率。但这并不是lvgl的问题,是AXI QuadSpi的问题。
这里的地址,就是前面设置的lvgl的内存地址,这里的size就是指定的lvgl的容量 这样设置后,在st77916上,速度大幅度提高。