
简介本资源是一套基于STM32H743微控制器硬件SPI接口驱动ST7789彩色LCD显示屏的完整工程实现面向嵌入式开发工程师、高校电子类专业学生及RTOS/裸机显示系统实践者解决高性能MCU与SPI屏高效通信、初始化时序控制、帧缓冲管理及DMA加速等核心问题。压缩包为ZIP格式共包含若干源码文件如SPI底层驱动、ST7789初始化与绘图函数、CubeMX配置工程、头文件及MD文档说明整体大小19.52MB结构清晰便于快速集成到自有项目中。已有4353人学习下载资源提供可直接编译运行的HAL库工程涵盖CPOL0/CPHA1精准时序适配、命令/数据双模式SPI传输封装、RGB565帧缓冲填充示例及DMA批量刷屏优化方案显著降低CPU占用并提升刷新效率是深入理解高速SPI外设与LCD驱动协同设计的实用参考。1. 为什么H743点屏必须用硬件SPI从软件模拟的局限说起1.1 软件模拟SPI在H743上为什么是浪费不少朋友是从F1、F4平台迁移过来的手上攒了一套“万能”的IO模拟SPI点屏代码到H743上第一反应就是直接移植。我一开始也这么干实测下来发现两个问题一是模拟SPI的时钟频率上不去二是M7内核的流水线优化会对软件时序产生肉眼可见的干扰。先说频率。ST7789这颗控制器绝大多数模组规格书上写SPI时钟最高能到62.5MHz左右但软件模拟SPI在72MHz的F103上能稳定跑到4MHz已经是极限再高的话GPIO翻转速度跟不上逻辑分析仪上看波形都是一堆毛刺。H743主频虽然到了480MHz但GPIO的翻转频率天花板就在那里软件翻转一个引脚的成本读改写寄存器、循环判断、函数调用摆着实测用HAL库GPIO翻转跑模拟SPI能稳定到8MHz都算优化得不错。这等于把一颗480MHz的M7当成了高级版51单片机用CPU占用率还高达40%以上纯粹是暴殄天物。另一个更隐蔽的问题是时序抖动。M7是双发射乱序执行的流水线结构加上D-Cache和I-Cache的命中率波动软件里GPIO_ResetBits和GPIO_SetBits之间插入的延时循环实际执行周期在不同优化等级下可能差出好几倍。结果就是同一份代码-O0下屏能正常显示-O2下画面直接撕裂、颜色错乱。你在F1上踩不到这个坑是因为F1的M3内核简单、流水线短、执行时间可预测但M7不行。这个坑我排查了整整一个晚上才定位到原因后面改用硬件SPI后彻底消失。用硬件SPI就完全是另一回事SPI外设自己管理时钟移位、数据收发不占用CPU指令周期GPIO翻转次数也降到了最低。H743的SPI外设时钟可以走PLLSPI1的时钟源能配到125MHz实际通信时钟可以到62.5MHz是软件模拟的8倍以上。这个差距直接就决定了你刷一张240×240的图是等3秒还是等0.3秒。1.2 H743硬件SPI的资源与速度边界H743一共有6个SPI外设SPI1~SPI6引脚复用足、分布广基本不会出现引脚冲突。对于点ST7789这种单主设备、单向写数据的场景选哪个SPI都行但要注意它们挂在不同的总线桥上决定了可用的最大时钟。关键点是H743的SPI1挂在APB2上SPI2和SPI3挂在APB1上。APB2的最大时钟可以来自PLL1Q在CubeMX里可以配置到最高125MHzAPB1最大是125MHzH743的APB1和APB2都是125MHz级别比F4的84MHz宽松很多。SPI的实际“波特率”是由SPI时钟源 / 分频系数算出来的比如SPI1时钟125MHz、预分频2得到62.5MHz的SCLK如果预分频4就是31.25MHz。ST7789数据手册写的最大SPI时钟一般按60MHz上下算实际工程中建议不要跑满50MHz以下稳定很多。硬件SPI本身还带FIFOTX FIFO和RX FIFO各32位配合DMA可以做到极高吞吐。CPU只需要把待发送数据写到内存DMA自动搬运到SPI的TX寄存器SPI外设自动移位输出CPU全程零参与。对于要跑流畅GUI或者波形显示的场景这是最核心的加速手段。1.3 硬件片选与软件片选你以为的“自动”并不自动先说结论ST7789这种设备多数情况下我建议用软件片选GPIO控制CS但这不是说硬件NSS不能用而是要在理解的前提下用。H743的SPI支持NSS硬控制硬件片选。当配置为硬件NSS SSOE置位时SPI外设会在发送第一个bit之前自动把NSS引脚拉低最后一个bit发送完成后自动拉高。看起来很美实际有两个坑。坑一硬件NSS的自动翻转时机是“按帧”的。如果你用SPI发送命令字节和参数数据时希望CS在整个“命令参数”序列期间保持低电平只靠硬件NSS是做不到的——它会在你每发完一个字节就把CS抬起来而ST7789的命令时序往往要求CS在整个命令码和参数传输期间保持低电平。解决办法要么是用软件片选包住整个序列要么就把命令和参数拼成一个连续的发送缓冲一次DMA全部发完。坑二硬件NSS作为主设备使用时引脚复用配置不对会导致CS完全不受控。很多人把NSS引脚在CubeMX里配成GPIO输出而不是AF模式结果SPI外设根本操作不了这个引脚。H743的SPI1_NSS是复用AF5必须先把引脚复用配置成SPI1_NSS然后在外设初始化里选HAL_SPI_MASTER、NSS为硬件模式CS才会被外设接管。如果用软件片选就是纯粹把CS当作普通GPIO拉低、发数据、拉高。这个方式在驱动ST7789时最稳妥因为ST7789的CS时序要求比较严格命令帧和数据帧之间的CS保持时间、CS释放时间都有最小要求软件控制可以精确满足。而且DMA发送模式下CS拉高的时机最好放在DMA传输完成中断里处理软件片选显然是唯一能精确做到这点的方案。2. 硬件连接与SPI模式配置先调通信号再谈画面2.1 最小接线清单与电平风险ST7789驱动的小屏模组常见规格是1.3寸240×240、1.54寸240×240、2.4寸320×240等。无论哪个尺寸控制器的接口信号都一样。以下是驱动ST7789最精简的接线引脚方向接H743引脚说明VCC电源3.3V模组一般自带稳压极少数需要5VGND地GND共地必须否则电平参考不一致SCL/SCLK输入SPI_SCKSPI时钟SDA/MOSI输入SPI_MOSISPI数据单向写屏不需要MISOCS输入任意GPIO或NSS片选建议先软件控制DC/RS输入任意GPIO命令/数据选择RST输入任意GPIO硬件复位BLK/BL输入任意GPIO或PWM背光控制不需要可以接VCC这里重点提醒一个新手最容易犯的错误ST7789的MOSI叫SDA是单向的。这块屏不需要MISO除非你要通过MISO读取GRAM内容ST7789支持读显存但SPI半双工模式下读数据很麻烦大多数应用也用不上。所以接线清单里根本没有MISO这一项别在CubeMX里把MISO也配上了多配一个引脚倒不会出事但容易让人困惑。关于电平匹配H743的IO是3.3VST7789模组逻辑电平也基本是3.3V直连没问题。但市场上有些带转接板的小屏模组逻辑电压是5V供电、板载稳压到3.3V后驱动屏幕它的信号输入引脚本身就是3.3V电平标准和H743直连也安全。唯一要注意的是别把VCC直接接到5V去驱动那些写明“3.3V only”的模组就一个结果冒烟。接之前仔细读模组丝印和店家说明别问我为什么知道。2.2 SPI初始化时钟、极性、相位、波特率直接用HAL库配置H743的SPI外设驱动ST7789CubeMX里要设置的核心参数如下ModeTransmit Master只发送的主机模式Hardware NSSDisable先用软件片选Data Size8 bitFirst BitMSB FirstST7789高位在前Clock PolarityLow空闲电平为低Clock Phase1 Edge第一个边沿采样Baud Rate先设4分频慢慢往上提对应初始化代码如下SPI_HandleTypeDef hspi1; void MX_SPI1_Init(void) { hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES_TXONLY; // 2线只发送 hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; // 软件片选 hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_4; // 先低频再提速 hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 7; hspi1.Init.CRCLength SPI_CRC_LENGTH_8BIT; hspi1.Init.NSSPMode SPI_NSS_PULSE_DISABLE; if (HAL_SPI_Init(hspi1) ! HAL_OK) { Error_Handler(); } }关于时钟极性和相位ST7789数据手册里的SPI时序图很明确SCLK空闲为低、在SCLK的第一个边沿采样数据、第二个边沿移位输出对应SPI Mode 0CPOL0, CPHA0。实际上Mode 3CPOL1, CPHA1也能点亮因为Mode 0和Mode 3的采样边沿关系是镜像的ST7789内部对SCLK的容忍度不低。但既然手册写的是Mode 0就按Mode 0配别在这个地方搞花活。波特率的选择从低往高试。先用8分频约15.6MHz确保点亮的信号完整性画面稳定后再逐步提到4分频31.25MHz、2分频62.5MHz。用杜邦线连接时高频下信号反射和串扰会明显影响通信质量建议降到4分频以下。如果是PCB板走线短、信号完整2分频甚至1分频125MHz都可能点亮但ST7789的规格上限摆在那里跑太高没有实际意义反而增加了误码概率。2.3 片选和DC引脚的配合逻辑ST7789的DC引脚是命令/数据选择线DC为低时SPI总线上传输的是命令字节DC为高时传输的是命令参数或像素数据。所有命令都由“命令码 若干参数”组成在CS拉低期间DC和MOSI上每一位的时序必须严格对齐。软件片选下的标准做法static void LCD_Write_Cmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); // 命令标志 HAL_SPI_Transmit(hspi1, cmd, 1, 100); LCD_CS_HIGH(); } static void LCD_Write_Data(uint8_t data) { LCD_CS_LOW(); LCD_DC_HIGH(); // 数据标志 HAL_SPI_Transmit(hspi1, data, 1, 100); LCD_CS_HIGH(); }这里有个隐藏的时序细节HAL_SPI_Transmit是阻塞函数返回时数据已经全部从SPI外设的移位寄存器输出完成所以函数返回后马上拉高CS是安全的。但注意H743的SPI带FIFOHAL_SPI_Transmit在数据写入FIFO后可能不会等最后一个bit彻底移出就返回具体要看HAL库实现和FIFO水位保险起见在CS拉高前等SPI总线空闲while (__HAL_SPI_GET_FLAG(hspi1, SPI_FLAG_BSY) ! RESET);这一点对高频SPI尤其重要。SPI的BSY标志在移位寄存器里还有数据时为高等它清零CS再抬起来ST7789才能正确识别一次传输的结束。我的习惯是封装一个LCD_SPI_Transmit函数统一处理FIFO和BSY的等待逻辑避免每个调用点都写一遍。3. ST7789初始化序列逐条拆解每条命令的作用是什么3.1 一套能用且注释清楚的初始化代码网上一搜ST7789初始化代码能搜到十几个版本长得都差不多但没人告诉你为什么要这么写。先把一套测试过、能点亮240×240屏的初始化代码贴出来然后逐条解释static void ST7789_Init(void) { // 硬件复位RST低电平至少10us然后拉高等待120ms LCD_RST_LOW(); HAL_Delay(20); LCD_RST_HIGH(); HAL_Delay(120); // 软复位 LCD_Write_Cmd(0x01); // SWRESET HAL_Delay(150); // 关闭睡眠模式 LCD_Write_Cmd(0x11); // SLPOUT HAL_Delay(500); // 像素格式RGB56516位色 LCD_Write_Cmd(0x3A); // COLMOD LCD_Write_Data(0x55); // 扫描方向根据模组实际方向调整 LCD_Write_Cmd(0x36); // MADCTL LCD_Write_Data(0x00); // 孔隙/栅极设置不同尺寸屏不同 LCD_Write_Cmd(0xB2); // PORCTRL LCD_Write_Data(0x0C); LCD_Write_Data(0x0C); LCD_Write_Data(0x00); LCD_Write_Data(0x33); LCD_Write_Data(0x33); // 电压设置 LCD_Write_Cmd(0xB7); // GCTRL LCD_Write_Data(0x35); // VCOM设置 LCD_Write_Cmd(0xBB); // VCOMS LCD_Write_Data(0x19); // LCM控制 LCD_Write_Cmd(0xC0); // LCMCTRL LCD_Write_Data(0x2C); // VDV和VRH命令使能 LCD_Write_Cmd(0xC2); // VDVVRHEN LCD_Write_Data(0x01); // 液晶电压 LCD_Write_Cmd(0xC3); // VRHS LCD_Write_Data(0x12); // VDV设置 LCD_Write_Cmd(0xC4); // VDVS LCD_Write_Data(0x20); // 帧率控制 LCD_Write_Cmd(0xC6); // FRCTRL2 LCD_Write_Data(0x0F); // 电源控制 LCD_Write_Cmd(0xD0); // PWCTRL1 LCD_Write_Data(0xA4); LCD_Write_Data(0xA1); // 关闭反转显示 LCD_Write_Cmd(0x20); // INVOFF // 开启显示 LCD_Write_Cmd(0x21); // INVON // 进入正常显示模式 LCD_Write_Cmd(0x13); // NORMALON HAL_Delay(10); // 打开面板显示 LCD_Write_Cmd(0x29); // DISPON HAL_Delay(10); }这套代码是从一个240×240 IPS模组上实测验证过的。不同厂家的模组PORCTRL、GCTRL、VCOMS这些参数会有些差异不影响点亮但会影响色彩的饱和度、对比度、偏色程度。如果你发现颜色发白、发灰或者对比度不对优先调这几个参数。3.2 几个关键命令必须搞清楚COLMOD (0x3A)像素格式。0x55是RGB56516位/像素2字节0x66是RGB66618位/像素但是通过3字节传输。对SPI这种串行总线来说RGB565的流量是RGB666的三分之二同样的时钟频率下刷屏速度可以快三分之一。ST7789原生支持RGB565所以绝大多数应用选0x55既省流量又省内存。MADCTL (0x36)内存访问控制。这个寄存器控制扫描方向、行/列交换、RGB顺序说人话就是它决定了图像的“上下左右”和“镜像”。0x00是标准方向从左上角开始扫0xC0是旋转180度0xA0是垂直镜像0x60是水平镜像。想要旋转90度或270度配置0x40或0x80但这时候屏幕逻辑分辨率的宽高也要对调。这块没有任何技巧老老实实一个一个试。PORCTRL (0xB2)孔隙控制寄存器。不同尺寸的ST7789屏需要不同的扫面线设置。第5、第6个数据是栅极扫描起始/结束位置相关参数240×240和320×240的屏差异比较大。如果你用的屏是320×240网上很多240×240的初始化代码直接抄过来有可能出现显示错位或上下偏移届时需要调整0xB2的参数。这是我实际遇到的坑后面细说。INVON (0x21) 和 INVOFF (0x20)显示反转开关。IPS屏和TN屏对于“电平时像素亮还是灭”的定义相反所以有些屏初始化后画面是反色的字是黑色、底是白色或者反过来只要把0x20改成0x21或者把0x21改成0x20颜色就正常了。这是第一步点屏最常见的问题之一。3.3 没有资料时怎么确定扫描方向第一次拿一块不知道确切的模组型号、没有完整数据手册的屏怎么写MADCTL我的方法是先按0x00点亮然后在屏幕上显示一个带方向性的测试图——比如画一个左上角写“UP”、右下角写“DOWN”的矩形。然后依次改MADCTL为0x00、0x60、0xA0、0xC0镜像、旋转组合的四个基本方向每次刷新观察文字方向找到一个“文字正常、不镜像”的组合。如果四个方向都不正常再把RGB顺序反转MADCTL的BGR位0x08重新试一遍。把这套方法录成函数点新屏时直接循环切换MADCTL参数按键看效果效率极高。我在调试新模组时就这么干省去了反复烧录的麻烦。4. 从初始化到“点亮”画点、区域写与颜色格式4.1 画点函数的本质设定窗口写显存ST7789内部有一块完整的GRAM显存尺寸等于屏的物理分辨率如240×240×2字节。SPI数据并不会直接映射到物理像素上而是一条“像素写入流”你先告诉控制器往GRAM的哪块区域写入列地址、行地址然后连续发送的像素数据会自动从左到右、从上到下填充这个区域。这个过程就是ST7789的“窗口模式”Partial/Rolling Mode的简化应用。对应到代码上static void ST7789_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { LCD_Write_Cmd(0x2A); // CASET列地址设置 LCD_Write_Data(x0 8); LCD_Write_Data(x0 0xFF); LCD_Write_Data(x1 8); LCD_Write_Data(x1 0xFF); LCD_Write_Cmd(0x2B); // RASET行地址设置 LCD_Write_Data(y0 8); LCD_Write_Data(y0 0xFF); LCD_Write_Data(y1 8); LCD_Write_Data(y1 0xFF); LCD_Write_Cmd(0x2C); // RAMWR开始写显存 } void ST7789_DrawPixel(uint16_t x, uint16_t y, uint16_t color) { ST7789_SetWindow(x, y, x, y); LCD_Write_Data(color 8); LCD_Write_Data(color 0xFF); // 实际上RAMWR命令只需要发一次后面连续跟数据即可 }注意0x2CRAMWR命令一旦发出后续的SPI数据都会被视为像素数据按顺序写入GRAM直到下一次发送命令通过DC引脚切回命令模式。所以在连续填充一块区域时只需要开头发一次0x2A0x2B0x2C后面跟一大串像素数据即可不需要每个像素都重新设置窗口。DMA刷屏就是基于这个特性一次性把整块区域的像素数据灌进去。4.2 RGB565颜色格式下的数据字节序ST7789在COLMOD0x55RGB565下每个像素是2字节高字节是高5位红色高3位绿色低字节是低5位绿色低5位蓝色。发送顺序是高字节在前MSB First还是低字节在前取决于你前面SPI配置的FirstBit参数和ST7789的字节序要求。ST7789手册规定SPI数据总是MSB First也就是第一个bit是字节的最高位字节之间按你发送的顺序原样写入GRAM。这就带来一个容易踩的坑你的颜色值0xF800红色在RGB565里是红色分量拉满发送时应该先发0xF8再发0x00。如果在代码里直接LCD_Write_Data(color 8); LCD_Write_Data(color 0xFF);就是正确的。但如果某些库函数写成了先发低字节再发高字节颜色就会完全错乱——红色变成蓝色、绿色还是绿色、蓝色变成红色。看起来像颜色通道互换其实是字节序反了。4.3 清屏与局部刷新别整个屏从头推很多人第一步写清屏函数就是全屏刷一整个颜色用硬件SPI加DMA之后这个操作只需要几十毫秒CPU基本不参与所以清屏不是大问题。但在实际GUI应用里如果每次点击按钮都把全屏重新刷一遍帧率会迅速拉胯。更好的做法是只对变化区域调用ST7789_SetWindow把变化矩形的最小外包矩形算出来只刷新这部分。比如你有一个数值显示区域在(20, 40)到(120, 60)数字变化时只需要刷新这100×20的区域而不是整屏240×240。这个小技巧能把刷屏时间缩短到原来的十分之一以下。ST7789的窗口模式本来就是为这个设计的不用白不用。配合一个简单的脏矩形队列效果非常明显。5. 硬件SPIDMA把帧率真正跑起来的组合5.1 DMA配置要点与等待发送完成的细节光有硬件SPI还不够要让H743的SPI发挥全部性能必须上DMA。H743的DMA1和DMA2各有多个Stream通道SPI1_TX的DMA请求可以在CubeMX里直接看到映射关系。配置DMA传输spi_tx的核心步骤在CubeMX里给SPI1添加DMA请求方向Memory To Peripheral外设地址固定为SPI1-TXDR内存地址为显存缓冲区的地址。数据宽度SPI配置为8bit时DMA外设宽度和数据宽度都选Byte也可以在数据量大的时候把SPI配置为16bit、DMA用Half Word减少DMA请求次数提高一点效率。DMA模式选Normal非循环因为每次刷屏的缓冲可能不同。优先级按需设置不需要很高只要别在刷屏时被其他DMA抢断即可。然后用HAL库接口启动一次刷屏HAL_StatusTypeDef LCD_DMA_WriteData(uint8_t *buf, uint32_t len) { LCD_CS_LOW(); LCD_DC_HIGH(); // 数据模式 HAL_SPI_Transmit_DMA(hspi1, buf, len); // 注意此时DMA已经启动函数立即返回不能立刻拉高CS // 必须等DMA传输完成和SPI总线空闲后再拉高 }重点来了HAL_SPI_Transmit_DMA是异步函数它把DMA请求提交给控制器后立即返回。你需要在DMA传输完成中断或者SPI发送完成中断里等待SPI外设的BSY标志清零后再拉高CS。这个等待非常关键因为SPI外设的TX FIFO可以预取多个数据DMA报告“传输完成”时FIFO里可能还有好几个字节没移出移位寄存器。如果这时候把CS拉高屏幕收到的数据是不完整的画面会出现随机位置的撕裂线。标准写法是在DMA中断回调里做void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { // 等待SPI总线空闲后再拉高CS while (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_BSY) ! RESET); LCD_CS_HIGH(); // 刷屏完成标志位方便主循环判断 lcd_dma_busy 0; } }如果漏了这个BSY等待画面会出现偶发的撕裂或者最后一行缺失。这个问题在低频SPI下不明显因为DMA传输完成时最后一个bit早就移出了但在高频30MHz以上下几乎必现。5.2 实测帧率对比数据我对同一块1.3寸240×240的ST7789屏做了几组帧率实测缓冲队列就是显存的一个240×240×2数组测试内容是纯色全屏填充驱动方式SPI时钟清屏时间每秒帧数全屏刷新CPU占用软件模拟SPI约4MHz约230ms约4帧高硬件SPI阻塞31.25MHz约30ms约30帧中硬件SPI DMA31.25MHz约29ms约34帧极低看到没有硬件SPI DMA相比硬件SPI阻塞在帧率上没有本质提升因为瓶颈在SPI总线上。但CPU占用天差地别阻塞模式下CPU全程傻等DMA模式下CPU可以去做别的计算比如处理触摸输入、更新GUI逻辑、跑算法。对于需要“刷屏同时做其他事”的应用DMA是必须的。如果想进一步提高帧率可以尝试把SPI时钟提到62.5MHz全屏刷新时间理论可以压到约20ms也就是约50帧。但高频模拟屏的信号完整性和屏幕本身的最大时钟都是限制因素需要实测确认稳定性。5.3 全刷屏时为什么用双缓冲如果你刷的是动态内容波形、动画、视频流单缓冲很容易出现“刷到一半被截断”的撕裂。原因很简单屏幕正在从GRAM读数据到LCD面板同时你的DMA又在往GRAM写数据两者速率不同步显示中的一帧就被“撕”开了。解决方法是双缓冲定义两个240×240×2字节的数组一个叫draw_buf一个叫display_buf。你在draw_buf里画下一帧的内容画完后通过DMA把draw_buf的数据发到屏幕下一次画的时候用display_buf屏幕正在显示的数据画完后DMA再发过去。哪个缓冲正在被DMA使用就不动它只画另一个。这样屏幕上永远显示完整、连续的画面。H743内部RAM有1MB两个150KB的缓冲完全放得下这是F103这种20KB RAM平台想都不敢想的玩法。6. 踩坑记录几个高频问题的完整排查链路6.1 白屏先查复位与背光再查初始化是否执行白屏是第一大问题。排查顺序按“信号链”从外到内背光亮不亮不亮那就是BLK引脚或者背光供电没接对跟SPI没关系。复位引脚时序对不对ST7789上电后需要硬件复位RST拉低至少10us再拉高然后至少等120ms再发命令。如果RST引脚悬空或者上拉不足屏幕会一直处于复位状态。供电电压对不对用万用表量屏的VCC3.3V正负10%都在范围内但有些劣质模组在灌电流大的时候电压会塌陷会导致初始化失败。初始化代码有没有真正执行在初始化函数的最后加一个标志或者翻转一个LED确认代码确实跑到了。SPI通信有没有收到数据用逻辑分析仪抓SCLK和MOSI看有没有波形。如果SCLK是平的检查CubeMX引脚复用配置。如果MOSI一直是高检查DC控制是否把命令和数据都发到命令模式了。这五步走完白屏问题基本都能定位。90%的情况是复位或SPI初始化没配好。6.2 花屏SPI时钟太高、信号线太长、干扰花屏和闪屏不一样花屏是图像显示杂乱、颜色错乱、像打翻了调色盘。排查链路如下降低SPI时钟。先从8分频开始如果降到8分频后画面正常说明高频信号完整性有问题。杜邦线超过10cm时30MHz以上信号反射会很严重。解决方案换短线、用杜邦线时把SCLK和GND相邻排列、或者干脆做块PCB。检查电源去耦。ST7789供电脚附近应该有100nF 10uF电容如果没有高速SPI切换时电源噪声会耦合进数据信号。检查MADCTL。花屏不一定是信号问题也可能只是扫描方向错了图像被镜像或旋转了看起来像“花”实际只是方向不对。检查是否所有GS级初始化参数都发了。特别是PORCTRL、GCTRL这些参数如果漏了显示可能严重偏色或者花屏。6.3 画面闪烁、撕裂DMA与主循环的竞争现象刷新过程中画面闪烁、颜色断裂有个别行颜色不对。这是DMA和主循环在同时访问同一块显存导致的。排查方式在DMA回调里设置一个lcd_dma_busy标志主循环在刷屏之前检查标志如果DMA还在忙就先不往显存写数据。或者严格双缓冲DMA只负责发送display_buf画图只在draw_buf上操作两者物理隔离互不干扰。如果实在需要在DMA传输过程中修改显存可以把DMA缓冲区定义成volatile并在修改前关中断修改后开中断。但这对大块数据来说不可行还是要双缓冲。6.4 硬件NSS始终不受控引脚复用查了一遍还是不行这个坑值得单独说。用硬件NSS驱动SPI时代码和CubeMX配置看起来都对但CS引脚纹丝不动一直是低电平。排查链路先在CubeMX里确认SPI1_NSS引脚通常是PA4被复用为AF5不是GPIO_Output。如果选成GPIO_OutputSPI外设根本控制不了它。检查HAL_SPI_Init结构体里的NSS字段。SPI_NSS_HARD_INPUT模式下外部NSS引脚的电平由外部驱动决定SPI_NSS_HARD_OUTPUTH743支持模式下SPI外设才会主动驱动NSS。很多人用HAL库默认的SPI_NSS_HARD即HARD_INPUT在主设备模式下完全不输出片选信号CS引脚对外是高阻态。在H743上正确配置是hspi1.Init.NSS SPI_NSS_HARD_OUTPUT;且hspi1.Init.NSSPMode SPI_NSS_PULSE_ENABLE;。这样SPI外设会在每个数据帧开始前自动拉低NSS发送完成后拉高——如果你需要整帧CS保持低则用PULSE_DISABLE。用软件片选时CS引脚配置为普通GPIO输出不复用AF所有CS时序由代码控制。两条路线各有适用范围但我的建议是批量刷大块数据用软件片选 DMA精确控制CS时序单发一两个字节的命令用软件片选也无妨。硬件NSS的自动翻转反而会让你在“一条命令多个参数”这种常见组合上束手束脚除非你把一整帧的“命令参数”数据拼成一个连续的buffer然后一次性DMA发送这样硬件NSS所有帧都保持CS低也是可以的只是拼接缓冲要花点心思。我在实际项目中反复对比过这两种方案承认硬件NSS确实省引脚和代码但可维护性和灵活性不如软件片选。如果项目对引脚数量不敏感还是建议软件片选。刷屏性能完全不受影响因为瓶颈从来都不是CS翻转的微秒级时间而是SPI总线本身的带宽。本文还有配套的精品资源点击获取