发布时间:2026/8/2 10:04:00
1.69英寸SPI LCD驱动全解析:从硬件连接到GUI优化实战 1. 从选型到点亮为什么是1.69英寸SPI LCD如果你正在为一个便携式设备、智能手表或者一个需要紧凑显示界面的嵌入式项目寻找屏幕那么1.69英寸的LCD显示屏很可能已经进入了你的视野。这个尺寸非常微妙——它比常见的0.96英寸OLED大得多能显示更丰富的内容但又比2英寸以上的屏幕小巧非常适合对空间有严格限制的应用。而SPI接口则是让这颗“小眼睛”亮起来的关键钥匙。在嵌入式开发中显示屏的驱动方式直接决定了硬件设计的复杂度和软件资源的消耗。面对I2C、8080并行、RGB并行和SPI这几种常见接口为什么SPI成为了这类小尺寸LCD的绝对主流简单来说SPI在引脚占用、驱动复杂度和速度之间取得了最佳平衡。一个标准的4线SPISCLK MOSI MISO CS加上DC数据/命令和RST复位引脚通常6个GPIO就能搞定这极大地节省了宝贵的MCU引脚资源。相比之下并口驱动动辄需要16根以上的数据线对于主控芯片的引脚数量和PCB布线都是巨大挑战。我手头这块1.69英寸的屏幕分辨率通常是240x280或240x240色彩深度为16位RGB565。这意味着它需要驱动近7万个像素点每个点包含16位颜色信息。如果使用低速I2C刷屏速度会慢到令人难以接受而SPI尤其是配合MCU的硬件SPI和DMA完全能满足这类屏幕的刷新需求实现流畅的界面效果。从网络上的热议也能看出无论是STM32、ESP32还是FPGA开发者们都在围绕“SPI驱动LCD”进行着深入的探讨和优化这恰恰证明了其广泛的应用基础和持续的技术生命力。接下来的内容我将以一个典型的1.69英寸SPI LCD驱动芯片多为ST7789或ST7735S为例带你从硬件连接到软件驱动从底层时序到上层GUI完整地走通点亮、绘图、显示中文乃至优化性能的全过程。无论你是刚接触嵌入式显示的初学者还是想优化现有驱动性能的进阶者这里都有你需要的“干货”。2. 硬件连接与SPI模式深度解析拿到一块屏幕第一步永远是看手册Datasheet。但很多廉价模块附带的手册可能语焉不详这时就需要我们根据驱动芯片的通用特性和引脚定义进行推断和验证。2.1 引脚定义与硬件连接一块典型的1.69英寸SPI LCD模块其引脚通常包括以下核心信号VCC/GND电源与地。常见工作电压为3.3V务必确认你的主控电平与之匹配。SCLK (SCK)串行时钟线由主控产生用于同步数据。MOSI (SDA)主设备输出从设备输入。即主控发送数据/命令给屏幕的线路。DC (RS, A0)数据/命令选择线。这是SPI驱动LCD区别于其他SPI设备如Flash的关键引脚。它告诉驱动芯片当前在MOSI上发送的是命令如设置显示区域还是数据如像素颜色。CS (CSX)片选信号低电平有效。用于在多个SPI设备共存时选择当前要通信的设备。RST (RESX)复位引脚低电平复位。用于硬件复位屏幕驱动芯片确保其从一个已知的初始状态开始工作。BLK (LED, LEDK)背光控制。可以是简单的PWM调光控制也可能是直接接电源常亮。连接时将VCC/GND接好SCLK、MOSI、DC、CS、RST分别连接到MCU的任意GPIO对于SCLK和MOSI建议使用硬件SPI引脚以获得最佳性能。一个常见的连接示例如下以STM32F103为例LCD_VCC - 3.3V LCD_GND - GND LCD_SCLK - PA5 (SPI1_SCK) LCD_MOSI - PA7 (SPI1_MOSI) LCD_CS - PA4 (任意GPIO) LCD_DC - PA3 (任意GPIO) LCD_RST - PA2 (任意GPIO) LCD_BLK - PA1 (PWM输出用于调光)注意MISO引脚在这类屏幕上通常不需要连接因为屏幕是纯输出设备不会向主控发送数据。但有些屏幕的MISO可能用于读取芯片ID或状态可根据具体需求决定是否连接。2.2 SPI四种模式与驱动芯片的匹配SPI有四种时钟模式由时钟极性CPOL和时钟相位CPHA决定。这是驱动任何SPI设备前必须确认的参数一旦错误通信将完全失败。CPOL (Clock Polarity)时钟空闲状态。0空闲时低电平1空闲时高电平。CPHA (Clock Phase)数据采样时刻。0在时钟的第一个边沿采样1在时钟的第二个边沿采样。对于ST7789/ST7735S这类驱动芯片最常用的模式是SPI Mode 0 (CPOL0, CPHA0) 或 SPI Mode 3 (CPOL1, CPHA1)。具体需要查阅芯片手册。一个快速验证的方法是如果模块供应商提供了示例代码直接查看其SPI初始化配置如果没有可以先用Mode 0尝试如果不行再换Mode 3。在STM32的HAL库中初始化SPI时需在SPI_HandleTypeDef结构中设置hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; // 仅发送可设为2LINES_TXONLY hspi1.Init.DataSize SPI_DATASIZE_8BIT; // 通常使用8位数据帧 hspi1.Init.CLKPolarity SPI_POLARITY_LOW; // CPOL 0 hspi1.Init.CLKPhase SPI_PHASE_1EDGE; // CPHA 0 hspi1.Init.NSS SPI_NSS_SOFT; // 使用软件控制CS引脚 hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_2; // 时钟分频决定SPI速度 hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; // 通常为MSB先行 hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 10; if (HAL_SPI_Init(hspi1) ! HAL_OK) { Error_Handler(); }这里的关键是CLKPolarity和CLKPhase。一个常见的踩坑点有些屏幕模块为了兼容性内部可能已经有上拉或下拉电阻影响了信号边沿。如果严格按照手册配置模式仍无法通信可以尝试用逻辑分析仪或示波器抓取SCLK和MOSI的波形对照芯片手册的时序图检查数据采样边沿是否正确。我曾遇到过一款屏幕手册写Mode 0但实际必须用Mode 3才能驱动最后就是靠抓波形发现的。3. 驱动层构建从复位初始化到像素写入硬件连通后我们进入软件驱动层。这一层的目标是封装出最基础的三个函数写命令、写数据、以及最核心的“在指定位置画一个点”。3.1 底层GPIO控制与通信时序在调用HAL库的HAL_SPI_Transmit之前我们需要手动控制DC和CS引脚。这是因为SPI外设本身不管理这些与协议相关的控制信号。// 假设引脚已定义为宏 #define LCD_CS_LOW() HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET) #define LCD_CS_HIGH() HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET) #define LCD_DC_CMD() HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET) #define LCD_DC_DATA() HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET) #define LCD_RST_LOW() HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET) #define LCD_RST_HIGH() HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET) // 写命令函数 void LCD_Write_Cmd(uint8_t cmd) { LCD_DC_CMD(); // 设置DC为命令状态 LCD_CS_LOW(); // 选中设备 HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); LCD_CS_HIGH(); // 取消选中可选有些驱动要求CS一直保持有效直到数据写完 } // 写数据函数 void LCD_Write_Data(uint8_t data) { LCD_DC_DATA(); // 设置DC为数据状态 LCD_CS_LOW(); HAL_SPI_Transmit(hspi1, data, 1, HAL_MAX_DELAY); LCD_CS_HIGH(); } // 写多个数据用于连续写入像素数据 void LCD_Write_Data_Buffer(uint8_t *data, uint16_t length) { LCD_DC_DATA(); LCD_CS_LOW(); HAL_SPI_Transmit(hspi1, data, length, HAL_MAX_DELAY); LCD_CS_HIGH(); }注意关于CS引脚的操作时机存在两种流派。一种是每次传输命令或数据都拉低再拉高另一种是在一系列连续操作如初始化序列、填充整屏前拉低全部完成后才拉高。后者效率稍高但需要确保操作序列间没有其他SPI设备干扰。对于单一屏幕的应用两种方式均可我通常使用第一种逻辑更清晰。3.2 初始化序列唤醒屏幕的“咒语”驱动芯片上电后处于休眠或未知状态需要发送一系列特定的命令和参数进行初始化。这个过程就是初始化序列。不同驱动芯片、甚至同芯片不同分辨率的屏幕初始化序列都可能不同。最好的来源是供应商提供的示例代码或驱动芯片的数据手册。一个ST7789V的简化初始化流程如下硬件复位拉低RST引脚至少10ms再拉高等待120ms以上让芯片稳定。发送软件复位命令通常为0x01并延迟等待。退出睡眠模式命令0x11并延迟等待。设置颜色格式如0x3A命令参数0x55代表16位RGB565。设置显示方向旋转命令0x36通过参数控制MX,MY,MV等位。设置列地址和行地址范围命令0x2A和0x2B这决定了后续像素数据写入的窗口。打开显示命令0x29。void LCD_Init(void) { // 1. 硬件复位 LCD_RST_LOW(); HAL_Delay(20); LCD_RST_HIGH(); HAL_Delay(120); // 等待复位完成 // 2. 发送初始化命令序列 LCD_Write_Cmd(0x01); // 软件复位 HAL_Delay(150); LCD_Write_Cmd(0x11); // 退出睡眠 HAL_Delay(255); // 设置颜色接口格式 16bit/pixel LCD_Write_Cmd(0x3A); LCD_Write_Data(0x55); HAL_Delay(10); // 设置显示方向 (0度 可根据需要调整) LCD_Write_Cmd(0x36); LCD_Write_Data(0x00); // MY0, MX0, MV0, ML0, BGR0, MH0 // 设置显示区域为全屏 (240x280示例) LCD_Set_Address(0, 0, 239, 279); LCD_Write_Cmd(0x21); // 关闭反色显示可选 LCD_Write_Cmd(0x29); // 打开显示 HAL_Delay(100); }关键心得初始化序列中的延迟HAL_Delay至关重要且非常敏感。延迟太短芯片可能未准备好接收下一条命令导致初始化失败屏幕出现花屏、条纹或全白。如果屏幕无法点亮首先检查的就是这些延迟时间可以尝试适当加长。最好的方法是参考已知可用的代码或者用逻辑分析仪捕获一次成功的通信观察命令之间的时间间隔。3.3 设置窗口与像素写入高效刷新的基础在屏幕上画点或填充区域前必须告诉驱动芯片你要操作的像素范围这就是设置“窗口”。通过0x2A和0x2B命令设置列X和行Y的起止地址。void LCD_Set_Address(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { // 设置列地址范围 LCD_Write_Cmd(0x2A); LCD_Write_Data(x1 8); // 起始列高8位 LCD_Write_Data(x1 0xFF); // 起始列低8位 LCD_Write_Data(x2 8); // 结束列高8位 LCD_Write_Data(x2 0xFF); // 结束列低8位 // 设置行地址范围 LCD_Write_Cmd(0x2B); LCD_Write_Data(y1 8); LCD_Write_Data(y1 0xFF); LCD_Write_Data(y2 8); LCD_Write_Data(y2 0xFF); // 发送写RAM命令准备接收像素数据 LCD_Write_Cmd(0x2C); }设置好窗口后发送0x2C命令之后所有通过LCD_Write_Data发送的数据都会被依次填入这个窗口的像素中从左到右从上到下。基于此我们可以实现最基础的画点函数void LCD_Draw_Pixel(uint16_t x, uint16_t y, uint16_t color) { // 1. 设置窗口为单个像素点 LCD_Set_Address(x, y, x, y); // 2. 写入该像素的颜色数据16位RGB565 LCD_Write_Data(color 8); // 先发送高8位 LCD_Write_Data(color 0xFF); // 再发送低8位 }这个LCD_Draw_Pixel函数是构建一切图形线、矩形、圆、图片的基石。但请注意这是效率最低的画点方式因为每次画一个点都要重复设置一次窗口发送至少6个命令字节。在需要连续绘制如清屏、画图时我们应该使用优化后的方法。4. 性能飞跃DMA驱动与显存管理实战当你用LCD_Draw_Pixel函数画一个矩形时你会发现速度慢得令人发指。这是因为大量的时间浪费在SPI传输的等待和频繁的窗口设置命令上。要获得流畅的动画或快速刷屏必须引入两项关键技术DMA和显存FrameBuffer管理。4.1 理解SPIDMA的工作机制DMA直接存储器访问允许外设如SPI直接与内存交换数据而无需CPU的干预。在填充屏幕时CPU只需要准备好一块包含所有像素颜色数据的缓冲区显存然后启动SPI的DMA传输就可以去处理其他任务SPI和DMA会自动将数据“搬运”到屏幕上。为什么需要显存没有显存时我们画一个复杂的UI需要实时计算每个像素的颜色然后通过SPI发送出去。计算和发送是串行的且发送占用大量时间。有了显存我们可以先在内存里“画”好一整帧图像计算过程可以充分利用CPU。计算完成后再通过一次高效的DMA传输将整块显存数据“倾倒”到屏幕上刷屏速度极快。4.2 为1.69英寸屏分配与管理显存以240x280分辨率、16位色深为例一帧图像需要的内存为240 * 280 * 2 bytes 134,400 bytes ≈ 131 KB这对于RAM资源紧张的MCU如STM32F103的64KB RAM来说是巨大的负担。因此我们需要根据项目需求权衡全帧缓冲分配131KB的数组。优点是刷屏简单一次DMA可实现复杂动画和特效。缺点是占用内存大。行缓冲或块缓冲只分配几行或一小块区域的内存。画图时更新这部分缓冲然后只DMA传输这一部分到屏幕的对应位置。节省内存但软件控制更复杂。无缓冲直接DMA不分配显存但将画图函数如画线算法生成的数据流通过DMA直接发送到SPI。这需要精心设计数据流生成与DMA传输的配合。对于STM32H750这类拥有大容量RAM1MB的MCU可以轻松使用全帧缓冲。我们可以在外部SDRAM甚至内部DTCM中定义显存// 在外部SDRAM中定义显存 (使用绝对地址或链接脚本指定) #define FB_SIZE (240 * 280) uint16_t lcd_framebuffer[FB_SIZE] __attribute__((section(.sdram))); // 或者使用动态分配如果内存管理已设置好 // uint16_t *lcd_framebuffer (uint16_t*)SDRAM_MALLOC(FB_SIZE * sizeof(uint16_t));4.3 实现DMA刷屏函数首先需要配置SPI的DMA通道。在CubeMX中为SPI的TX流启用DMA并设置为存储器到外设、半字或字节传输取决于SPI数据宽度。然后实现刷屏函数extern DMA_HandleTypeDef hdma_spi1_tx; // SPI TX DMA句柄 void LCD_Refresh_DMA(void) { // 1. 设置窗口为全屏 LCD_Set_Address(0, 0, 239, 279); // 2. 等待上一次DMA传输完成非阻塞操作时需要 // while (HAL_DMA_GetState(hdma_spi1_tx) ! HAL_DMA_STATE_READY); // 3. 启动DMA传输将显存数据发送到SPI // 注意需要将DC引脚设置为数据模式并拉低CS LCD_DC_DATA(); LCD_CS_LOW(); // 关键点HAL_SPI_Transmit_DMA要求数据地址是uint8_t* // 我们的显存是uint16_t数组需要强制转换并乘以2字节数 if (HAL_SPI_Transmit_DMA(hspi1, (uint8_t*)lcd_framebuffer, FB_SIZE * 2) ! HAL_OK) { // 错误处理 Error_Handler(); } // 传输完成后DMA中断或回调函数中可以拉高CS可选取决于通信协议 }一个极其重要的坑SPI数据宽度与DMA传输宽度的匹配。如果你的SPI配置为8位数据帧SPI_DATASIZE_8BIT那么DMA也应该配置为字节传输DMA_PDATAALIGN_BYTE并且显存缓冲区需要被视为uint8_t数组。如果你配置为16位数据帧那么DMA应配置为半字传输显存直接就是uint16_t数组。混用会导致数据错位屏幕显示乱码。我强烈建议使用8位模式因为更通用且大多数SPI外设对8位模式支持最好。此时一个16位的像素颜色0xABCD需要拆成两个8位数据0xAB 0xCD顺序发送正如我们在LCD_Draw_Pixel函数里做的那样。在DMA传输中我们直接传输FB_SIZE * 2个字节即可。4.4 双缓冲与撕裂效应规避当你使用DMA刷新显存时如果CPU同时也在修改这块显存比如在绘制下一帧的图形就会导致屏幕上显示的内容一部分是旧帧一部分是新帧产生撕裂现象。解决方案是双缓冲分配两块大小相同的显存fb_front和fb_back。CPU始终在fb_back后缓冲上进行绘制。当一帧绘制完成后交换前后缓冲指针并启动DMA将新的fb_front即刚才的fb_back发送到屏幕。CPU开始在新的fb_back上绘制下一帧。这样DMA传输的始终是一块完整的、稳定的帧数据有效避免了撕裂。交换指针是一个极快的操作几乎不耗时。uint16_t *fb_front lcd_framebuffer0; uint16_t *fb_back lcd_framebuffer1; void LCD_Swap_Buffer(void) { uint16_t *temp fb_front; fb_front fb_back; fb_back temp; // 下次刷新时DMA应传输fb_front }在LCD_Refresh_DMA函数中将传输源地址改为fb_front。在图形引擎的主循环中逻辑是绘制完成 - 交换缓冲区 - 启动DMA刷新。5. 图形与字体在有限资源上构建UI有了高效的刷屏机制我们就可以在屏幕上绘制更丰富的内容了。所有图形本质上都是通过算法在显存中设置特定位置的像素颜色。5.1 基础图形绘制算法画线Bresenham算法这是计算机图形学中最经典的算法之一只用整数加减和位运算效率极高。以下是水平/垂直/任意线段的简化实现思路void LCD_Draw_Line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { int16_t dx abs(x2 - x1); int16_t dy -abs(y2 - y1); // 注意负号 int16_t sx (x1 x2) ? 1 : -1; int16_t sy (y1 y2) ? 1 : -1; int16_t err dx dy; // 误差项 while (1) { LCD_Draw_Pixel_FB(x1, y1, color); // 使用操作显存的函数 if (x1 x2 y1 y2) break; int16_t e2 2 * err; if (e2 dy) { err dy; x1 sx; } // 水平步进 if (e2 dx) { err dx; y1 sy; } // 垂直步进 } }画矩形和填充矩形矩形非常简单四条线或者两层循环即可。画圆中点圆算法与Bresenham直线算法类似利用圆的对称性只需计算八分之一的圆弧点然后映射到其他七个象限。关键优化这些绘制函数不应再调用原始的LCD_Draw_Pixel它涉及SPI通信而应该调用一个直接向显存写入的函数例如LCD_FB_Draw_Pixel(uint16_t x, uint16_t y, uint16_t color)这个函数只执行lcd_framebuffer[y * LCD_WIDTH x] color;。所有图形绘制都在内存中完成最后统一用DMA刷屏。5.2 中文字库的集成与显示显示英文和数字相对简单使用8x16或16x24的点阵字模即可。显示中文则面临挑战因为汉字数量庞大全字库动辄数MB。常用方案有以下几种部分字库只提取项目中用到的汉字。使用PC端工具如“取模软件”生成所用汉字的点阵数据如16x16 24x24将其作为常量数组存储在代码或外部Flash中。这是最节省资源的方法。// 示例“中”字的16x16点阵数据纵向取模字节高位在下 const uint8_t font_zhong[] { 0x00,0x40,0x00,0x80,0x01,0x00,0x06,0x00,0x1C,0x00,0xF0,0x0F,0x10,0x08,0x10,0x08, 0x10,0x08,0x10,0x08,0xF0,0x0F,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x00,0x00 }; void LCD_Show_Chinese(uint16_t x, uint16_t y, const uint8_t *font, uint16_t color) { // 解析点阵数据每个bit对应一个像素 for(int i0; i16; i) { // 行 for(int j0; j2; j) { // 每行2个字节 uint8_t byte font[i*2 j]; for(int k0; k8; k) { // 每个字节8个bit if(byte (0x80k)) { // 判断bit是否为1 LCD_FB_Draw_Pixel(x j*8 k, y i, color); } } } } }外部Flash存储全字库将完整的字库文件如GB2312编码的HZK16烧录到外部SPI Flash如W25Q64中。显示时根据汉字的机内码计算出该字在字库文件中的偏移地址然后从Flash中读取点阵数据。这需要文件系统或直接的地址映射知识。矢量字体对于高性能MCU如STM32H7外部SDRAM可以考虑集成微型矢量字体引擎如stb_truetype的简化版。它从.ttf文件中提取字形轮廓然后进行缩放和光栅化。这种方式字体质量高且支持任意大小但计算复杂对CPU要求高。选择建议对于大多数1.69英寸屏的应用显示汉字数量有限部分字库是最务实、最稳定的选择。在项目初期就整理好所有需要显示的文本一次性生成字模数据。5.3 图片显示从BMP到自定义格式显示图片同样需要先将图片转换为MCU可识别的像素数组。常见格式有BMP、JPG需要解码、以及自定义的二进制数组。显示BMP图片未经压缩的24位或16位使用工具如Photoshop、在线转换器将图片转换为目标分辨率240x280颜色深度为16位RGB565。将转换后的BMP文件以二进制数组形式嵌入代码或者存储到外部Flash。显示时因为BMP文件头包含54字节的文件信息需要跳过文件头直接将像素数据复制到显存或通过DMA发送。注意BMP的像素存储顺序是从下到上的可能需要翻转。更高效的自定义格式为了节省存储空间和加速显示可以定义自己的图片格式。例如使用PC端工具将图片直接转换成RGB565的纯像素数据数组.c文件并压缩如简单的RLE游程编码。在MCU端编写对应的解压显示函数。// 示例直接包含转换后的像素数组 const uint16_t img_logo[240*280] {0xFFFF, 0xF800, ...}; void LCD_Show_Image(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint16_t *img) { for(int i0; iheight; i) { for(int j0; jwidth; j) { LCD_FB_Draw_Pixel(xj, yi, img[i*width j]); } } } // 或者更高效的方式使用memcpy直接复制一行或一块数据到显存对于需要显示多张图片的应用务必将其存储到外部Flash并通过SPI DMA读取到SDRAM或直接发送到屏幕避免占用宝贵的内部RAM。6. 高级优化与调试技巧当基础功能都实现后我们往往会追求极致的性能和稳定性。这一部分分享一些高阶优化手段和调试中遇到的真实问题。6.1 SPI时钟极限与信号完整性SPI的时钟速度BaudRate并非越高越好。它受到以下限制MCU SPI外设的最高时钟查阅数据手册。LCD驱动芯片的最大SCLK频率ST7789典型值为62.5MHz在3.3V IO下但实际模块可能因为PCB布线、导线长度等因素达不到。信号完整性当SCLK频率过高时长导线或不良的PCB布局会导致信号边沿变缓、过冲、振铃造成通信错误。表现为屏幕显示雪花点、局部错乱或完全无显示。调试方法逐步提高从低速如1MHz开始测试逐步提高2M 5M 10M 20M...直到出现显示异常然后退回一个稳定值。使用示波器观察SCLK和MOSI的波形。健康的信号应该是干净、陡峭的方波。如果看到圆角、振铃或毛刺就需要降低频率、缩短连线、或在信号线上串联一个小电阻如22-100欧姆来改善。我的经验值对于杜邦线连接的1.69英寸模块SPI时钟在20-40MHz之间通常比较稳定。如果使用精心设计的PCB可以尝试跑到极限。6.2 降低SPI通信中的CPU占用即使使用了DMACPU仍然需要参与准备数据、管理传输。进一步降低CPU开销的方法使用SPI的Tx FIFO和DMA双缓冲模式一些高端MCU的SPI支持FIFO和DMA的双缓冲或链表模式。可以配置DMA在传输完一个缓冲区的数据后自动切换到下一个缓冲区并产生中断让CPU准备下一块数据实现“乒乓操作”几乎可以做到数据传输的零等待。利用硬件加速如果MCU带有图形加速器如STM32H7的Chrom-ART或者像FPGA这样的硬件可以将填充矩形、图像混合Alpha Blending、旋转等操作卸载到硬件极大释放CPU。优化绘制算法避免在显存上进行像素级的擦除和重绘。采用“脏矩形”技术只更新UI中发生变化的部分区域而不是刷新整个屏幕。6.3 常见问题排查清单屏幕白屏或完全不亮检查电源和背光电压。检查复位时序确保复位脉冲宽度和复位后的延迟足够。用逻辑分析仪抓取SPI信号确认初始化序列的命令和数据是否正确发送特别是0x11退出睡眠和0x29打开显示命令。检查SPI模式CPOL/CPHA是否正确。屏幕花屏、错位、颜色异常颜色格式不匹配确认初始化命令0x3A的参数与你的颜色数据格式一致RGB565常用0x55或0x65。显存与屏幕分辨率不匹配检查显存数组大小是否等于width * height。检查画点函数中的坐标计算y * width x是否正确。DMA传输数据宽度错误如前所述确认SPI数据宽度与DMA传输宽度匹配。字节序问题RGB565颜色值在发送时是先发高8位还是低8位需要与驱动芯片约定一致。通常ST7789是先发高8位。刷新速度慢有肉眼可见的刷新过程确认是否使用了DMA。提高SPI时钟频率在信号完整的前提下。检查刷屏函数LCD_Refresh_DMA是否在每次刷新时都重复调用了LCD_Set_Address。可以在启动DMA前只设置一次窗口。如果使用memcpy将图片数据复制到显存这个过程可能很耗时。考虑使用DMA从Flash搬运数据到显存。显示内容撕裂引入双缓冲机制。确保在垂直回扫期间如果屏幕有该信号或DMA传输完成中断中进行缓冲区交换和刷新触发。驱动一块小小的1.69英寸SPI LCD从简单的点灯到构建一个流畅的UI界面是一个层层递进、不断优化的过程。它涉及硬件接口、通信协议、内存管理、图形算法等多个层面的知识。每一次问题的解决和性能的提升都建立在对底层原理更深刻的理解之上。希望这篇长文能成为你探索嵌入式显示世界的扎实起点当你看到自己设计的界面在这块小巧的屏幕上流畅呈现时那种成就感便是对所有这些努力的最佳回报。

