低资源MCU驱动大尺寸LCD:用IO模拟8080协议实现画面稳定刷新
大屏全帧缓冲太吃RAM:800×480的RGB565要768KB,F103/F0根本塞不下。这类屏走8080并口+IO模拟反而合适——CS/RS/WR/RD加8位数据线,软件拼时序,配合地址窗口做局部刷新,几十KB缓冲就能稳住。
一、8080引脚与写时序
最小控制线四根:
CS:片选,低有效
RS(DC):0=命令,1=数据
WR:上升沿锁存数据
RD:写操作时固定拉高即可省IO
写命令/数据核心就一套顺序:
#define LCD_CS_L() GPIOA->BSRR = PIN_CS<<16
#define LCD_CS_H() GPIOA->BSRR = PIN_CS
#define LCD_RS_L() GPIOA->BSRR = PIN_RS<<16
#define LCD_RS_H() GPIOA->BSRR = PIN_RS
#define LCD_WR_L() GPIOB->BSRR = PIN_WR<<16
#define LCD_WR_H() GPIOB->BSRR = PIN_WR
static inline void gpio_data(uint8_t v){
GPIOB->ODR = (GPIOB->ODR & 0xFF00) | v; // D0~D7接PB0~PB7
}
void lcd_wr_cmd(uint8_t c){
LCD_CS_L(); LCD_RS_L();
gpio_data(c);
LCD_WR_L(); __NOP(); __NOP(); // 脉宽余量
LCD_WR_H(); LCD_CS_H();
}
void lcd_wr_data(uint8_t d){
LCD_CS_L(); LCD_RS_H();
gpio_data(d);
LCD_WR_L(); __NOP(); __NOP();
LCD_WR_H(); LCD_CS_H();
}
WR低电平宽度别顶着手册最小值走,老屏或排线长就多加几个NOP,花屏大多出在这。
二、地址窗口:局部刷新的钥匙
别整屏搬。先设列地址、行地址,之后连续写像素只进窗口内:
void lcd_set_window(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1){
lcd_wr_cmd(0x2A);
lcd_wr_data(x0>>8); lcd_wr_data(x0);
lcd_wr_data(x1>>8); lcd_wr_data(x1);
lcd_wr_cmd(0x2B);
lcd_wr_data(y0>>8); lcd_wr_data(y0);
lcd_wr_data(y1>>8); lcd_wr_data(y1);
lcd_wr_cmd(0x2C); // 进入RAM写
}
窗口设完,后面只发像素数据,不用再发坐标,总线效率最高。
三、低RAM刷新策略
全屏帧缓冲没有就别硬开。正确做法:
开一小块行缓冲/块缓冲(如160×行宽)
GUI只画脏矩形
flush时set_window锁区域,连续写像素
变化面积超阈值再整屏
void lcd_flush_rect(uint16_t x,uint16_t y,uint16_t w,uint16_t h,
uint16_t*px){
lcd_set_window(x,y,x+w-1,y+h-1);
LCD_RS_H();
for(uint32_t i=0;i<w*h;i++){
gpio_data(px[i]>>8); // 16位分两拍或接16位数据线
LCD_WR_L(); LCD_WR_H();
gpio_data(px[i]&0xFF);
LCD_WR_L(); LCD_WR_H();
}
}
数值跳变、按钮反色、图标动一下,都只刷那块矩形,800×480屏单控件刷新能比全屏快十几倍。
四、16位数据线拼端口
8位慢但省IO,16位快但要16根线。拼端口写比库函数快几十倍:
#define LCD_DATA16(v) do{ \
GPIOB->ODR = (GPIOB->ODR&0x00FF)|((v)&0x00FF); \
GPIOE->ODR = (GPIOE->ODR&0x00FF)|((v)&0xFF00); \
}while(0)
写像素时LCD_DATA16(color); LCD_WR_L(); LCD_WR_H();即可。
五、稳定刷新的几个硬规矩
WR脉宽留余量:手册写15ns,实际给30~50ns更稳
CS别提前拉高:像素流没发完就释放CS,窗口会断
RD固定高:只写屏不读就别切RD,少一个时序变量
数据线映射对齐原理图:D0~D7接哪组GPIO必须一一对应,错一位整屏偏移
复位延时别省:RES拉低几ms再初始化,0x11后等电源稳定
中断别插进写像素流:半途抢走总线会写串,关键flush关中断或放DMA
MADCTL/方向初始化一次:别每次flush重设
六、再往上:DMA搬数据
纯IO模拟刷大图还是占CPU。进阶是把像素缓冲交给SPI/DMA,或者MCU有FSMC就别模拟了,直接映射地址写。但IO模拟的价值在——FSMC引脚被占用、低成本MCU、调试时序问题时,它是最稳的兜底方案。
七、落坑清单
全屏memcpy→RAM爆、刷得慢
WR不延时→随机花屏横纹
像素流中间被中断打断→区域错乱
地址窗口算错→画面偏移重叠
RD不固定→偶发读到垃圾
数据线顺序想当然→红蓝反/整列错
每次画都重设窗口→带宽浪费
复位延时砍掉→冷启动偶尔不亮
八、落地顺序
确认屏是8080并口(CS/RS/WR/RD)
数据线接固定GPIO端口,写BSRR/ODR宏
实现wr_cmd/wr_data,示波器看WR脉宽
初始化里设MADCTL、扫描方向
封装set_window
GUI只算脏矩形,flush走局部写
小缓冲块刷新,超阈值才整屏
后期换FSMC或DMA提速度
低资源MCU驱动大屏的本质不是“算力够不够”,是“别碰全帧缓冲”。8080时序本身不复杂,CS/RS/WR三相序对齐、WR留余量、刷新只走地址窗口——几十KB RAM也能把800×480界面刷稳。IO模拟不是落后方案,是低成本和大屏之间的那座桥。





