STM32H7 SDRAM非对齐访问触发HardFault的MPU配置优化方案

张开发
2026/4/16 20:54:33 15 分钟阅读

分享文章

STM32H7 SDRAM非对齐访问触发HardFault的MPU配置优化方案
1. 问题现象与复现最近在调试STM32H7的外部SDRAM时遇到了一个奇怪的问题当使用memcpy函数进行数据拷贝时如果源地址或目标地址不是4字节对齐的就会触发HardFault异常。具体表现为uint8_t aStr[100]; // 分配地址到0xD0000000 memcpy(aStr[0], aStr[0], 7); // OK memcpy(aStr[0], aStr[1], 7); // 进入HardFault_Handler() memcpy(aStr[4], aStr[0], 7); // OK memcpy(aStr[1], aStr[0], 7); // 进入HardFault_Handler()从上面的测试代码可以看出只有当地址是4字节对齐时如0xD0000000、0xD0000004memcpy才能正常工作而非对齐地址如0xD0000001则会触发硬件异常。这个问题在内部SRAM0x24000000上不会出现只有在外部SDRAM区域才会发生。2. 问题原因分析查阅STM32H7的参考手册在Memory map and register boundary addresses部分可以找到答案。手册明确指出0xC0000000 - 0xDFFFFFFF这个地址范围被归类为Device Memory Type。对于Device Memory Type的访问有以下严格限制所有访问必须是4字节对齐的不支持非对齐访问如果尝试非对齐访问将直接触发硬件异常而我们的外部SDRAM正好位于这个地址范围内0xD0000000因此受到了这个限制的影响。相比之下内部SRAM0x24000000属于Normal Memory Type没有对齐访问的限制。3. MPU配置解决方案3.1 MPU的基本概念MPUMemory Protection Unit是ARM Cortex-M系列处理器提供的一个内存保护单元主要功能包括定义内存区域的访问权限设置内存区域的缓存策略配置内存区域的类型Normal/Device通过合理配置MPU我们可以改变SDRAM区域的类型从Device Memory改为Normal Memory从而解除对齐访问的限制。3.2 具体配置步骤以下是完整的MPU配置代码我已经在实际项目中验证过其有效性// 定义一个MPU区域初始化结构体 MPU_Region_InitTypeDef MPU_InitStruct; // 禁用MPU HAL_MPU_Disable(); // 配置MPU区域参数 MPU_InitStruct.Enable MPU_REGION_ENABLE; // 启用该区域 MPU_InitStruct.BaseAddress 0xD0000000; // 基地址 MPU_InitStruct.Size MPU_REGION_SIZE_32MB; // 区域大小为32MB MPU_InitStruct.AccessPermission MPU_REGION_FULL_ACCESS; // 全权限访问 MPU_InitStruct.IsBufferable MPU_ACCESS_NOT_BUFFERABLE; // 不可缓冲 MPU_InitStruct.IsCacheable MPU_ACCESS_NOT_CACHEABLE; // 不可缓存 MPU_InitStruct.IsShareable MPU_ACCESS_NOT_SHAREABLE; // 不可共享 MPU_InitStruct.Number MPU_REGION_NUMBER0; // 区域编号为0 MPU_InitStruct.TypeExtField MPU_TEX_LEVEL1; // 类型扩展字段 MPU_InitStruct.SubRegionDisable 0x00; // 不禁用子区域 MPU_InitStruct.DisableExec MPU_INSTRUCTION_ACCESS_DISABLE; // 禁止指令访问 // 配置MPU区域 HAL_MPU_ConfigRegion(MPU_InitStruct); // 启用MPU HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);这段代码的关键在于TypeExtField参数的设置。通过将其配置为MPU_TEX_LEVEL1我们将SDRAM区域标记为Normal Memory类型从而解除了对齐访问的限制。4. 配置参数详解4.1 内存类型与缓存策略MPU配置中最容易混淆的就是内存类型和缓存策略的设置。这里我整理了一个简单的对照表参数可选值说明TypeExtFieldMPU_TEX_LEVEL0Device MemoryMPU_TEX_LEVEL1Normal MemoryIsCacheableMPU_ACCESS_CACHEABLE启用缓存MPU_ACCESS_NOT_CACHEABLE禁用缓存IsBufferableMPU_ACCESS_BUFFERABLE启用缓冲MPU_ACCESS_NOT_BUFFERABLE禁用缓冲对于SDRAM来说最常用的配置组合是MPU_TEX_LEVEL1Normal MemoryMPU_ACCESS_CACHEABLE启用缓存MPU_ACCESS_NOT_BUFFERABLE禁用缓冲4.2 区域大小设置MPU支持的区域大小是固定的几个选项常见的有大小选项实际大小MPU_REGION_SIZE_32MB32MBMPU_REGION_SIZE_16MB16MBMPU_REGION_SIZE_8MB8MBMPU_REGION_SIZE_4MB4MB选择区域大小时需要注意必须大于等于实际使用的SDRAM大小必须是2的幂次方基地址必须对齐到区域大小例如如果你的SDRAM是16MB可以设置为16MB或32MB但不能设置为8MB。5. 实际应用中的注意事项5.1 多区域配置在实际项目中我们可能需要配置多个MPU区域。例如内部Flash0x08000000内部SRAM0x24000000外部SDRAM0xD0000000每个区域都需要单独配置并分配不同的区域编号Number。STM32H7的MPU最多支持8个区域。5.2 性能优化建议虽然我们已经解决了非对齐访问的问题但在实际使用中还有一些性能优化的技巧启用缓存将IsCacheable设置为MPU_ACCESS_CACHEABLE可以显著提高访问速度合理对齐数据即使不再强制要求对齐保持数据对齐仍然能获得更好的性能批量操作对于大量数据传输使用DMA比CPU直接操作更高效5.3 调试技巧如果在配置MPU后仍然遇到问题可以检查以下几点MPU是否成功启用在调试器中查看MPU控制寄存器的值配置是否正确检查每个参数是否符合预期区域重叠确保不同MPU区域没有地址重叠权限设置确认访问权限AccessPermission设置正确6. 扩展应用6.1 其他存储器的配置同样的MPU配置方法也适用于其他类型的外部存储器如NOR FlashSRAMPSRAM只需要根据具体存储器的特性调整缓存和缓冲的设置即可。6.2 RTOS环境下的MPU配置如果使用RTOS如FreeRTOS、RT-Thread需要注意在任务切换时保存/恢复MPU配置为不同任务分配不同的内存访问权限确保内核空间和用户空间的隔离大多数RTOS都提供了MPU支持的版本可以直接使用。7. 常见问题解答7.1 为什么内部SRAM不需要这个配置内部SRAM0x24000000默认就是Normal Memory类型所以不受对齐访问的限制。只有Device Memory类型的区域才有这个限制。7.2 配置MPU会影响性能吗正确配置MPU不仅不会降低性能反而可能提高性能。例如启用缓存可以减少内存访问延迟合理的内存类型设置可以让总线传输更高效7.3 能否完全禁用对齐检查不建议这样做。对齐检查是一个重要的硬件保护机制可以捕获很多潜在的内存访问错误。我们的解决方案是通过MPU改变内存类型而不是禁用对齐检查。在实际项目中我遇到过很多次因为非对齐访问导致的奇怪问题。通过合理配置MPU不仅可以解决HardFault异常还能提高系统的稳定性和性能。特别是在使用外部SDRAM存储大量数据时这个技巧非常实用。

更多文章