嵌入式C语言驱动MIPI屏幕:实现高分辨率LCD的无卡顿帧数据传输
高分辨率MIPI屏“卡顿/撕裂”通常不是时钟不够,而是显示扫描和CPU写帧缓冲没同步。驱动写法先分两种模式:带GRAM的屏走Command Mode,主机写显存、屏自己刷;不带GRAM的大屏走Video Mode,主机按时序持续推像素流。
一、先定模式再写代码
Command Mode:
初始化发厂家的sleep-out、像素格式、display on
画面变化才写GRAM,支持局部刷新
防撕裂靠TE信号
Video Mode:
主机必须持续发像素流,停发就黑屏
无GRAM,无TE概念,靠VSYNC/时序边界
高分辨率要算带宽:分辨率×位深×帧率×通道开销
配错模式最典型现象:命令屏当视频屏配→只亮一帧;视频屏当命令屏配→无持续流、画面丢。
二、双缓冲:绘制和扫描分离
单帧缓冲下CPU改到一半,LCD控制器正好扫到该行,就出撕裂线。正确开前后缓冲:
static uint8_t *fb_front;
static uint8_t *fb_back;
void gui_render(void){
draw_ui(fb_back); // CPU只写back buffer
}
void swap_buffers(void){
uint8_t *tmp = fb_front;
fb_front = fb_back;
fb_back = tmp; // 只换指针
}
双缓冲解决“同时写同一块”,但不解决“什么时候换”——换早了照样撕。
三、Command Mode:TE同步写GRAM
TE脚接MCU外部中断,屏进入垂直消隐时拉脉冲,MCU收到后才启动DMA写下一帧:
volatile int te_flag = 0;
void EXTI_TE_IRQHandler(void){
te_flag = 1;
}
void display_flush(void){
if(!te_flag) return; // 不在扫描安全窗不写
te_flag = 0;
dsi_dcs_write(0x2C, NULL, 0); // write_memory_start
dma_start(fb_back, LCD_WIDTH*LCD_HEIGHT*2);
}
void DMA_Complete_IRQ(void){
lv_disp_flush_ready(disp); // 或置flush_done
}
初始化里开TE:
uint8_t te = 0x00;
dsi_dcs_write(0x35, &te, 1); // TE on
注意:TE只把“写GRAM”错开到屏不读的时刻;单GRAM屏如果一帧写入时间大于屏扫描周期,仍可能套圈撕裂,这时候要降分辨率/局部刷新/上视频模式。
四、Video Mode:VSYNC或行中断切缓冲
视频模式屏没GRAM,缓冲在MCU侧,LCDC/DMA持续搬:
RGB/LTDC屏用控制器VSYNC中断切前后缓冲
MIPI DPI视频流由DSI主机按时序推,CPU别中途换fb指针
切换只在帧边界,扫描中换=撕裂
五、高分辨率带宽优化
720p/1080p下卡顿多是搬运瓶颈,不是绘制慢:
帧缓冲放外部RAM/PSRAM,DMA直读,CPU不碰像素
减少memcpy:解码器直接输出到fb_back,别SRAM→PSRAM→DSI再拷一遍
Cache一致性:DMA读前Invalidate,CPU写后Clean,否则屏收到旧数据
像素格式:车机仪表用RGB565省一半带宽,主屏才RGB888
lane数匹配:高分辨率开2/4 lane,单lane硬顶会掉帧
局部刷新:命令模式只刷变化矩形,仪表指针区/告警条单独更新
DMA完成和链路完成分开:源缓冲DMA读完才能复用,DSI包发完才能起下一笔
六、flush_cb写法(LVGL场景)
void lv_flush_cb(lv_disp_t*d, const lv_area_t*a, uint8_t*px){
if(te_mode) wait_te(); // 命令屏等TE
dsi_set_column(a->x1, a->x2);
dsi_set_page(a->y1, a->y2);
dsi_dcs_write(0x2C, NULL, 0);
dma_start(px, (a->x2-a->x1+1)*(a->y2-a->y1+1)*2);
// DMA ISR里再调 lv_disp_flush_ready
}
别在flush_cb里阻塞等发完,也别DMA没完成就重用draw buffer。
七、落坑清单
模式配反:命令屏当视频屏,亮一下不动
双缓冲但随时swap:撕裂照旧
命令屏不开TE:写GRAM撞扫描
DMA没完成就改back buffer:新旧帧混屏
Cache没清:MIPI偶发花屏
高分辨率单lane:带宽不够掉帧
初始化顺序错:0x11后延时不够、0x29前开TE反而无脉冲
局部刷新区域算错:写入区和扫描区重叠仍撕
按“确认GRAM→命令模式开TE/视频模式靠VSYNC→前后双缓冲→DMA搬帧→Cache清理→flush完成回调”落地,MIPI屏高分辨率下才能无卡顿无撕裂。核心就一句:显示控制器在读的那一刻,你写的缓冲不能是同一块,也不能在中途换指针。





