STM32驱动ST7735液晶屏:从初始化到图形显示的完整指南

📅 发布时间:2026/7/3 15:05:41 👁️ 浏览次数:
STM32驱动ST7735液晶屏:从初始化到图形显示的完整指南
1. 硬件连接为你的STM32和ST7735“牵线搭桥”拿到一块ST7735液晶屏和一块STM32开发板第一步就是把它们正确地连接起来。这就像给两个设备“牵线搭桥”线接对了通信才能顺畅。ST7735通常支持两种通信方式4线SPI和8位并行。对于大多数单片机项目尤其是资源有限的STM32F1/F0系列4线SPI因其接线简单、占用IO少而成为首选。你需要准备至少6根杜邦线。核心的SPI三根线必不可少SCK时钟、MOSI主机输出从机输入也就是数据线、CS片选。片选信号CS非常重要它告诉屏幕“现在主机要和你说话了”当有多个SPI设备时靠它来区分。然后是DC数据/命令线这是控制液晶屏的关键。你发给屏幕的数据可能是命令比如设置显示区域也可能是真正的像素颜色数据全靠DC引脚的电平高低来区分。通常低电平表示命令高电平表示数据。接着是RESET复位线用于对屏幕进行硬件复位确保从一个已知的确定状态开始工作。最后是背光控制线虽然你可以直接接VCC让背光常亮但单独控制可以省电。具体到STM32的引脚你需要查看芯片的数据手册找到SPI1或SPI2对应的引脚。例如对于STM32F103C8T6蓝桥杯常用的芯片SPI1的引脚通常是PA5(SCK), PA7(MOSI), PA6(MISO驱动屏幕通常不需要)。你可以将屏幕的SCK接PA5MOSI接PA7。然后找三个普通的GPIO口比如PB0接CSPB1接DCPB2接RESETPB3接背光。接线时务必确认开发板和屏幕的电压一致常见的是3.3V。如果屏幕是5V tolerant容忍5V的那从3.3V的STM32驱动也没问题但如果屏幕逻辑电压就是5V你可能需要电平转换电路不过现在很多1.8寸屏都是3.3V的直接连就行。我刚开始玩的时候曾经因为把MOSI和MISO接反而导致屏幕毫无反应排查了半天。所以接线这一步虽然基础但一定要仔细。接好后可以用万用表测一下通断或者上电后用手感受一下屏幕主控芯片是否有轻微发热正常工作时微温是正常的烫手就不对了。一个可靠的硬件连接是后续所有软件工作的基石。2. 软件环境搭建与SPI配置硬件连好了接下来就要在STM32的代码里“打通”SPI通信的通道。我习惯使用STM32CubeMX这个图形化工具来初始化这对新手特别友好能避免很多底层配置的坑。首先在CubeMX里选择你使用的STM32具体型号比如STM32F103C8。第一步是配置系统时钟SYS。在Debug里建议选Serial Wire这样可以用ST-Link进行调试。然后配置RCC高速外部时钟HSE选择Crystal/Ceramic Resonator这样系统就能使用外部晶振获得更精准的时钟。重头戏是配置SPI1。在Mode里选择“Full-Duplex Master”即全双工主机模式。虽然我们只向屏幕发送数据不读取但全双工模式是标准配置。然后看下面的参数设置Baud Rate波特率决定了通信速度。ST7735的SPI时钟最高可以到15MHz左右但为了稳定起见尤其是接线较长或有干扰时我通常会先设一个保守的值比如9MHzPCLK2分频系数设为4。等全部调通后再尝试提高速度。Clock Polarity时钟极性CPOL和Clock Phase时钟相位CPHA这两个参数必须和屏幕要求的一致。绝大多数ST7735屏都工作在CPOL0 CPHA0的模式下。这意味着时钟空闲时为低电平数据在时钟的第一个边沿上升沿被采样。你可以在屏幕的数据手册里确认这一点。如果这两个参数设错屏幕会完全收不到正确的数据。数据帧格式选择8 bits帧格式选择MSB First高位在前。这些设置好之后SPI1的引脚PA5, PA7就会被自动配置好。接下来我们要配置那几个关键的GPIO口CS、DC、RESET和背光。在CubeMX的引脚图上找到PB0, PB1, PB2, PB3分别将它们设置为GPIO_Output。你可以给它们起个有意义的标签比如“LCD_CS”、“LCD_DC”这样生成的代码可读性更好。最后配置一下时钟树确保系统时钟是你期望的频率比如72MHz然后就可以生成代码了。CubeMX会为你生成一个完整的Keil或IAR工程。在生成的代码里SPI的初始化函数MX_SPI1_Init和GPIO的初始化都已经就绪。你需要做的就是编写驱动屏幕的具体函数了。这里有个小技巧在main.c里记得在while(1)循环之前调用HAL_SPI_Init并确保所有GPIO的初始化已完成。一个常见的错误是顺序不对导致SPI外设还没准备好就开始发送数据。3. ST7735初始化序列详解唤醒你的屏幕屏幕硬件和MCU的SPI都准备好了现在我们要通过一系列的命令和数据来“唤醒”ST7735让它进入正常工作状态。这个过程就是发送初始化序列。如果你直接看原始代码里那一长串LCD_SPI_Send_Cmd和LCD_SPI_Send_Data_8可能会有点懵其实它们是在依次设置屏幕的各种内部寄存器。首先必须是硬件复位。拉低RESET引脚至少5ms然后再拉高等待一段时间比如120ms。这个操作能让屏幕控制器恢复到一个确定的初始状态避免之前的状态干扰。紧接着可以打开背光。然后发送软件复位命令0x01。这是屏幕控制器内部的复位同样需要延时等待。之后是退出睡眠模式命令0x11屏幕从低功耗的睡眠状态进入正常工作状态这个命令后通常需要120ms以上的延时。接下来的命令开始配置屏幕的各种参数。帧率控制命令0xB1, 0xB2, 0xB3用于设置正常模式、空闲模式和部分模式下的刷新率参数。这些参数如原始代码中的0x01, 0x2C, 0x2D会影响显示画面的流畅度和功耗一般按照数据手册推荐的典型值设置即可。显示反转控制0xB4设置像素点的扫描方式。电源控制序列0xC0, 0xC1, 0xC2, 0xC3, 0xC4是一组非常重要的命令用于配置屏幕内部的电荷泵、电压放大器等电源电路确保屏幕有稳定且合适的驱动电压。这些参数值通常由屏幕厂商根据具体面板优化确定不建议随意修改。内存访问控制0x36这个命令是我要重点说的。它决定了屏幕的显示方向旋转和颜色格式。它的参数是一个字节每一位都有含义MY设置上下翻转MX设置左右翻转MV设置行列交换横竖屏切换RGB设置颜色顺序是RGB还是BGR。原始代码中设置的是0x00即采用默认方向RGB顺序。如果你发现你的屏幕显示颜色不对比如红色显示成蓝色可能就是这里的RGB/BGR位设错了如果显示方向不对就调整MY、MX、MV位。颜色模式设置0x3A告诉屏幕我们使用哪种颜色深度。对于65K色16位色的ST7735这里要写入0x05表示16位/像素。伽马校正序列0xE0, 0xE1提供两组正负极性伽马校正表用于调整屏幕的亮度和色彩响应曲线使显示效果更佳。最后发送打开显示命令0x29屏幕才会开始显示帧内存中的内容。整个初始化序列看起来复杂但其实很多都是固定的“套路”。在实际项目中我通常会把这一大段初始化代码封装成一个函数ST7735_Init()并且使用一个结构化的命令列表来组织就像原始代码中后面部分展示的那样用一个数组包含命令、参数个数和延时信息这样代码更清晰也便于维护和移植。4. 核心驱动函数编写画点、画线与填充屏幕初始化成功后那片黑暗的玻璃下面就已经准备好了显示内容只等我们向它的“帧缓存”写入数据。驱动液晶屏最基础、最核心的函数就是画点函数。所有复杂的图形、文字归根结底都是由无数个点组成的。写一个画点函数ST7735_DrawPixel(x, y, color)你需要做三件事。第一进行坐标边界检查确保x和y的值在屏幕分辨率例如128x160之内防止写入非法地址。第二设置显示窗口。屏幕内部有一个行列地址指针你需要通过CASET0x2A和RASET0x2B命令告诉屏幕接下来要写入的数据对应屏幕上哪个矩形区域。对于单个像素点这个区域就是(x, y)到(x, y)本身。第三发送像素颜色数据。发送RAMWR0x2C命令后紧接着发送颜色值。在16位色模式下颜色值是一个16位数常用RGB565格式高5位是红色中间6位是绿色低5位是蓝色。你可以用宏ST7735_COLOR565(r, g, b)来方便地合成颜色。有了画点函数你就可以构建更复杂的图形函数。画线函数无论是水平线、垂直线还是斜线都可以通过布雷森汉姆算法高效实现。这个算法避免了浮点运算只使用整数加法和比较非常适合单片机。画矩形函数可以先画四条边或者更高效地直接调用填充矩形函数只填充边框。画圆函数则可以利用圆的对称性只计算八分之一的圆弧点然后映射到其他七个对称位置大大减少计算量。但真正影响显示效率的往往是填充函数比如ST7735_FillRectangle和ST7735_FillScreen。最朴素的方法是循环调用画点函数但这样效率极低因为每画一个点都要重复设置一次地址窗口和发送命令。正确的优化方法是先通过CASET和RASET设置好要填充的整个矩形区域然后发送RAMWR命令接着在一个循环里连续发送所有像素的颜色数据。屏幕的地址指针会在收到数据后自动递增当到达行尾时会自动换到下一行的起始列。这里还可以进一步优化对于单色填充我们可以先在内存中构造一行像素的数据比如要填充宽度为w的矩形就构造w个相同的颜色值然后循环h次每次发送这一整行数据。这比逐个像素发送要快得多因为减少了函数调用和循环开销。原始代码中的ST7735_FillRectangleFast函数就采用了这种思路它使用malloc动态分配一行数据的缓冲区填充完成后释放。对于整屏填充直接设置全屏窗口然后发送width*height次颜色数据即可。实测下来优化后的填充函数速度可以提升5到10倍动画效果会流畅很多。5. 显示文字与图像让屏幕会说话图形界面离不开文字显示。在单片机这种资源有限的环境下我们通常使用位图字体。所谓位图字体就是事先把每个字符比如ASCII码从32到126的可见字符在特定大小如7x10像素、11x18像素下的样子用0和1表示出来1代表点亮0代表不点亮然后把这些数据做成一个常量数组存到程序的Flash里。原始代码中提供了Font_7x10,Font_11x18,Font_16x26三种字体就是很好的例子。以Font_7x10为例每个字符用10个uint16_t16位整数表示每个整数代表字符的一行像素共7列虽然用了16位存储但只用了高7位。显示字符时函数ST7735_WriteChar会取出对应字符的字体数据逐行、逐位判断如果是1就画一个前景色的点如果是0就画一个背景色的点。写字符串函数ST7735_WriteString则是在此基础上逐个字符显示并处理自动换行当当前行剩余宽度不足以显示下一个字符时将光标移动到下一行的开头。这里需要注意字体的宽度和高度信息以及屏幕的边界防止字符串显示到屏幕外面去。显示图像的原理类似但数据量更大。图像需要事先转换成单片机可用的格式通常是RGB565格式的数组。你可以用一些电脑上的工具如Image2Lcd、LCD Image Converter把BMP、PNG等图片转换成C语言数组。这个数组就是图像所有像素的颜色值按行优先顺序排列。显示时只需要像填充矩形一样设置好图像所在的窗口区域然后将整个数组通过SPI发送出去即可。原始代码中的ST7735_DrawImage函数就实现了这个功能。这里有几个坑我踩过一是图片尺寸一定要和函数调用时指定的宽高一致否则会显示错乱。二是图片数组很大会占用大量的Flash空间STM32F103C8T6只有64KB Flash放一张128x128的RGB565图片128128232768字节就占了一半所以要谨慎使用大图。三是SPI发送大量数据时如果使用HAL库的HAL_SPI_Transmit要注意它可能会因为超时或中断而发送失败在发送图像这种大数据块时可以考虑使用DMA来释放CPU实现更流畅的显示。6. 高级技巧与性能优化实战当基本的显示功能都实现后我们往往会追求更流畅的动画、更低的功耗或者更丰富的界面。这就涉及到一些高级技巧和性能优化。首先是双缓冲与局部刷新。在动画中如果直接在前景缓冲区即屏幕正在显示的内存上绘图可能会看到闪烁因为绘图过程中屏幕可能刷新到了半成品画面。一种解决方法是使用双缓冲在MCU的内存里开辟一块和屏幕分辨率一样大的“后台缓冲区”所有的绘图操作都先在这个缓冲区里进行这其实就是操作一个二维数组当一整帧画面准备好后再一次性将这个缓冲区的数据全部更新到屏幕上。虽然STM32内部RAM有限F103只有20KB对于128x160的屏幕需要40KB缓冲区可能放不下整屏但可以用于局部动画区域。更实用的技巧是局部刷新只更新屏幕上发生变化的那一小块区域而不是刷新整个屏幕这能极大提高效率。其次是利用DMA释放CPU。SPI传输数据时CPU需要不断搬运数据并等待传输完成。使用DMA直接内存访问控制器可以让DMA自动将内存中的数据搬运到SPI的数据寄存器传输完成后产生中断通知CPU。这样在传输大量数据比如填充屏幕、显示图像时CPU就可以去处理其他任务如读取传感器、更新逻辑实现并行处理。配置SPI的DMA传输稍微复杂一些需要在CubeMX中开启SPI Tx的DMA通道并编写相应的传输完成回调函数。然后是低功耗考虑。ST7735屏幕本身有几个省电命令SLPIN0x10进入睡眠模式此时功耗可以降到微安级DISPOFF0x28关闭显示但控制器部分电路仍在工作。在设备待机时可以依次调用这些命令。记得在重新唤醒时需要先退出睡眠SLPOUT再打开显示DISPON并可能需要重新初始化部分参数。最后是驱动代码的模块化与可移植性。一个好的驱动应该将硬件相关的部分如GPIO操作、SPI发送函数通过宏或函数指针抽象出来将屏幕命令和图形算法放在独立的层。原始代码中将引脚定义放在头文件里通过修改宏就能适配不同板子这就是很好的实践。你还可以定义一套简单的图形API比如GUI_DrawLine,GUI_DrawCircle底层调用具体的屏幕驱动这样以后更换不同型号的屏幕如ILI9341时只需要替换底层驱动上层应用代码几乎不用改动。7. 调试与常见问题排查指南即使按照指南一步步操作第一次驱动屏幕也很可能遇到问题。这里我总结了一些常见的“症状”和排查方法希望能帮你快速定位问题。问题一屏幕完全无反应背光都不亮。这是最让人头疼的情况。首先检查电源和背光用万用表测量屏幕的VCC和GND引脚是否有正确的电压3.3V或5V背光引脚是否被拉高。如果背光不亮可能是背光控制线接错了有些屏幕背光是共阳极需要接VCC有些是共阴极需要接GND。如果电源正常用手触摸屏幕主控芯片应该有微温如果冰凉可能是芯片根本没工作或损坏。问题二背光亮但屏幕全白、全黑或有杂乱色块。这说明屏幕已经上电但初始化序列可能有问题。首先检查SPI的时钟极性和相位CPOL/CPHA这是最容易出错的地方。ST7735通常要求Mode 0CPOL0 CPHA0。你可以在SPI初始化后用逻辑分析仪或示波器抓一下SCK和MOSI的波形看是否有数据发出时序是否正确。其次检查复位时序复位引脚拉低的时间是否足够至少5ms复位后等待的时间是否足够长比如100ms以上。可以尝试在初始化代码中增加更多的HAL_Delay。问题三能显示但颜色完全不对比如红色显示为蓝色。这通常是颜色格式RGB/BGR设置错误。检查初始化命令MADCTL0x36中的RGB位或者COLMOD0x3A命令设置的颜色深度。RGB565格式下颜色数据的字节顺序也可能是问题尝试交换颜色值的高低位发送。问题四显示内容错位、镜像或旋转。这是显示方向MADCTL设置的问题。调整MADCTL命令参数中的MY行地址顺序、MX列地址顺序、MV行列交换位。通常有四个方向可选你可以写一个简单的测试程序依次尝试这四个参数看看哪个方向是你想要的。问题五显示有毛刺、闪烁或部分区域不正常。可能是电源噪声或SPI速度过快。尝试降低SPI的波特率比如降到1MHz以下看是否改善。检查电源线上是否并联了足够的去耦电容通常屏幕模块上已有但开发板电源可能不稳。如果使用杜邦线连接线太长也可能引入干扰尽量缩短连线。问题六运行一段时间后死机或花屏。检查堆栈空间是否足够。大量局部数组如图像缓冲区可能造成栈溢出。可以将大数组定义为静态加static关键字或全局变量。另外检查SPI传输函数是否正确处理了所有错误标志并考虑了重试机制。调试时一个串口打印调试信息的功能非常有用。你可以在每个关键步骤如复位完成、发送某命令前后通过串口打印一条信息这样就能知道程序卡在了哪里。如果条件允许逻辑分析仪是调试SPI通信的神器它能直观地显示每一个命令和数据字节让你对通信过程了如指掌。记住耐心和有条理的排查是解决硬件驱动问题的关键。