尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

中景园1.8寸128x160屏LVGL移植全流程:驱动、显存与SPI配置实战

中景园1.8寸128x160屏LVGL移植全流程:驱动、显存与SPI配置实战 简介面向嵌入式开发者的LVGL移植工程将开源图形库LVGL成功适配到中景园1.8寸128x160分辨率显示屏上解决小内存微控制器上构建图形界面这一典型需求。资源基于STM32F10x标准外设库包含LCD显示驱动、LVGL中间件、Keil工程配置及字体资源适合正在学习LVGL或需要快速在1.8寸屏上实现交互界面的开发者参考。包内共412个文件以204个C源码和183个头文件为主另有Makefile脚本、启动汇编文件、字体文件及Keil工程文件等整体压缩包仅3.86MB目录结构清晰便于按模块查阅。已有804人学习下载。读者可以从中获取完整的显示驱动适配思路、LVGL配置与移植步骤以及STM32硬件初始化代码工程内的HARDWARE、Middlewares、USER等目录划分也为类似嵌入式项目提供了可复用的组织参考是一份便于对照学习和二次开发的样例工程。 拿到中景园那块1.8寸128x160屏幕的LVGL代码时我第一反应是这就能跑了结果还真不是。市面上中景园屏的驱动例程满天飞但多数是单色屏的刷图demo真正能把LVGL完整跑起来的工程驱动、显存、心跳、输入设备这四样缺一不可。这篇记录一下我把中景园1.8寸128x160的LVGL代码移植到自己板子上的全过程包括踩过的坑和最后跑通的配置适合手里有同款屏、想在STM32或ESP32上快速跑起LVGL的朋友。1. 移植前必须想清楚的三件事1.1 拿到中景园例程先看什么中景园官方给的例程一般包括LCD驱动、SPI配置和测试代码但我第一次打开时差点被绕晕。例程里Initialization函数是屏幕驱动IC的初始化序列一堆数组赋值看起来枯燥却是整个移植的地基。你要先确认手里的屏幕是ILI9341还是ST7735S驱动IC——1.8寸128x160绝大多数是ST7735S但偶尔有改良版用ST7735R两者初始化代码略有差异。中景园例程文件名一般会带IC型号比如LCD_ST7735S.c别拿错。另外例程里的SPI是软件模拟还是硬件SPI直接决定了后续要不要改时序。中景园默认给的是GPIO模拟SPI好处是任意引脚都能用坏处是刷屏慢。要想跑LVGL流畅强烈建议改成硬件SPI。我当时第一次移植图省事直接沿用软件模拟SPI结果40MHz主频下全屏刷新要1秒多动效卡成PPT。后面改成硬件SPI直接起飞。1.2 屏幕驱动IC和接口类型确认1.8寸128x160屏幕常见的有两种接口一种排针是4线SPI控制引脚另一种是8位并口。中景园这块基本上都是SPI接口——CS、DC、RST、BL、SCLK、MOSI共6根线。注意ST7735S只支持SPI模式不像ILI9341那样还能切并口这也简化了接线。关键点在于DC引脚数据/命令选择和RST引脚。其中DC引脚必须单独接GPIO不能并到SPI的CS上。有些新手图省事想用硬件NSS引脚控制CS结果发现CS和DC共用了一个GPIO导致初始化序列全部乱套。我自己经历过一次屏幕白屏排查了半天才发现是这个低级错误。RST引脚可以直接接MCU的GPIO控制也可以接RC复位电路。但如果是LVGL场景建议GPIO控制复位这样可以在初始化之前做一次完整的硬件复位降低因为电源时序不稳定导致白屏的概率。1.3 LVGL版本选择和内存预算LVGL目前主流的稳定版是8.x系列和旧版7.x差别挺大尤其是配置文件和刷新机制。移植时最忌讳直接在网上找一份7.x的旧教程硬套到8.x上API名字、结构体定义都对不上会折腾很久。我的建议是直接上8.3.x或8.4.x它们的中文文档和社区资料都比较全遇到问题也好查。内存预算是很多人忽略的重灾区。LVGL在default模式下需要给flush buffer分配一块显存区域——严格来说是两个buffer每个buffer的大小至少是屏幕一行像素的字节数。128x160分辨率RGB565格式下一行像素是128*2256字节但实际建议直接给整块屏幕一半大小的缓冲128x160x2/220480字节也就是20KB。这个数字在STM32F103上已经有点吃紧了但能跑。如果你的MCU RAM只有20KB左右建议把buffer降为单行缓冲虽然刷新慢一点但至少能跑起来。内存分配函数也要做适配。LVGL默认使用标准库的malloc/free但在嵌入式环境里经常需要重定向到FreeRTOS的pvPortMalloc/vPortFree否则多线程环境下内存管理会出问题。这一条在裸机环境下可以跳过但只要上了RTOS必须改。2. 驱动层移植从中景园例程到LVGL的桥梁2.1 底层SPI读写函数要改什么驱动层移植的核心是三个函数初始化函数、写命令函数、写数据函数。中景园例程里这三个函数基本都能直接用但有个细节要注意——例程里写命令和写数据是分开关CS的而LVGL的flush回调要求一次完整事务内连续发送整块数据。如果你不做任何修改直接调LVGL可能导致CS被频繁拉高拉低屏幕花屏。解决办法有两种一种是把CS引脚在初始化后直接拉低不参与切换另一种是在写数据函数里不关CS只在写命令函数开头拉低CS、结尾拉高CS。我实际测试下来第二种更稳因为LVGL的flush经常会在命令和数据之间来回切换如果CS到处跳硬件SPI的NSS配置也会乱。ST7735S的写命令和写数据本质上是DC引脚的高低电平。写命令时DC拉低写数据时DC拉高。这一点在移植LVGL的flush函数时要特别注意——flush时要发Memory Write命令0x2C然后在DC为高的情况下连续发送像素数据。注意ST7735S的窗口设置命令是CASET0x2A和RASET0x2B和ILI9341完全一样。如果你之前只写过ILI9341的驱动这块可以无缝切换。但两者的初始化序列完全不同不能拿ILI9341的初始化代码直接跑ST7735S会白屏或者显示颜色错乱。2.2 颜色格式为什么必须是RGB565LVGL默认的颜色深度在lv_conf.h里配置常见的有LV_COLOR_DEPTH 16和LV_COLOR_DEPTH 32。中景园1.8寸屏幕是16位色RGB565所以LVGL这边必须配置为16位同时屏幕驱动的颜色宏也要保持一致。我在第一次移植时犯过一个错误LVGL配置为16位色但驱动函数的颜色格式宏写成了RGB888导致显示出来的颜色全部偏色。后来排查发现ST7735S的颜色字节序是高位在前RGB565的两个字节是先高位后低位如果MCU是小端模式直接memcpy过去就会高低字节颠倒。解决办法是在flush里手动交换高低字节或者调整SPI的数据发送顺序。如果你的屏幕显示的颜色整体偏蓝或者偏红大概率就是RGB和BGR的映射反了。ST7735S有一个命令叫MADCTL0x36它的bit3控制RGB/BGR顺序。中景园例程里默认是RGB但不同批次屏幕可能不一样如果偏色就改这个bit。2.3 flush回调怎么实现最稳LVGL的flush回调是移植中最关键的环节它的作用是把LVGL绘制好的一整块显存数据送到屏幕。具体实现是先设置窗口再发送像素数据。窗口设置用CASET和RASET分别指定x0/x1和y0/y1然后在DC拉高的状态下把整个buffer发出去。流程上最关键的是发送完数据后要调用lv_disp_flush_ready函数告诉LVGL这块buffer已经用完可以继续绘制了。如果你漏掉这一句LVGL会一直等待界面卡死看起来像是MCU跑飞了其实只是buffer没释放。如果用双缓冲flush要配合“两个buffer交替使用”的机制。LVGL在绘制一帧画面时会按照设定的刷新模式自动切换buffer地址这个逻辑由LVGL管理驱动层只需要保证flush完成后调用lv_disp_flush_ready即可。我当时自作聪明在flush函数里加了一个延时等待SPI发送完结果反而把双缓冲机制打乱了卡顿更明显。正确做法是不加延时用DMA或者SPI中断去判断是否发送完成。2.4 硬件SPI的DMA配置心得跑LVGL如果不给SPI开DMACPU会被刷屏占掉一大半界面跟手都是问题。以STM32为例SPI1的TX可以映射到DMA1的Channel3配置成Memory-to-Peripheral模式数据宽度16位开启循环模式。这样一次flush只需要启动一次DMA传输后续全部由硬件搬数据CPU可以专心跑LVGL的任务。但DMA也有坑——SPI的波特率不能太高。我试过把SPI时钟调到36MHzDMA传输直接乱码屏幕出现横条纹。原因是我的板子的PCB布线没有做阻抗匹配高频信号的振铃效应导致数据错位。后面降到18MHz稳如老狗。如果你用的是ESP32SPI时钟可以顶到40MHz甚至80MHz但前提是杜邦线要短最好控制在10cm以内。提示DMA传输完成后记得清SPI的EOT标志位否则下一次传输可能失败。不同MCU库的处理方式不一样ST库是SPI_I2S_GetFlagStatus判断标志HAL库是查询hspi-Instance-SR寄存器。这个细节不处理DMA模式下的二次刷新会随机性失败。3. LVGL适配细节让界面跑起来还需要做什么3.1 tick心跳LVGL的时间基准LVGL的所有动画、超时、长按检测都要依赖系统心跳。裸机环境下用SysTick中断每1ms调用一次lv_tick_inc(1)FreeRTOS环境下可以用vTaskDelay(1)创建一个死循环任务每隔1ms调用一次lv_tick_inc。有个坑是心跳任务的优先级。如果优先级比lvgl任务低动画会一顿一顿的太高了又会抢CPU。我一般是把心跳任务优先级设为等于或略高于LVGL任务。更省事的做法是直接用LVGL配置里的LV_TICK_CUSTOM1表示启用自定义tick这样只需实现lv_tick_get函数即可内部自动处理。另外如果开了LVGL的日志功能心跳不准时日志会显示“tick is not increasing”这时检查一下是中断没开还是时间基准的定时器没配置。这个问题排查起来不难但容易让人误判为LVGL移植失败。3.2 配置文件lv_conf.h的关键宏lv_conf.h是LVGL的核心配置一般放在工程根目录通过LV_CONF_INCLUDE_SIMPLE宏引用。移植时我重点关注这几个宏LV_COLOR_DEPTH设为16对应RGB565。LV_MEM_SIZELVGL自带的内存池大小至少设为16KB建议32KB。太小的话复杂控件无法创建界面直接崩溃。LV_HOR_RES_MAX和LV_VER_RES_MAX分别设为128和160匹配屏幕分辨率。这两个值影响LVGL内部buffersize的计算。LV_DPI根据屏幕尺寸设为100左右。1.8寸屏幕实际PPI比较高但LVGL的DPI只影响控件尺寸缩放不用太纠结。配置完这些后要重新编译整个工程。LVGL的配置文件头保护区会保留之前的内容新版修改后只需要清理并重新编译不要手动删掉保护区的代码。3.3 输入设备和屏幕方向调整LVGL要支持触摸或者按键必须注册输入设备驱动。中景园1.8寸很多是裸屏不带触摸如果你外接了独立的触摸板驱动方式和屏幕驱动类似同样需要实现read_cb回调。屏幕方向通过ST7735S的MADCTL寄存器控制可以切换横竖屏。128x160是竖屏分辨率如果你要做横屏需要把LV_HOR_RES_MAX和LV_VER_RES_MAX对调同时修改MADCTL中的MV、MX、MY位。这里有个容易混淆的点MADCTL的bit5是MV行/列交换bit6是MX左右镜像bit7是MY上下镜像。三个位组合能实现4个方向但每个方向对应的坐标范围要同步调整。注意修改屏幕方向后触摸坐标也要做相应旋转否则点和图标对不上。触摸旋转可以用lv_indev_set_rotation函数也可以在read_cb里手动转换坐标两种方式都可以。4. 实际移植中遇到的问题和排查方法4.1 白屏、花屏、黑屏的三个方向白屏大概率是复位或CS/DC配置问题。第一步检查RESET引脚有没有正常拉高拉低时间够不够第二步检查CS有没有正确拉低第三步用示波器看DC引脚在写命令时是否为低电平。花屏要重点查SPI速率和字节序。把SPI波特率降低一半试一下如果花屏消失就是速率过高如果颜色错乱就是RGB565字节序问题。另外也要检查flush回调中是否每帧都正确调用了lv_disp_flush_ready忘记调用会导致刷新一半就停止。黑屏最常见的原因是初始化序列根本没执行成功或者LVGL的buffer全为0x00。可以用一个简单的方法定位在初始化屏幕后、LVGL启动前直接往屏幕写一帧纯红色数据。如果能显示红色说明驱动OK问题在LVGL层如果还是黑屏问题在驱动层。4.2 LVGL内存不足导致控件闪退如果界面运行时突然跳回初始画面或者控件创建后不显示基本可以判定是内存不足。LVGL的失败日志一般会打印lv_mem_alloc失败的信息。我的建议是在lv_conf.h里把LV_MEM_SIZE先设大一点跑通后再慢慢调小找一个临界值。另外检查是否开启了LV_USE_MEM_MONITOR这个宏开启后可以把内存使用情况打印出来对调试帮助很大。跑一个包含label、button、slider的页面看峰值内存是多少再决定最终内存池大小。4.3 汉字显示不出来怎么处理LVGL显示中文需要先把字库转成C数组再用lv_font_t结构引用。常用的转换工具是LVGL官方的字体转换器也可以在线转换。1.8寸屏幕上显示中文建议字号不超过16px否则太糊。我在中景园例程上试过直接调用lv_label_set_text显示中文默认字体只有ASCII中文显示成方块。需要额外加载一个中文字体文件之后再设置label的样式为这个字体。计算字库空间时要注意一个16px简体中文字库包含常用汉字大概几十KB直接放大MCU Flash里RAM不用额外增加。5. 这波移植的总结与心得整个移植过程走下来个人感觉最花时间的不是驱动代码本身而是LVGL的配置和内存规划。SPI驱动只要看懂中景园例程里的初始化序列改成硬件SPI加DMA性能就上来了反倒是内存冲突、tick不准这类问题在裸机和RTOS下的表现完全不同需要分别对待。如果再让我移植一次我会先把屏幕方向、颜色格式、分辨率这些硬性参数确定好然后直接跑LVGL自带的demo程序验证基础显示再陆续加入自己的控件逻辑。这样每一步都有明确的验证标准遇到问题能快速定位而不是一头扎进去改一堆东西然后找不到bug。本文还有配套的精品资源点击获取
返回列表