你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM32H7S78-DK】基于 rtthread 适配 lcd 驱动移植 lvgl

[复制链接]
chrome777 发布时间:2024-9-24 10:36

前言

  1. 【STM32H7S78-DK】开箱与rtthread工程初体验
  2. 【STM32H7S78-DK】rtthread 增加 psram 内存管理
  3. 【STM32H7S78-DK】基于 rtthread 适配 sdcard 文件系统

概述

原先 Kconfig 已经有相应的配置项,但是存在很多问题,现在我们修改下,增加 lcd 驱动,并开启 lvgl

配置工程

打开 bsp/stm32/stm32h7s7-st-disco 目录

  1. 打开 board/kconfig 文件,在 chip driver 目录增加 lcd 配置,修改 onboard driver 的宏依赖
         config BSP_USING_LCD
             bool "Enable LCD"
             select BSP_USING_LTDC
             select BSP_USING_I2C
             select BSP_USING_I2C1
             select PKG_USING_GT911
             default n
  2. 修改 i2c 的引脚号,SCL 对应 PB6 引脚号为 22,SDA 对应 PB9 引脚号为 25 config_01.png
  3. 打开 bsp\stm32\libraries\STM32H7RSxx_HAL\SConscript 文件,增加开启 lcd 时的 stm32 hal 库文件
     if GetDepend(['BSP_USING_LTDC']):
         src += ['STM32H7RSxx_HAL_Driver/Src/stm32h7rsxx_hal_ltdc.c']
         src += ['STM32H7RSxx_HAL_Driver/Src/stm32h7rsxx_hal_ltdc_ex.c']
         src += ['STM32H7RSxx_HAL_Driver/Src/stm32h7rsxx_hal_dma2d.c']
         src += ['STM32H7RSxx_HAL_Driver/Src/stm32h7rsxx_ll_dma2d.c']
     
     if GetDepend(['RT_USING_I2C']):
         src += ['STM32H7RSxx_HAL_Driver/Src/stm32h7rsxx_hal_i2c.c']
         src += ['STM32H7RSxx_HAL_Driver/Src/stm32h7rsxx_hal_i2c_ex.c']
  4. 引脚配置在上一章已经进行了对应的初始化,这边就不用处理了
  5. bsp/stm32/stm32h7s7-st-disco/board/port/ 增加 lcd_port.h 文件,添加如下内容
     /*
      * Copyright (c) 2006-2024, RT-Thread Development Team
      *
      * SPDX-License-Identifier: Apache-2.0
      *
      * Change Logs:
      * Date           Author       Notes
      * 2024-08-28     morph        first version
      */
     
     #ifndef __LCD_PORT_H__
     #define __LCD_PORT_H__
     
     
     #define LCD_WIDTH           800
     #define LCD_HEIGHT          480
     #define LCD_BITS_PER_PIXEL  16
     #define LCD_BUF_SIZE        (LCD_WIDTH * LCD_HEIGHT * LCD_BITS_PER_PIXEL / 8)
     #define LCD_PIXEL_FORMAT    RTGRAPHIC_PIXEL_FORMAT_RGB565
     
     #define LCD_HSYNC_WIDTH     5
     #define LCD_VSYNC_HEIGHT    5
     #define LCD_HBP             8
     #define LCD_VBP             8
     #define LCD_HFP             8
     #define LCD_VFP             8
     
     #define LCD_BACKLIGHT_USING_GPIO
     #define LCD_BL_GPIO_NUM     GET_PIN(G, 15)
     #define LCD_DISP_GPIO_NUM   GET_PIN(E, 15)
     
     
     void drv_lcd_flush(unsigned int *addr);
     
     #endif /* __LCD_PORT_H__ */
  6. 使用 ****menuconfig** 查看 onboard 驱动,看到已经增加 lcd 配置,保存退出** config_02.png
  7. 使用 ****scons --target=mdk5** 工具同步工程**
  8. 编译下载,查看 lcd 驱动,并运行测试指令 lcd_test lcd_test.gif

