news 2026/4/29 15:04:45

STM32F103标准库SPI+DMA驱动ST7789屏幕,实测帧率从3FPS提升到10+FPS的完整配置流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103标准库SPI+DMA驱动ST7789屏幕,实测帧率从3FPS提升到10+FPS的完整配置流程

STM32F103标准库SPI+DMA驱动ST7789屏幕:从3FPS到10+FPS的性能飞跃实战

去年接手一个工业HMI项目时,遇到个棘手问题——STM32驱动的1.54寸ST7789屏幕刷新率始终卡在3帧/秒。当需要快速更新数据仪表时,肉眼可见的拖影让用户体验大打折扣。经过两周的调试与优化,最终通过SPI+DMA方案将帧率稳定提升到12FPS以上。本文将分享完整配置流程,包括那些容易踩坑的细节。

1. 性能瓶颈诊断与方案选型

1.1 传统SPI轮询模式的问题根源

用逻辑分析仪抓取原始代码的SPI波形时,发现了三个关键问题点:

  1. CPU等待浪费:每发送一个字节都需要轮询SPI_I2S_FLAG_TXE标志位,在72MHz主频下实测有约1.2μs的空转等待
  2. 数据搬运开销fillScreen函数中双重循环每次都要处理颜色分解,占用了23%的CPU时间
  3. 总线利用率低:SPI时钟分频设为2时(36MHz),实际有效数据传输率不足40%
// 典型阻塞式发送函数 u8 SPI1_ReadWriteByte(u8 TxData) { 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); // 等待接收完成 return SPI_I2S_ReceiveData(SPI1); }

1.2 DMA传输的优势对比

通过DMA控制器搬运数据,能实现:

特性轮询模式DMA模式
CPU占用率85%-100%<5%
最大理论吞吐量4.5MB/s8.2MB/s
帧率提升空间3-5FPS10-15FPS
多任务支持不可行可并行处理

实测数据基于STM32F103C8T6@72MHz,SPI时钟36MHz,240x320分辨率全屏刷新

2. 硬件设计关键要点

2.1 最小系统连接方案

ST7789与STM32的硬件连接需要特别注意信号完整性:

ST7789 STM32F103 备注 ----------------------------------------- DC PB11 数据/命令选择 CLK PA5 SPI1_SCK MOSI PA7 SPI1_MOSI CS GND 硬件接地使能 RESET 10k上拉 可选硬件复位 BLK PWM控制 背光调节

布线建议

  • 时钟线长度不超过15cm
  • 在MOSI线上串联22Ω电阻抑制振铃
  • 避免与高频信号线平行走线

2.2 电源设计陷阱

遇到屏幕闪烁问题时,检查以下电源参数:

  1. 3.3V电源波纹需<50mV
  2. 背光电路单独供电(典型100mA以上)
  3. 在VCC与GND间添加10μF+0.1μF去耦电容

3. 软件配置全流程

3.1 SPI初始化优化配置

void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE); // GPIO配置为复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK,MOSI GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // SPI主模式配置 SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; // 单线发送 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 极性相位与ST7789匹配 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 36MHz SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }

关键参数说明:

  • SPI_Direction_1Line_Tx:节省MISO线资源
  • SPI_BaudRatePrescaler_2:在72MHz PCLK下达到最大36MHz时钟
  • CPOL/CPHA必须与屏幕规格书一致

3.2 DMA通道配置详解

STM32F103的DMA1通道3对应SPI1_TX:

#define BUFFER_SIZE 480 // 双行缓冲区 u8 sendBuffer[BUFFER_SIZE]; u16 dma_transfer_count; void DMA1_Channel3_Config(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)sendBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; 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); DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE); // 开启传输完成中断 dma_transfer_count = BUFFER_SIZE; }

常见配置错误

  1. 未使能AHB时钟(RCC_AHBPeriphClockCmd
  2. 外设地址未加(u32)强制转换
  3. 缓冲区大小与实际数据不匹配

3.3 屏幕驱动关键函数重写

优化后的全屏填充函数:

void ST7789_FillColor_DMA(u16 color) { // 设置行列地址 ST7789_SetWindow(0, 0, 239, 319); // 准备颜色缓冲区 for(int i=0; i<BUFFER_SIZE; i+=2) { sendBuffer[i] = color >> 8; // 高字节 sendBuffer[i+1] = color & 0xFF; // 低字节 } // 启动DMA传输 DMA_SetCurrDataCounter(DMA1_Channel3, BUFFER_SIZE); DMA_Cmd(DMA1_Channel3, ENABLE); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // 等待传输完成 while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET); DMA_ClearFlag(DMA1_FLAG_TC3); }

性能提升技巧:

  • 使用双行缓冲区减少内存操作
  • 提前分解颜色值避免实时计算
  • 合理设置传输计数器

4. 调试技巧与性能优化

4.1 帧率测量方法

三种实用的性能评估方式:

  1. GPIO翻转法:在刷屏前后切换IO,用示波器测量脉冲间隔

    GPIO_SetBits(GPIOB, GPIO_Pin_12); ST7789_FillColor_DMA(0xF800); GPIO_ResetBits(GPIOB, GPIO_Pin_12);
  2. 定时器计数:利用SysTick统计每秒调用次数

    volatile u32 fps_counter = 0; void SysTick_Handler(void) { static u32 last_count = 0; fps = fps_counter - last_count; last_count = fps_counter; }
  3. 逻辑分析仪:抓取CS/DC信号分析时序

4.2 高级优化策略

内存布局优化

MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 6K } SECTIONS { .dma_buffer (NOLOAD) : { *(.dma_buffer) } >CCMRAM }

DMA中断优化

void DMA1_Channel3_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC3)) { // 处理下一块数据传输 DMA_ClearITPendingBit(DMA1_IT_GL3); } }

实测优化效果对比:

优化措施帧率提升CPU占用降低
基础DMA传输3→8 FPS85%→15%
双缓冲机制8→10 FPS15%→8%
内存对齐访问10→12 FPS8%→5%
CCMRAM加速12→14 FPS5%→3%

4.3 典型问题排查指南

现象1:屏幕显示错乱

  • 检查SPI相位/极性配置
  • 验证DC信号时序
  • 测量电源稳定性

现象2:DMA传输不启动

  1. 确认DMA时钟使能
  2. 检查通道映射是否正确
  3. 验证缓冲区地址是否有效

现象3:帧率不达预期

# 计算理论最大帧率 spi_clock = 36e6 # 36MHz pixels = 240*320 bytes_per_pixel = 2 overhead = 1.2 # 命令开销系数 max_fps = spi_clock / (pixels * bytes_per_pixel * 8 * overhead) print(f"理论最大帧率: {max_fps:.1f}FPS")

在最近的一个智能家居面板项目中,这套优化方案使得UI动画流畅度从卡顿的3FPS提升到稳定的14FPS。特别是在多级菜单切换时,DMA传输让CPU有足够资源同时处理触摸输入和网络通信。有个值得分享的细节:将DMA缓冲区对齐到4字节边界后,帧率又提升了约8%。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 15:04:41

BiliTools:你的跨平台哔哩哔哩下载终极指南

BiliTools&#xff1a;你的跨平台哔哩哔哩下载终极指南 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools 还在为无法离…

作者头像 李华
网站建设 2026/4/29 15:03:24

专业自动化OpenCore EFI配置:OpCore-Simplify深度实战指南

专业自动化OpenCore EFI配置&#xff1a;OpCore-Simplify深度实战指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpCore-Simplify是一款专为黑苹…

作者头像 李华