4. 开发环境搭建
LCD驱动芯片,说白了就是MCU和屏幕之间的翻译官。你给它发指令和数据,它负责点亮像素。TouchGFX要跑起来,底层驱动必须和这颗芯片对上话。市面上常见的驱动芯片五花八门,但核心套路其实差不多。
我个人习惯把驱动适配分成三层:硬件接口层(SPI/8080并口/DSI)、寄存器配置层(初始化序列)、帧缓冲层(数据搬运)。不管芯片型号怎么变,这三层搞定了,屏幕就能亮。
核心观点:驱动适配的本质不是背寄存器手册,而是理解「MCU怎么把像素数据塞进GRAM,GRAM怎么刷新到面板」。不同芯片只是GRAM大小和接口时序的差异。
4.1 ILI9341驱动适配:最经典的SPI/8080方案
ILI934I是2.8寸~3.5寸屏幕的常客,240×320分辨率,支持SPI和8080并口。我在项目中遇到过最坑的事——SPI模式下的读写时序没对齐,屏幕死活不亮。
先看初始化序列。ILI9341上电后需要发一串配置指令,比如退出睡眠、设置像素格式、内存访问控制等。我一般这样写:
static void ILI9341_Init(void)
{
// 硬件复位
LCD_RST_LOW();
HAL_Delay(10);
LCD_RST_HIGH();
HAL_Delay(120);
// 退出睡眠模式
ILI9341_WriteCmd(0x01);
HAL_Delay(150);
// 像素格式:16位色
ILI9341_WriteCmd(0x3A);
ILI9341_WriteData(0x55); // 0x55 = RGB565
// 内存访问控制:BGR顺序
ILI9341_WriteCmd(0x36);
ILI9341_WriteData(0x08); // 根据屏幕方向调整
// 开启显示
ILI9341_WriteCmd(0x29);
}
嗯,这里要注意:每个指令后的延时不能省。尤其是退出睡眠(0x01),手册写的是120ms,我试过缩短到50ms,结果屏幕闪烁。
TouchGFX的适配点在于TouchGFXHAL::flushFrameBuffer()。你需要把帧缓冲区的数据通过SPI或并口刷到ILI9341的GRAM。我习惯用DMA传输,避免CPU空转:
void TouchGFXHAL::flushFrameBuffer(const Rect& rect)
{
// 设置窗口区域
ILI9341_SetWindow(rect.x, rect.y, rect.width, rect.height);
// 通过DMA发送像素数据
HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*)frameBuffer, rect.width * rect.height * 2);
}
个人经验:ILI9341的GRAM写入速度上限约15MHz SPI时钟。如果TouchGFX的帧率要求超过30fps,建议用8080并口模式,或者降低分辨率。
4.2 ST7789驱动适配:小尺寸高刷的宠儿
ST7789常见于1.3寸~1.8寸圆形或方形屏,分辨率240×240或240×280。它和ILI9341的寄存器高度相似,但有个坑——初始化序列必须关闭Tearing Effect(撕裂效应)。
我曾经在一个手表项目里,屏幕刷新时出现水平撕裂线。查了半天,发现是TE引脚没处理。解决方案很简单:
// 关闭撕裂效应
ST7789_WriteCmd(0x35); // TEON
ST7789_WriteData(0x00); // 关闭
ST7789的另一个特点是支持硬件旋转。通过寄存器0x36的MV、MX、MY位,可以改变扫描方向。TouchGFX里不需要软件旋转,直接配置硬件即可:
| 旋转角度 | 0x36值 | 说明 |
|---|---|---|
| 0° | 0x00 | 默认方向 |
| 90° | 0x60 | MV=1, MX=1 |
| 180° | 0xC0 | MX=1, MY=1 |
| 270° | 0xA0 | MV=1, MY=1 |
避坑指南:ST7789的VCI电压范围是2.4V~3.3V,如果MCU是5V电平,必须加电平转换。我曾经直接连5V,烧了三块屏才反应过来。
4.3 NT35510驱动适配:大屏高分辨率的挑战
NT35510是3.97寸~5.5寸屏的常用驱动,分辨率高达480×800甚至720×1280。它使用MIPI DSI接口,和前面两种芯片完全不同。
DSI接口的适配难点在于初始化序列更长,而且需要配置LP(低功耗)和HS(高速)模式切换。我建议把初始化序列做成数组,方便调试:
static const uint8_t nt35510_init_seq[] = {
0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01, // 进入工厂模式
0xB0, 0x08, // 设置RGB接口
0xB1, 0x00, // 设置帧率
0xB4, 0x00, // 设置显示模式
0xB6, 0x00, 0x02, 0x00, 0x02, // 设置分辨率
0xB7, 0x00, 0x00, // 设置VBP
0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00, // 退出工厂模式
0x11, // 退出睡眠
0x29, // 开启显示
};
TouchGFX适配NT35510时,帧缓冲区的尺寸会很大。480×800×2字节 ≈ 768KB,很多MCU内部SRAM装不下。这时候必须用外部SDRAM,或者使用TouchGFX的Partial Buffer机制。
关键点:NT35510的DSI时钟频率通常设置在200MHz~500MHz。如果屏幕闪烁,先检查DSI的CLK和DATA线的等长布线,再检查初始化序列中的时序参数。
4.4 RA8875驱动适配:自带字库和图形加速的异类
RA8875不是单纯的驱动芯片,它更像一个显示协处理器。内置BTE(Block Transfer Engine)、几何图形加速、甚至中文字库。适合7寸~10寸的大屏,分辨率最高800×480。
我建议把RA8875当成一个「外置显卡」来用。MCU只需要通过8080并口发命令,RA8875自己完成像素填充。比如画一个矩形:
void RA8875_DrawRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)
{
RA8875_WriteReg(0x91, 0x00); // 设置绘制模式为矩形填充
RA8875_WriteReg(0x92, color >> 8); // 颜色高字节
RA8875_WriteReg(0x93, color & 0xFF); // 颜色低字节
RA8875_WriteReg(0x94, x >> 8); // X起始高字节
RA8875_WriteReg(0x95, x & 0xFF);
RA8875_WriteReg(0x96, y >> 8); // Y起始高字节
RA8875_WriteReg(0x97, y & 0xFF);
RA8875_WriteReg(0x98, (x + w) >> 8); // X结束
RA8875_WriteReg(0x99, (x + w) & 0xFF);
RA8875_WriteReg(0x9A, (y + h) >> 8); // Y结束
RA8875_WriteReg(0x9B, (y + h) & 0xFF);
RA8875_WriteReg(0x02, 0x80); // 启动绘制
}
TouchGFX适配RA8875时,有个取巧的方法——直接使用RA8875的GRAM作为帧缓冲区。MCU只需要把TouchGFX渲染好的局部区域,通过DMA刷到RA8875的GRAM即可。这样MCU的RAM压力会小很多。
个人经验:RA8875的BTE引擎可以用来做UI动画的加速。比如滑动效果,让RA8875自己搬运GRAM数据块,MCU只需要算坐标。帧率能提升30%以上。
4.5 四种芯片的对比与选型建议
你想想看,项目里到底选哪颗芯片?我列个表,方便你快速决策:
| 芯片型号 | 接口类型 | 最大分辨率 | 典型应用 | 适配难度 |
|---|---|---|---|---|
| ILI9341 | SPI / 8080 | 320×240 | 2.8~3.5寸小屏 | 低 |
| ST7789 | SPI / 8080 | 240×280 | 1.3~1.8寸圆屏 | 低 |
| NT35510 | MIPI DSI | 720×1280 | 4~5.5寸手机屏 | 高 |
| RA8875 | 8080 | 800×480 | 7~10寸工控屏 | 中 |
选型时记住一句话:小屏用SPI,大屏用DSI,需要加速用RA8875。别在ILI9341上跑480×320的分辨率,帧率会很难看。
避坑指南:RA8875的初始化序列必须严格按照手册的步骤来,尤其是PLL配置。我曾经跳过PLL稳定等待,结果屏幕出现随机花屏。后来加了50ms延时才解决。
4.6 驱动适配的通用框架
不管哪颗芯片,驱动适配的代码结构可以统一。我习惯这样组织:
// lcd_driver.h
typedef struct {
void (*init)(void);
void (*setWindow)(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void (*writePixels)(uint16_t *data, uint32_t len);
void (*refresh)(void);
} LCD_Driver_t;
// 实例化
extern LCD_Driver_t ILI9341_Driver;
extern LCD_Driver_t ST7789_Driver;
extern LCD_Driver_t NT35510_Driver;
extern LCD_Driver_t RA8875_Driver;
TouchGFX的HAL层只需要调用setWindow和writePixels,底层驱动具体实现。这样换屏时只需要改一个宏定义,代码复用率很高。
最后,送你一张驱动适配的流程图,理清整体思路:
这张图把四种芯片的适配路径串起来了。从选型开始,根据接口类型分流,然后统一做初始化序列和帧缓冲适配。实际项目中,你只需要按这个流程走,基本不会出大问题。