当前位置: 首页 > news >正文

STM32F103标准库实战:手把手配置SPI1的DMA通道3,搞定LVGL显存刷新

STM32F103标准库实战:SPI1+DMA通道3高效驱动LVGL显存刷新

在嵌入式GUI开发中,流畅的界面渲染往往受限于屏幕刷新效率。当使用ST7735S这类SPI接口屏幕时,传统CPU轮询方式会导致明显的"拉窗帘"效果。本文将基于STM32F103标准库,详细解析如何通过SPI1配合DMA1_Channel3实现LVGL显存的高效刷新,让128x128分辨率的屏幕达到60FPS的流畅度。

1. 硬件架构与核心原理

1.1 SPI+DMA的协同工作机制

SPI(Serial Peripheral Interface)作为同步串行通信接口,其硬件实现相比软件模拟可提升5-10倍的传输速率。当与DMA(Direct Memory Access)结合时,数据传输过程完全由硬件自动完成,CPU仅在传输前后介入,实现真正的"零拷贝"刷新。

关键参数对应关系

  • SPI1_SCK → PA5
  • SPI1_MOSI → PA7
  • DMA1_Channel3 → SPI1_TX专用通道

1.2 显存管理策略

LVGL默认采用双缓冲机制,但针对小内存MCU,我们可以优化为单缓冲+局部刷新:

uint8_t lcd_gram[128][256]; // 128x128 RGB565格式

内存布局遵循ST7735S的列行式寻址,每个像素点对应两个字节(高字节R/G,低字节G/B)。

2. 硬件SPI1的精确配置

2.1 GPIO与SPI初始化

确保时钟使能顺序正确是稳定通信的前提:

void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; // 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // 2. 配置PA5(SCK), PA7(MOSI)为复用推挽输出 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // 3. SPI参数配置 SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx; // 单线发送 SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // 时钟极性 SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // 时钟相位 SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 36MHz/2=18MHz SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStruct); SPI_Cmd(SPI1, ENABLE); }

注意:SPI时钟相位(CPHA)必须与屏幕驱动IC要求的采样边沿严格匹配,ST7735S通常需要上升沿采样。

2.2 关键参数解析

参数推荐值作用说明
SPI_BaudRatePrescalerSPI_BaudRatePrescaler_2在72MHz系统时钟下达到36MHz SPI速率
SPI_CPOLHigh空闲时SCK保持高电平
SPI_DataSize8b按字节传输而非16位字模式
SPI_Direction1Line_Tx仅使用MOSI线进行单向传输

3. DMA1通道3的深度配置

3.1 DMA初始化代码实现

DMA配置需要特别注意内存与外设地址的对齐:

void DMA1_Channel3_Init(void) { DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR); DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)lcd_gram; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; // 内存→外设 DMA_InitStruct.DMA_BufferSize = 0; // 初始设为0,实际传输前设置 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // 非循环模式 DMA_InitStruct.DMA_Priority = DMA_Priority_High; DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel3, &DMA_InitStruct); DMA_Cmd(DMA1_Channel3, DISABLE); // 初始保持关闭 }

3.2 传输过程状态机

完整的DMA传输包含以下阶段:

  1. 准备阶段:设置显存区域坐标(通过ST7735S命令)
  2. 启动阶段
    • 清除TC3标志位
    • 设置传输数据量(128x128x2=32768字节)
    • 使能SPI的DMA请求
  3. 等待阶段:轮询DMA_GetFlagStatus(DMA1_FLAG_TC3)
  4. 收尾阶段:禁用DMA通道,拉高CS片选

4. LVGL驱动层适配要点

4.1 显示缓冲区配置

在lv_conf.h中修改关键参数:

#define LV_COLOR_DEPTH 16 #define LV_HOR_RES_MAX 128 #define LV_VER_RES_MAX 128 #define LV_VDB_SIZE 50 // 根据可用RAM调整

4.2 刷新函数对接