添加 LVGL

  1. 打开 board/kconfig 文件,在 chip driver 目录增加 lvgl 配置,修改 onboard driver 的宏依赖
         menuconfig BSP_USING_LVGL
             bool "Enable LVGL for LCD"
             select BSP_USING_LCD
             select PKG_USING_LVGL
             default n
     
             if BSP_USING_LVGL
                 config BSP_USING_LVGL_DEMO
                     bool "Enable LVGL demo"
                     select PKG_USING_LV_MUSIC_DEMO
                     default y
             endif
  2. 使用 ****pkgs --update** 拉取依赖软件包,然后使用 **scons --target=mdk5** 工具同步工程**
  3. 软件包同步进来之后,还需要修改application/lvgl 目录的文件
  4. 修改 application/lvgl/lv_conf.h
     /*
      * Copyright (c) 2006-2022, RT-Thread Development Team
      *
      * SPDX-License-Identifier: Apache-2.0
      *
      * Change Logs:
      * Date           Author        Notes
      * 2022-01-28     Rudy Lo       The first version
      */
     
     #ifndef LV_CONF_H
     #define LV_CONF_H
     
     #include <rtconfig.h>
     
     
     #define LV_USE_PERF_MONITOR     1
     #define LV_COLOR_DEPTH          16
     #define LV_HOR_RES_MAX          800
     #define LV_VER_RES_MAX          480
     
     #ifdef PKG_USING_LV_MUSIC_DEMO
     /* music player demo */
     #define LV_USE_DEMO_RTT_MUSIC       1
     #define LV_DEMO_RTT_MUSIC_AUTO_PLAY 0
     #define LV_FONT_MONTSERRAT_12       1
     #define LV_FONT_MONTSERRAT_16       1
     #define LV_COLOR_SCREEN_TRANSP      1
     #endif /* PKG_USING_LV_MUSIC_DEMO */
     
     #endif
  5. 修改 application/lvgl/lv_demo.c
     /*
      * Copyright (c) 2006-2022, RT-Thread Development Team
      *
      * SPDX-License-Identifier: Apache-2.0
      *
      * Change Logs:
      * Date           Author        Notes
      * 2024-08-28     Morph         The first version
      */
     
     #include <rtthread.h>
     #include <lvgl.h>
     
     void lv_user_gui_init(void)
     {
         /* display demo; you may replace with your LVGL application at here */
         extern void lv_demo_music(void);
         lv_demo_music();
     }
  6. 修改 application/lvgl/lv_port_disp.c ,将显示方式修改为双缓存模式,并更新屏幕刷新函数
     /*
      * Copyright (c) 2006-2022, RT-Thread Development Team
      *
      * SPDX-License-Identifier: Apache-2.0
      *
      * Change Logs:
      * Date           Author       Notes
      * 2022-02-01     Rudy Lo      The first version
      */
     
     #include <lvgl.h>
     #include <rtthread.h>
     #include <rtdevice.h>
     #include "lcd_port.h"
     #define DBG_TAG "lv_port_disp"
     #define DBG_LVL DBG_INFO
     #include <rtdbg.h>
     
     
     #define LV_DISP_USING_ONE_BUFFER        1
     #define LV_DISP_USING_TWO_BUFFER        2
     #define LV_DISP_USING_DOUBLE_BUFFERING  3
     #define LV_DISP_BUFFER_MODE     LV_DISP_USING_DOUBLE_BUFFERING
     #define LV_DISP_BUF_SIZE   LCD_BUF_SIZE
     #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    LCD_WIDTH
     #endif
     
     #ifndef MY_DISP_VER_RES
     //    #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
         #define MY_DISP_VER_RES    LCD_HEIGHT
     #endif
     
     static rt_device_t lcd_device = 0;
     static struct rt_device_graphic_info info = {0};
     
     /*A static or global variable to store the buffers*/
     static lv_disp_draw_buf_t disp_buf;
     
     /*Descriptor of a display driver*/
     static lv_disp_drv_t disp_drv;
     
     /*Static or global buffer(s). The second buffer is optional*/
     //static lv_color_t buf_1[MY_DISP_HOR_RES * DISP_BUFFER_LINES];
     //static lv_color_t buf_2[MY_DISP_HOR_RES * DISP_BUFFER_LINES];
     
     /*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)
     {
     #if LV_DISP_BUFFER_MODE == LV_DISP_USING_DOUBLE_BUFFERING
         drv_lcd_flush((uint32_t *)color_p);
     #else
         /* color_p is a buffer pointer; the buffer is provided by LVGL */
         uint32_t x, y;
         uint32_t location = 0;
     
     //    lv_color_t *fbp = (lv_color_t *)info.framebuffer;
         /* 16 bit per pixel */
         lv_color16_t *fbp = (lv_color16_t *)info.framebuffer;
     
         for (y = area->y1; y < area->y2 + 1; y++)
         {
             for (x = area->x1; x < area->x2 + 1; x++)
             {
                 location = x + y * info.width;
                 fbp[location].full = color_p->full;
                 color_p++;
             }
         }
     //    rt_device_control(lcd_device, RTGRAPHIC_CTRL_RECT_UPDATE, &info);
         drv_lcd_flush((uint32_t *)info.framebuffer);
     #endif /* LV_DISP_BUFFER_MODE */
     
         /*IMPORTANT!!!
          *Inform the graphics library that you are ready with the flushing*/
         lv_disp_flush_ready(disp_drv);
     }
     
     void lv_port_disp_init(void)
     {
         rt_err_t result;
         lv_color_t *buf_1, *buf_2;
     
         lcd_device = rt_device_find("lcd");
         if (lcd_device == RT_NULL)
         {
             LOG_E("find lcd error!");
             return;
         }
         result = rt_device_open(lcd_device, 0);
         if (result != RT_EOK)
         {
             LOG_E("open lcd error!");
             return;
         }
     
         /* get framebuffer address */
         result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &info);
         if (result != RT_EOK)
         {
             /* get device information failed */
             LOG_E("lcd get info error!");
             return;
         }
     
         RT_ASSERT(info.bits_per_pixel == 8 || info.bits_per_pixel == 16 ||
                   info.bits_per_pixel == 24 || info.bits_per_pixel == 32);
     
     #if LV_DISP_BUFFER_MODE == LV_DISP_USING_ONE_BUFFER
         buf_1 = rt_malloc_align(MY_DISP_HOR_RES * 200, 4);
         if (buf_1 == RT_NULL) {
             LOG_E("alloc disp buf 1 failed");
             return;
         }
         /*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */
         lv_disp_draw_buf_init(&disp_buf, buf_1, NULL, MY_DISP_HOR_RES * 200);
     #elif LV_DISP_BUFFER_MODE == LV_DISP_USING_TWO_BUFFER
         buf_1 = rt_malloc_align(MY_DISP_HOR_RES * 100, 4);
         if (buf_1 == RT_NULL) {
             LOG_E("alloc disp buf 1 failed");
             return;
         }
     
         buf_2 = rt_malloc_align(MY_DISP_HOR_RES * 100, 4);
         if (buf_2 == RT_NULL) {
             LOG_E("alloc disp buf 2 failed");
             rt_free(buf_1);
             return;
         }
         /*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */
         lv_disp_draw_buf_init(&disp_buf, buf_1, buf_2, MY_DISP_HOR_RES * 100);
     #elif LV_DISP_BUFFER_MODE == LV_DISP_USING_DOUBLE_BUFFERING
         buf_1 = rt_malloc_align(LV_DISP_BUF_SIZE, 4);
         if (buf_1 == RT_NULL) {
             LOG_E("alloc disp buf 1 failed");
             return;
         }
     
         buf_2 = rt_malloc_align(LV_DISP_BUF_SIZE, 4);
         if (buf_2 == RT_NULL) {
             LOG_E("alloc disp buf 2 failed");
             rt_free(buf_1);
             return;
         }
         /*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */
         lv_disp_draw_buf_init(&disp_buf, buf_1, buf_2, LV_DISP_BUF_SIZE);
     #endif /* LV_DISP_BUFFER_MODE */
     
         lv_disp_drv_init(&disp_drv); /*Basic initialization*/
     
         /*Set the resolution of the display*/
         disp_drv.hor_res = info.width;
         disp_drv.ver_res = info.height;
     
         /*Set a display buffer*/
         disp_drv.draw_buf = &disp_buf;
     
         /*Used to copy the buffer's content to the display*/
         disp_drv.flush_cb = disp_flush;
     #if LV_DISP_BUFFER_MODE == LV_DISP_USING_DOUBLE_BUFFERING
         disp_drv.full_refresh = 1;
     #endif /* LV_DISP_BUFFER_MODE */
     
         /*Finally register the driver*/
         lv_disp_drv_register(&disp_drv);
     }
  7. 在驱动 bsp/stm32/libraries/HAL_Drivers/drivers/drv_lcd.c 增加新的屏幕刷新函数,原先的拷贝方式太慢了
     void drv_lcd_flush(unsigned int *addr)
     {
         if (SCB->CCR & SCB_CCR_DC_Msk) {
             SCB_CleanInvalidateDCache();
         }
     
         /* Configure the color frame buffer start address */
     //    LTDC_LAYER(&LtdcHandle, 0)->CFBAR &= ~(LTDC_LxCFBAR_CFBADD);
         LTDC_LAYER(&LtdcHandle, 0)->CFBAR = (uint32_t)(addr);
         rt_sem_take(&_lcd.lcd_lock, RT_TICK_PER_SECOND / 20);
     
         HAL_LTDC_Relaod(&LtdcHandle, LTDC_SRCR_VBR);
     }
  8. 修改 application/lvgl/lv_port_indev.c,修改触摸驱动为 gt911,并更新读取函数
     /*
      * Copyright (c) 2006-2022, RT-Thread Development Team
      *
      * SPDX-License-Identifier: Apache-2.0
      *
      * Change Logs:
      * Date           Author       Notes
      * 2024-08-28     Morph        The first version
      */
     
     #include <lvgl.h>
     #include <stdbool.h>
     #include <rtdevice.h>
     #include <drv_gpio.h>
     #include "gt911.h"
     #define DBG_TAG "lv_port_indev"
     #define DBG_LVL DBG_INFO
     #include <rtdbg.h>
     
     
     #define TOUCH_DEVICE_NAME    "touch_gt"    /* Touch device name */
     #define TOUCH_DEVICE_I2C_BUS "i2c1"        /* SCL -> PB6(22), SDA -> PB9(25) */
     //#define REST_PIN             GET_PIN(A, 3) /* reset pin */
     //#define USER_BUTTON_PIN      GET_PIN(C, 13) /* Reserve for LV_INDEV_TYPE_BUTTON */
     
     
     lv_indev_t *touch_indev;
     static rt_device_t ts;    /* Touch device handle, Touchscreen */
     static struct rt_touch_data *read_data;
     
     
     static void touchpad_read(lv_indev_drv_t *indev, lv_indev_data_t *data)
     {
         if (rt_device_read(ts, 0, read_data, 1) != 1)
             return;
     
         if (read_data->event == RT_TOUCH_EVENT_NONE)
            return;
     
         data->point.x = read_data->x_coordinate;
         data->point.y = read_data->y_coordinate;
     
         if (read_data->event == RT_TOUCH_EVENT_DOWN)
             data->state = LV_INDEV_STATE_PR;
         if (read_data->event == RT_TOUCH_EVENT_MOVE)
             data->state = LV_INDEV_STATE_PR;
         if (read_data->event == RT_TOUCH_EVENT_UP)
             data->state = LV_INDEV_STATE_REL;
     
         LOG_D("touch state %d x = %d, y = %d", data->state, data->point.x, data->point.y);
     }
     
     rt_err_t rt_hw_gt911_register(void)
     {
         struct rt_touch_config config;
         config.dev_name = TOUCH_DEVICE_I2C_BUS;
         rt_hw_gt911_init(TOUCH_DEVICE_NAME, &config);
     
         ts = rt_device_find(TOUCH_DEVICE_NAME);
         if (!ts) {
             return -RT_ERROR;
         }
     
         read_data = (struct rt_touch_data *)rt_calloc(1, sizeof(struct rt_touch_data));
         if (!read_data) {
             return -RT_ENOMEM;
         }
     
         if (!rt_device_open(ts, RT_DEVICE_FLAG_RDONLY)) {
             struct rt_touch_info info;
             rt_device_control(ts, RT_TOUCH_CTRL_GET_INFO, &info);
             rt_kprintf("type       :%d\n", info.type);
             rt_kprintf("vendor     :%s\n", info.vendor);
             rt_kprintf("point_num  :%d\n", info.point_num);
             rt_kprintf("range_x    :%d\n", info.range_x);
             rt_kprintf("range_y    :%d\n", info.range_y);
             return RT_EOK;
         } else {
             rt_kprintf("open touch device failed.\n");
             return -RT_ERROR;
         }
     }
     
     void lv_port_indev_init(void)
     {
         static lv_indev_drv_t indev_drv;         /* Descriptor of a input device driver */
         lv_indev_drv_init(&indev_drv);           /* Basic initialization */
         indev_drv.type = LV_INDEV_TYPE_POINTER;  /* Touch pad is a pointer-like device */
         indev_drv.read_cb = touchpad_read;       /* Set your driver function */
     
         /* Register the driver in LVGL and save the created input device object */
         touch_indev = lv_indev_drv_register(&indev_drv);
     
         /* Register touch device */
         rt_hw_gt911_register();
     }
  9. 修改 packages/gt911-latest/src/gt911.c,中断控制脚使用宏约束下,修改触摸检测逻辑 modify_01.png
  10. 编译运行,可以看到 demo 正常运行 lcd_lvgl.gif

总结

得益于 st 的通用 hal 库,在 rtthread 的驱动设计里基本都可以直接使用,只需要添加小部分的芯片寄存器差异,主要是 kconfig 和 SConscript 的修改,需要匹配对应的宏。

*按现在的配置去刷新 800480 的 lcd 整体还是偏慢,而且占用比较大的 cpu 资源,后续开启 dma2d 和 gpu2d 在对比一下。**

收藏 评论1 发布时间:2024-9-24 10:36

举报

1个回答
STMCU-管管 回答时间:2024-11-7 15:22:13
可以搞个Demo视频看看效果
8 d6 J& Y, I; s9 v0 ^

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版