移植 LVGL STM32F407VGT6 8bit FSMC TFT-LCD

Posted RT-Thread物联网操作系统

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移植 LVGL STM32F407VGT6 8bit FSMC TFT-LCD相关的知识,希望对你有一定的参考价值。

本文由RT-Thread论坛@ppacctv 原创发布:https://club.rt-thread.org/ask/article/15d851442a2670c3.html

我的板子是这样的,MCU是STM32F407VGT6

我的TFT-LCD 是这样的,驱动是ili9486,320x480,8bit并口

组合到一起是这样的

LCD 找卖家要的驱动

第一步

第一次移植,不确定问题会在哪发生,所以
Keil + STM32CubeMX
先裸机驱动LCD,验证代码的正确性,减少问题出在LCD这边的可能性。

第二步

裸机没问题,使用RT-Thread Studio新建工程。

在RT-Thread Studio 里,双击 CubeMX Settings,打开CubeMX进行硬件配置

下面是我的配置,Timing的参数先保持CubeMX生成的默认值就好,后面可以慢慢调,调不好LCD没显示的,或者不稳定,不利于移植

时钟、串口、FSMC,设置完,File 菜单,save一下,然后右上角生成 CODE,完事关闭CubeMX。编译一下,应该没有错误。

第三步


https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32f407-atk-explorer/applications/
下载整个lvgl文件夹,复制到我们工程的applications 文件夹下;

https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32f407-atk-explorer/board/ports
下载drv_lcd.c/drv_lcd.h 复制到我们工程的drivers 文件夹下;
还有我们的LCD驱动,建个ili9486文件夹,放到 drivers 文件夹下。

然后我们右键我们的工程,属性->C/C++常规->路径和符号->包含->GNU C,将我们的lvgl、ili9486、lvgl/demo 文件夹加入。
这步总之就是将以上文件(夹)加入工程并确保能够被找到,怎么加,个人随便!

第四步

编辑 drv_lcd.h

#define LCD_W 320	//LCD水平宽
#define LCD_H 480	//LCD垂直高

#define LCD_ADDR_BASE		0x60000000	//FSMC地址,命令RAM地址
#define LCD_REGSELECT_BIT	16
#define LCD_ADDR_DATA		(LCD_ADDR_BASE + (1 << (LCD_REGSELECT_BIT + 2)) - 2)	//数据RAM地址
#define LCD_ADDR_REG		LCD_ADDR_BASE	//为了方便记忆,改个名

void lcd_fill_array(rt_uint16_t x_start, rt_uint16_t y_start, rt_uint16_t x_end, rt_uint16_t y_end, void *pcolor);

编辑 drv_lcd.c

保留

static struct drv_lcd_device _lcd;

以上,及

void lcd_fill_array(rt_uint16_t x_start, rt_uint16_t y_start, rt_uint16_t x_end,
		rt_uint16_t y_end, void *pcolor) //……
static rt_err_t drv_lcd_init(struct rt_device *device)//……
struct rt_device_graphic_ops fsmc_lcd_ops = //……

以下的内容,其它都可以删掉,注意定义的一些结构体,后面少了再加。

static rt_err_t drv_lcd_init(struct rt_device *device) 

	__HAL_RCC_GPIOD_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	__HAL_RCC_GPIOF_CLK_ENABLE();
	__HAL_RCC_GPIOG_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();

	extern void MX_FSMC_Init(void);//CubeMX生成的,图方便把static去掉了
	MX_FSMC_Init();
	rt_thread_mdelay(50);
	ili9486_Init();//驱动程序提供的初始化函数
	return RT_EOK;


void lcd_fill_array(rt_uint16_t x_start, rt_uint16_t y_start, rt_uint16_t x_end, rt_uint16_t y_end, void *pcolor) 

    rt_uint16_t *pixel = RT_NULL;
    pixel = (rt_uint16_t *)pcolor;
    ili9486_DrawRGBImage(x_start, y_start, x_end-x_start+1, y_end-y_start+1, pixel);

向给定区域填充内容,内容由 *pcolor 指向,LVGL好像就靠它绘图的。
注意,为社么要 +1 ,因为 3到5,有5-3+1个端点,分别是3,4,5号端点,LCD填充的是点。

struct rt_device_graphic_ops fsmc_lcd_ops =  RT_NULL, RT_NULL,
		RT_NULL,
		RT_NULL,
		RT_NULL;