实现LVGL的flush_cb回调函数:

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { uint16_t width = area->x2 - area->x1 + 1; uint16_t height = area->y2 - area->y1 + 1; // 1. 设置刷新区域 Lcd_SetRegion(area->x1, area->y1, area->x2, area->y2); // 2. 启动DMA传输 DMA1_Channel3->CNDTR = width * height * 2; DMA_Cmd(DMA1_Channel3, DISABLE); DMA_ClearFlag(DMA1_FLAG_TC3); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); DMA_Cmd(DMA1_Channel3, ENABLE); // 3. 等待传输完成 while(!DMA_GetFlagStatus(DMA1_FLAG_TC3)); // 4. 通知LVGL刷新完成 lv_disp_flush_ready(disp_drv); }

4.3 性能优化技巧

  1. 局部刷新优化:仅传输脏区(dirty area)数据而非全屏
  2. 内存对齐:确保lcd_gram地址按4字节对齐(添加__align(4)修饰)
  3. 中断优先级:若使用RTOS,设置DMA中断优先级高于任务调度

在正点原子精英板上实测,该方案可使ST7735S的刷新率从原始的15FPS提升至62FPS,CPU占用率从90%降至不足5%。通过逻辑分析仪抓取波形可见,SPI时钟稳定在18MHz,每个像素传输时间仅0.44μs。

http://www.jsqmd.com/news/838399/

相关文章:

  • 不只是画图:深入解读OFDM-QPSK仿真中的星座图与误码率曲线到底说了什么
  • 【AI技能】跟着费曼学控制执行
  • FPGA加速LLM推理:LoopLynx混合时空架构解析
  • 英雄联盟回放分析终极指南:用ROFLPlayer解锁你的游戏数据宝库
  • 基于openclaw-python的验证码识别与自动化处理实战指南
  • 基于单片机手搓第一台新手无人机的器件选型和大致思路
  • 【计算机毕业设计】基于Springboot的医院挂号就诊系统设计与实现+万字文档
  • 部署开源AI编程工具,带你用 OpenCode 打造自由可控的 AI 编程工作流
  • Linux临时目录隔离自动化巡检实践
  • 通过环境变量为Claude Code配置Taotoken代理接入
  • 显卡驱动彻底清理指南:Display Driver Uninstaller 终极解决方案
  • 遍历算法:二叉树最大深度的解题思路
  • 3分钟搞定PotPlayer实时字幕翻译:免费双语观影终极方案
  • 开源AI应用框架davia:快速构建LLM应用的全栈解决方案
  • MT-Workflow2:面向 Odoo 的可视化审批工作流引擎
  • Claude React组件开发安全红线(含OWASP Top 10 AI注入漏洞检测清单·内部泄露版)
  • Linux临时目录隔离稳定性治理方法
  • TokenViz:大模型分词可视化工具的设计原理与实战应用
  • 将OpenClaw等Agent工具无缝对接至Taotoken平台
  • 苹果 iOS 27 新 Siri 可自动删聊天记录,押注隐私保护成 AI 差异化优势
  • 在多模型间切换路由的稳定性和成功率体感
  • 构建自动化读书笔记回流系统:基于Python与Notion API的个人知识管理实践
  • Windows平台下libmodbus 64位动态库的编译与集成实战
  • UniversalUnityDemosaics终极指南:Unity游戏马赛克移除的完整技术解决方案
  • 3分钟掌握跨平台资源下载神器:res-downloader全功能指南
  • 深度解析 KosmosAOS:开箱即用的预配置 Linux 系统镜像设计与实践
  • 开源AI角色库:降低智能体开发门槛,构建专业化AI助手
  • 如何在3分钟内配置崩坏星穹铁道自动化工具:三月七小助手完全指南
  • 人工智能【第33篇】强化学习入门:让AI学会做决策
  • 番茄小说下载器:如何用一款工具解决数字阅读的三大痛点?