sdk例程:st7789屏移植lvgl_v82,以及按键模拟touch功能 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

说明

  • 这个例程,重点讲解lvgl_v82的裁剪和移植。例程基本可以参考前面的一个例程。
  • lvgl_v82,与前面版还是有些差别的。
  • 所需资源:lvgl到官网下载,定时器采用的是小梅哥的库,按键key_scan()采用的原子的程序,gpio读写采用的是小梅哥的库

移植过程

image

  • 裁剪:这保留这5样 image

  • 拷贝到项目下,将lv_conf_template.h放到上层,与main.c一个层,改名为lv_conf.h image

  • 相关的头文件添加到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看效果。

关键的程序源码

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"

//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;
}

lv_port_disp_template.c

/**
 * @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

lv_port_indev_template.c

/**
 * @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

补充说明

LV_COLOR_16_SWAP

对于spi这种接口,rgb565数据需要msb/lsb交换后才能发送,很麻烦。对此lvgl支持LV_COLOR_16_SWAP,非常贴心。当开启后,可以大大简化lcdqspi_fill_pcolor函数的实现,不需要软件手动交换了。这样可以直接用spi直接发送。不过令人遗憾的是,由于AXI QuadSpi block传输性能实在太差(每2个block传输之间的等待时间,与block的长度成正比,导致实际几乎没有提示速率。但这并不是lvgl的问题,是AXI QuadSpi的问题。 image

2个性能监视的建议开启:LV_USE_PERF_MONITOR, LV_USE_MEM_MONITOR

image image image

精准配置MMU,代替DcacheDisable(),可以大幅加快速度

image image

这里的地址,就是前面设置的lvgl的内存地址,这里的size就是指定的lvgl的容量 这样设置后,在st77916上,速度大幅度提高。

⚠️ **GitHub.com Fallback** ⚠️