资讯详情

STM32软件SPI驱动1.8寸TFT-LCD完整教程

发布时间:2026/9/21 11:46:40

500+
企业客户服务经验
120+
行业领域内容覆盖
3000+
原创页面设计沉淀
98%
客户满意度

STM32软件SPI驱动1.8寸TFT-LCD完整教程

如果你第一次玩1.8寸TFT-LCD大概率会经历这些屏幕接上电背光亮了但是屏幕白茫茫一片不管你往寄存器里写多少命令都没反应。我也从这个状态过来的。后来发现卡住大多数人的往往不是屏幕本身而是驱动方式的选择——很多店家给的例程默认走硬件SPI硬件SPI的引脚在自己板子上又经常和别的外设撞车一旦出问题很难定位。这篇文章我要分享的就是我最终稳定跑通的方案STM32通过软件SPI驱动1.8寸TFT-LCD也就是用GPIO手动模拟SPI时序把底层的每一位时钟、每一个电平都摊开来看。这套方案适合刚学会GPIO输出、还没系统学过SPI外设的初学者也适合硬件SPI引脚被其他模块占用、需要灵活换引脚的项目。128x160的分辨率不大软件SPI的带宽完全够用。1. 为什么这块屏的驱动要用软件SPI先说结论软件SPI不是来替代硬件SPI的它是很多实际场景里更省事、更好排查问题的选择。1.8寸TFT-LCD的分辨率只有128x160满屏刷一种颜色也才40多KB像素数据软件模拟的方式完全扛得住。如果你只是做菜单界面、显示传感器数据、画个波形曲线那软件SPI的刷新速度绰绰有余。1.1 硬件SPI和软件SPI到底差在哪硬件SPI是芯片内部集成的外设你只要配置好SPI_CR1寄存器、指定好波特率、极性/相位然后往数据寄存器里丢一个字节剩下的移位输出、时钟生成都由硬件电路自动完成CPU基本不参与。软件SPI则完全是另一条路把SCK、SDA对应的GPIO引脚当作普通IO口用代码循环去翻转电平往SDA上放一个bit再让SCK来一个上升沿如此重复8次凑够一个字节。这两种方式的区别可以列个表看对比项硬件SPI软件SPI引脚灵活性固定绑定在复用引脚上任意GPIO都能用传输速度轻松跑几MHz甚至更高实测大约几百kHz到1MHz左右CPU占用外设自动处理占用极低每个bit都要CPU翻转占用高调试难度配置项多出问题排查链路长代码直观波形一眼能看懂适用场景刷视频、大屏动画、高速传感器菜单、仪表盘、数据展示在实际使用中软件SPI还有一个隐藏优势你可以随时调整引脚连接。比如画好的PCB走线发现问题或者手头只剩某个空闲引脚只需要改一下宏定义不用重新配置SPI外设。1.2 什么场景下优先考虑软件SPI我归纳了三个最典型的场景。第一种是硬件SPI被占用了。STM32F103C8T6这颗芯片虽然有两个SPI外设但项目中W25Q128存储、RF24L01无线模块往往各占一个剩下的引脚还要给串口、定时器、ADC屏幕就只能去普通GPIO上想办法。这时候软件SPI几乎是唯一解。第二种是引脚布局问题。很多蓝板STM32F103C8T6最小系统板的硬件SPI1引脚在PA5/PA6/PA7如果这些引脚已经接了编码器或者舵机再去接屏幕就得飞线绕板子信号不稳定不说看着也别扭。软件SPI可以直接把屏幕接到闲置的PB口一排排插针上整洁又可靠。第三种是学习SPI协议本质。用软件模拟时序的时候你能直观看到SCK电平怎么翻转、SDA数据什么时候被采样、为什么CS要拉低才能选中设备。这些硬核知识通过波形和代码双重验证比单纯用硬件外设黑盒操作理解深得多。之后再去用硬件SPI配置寄存器时你心里会非常有底。2. 动手之前的准备屏幕引脚、接线与工程环境硬件准备的核心就一句话把屏幕的6根信号线接到STM32的任意GPIO上供电方式看清模块丝印。下面我以最常见的STM32F103C8T6蓝板为例把整个准备过程拉通。2.1 1.8寸TFT-LCD引脚定义与接线表1.8寸TFT-LCD模块一般引出8个引脚但实际上我们需要操作的只有6个信号引脚。各厂家丝印命名略有差异最常见的标注是VCC、GND、CS、RES、DC、SDA、SCK、BLK其中SDA也叫SDI或MOSIDC也叫RSSCK也叫SCL或者CLK别被叫法绕晕对应关系如下屏幕引脚类型说明VCC电源供电正极3.3V或5V取决于模块GND电源供电地CS片选拉低选中屏幕芯片RES复位硬件复位信号低电平有效DC数据/命令选择低电平表示写命令高电平表示写数据SDA/MOSISPI数据输入主控向屏幕发送数据SCK/SCLSPI时钟主控产生的时钟信号BLK背光控制拉高或接电源点亮背光我的接线方案是全部走PB口避免和SWD调试口PA13/PA14冲突也和串口1的PA9/PA10分开屏幕引脚STM32引脚说明CSPB10软件片选RESPB12复位控制DCPB14命令/数据切换SDAPB15数据输出SCKPB13时钟输出BLK3.3V串100R电阻背光常亮VCC3.3V模块供电GNDGND共地这里有个注意点模块背面的丝印如果写5V是可以接5V供电的因为模块上大多有稳压芯片转成3.3V给屏幕核心供电如果丝印直接写3.3V老老实实接3.3V。但无论哪种情况ST7735的逻辑IO电平都不支持5VSTM32输出3.3V直接接过去是安全的。BLK背光引脚直接接3.3V也可以但我建议串一个100R电阻防止背光电流过大实测亮度也足够。2.2 STM32CubeMX配置与GPIO初始化现在新学STM32基本都是CubeMX HAL库这套组合了配置起来比标准库快很多。打开STM32CubeMX芯片选STM32F103C8Tx按下面步骤配置RCC选项里HSE选择Crystal/Ceramic Resonator让外部8MHz晶振作为时钟源。SYS选项里Debug选择Serial Wire保留SWD下载调试功能。在引脚规划界面把PB10、PB12、PB13、PB14、PB15分别点击配置为GPIO_Output。逐个引脚检查GPIO Output levelCSPB10初始化为High因为片选默认高电平不选中设备其余引脚初始化为Low。GPIO Speed统一选择High这个选项影响IO翻转速度选High能确保软件SPI高电平持续时间够短、时序更稳定。Clock Configuration里确认SYSCLK为72MHzAPB2外设时钟为72MHz。Project Manager里Toolchain选择MDK-ARM生成工程。生成之后HAL_GPIO_WritePin这个函数够用了。我习惯用宏把引脚操作封装一层这样万一后面要换引脚只需要改宏定义不用在代码里到处找#define LCD_CS_PORT GPIOB #define LCD_CS_PIN GPIO_PIN_10 #define LCD_RES_PORT GPIOB #define LCD_RES_PIN GPIO_PIN_12 #define LCD_DC_PORT GPIOB #define LCD_DC_PIN GPIO_PIN_14 #define LCD_SDA_PORT GPIOB #define LCD_SDA_PIN GPIO_PIN_15 #define LCD_SCK_PORT GPIOB #define LCD_SCK_PIN GPIO_PIN_13 #define LCD_CS_LOW() HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET) #define LCD_CS_HIGH() HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET) #define LCD_RES_LOW() HAL_GPIO_WritePin(LCD_RES_PORT, LCD_RES_PIN, GPIO_PIN_RESET) #define LCD_RES_HIGH() HAL_GPIO_WritePin(LCD_RES_PORT, LCD_RES_PIN, GPIO_PIN_SET) #define LCD_DC_LOW() HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_RESET) #define LCD_DC_HIGH() HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET) #define LCD_SDA_LOW() HAL_GPIO_WritePin(LCD_SDA_PORT, LCD_SDA_PIN, GPIO_PIN_RESET) #define LCD_SDA_HIGH() HAL_GPIO_WritePin(LCD_SDA_PORT, LCD_SDA_PIN, GPIO_PIN_SET) #define LCD_SCK_LOW() HAL_GPIO_WritePin(LCD_SCK_PORT, LCD_SCK_PIN, GPIO_PIN_RESET) #define LCD_SCK_HIGH() HAL_GPIO_WritePin(LCD_SCK_PORT, LCD_SCK_PIN, GPIO_PIN_SET)环境方面再提醒一句第一次在Keil里下载程序记得先把ST-Link驱动装好Options for Target - Debug里选择ST-Link Debugger并确认能识别到芯片IDCODE。如果板子上是CH340或CP2102串口芯片那对应的串口驱动也要装好否则printf调试信息永远看不到。很多人卡在这一步以为代码有问题其实是电脑根本没枚举出设备。3. ST7735初始化序列从复位到亮屏每个命令都在干什么1.8寸TFT-LCD模块的核心是一颗叫ST7735的驱动芯片。这块芯片内部集成了显存GRAM和扫描控制逻辑MCU不需要一直去刷新屏幕只要把像素数据通过SPI写到它的GRAM里ST7735自己会不停地扫描GRAM并驱动液晶面板显示。理解这一点特别关键。3.1 ST7735的核心工作原理整个通信模型可以类比成向一块画板填格子CS是“点名”拉低表示“选中你这块画板我开始跟你说话”DC是“告诉画板我接下来要说什么”说命令还是说数据SDA和SCK则是真正传递信息的那根线和那个时钟节拍RES是“把画板恢复成出厂状态”。ST7735有很多寄存器有的控制显示方向MADCTL有的控制像素格式COLMOD有的控制电源和伽马曲线。初始化就是按照芯片手册规定的顺序依次设置这些寄存器让它进入可以正常显示的状态。ST7735内部GRAM的容量是128x160x16bit因为每个像素用16位RGB565表示刚好是40KB左右。当MCU连续写入像素数据时GRAM的写地址会自动递增自动从左到右、从上到下填充满你设置好的窗口区域这个自动递增特性是我们后面能高效刷屏的基础。3.2 初始化代码逐段拆解ST7735芯片有R版和S版两个常见版本初始化序列有差异。ST7735R老版常见红色PCB需要配置一整套电源、帧率、伽马寄存器才稳定ST7735S新版常见蓝色/黑色PCB初始化简单很多有些屏只要最基本几步就能点亮。判断方法很简单看模块背面丝印或者问卖家。先给一版最简初始化适合ST7735Svoid LCD_Init(void) { // 硬件复位 LCD_RES_LOW(); HAL_Delay(120); LCD_RES_HIGH(); HAL_Delay(120); // 软件复位 LCD_Write_Cmd(0x01); // SWRESET HAL_Delay(150); // 退出睡眠模式 LCD_Write_Cmd(0x11); // SLPOUT HAL_Delay(200); // 像素格式设为16位色 LCD_Write_Cmd(0x3A); // COLMOD LCD_Write_Data(0x05); // 16bit/pixel // 关闭反显 LCD_Write_Cmd(0x20); // INVOFF // 开启显示 LCD_Write_Cmd(0x29); // DISPON HAL_Delay(100); }如果这版点不亮换成ST7735R的完整初始化。完整初始化会在SLPOUT之后、DISPON之前加入FRMCTR1帧率控制0xB1、PWCTR1电源控制0xC0、VMCTR1电压控制0xC4、GAMMA曲线0xE0、0xE1等寄存器配置。这些参数每家面板略有差异最靠谱的来源是你买屏的店铺给例程源码照抄对应的初始化函数即可。我把自己手头这块红色PCB屏的参数补全过代码如下void LCD_Init(void) { LCD_RES_LOW(); HAL_Delay(120); LCD_RES_HIGH(); HAL_Delay(120); LCD_Write_Cmd(0x01); // SWRESET HAL_Delay(150); LCD_Write_Cmd(0x11); // SLPOUT HAL_Delay(200); LCD_Write_Cmd(0xB1); // FRMCTR1 帧率控制 LCD_Write_Data(0x01); LCD_Write_Data(0x2C); LCD_Write_Data(0x2D); LCD_Write_Cmd(0xB2); // FRMCTR2 LCD_Write_Data(0x01); LCD_Write_Data(0x2C); LCD_Write_Data(0x2D); LCD_Write_Cmd(0xC0); // PWCTR1 电源控制 LCD_Write_Data(0xA2); LCD_Write_Data(0x02); LCD_Write_Data(0x84); LCD_Write_Cmd(0xC1); // PWCTR2 LCD_Write_Data(0xC5); LCD_Write_Cmd(0xC4); // VMCTR1 LCD_Write_Data(0x0A); LCD_Write_Data(0x00); LCD_Write_Cmd(0xE0); // GAMMA LCD_Write_Data(0x02); LCD_Write_Data(0x1C); LCD_Write_Data(0x07); LCD_Write_Data(0x12); LCD_Write_Data(0x37); LCD_Write_Data(0x32); LCD_Write_Data(0x29); LCD_Write_Data(0x2D); LCD_Write_Data(0x29); LCD_Write_Data(0x25); LCD_Write_Data(0x2B); LCD_Write_Data(0x39); LCD_Write_Data(0x00); LCD_Write_Data(0x01); LCD_Write_Data(0x03); LCD_Write_Data(0x10); LCD_Write_Cmd(0xE1); // GAMMA - LCD_Write_Data(0x03); LCD_Write_Data(0x1D); LCD_Write_Data(0x07); LCD_Write_Data(0x06); LCD_Write_Data(0x2E); LCD_Write_Data(0x2C); LCD_Write_Data(0x29); LCD_Write_Data(0x2D); LCD_Write_Data(0x2E); LCD_Write_Data(0x2E); LCD_Write_Data(0x37); LCD_Write_Data(0x3F); LCD_Write_Data(0x00); LCD_Write_Data(0x00); LCD_Write_Data(0x02); LCD_Write_Data(0x10); LCD_Write_Cmd(0x3A); // COLMOD LCD_Write_Data(0x05); LCD_Write_Cmd(0x20); // INVOFF LCD_Write_Cmd(0x29); // DISPON HAL_Delay(100); }初始化里这些延时不是随便拍的ST7735从睡眠模式唤醒后内部DC-DC转换器和振荡器需要时间稳定延时不够可能出现“十次有三次亮不了”这种玄学问题。我踩过一次RES拉低只等了10ms结果屏幕偶尔亮偶尔不亮把延时加到120ms之后彻底稳定。4. 软件SPI核心收发代码上升沿采样到底怎么模拟软件SPI没有硬件外设帮你计数和采样所以一切逻辑都靠循环里的人工操作。代码并不长核心就一个字节发送函数搞清楚这一段的电平变化逻辑整个驱动就拿下了一大半。4.1 字节发送函数的时序逻辑我在PB13上用GPIO生成时钟PB15上放数据模拟的是SPI模式0空闲时SCK为低电平在SCK上升沿时数据被采样有效。发送一个字节的流程是先把SCK拉低把SDA设置成当前要发送的bit最高位先发再把SCK拉高产生一个上升沿这时ST7735会在SDA上读到一个稳定的电平。循环8次后8个bit全部完成一字节发送结束。void SPI_WriteByte(uint8_t dat) { for (uint8_t i 0; i 8; i) { LCD_SCK_LOW(); // 时钟拉低准备一次数据发送 if (dat 0x80) // 取出最高位 LCD_SDA_HIGH(); else LCD_SDA_LOW(); dat 1; // 左移准备下一位 LCD_SCK_HIGH(); // 时钟拉高产生上升沿从设备采样 } }注意循环体内的顺序先拉低SCK再改SDA最后拉高SCK。如果顺序反了比如先改SDA再拉低SCK就会出现SDA变化瞬间和上升沿重叠的情况从设备可能采到不稳定的电平。顺序看似简单却是软件SPI可靠性的关键。在STM32F103这种72MHz主频下GPIO翻转本身只要几个时钟周期整个函数跑起来大概1微秒多一字节完全满足ST7735的工作要求。如果你发现屏幕偶尔显示乱码可以在这个函数里加几个空操作延时比如每次电平切换后加两个__NOP()给信号稳定留点余量。但多数情况下不加延时也稳定我自己跑下来满速刷屏没有任何问题。4.2 命令和数据的区分DC引脚的作用光有字节发送还不够ST7735要知道你发的是命令还是数据这个信号由DC引脚决定。DC为低芯片把你的数据当成要写入的寄存器地址或命令DC为高芯片把你的数据当成显示内容或者寄存器参数写入GRAM。封装成两个函数void LCD_Write_Cmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); // 命令模式 SPI_WriteByte(cmd); LCD_CS_HIGH(); } void LCD_Write_Data(uint8_t dat) { LCD_CS_LOW(); LCD_DC_HIGH(); // 数据模式 SPI_WriteByte(dat); LCD_CS_HIGH(); }有些初始化命令后面要跟多个参数比如设置显示窗口的CASET命令0x2A后面要跟4个字节参数所以实际使用中先写命令再连续写多个数据LCD_Write_Cmd(0x2A); // 列地址设置 LCD_Write_Data(0x00); LCD_Write_Data(0x00); LCD_Write_Data(0x00); LCD_Write_Data(0x7F); // 0~127共128列这里有一个优化点如果在写确认是命令时已经拉低了CS那同一个命令后续的数据其实可以保持CS拉低连续写不需要在每写一个字节时都切换一次CS。但为了防止逻辑混乱和不必要的时序风险我封装的两个函数之间会切换CS。这个速度损失在初始化阶段无所谓但在刷屏阶段就必须优化了下面说。4.3 优化连续刷数据时别频繁切换CS刷屏的本质是在设置好窗口之后连续不断地向GRAM写入像素颜色。如果每个像素都调用一次上面那个带CS切换的LCD_Write_Data你会发现刷一屏慢得离谱。因为每次CS拉高再拉低ST7735的串行接口会重新等待命令/数据标识窗口地址寄存器也可能被重置。正确做法是设置好窗口后把CS拉低不要动把DC拉高告诉芯片“下面全是数据”然后循环发送所有像素字节最后再把CS拉高。我实测这个改动让全屏填充速度提升了接近一倍。void LCD_SetWindows(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end) { LCD_Write_Cmd(0x2A); // CASET 列地址 LCD_Write_Data(x_start 8); LCD_Write_Data(x_start 0xFF); LCD_Write_Data(x_end 8); LCD_Write_Data(x_end 0xFF); LCD_Write_Cmd(0x2B); // RASET 行地址 LCD_Write_Data(y_start 8); LCD_Write_Data(y_start 0xFF); LCD_Write_Data(y_end 8); LCD_Write_Data(y_end 0xFF); LCD_Write_Cmd(0x2C); // RAMWR 准备写GRAM }设置窗口时x、y都是16位参数所以分成高8位和低8位两次发送。屏幕分辨率128x160CASET的范围是0~127RASET的范围是0~159参数写错就会出现显示错位或者花屏。后面填充颜色时CS只切换一次void LCD_Fill(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { uint32_t count (uint32_t)(x2 - x1 1) * (y2 - y1 1); LCD_SetWindows(x1, y1, x2, y2); LCD_CS_LOW(); LCD_DC_HIGH(); // 后面全是数据 while (count--) { SPI_WriteByte(color 8); // 高字节 SPI_WriteByte(color 0xFF); // 低字节 } LCD_CS_HIGH(); }5. 刷屏与显示内容填充颜色、ASCII字符和BMP图片屏幕上显示任何内容本质都是往GRAM里写颜色数据。把这一层抽象出来之后画点、画线、画矩形、显示字符甚至显示图片都是同一个套路先设置窗口再往窗口里填充颜色值。5.1 RGB565颜色编码与单色填充ST7735在COLMOD设为0x05时每个像素用16位RGB565格式表示高5位是红色中间6位是绿色低5位是蓝色。这种格式舍弃了每个颜色分量的一部分精度把16位空间利用率拉满。实际使用中我习惯用一个宏把RGB三通道的0~255值转换成RGB565#define RGB565(r, g, b) (((r 0xF8) 8) | ((g 0xFC) 3) | ((b 0xF8) 3))注意转换后的16位数据在字节发送时高位在前color 8先发高字节再发color 0xFF低字节。很多刚上手的朋友把顺序搞反结果颜色整体偏色或者红蓝互换屏幕上图形轮廓是对的但颜色全乱了。全屏填充一种颜色一行代码就能实现LCD_Fill(0, 0, 127, 159, RGB565(255, 0, 0)); // 全屏红色 LCD_Fill(0, 0, 127, 159, RGB565(0, 255, 0)); // 全屏绿色 LCD_Fill(0, 0, 127, 159, RGB565(0, 0, 255)); // 全屏蓝色5.2 显示ASCII字符需要自己准备字模液晶屏本身不认字符要显示字母和数字必须把字符转换成像素点阵数据插进GRAM。我用的是一种常用的8x16 ASCII字模库每个字符占16个字节每个字节对应一列的8个像素点从左到右共8列加上纵向16列正好组成8x16的字符。每个字节的最低位对应字符的顶部还是底部不同的取模工具可能不同如果显示效果是上下颠倒的把取模方向改成从下到上即可。显示字符函数的核心逻辑是先按字符的位置设置显示窗口然后逐列读字模遇到为1的bit就填前景色遇到为0的bit就填背景色。关键是把窗口设置成8像素宽、16像素高然后逐像素输出。void LCD_ShowChar(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg) { const uint8_t *font ASCII_8x16[ch - ]; LCD_SetWindows(x, y, x 7, y 15); LCD_CS_LOW(); LCD_DC_HIGH(); for (uint8_t i 0; i 16; i) { uint8_t byte font[i]; for (uint8_t j 0; j 8; j) { if (byte (0x80 j)) { SPI_WriteByte(color 8); SPI_WriteByte(color 0xFF); } else { SPI_WriteByte(bg 8); SPI_WriteByte(bg 0xFF); } } } LCD_CS_HIGH(); }字模数组ASCII_8x16的代码网上很多直接下载或者自己用取模软件生成都行。这段逻辑里0x80 j是在从高到低逐位读取一个字节的8个bit刚好对应屏幕上从左到右的8列像素。显示中文需要额外准备16x16或者32x32的全角字模思路完全一样只是字节数和行列数更多。5.3 用取模软件显示一张完整图片显示BMP图片的流程本质上就是把你准备好的图片数据数组逐字节灌进GRAM。图片需要预先在电脑上转换成C语言的const数组我用的是Image2Lcd这款工具设置非常关键设置项推荐值输出颜色16位真彩色像素格式RGB565扫描方式水平扫描行优先高位在前勾选最大宽度/高度128 / 160生成的数组是const unsigned char gImage_xxx[]每个像素占2字节。显示时直接设置窗口为图片大小然后连续发送整个数组void LCD_ShowPicture(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *pic) { uint32_t total (uint32_t)width * height * 2; // 总字节数 LCD_SetWindows(x, y, x width - 1, y height - 1); LCD_CS_LOW(); LCD_DC_HIGH(); while (total--) { SPI_WriteByte(*pic); } LCD_CS_HIGH(); }这个函数不关心图片内容只管逐字节搬运所以任何图片只要转成数组就能显示。要注意取模软件的扫描方式必须和你的窗口设置一致如果取模时是垂直扫描而代码里窗口是水平填充图像会变成“拉丝”效果一行一行错位错开。6. 白屏、花屏、偏色我把常见的坑都踩了一遍配置完这些代码之后并不是所有板子都能一次点亮。下面这些坑都是我实际踩过的逐个排查基本能解决90%的问题。6.1 白屏的排查链路如果背光亮了但屏幕全白按下述顺序查。第一步查硬件复位。很多模块的RES引脚需要稳定拉低再拉高的过程STM32上电瞬间RES引脚的电平如果被其他初始化逻辑干扰屏幕就会停留在复位状态。用代码在初始化最开始显式做一次RES低120ms - 高120ms能排除绝大多数复位问题。第二步查DC引脚。DC接错是白屏的另一个高发原因。DC一旦接反屏幕会把命令当成数据处理、把数据当成命令处理所有寄存器配置全部错乱屏幕上就是一片白。确认DC接到了PB14代码里DC宏定义正确。第三步查初始化序列是否匹配你的芯片版本。红板ST7735R没有完整伽马配置就是白屏蓝板ST7735S用完整配置反而可能花屏。先判断版本再决定初始化方案不要混用。第四步如果你有逻辑分析仪直接抓SCK和SDA波形看初始化阶段是否有电平翻转。软件SPI的速度正好在逻辑分析仪的采样范围内方便得很。抓不到波形说明代码根本没跑起来那就是工程配置问题去查时钟树和GPIO模式。6.2 花屏和错位的真正原因花屏的排查点比白屏更多但特征很明显屏幕上能看出有东西在显示只是色彩和位置不对。最常见的花屏原因是窗口设置和实际像素尺寸不匹配。比如把128x160的屏幕错配成132x162或者CASET/RASET参数写反了GRAM的写入指针就会溢出到非预期区域出现“斜着错位”的条纹。把窗口参数改成0~127和0~159通常就能解决。另一种花屏表现为颜色渐变、色斑明显这不一定是代码问题可能是模块电源纹波偏大。我在用杜邦线供电、电源线较长的时候遇到过后来在VCC和GND之间并了一个100uF电解电容现象立刻消失。如果你用的是面包板飞线给屏幕供电多并个电容是好习惯。还有一种是MADCTL方向设置导致的“翻转错位”。ST7735的0x36寄存器控制扫描方向和RGB/BGR顺序不同厂家的屏初始值不同。如果你发现显示内容上下颠倒或者左右反转通过修改LCD_Write_Data(0x00)这个参数来调整。常用几组值显示方向MADCTL值竖屏普通0x00横屏90度0x60竖屏180度0xC0横屏270度0xA0这几个值是经验值不同批次屏幕存在细微差异以实测为准。6.3 引脚电平与供电的灰色地带ST7735的IO逻辑电平是1.8V~3.3V不支持5V。如果你的主控是5V供电的Arduino或者51单片机必须加电平转换直接接大概率烧坏屏幕控制器或者出现各种奇怪乱码。STM32本身输出3.3V这一步反而安全但要注意千万不能图省事用5V供电然后直接接3.3V逻辑——电源域和IO电平域是两回事。另外一个容易被低估的是杜邦线长度。软件SPI虽然速度慢但线上反射噪声依然可能导致数据错误。我试过用30cm的杜邦线连接屏幕满速刷屏时偶发花屏缩短到15cm以内就完全正常。如果现场不得不走长线那就把SPI_WriteByte的循环里加几个__NOP()降速或者干脆把时钟半周期拉长牺牲一点速度换稳定性。最后再提一个值得一提的细节如果屏幕模块是带有PCB板载稳压的版本供电电压标称5V实际芯片逻辑部分还是3.3V。这种情况下供电接5V没问题但不要同时把屏幕的SDA、SCK信号接到5V电平上信号线上没有稳压芯片保护5V逻辑电平直接怼上去照样可能损坏ST7735。写在最后的一点实际建议这套STM32软件SPI驱动1.8寸TFT-LCD的方案我已经在好几个项目里复用了包括一个室内温湿度监测器和一个手持LCR表界面。我的体会是屏幕驱动本身不复杂但一定要把“时序”两个字刻在脑子里。SCK什么时候翻转、SDA什么时候变化、DC什么时候切换每一步都要清清楚楚。软件SPI虽然慢但它把整个通信过程变得完全可控一旦出问题用逻辑分析仪看波形比对着硬件SPI寄存器猜原因高效太多。最后分享一个经验在调试阶段建议把SPI_WriteByte里的时钟速度刻意调慢一点比如在每次SCK翻转后加几个__NOP()这样逻辑分析仪能更清晰地看清楚每一位数据。等整个链路确认无误后再把这些延时拿掉恢复满速刷屏。这种方法能有效区分“时序错误”和“逻辑错误”排查起问题来快得多。
热门专题

