LVGL在STM32F7上的移植测试(LVGL基于9.2.2版本+CubeIDE+ST7789显示屏)

目录

一、下载LVGL源码

二、创建STM32工程

三、添加LVGL库代码

四、增加基础调用代码

1、增加头文件

2、增加LVGL库和demo

3、增加LGVL的时基

五、配置LVGL

1、开启配置文件

2、修改内存大小

3、开启ST7789驱动

4、开启demo程序

六、移植显示接口

1、创建显示接口文件

2、适配屏幕分辨率

3、适配显示初始化函数

4、硬件接口初始化设置

5、适配发送命令接口

6、适配像素显示接口

七、移植ST7789驱动

八,运行效果


LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素,美丽的视觉效果和低内存占用。

控制器采用STM32F767IGT6,显示屏采用中景园1.3 英寸240RGB*240分辨率类似的拆机小显示屏,此显示屏采用ST7789Z作为驱动芯片,SPI接口。CubeIDE使用1.16.1版本,内嵌CubeMX。STM32固件库采用1.17.2版本。注意:本次移植只为使用最少的操作让LVGL的demo程序运行起来,不含剪裁和优化。

一、下载LVGL源码

LVGL最新源代码下载地址:https://github.com/lvgl/lvgl/archive/refs/tags/v9.2.2.zip

下载解压后目录:

二、创建STM32工程

使用CubeIDE自带的CubeMAX创建STM32工程,硬件使用自己设计的STM32开发板,通过杜邦线链接验证。

本次使用硬件SPI4驱动显示屏。单片机管脚配置如下:

显示屏使用到的管脚为:

LCD_CS:PA0 片选信号,低电平有效

LCD_ENA:PA1背光使能信号,高电平有效

LCD_DCS:PA2指令数据选择信号

LCD_RST:PA3复位信号

LCD_SCK:PE2时钟信号

LCD_MOSI:PE6数据信号

SPI接口,采用DMA发送,因此需开启DMA,具体配置如下:

工程创建基本上外设使用系统默认参数即可。

三、添加LVGL库代码

工程创建好后添加LVGL代码目录,在此目录下创建lvgl文件夹,并将LVGL源码目录中src、demos、examples三个文件夹内容以及根目录下的lv_version.h、lvgl.h全部拷贝到lvgl文件夹。将根目录下的lv_conf_template.h拷贝到 LVGL目录下,并修改lv_conf_template.h文件名称为lv_conf.h。

配置工程属性,增加LVGL相关的头文件包含目录,修改后的配置如下:

工程整体结构如下所示:

四、增加基础调用代码

1、增加头文件

修改main.c,增加lvgl库相关头文件和demo相关都文件包含(lvgl.h和demos/lv_demos.h)

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lvgl.h"
#include "examples/porting/lv_port_disp.h"
#include "demos/lv_demos.h"
/* USER CODE END Includes */

2、增加LVGL库和demo

调用初始化函数初始化lvgl库和显示驱动(lv_init()和lv_port_disp_init()),调用demo创建函数(lv_demos_create()),由于此函数需传入启动参数自动识别demo名称调用对应的demo,在STM32系统并不适合,因此传入空参数即可运行默认的widgets demo,再在主循环添加lvgl的任务管理器(lv_task_handler()),要处理lvgl的任务就需要定期调用 lv_task_handler,main函数修改后如下:

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MPU Configuration--------------------------------------------------------*/
  MPU_Config();

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI4_Init();
  MX_UART8_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  lv_init();
  lv_port_disp_init();
  lv_demos_create(NULL,0);
  while (1)
  {
	  lv_task_handler();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

注意,此时的lv_port_disp.h头文件不存在,lv_port_disp_init()函数还未实现,后面的显示接口移植会实现此函数。

3、增加LGVL的时基

LVGL 需要一个系统滴答来了解动画和其他任务所用的时间。需要定期调用 lv_tick_inc(tick_period) 函数,并提供以毫秒为单位的调用周期。使用CubeMax创建的STM32工程默认使用滴答定时器作为HAL库的延时定时器,且默认周期为1ms,因此可以直接使用。

在stm32f7xx_it.c文件中的void SysTick_Handler(void)中断函数中添加函数调用:

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  lv_tick_inc(1);
  /* USER CODE END SysTick_IRQn 1 */
}

五、配置LVGL

1、开启配置文件

打开lv_conf.h修改15行代码,生效配置文件:

#if 0 /*Set it to "1" to enable content*/
修改为
#if 1 /*Set it to "1" to enable content*/

2、修改内存大小

由于控件内存都是动态创建的,在运行复杂控件或控件较多时需要修改第56行,增加lv_malloc()的内存大小,此处为了运行自带的默认demo程序( widgets demo)默认的64K内存不够,需要根据实际需求扩大:

    #define LV_MEM_SIZE (64 * 1024U)          /*[bytes]*/