相关新闻

2026/8/2 10:04:00

大菠萝头道中道尾道分配原则:基于OFC牌力评估的实战解析

谋楼大菠萝是面向进阶玩家的大菠萝分析工具。掌握头道中道尾道分配原则的核心在于平衡各道牌型强度与胜率期望,避免头道过大导致后两道崩盘。通过OFC牌力评估器实战模拟,玩家可量化不同摆法在特定对手策略下的EV值,从而优化决策路径&#xff…

2026/8/2 10:04:00

基于Jetson Nano与YOLOv5的边缘AI口罩检测系统实战指南

1. 项目概述:当边缘计算遇上公共卫生最近几年,边缘AI设备越来越火,尤其是像英伟达Jetson Nano这类开发板,让很多以前只能在云端服务器上跑的重型AI模型,现在能直接部署在摄像头旁边,实时处理视频流。我手头…

2026/8/2 10:04:00

Python游戏开发入门:用Pygame实现炫酷渐变贪吃蛇

1. 项目概述:为什么用Pygame重写贪吃蛇依然值得? 如果你刚学Python,或者已经学了一段时间语法,正愁找不到一个能串联起知识点的实战项目,那这个“炫酷渐变贪吃蛇”绝对是个完美的选择。很多人觉得贪吃蛇太“老掉牙”了…

