STM32 + RT-Thread + LVGL

stm32,rt,thread,lvgl · 浏览次数 : 0

小编点评

**标题:基于STM32F103ZET微控制器的LVGL简易移植指南** **一、基本信息** - **MCU**: STM32F103ZET6 - **Thread**: 5.0.2 - **LVGL**: 8.3.11 - **LCD**: ST7735s - **编译环境**: RTThread Studio **二、LVGL 移植要求** - 建议使用16、32或64位微控制器或处理器 - 建议速度大于16 MHz - 闪存/ROM: 大于64 kB(建议180 kB) - 内存: 8 kB(建议24 kB) - 1个帧缓冲器 - LVGL的图形缓冲: 水平分辨率像素(推荐1/10屏幕尺寸) **三、添加 LVGL 软件包** - 已经完成三个函数的调用 - 避免使用触摸屏时调用 `lv_port_indev_init()` 函数 **四、`lv_user_gui_init()` 函数** - 主要作用是 LVGL 启动的初始化界面 - 消除初始化启动功能时导致屏幕出现长时间的白屏现象 **五、`lv_port_disp_init()` 函数** - 主要作用是,初始化显示屏 - 将显示屏的图像刷新函数与 `flush_cb` 函数进行绑定 **六、ST7735s 驱动程序** - 屏幕驱动程序的描述符 - 屏幕参数获取 - 硬件抽象层(HAL) **七、总结** - 移植 LVGL 的过程很简单,主要关心 `lv_disp_flush()` 函数的实现 - 注意添加 `lv_disp_flush_ready(disp_drv)` 函数 - 最后 添加适当的头文件 通过本文的指引,您应该可以轻松地将 LVGL 移植到基于 STM32F103ZET 微控制器的开发板上。

正文

一、基本信息

  • MCU:STM32F103ZET6
  • RT-Thread:5.0.2
  • LVGL:8.3.11
  • LCD:ST7735s
  • 编译环境:RTThread studio

二、LVGL 移植要求

  • 16、32或64位微控制器或处理器
  • 建议速度大于16 MHz
  • 闪存/ROM: > 64 kB(建议180 kB)
  • 内存:8 kB(建议24 kB)
  • 1个帧缓冲器:在MCU、外部RAM或显示控制器中
  • LVGL的图形缓冲:>“水平分辨率”像素(推荐1/10“屏幕尺寸”)
  • C99或更新的编译器
  • 基本的C(或C++)知识:指针、结构、回调

三、添加 LVGL 软件包

  1. 添加软件包

  2. LVGL 文件目录

  3. lv_rt_thread_port.c 文件

    由上图可知:文件已经帮我们完成了三个函数的调用,只需要在对函数进行实例即可,由于我没用到触摸屏,所以将 lv_port_indev_init() 的调用屏蔽了

四、lv_user_gui_init() 函数

此函数的主要作用是 LVGL 启动的初始化界面,相当于开机界面,主要是消除初始化启动功能时导致屏幕出现长时间的白屏的现象,程序如下

点击查看代码
#include <lvgl.h>

void lv_user_gui_init(void)
{
    /* 获取默认显示器的活动屏幕 */
    lv_obj_t *scr = lv_scr_act();
    lv_obj_clean(scr); /* 清屏 */

    /* 创建界面启动界面 */
    lv_obj_t *page = lv_obj_create(scr);
    lv_obj_set_size(page, LV_HOR_RES, LV_VER_RES);
    lv_obj_set_style_bg_color(page, lv_color_black(), LV_PART_MAIN);            /* 设置背景颜色 */
    lv_obj_set_style_radius(page, 0, LV_PART_MAIN | LV_STATE_DEFAULT);          /* 设置导角为0 */
    lv_obj_set_style_border_width(page, 0, LV_PART_MAIN | LV_STATE_DEFAULT);    /* 设置边框为0 */

    /* 添加标签 */
    lv_obj_t *label = lv_label_create(page);
    lv_label_set_text(label, "Loading");
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}

五、lv_port_disp_init() 函数

此函数主要的作用是,初始化显示屏,并将显示屏的图像刷新函数与 flush_cb 函数进行绑定,程序如下

点击查看代码
#include <lvgl.h>
#include <rtthread.h>
#include <board.h>

//#define DRV_DEBUG
#define LOG_TAG             "LVGL.port.disp"
#include <drv_log.h>

static rt_device_t lcd_device = RT_NULL;
static struct rt_device_graphic_info lcd_info;

static lv_disp_drv_t disp_drv;  /* 显示驱动程序的描述符 */
/* 用于存储缓冲区的静态或全局变量 */
static lv_disp_draw_buf_t disp_buf;

