1. 屏幕不亮了先别急着查接线先确认它用的是哪颗屏驱做嵌入式项目的人应该都有过这种经历一块LCD模块拿在手里四五个引脚、一个背光、一根排线看着和网上教程里的一模一样照着别人的代码接上去结果要么白屏、要么花屏、要么颜色完全不对。大多数人的第一反应是查接线、量电平、怀疑自己的SPI配置有问题。但我后来发现至少有三分之一的情况问题压根不在你的代码而在屏幕背面那颗不起眼的芯片——LCD驱动IC屏驱。屏驱这个东西简单说就是屏幕的“大脑”兼“翻译官”。主控芯片MCU并不直接控制屏幕上的每一个像素它只需要按照某种协议把像素数据发给屏驱由屏驱把数据转成玻璃基板上对应的扫描信号、灰阶电压、刷新时序。换句话说MCU和屏幕之间隔着一个屏驱所有你写给屏幕的“话”都是说给屏驱听的而不同屏驱的“听力习惯”完全不一样。这就是为什么同样2.4寸、同样240x320分辨率的屏幕用A驱动芯片的代码去驱动B芯片的模块大概率跑不起来或显示异常。市面上大量廉价模块外形相似但背后用的驱动芯片五花八门ST7735、ILI9341、ST7789、ILI9488、GC9A01……它们在命令集、初始化序列、接口定义上各有各的习惯。今天这篇文章就围绕“常用LCD屏驱”这个主题把我实际调屏过程中用过的、见过的、踩过坑的驱动芯片和对应经验梳理一遍。适合正在入门显示模块的同学也适合已经会点基础的开发者系统补一遍背后的原理。我的建议是拿到任何一块新屏先别急着接线花两分钟搞清楚屏驱型号再动手。下面就从这个动作说起。2. 市面常见的几颗屏驱各自是什么脾气2.1 一张表看清主流屏驱先把我用过的几颗常见屏驱列出来方便做选型和对比。这里的“常见”指的是在嵌入式DIY、开源硬件、小型产品里出现频率最高的那几颗它们覆盖了从0.96寸到3.5寸的绝大多数小尺寸LCD场景。屏驱型号常见分辨率典型尺寸主要接口色彩格式备注ST7735128x160、80x1600.96寸/1.8寸SPIRGB444/565/666小尺寸TFT的老将ST7789240x240、135x2401.3寸/1.54寸SPIRGB565/666近几年小屏新宠ILI9341240x3202.4寸/2.8寸/3.2寸SPI/并行RGB565/666最经典的通用屏驱ILI9488320x4803.5寸SPI/并行RGB666大分辨率、颜色要求高的屏GC9A01240x2401.28寸圆形SPIRGB565圆形表盘类屏幕常见NT35510720x1280等手机屏级别MIPI/并行RGB888移动设备市场的常见型号SSD1306128x640.96寸OLEDI2C/SPI单色OLED严格来说不算LCD这张表里ST7735和ILI9341是真正的“老资历”网上能搜到的显示类教程起码有一半是围绕它们写的。ST7789是近年来的后起之秀很多1.3寸、1.54寸小方屏在用单颗价格比同尺寸的ST7735方案还便宜而且分辨率更高。GC9A01则几乎成了圆形智能手表、电子胸牌类产品的默认选择。2.2 为什么不能“一套代码走天下”很多人不理解都是SPI接口都是发一串命令凭什么换颗芯片就要改初始化这里的关键在于屏驱芯片内部的寄存器地址和命令码并没有一个统一的标准。比如ST7735的“开显示”命令是0x29ILI9341也一样是0x29但你一深入就发现差异很大颜色深度设置命令在ILI9341上是0x3A在ST7735上虽然有0x3A但可用的色彩格式不一样屏幕方向控制命令0x36MADCTL各家都有但某些保留位的行为可能不同尤其是不同批次、不同厂商生产的同型号芯片初始化序列甚至存在细微差别。就算主控的驱动程序写得再优雅最终还是要适配“这一颗屏驱”的寄存器行为。所以结论很直接先用芯片型号反查数据手册再写初始化。省掉这一步后面八成要返工。3. 拿到新屏先“验明正身”从物理型号到数据手册的关键信息3.1 三步认清屏驱第一步看模块背面的PCB丝印。绝大多数独立模块上会印着驱动芯片型号比如一块小板子上印着“ST7789V”或者“ILI9341C”基本就是答案。但丝印位置不固定有时候字很小建议直接手机拍照放大看。我踩过的那个坑就是在1.8寸屏上PCB丝印只印了模组型号真正标注驱动芯片的地方藏在FPC排线根部用放大镜才看清。第二步查驱动芯片型号对应的数据手册Datasheet。搜索引擎输入型号加“datasheet”字样很容易找到但要注意版本同一个型号会分“PRELIMINARY”和“FINAL”版本命令定义可能有修订。以英文版PDF为准中文二次翻译的资料建议只当参考。第三步也是我自己常用的办法如果模块卖家提供了示例代码直接看示例里的初始化数组和命令注释通常能反推芯片型号。初始化数组里出现频率最高的那几个命令比如0x36、0x3A、0x2A、0x2B、0x2C是TFT驱动家族的通用“骨架”但具体的参数值会因芯片而异。3.2 数据手册里只抓这五件事数据手册动辄几十页大部分内容是这个芯片内部的栅极驱动逻辑和电压生成电路对做应用层的人来说不需要逐页啃。我调屏时的习惯是只看五个部分接口选择确认屏驱支持哪几种通信方式有哪些配置引脚决定工作模式。比如ILI9341支持三线/四线SPI、8位/16位并行接口这些模式靠模块上的电阻或引脚跳线决定。如果你的模块只引出了SPI引脚那就不用管并行部分。命令总表和关键命令看命令号、命令名称、参数个数和参数含义。重点抓0x36MADCTL内存访问方向、0x3A像素格式、0x2A/0x2B/0x2C窗口地址和写显存这几条是所有TFT屏都绕不开的基础。电源与复位时序芯片对VDD上电、复位释放和退出睡眠之间的延时都有要求。有些屏一上电就有短暂的“高电平复位”过程如果MCU的复位脚时序和屏驱不匹配芯片可能处于异常状态。显存布局和偏移很多屏驱内部的GRAM坐标和物理可显示区域并不是从(0,0)开始的这就是“偏移量”问题。比如ST7789的某些1.54寸屏真正可显示的像素区域在GRAM里偏移了(34,52)写代码时必须把这个偏移算进去否则画面会偏。帧率和背光控制如果做动态显示需要了解屏驱支持的扫描方向、撕裂检测TE引脚以及帧率设置范围。这些决定你后面是否需要做防撕裂处理。3.3 用最小系统验证“通讯通没通”在写完整初始化之前我强烈建议先做一次“读ID”测试。绝大多数屏驱支持读取芯片ID比如ILI9341的0xD3命令会返回0x93ST7789的0x04命令会返回一系列ID字节。如果读回来的值和预期一致说明接线、SPI模式、主控配置全对如果读不到大概率是SPI的时钟极性CPOL或相位CPHA不对或者DC引脚数据/命令切换接反了。这一步只花十分钟却能排除掉一半以上的低级问题。很多调屏教程直接跳过了读ID这一步结果后面颜色不对时不知道是屏驱问题还是参数问题排查起来特别痛苦。4. 初始化序列实战以ILI9341为例从零跑通一块屏4.1 初始化到底在“初始化”什么屏驱内部的寄存器在上电后的默认值往往处于“低功耗待机”状态画面是关闭的像素格式也不是我们常用的RGB565甚至扫描方向是倒的。初始化序列的作用就是把这堆默认状态一项一项改成我们要的状态。有人迷信网上抄来的几十行初始化数组觉得“只要照抄就行”。我建议至少理解其中几行在干什么一旦屏驱动不了你才能判断是数组问题还是接线问题。一个典型的初始化流程包含这几步硬复位 → 软件复位 → 退出睡眠 → 设置像素格式 → 设置扫描方向 → 设置窗口地址 → 开显示 → 清除画面。下面这段代码是我在一个模拟项目X上用SPI方式驱动ILI9341时整理的简化版可以直接抄进自己的工程里改引脚编号使用。4.2 完整初始化代码#include stdint.h // 假设已经实现以下底层函数 void LCD_SPI_SendByte(uint8_t data); void LCD_DC_HIGH(void); // 数据模式 void LCD_DC_LOW(void); // 命令模式 void LCD_CS_LOW(void); void LCD_CS_HIGH(void); void LCD_RST_HIGH(void); void LCD_RST_LOW(void); void Delay_ms(uint32_t n); // 发送一条命令参数列表以0x00结尾 static void lcd_write_cmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); LCD_SPI_SendByte(cmd); LCD_CS_HIGH(); } static void lcd_write_data(uint8_t data) { LCD_CS_LOW(); LCD_DC_HIGH(); LCD_SPI_SendByte(data); LCD_CS_HIGH(); } // 带参数的命令 static void lcd_write_cmd_data(uint8_t cmd, uint8_t *data, uint8_t len) { lcd_write_cmd(cmd); for (uint8_t i 0; i len; i) { lcd_write_data(data[i]); } } // 复位 static void lcd_reset(void) { LCD_RST_HIGH(); Delay_ms(50); LCD_RST_LOW(); Delay_ms(50); LCD_RST_HIGH(); Delay_ms(120); } // ILI9341 初始化 void ili9341_init(void) { lcd_reset(); lcd_write_cmd(0x01); // 软件复位 Delay_ms(120); lcd_write_cmd(0x11); // 退出睡眠模式 Delay_ms(150); uint8_t cmd36 0x48; // MADCTL: MXMV? 实际根据屏幕方向定 lcd_write_cmd_data(0x36, cmd36, 1); uint8_t cmd3A 0x55; // RGB565格式 lcd_write_cmd_data(0x3A, cmd3A, 1); lcd_write_cmd(0x20); // 关闭反显部分屏默认开 lcd_write_cmd(0x29); // 打开显示 Delay_ms(50); // 清屏为黑色 lcd_set_window(0, 0, 239, 319); for (uint32_t i 0; i 240u * 320u; i) { lcd_write_data(0x00); lcd_write_data(0x00); } }这段代码里最重要的几个命令0x01软件复位和0x11退出睡眠是开屏前必须满足的时序条件。上电后屏驱默认处于睡眠态不退出睡眠你发后面所有命令都没用。0x36的0x48是我这块模拟屏需要的一个扫描方向值。它的含义是MY0,MX1,MV1,BGR1表示从左到右、从上到下的扫描再加上RGB/BGR颜色顺序调整。如果你发现颜色反了就是这个字节里的BGR位在起作用。0x3A设置像素格式0x55等于RGB5655位红、6位绿、5位蓝。如果设成0x66就是RGB666颜色空间和显存占用都不一样不能乱配。清屏操作实际上就是不断往0x2C写显存命令后面灌像素数据灌多少取决于窗口大小。这里的窗口函数用到了0x2A和0x2B我会在下面补充说明。4.3 窗口设置函数理解屏幕的“画布裁剪”void lcd_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { uint8_t addr[4]; addr[0] x0 8; addr[1] x0 0xFF; addr[2] x1 8; addr[3] x1 0xFF; lcd_write_cmd_data(0x2A, addr, 4); // 列地址范围 addr[0] y0 8; addr[1] y0 0xFF; addr[2] y1 8; addr[3] y1 0xFF; lcd_write_cmd_data(0x2B, addr, 4); // 行地址范围 lcd_write_cmd(0x2C); // 进入写显存模式 }窗口机制可以理解成“画布裁剪”。系统默认的GRAM是一个完整的帧缓冲窗口命令的作用是把后续写入的数据限定在一个矩形区域里。比如做UI时只需要更新数字区域设置窗口后再写少量像素MCU的SPI总线压力就能大幅降低刷新率也跟着上来。5. 移植调屏最容易翻车的五个瞬间这一节讲几类我实际踩过、也帮别人排查过的高频问题每一个都有明确的排查路径而不是直接给你“改一个参数”的结论。5.1 问题一颜色整体反相红色变蓝色典型症状屏幕上显示红色肉眼看到的却是蓝色绿色变成紫色。根因0x36命令里的BGR位设置错了。LCD的RGB条子分RGB排布和BGR排布两种很多屏驱同时支持两种靠MADCTL里第3位来切换。如果位值设反颜色就整体互换。排查办法是先显示纯红、纯绿、纯蓝三个色块看到哪两个对调了就能确认是BGR方向问题。我自己的排查经验是不要用彩色照片测试那样根本分不清是颜色偏还是反。用三原色块测一眼定位。5.2 问题二画面偏上或偏左像被裁了一刀典型症状图像整体不在屏幕中央有的方向甚至有一圈黑边。根因屏驱内部的GRAM物理布局和LCD面板的可见区域并不完全重合。比如ST7789这颗芯片本身GRAM是240x320但很多1.54寸面板实际只显示240x240区域驱动数据写入时必须设置GRAM偏移让可见区域从(0,0)开始。更麻烦的是某些模块的FPC连接方式还会改变偏移方向。排查办法读模块规格书里关于“Display Area”和“GRAM Offset”的表格。一般厂商会把偏移量写成一个明确数值比如“Column offset: 34, Row offset: 52”直接在窗口设置时把坐标加上这个偏移即可。这里要特别提醒网上很多ST7789驱动代码针对某一款1.54寸圆角屏写死了偏移量你换一家供应商的屏幕时偏移可能就变了所以自己一定要会验证。5.3 问题三白屏但背光亮SPI数据发不进去典型症状背光正常亮屏幕全白MCU发了命令没反应。根因优先怀疑DC引脚命令/数据选择和CS片选时序。有些模块把DC引脚接到了固定电平上导致屏幕永远认为自己收到的是数据而不是命令。还有一种情况是SPI的CPHA配置错数据采样沿不对屏幕上收到的字节全是错的。排查顺序先用逻辑分析仪抓SPI波形看CS、DC、SCLK、MOSI四个信号是否和代码逻辑一致。没有逻辑分析仪的话可以把DC引脚暂时固定在高电平或低电平再发“清屏”命令观察画面变化能快速判断DC接线是否有问题。5.4 问题四画面闪烁、刷新时有撕裂感典型症状动态画面刷新时出现横向的割裂感像两帧画面拼在一起。根因屏驱内部GRAM写入和面板扫描是异步的。当MCU正在往GRAM里写新一帧数据时面板扫描刚好扫描到同一区域就会把新旧数据各显示一部分形成撕裂。屏驱一般会提供撕裂检测TE引脚配合垂直同步可以解决但很多低端模块没引出这个引脚。如果是SPI接口的简单应用更常见的做法是把刷新率降到肉眼可接受的范围或者把刷新时机固定到一帧完整写完后再触发。我实测下来对于240x320分辨率的SPI屏如果刷新频率在30-60Hz之间配合0x37垂直滚动起始地址做整帧一次性写入撕裂感会小很多。5.5 问题五初始化代码一样但不同批次屏幕显示效果不同典型症状同型号屏驱换了供应商显示发白、发暗或者颜色偏。根因屏幕厂商为了匹配自家面板的玻璃特性会在出厂时推荐一套“Gamma校正值”和“电压设置值”这些值以寄存器命令形式存在初始化序列里。同一颗ILI9341芯片A厂商面板和B厂商面板的Gamma寄存器参数往往不一样网上抄来的初始化数组可能只适配某一家的面板。排查办法向模块供应商索要针对具体面板的初始化代码尤其是那些以0xE0、0xE1开头的Gamma设置命令。不要迷信“ILI9341通用的初始化数组”通用数组能点亮但画质不一定是最佳状态。6. 实际选型时的几条经验6.1 不同场景适合的屏驱选LCD屏驱本质上是在分辨率、接口速度、价格、资料丰富度之间做权衡。根据我手里的项目经验大概可以这样归类项目场景推荐屏驱理由低成本小屏显示文字ST7735/ST7789单价低、资料多、SPI速度快普通仪表盘、菜单UIILI9341240x320性价比高、并行接口可选圆形表盘界面GC9A01专为圆形显示优化边缘处理方便彩色图片展示、3.5寸大屏ILI9488分辨率高、支持RGB666色彩黑白低功耗显示SSD1306(OLED)对比度高、功耗低、直接支持I2C工业级大尺寸屏带内置控制器的大屏驱如RA8876类自带显存管理和图层功能需要提醒的是很多小屏模块虽然“看起来一样”但引脚定义可能完全不同。同一颗ST7735有的模块引脚顺序是VCC-GND-CLK-DIN-DC-CS-RST-BL有的会多一个MISO引脚。买之前一定要确认引脚排列别拿另一块板的排线直接插容易烧背光。6.2 关于“便宜屏”的一个实在建议市面上二三十块钱的小屏驱动芯片可能是原厂正品也可能是兼容IC。兼容IC的命令集大部分和原厂一致但某些细节会有差异比如ID读取命令返回值不同、初始化时序要求更严格。我的建议是商业产品尽量选成熟方案至少先验证同一型号的屏驱一致性DIY项目则无所谓反正便宜的坏了不心疼。从我个人经验来看如果项目对刷新率和可靠性要求不高SPI接口的ILI9341方案最稳妥资料密度高、踩坑的人多、可参考代码成熟如果做的是追求外观的小尺寸产品ST7789加1.54寸圆角屏的组合在视觉上确实更讨喜。最后分享一个我自己保留的习惯每调通一块新屏就把完整的初始化数组、窗口偏移、MADCTL方向值、坑点备注整理到工程注释里然后保存一份带日期的副本。因为同一型号的屏驱动芯片在不同批次上真的可能“性格”不同这份记录能帮你下次遇到同型号屏幕时直接避坑。调屏这件事说难不难但细节决定成败多掌握一个排查方向就少熬一个晚上。
阅读完成 · 觉得有帮助?