改为
    #define LV_MEM_SIZE (128 * 1024U)          /*[bytes]*/

3、开启ST7789驱动

LVGL自带ST7789驱动,需要开启ST7789驱动,修改1048行:

#define LV_USE_ST7789        0
改为
#define LV_USE_ST7789        1

4、开启demo程序

开启widgets demo程序,修改1084行:

#define LV_USE_DEMO_WIDGETS 0
改为
#define LV_USE_DEMO_WIDGETS 1

六、移植显示接口

显示驱动适配在examples目录的porting目录内:

1、创建显示接口文件

修改lv_port_lcd_stm32_template.c为lv_port_disp.c,修改lv_port_lcd_stm32_template.h为lv_port_disp.h

修改后如下:

增加头文件:

#include "main.h"
#include "spi.h"
#include "gpio.h"
#include "lv_port_disp.h"
#include "./src/drivers/display/st7789/lv_st7789.h"

开启代码,修改lv_port_disp.c第8行和修改lv_port_disp.h第7行:

#if 0
改为
#if 1

2、适配屏幕分辨率

修改屏幕分辨率为240x240:

#ifndef MY_DISP_HOR_RES
    #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
    #define MY_DISP_HOR_RES    240
#endif

#ifndef MY_DISP_VER_RES
    #warning Please define or replace the macro MY_DISP_VER_RES with the actual screen height, default value 240 is used for now.
    #define MY_DISP_VER_RES    240
#endif

3、适配显示初始化函数

修改函数名称void lv_port_display_init(void)为void lv_port_disp_init(void),这里应该是个小BUG,跟

lv_port_disp.h文件申明的函数名称不同。

void lv_port_display_init(void)
改为
void lv_port_disp_init(void)

删除老版本STM固件库使用的SPI传输回调函数static void lcd_color_transfer_ready_cb(SPI_HandleTypeDef * hspi);

增加新版固件库使用的回调函数:

/* Callback is called when background transfer finished */
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef * hspi)
{
    /* CS high */
    HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
    lcd_bus_busy = 0;
    lv_display_flush_ready(lcd_disp);
}

4、硬件接口初始化设置

修改lcd_io_init函数,适配对应的管脚,删除老版本STM固件库使用的SPI回调注册函数HAL_SPI_RegisterCallback(&hspi1, HAL_SPI_TX_COMPLETE_CB_ID, lcd_color_transfer_ready_cb),增加背光开启,修改后如下:

/* Initialize LCD I/O bus, reset LCD */
static int32_t lcd_io_init(void)
{
    /* reset LCD */
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
    HAL_Delay(100);
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
    HAL_Delay(100);

    HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(LCD_ENA_GPIO_Port, LCD_ENA_Pin, GPIO_PIN_SET);
    return HAL_OK;
}

5、适配发送命令接口

修改lcd_send_cmd函数,原SPI1适配为SPI4:

static void lcd_send_cmd(lv_display_t * disp, const uint8_t * cmd, size_t cmd_size, const uint8_t * param,
                         size_t param_size)
{
    LV_UNUSED(disp);
    while(lcd_bus_busy);    /* wait until previous transfer is finished */
    /* Set the SPI in 8-bit mode */
    hspi4.Init.DataSize = SPI_DATASIZE_8BIT;
    HAL_SPI_Init(&hspi4);
    /* DCX low (command) */
    HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_RESET);
    /* CS low */
    HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
    /* send command */
    if(HAL_SPI_Transmit(&hspi4, cmd, cmd_size, BUS_SPI1_POLL_TIMEOUT) == HAL_OK) {
        /* DCX high (data) */
        HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET);
        /* for short data blocks we use polling transfer */
        HAL_SPI_Transmit(&hspi4, (uint8_t *)param, (uint16_t)param_size, BUS_SPI1_POLL_TIMEOUT);
        /* CS high */
        HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
    }
}

6、适配像素显示接口

修改lcd_send_color函数,原SPI1适配为SPI4,由于默认颜色为16位色,需且将原传输大小(param_size / 2)修改为param_size :

static void lcd_send_color(lv_display_t * disp, const uint8_t * cmd, size_t cmd_size, uint8_t * param,
                           size_t param_size)
{
    LV_UNUSED(disp);
    while(lcd_bus_busy);    /* wait until previous transfer is finished */
    /* Set the SPI in 8-bit mode */
    hspi4.Init.DataSize = SPI_DATASIZE_8BIT;
    HAL_SPI_Init(&hspi4);
    /* DCX low (command) */
    HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_RESET);
    /* CS low */
    HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
    /* send command */
    if(HAL_SPI_Transmit(&hspi4, cmd, cmd_size, BUS_SPI1_POLL_TIMEOUT) == HAL_OK) {
        /* DCX high (data) */
        HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET);
        /* for color data use DMA transfer */
        /* Set the SPI in 16-bit mode to match endianness */
        hspi4.Init.DataSize = SPI_DATASIZE_16BIT;
        HAL_SPI_Init(&hspi4);
        lcd_bus_busy = 1;
        HAL_SPI_Transmit_DMA(&hspi4, param, (uint16_t)param_size);
        /* NOTE: CS will be reset in the transfer ready callback */
    }
}

