多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于瑞萨RA MCU+FreeRTOS+LVGL V8的智能家居仪表盘开发

基于瑞萨RA MCU+FreeRTOS+LVGL V8的智能家居仪表盘开发 最近把家里一堆分散的传感器数据、灯光开关、室内温湿度集中到了一块屏上做了一个基于瑞萨RA MCU的智能家居仪表盘。项目本身拿了瑞萨RA MCU创意氛围赛的参赛编号但对我来说更重要的收获是完整趟了一遍FreeRTOS LVGL V8在Cortex-M33平台上的开发流程从FSP图形化配置生成工程、FreeRTOS任务划分、LVGL V8的移植与适配到界面控件的组合、帧缓冲调优、触摸输入处理再到MQTT数据上屏和本地场景联动。整个过程踩了不少坑也积累了一些常规文档里不会写的经验这篇文章就当作一次完整的项目复盘。这篇文章适合正在做嵌入式GUI开发、想学FreeRTOS任务调度、或者打算在STM32/RA等MCU上跑LVGL的工程师。哪怕你之前只在单片机上点过LED、写过裸机轮询只要有一块RA开发板和一块SPI接口的屏幕按文中的步骤也能把这个仪表盘搭起来。我会尽量把“为什么这么做”讲清楚而不只是贴代码。1. 项目概述与整体方案拆解1.1 这个仪表盘到底做了什么先说说项目最终呈现的样子。一块4.3英寸的TFT屏幕作为主界面首页分成几个功能卡片左上角是日期时间和天气信息中间是室内温湿度实时曲线下方排列着阳台灯、客厅灯、加湿器等设备的开关控制右上角有一个室内空气质量指示。切换页面还能看到历史温湿度曲线、设备告警日志、路由器连通的几个网络节点状态。听起来功能不少但MCU端其实只做三件事第一周期性采集传感器数据温湿度、光照、PM2.5等第二通过MQTT协议跟家里的网关通信既上行上报数据也下行接收设备控制指令第三用LVGL把数据画到屏幕上同时把触摸事件转换成控制指令。这三件事分别对应着三个独立的FreeRTOS任务互不阻塞各自维护自己的生命周期。选这个方向的原因很简单智能家居仪表盘是一个非常适合展示“MCU RTOS GUI”三者协作的载体。它有输入触摸、传感器、网络报文、有输出屏幕刷新、继电器/灯光控制、有实时性要求曲线更新、告警弹窗而且无论是做产品原型还是个人DIY都有明确的落地场景。1.2 主控选型为什么用瑞萨RA系列主控选的是瑞萨RA6M4Cortex-M33内核主频200MHz2MB Flash640KB SRAM。选这颗芯片有几个很实际的原因性能足够跑LVGL。LVGL V8在Cortex-M33 200MHz上跑SPI屏幕不做复杂特效的情况下帧率能做到20-30 FPS完全能满足仪表盘的交互需求。FSPFlexible Software Package配置外设极其方便。时钟树、引脚复用、中断优先级、DMA通道全部在e² studio里图形化配置代码自动生成不用像以前那样查数据手册翻寄存器。FSP对FreeRTOS有官方集成。在FSP的配置界面里勾选FreeRTOS系统会自动完成内核移植、启动调度器、生成线程模板省掉了最痛苦的移植步骤。RA6M4的容量够大。LVGL本身需要一块不小的动态内存加上显示缓冲、任务栈、网络协议栈RAM少于256KB的芯片会非常紧张640KB SRAM让整个项目从容很多。当然用RA4系列例如RA4M3也能跑只是RAM小一些需要把显示缓冲调小、减少LVGL的缓存池代码层面基本可以复用。1.3 软件架构FreeRTOS LVGL V8为什么是最优组合软件层面锁定两个选型FreeRTOS和LVGL V8。FreeRTOS是市场占有率最高的嵌入式RTOS资料多、问答多、面试题多团队招人也好招。虽然FSP里默认也集成了ThreadX但ThreadX在国内嵌入式圈子的普及度远不如FreeRTOS遇到问题想搜个解决方案都费劲。所以即便瑞萨官方对ThreadX的支持也很好我还是选了FreeRTOS——求稳求社区资料丰富。LVGL则锁定了V8分支具体是8.3.x。说实话现在LVGL已经出到V9甚至V10了V9对渲染架构做了大改引入了更多面向复杂UI的特性但内存占用也明显上涨驱动接口变化大网上能查到的资料很多还停留在V8。V8.3是LVGL官方在嵌入式MCU上最成熟稳定的版本API文档齐全、SquareLine Studio可以直接导出适配V8的代码、中文社区教程丰富。对一个要稳定交付的项目来说选V8是投入产出比最高的选择。V9那套渲染加速、GPU适配的玩意为时尚早等它在MCU生态上沉淀一段时间再说。2. 硬件组成与开发环境搭建2.1 整机硬件链路我的硬件拓扑大概是这样的主控板瑞萨RA6M4开发板EK-RA6M4板载调试器直接用USB线连电脑就能下载程序。屏幕4.3寸TFT LCDSPI接口驱动IC是ST7789分辨率480x272带电阻触摸或电容触摸。SPI屏是首选因为RA6M4内部没有LCD控制器RGB并口屏会占用大量引脚在布线上会很痛苦。传感器一个I2C接口的温湿度传感器SHT30、一个模拟输出的光照传感器GUVA-S12SD、一个串口输出的PM2.5激光粉尘传感器。网络模块ESP8266串口WiFi模块通过AT指令集连接家里的路由器走MQTT协议与本地网关通信。执行器几个5V继电器模块用来演示开关灯、开关加湿器。这种“主控MCU 串口WiFi模块”的架构在当前阶段比较务实。RA6M4本身不带无线能力外挂一颗ESP8266或者ESP32模块成本很低而且把AT指令解析做个独立任务网络异常时不影响本地的显示和控制。2.2 e² studio FSP创建工程勾选FreeRTOS很多人第一次接触瑞萨芯片会觉得陌生其实用惯了STM32CubeMX之后e² studio FSP的套路几乎一模一样。创建工程的步骤如下在e² studio里新建C/C项目芯片型号选择R7FA6M4AF。打开FSP配置界面先配置时钟。把主时钟设为240MHzEK板载晶振通常是24MHz通过PLL倍频外设总线时钟也就是PCLKD设为120MHz这样SPI外设的时钟源就够干净了。配置引脚。SPI屏幕占用SCK、MOSI、CS、DC、RST、BL这六根线触摸I2C占用SDA、SCL两根加上传感器I2C——注意RA6M4有多个I2C外设注意引脚复用别冲突。在“Stacks”页面添加FreeRTOS。FSP会自动把FreeRTOS内核源码加进工程并且在启动时自动调用vTaskStartScheduler()。不需要像STM32裸机移植那样手动改startup文件。在FreeRTOS配置里创建三个线程task分别对应UI任务、传感器任务、通信任务。FSP生成的线程直接对应FreeRTOS的Task函数名字、栈大小、优先级在配置界面里就能填好。这里有个坑需要特别注意FSP里默认的FreeRTOS堆大小configTOTAL_HEAP_SIZE是8KB对LVGL来说远远不够。LVGL的动态内存池如果复用FreeRTOS的堆至少要给到120KB以上否则运行一段时间后控件创建就会失败。我后面会把Heap大小调到200KB并在配置里确认开关configUSE_HEAP_SECTION没有把堆限制到某个特殊内存区域。2.3 显示与触摸驱动的接线方式屏幕这边的接线我直接列一个表格方便抄作业屏幕引脚RA6M4引脚说明SCKP409SPI0 SCKSPI时钟最高20MHzMOSIP410SPI0 MOSI主机发送数据CSP400GPIO片选低有效DCP401GPIO数据/命令切换RSTP402GPIO硬件复位BLP403GPIO背光控制可PWM调亮度SDA触摸P512I2C1 SDA触摸芯片数据线SCL触摸P511I2C1 SCL触摸芯片时钟线触摸芯片常见的是GT911或者FT5x06。FT5x06的手感更跟手GT911支持多点触摸。我这个项目用的是FT5x06单点触摸对仪表盘场景完全够用。接线时最容易被忽略的是DC引脚的时序要求。ST7789要求DC引脚必须在SCK拉低之前就切换到正确的电平如果DC和SCK走线过长或者GPIO翻转太慢会出现颜色错乱或者花屏现象。调试时如果遇到诡异的颜色偏色先怀疑DC引脚的GPIO速度配置别急着怀疑驱动代码。3. FreeRTOS任务划分与数据流设计3.1 三个任务各司其职FreeRTOS在这个项目里不是摆设它是整个软件系统的骨架。我把整个应用拆成了三个任务加一个中断回调UI任务优先级2栈大小4096字唯一允许调用LVGL API的任务负责调用lv_timer_handler()、处理触摸事件、刷新屏幕。传感器任务优先级1栈大小2048字定时读取SHT30、光照传感器、PM2.5传感器数据解析后封装成结构体通过消息队列发给UI任务。通信任务优先级2栈大小4096字轮询ESP8266收到的串口数据解析AT指令返回和MQTT报文把设备控制指令提取出来后发给UI任务同时把传感器任务发布的数据打包成MQTT报文发出。滴答定时器回调在FreeRTOS的SysTick里或者一个高优先级定时器中断里调用lv_tick_inc(1)给LVGL提供时间基准。这里有一个设计原则LVGL不是线程安全的V8版本尤其不建议在多个任务里同时调用lv_*函数。所以我把所有LVGL操作锁死在UI任务里其他任务想更新界面只能通过队列把数据丢给UI任务。宁可多写一层数据结构转换也不要让两个任务同时操作LVGL的对象树——那是系统崩溃最快的路径。3.2 任务间通信队列、信号量、互斥锁的取舍任务间通信我用了三种FreeRTOS的IPC机制各有各的用处消息队列传感器任务采集到的温湿度数据通过xQueueSend()发送给UI任务。UI任务在lv_timer_handler()前后接收队列收到数据就更新对应的lv_label或lv_chart。队列长度设成8数据没被及时取走时自动覆盖旧数据避免传感器任务被阻塞。二值信号量通信任务每收到一条完整的MQTT报文就释放一个信号量通知UI任务。UI任务在空闲时等待信号量收到后解析报文并更新界面控件状态。用二值信号量而不是队列是因为报文内容已经被通信任务解析成结构体放到全局变量里了UI任务只需要知道“有新数据到了”。互斥锁Mutex这个项目里只用于保护一段共享内存区域即传感器数据的原始缓冲区。传感器任务写入、通信任务读取两边加同一个互斥锁防止读到半个结构体。实际调试中我发现一个有意思的问题如果在UI任务的lv_timer_handler()里直接调用xQueueReceive()等待队列数据一旦队列为空lv_timer_handler()就会被卡住整个界面刷新停顿。最好的做法是先调用lv_timer_handler()让它处理完这一帧的渲染任务再去非阻塞地接收队列数据设置超时为0。这样既保证了LVGL的刷新频率又不会漏掉传感器数据。3.3 任务栈大小估算与堆栈溢出检测任务栈大小这玩意儿没有公式能一次算准只能“估算 压测 修正”。我一开始给UI任务分了2048字8KB结果跑起来大概20分钟就死机一次最后查出来是任务栈溢出了——LVGL在创建控件、格式化字符串时需要不小的临时栈空间特别是调用lv_label_set_text_fmt()这类格式化函数时栈消耗可能瞬间涨到几千字节。几个有效的排查手段开启FreeRTOS的堆栈溢出检测功能。把configCHECK_FOR_STACK_OVERFLOW设为2实现vApplicationStackOverflowHook()函数在钩子里把系统状态打印出来或者拉一个GPIO电平翻转方便发现溢出点。利用uxTaskGetStackHighWaterMark()读取栈剩余最小水位打印出来看各任务栈的实际峰值。我实测下来UI任务峰值栈用量约3500字所以栈大小给了4096字传感器任务峰值约1200字给2048字就很宽裕。如果你用的是FSP自带的FreeRTOS集成注意在FreeRTOS配置界面里手动开启configCHECK_FOR_STACK_OVERFLOWFSP的默认配置里这个功能是关闭的。开发阶段务必打开能救你无数次。4. LVGL V8移植与界面实现细节4.1 LVGL V8在RA6M4上的移植要点LVGL的移植分两部分显示驱动和输入驱动。过程不复杂但细节多。首先把LVGL V8的源码下载下来放到工程的src目录下。在lv_conf.h里开启LV_CONF_INCLUDE_SIMPLE然后把关键配置项调好#define LV_COLOR_DEPTH 16 #define LV_MEM_SIZE (128U * 1024U) /* LVGL动态内存池128KB */ #define LV_TICK_CUSTOM 1LV_COLOR_DEPTH设为16位色因为ST7789就是RGB565屏16位既能节省一半缓冲内存也避免了颜色格式转换的CPU开销。LV_MEM_SIZE给128KB这个容量对仪表盘界面来说刚刚好控件数量多、图表数据点多的时候不会频繁触发内存分配失败。然后是显示驱动的flush回调。LVGL在渲染完一帧局部更新后会调用disp_flush()把像素数据交给底层驱动。我的实现是static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { // 1. 设置显示窗口区域 ST7789_SetWindow(area-x1, area-y1, area-x2, area-y2); // 2. 片选拉低 ST7789_CS_LOW(); // 3. 通过SPI DMA发送像素数据 SPI_SendData((uint8_t *)color_p, (area-x2 - area-x1 1) * (area-y2 - area-y1 1) * 2); // 4. 发送完成中断里调用 lv_disp_flush_ready(disp_drv) }第4步很关键。LVGL V8要求flush函数在数据真正发送完成之后调用lv_disp_flush_ready()通知LVGL“这块缓冲区可以继续用了”如果过早调用LVGL可能在上一次DMA还没传完时就往同一块缓冲区写入新数据产生撕裂和闪烁。我这边是通过SPI的传输完成中断来调用lv_disp_flush_ready()。输入驱动的移植更简单一些。在read_cb回调里读取FT5x06的触摸坐标然后调用lv_indev_read_cb把坐标填充给LVGL。注意LVGL的坐标原点在屏幕左上角而触摸芯片的坐标系可能需要镜像翻转具体看屏幕的安装方向。如果发现触摸点跟实际点击位置呈镜像关系在回调里对x或y做一次坐标变换即可。4.2 仪表盘界面设计与控件组合界面设计上我没有用现成的模板而是在LVGL的默认风格基础上做了一套卡片式布局。核心思想是用lv_obj作为卡片容器设置背景色、圆角半径、阴影再往容器里放lv_label、lv_chart、lv_switch、lv_arc等控件。首页的布局大概是这样的顶部状态栏高度40像素背景深色左侧放WiFi图标和信号强度中间放当前时间每秒刷新一次右侧放设置图标。中间主区域一个lv_chart控件显示温湿度历史曲线。图表的y轴范围根据季节动态调整数据点每秒追加一个保留最近300个点。底部卡片区三个功能卡片并排每张卡片是一个容器里面有图标、标题、数据值。点一下某个卡片弹出一个包含lv_switch和滑动条的控制面板用来控制对应设备。在V8里控件的样式调整用的是“style”机制。比如给卡片加圆角和阴影的代码大概是static lv_style_t style_card; lv_style_init(style_card); lv_style_set_bg_color(style_card, lv_color_hex(0xFFFFFF)); lv_style_set_radius(style_card, 12); lv_style_set_shadow_width(style_card, 10); lv_style_set_shadow_color(style_card, lv_color_hex(0xCCCCCC)); lv_style_set_pad_all(style_card, 12); lv_obj_t * card lv_obj_create(parent); lv_obj_add_style(card, style_card, 0);这里有一个LVGL的易错点lv_style_set_*系列函数设置的是样式的默认值但如果你要先修改某个已存在控件的局部样式需要额外使用LV_STATE_DEFAULT状态。另外V8引入了一个叫“局部样式覆盖”的概念——同一个控件可以通过lv_obj_add_style(card, style_card, LV_PART_MAIN)来指定样式作用域我建议刚开始接触时先把样式都存成全局静态变量不要写到函数里否则每次进入界面时都会重新分配样式内存时间长了容易出现内存碎片。4.3 图形性能调优帧缓冲策略与DMA加速LVGL刷新慢会导致两个问题一是界面卡顿体验差二是CPU占用过高挤压其他任务的时间。这一节给几个实测有效的调优手段。第一个手段是合理选择缓冲模式。LVGL V8支持三种显示缓冲模式模式说明适用场景单缓冲一次渲染一行最省内存RAM极小64KB双缓冲两块局部缓冲轮流使用推荐RAM 64-200KB全屏缓冲一整块帧缓冲渲染显示不串行RAM充足且需要最高性能我这个项目用的是“双缓冲 局部刷新”。屏幕分辨率480x272一帧的完整缓冲需要480x272x2≈261KB对640KB的RAM来说虽然能放得下但加上LVGL的128KB内存池、三个任务栈、协议栈RAM就捉襟见肘了。所以我把缓冲设为“半行”模式两块高32像素、宽480像素的局部缓冲每块约30KB。LVGL内部会把屏幕分成多个小块来渲染实测刷新一屏大概3-5ms肉眼完全无感。第二个手段是SPI时钟和DMA。RA6M4的SPI外设最高可以跑到50MHz左右但受限于屏幕走线的抗干扰能力我最终锁在20MHz。刷新时用DMA搬运数据CPU不用等SPI字节发送等DMA完成后在中断里调用lv_disp_flush_ready()。配合双缓冲CPU每帧花在显示上的时间不到1ms。第三个手段是关闭用不到的特效。LVGL V8里有一些高级效果比如模糊、阴影、渐变这些在MCU上都是吞噬性能的黑洞。我只保留了阴影和圆角把LV_USE_SHADOW保持开启但阴影宽度控制在20像素以内把LV_USE_GRADIENT关掉。5. 数据采集、网络接入与场景联动5.1 传感器数据上屏的完整链路传感器数据从采集到显示走的是这样一条链路传感器任务每2秒唤醒一次通过I2C读取SHT30的温湿度数据通过ADC读取光照传感器电压值并换算成Lux通过UART读取PM2.5传感器的串口指令结果。传感器任务把数据封装成一个SensorData结构体通过队列发送给UI任务。UI任务收到SensorData后更新温湿度lv_label的文本、追加历史曲线数据点、刷新空气质量的仪表盘弧线。与此同时通信任务每2秒从全局区域读取最新的SensorData打包成JSON格式通过ESP8266的MQTT发布到home/room/env主题。这个过程中最容易出的问题是传感器任务和通信任务同时访问SensorData全局变量导致的读到半截数据。我的解法是专门用一个互斥锁保护这个全局区域读取和写入都先上锁。虽然会带来一点延迟但对2秒级的采集周期来说完全无感。5.2 基于ESP8266的MQTT数据交互ESP8266模块通过UART口连接RA6M4波特率115200。通信任务的做法是开机后发送ATCWMODE1设置Station模式然后通过ATCWJAP连接WiFi热点。连接成功后通过ATCIPSTARTTCP,...建立到MQTT broker的TCP连接这里用的是本地路由器上跑的一个轻量MQTT服务比如Mosquitto。MQTT协议本身比较重直接在MCU上跑完整协议栈对内存不友好。我的做法是用ESP8266上的AT指令接入TCP层然后在MCU端实现一个极简MQTT客户端只实现CONNECT、PUBLISH、SUBSCRIBE、PINGREQ几条报文手工拼接报文即可。这里不展开MQTT报文格式的细节但有一点值得提醒MQTT的心跳包PINGREQ间隔不要太长。ESP8266模块如果长时间没有发送数据路由器可能把TCP连接踢掉。我设的是30秒一个心跳包实测运行一周没掉线。通信任务还负责接收下发的控制指令。MQTT broker收到手机App或者自动化规则发的home/room/device/set消息后会把消息推给ESP8266ESP8266通过串口把原始TCP数据交给MCU。通信任务解析出JSON字段里的设备ID和开关状态然后写一个全局的DeviceCtrl结构体并释放二值信号量通知UI任务更新开关状态。5.3 本地联动逻辑设计仪表盘不是只做展示我还在代码里做了一条简单的联动逻辑当室内温度超过28度时自动打开风扇继电器当湿度低于40%时自动打开加湿器。这个逻辑放在传感器任务里每次采集数据后判断一次阈值。因为优先级较低即使联动判断稍微卡一下也不会影响UI刷新和触摸响应。联动逻辑里有一个实际考虑阈值判断里加了“最近一次操作时间”的滤波避免设备在阈值边界反复抖动频繁开关。程序是这样的只有当温度连续三次采样都超过28度时才执行“打开风扇”动作一旦超过就会进入至少5分钟的保持窗口。这个简单状态机有效解决了继电器不断通断的问题也是实际项目里非常常见的需求。6. 常见问题与调试实录6.1 白屏、花屏、颜色错乱的排查白屏先查三处背光信号、复位时序、SPI初始化是否完成。背光引脚如果被复用成别的外设屏幕就会黑屏或者白屏这是最早容易踩的坑。复位的做法是上电后拉低至少10ms再拉高如果初始化代码里没有延时屏幕中的驱动IC可能还处于复位态自然不工作。花屏和颜色错乱多半是行偏移参数不对。ST7789这类驱动IC默认的输出扫描方向和屏幕玻璃的引脚绑定不匹配时需要在初始化代码里设置偏移寄存器GASET、VASET。比如显示内容整体上移了几行、左移了几列大概率就是偏移寄存器没配对。还有一个经常被忽略的点如果SPI时钟信号波形塌陷也会导致花屏。SPI线过长或者没接上拉电阻时高电平幅度不够屏幕就会随机花屏。可以试着降低SPI时钟频率到5MHz如果问题消失基本就是信号完整性问题。6.2 系统跑一会儿就死机先查三样东西LVGL项目死机我经历过太多次了。优先级从高到低排查LVGL内存池耗尽。用lv_mem_monitor()打印当前内存池的剩余量如果数值持续下降直到0说明有内存在分配后没有释放。最常见的泄漏场景是某个控件在响应事件时被重复创建或者lv_label_set_text()接收了动态字符串但没有及时释放。FreeRTOS任务栈溢出。开启uxTaskGetStackHighWaterMark()监控一旦某个任务的栈水位低于100字赶紧调大栈。任务优先级导致的优先级翻转。如果通信任务的优先级高于UI任务通信任务里长时间阻塞等待串口数据UI任务就一直得不到调度界面卡死。我的解法是把UI任务提到最高优先级通信任务次之传感器任务最低。6.3 LVGL界面操作卡顿的优化顺序界面卡顿的问题优化顺序也很重要。先看lv_timer_handler()在UI任务里每轮执行耗时多少用GPIO拉电平测量一下如果单帧执行时间超过10ms先把屏幕的刷新区域打开脏矩形模式默认就是确保LVGL只刷新变化区域然后把图表的点数和刷新频率降下来最后再考虑提高SPI时钟。如果你发现即使不做任何操作CPU占用也很高多半是LVGL定时器里面创建了周期性回调每几十毫秒刷新一次时钟或者动态动画。这种动画控件使用完成后要记得lv_timer_del()或者把动画状态设为LV_ANIM_OFF否则会成为一个永不停歇的CPU消耗点。6.4 触摸偏移、误触和响应延迟触摸偏移最直接的原因就是坐标变换没做对。FT5x06读出来的坐标和屏幕显示坐标的关系取决于屏幕安装朝向它不一定是1:1对应。我的屏在竖放时触摸的x轴和显示x轴方向相反解决方法是把读取的x坐标做一次480 - x的映射就正常了。如果你用的是GT911还多一步它的触摸坐标可能存在24字节的校验如果校验失败数据会被忽略表现为偶尔点不中。至于响应延迟多数情况下是UI任务太忙导致LVGL的输入事件得不到及时处理。只要保证lv_timer_handler()的调用周期不超过20ms触摸响应手感就基本OK。6.5 其他杂项问题避坑不少人在用SquareLine Studio生成界面代码后直接复制进工程编译发现一堆报错。原因是SquareLine Studio导出的代码默认包含lvgl.h的引用路径而FSP的工程目录结构和普通的LVGL工程不完全一样。解决办法是给编译器添加LVGL源码目录的路径以及在lv_conf.h里开启LV_CONF_INCLUDE_SIMPLE。中文字库问题。LVGL默认字体是英文字体要想显示中文需要额外生成中文字库。但完整的中文字库体积很大一两百KB级别对Flash空间不友好。我的做法是只用无衬线的16号中文字库只打包界面里真正出现的那几百个汉字。用LVGL自带的lv_font_conv工具可以把文本文件里出现过的生僻字和常用字单独提取出来生成一个很小的自定义字库实测汉子数不超过300个时字库体积可以控制在30KB以内。如果用到LVGL的“毛玻璃”或者“模糊”效果建议直接放弃。LVGL V8的模糊效果是通过软件算法模拟出来的在200MHz MCU上跑一帧要几十毫秒完全带不动。想让界面更有质感多利用圆角、阴影、半透明遮罩来替代。调这种GUI项目我最大的体会是问题往往不是出在某一处而是出在各模块之间的衔接处。比如SPI DMA发送完成后没有正确通知LVGL比如队列数据发送频率和接收频率不匹配导致数据覆盖再比如任务优先级设计不合理导致UI刷新被反复打断。这些问题的共同特点是单看某一处代码都“没错”但组合在一起就会出诡异的故障。所以我最后想分享一个非常朴素的调试习惯每个模块做完之后先单独压测再集成。比如LVGL移植完就跑官方的demo确认触摸和显示都没问题了FreeRTOS任务写完先只跑打印日志确认任务调度和数据队列正常最后再合到一起。每次只引入一个变量出问题的时候就能快速定位。这个仪表盘后续我准备再加一块锂电池供电把功耗压一压然后试着用LVGL V9的渲染接口做一次迁移验证看看新的架构在RA6M4上的表现到底值不值得升级。如果你也想在RA系列上做类似的东西建议先从FSP自带的FreeRTOS集成模版起步然后找一块SPI屏幕把LVGL跑起来一步步往里面加业务。那之后你对FreeRTOS任务调度和LVGL渲染模型的理解就不是看几篇教程能比的了。
返回列表