4、LCD驱动芯片适配:ILI9341驱动适配、ST7789驱动适配、NT35510驱动适配、RA8875驱动适配

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,底层驱动具体实现。这样换屏时只需要改一个宏定义,代码复用率很高。

最后,送你一张驱动适配的流程图,理清整体思路:

LCD驱动芯片适配流程图 LCD驱动芯片适配流程 选择驱动芯片 接口类型:SPI / 8080 / DSI SPI:ILI9341/ST7789 8080:RA8875 DSI:NT35510 配置初始化序列 TouchGFX帧缓冲适配 注意: DSI需配置LP/HS模式 RA8875可复用BTE加速

这张图把四种芯片的适配路径串起来了。从选型开始,根据接口类型分流,然后统一做初始化序列和帧缓冲适配。实际项目中,你只需要按这个流程走,基本不会出大问题。