void lv_port_disp_init(void)
{
    rt_err_t result;

   void *lv_disp_buf1 = RT_NULL;
   void *lv_disp_buf2 = RT_NULL;

   /* 查找 LCD 设备 */
   lcd_device = rt_device_find("lcd");
   if (lcd_device == 0)
   {
       LOG_E("lcd_device error!");
       return;
   }

   result = rt_device_open(lcd_device, RT_DEVICE_FLAG_RDWR);
   if(result != RT_EOK)
   {
       LOG_E("open lcd device failed");
       return;
   }

   /* get framebuffer address */
   result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &lcd_info);
   if (result != RT_EOK)
   {
       LOG_E("error!");
       /* get device information failed */
       return;
   }

   RT_ASSERT (lcd_info.bits_per_pixel == 8 || lcd_info.bits_per_pixel == 16 ||
           lcd_info.bits_per_pixel == 24 || lcd_info.bits_per_pixel == 32);

   lv_disp_buf1 = rt_malloc(lcd_info.smem_len * sizeof(lv_color_t));
   rt_memset(lv_disp_buf1, 0, lcd_info.smem_len * sizeof(lv_color_t));
   RT_ASSERT(lv_disp_buf1 != RT_NULL);

   lv_disp_buf2 = rt_malloc(lcd_info.smem_len * sizeof(lv_color_t));
   rt_memset(lv_disp_buf2, 0, lcd_info.smem_len * sizeof(lv_color_t));
   RT_ASSERT(lv_disp_buf2 != RT_NULL);

   /* 使用缓冲区初始化 disp_buf */
   lv_disp_draw_buf_init(&disp_buf, lv_disp_buf1, lv_disp_buf2, lcd_info.smem_len);

   lv_disp_drv_init(&disp_drv);

   /* 设置显示器的分辨率 */
   disp_drv.hor_res = lcd_info.width;
   disp_drv.ver_res = lcd_info.height;

   /* 设置显示缓冲区 */
   disp_drv.draw_buf = &disp_buf;

   /* 用于将缓冲区的内容复制到显示器 */
   disp_drv.flush_cb = lcd_device->user_data;

   /* 注册驱动程序 */
   lv_disp_drv_register(&disp_drv);


}

六、ST7735s 驱动程序

这里不要局限于 ST7735s 这个显示屏,主要是介绍 LCD 与 LVGL 对接的 bsp 的编写过程。程序中的其他函数主要都是初始化 lcd 的工作,主要关注 lcd_fb_flush 函数,此函数会在 LVGL 中界面更新的时候调用,从而刷新屏幕的显示。

点击查看代码
/**
 * @brief  LCD 驱动的操作函数
 * @param  device LCD 设备结构体
 * @param  cmd 操作命令
 * @param  args 传入的参数
 * @retval None
 */
static rt_err_t drv_lcd_control(struct rt_device *device, int cmd, void *args)
{
    LOG_D("drv_lcd_control cmd is: %d\n", cmd);
    switch (cmd)
    {
        case RTGRAPHIC_CTRL_RECT_UPDATE:
        break;

        case RTGRAPHIC_CTRL_POWERON:
        {
            /* LCD 退出睡眠模式 */
            lcd_display_on();
            lcd_exit_sleep();
        }
        break;

        case RTGRAPHIC_CTRL_POWEROFF:
        {
            /* LCD 进入睡眠模式 */
            lcd_display_off();
            lcd_enter_sleep();
        }
        break;

        case RTGRAPHIC_CTRL_GET_INFO:
        {
            /* 获取 LCD 参数 */
            memcpy(args, &lcd_info, sizeof(lcd_info));
        }
        break;


        default:
            return -RT_EINVAL;
    }

    return RT_EOK;
}

static void lcd_fb_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
    rt_uint32_t px_size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);

    /* 设置屏幕刷新区域 */
    lcd_draw_area_set(area->x1, area->y1, area->x2, area->y2);

    /* 这里直接通过 SPI 发送数据,所以需要单独将数据引脚拉高 */
    rt_pin_write(LCD_DC_PIN, PIN_HIGH);

    /* SPI 发送时是 uint_8, 而像素是  uint_16 */
    rt_spi_send(spi_dev_lcd, color_p, px_size * 2);

    lv_disp_flush_ready(disp_drv);
}

/* 驱动函数实现的结构体 */
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops lcd_ops =
{
    drv_lcd_init,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    drv_lcd_control
};
#endif

/**
 * @brief  LCD 设备注册
 *
 * @param  None
 * @retval int 注册结果
 */
