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 kB)
- 内存:8 kB(建议24 kB)
- 1个帧缓冲器:在MCU、外部RAM或显示控制器中
- LVGL的图形缓冲:>“水平分辨率”像素(推荐1/10“屏幕尺寸”)
- C99或更新的编译器
- 基本的C(或C++)知识:指针、结构、回调
三、添加 LVGL 软件包
添加软件包


LVGL 文件目录

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 OS 学习笔记[二]
串口通讯例程 通过上面的练习,对STM32项目开发有了一个直观印象,接下来尝试对串口RS232进行操作. 1. 目标需求: 开机打开串口1,侦听上位机(使用电脑串口测试软件)发送的信息,然后原样输 ...
- STM32 + RT Thread OS 串口通讯
1. 创建项目 a) 禁用Finsh和console b) 默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1.因 ...
- STM32 + RT Thread OS 学习笔记[三]
RTGUI 据说RTGUI是多线程的,因此与RT-Thread OS的耦合度较高,有可能要访问RT-Thread的线程控制块.如果要移植到其它OS,估计难度较大.目前还处于Alpha状态,最终将会包含 ...
- STM32 + RT Thread OS 学习笔记[四]
1. 补注 a) 硬件,打通通讯通道 若学习者购买了学习板,通常可以在学习板提供的示例代码中找到LCD的相关驱动代码,基本上,这里的驱动的所有代码都可以从里面找到. 从上面的示意图可见,M ...
- RT Thread 通过ENV来配置SFUD,操作SPI Flash
本实验基于正点原子stm32f4探索者板子 请移步我的RT Thread论坛帖子. https://www.rt-thread.org/qa/forum.php?mod=viewthread& ...
- RT thread 设备驱动组件之USART设备
本文以stm32f4xx平台介绍串口驱动,主要目的是:1.RTT中如何编写中断处理程序:2.如何编写RTT设备驱动接口代码:3.了解串行设备的常见处理机制.所涉及的主要源码文件有:驱动框架文件(usa ...
- RT Thread的SPI设备驱动框架的使用以及内部机制分析
注释:这是19年初的博客,写得很一般,理解不到位也不全面.19年末得空时又重新看了RTThread的SPI和GPIO,这次理解得比较深刻.有时间时再整理上传. -------------------- ...
- RT Thread SPI设备 使用
后记: 之前,我把SPI的片选在Cubemx中配置成了SPI_NSS.现在我给它改为了GPIO_OUTPUT. 同时参考了别人的类似的一个操作无线模块(采用SPI设备驱动)的例子程序(清楚了RTT的 ...
- LwIP学习笔记——STM32 ENC28J60移植与入门
0.前言 去年(2013年)的整理了LwIP相关代码,并在STM32上"裸奔"成功.一直没有时间深入整理,在这里借博文整理总结.LwIP的移植过程细节很多,博文也不可能一一 ...
- USB虚拟串口 使用基于stm32的RT-Thread
参考我的RT Thread论坛文章 https://www.rt-thread.org/qa/thread-422644-1-1.html
随机推荐
- 力扣69(java&python)-x的平方根(简单)
题目: 给你一个非负整数 x ,计算并返回 x 的 算术平方根 . 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 . 注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0. ...
- EasyCV带你复现更好更快的自监督算法-FastConvMAE
简介: 近期FastConvMAE工作在EasyCV框架内首次对外开源,本文将重点介绍ConvMAE和FastConvMAE的主要工作,以及对应的代码实现,最后提供详细的教程示例如何进行FastCon ...
- 阿里云云效发布研发协同工具,以新的产研协同工作方式助力实现BizDevOps
简介:2021云栖大会云效BizDevOps分论坛上,阿里云云效技术负责人陈鑫发布阿里云云效产品研发协同工具支撑ALPD理论,以新的产研协同工作方式助力实现BizDevOps. 编者按:10月21日 ...
- [FE] iframe 相关选项 x-frame-options: 设置 meta 标签无效 & helmet
The X-Frame-Options HTTP 响应头是用来给浏览器 指示允许一个页面 可否在 <frame>, <iframe>, <embed> 或者 < ...
- [Mobi] 什么是手机 Root 和 Magisk、Magisk App
手机进行 Root 操作就是让我们能够拥有超级权限,包括被手机厂商所禁止的一些操作. 传统 Root 手段会修改系统文件,因而一些安全性要求较高的 App 会禁止自己在 Root 过的手机上运行. M ...
- [Trading] 如何应对股价下跌, 投资技巧
如果你想利用股价下跌的机会,你可以做以下事情来潜在地赚取利润. 为什么股价下跌会吓到投资者 商品在低价时,我们都倾向于买入,对于股票,价格暴跌以投资的形式损失你的钱,令人产生卖出情绪. 然而在卖掉之前 ...
- CSS属性继承问题,那些会被继承,哪些不继承?
总的来能被继承的就是三大类 一,字体有关的的,font-开头的 二,文本有关的,text- 开头的 三,visibility , cursor 其它的基本都是不能被继承 比如说这个,你以为它继承了ma ...
- Maven的核心解压与配置
Maven的核心解压与配置 @ 目录 Maven的核心解压与配置 1. Maven 官网地址 2. 解压Maven核心程序 3. 指定本地仓库 4. 配置阿里云提供的镜像仓库 5. 配置 Mave ...
- nim 2. 变量与函数
对于系统的学习,官网上的相应的资料, 这里通过一个简单的例程,演示一下. 这个例程里有一个根据半径计算圆面积的方法,以及调用的示例: 演示代码 1 #计算圆的面积 2 proc calcArea(r: ...
- $KMP$学习记
<不浪漫罪名>--王杰 没有花 这刹那被破坏吗 无野火都会温暖吗 无烟花一起庆祝好吗 若爱恋 仿似戏剧那样假 如布景一切都美化 连相拥都参照主角吗 你说我未能定时 令你每天欢笑一次 我没说 ...