简介:一套面向嵌入式学习与开发的1.44寸LCD串口模块(ST7735)软硬件资料包,覆盖C51、STM32、Arduino三大平台,主线清晰。资料提供各平台下的SPI驱动源码与接线说明,其中STM32硬件SPI测试代码区分中文、英文显示场景,C51源码适合入门练手,Arduino串口版本可快速验证模块效果;同时附有模块原理图、尺寸图、Altium封装库、芯片数据手册以及图片和文字取模软件,既能帮助初学者从零点亮屏幕,也能让开发者直接参考硬件设计、快速生成字库与图片数据,节省调试时间。压缩包整体约18MB,内部文件以工程源码、原理图、PDF手册、取模工具等为主,目录按平台和功能分类存放,结构一目了然。已有219人参与学习,适合学生、电子爱好者及嵌入式工程师作为ST7735屏幕驱动移植与硬件设计的参考资料。
1. 把“串口模块”当成UART接,大概率白屏一整晚
很多第一次拿到1.44寸LCD串口模块的人,都会被背面的“串口”两个字带偏,以为接上USB转串口就能发AT指令。实际上这块屏用的是SPI串行总线:SCL、SDA、DC、CS几根信号线协同工作,和TXD/RXD的UART完全不是一回事。真正决定它能不能亮的是屏内的ST7735控制芯片,而芯片版本、初始化序列、引脚电平,都会因同型号不同批次的模块悄悄改变。下面把这套屏从硬件原理图、封装库,到C51、STM32、Arduino三套驱动源码的常见做法讲一遍,目标是让你拿到任何一块1.44寸ST7735,都能在两小时内点亮并跑出稳定显示。
2. 1.44寸ST7735模块的引脚定义、原理图分析与封装库核查
市场上1.44寸模块绝大多数是8Pin:VCC、GND、SCL、SDA、RES、DC、CS、BL。不同商家会把排针顺序打乱,有的把电源放在左侧,有的放在右侧,所以接线前一定先看模块丝印,不要相信排线颜色顺序。
2.1 认识模块上的引脚:SDA/SCL并不是I2C
先给出一份常见的引脚定义与接线对照表,以STM32和Arduino Uno为例:
| 引脚 | 方向 | 功能 | STM32示例 | Arduino Uno示例 |
|---|---|---|---|---|
| VCC | 电源 | 3.3V供电 | 3.3V | 3.3V |
| GND | 电源 | 地 | GND | GND |
| SCL | 输入 | SPI时钟SCK | PA5 | D13 |
| SDA | 输入 | SPI数据MOSI | PA7 | D11 |
| RES | 输入 | 复位,低有效 | PB0 | D8 |
| DC | 输入 | 数据/命令选择 | PA4 | D9 |
| CS | 输入 | 片选,低有效 | PA3 | D10 |
| BL | 输入 | 背光控制 | PA1(PWM) | D6(PWM) |
注意这里的SCL/SDA命名确实容易和I2C搞混,但它们就是SPI的SCK和MOSI,这块屏只写不读,所以没有MISO。DC和CS是ST7735驱动中最重要的两根控制线:DC为低时,主机发送的字节被解释为命令;DC为高时,主机发送的字节被写入显存。如果STM32把CS接到了硬件NSS引脚,又没有把NSS配置成软件模式,片选会被SPI外设自动拉掉,屏幕就会出现时好时坏的诡异现象。
2.2 原理图里容易看走眼的背光、复位和逻辑电平
BL引脚控制背光LED,多数模块板载了限流电阻,IO给高电平背光亮,给低电平背光灭。自己画主板原理图时,建议用三极管或MOS管做背光开关,再用PWM引脚调亮度。PWM调亮度靠的是占空比而不是降低电压,频率在1kHz以上基本看不到闪烁。
C51平台没有硬件PWM时,用定时器中断翻转引脚即可,代码可以这样写:
// C51定时器0中断,约1kHz频率做背光PWM,BL_DUTY控制亮度 void Timer0_ISR(void) interrupt 1 { static unsigned char cnt = 0; TH0 = 0xFC; // 12MHz晶振下约256us重装 TL0 = 0x18; if (++cnt < BL_DUTY) { LCD_BL = 1; } else { LCD_BL = 0; } if (cnt >= 100) { cnt = 0; } }这段代码里BL_DUTY的取值范围是0到100,代表一个PWM周期内高电平的百分比。TH0=0xFC、TL0=0x18的初值需要根据主频微调,核心是让中断周期稳定在1ms以内,否则背光频闪会被眼睛察觉。STM32和Arduino平台直接用定时器PWM外设会更省事,只需把BL引脚映射到定时器通道。
复位电路方面,RES引脚在模块上通常有RC复位,MCU侧用GPIO控制更可靠。上电后先把RES拉低至少10us,再拉高,然后等待120ms让ST7735内部DCDC稳定。如果跳过复位直接初始化,偶发情况是第一次白屏、第二次正常,排查时容易误判为屏坏。
逻辑电平是另一个高频翻车点。ST7735本身工作电压在2.8V左右,多数模块板载了LDO或稳压电路,标称支持5V供电。C51平台用5V单片机时,SPI引脚输出的高电平明显超过3.3V,如果模块没有做电平转换,长期使用建议串100到220欧电阻限流,或者用74LVC4245这一类电平转换芯片。STM32和Arduino平台则直接按3.3V逻辑电平对待。
2.3 从硬件原理图到封装库:画板前必须核对的三项
拿到所谓的“软硬件资料”后,不要急着把封装往EDA里拖。常见做法是先打开原理图PDF,核对三处:模块接插件的引脚顺序、ST7735供电去耦电容的位置、PCB排线的安装方向。商家给的原理图库和封装库,多数是把座子画成2x4排针,实际打样时还经常遇到丝印方向和排线方向相反的问题。
封装库应用时重点检查三点:
- 检查原理图符号的引脚编号,务必和实物丝印逐一对应,引脚交换只在封装内部处理;
- 检查封装焊盘间距,标准排针间距是2.54mm,但部分模块把排针做成了1.27mm,封装不匹配会直接插不进去;
- 检查机械层丝印,给屏幕排线和固定孔留出空间,避免元器件与屏幕FPC干涉。
封装库里的位号、丝印和3D模型只是参考,真正决定能否装配的是排针间距、PCB外形和屏幕排线长度。画完板子再拿游标卡尺量一次实物,比反复看PDF更有价值。
3. ST7735初始化序列:从寄存器到一行能显示的驱动协议
ST7735是单芯片LCD控制器,内部GRAM对应132列162行,1.44寸屏实际使用的是128x128区域。每像素在RGB565模式下占16bit,算下来全屏缓冲是128乘以128乘以2字节,等于32KB。这个数字在后面讨论帧率和内存占用时会反复出现。
3.1 关键寄存器与初始化顺序
上电后的固定流程是:硬件复位、软件复位、Sleep Out、等待、像素格式、帧率、内存数据访问控制、正负伽玛校正、显示开启。任何一步顺序错乱都可能花屏,最常见的是跳过Sleep Out之后的120ms延时,导致首屏显示噪点。
下面是ST7735初始化过程中最关键的一组寄存器:
| 指令 | 值 | 含义 | 参数说明 |
|---|---|---|---|
| SWRESET | 0x01 | 软件复位 | 无参数 |
| SLPOUT | 0x11 | 退出睡眠模式 | 无参数,随后需延时约120ms |
| COLMOD | 0x3A | 像素格式 | 0x05表示RGB565 16bit |
| MADCTL | 0x36 | 内存数据访问控制 | 高两位控制行列扫描,BIT3控制RGB/BGR |
| FRMCTR1 | 0xB1 | 正常模式帧率 | 三个参数,设置时钟与分频 |
| INVCTR | 0xB4 | 反色控制 | 0x07为常见值 |
| RAMWR | 0x2C | 写显存 | 每个像素16bit,高字节在前 |
| DISPON | 0x29 | 显示开启 | 无参数 |
3.2 一套通用的ST7735初始化代码
下面这段初始化代码不依赖具体平台,把DC和CS的翻转封装在命令和数据函数里,移植到C51、STM32、Arduino时只需要改底层的SPI_WriteByte:
static void LCD_WriteCmd(uint8_t cmd) { LCD_DC_LOW(); // 命令模式 LCD_CS_LOW(); // 拉低片选 SPI_WriteByte(cmd); LCD_CS_HIGH(); } static void LCD_WriteData(uint8_t dat) { LCD_DC_HIGH(); // 数据模式 LCD_CS_LOW(); SPI_WriteByte(dat); LCD_CS_HIGH(); } static void LCD_Init(void) { LCD_RST_LOW(); // 硬件复位 delay_ms(20); LCD_RST_HIGH(); delay_ms(120); LCD_WriteCmd(0x01); // 软件复位 delay_ms(150); LCD_WriteCmd(0x11); // 退出睡眠 delay_ms(120); LCD_WriteCmd(0x3A); // RGB565 16位色 LCD_WriteData(0x05); LCD_WriteCmd(0xB1); // 帧率配置 LCD_WriteData(0x01); LCD_WriteData(0x2C); LCD_WriteData(0x2D); LCD_WriteCmd(0x36); // 行列扫描方向 LCD_WriteData(0xC0); LCD_WriteCmd(0x29); // 显示开启 }0x36写入的0xC0设置了MY和MX位,让扫描方向从上到下、从左到右,同时保持RGB色序。不同模块的FPC走线可能不同,屏幕镜像或上下颠倒时优调这个字节,而不是怀疑屏坏。0x3A设为0x05后,后续所有写显存的数据都按RGB565高字节先行发送,CRAM地址由CASET和RASET决定。
3.3 初始化后花屏、白屏的排查顺序
初始化完屏幕全白、全黑或花屏,按下面顺序排查:
- 先量VCC电压,模块上如果是LDO供电,输入5V时输出应在3.3V附近,低太多会白屏;
- 再用示波器抓RES引脚,确认低电平保持时间超过10us,释放后延时够不够;
- 然后核对SPI模式,ST7735要求CPOL=0、CPHA=0,也就是Mode0,有些STM32库默认Mode3会直接采错数据;
- 最后检查DC接线,命令阶段DC必须为低,接反的表现为屏幕有背光但一直不显示内容。
这里还有一个隐藏坑:ST7735有多个版本,绿色FPC和红色FPC对坐标原点定义不同。128x128的1.44寸屏通常没有偏移,但用CASET/RASET做局部刷新时,坐标原点和窗口偏移量不对,会出现内容错位。这个问题放到第6章具体讲。
4. C51、STM32、Arduino三平台驱动移植:从SPI字节到显示函数
4.1 先统一SPI接口抽象层
写驱动时不要把SPI细节和画点函数揉在一起。把底层抽象成三个函数:SPI_WriteByte负责一个字节的时序,LCD_DC(LOW/HIGH)和LCD_CS(LOW/HIGH)负责控制线,再加上LCD_RST和LCD_BL,上层的画点、画线、刷屏就只调同一套接口。
这样做的直接好处是:C51上验证过的初始化序列,迁到STM32时只需要替换SPI_WriteByte的实现;Arduino平台如果不想用现成库,也可以套同一套逻辑。
4.2 C51平台:IO模拟SPI,注意位序和IO模式
C51平台最常用的是STC89C52这类8位单片机,没有SPI外设或者引脚不够时,直接用普通IO模拟。下面这个函数是完整可用的:
sbit LCD_SCL = P1^0; sbit LCD_SDA = P1^1; sbit LCD_DC = P1^2; sbit LCD_CS = P1^3; sbit LCD_RST = P1^4; void SPI_WriteByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { LCD_SCL = 0; // 时钟拉低,准备改变数据 if (dat & 0x80) { LCD_SDA = 1; } else { LCD_SDA = 0; } dat <<= 1; // MSB先行 LCD_SCL = 1; // 上升沿,从机采样 } }这段代码的核心是“先拉低SCL,再放数据,最后拉高SCL”,ST7735在SCL上升沿锁存SDA上的电平。数据位顺序必须是MSB first,dat <<= 1每次把最高位移走,配合dat & 0x80判断当前位。STC89C52的P1口是准双向口,输出高电平驱动能力偏弱,接ST7735这种需要一定灌电流的芯片时,优先用P0口加外部上拉,或者换STC15系列的推挽输出。
Keil5里同时安装C51和ARM芯片包,同一个IDE就能维护C51小项目和STM32大工程,这个组合在开发多屏项目时很顺手。但要注意,模拟SPI的频率由每条C语句编译出的汇编周期决定,12MHz晶振下SCK大约在几百kHz级别,全屏刷新会明显偏慢,所以C51平台更常见的用法是显示静态字符、小图标和数字仪表。
4.3 STM32平台:硬件SPI外设换帧率
STM32平台建议直接用硬件SPI,F103这类芯片的SPI外设跑4到9MHz很轻松,刷屏速度和IO模拟不在一个量级。基于HAL库的SPI1初始化如下:
SPI_HandleTypeDef hspi1; hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0,对应Mode0 hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; HAL_SPI_Init(&hspi1);这里的CLKPolarity和CLKPhase合起来就是Mode0,与ST7735要求的空闲低电平、第一个边沿采样一致。NSS设成SOFT后,CS引脚完全由GPIO控制,避免硬件自动片选带来的干扰。BaudRatePrescaler_8在72MHz主频下得到9MHz SCK,这已经是很多模块稳定性上限,遇到大花屏或偶发错位,先把分频调到16甚至32,SCK降到4.5MHz以下再说。
SDA接PA7,SCK接PA5,DC、CS、RES用三个普通GPIO。在意刷屏速度时,可以把整帧数据放到缓冲区,用HAL_SPI_Transmit_DMA异步发送,发送期间CPU去准备下一帧或处理按键。DMA传输过程中不要动CS,否则ST7735会把中途的字节当成新命令。
4.4 Arduino平台:现成库和自驱动两条路
Arduino平台最省事的是Adafruit_ST7735加Adafruit_GFX组合:
#include <Adafruit_GFX.h> #include <Adafruit_ST7735.h> #define TFT_CS 10 #define TFT_DC 9 #define TFT_RST 8 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); void setup() { tft.initR(INITR_144GREENTAB); // 1.44寸,ST7735S,绿色FPC tft.fillScreen(ST7735_BLUE); } void loop() { tft.drawChar(0, 0, 'A', ST7735_WHITE, ST7735_BLUE, 1); delay(1000); }INITR_144GREENTAB是Adafruit库预置的初始化参数,包含延时和偏移量,只对绿色FPC的1.44寸屏有效。如果你的模块花屏,尝试换成INITR_BLACKTAB,或者在库源码里直接修改_colstart和_rowstart。U8g2库的字体支持更好,显示中文和混合字符时比Adafruit方便,但占用的Flash也更大。
Arduino Uno的硬SPI引脚固定为D13、D11、D10,分别对应SCK、MOSI、CS。如果SD卡和其他SPI设备共用了这几个引脚,每次访问屏幕前要重新设置CS,否则总线竞争会显示乱码。
4.5 三平台驱动方案对比
| 平台 | SPI来源 | 典型SCK | 内存占用 | 适用场景 |
|---|---|---|---|---|
| C51 | GPIO模拟 | 约500kHz | 极小,需省RAM | 静态仪表、字符显示 |
| STM32 | SPI外设+DMA | 4-9MHz | 可外扩帧缓冲 | 动态图形、中等帧率 |
| Arduino | SPI库 | 4-8MHz | 库本身占几KB | 快速原型、教学验证 |
STM32和Arduino的选择主要看实时性和内存;C51的优势在低成本、低功耗,劣势是全屏刷新慢。对C51来说,局部刷新不是优化手段,而是唯一可行的方案。
5. 软硬件资料里的原理图与封装库:怎么核对、怎么整理、怎么复用
网上解压出来的资料包通常有几十个文件夹,命名混乱,真正能用的可能只有原理图PDF、几个驱动源码文件和封装库压缩包。第一步不是打开源码,而是先整理资料。
5.1 拿到资料包先做三件事
第一,把资料按docs、libs、firmware三类分开;第二,打开原理图PDF只看三张图:系统框图、接口电路、封装尺寸图;第三,核对封装库的引脚和模块实物丝印是否一致。
原理图里重点看BL和RES是否直接连到GPIO,有没有串二极管或限流电阻。如果BL线上已经有一路三极管驱动,主板上的IO就只负责给高电平或PWM,不需要再加驱动。反过来,如果原理图上BL直接悬空,模块可能默认不亮,需要外部上拉。
5.2 封装库导入时的细节
封装库导入时不要只拖PCB封装。先导入原理图符号,PDF标注的模块引脚和原厂符号经常对不上,需要手动改引脚名。然后是PCB封装,注意排针类型,1.44寸模块8P单排居多,间距2.54mm,也有双排2x4的设计。封装画错最常见的是丝印画到屏幕排线正下方,结果装机时FPC没有空间弯折。
各EDA之间也存在差异:立创EDA导入Altium的封装库时,部分焊盘属性和3D模型会丢失,需要重新关联;Altium导入立创EDA的封装则要检查丝印层和机械层的映射。无论从哪个方向转,最终都要拿数据手册里的封装尺寸图核对一遍。
5.3 建议的工程目录结构
一个能长期复用的资料目录是这样的:
lcd_1in44_st7735/ ├── docs/ │ ├── ST7735_datasheet.pdf │ ├── Module_schematic.pdf │ └── 1in44_mechanical.pdf ├── libs/ │ ├── ST7735_1in44.schlib │ ├── ST7735_1in44.pcblib │ └── README.txt ├── firmware/ │ ├── c51_lcd_demo/ │ ├── stm32_lcd_demo/ │ └── arduino_lcd_demo/ └── tools/ └── lcd_test.pydocs里放数据手册和机械图,libs里放原理图库、封装库和说明,firmware下放三个平台的驱动源码,tools里放字模生成和测试脚本。这样的组织方式在后续画板、改板、换平台时都能直接复用。
5.4 用字模生成脚本替代手敲点阵
驱动源码里最繁琐的是中文字模和ASCII字模。手工按位填十六进制数组容易错位,常见做法是写脚本生成。下面这段Python脚本可以从点阵字库读取字符并输出C数组:
def read_glyph(ch, w=8, h=8): # 按GB2312或Unicode定位字库中的位图数据 # 返回一个长度为h的整数列表,每个整数代表一行的bit pass def glyph_to_c(ch, w=8, h=8): rows = read_glyph(ch, w, h) return ", ".join(f"0x{row:02X}" for row in rows) print(glyph_to_c('A'))用FreeType或点阵字库生成出来的数组,可以直接粘贴进C51的code区或STM32的const数组。比起手敲点阵,脚本生成快且不会错位。这里还有一个常见误用:直接把8x8字模塞进16位色LCD,字形会显得很小,建议放大两倍或改用12x12、16x16字模。
配合本地仿真工具或逻辑分析仪调试时,资料包的整洁程度直接影响排查速度。同一批买的屏,厂家给出的初始化代码可能只适配其中某一批FPC,保留好每批模块的偏移量记录,比反复刷固件更高效。
6. 点亮之后:用逻辑分析仪验证ST7735时序,再用局部刷新榨干小屏性能
屏幕能亮只是开始。判断驱动是否真的健康,靠逻辑分析仪抓SPI总线是最快的方式。CLK接模块SCL,MOSI接SDA,CS和DC同时采样,触发方式设成CS下降沿。上电后第一帧数据里应当能看到0x11、0x3A、0x36、0x29这一串命令,且每条命令前的DC为低,数据前的DC为高。SCK频率超过模块标称时,波形上会出现台阶或间距不均匀,这就是偶发花屏的来源。
验证完以后,最值得做的优化是局部刷新。ST7735支持CASET和RASET设定矩形窗口,向RAMWR写数据时只填充窗口区域:
void LCD_SetWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { LCD_WriteCmd(0x2A); // 列地址 LCD_WriteData(0x00); LCD_WriteData(x0 + XOFF); LCD_WriteData(0x00); LCD_WriteData(x1 + XOFF); LCD_WriteCmd(0x2B); // 行地址 LCD_WriteData(0x00); LCD_WriteData(y0 + YOFF); LCD_WriteData(0x00); LCD_WriteData(y1 + YOFF); LCD_WriteCmd(0x2C); // 开始写显存 }显示时钟或百分比数字时,只需要更新数字所在的小窗口。128x128全屏刷新要写32KB数据,窗口宽度20、高度16时只有640字节,C51平台也能稳定跑到每秒几次更新。XOFF和YOFF由屏幕FPC版本决定,绿色FPC多为0,红色FPC常见值为2和1,颜色偏移时先换这两个偏移量。
用逻辑分析仪观察局部刷新时,窗口设置帧和写显存帧之间不能夹带其他SPI传输,否则坐标会被写乱。点亮后如果出现噪点,优先检查0x36的RGB/BGR位,而不是怀疑硬件损坏。
本文还有配套的精品资源,点击获取