int drv_lcd_hw_init(void)
{
    rt_err_t result = RT_EOK;
    rt_uint32_t lcd_buff_size = LCD_HEIGHT * LCD_WIDTH * 2;

    device.user_data = lcd_fb_flush;

    /* 设置 LCD 设备信息 */
    lcd_info.height = LCD_HEIGHT;
    lcd_info.width = LCD_WIDTH;
    lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;
    lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565;     // 图像的格式(RGB:565)

    /* LCD 显示缓冲区,大小为显示一帧图像所需空间 */
    lcd_info.smem_len = lcd_buff_size;

#ifdef RT_USING_DEVICE_OPS
    device.ops     = &lcd_ops;
#else
    device.init    = drv_lcd_init;
    device.control = drv_lcd_control;
#endif

    /* 注册 LCD 设备 */
    result = rt_device_register(&device, "lcd", RT_DEVICE_FLAG_RDWR);

    return result;
}

INIT_DEVICE_EXPORT(drv_lcd_hw_init);

七、总结

从上面的过程可以看出,移植 LVGL 的过程很简单,最主要的是 lcd_fb_flush 函数的实现。需要注意的便是 lv_disp_flush_ready(disp_drv) 这个函数一定要添加,后面的界面可能不刷新,或者刷新不正常等现象。最后还需要添加一个头文件,如下所示

点击查看代码
#ifndef LV_CONF_H
#define LV_CONF_H

#define LV_COLOR_DEPTH          16


#endif /*LV_CONF_H*/

与STM32 + RT-Thread + LVGL相似的内容:

STM32 + RT-Thread + LVGL

一、基本信息 MCU:STM32F103ZET6 RT-Thread:5.0.2 LVGL:8.3.11 LCD:ST7735s 编译环境:RTThread studio 二、LVGL 移植要求 16、32或64位微控制器或处理器 建议速度大于16 MHz 闪存/ROM: > 64 kB(建议180

手把手实践丨基于STM32+华为云设计的智慧烟感系统

摘要:当前基于STM32和华为云,设计了一种智慧烟感系统,该系统可以检测烟雾,同时将检测到的数据上传到云端进行处理和分析。 本文分享自华为云社区《基于STM32+华为云设计的智慧烟感系统》,作者:DS小龙哥 。 一、概述 当前基于STM32和华为云,设计了一种智慧烟感系统,该系统可以检测烟雾,同时将

STM32 USB CDC调试记录

STM32 USB CDC调试 一、前言 最近在做STM32的IAP方案,官方提供的demo是基于USART实现,但是使用USART的话要和电脑通信要么借助USB转TTL工具;要么在板子上加一颗CH340类似的转换芯片。这就不是很方便,就想着直接可以通过USB线进行升级,所以USB CDC就进入我的

RTThread 自动网卡使用问题

最近使用 STM32 测试了一下 lwip 和 esp8266 的网络连接问题,使用 RTThread 的自动网卡时,发现不能很好的自动切换默认网卡,不能满足需求,所以自己简单的改了一下。 一、准备材料 MCU:STM32F103ZT6 RTThread:5.0.2 工具:RTThread stud

STM32F1和STM32F4系列DMA的不同之处——对STM32的DMA的工作机制和场景的一些理解[原创www.cnblogs.com/helesheng]

比较STM32F4和STM32F1系列的DMA控制器,区别主要有三:1)增加了DMA流(Stream)的概念;2)限制了两个DMA控制器的数据流向;3)为每个数据流添加了可配置的FIFO缓冲区。 本文逐一比较了以上三种硬件上的改变带来的功能方面的升级和不同。另外,还大胆猜测了STM32的芯片设计者对...

手把手实践丨基于STM32+NBIOT+华为云IOT设计智能井盖

摘要:本文介绍基于STM32微控制器、BC26 NBIOT模组和华为云IOT平台,实现了一款智能井盖系统。 本文分享自华为云社区《基于STM32+NBIOT+华为云IOT设计的智能井盖》,作者:DS小龙哥 。 一、概述 智能井盖是一种通过物联网技术实现对井盖状态监测和管理的设备。当前介绍基于STM3

STM32WB55 BLE双核flash擦写程序深度解析

简介 STM32WB55的flash擦除有两种机制,一种是只有单核运行下的flash擦除,这种模式下,flash擦除的步骤同其他STM32的flash擦除一样,直接调用HAL库中flash擦除的库函数即可;另一种是双核运行下的flash擦除,这种模式下,因为两颗CPU内核都会访问地址总线,可能会有访