STM32标准库SPI驱动ST7789屏太慢?试试DMA搬运,帧率飙升5倍(附完整代码)

张开发
2026/4/17 11:59:57 15 分钟阅读

分享文章

STM32标准库SPI驱动ST7789屏太慢?试试DMA搬运,帧率飙升5倍(附完整代码)
STM32标准库SPI驱动ST7789屏性能优化实战DMA搬运实现帧率5倍提升如果你正在使用STM32标准库的SPI接口驱动ST7789屏幕可能会遇到刷新速度慢的问题。传统轮询方式发送数据会占用大量CPU资源导致帧率低下。本文将带你深入分析性能瓶颈并通过DMA技术实现帧率的大幅提升。1. 标准SPI轮询模式的性能瓶颈分析在嵌入式显示应用中ST7789作为一款常见的TFT液晶控制器通常通过SPI接口与主控芯片通信。使用STM32标准库的传统SPI轮询方式时开发者经常会遇到以下典型问题全屏刷新速度仅能达到3-5帧/秒CPU利用率接近100%无法执行其他任务SPI时钟速率已经调到最高但性能仍不理想造成这些问题的核心原因在于轮询传输机制。以240x320分辨率的屏幕为例每次全屏刷新需要发送153,600字节数据320x240x2。标准库的SPI1_ReadWriteByte函数内部包含两个等待循环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参与产生了大量无效等待。实测数据表明在72MHz系统时钟下使用SPI时钟分频为2即36MHz时操作类型耗时(μs)帧率(fps)单字节轮询发送~1.2~3.5整屏刷新总耗时~285,000-2. DMA传输原理与STM32实现机制DMADirect Memory Access是一种不经过CPU直接在外设和内存间传输数据的技术。在STM32中DMA控制器具有以下关键特性独立于CPU工作的专用总线支持内存到外设、外设到内存的双向传输可配置的数据宽度8/16/32位多种传输模式单次、循环等对于SPI1的TX传输STM32F103系列对应的是DMA1通道3。配置DMA需要关注以下几个核心参数DMA_InitStructure.DMA_PeripheralBaseAddr (u32)SPI1-DR; //SPI数据寄存器地址 DMA_InitStructure.DMA_MemoryBaseAddr (u32)SendBuff; //内存缓冲区地址 DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; //传输方向 DMA_InitStructure.DMA_BufferSize 480; //传输数据量 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_Medium; //优先级 DMA_InitStructure.DMA_M2M DMA_M2M_Disable; //禁用内存到内存注意DMA传输期间CPU可以执行其他任务但需要确保源数据缓冲区在传输完成前不被修改。3. 代码改造从轮询到DMA的完整实现3.1 DMA初始化与配置首先需要在屏幕初始化函数中添加DMA配置。以下代码展示了DMA通道的初始化过程void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx, u32 cpar, u32 cmar, u16 cndtr) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA_CHx); DMA_InitStructure.DMA_PeripheralBaseAddr cpar; DMA_InitStructure.DMA_MemoryBaseAddr cmar; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize cndtr; 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_PeripheralDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; DMA_InitStructure.DMA_Priority DMA_Priority_Medium; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA_CHx, DMA_InitStructure); }在initlcd()函数中调用配置MYDMA_Config(DMA1_Channel3, (u32)SPI1-DR, (u32)SendBuff, 480);3.2 改造fillScreen函数实现DMA传输原轮询方式的fillScreen需要逐像素发送数据改造后使用DMA批量传输void fillScreen(u16 color) { u16 i,j; writeCommand(0x2A); //列地址设置 writeData(0); writeData(0); writeData(0); writeData(240); writeCommand(0x2B); //行地址设置 writeData(0); writeData(0); writeData(0x01); writeData(0x40); writeCommand(0x2C); //内存写入 DC 1; //准备发送缓冲区 for(j0; j480; j2) { SendBuff[j] color8; SendBuff[j1] color; } //DMA传输所有行 for(i0; i320; i) { SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); MYDMA_Enable(DMA1_Channel3); while(DMA_GetFlagStatus(DMA1_FLAG_TC3)RESET); //等待传输完成 DMA_ClearFlag(DMA1_FLAG_TC3); //清除标志位 } }关键改进点预先填充发送缓冲区减少循环内操作使用DMA连续发送240像素480字节数据通过标志位确保每次DMA传输完成4. 性能对比与优化技巧4.1 实测性能数据在STM32F103C8T672MHz平台上测试不同方案的性能表现方案帧率(fps)CPU占用率SPI时钟(MHz)轮询SPI3.5~100%18DMA传输18.75%18DMASPI超频22.45%36提示实际帧率还受屏幕刷新时序限制ST7789最大支持约30fps的刷新率。4.2 常见问题与解决方案DMA传输不完整检查DMA缓冲区地址是否有效确认DMA通道与SPI外设匹配验证传输计数器设置是否正确屏幕显示错乱确保在DMA传输前正确设置DC引脚检查SPI相位和极性配置验证颜色数据格式RGB565或其它性能未达预期尝试提高SPI时钟频率增大DMA单次传输数据量优化内存缓冲区访问对齐到4字节边界4.3 进阶优化方向对于追求极致性能的开发者还可以考虑以下优化手段使用双缓冲技术减少等待时间采用SPI的16位数据模式需修改ST7789配置利用STM32的硬件NSS信号管理片选在DMA传输间隙插入其他任务处理通过本文介绍的DMA优化方法开发者可以轻松实现ST7789屏幕刷新性能的显著提升同时大幅降低CPU负载为嵌入式GUI应用提供更流畅的显示体验。

更多文章