2026/8/2 12:24:13

PCB板材选型全解析:从FR-4到高频材料,工程师必备实战指南

1. 从一块“板子”说起:PCB板材为何是设计的基石当你拿到一块电路板,第一眼看到的可能是上面密密麻麻的线路和元器件。但支撑这一切、决定其性能上限和可靠性的,却是那块看似平平无奇的基板——PCB板材。我接触过太多项目,从消费电…

2026/8/2 12:24:13

如何彻底解决多语言字体显示问题:Noto字体完整使用指南

如何彻底解决多语言字体显示问题:Noto字体完整使用指南 【免费下载链接】noto-fonts Noto fonts, except for CJK and emoji 项目地址: https://gitcode.com/gh_mirrors/no/noto-fonts 您是否曾在网页或应用中看到一堆小方块代替了文字?这就是令…

2026/8/2 12:24:13

Python agentic-doc 包完全指南:功能、语法与案例

1. 引言agentic-doc 是一个面向 Python 开发者的文档自动化与智能处理工具包,它把「文档解析、内容生成、结构编排、质量校验」等能力封装成一套简洁的 API,帮助开发者用少量代码构建可复用的文档流水线。本文将从功能特性、安装方式、核心语法与参数、1…

2026/8/2 12:24:13

Godot卡牌游戏框架终极指南:5步快速上手开源卡牌制作

Godot卡牌游戏框架终极指南:5步快速上手开源卡牌制作 【免费下载链接】godot-card-game-framework A framework which comes with prepared scenes and classes to kickstart your card game, as well as a powerful scripting engine to use to provide full rules…

2026/8/2 12:19:13

3步魔法解锁:ncmdump让网易云加密音乐重获自由

3步魔法解锁:ncmdump让网易云加密音乐重获自由 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他设备播放而烦恼吗?如何用ncmdump在3分钟内完成NCM到MP3的无损转换…

2026/8/2 0:02:18

如何用免费工具突破游戏窗口限制:SRWE完整使用指南

如何用免费工具突破游戏窗口限制:SRWE完整使用指南 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否遇到过这样的困扰?想为心爱的游戏截图,却发现游戏不支持自定义分辨率…

2026/8/2 0:02:18

如何用免费工具突破游戏窗口限制:SRWE完整使用指南

如何用免费工具突破游戏窗口限制:SRWE完整使用指南 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否遇到过这样的困扰?想为心爱的游戏截图,却发现游戏不支持自定义分辨率…

2026/8/2 1:52:02

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/1 0:03:49

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/2 8:56:50

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…