手把手教你用STM32标准库的SPI DMA,给1.3寸ST7789屏做一次“性能手术”

张开发
2026/4/20 18:57:27 15 分钟阅读

分享文章

手把手教你用STM32标准库的SPI DMA,给1.3寸ST7789屏做一次“性能手术”
手把手教你用STM32标准库的SPI DMA给1.3寸ST7789屏做一次“性能手术”当你的嵌入式系统需要实时显示动态波形或流畅动画时1.3寸ST7789屏幕的刷新率可能成为瓶颈。传统SPI驱动方式就像让CPU亲自搬运每一块砖头而DMA技术则是请来一支专业的施工队——本文将带你完成这场从徒手劳动到机械化施工的技术升级。1. 术前诊断传统SPI驱动的性能瓶颈在STM32标准库环境下用SPI直接驱动ST7789液晶屏时开发者常会遇到这样的场景即便将SPI时钟设置为最高72MHz全屏刷新率仍难以突破5帧/秒。通过逻辑分析仪捕捉到的波形显示CPU大部分时间都在等待SPI传输完成。典型阻塞式SPI代码的症结在于while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); // 等待发送缓冲区空 SPI_I2S_SendData(SPI1, TxData); // 写入数据 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); // 等待接收完成这种轮询方式导致两个关键问题CPU利用率过高实测显示填充320x240全屏时CPU占用率超过90%帧间隔不稳定由于其他中断可能插入帧率波动明显提示通过测量GPIO翻转频率可以发现传统方式下CPU只能额外处理不到10%的其他任务2. 解剖DMA内存到外设的直达通道DMA直接内存访问控制器如同一个智能快递系统其工作流程可分为三个关键阶段阶段操作硬件行为初始化配置源地址、目标地址、传输量DMA控制器建立传输通道触发SPI发起传输请求DMA将数据从内存搬运到SPI数据寄存器完成传输计数器归零产生中断标志可触发回调函数STM32F103的DMA1通道与SPI1的对应关系SPI1_TX→ DMA1通道3SPI1_RX→ DMA1通道2配置代码的核心参数解析DMA_InitStructure.DMA_PeripheralBaseAddr (u32)SPI1-DR; // SPI数据寄存器地址 DMA_InitStructure.DMA_MemoryBaseAddr (u32)SendBuff; // 内存缓冲区地址 DMA_InitStructure.DMA_BufferSize 480; // 每次传输480字节 DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; // 内存到外设3. 手术实施DMA接入SPI驱动框架3.1 硬件连接检查清单CLK→ PA5 (SPI1_SCK)MOSI→ PA7 (SPI1_MOSI)DC→ PB11 (GPIO控制数据/命令)CS→ 接地硬件片选3.2 关键改造步骤内存缓冲区准备uint8_t frameBuffer[320*240*2]; // 16位色深缓冲区DMA初始化增强版void DMA_Config() { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)SPI1-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)frameBuffer; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize sizeof(frameBuffer); DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel3, DMA_InitStructure); }SPIDMA协同工作void ST7789_Refresh() { SPI_Cmd(SPI1, DISABLE); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); DMA_Cmd(DMA1_Channel3, ENABLE); while(!DMA_GetFlagStatus(DMA1_FLAG_TC3)); DMA_ClearFlag(DMA1_FLAG_TC3); }4. 术后护理性能优化与异常处理4.1 帧率对比测试驱动方式320x240全屏刷新率CPU占用率纯SPI3.2 fps92%SPIDMA18.7 fps15%4.2 常见并发症处理数据撕裂启用双缓冲机制uint8_t frameBuffer[2][SCREEN_BUFFER_SIZE]; volatile uint8_t activeBuffer 0;DMA传输不完整检查DMA中断标志清除时序if(DMA_GetITStatus(DMA1_IT_TC3)) { DMA_ClearITPendingBit(DMA1_IT_GL3); // 处理下一帧 }SPI时钟配置确保不超过显示屏最大速率通常15-30MHz在最终实现的Demo中通过GPIO引脚测量显示DMA传输期间CPU可完全处理其他任务。一个实用的技巧是将屏幕刷新同步到VSYNC信号可以避免画面撕裂现象。实际项目中这种方案已成功应用在需要实时显示ECG波形的医疗设备上。

更多文章