STM32H7-MPU配置实战:Cache策略与DMA性能优化

1. STM32H7的MPU到底是什么?

第一次接触STM32H7的MPU(Memory Protection Unit)时,我盯着手册发呆了半小时——这玩意儿不就是个高级版的内存保安吗?想象一下:你的芯片内部有个带16个摄像头的监控中心,每个摄像头(MPU区域)可以自定义监控规则,比如"只允许CPU读这个内存块"或者"DMA禁止碰这段数据"。实际项目中,我拿它干过两件大事:防止程序跑飞时篡改关键数据,以及优化DMA和Cache配合时的性能问题。

MPU最实用的功能是它能定义16个独立内存区域的访问权限。比如你可以:

  • 把关键配置区设为只读(AP=0x01)
  • 禁止非特权模式访问外设寄存器(AP=0x02)
  • 关闭某块内存的Cache(TEX/C/B位组合) 上周调试QSPI Flash时,就因为没设Strongly Ordered属性,DMA传的数据全是乱码。后来用MPU把0x90000000开始的区域配置为Device模式,问题立刻解决。这里有个坑要注意:H7的MPU区域最小256字节(不是32字节!),配置时BaseAddress必须对齐到区域大小。

2. Cache策略与DMA的生死局

去年做音频处理项目时,我踩过一个经典大坑:DMA从SDRAM搬运数据到I2S外设,结果输出的音频全是杂音。示波器一看,数据根本没更新!根本原因是Cache作祟——CPU写入SDRAM的数据还躺在L1 Cache里,DMA却直接从物理内存读取旧数据。这时候MPU就是救世主,通过三种武器解决问题:

武器1:内存属性定义

MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // 关闭Cache MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // 禁止写缓冲 MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; // 允许多核共享

武器2:Write-Through策略当配置为Write-Through时,每次CPU写操作会同时更新Cache和主存,保证DMA总能拿到最新数据。代价是性能损失约15%,实测在240MHz主频下,写AXI SRAM的速度从180MB/s降到153MB/s。

武器3:手动Cache维护

SCB_CleanDCache_by_Addr((uint32_t*)buffAddr, buffSize); // DMA发送前刷Cache SCB_InvalidateDCache_by_Addr((uint32_t*)buffAddr, buffSize); // DMA接收前无效化Cache

最稳的组合拳是把DMA缓冲区所在区域通过MPU设为Non-Cacheable,再配合手动维护。我在处理1080P视频流时,这样配置后DMA性能提升40%,CPU负载反而降低12%。

3. 实战:QSPI Flash的MPU配置

最近用QSPI Flash存字库时,发现直接读取速度只有8MB/s,启用Cache后飙升到52MB/s——但代价是偶尔会读到错误数据。经过反复测试,最优配置如下:

void MPU_Config(void) { HAL_MPU_Disable(); MPU_Region_InitTypeDef mpui; mpui.Enable = MPU_REGION_ENABLE; mpui.BaseAddress = 0x90000000; // QSPI映射地址 mpui.Size = MPU_REGION_SIZE_64MB; mpui.AccessPermission = MPU_REGION_FULL_ACCESS; mpui.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; mpui.IsCacheable = MPU_ACCESS_CACHEABLE; // 必须开启 mpui.IsShareable = MPU_ACCESS_NOT_SHAREABLE; mpui.TypeExtField = MPU_TEX_LEVEL0; // 强序内存 HAL_MPU_ConfigRegion(&mpui); HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); }

关键点在于TypeExtField和IsCacheable的组合:

  • TEX=0, C=1, B=0 表示Write-Through模式
  • TEX=0, C=1, B=1 变成Write-Back模式(危险!)
  • TEX=1, C=0, B=0 才是Device模式(DMA必备)

实测发现,当使用DMA从QSPI读取数据时,必须配置为Device模式。而CPU直接访问时,Write-Through模式既能加速又保证安全。这解释了为什么我的UI界面偶尔会花屏——DMA传输时Cache没及时同步。

4. 性能优化三重奏

在电机控制项目中,通过MPU配置让FOC算法循环从5.2us降到3.8us,具体操作:

第一重:关键数据区配置

// 电流采样缓冲区 (AXI SRAM) mpui.BaseAddress = 0x24000000; mpui.Size = MPU_REGION_SIZE_32KB; mpui.IsCacheable = MPU_ACCESS_CACHEABLE; mpui.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // Write-Through mpui.TypeExtField = MPU_TEX_LEVEL1; // 普通内存 HAL_MPU_ConfigRegion(&mpui); // 外设寄存器区 (APB1/APB2) mpui.BaseAddress = 0x40000000; mpui.Size = MPU_REGION_SIZE_512MB; mpui.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // 必须关闭 mpui.IsBufferable = MPU_ACCESS_BUFFERABLE; // 外设需要写缓冲

第二重:DMA缓冲区特殊处理把ADC采样区设为Non-Cacheable后,发现CPU处理速度下降。最终方案是:

  1. DMA传输时区域设为Non-Cacheable
  2. 处理前调用SCB_InvalidateDCache
  3. 处理完改回Write-Through

第三重:优先级调整通过MPU_InitStruct.Number设置区域优先级。比如把PWM寄存器的区域号设为15(最高),防止其他区域配置覆盖它。