4. 图形显示缓冲区管理
在RA MCU上驱动图形HMI时,屏幕上的每一个像素最终都来自一块内存区域,这块区域就是帧缓冲区(Frame Buffer)。图形刷新是否流畅、是否出现撕裂,本质上取决于帧缓冲区如何组织、由谁写入、何时切换。本节从显存布局出发,逐步展开单缓冲、双缓冲、三缓冲的差异,并给出在RA MCU上使用SDRAM或内部SRAM作为显存的实际配置思路。
一、帧缓冲区的基本结构
帧缓冲区是一段连续内存,按扫描顺序存放每个像素的颜色值。以常见的RGB565格式为例,每个像素占2字节,若分辨率为480×272,则单帧所需空间为:
480 × 272 × 2 Byte = 261,120 Byte ≈ 255 KB
RA系列MCU内部SRAM容量因型号而异,例如RA6M5拥有512 KB SRAM,理论上可容纳一帧RGB565图像,但若同时运行GUIX、网络协议栈和应用程序,剩余空间会非常紧张。因此,当分辨率提高到800×480或使用RGB888时,通常需要外扩SDRAM作为显存。RA MCU通过外部总线控制器(如RA6M5的BUS模块)连接SDRAM,并将帧缓冲区定位到SDRAM地址空间,例如0x68000000起始区域。
帧缓冲区的关键参数包括:起始地址、宽度(像素)、高度(像素)、像素格式、行跨度(Stride)。行跨度不一定等于宽度×像素字节数,因为硬件可能要求每行按16字节或32字节对齐,以提升LCD控制器或DMA2D的读取效率。配置时若忽略对齐,可能出现图像错位或刷新异常。
二、单缓冲、双缓冲与三缓冲的机制差异
单缓冲只有一块帧缓冲区。CPU或DMA2D直接向这块区域绘制,LCD控制器同时从同一区域读取并扫描输出。当绘制速度与扫描速度不同步时,屏幕上会出现上半部分是新画面、下半部分是旧画面的撕裂(Tearing)现象。单缓冲实现简单、占用内存最少,但只适合静态画面或对撕裂不敏感的场景。
双缓冲使用两块帧缓冲区:前台缓冲(Front Buffer)供LCD控制器读取,后台缓冲(Back Buffer)供CPU/DMA2D绘制。绘制完成后,在垂直消隐期(VBlank)交换前后台指针,使新画面整帧生效。双缓冲消除了撕裂,但若绘制一帧的时间超过一个刷新周期,交换时后台缓冲尚未完成,仍可能出现卡顿或等待。双缓冲需要两倍显存,480×272 RGB565下约510 KB。
三缓冲在前台和后台之外再增加一块待显示缓冲。绘制完成的缓冲进入“就绪”队列,LCD控制器在VBlank时从队列中取最新完整帧。三缓冲进一步解耦绘制与显示,允许CPU在LCD读取某一帧时继续绘制下一帧,适合动画、视频或高帧率UI。代价是显存增至三倍,且需要更精细的队列管理,避免帧序错乱。
三、在RA MCU上配置显存
以RA6M5 + 外部SDRAM + RGB565 LCD为例,显存配置通常包含以下环节。
1. 确定显存位置与容量。若使用SDRAM,需在链接脚本或FSP的Memory配置中将帧缓冲区段定位到SDRAM区域。例如定义fb0、fb1两个数组,并指定段属性:
#define LCD_WIDTH 480
#define LCD_HEIGHT 272
#define FB_SIZE (LCD_WIDTH * LCD_HEIGHT * 2)
uint16_t fb0[LCD_WIDTH * LCD_HEIGHT] __attribute__((section(".sdram_fb")));
uint16_t fb1[LCD_WIDTH * LCD_HEIGHT] __attribute__((section(".sdram_fb")));
若使用内部SRAM,则需评估剩余空间。RA6M5的512 KB SRAM中,若GUIX占用约120 KB,协议栈占用约80 KB,剩余约300 KB,仅能容纳一帧480×272 RGB565。此时可考虑降低分辨率、使用RGB332,或采用局部刷新策略。
2. 配置LCD控制器或DMA2D。RA MCU通常通过GLCDC(Graphics LCD Controller)驱动RGB接口屏。在FSP中配置GLCDC时,需指定输入帧缓冲区地址、行跨度、像素格式和输出时序。若使用DMA2D加速绘制,则需配置源地址、目标地址、区域大小和颜色格式,使绘制操作直接写入后台缓冲。
3. 实现缓冲切换。双缓冲切换通常在VBlank中断中完成。GLCDC提供垂直消隐中断,可在回调中交换前后台指针:
volatile uint16_t *front = fb0;
volatile uint16_t *back = fb1;
void glcdc_vblank_callback(display_callback_args_t *p_args)
{
if (p_args->event == DISPLAY_EVENT_VBLANK) {
volatile uint16_t *tmp = front;
front = back;
back = tmp;
R_GLCDC_BufferChange(&g_display0_ctrl, (uint8_t *)front, DISPLAY_FRAME_LAYER_1);
}
}
三缓冲则需要维护一个就绪队列,在VBlank中判断是否有新帧就绪,若有则切换,否则保持当前帧。队列深度通常为2或3,需防止生产者覆盖尚未显示的就绪帧。
四、无撕裂刷新的判断与验证
验证是否实现无撕裂,可从以下方面观察:
- 视觉检查:播放快速移动的矩形或滚动文字,若边缘整齐、无水平错位,说明切换与VBlank同步良好。
- 帧率测量:使用GPIO翻转或DWT计数器统计每秒实际切换次数,与LCD刷新率(如60 Hz)对比。若切换次数明显低于刷新率,说明绘制耗时过长,需优化DMA2D或降低绘制区域。
- 缓冲状态监控:在调试器中观察前后台指针是否在VBlank中成对交换,避免在非消隐期切换导致撕裂。
总结来说,帧缓冲区管理是图形HMI稳定刷新的基础。单缓冲适合静态低要求场景,双缓冲是消除撕裂的通用方案,三缓冲则为高帧率动画提供更充裕的绘制窗口。在RA MCU上,显存可位于内部SRAM或外部SDRAM,关键在于根据分辨率、像素格式和可用内存做出权衡,并通过VBlank同步切换实现无撕裂显示。