继续阅读更多专题内容

围绕企业服务、数字化转型与官网运营的常青话题,持续输出深度内容

企业官网建设指南 企业托管服务模式 财税政策与解读 企业数字化转型 官网SEO与获客 网站安全与运维
配套服务

读完这篇文章,了解更多服务

从整站搭建到SEO布局,17项核心服务助您打造高转化的企业官网

01

企业托管整站搭建

从信息架构到栏目预留,搭建可生长的企业站点骨架,每个页面独立原创设计。...

了解详情
02

规整可信网页设计

雪地靴温暖风原创设计,金属铜线条贯穿全页,拒绝通用模板与AI流水线。...

了解详情
03

企业服务SEO布局

关键词体系与语义化结构,从建站源头为搜索排名而生。...

了解详情
04

业务预约咨询表单

多场景表单与线索收集体系,把访问流量转化为可追踪的销售线索。...

了解详情
05

企业服务站点运维

安全巡检、数据备份与内容更新支持,全年守护网站稳定运行。...

了解详情
06

全终端商务适配

电脑、平板、手机一致呈现,移动端体验与转化同样出色。...

了解详情
需要专业建议?

让专业顾问为您解读行业趋势

关于企业官网建设、SEO获客与数字化转型的任何疑问,欢迎一对一咨询我们的专业顾问。