七、移植ST7789驱动

LVGL自带ST7789驱动,用的MIPI接口框架,适配屏幕只需要修改参数列表即可,驱动位于:

打开lv_st7789.c,修改init_cmd_list数组,这里主要是设置显示屏的显示参数,加入适配自己屏幕的参数即可,我的屏幕使用如下参数:

static const uint8_t init_cmd_list[] = {
	CMD_PORCTRL,	5,	0x0C,0x0C,0x00,0x33,0x33,
    CMD_GCTRL,      1,  0x35,       /* GCTRL -- panel dependent */
    CMD_VCOMS,      1,  0x19,       /* VCOMS -- panel dependent */
	CMD_LCMCTRL,	1,	0x2c,
	CMD_VDVVRHEN,	1,	0x01,
    CMD_VRHS,       1,  0x12,       /* VRHS - panel dependent */
	CMD_VDVSET,		1,	0x20,
	CMD_FRCTR2,		1,	0x0F,
    CMD_PWCTRL1,    2,  0xa4, 0xa1,
    CMD_PVGAMCTRL,  14, 0xd0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23,
    CMD_NVGAMCTRL,  14, 0xd0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23,
    LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF
};

除此之外,我的屏幕还得开启反向显示,在初始化代码增加lv_st7789_set_invert(disp,true)调用:

lv_display_t * lv_st7789_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags,
                                lv_st7789_send_cmd_cb_t send_cmd_cb, lv_st7789_send_color_cb_t send_color_cb)
{
    lv_display_t * disp = lv_lcd_generic_mipi_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb);
    lv_st7789_set_invert(disp,true);
    lv_lcd_generic_mipi_send_cmd_list(disp, init_cmd_list);
    return disp;
}

至此编译工程,运行即可正常显示。

八,运行效果

实际显示效果如图所示:

### 将LVGL 9.2移植STM32的指南 #### 准备工作 为了成功将LVGL 9.2移植STM32平台,需先完成一系列准备工作。这包括但不限于获取必要的开发工具链以及硬件资源。具体来说,应安装适合目标板卡(如STM32F429 Discovery Kit)使用的集成开发环境(IDE),例如SW4STM32 (Ac6)[^1]。 #### 获取并配置LVGL库文件 访问官方GitHub或其他可信镜像站点下载最新版本LVGL库,确保所选版本9.2。对于特定于STM32的应用场景,可以从专门针对此系列微控制器优化过的分支或仓库中拉取代码,比如`lv_port_stm32f429_disco`项目提供了适配STM32F429I-DISC1的具体实现方案。此外,还有其他开源社区贡献者分享的不同型号STM32上的LVGL移植实例可供参考,这些资料往往包含了详细的设置步骤和注意事项说明[^3]。 #### 配置显示驱动接口 根据实际使用的显示屏模块特性,在应用层面上定义好相应的初始化函数与数据传输协议。通常情况下,需要编写一段C/C++代码来桥接底层硬件抽象层(HAL) API 和上层GUI框架之间的交互逻辑。下面给出了一段简化后的伪代码片段作为示范: ```c #include "lvgl.h" // 假设已经通过HAL完成了SPI/IIC等通信外设的初始化操作... void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p){ /* 使用DMA发送缓冲区中的像素颜色值 */ HAL_DMA_Transmit(&hdma_spi1, color_p, area->x2 - area->x1 + 1); // 发送完成后通知LVGL刷新已完成 lv_disp_flush_ready(disp); } ``` 这段代码展示了如何利用DMA机制高效地向屏幕写入图像帧缓存的内容,并告知LVGL当前绘图任务已结束以便继续处理后续事件循环。 #### 初始化触摸屏输入设备 如果项目涉及触控功能,则还需额外考虑接入合适的传感器并与之建立有效的通讯连接。一般而言,会涉及到读取坐标位置信息并通过回调机制传递给LVGL内核用于解析用户手势动作。这里不再赘述具体的编码细节,因为不同类型的触摸芯片可能有着各自独特的API调用方式[^2]。 #### 编译链接与调试部署 最后一步就是把整个工程编译成可执行映像文件烧录进目标单片机内部闪存空间里去跑起来看效果啦!记得开启串口监视器观察启动过程是否有异常报错提示哦~一旦遇到棘手的技术难题不妨查阅相关论坛帖子求助或者直接联系作者交流心得经验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值