sdk例程:st7789屏移植lvgl,以及按键模拟touch功能 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
- st7789, 1.3寸屏,240x240分辨率, spi-mode=3接口
- 按键采用小梅哥的扩展板,一共有5个按键,可以设置5个坐标
- 测试发现,用ps_spi不能驱动这个屏,可能是时序不兼容。用axi_spi可以驱动
- 小梅哥的库非常好用:包括gpio,spi, i2c, scu_timer的使用,非常方便的API函数
- 吐槽一下:axi_spi实在太垃圾,它虽然支持block_transfer,但是传输的个数增大一倍,block与block之间的冷却时间也需要增大一倍,对提速没有帮助。
- 很简单,即使一个axi_spi,以及许多个emio
#dcx
#rst_n
#mosi
#sck
# PS_SPI
set_property -dict {PACKAGE_PIN F16 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[0]]
set_property -dict {PACKAGE_PIN E17 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[1]]
set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS33} [get_ports SPI_0_0_io0_io]
set_property -dict {PACKAGE_PIN G17 IOSTANDARD LVCMOS33} [get_ports SPI_0_0_sck_io]
#AXI_SPI
set_property -dict {PACKAGE_PIN F17 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[2]]
set_property -dict {PACKAGE_PIN D18 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[3]]
set_property -dict {PACKAGE_PIN E19 IOSTANDARD LVCMOS33} [get_ports spi_rtl_0_io0_io]
set_property -dict {PACKAGE_PIN G18 IOSTANDARD LVCMOS33} [get_ports spi_rtl_0_sck_io]
#key
set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[4]]
set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[5]]
set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[6]]
set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[7]]
set_property -dict {PACKAGE_PIN U15 IOSTANDARD LVCMOS33} [get_ports GPIO_0_0_tri_io[8]]
#其余不用的输入/输出可以不分配引脚,并避免报错
set_property IOSTANDARD LVCMOS33 [get_ports *]
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks RTSTAT-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]
#include "ACZ702_Lib/COMMON.h"
#include "lvgl/lvgl.h"
#include "lv_examples/lv_examples.h"
#include "lvgl/port/lv_port_disp.h"
#include "lvgl/port/lv_port_indev.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;
}
}
}
//static void ScuTimer_InterruptHandler(void *data)
//{
// XScuTimer *timer = (XScuTimer *)data;
// lv_tick_inc(1);
// XScuTimer_ClearInterruptStatus(timer);
// //xil_printf("1\r\n");
//}
void ScuTimer_IRQ_Handler(void *CallBackRef)
{
/* ↓↓↓用户处理↓↓↓ */
lv_tick_inc(1);
/* ↑↑↑结束处理↑↑↑ */
XScuTimer_ClearInterruptStatus(&ScuTimer);
}
int main(void)
{
XScuGic gic;
XScuGic_Config *gic_cfg;
XScuTimer timer;
XScuTimer_Config *timer_cfg;
//初始化通用中断控制器
Xil_DCacheDisable();
ScuGic_Init();
// /* 私有定时器初始化 */
// gic_cfg = XScuGic_LookupConfig(XPAR_SCUGIC_0_DEVICE_ID);
// XScuGic_CfgInitialize(&gic, gic_cfg, gic_cfg->CpuBaseAddress);
// timer_cfg = XScuTimer_LookupConfig(XPAR_XSCUTIMER_0_DEVICE_ID);
// XScuTimer_CfgInitialize(&timer, timer_cfg, timer_cfg->BaseAddr);
//
// XScuTimer_LoadTimer(&timer, XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ / 2000 - 1); // 1ms
// XScuTimer_EnableAutoReload(&timer);
//
// Xil_ExceptionInit();
// Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &gic);
// Xil_ExceptionEnable();
//
// XScuGic_Connect(&gic, XPAR_SCUTIMER_INTR, (Xil_ExceptionHandler)ScuTimer_InterruptHandler, (void *)&timer);
// XScuGic_Enable(&gic, XPAR_SCUTIMER_INTR);
//
// XScuTimer_EnableInterrupt(&timer);
// XScuTimer_Start(&timer);
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();
/* 死循环 */
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.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);
#if LV_USE_GPU
static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
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);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
#define LCD_DOUBLE_BUFFER_ADDR 0x10100000
#define LCD_DOUBLE_BUFFER_ADDR2 0x10200000
void lv_port_disp_init(void)
{
static lv_disp_buf_t disp_buf;
lv_disp_buf_init(&disp_buf, (void *)LCD_DOUBLE_BUFFER_ADDR,
(void *)LCD_DOUBLE_BUFFER_ADDR2,
LV_HOR_RES_MAX * LV_VER_RES_MAX); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
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 = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.buffer = &disp_buf;
#if LV_USE_GPU
/*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/
/*Blend two color array using opacity*/
disp_drv.gpu_blend_cb = gpu_blend;
/*Fill a memory array with a color*/
disp_drv.gpu_fill_cb = gpu_fill;
#endif
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/* 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*/
int16_t x;
int16_t y;
uint8_t *ptrBase;
uint8_t *ptr;
/* 优化后的结果 */
// ptrBase = (area->y1 * LV_HOR_RES_MAX + area->x1) * 2 + (uint8_t *)frame_buffer_addr;
// for(y = area->y1; y <= area->y2; y++, ptrBase += (LV_HOR_RES_MAX * 2)) {
// for(x = area->x1, ptr = ptrBase; x <= area->x2; x++, ptr += 2) {
// *(uint16_t *)ptr = color_p->full;
// 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 LV_USE_GPU
/* If your MCU has hardware accelerator (GPU) then you can use it to blend to memories using opacity
* It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
{
/*It's an example code which should be done by your GPU*/
uint32_t i;
for(i = 0; i < length; i++) {
dest[i] = lv_color_mix(dest[i], src[i], opa);
}
}
/* If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color
* It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
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*/
}
}
#endif /*LV_USE_GPU*/
#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.h"
// #include "../../touch/ft5x26.h"
// #include "../../touch/gt9xx.h"
// #include "../../TOUCH_NEW/ft5206.h"
// #include "../../TOUCH_NEW/gt9147.h"
#include "xil_printf.h"
extern unsigned int lcd_id;
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
//static bool gt9xx_touch_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static bool ft5x26_touch_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool key_touch_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
/**********************
* STATIC VARIABLES
**********************/
lv_indev_t * indev_touchpad;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
typedef bool (*read_cb)(struct _lv_indev_drv_t *, lv_indev_data_t *);
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
*/
lv_indev_drv_t indev_drv;
read_cb cb_ptr;
/*------------------
* Touchpad
* -----------------*/
// emio_init();
// /*Initialize your touchpad if you have*/
// switch(lcd_id) {
// case 0:
// case 1:
// case 5: {
// GT9147_Init();
// cb_ptr = gt9xx_touch_read;
// }
// break;
// case 2:
// case 4:
// default: {
// FT5206_Init();
// cb_ptr = ft5x26_touch_read;
// }
// break;
// }
cb_ptr=key_touch_read;
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = cb_ptr;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*------------------
* Touchpad
* -----------------*/
/* Will be called by the library to read the touchpad */
static bool key_touch_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*/
extern u8 KEY_Scan(u8 mode);
u8 key = KEY_Scan(0);
if (key != 0)
{
data->state = LV_INDEV_STATE_PR;
switch (key)
{
case 1:
last_x = 60, last_y = 60; break;
case 2:
last_x = 180, last_y = 60; break;
case 3:
last_x = 60, last_y = 180; break;
case 4:
last_x = 180, last_y = 180; break;
case 5:
last_x = 120, last_y = 120; break;
}
}
else
data->state = LV_INDEV_STATE_REL;
/*Set the last pressed coordinates*/
xil_printf("touch state=%d, x=%d, y=%d, key=%d\r\n", data->state, last_x, last_y, key);
data->point.x = last_x;
data->point.y = last_y;
/*Return `false` because we are not buffering and no more data to read*/
return false;
}
#else /* Enable this file at the top */
/* This dummy typedef exists purely to silence -Wpedantic. */
typedef int keep_pedantic_happy;
#endif
如果设置为10行,那么刷图时,需要调用disp_flush函数很多次