STM32F4 使用 SDRAM 作为显存时 MPU 配置不当导致的性能骤降与花屏排查
👁 2 阅读 · 2026-08-27 · 嵌入式
在 STM32F4 系列驱动 LCD 时,将 SDRAM 作为显存可大幅提升帧率,但若 MPU(内存保护单元)配置不当,轻则性能骤降,重则花屏闪烁。本文从 MPU 原理出发,剖析 SDRAM 区域缓存策略对读写性能的影响,给出标准配置步骤与完整代码示例,并总结常见坑点,助你快速定位问题。
# 引言
在嵌入式 GUI 开发中,STM32F4 搭配外部 SDRAM 作为显存是常见方案。然而,很多开发者发现代码逻辑正确,但 LCD 刷新率极低或出现随机花屏。问题往往不在 SDRAM 时序,而在于 MPU 配置。MPU 控制着 CPU 对内存区域的缓存与写回策略,不当配置会让 SDRAM 访问效率大打折扣,甚至导致数据不一致。
# MPU 与 SDRAM 的关系
## 为什么 MPU 影响性能?
STM32F4 的 Cortex-M4 内核内置 MPU,可对内存区域定义访问权限和缓存属性。SDRAM 位于外部存储器映射区(0xC0000000 起),默认情况下 MPU 未启用,此时 CPU 对 SDRAM 的访问是“无缓存”的,即每次读写都直接访问外部总线。LCD 控制器(如 LTDC)通过 DMA 读取显存时,若 CPU 同时写入,无缓存会导致频繁总线竞争,性能下降。
更严重的是,若 MPU 配置为“写回(Write Back)”模式,CPU 写入的数据会暂存在 Cache 中,而 LTDC 直接读取 SDRAM,可能读到旧数据,造成花屏。
## 关键概念
- **Cache 策略**:Write-through(直写)与 Write-back(回写)。直写每次写操作同步到内存,性能略低但数据一致;回写先写 Cache,延迟写回,性能高但需维护一致性。
- **Bufferable**:允许写操作缓冲,CPU 不等总线完成,提升写速度。
- **TEX 字段**:决定缓存类型,如 0b001 表示 Write-through,0b000 表示 Non-cacheable。
# 配置步骤
## 1. 确定 SDRAM 地址范围
假设 SDRAM 挂在 FMC 的 Bank1,起始地址 0xC0000000,大小 8MB(0x800000)。显存区域通常位于 SDRAM 的某段,如 0xC0000000 到 0xC07FFFFF(前 8MB)。
## 2. 选择缓存策略
对于显存,推荐使用 **Write-through + Bufferable**(即 TEX=0b001,C=1,B=1)。原因:
- 写操作直通 SDRAM,保证 LTDC 能立即看到最新数据,避免花屏。
- Bufferable 允许 CPU 写后不等待,提升写入速度。
若追求极致性能,可尝试 Write-back,但必须定期 Clean Cache 或使用 DMA 双缓冲,否则易出问题。
## 3. 配置 MPU 寄存器
使用 STM32CubeMX 或直接操作寄存器。下面以寄存器方式给出代码。
# 完整代码示例
以下代码基于 STM32F429,使用 HAL 库,配置 MPU 区域 0 覆盖 SDRAM 前 8MB。
```c
#include "stm32f4xx_hal.h"
void MPU_Config_SDRAM(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
// 禁用 MPU 进行配置
HAL_MPU_Disable();
// 配置区域 0
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000; // SDRAM 起始地址
MPU_InitStruct.Size = MPU_REGION_SIZE_8MB; // 8MB 区域
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; // TEX=0b001
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; // C=1
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; // B=1
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 使能 MPU
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
// 在 main 函数中调用
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_FMC_Init(); // 初始化 FMC 以访问 SDRAM
// 配置 MPU(必须在启用 SDRAM 后、使用显存前)
MPU_Config_SDRAM();
// 初始化 LCD 和 LTDC
// ...
while (1)
{
// 主循环
}
}
```
# 常见问题排查
## 1. 性能骤降
- **现象**:帧率比预期低 50% 以上。
- **原因**:MPU 未配置,或配置为 Non-cacheable(TEX=0b000)。CPU 每次写显存都直接访问 SDRAM,无缓冲,总线效率低。
- **解决**:确保配置为 Write-through + Bufferable,并检查是否启用了 MPU。
## 2. 花屏或闪烁
- **现象**:LCD 显示随机条纹或旧画面残留。
- **原因**:MPU 配置为 Write-back,CPU 写入的数据未及时同步到 SDRAM,LTDC 读取到旧数据。
- **解决**:改用 Write-through,或每次绘制后调用 `SCB_CleanDCache()`(若启用 D-Cache)。
## 3. 区域重叠
- **现象**:部分内存访问异常。
- **原因**:MPU 区域覆盖了其他外设或内存,导致访问权限冲突。
- **解决**:仔细规划区域,确保 SDRAM 区域不与 SRAM、Flash 等重叠。
# 注意事项
- **MPU 配置时机**:必须在 SDRAM 初始化之后、任何显存访问之前配置。
- **区域大小**:MPU 支持的最小区域为 32 字节,但建议按 1KB 对齐,避免覆盖无关地址。
- **多区域**:若 SDRAM 中同时存放帧缓冲和 GUI 数据,可配置多个区域,分别设置不同策略。
- **DMA 与 Cache 一致性**:即使使用 Write-through,DMA 读取也可能因总线缓冲而滞后,必要时使用 `__DSB()` 指令确保写完成。
- **测试建议**:先用小区域测试,如 1MB,验证性能与显示效果,再逐步扩大。
# 总结
MPU 配置是 STM32F4 使用 SDRAM 作为显存的关键一环。正确配置 Write-through + Bufferable 可兼顾性能与数据一致性,避免花屏。遇到性能问题时,优先检查 MPU 是否启用及缓存策略;遇到花屏时,检查是否误用了 Write-back。希望本文能帮你快速定位问题,提升开发效率。