直接使用RT_NULL填充,在LVGL当前我没发现有用的地方,有用到参照源文件加就好。

static rt_err_t drv_lcd_control(struct rt_device *device, int cmd, void *args) 
	struct drv_lcd_device *lcd = LCD_DEVICE(device);
	switch (cmd) 
		case RTGRAPHIC_CTRL_GET_INFO: 
			struct rt_device_graphic_info *info =
					(struct rt_device_graphic_info *) args;

			RT_ASSERT(info != RT_NULL);

			//this needs to be replaced by the customer
			info->pixel_format = lcd->lcd_info.pixel_format;
			info->bits_per_pixel = lcd->lcd_info.bits_per_pixel;
			info->width = LCD_W;//自己LCD的水平宽度
			info->height = LCD_H;//自己LCD的垂直高度
		
			break;
	

	return RT_EOK;

后面没有需要改的,最后的lcd_fill,需要的话可以用来测试,但要实现LCD_Clear函数,使用驱动提供的 ili9486_FillRect 函数实现一下。

void LCD_Clear(uint16_t color) 
	ili9486_FillRect(0, 0, LCD_W, LCD_H, color);

编辑lv_conf.h

具体意义不说了,论坛教程很多,就是注意自己LCD的色彩深度、宽高……

#define LV_COLOR_16_SWAP 0
#define LV_COLOR_DEPTH 16
#define LV_USE_PERF_MONITOR 1

#include <rtconfig.h>
#define LV_HOR_RES_MAX          320	//LCD的水平宽
#define LV_VER_RES_MAX          480	//LCD的垂直高
#define LV_USE_DEMO_RTT_MUSIC       1
#define LV_DEMO_RTT_MUSIC_AUTO_PLAY 1
#define LV_FONT_MONTSERRAT_12       1
#define LV_FONT_MONTSERRAT_16       1

编辑lv_port_disp.c

这个文件很关键,(个人看法,大佬可以打个脸)本质就是 LVGL 库提供给显示驱动的接口,要完成移植,就的实现这里面的接口。下面是我的实现。

#define MY_DISP_HOR_RES LCD_W		//我显示器每行LCD_W个点

static lv_disp_draw_buf_t disp_buf_dsc_2;		//可以查看移植模板
static lv_disp_drv_t disp_drv;

//注意缓存大了编译会提示RAM超限的,下面是我更具自己应用的实际取的
//每个缓存一次缓存20行,每行[MY_DISP_HOR_RES个点
lv_color_t buf_2_1[MY_DISP_HOR_RES * 20];		//定义了2块缓存
lv_color_t buf_2_2[MY_DISP_HOR_RES * 20];		//定义了2块缓存

//刷新回调函数
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) 
//主要就是lcd_fill_array函数的实现,在我们的drv_lcd.c 里面实现的
//画图就靠它
	lcd_fill_array(area->x1, area->y1, area->x2, area->y2, color_p);
	lv_disp_flush_ready(disp_drv);


void lv_port_disp_init(void) 

        lv_disp_draw_buf_init(&disp_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10);

	lv_disp_drv_init(&disp_drv);

	disp_drv.hor_res = LCD_W;		//LCD的水平宽度
	disp_drv.ver_res = LCD_H;		//LCD的垂直高度
	disp_drv.draw_buf = &disp_buf_dsc_2;
	disp_drv.flush_cb = disp_flush;		//回调函数

	lv_disp_drv_register(&disp_drv);

lv_port_indev.h/c个人不涉及,忽略,如编译有问题,请酌情处理!

第五步

双击打开RT-Thread Setting,软件包->多媒体包->LVGL,勾选LVGL及player demo就好。

保存,编译,下载,移植完毕。

不会用编辑器,有点乱!

2022深度学习开发者峰会 5月20日13:00让我们相聚云端,共襄盛会!

以上是关于移植 LVGL STM32F407VGT6 8bit FSMC TFT-LCD的主要内容,如果未能解决你的问题,请参考以下文章

基于Keil软件的MCU环境搭建

stm32f407v与stm32f407zg的区别

stm32f407H库和stm32f103H库有区别吗

stm32f407移植ucos怎么把ucos放在stm32上?

STM32F407第3章 ThreadX USBX协议栈移植到STM32F407

FREERTOS移植STM32F407