TouchGFX不是“画图工具”,而是一套嵌入式GUI的实时操作系统——从帧开始,讲清楚它怎么让MCU上的UI真正“活”起来
你有没有遇到过这样的场景:在STM32H7上跑TouchGFX,明明CPU负载不到30%,LCD却卡顿、触摸延迟、动画撕裂?或者在i.MX RT1064上移植成功后,换一块PCB就花屏,查了三天寄存器发现只是LTDC的PSAR(Pixel Start Address Register)没对齐32字节边界?
这不是你的代码写得不够好,而是你还没真正“看见”TouchGFX的骨架——它根本不是一个“调API画控件”的GUI库,而是一个以帧为时间单位、以内存布局为物理基础、以硬件事件为驱动原语的轻量级实时图形调度系统。它的每一行代码,都在和VSYNC抢时间,和DMA2D争带宽,和SRAM的bank冲突做博弈。
下面,我们不讲概念、不列模块、不堆术语。我们从一个最朴素的问题出发:
当用户手指划过屏幕的那一毫秒,到像素真正动起来,中间到底发生了什么?
一、第一帧,从VSYNC中断开始——渲染管线不是“流程”,是硬实时节拍器
很多人把renderFrame()当成一个普通函数去调用,甚至在FreeRTOS任务里用osDelay(16)模拟60FPS——这是TouchGFX最典型的“伪实时”陷阱。
真相是:TouchGFX的渲染节拍,必须由硬件垂直同步信号(VSYNC)物理锁定。为什么?
因为LCD控制器(如STM32的LTDC、NXP的LCDIF)在每帧开始前会拉低VSYNC引脚,这个边沿被配置为EXTI中断源。一旦进入中断服务程序(ISR),框架立刻执行:
extern "C" void LTDC_IRQHandler(void) { HAL_LTDC_IRQHandler(&hltdc); // → 最终触发 touchgfx::HAL::requestRendering(); }注意,这里没有delay,没有while,没有轮询。它是中断驱动的确定性调度——就像心跳一样稳定。如果某帧因draw()耗时过长而错过VSYNC窗口,下一帧不会“补上”,而是直接丢弃,保证长期帧率稳定(宁可掉帧,也不拖影)。
所以,HAL::swapBuffers()绝不是简单的指针交换:
void HAL::swapBuffers() { // 关键1:必须在VSYNC低电平期间写入新地址(LTDC规范要求) __HAL_LTDC_CLEAR_FLAG(&hltdc, LTDC_FLAG_VSYNC); // 关键2:更新的是Layer的CFBAR(Color Frame Buffer Address Register) // 而不是直接改显存内容!LTDC会自动从该地址DMA读取 LTDC_Layer1->CFBAR = (uint32_t)backBuffer; // 关键3:触发立即刷新(IMCR),而非等待下一个VSYNC LTDC->SRCR = LTDC_SRCR_IMCR; }这段代码背后,是三个硬约束:
-CFBAR必须在VSYNC低电平期写入(手册Section 42.5.4明确要求);
-IMCR触发后,LTDC会在 <