LVGL移植
准备工作
我使用的IDE是MDK.
硬件方面是自己画的硬件,主控F411CEUx(Flash:512/RAM:128),屏幕是1.14寸LCD屏幕.分辨率135*240,通信方式SPI.
下载源码
进入LVGL源码托管链接,下载release/v6版本(最新的已经到了V7).如下所示.
其中
master
是最新程序,修补程序直接在此合并.
dev
在此合并新功能,稳定后合并到master
release/Vx
代表了主要版本的稳定版本.
备注: 如果想知道版本之间的变化的话,可以查看CHANGELOG.md
文档(仅V7版本有这个文件).
准备工程文件
我是用的是CUBEMX生成的工程,外加一部分淘宝卖家提供的LCD驱动程序.
CUBEMX的SPI设置为默认即可,开启SPI_TX的DMA及DMA中断.如下
备注:实际的SPI频率不是图中的8M,我是用的频率是24Mhz
新建一个bsp_lcd.c和bsp_lcd.h文件,内容分别如下所示.
bsp_lcd.c
文件如下所示
#include "main.h"
#include "lcd.h"
#include "spi.h "
#define delay_ms HAL_Delay
//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;
static void LCD_direction(uint8_t direction);
//画笔颜色,背景颜色
//uint16_t POINT_COLOR = 0x0000, BACK_COLOR = 0xFFFF;
//uint16_t DeviceCode;
/*****************************************************************************
* @name :void LCD_WR_REG(uint8_t data)
* @date :2018-08-09
* @function :Write an 8-bit command to the LCD screen
* @parameters :data:Command value to be written
* @retvalue :None
******************************************************************************/
void LCD_WR_REG(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_CLR;
SPI_WriteByte(SPI3, data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_WR_DATA(uint8_t data)
* @date :2018-08-09
* @function :Write an 8-bit data to the LCD screen
* @parameters :data:data value to be written
* @retvalue :None
******************************************************************************/
void LCD_WR_DATA(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_SET;
SPI_WriteByte(SPI3, data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
* @date :2018-08-09
* @function :Write data into registers
* @parameters :LCD_Reg:Register address
LCD_RegValue:Data to be written
* @retvalue :None
******************************************************************************/
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
/*****************************************************************************
* @name :void LCD_WriteRAM_Prepare(void)
* @date :2018-08-09
* @function :Write GRAM
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
/*****************************************************************************
* @name :void LCD_RESET(void)
* @date :2018-08-09
* @function :Reset LCD screen
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_RESET(void)
{
LCD_RST_CLR;
delay_ms(200);
LCD_RST_SET;
delay_ms(300);
}
/*****************************************************************************
* @name :void LCD_Clear(uint16_t Color)
* @date :2018-08-09
* @function :Full screen filled LCD screen
* @parameters :color:Filled color
* @retvalue :None
******************************************************************************/
void LCD_Clear(uint16_t Color)
{
uint32_t i, m;
LCD_SetWindows(0, 0, lcddev.width - 1, lcddev.height - 1);
LCD_CS_CLR;
LCD_RS_SET;
for(i = 0; i < lcddev.height; i++)
{
for(m = 0; m < lcddev.width; m++)
{
SPI_WriteByte(SPI3, Color >> 8);
SPI_WriteByte(SPI3, Color);
}
}
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_RESET(void)
* @date :2018-08-09
* @function :Initialization LCD screen
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_Init(void)
{
LCD_RESET(); //LCD 复位
//************* ST7789V初始化**********//
// LCD_WR_REG(0x11); //Sleep out
// HAL_Delay(200); //Delay 120ms
LCD_WR_REG(0x36);
LCD_WR_DATA(0x00);
LCD_WR_REG(0x3A);
LCD_WR_DATA(0x05); //0x05( 65K Color)
LCD_WR_REG(0xB2);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x33);
LCD_WR_REG(0xB7);
LCD_WR_DATA(0x35);
LCD_WR_REG(0xBB);
LCD_WR_DATA(0x19);
LCD_WR_REG(0xC0);
LCD_WR_DATA(0x2C);
LCD_WR_REG(0xC2);
LCD_WR_DATA(0x01);
LCD_WR_REG(0xC3);
LCD_WR_DATA(0x12);
LCD_WR_REG(0xC4);
LCD_WR_DATA(0x20);
LCD_WR_REG(0xC6);
LCD_WR_DATA(0x0F);
LCD_WR_REG(0xD0);
LCD_WR_DATA(0xA4);
LCD_WR_DATA(0xA1);
// LCD_WR_REG(0xD6);
// LCD_WR_DATA(0xA1);
LCD_WR_REG(0xE0);
LCD_WR_DATA(0xD0);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x0D);
LCD_WR_DATA(0x11);
LCD_WR_DATA(0x13);
LCD_WR_DATA(0x2B);
LCD_WR_DATA(0x3F);
LCD_WR_DATA(0X53);
LCD_WR_DATA(0x4C);
LCD_WR_DATA(0x08);
LCD_WR_DATA(0x0D);
LCD_WR_DATA(0x0B);
LCD_WR_DATA(0x1F);
LCD_WR_DATA(0x23);
LCD_WR_REG(0xE1);
LCD_WR_DATA(0xD0);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x11);
LCD_WR_DATA(0x13);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x3F);
LCD_WR_DATA(0x44);
LCD_WR_DATA(0x51);
LCD_WR_DATA(0x2F);
LCD_WR_DATA(0x1F);
LCD_WR_DATA(0x1F);
LCD_WR_DATA(0x20);
LCD_WR_DATA(0x23);
LCD_WR_REG(0x21);
LCD_WR_REG(0x11);
LCD_WR_REG(0x29); //Display on
LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
LCD_LED = 1; //点亮背光
LCD_Clear(WHITE);//清全屏白色
}
/*****************************************************************************
* @name :void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
* @date :2018-08-09
* @function :Setting LCD display window
* @parameters :xStar:the bebinning x coordinate of the LCD display window
yStar:the bebinning y coordinate of the LCD display window
xEnd:the endning x coordinate of the LCD display window
yEnd:the endning y coordinate of the LCD display window
* @retvalue :None
******************************************************************************/
void LCD_SetWindows(uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA((xStar + lcddev.xoffset) >> 8);
LCD_WR_DATA(xStar + lcddev.xoffset);
LCD_WR_DATA((xEnd + lcddev.xoffset) >> 8);
LCD_WR_DATA(xEnd + lcddev.xoffset);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA((yStar + lcddev.yoffset) >> 8);
LCD_WR_DATA(yStar + lcddev.yoffset);
LCD_WR_DATA((yEnd + lcddev.yoffset) >> 8);
LCD_WR_DATA(yEnd + lcddev.yoffset);
LCD_WriteRAM_Prepare(); //开始写入GRAM
}
/*****************************************************************************
* @name :void LCD_direction(uint8_t direction)
* @date :2018-08-09
* @function :Setting the display direction of LCD screen
* @parameters :direction:0-0 degree
1-90 degree
2-180 degree
3-270 degree
* @retvalue :None
******************************************************************************/
static void LCD_direction(uint8_t direction)
{
lcddev.setxcmd = 0x2A;
lcddev.setycmd = 0x2B;
lcddev.wramcmd = 0x2C;
switch(direction)
{
case 0:
lcddev.width = LCD_W;
lcddev.height = LCD_H;
lcddev.xoffset = 52;
lcddev.yoffset = 40;
LCD_WriteReg(0x36, 0); //BGR==1,MY==0,MX==0,MV==0
break;
case 1:
lcddev.width = LCD_H;
lcddev.height = LCD_W;
lcddev.xoffset = 40;
lcddev.yoffset = 53;
LCD_WriteReg(0x36, (1 << 6) | (1 << 5)); //BGR==1,MY==1,MX==0,MV==1
break;
case 2:
lcddev.width = LCD_W;
lcddev.height = LCD_H;
lcddev.xoffset = 53;
lcddev.yoffset = 40;
LCD_WriteReg(0x36, (1 << 6) | (1 << 7)); //BGR==1,MY==0,MX==0,MV==0
break;
case 3:
lcddev.width = LCD_H;
lcddev.height = LCD_W;
lcddev.xoffset = 40;
lcddev.yoffset = 52;
LCD_WriteReg(0x36, (1 << 7) | (1 << 5)); //BGR==1,MY==1,MX==0,MV==1
break;
default:
break;
}
}
bsp_lcd.h
如下所示
#ifndef __LCD_H
#define __LCD_H
#include "main.h"
#include "sys.h"
//LCD重要参数集
typedef struct
{
uint16_t width; //LCD 宽度
uint16_t height; //LCD 高度
uint16_t id; //LCD ID
uint8_t dir; //横屏还是竖屏控制:0,竖屏;1,横屏。
uint16_t wramcmd; //开始写gram指令
uint16_t setxcmd; //设置x坐标指令
uint16_t setycmd; //设置y坐标指令
uint8_t xoffset;
uint8_t yoffset;
}_lcd_dev;
//LCD参数
/用户配置区///
#define USE_HORIZONTAL 0 //定义液晶屏顺时针旋转方向 0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转
//
//定义LCD的尺寸
#define LCD_W 135
#define LCD_H 240
//-----------------LCD端口定义----------------
#define LED 7 //背光控制引脚
#define CS 1 //片选引脚
#define RS 0 //寄存器/数据选择引脚
#define RST 4 //复位引脚
//QDtech全系列模块采用了三极管控制背光亮灭,用户也可以接PWM调节背光亮度
#define LCD_LED PAout(LED) //LCD背光
#define LCD_CS PBout(CS)
#define LCD_RS PBout(RS)
#define LCD_RST PBout(RST)
//如果使用官方库函数定义下列底层,速度将会下降到14帧每秒,建议采用我司推荐方法
//以下IO定义直接操作寄存器,快速IO操作,刷屏速率可以达到28帧每秒!
#define LCD_CS_SET LCD_CS=1 //GPIO_TYPE->BSRRL=1<<LCD_CS //片选端口 PB11
#define LCD_RS_SET LCD_RS=1 //GPIO_TYPE->BSRRL=1<<LCD_RS //数据/命令 PB10
#define LCD_RST_SET LCD_RST=1 //GPIO_TYPE->BSRRL=1<<LCD_RST //复位 PB12
#define LCD_CS_CLR LCD_CS=0 //GPIO_TYPE->BSRRH=1<<LCD_CS //片选端口 PB11
#define LCD_RS_CLR LCD_RS=0 //GPIO_TYPE->BSRRH=1<<LCD_RS //数据/命令 PB10
#define LCD_RST_CLR LCD_RST=0 //GPIO_TYPE->BSRRH=1<<LCD_RST //复位 PB12
//画笔颜色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
#define GRAY0 0xEF7D
#define GRAY1 0x8410 //灰色1 00000 000000 00000
#define GRAY2 0x4208
//GUI颜色
#define DARKBLUE 0X01CF //深蓝色
#define LIGHTBLUE 0X7D7C //浅蓝色
#define GRAYBLUE 0X5458 //灰蓝色
//以上三色为PANEL的颜色
#define LIGHTGREEN 0X841F //浅绿色
#define LIGHTGRAY 0XEF5B //浅灰色(PANNEL)
#define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色)
void LCD_Init(void);
void LCD_Clear(uint16_t Color);
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd);
#endif
上述的几个IO根据自己的硬件连线去修改.
开始移植
终于要开始了,墨迹了这么久了…
将下面所有文件夹内的C文件加入到MDK工程中去.
然后进行如下操作.
lvgl-release-v6\porting
目录下的lv_port_disp_template
c文件和头文件copy到工程中并改名为lv_port_disp.c
和lv_port_disp.h
文件.lvgl-release-v6
目录下的lv_conf_template.h
文件copy到工程中改名为lv_conf.h
.- 添加头文件路径.
最终我的工程目录如下所示.
LVGL/GUI目录下就是
lvgl-release-v6\src
目录下所有子目录的C文件集合.
适配工作
-
修改
lv_conf.h
文件,主要修改如下.-
#if 0
修改为#if 1
-
修改宏为如下所示.
#define LV_HOR_RES_MAX (135) #define LV_VER_RES_MAX (240)
- 打开
LV_COLOR_16_SWAP
- 修改
LV_DPI
为20 LV_MEM_SIZE
改为16*1024
- 关闭
LV_USE_GPU
和LV_USE_FILESYSTEM
LV_IMG_CACHE_DEF_SIZE
修改为128- 修改宏为如下所示
#define LV_TICK_CUSTOM 1 #if LV_TICK_CUSTOM == 1 #define LV_TICK_CUSTOM_INCLUDE "main.h" /*Header for the sys time function*/ #define LV_TICK_CUSTOM_SYS_TIME_EXPR (HAL_GetTick()) /*Expression evaluating to current systime in ms*/ #endif /*LV_TICK_CUSTOM*/
- 关闭
LV_USE_DEBUG
- 关闭
LV_THEME_LIVE_UPDATE
-
-
修改
lv_port_disp.c
文件为如下内容.
#include "lv_port_disp.h"
#include "lcd.h"
#include "spi.h"
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
static lv_disp_buf_t disp_buf_3;
static lv_color_t buf3_1[LV_HOR_RES_MAX * 20]; /*A screen sized buffer*/
static lv_color_t buf3_2[LV_HOR_RES_MAX * 20]; /*An other screen sized buffer*/
static lv_disp_drv_t * p_disp_drv;
void lv_port_disp_init(void)
{
lv_disp_buf_init(&disp_buf_3, buf3_1, buf3_2, sizeof(buf3_1)/sizeof(lv_color_t));
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = disp_flush;
disp_drv.buffer = &disp_buf_3;
lv_disp_drv_register(&disp_drv);
}
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*/
p_disp_drv = disp_drv;
int16_t w = (area->x2 - area->x1 + 1);
int16_t h = (area->y2 - area->y1 + 1);
uint32_t size = w * h * 2;
LCD_SetWindows(area->x1, area->y1, area->x2, area->y2);
LCD_CS_CLR;
LCD_RS_SET;
HAL_SPI_Transmit_DMA(&hspi3,(void *)color_p,size);
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
LCD_CS_SET;
lv_disp_flush_ready(p_disp_drv);
}
lv_port_disp.h
文件如下所示
#ifndef LV_PORT_DISP_H
#define LV_PORT_DISP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
/*********************
* DEFINES
*********************/
void lv_port_disp_init(void);
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_PORT_DISP_H*/
这个屏幕没有触摸IC,所以不用移植lv_port_indev.c
文件
-
在
SysTick_Handler
中断中添加函数lv_tick_inc(1)
. -
裸机的话在main函数内添加函数
lv_task_handler();
,带OS的话创建一个任务以一定间隔运行该函数.
至此,结束.