STM32基础 - MPU
·
STM32 - MPU
认识 MPU
- MPU(Memory Protection Unit): MPU 内存保护单元,是ARM架构中用于保护系统资源的硬件模块,通过划分内存区域,并对各个区域配置不同的访问权限(读/写/不可访问)、缓存策略(Strongly-ordered、Cacheable)和执行限制(Execute Never)等属性。适用于Cortex-M0+/M3/M4/M7等多款处理器。
- MPU通过协处理器CP15的寄存器配置8个保护域,每个域可管理4KB至4GB的物理空间,起始地址需为域大小的整数倍。内存访问时若违反规则将触发异常信号,异常处理程序根据类型跳转至相应服务例程,Cortex-M系列支持最多16个内存区域保护,区域可重叠并遵循优先级机制(域7最高)。
- 在Armv8架构中采用起始/终止地址定义区域,取消子区域概念且禁止重叠。
可配置属性
通过MPU区域初始化结构体及其参数来具体看下可配置哪些属性。
- uint8_t Enable : 启用或禁用指定区域的MPU配置
- MPU_REGION_ENABLE: 启用
- MPU_REGION_DISABLE: 禁用
- uint8_t Number: 要配置的MPU区域编号,STM32通常有8个区(07),高端芯片可能会有16个区(015)
- MPU_REGION_NUMBER0 ~ MPU_REGION_NUMBER7
- MPU_REGION_NUMBER8 ~ MPU_REGION_NUMBER15 M4 及以上
- 如果在配置过程有重叠或者包含的情况,区域越高优先级越高,也就是0最低,15最高。
- 另外还有一个背景区地(default region),序号为-1,用来定义未被MPU配置的内存区域的访问规则,所以优先级不0还低。
- uint8_t BaseAddress: 要配置区域的起始地址
- H7地址范围是4GB(0x00000000~0xFFFFFFFF),起始地址只要在这个范围内且符合对齐标准就行,没有固定值
- 也可以理解为必须能够被 size 的值整除
- uint8_t size: 从起始地址开始要保护的内存大小
- MPU_REGION_SIZE_32B
- MPU_REGION_SIZE_64B
- …
- MPU_REGION_SIZE_4GB
- uint8_t SubRegionDisable: 指定该区域中要禁用的子区域,禁用的区域不受规则保护
- 值在 0x000xFF(0000000011111111),每个区域共8个子区域,1为禁用,0为启用。
- 最低位代表子区域0,最高位代表子区域7
- uint8_t TypeExtField: 用于细化内存类型
- MPU_TEX_LEVEL0: 普通内存,访问普通的数据和程序代码等,它的访问顺序可以被改变。访问该内存区域的数据会被缓存,并且可以使用通用的缓存策略,如写回、写直通等。
- 此类型可以使CPU以最高效的方式加载和存储字节、半字和字,从而提高系统的性能。
- MPU_TEX_LEVEL1: 设备内存,访问外设的数据缓冲区和寄存器等,它的访问顺序可以被改变。访问该内存区域的数据会被缓存,但缓存策略不同于普通的数据缓存。
- 此类型可以确保外设的操作正确执行,防止出现数据错位或不完整的情况。
- MPU_TEX_LEVEL2: 强序内存,访问特定的外设寄存器和共享内存区域等,它的访问顺序不能被改变。访问该内存区域的数据会直接被发送到总线上,而不会被缓存。
- 此类型适用于对内存访问顺序有严格要求的场景,确保操作的正确性和可靠性。
- uint8_t AccessPermission: 用来配置访问权限
- MPU_REGION_NO_ACCESS ((uint8_t)0x00): 禁止任何访问,两种模式都不能读写。
- MPU_REGION_PRIV_RW ((uint8_t)0x01): 仅特权模式可读写,用户模式无权限。
- MPU_REGION_PRIV_RW_URO ((uint8_t)0x02): 特权模式可读写,用户模式仅可读。
- MPU_REGION_FULL_ACCESS ((uint8_t)0x03): 全权限访问(特权和用户模式均可读写)。
- MPU_REGION_PRIV_RO ((uint8_t)0x05): 仅特权模式可读,用户模式无权限。
- MPU_REGION_PRIV_RO_URO ((uint8_t)0x06): 特权模式和用户模式均只读。
- uint8_t DisableExec: 是否禁止指令执行,防止从数据区域执行代码。
- MPU_INSTRUCTION_ACCESS_ENABLE ((uint8_t)0x00): 允许
- MPU_INSTRUCTION_ACCESS_DISABLE ((uint8_t)0x01): 禁止
- uint8_t IsShareable: 是否为可共享内存(多总线 / 多核心访问时的数据一致性控制)。
- MPU_ACCESS_SHAREABLE ((uint8_t)0x01): 可共享
- MPU_ACCESS_NOT_SHAREABLE ((uint8_t)0x00): 不可共享
- uint8_t IsCacheable: 是否可缓存(通过 CPU 缓存提升访问速度)。
- MPU_ACCESS_CACHEABLE ((uint8_t)0x01):
- MPU_ACCESS_NOT_CACHEABLE ((uint8_t)0x00):
- uint8_t IsBufferable: 是否可缓冲(通过写缓冲区优化写操作性能)。
- MPU_ACCESS_BUFFERABLE ((uint8_t)0x01):
- MPU_ACCESS_NOT_BUFFERABLE ((uint8_t)0x00):
配置步骤
- 禁用MPU,确保在配置期间不会产生任何干扰。
- 函数:
void HAL_MPU_Disable(void);
- 配置某个或多个区域的MPU保护参数,全部参数如下
- 是否使能 - 区域编号 - 区域基址 - 区域大小 - 配置子区域 - 内存类型 - 访问权限 - 是否允许执行指令 - 是否允许共享 - 是否可缓存 - 是否可缓冲
- 函数:
void HAL_MPU_ConfigRegion(const MPU_Region_InitTypeDef *MPU_Init);
- 使用能MPU并且配置异常处理
- 函数:
void HAL_MPU_Enable(uint32_t MPU_Control);
void HAL_MPU_Enable(uint32_t MPU_Control)
{
/* 启用 MPU */
MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
/* 启用内存异常 */
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
/* 确保 MPU 配置生效 */
__DSB(); // 函数为汇编编写,暂时不去深度学习
__ISB(); // 函数为汇编编写,暂时不去深度学习
}
- 参数 MPU_Control 介绍
- MPU_Control 用于配置 MPU 在特殊场景下的工作模式,这些场景包括:硬 fault(HardFault)、NMI(不可屏蔽中断)、FAULTMASK( fault 屏蔽)以及特权模式对 “默认内存区域” 的访问。参数的取值决定了 MPU 在这些场景下是否生效,以及特权模式对未被 MPU 区域覆盖的内存(默认区域)的访问规则。
- 直接行为就是MPU_Control参数通过配置MPU_CTRL寄存器的两个关键位(HFNMIENA和PRIVDEFENA),控制 MPU 在 “异常场景” 和 “特权模式访问默认区域” 时的行为,
- HFNMIENA(HardFault/NMI Enable):决定硬 fault/NMI 时 MPU 是否生效;
- PRIVDEFENA(Privileged Default Enable):决定特权模式访问默认区域时是否受 MPU 控制。
- MPU_HFNMI_PRIVDEF_NONE: 对应寄存器配置:MPU_CTRL寄存器中 HFNMIENA=0 且 PRIVDEFENA=0。
- 当发生硬 fault 或 NMI 时,MPU 被禁用(HFNMIENA=0),此时 CPU 访问内存不受 MPU 规则限制(直接使用默认内存映射)。
- 特权模式访问未被任何 MPU 区域覆盖的内存(默认区域)时,不受 MPU 控制(PRIVDEFENA=0),遵循内核默认规则(通常允许无限制访问)。
- MPU_HARDFAULT_NMI: MPU_CTRL寄存器中 HFNMIENA=1 且 PRIVDEFENA=0。
- 发生硬 fault 或 NMI 时,MPU 仍保持启用(HFNMIENA=1),内存访问仍受 MPU 规则限制(确保异常处理过程中也不违反内存保护)。
- 同MPU_HFNMI_PRIVDEF_NONE,特权模式访问默认区域不受 MPU 控制。
- MPU_PRIVILEGED_DEFAULT: MPU_CTRL寄存器中 HFNMIENA=0 且 PRIVDEFENA=1。
- 发生硬 fault 或 NMI 时,MPU 被禁用(同MPU_HFNMI_PRIVDEF_NONE)。
- 特权模式访问未被 MPU 区域覆盖的内存(默认区域)时,受 MPU 控制(PRIVDEFENA=1),即默认区域的访问权限由 MPU 的 “默认映射规则” 约束(而非完全放开)。
- MPU_PRIVILEGED_DEFAULT: MPU_CTRL寄存器中 HFNMIENA=1 且 PRIVDEFENA=1。
- 发生硬 fault 或 NMI 时,MPU 仍启用(同MPU_HARDFAULT_NMI),内存访问受 MPU 规则限制。
- 特权模式访问默认区域时,受 MPU 控制(同MPU_PRIVILEGED_DEFAULT)。
- 实际使用时,需根据系统对 “异常安全性” 和 “特权访问限制” 的要求选择参数,通常高安全性场景优先选择MPU_HFNMI_PRIVDEF,简化场景可选择MPU_HFNMI_PRIVDEF_NONE。
示例
/**
* @brief STM32H743VIT6 MPU 配置示例
* @brief 示例中会有不同功能的配置
* @retval 无
*/
void MPU_Config(void){
MPU_Region_InitTypeDef MPU_InitStruct = {0}; // 定义 MPU 配置属性结构体
// 第一步:禁用 MPU
HAL_MPU_Disable();
// 第二步:配置MPU参数
/*区域0:闪存区域(代码区域),只读,可执行*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE; // 启用 MPU
MPU_InitStruct.Number = MPU_REGION_NUMBER0; // 区域0
MPU_InitStruct.BaseAddress = 0x08000000; // 闪存地址
MPU_InitStruct.Size = MPU_REGION_SIZE_2MB; // 根据实际闪存大小调整
MPU_InitStruct.SubRegionDisable = 0x0; // 全部启用,不禁用子区域
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; // 设置区域类型
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; // 禁止任何访问,两种模式都不能读写。
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; // 允许执行指令
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; // 不可共享
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; // 允许缓存
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // 禁止缓冲
HAL_MPU_ConfigRegion(&MPU_InitStruct); // MPU 配置函数
/* 区域1: DTCM RAM (112KB) 完全访问,可执行 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE; // 启用
MPU_InitStruct.Number = MPU_REGION_NUMBER1; // 区域1
MPU_InitStruct.BaseAddress = 0x20000000; // DTCM RAM 固定起始地址
MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 区域2: AXI SRAM (384KB),完全访问,不可执行 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.BaseAddress = 0x24000000; // AXI SRAM Base Address
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
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;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 区域3: SRAM1-4(最大512KB),完全访问,不可执行 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER3;
MPU_InitStruct.BaseAddress = 0x30000000; // SRAM1-4 Base Address
MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
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;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 区域4:外围设备(AHB1/2、APB1/2),仅限特权访问 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER4;
MPU_InitStruct.BaseAddress = 0x40000000; // Peripheral Base Address
MPU_InitStruct.Size = MPU_REGION_SIZE_512MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 区域5:QSPI内存(如果使用)-只读,可执行 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER5;
MPU_InitStruct.BaseAddress = 0x90000000; // QSPI Memory Base Address
MPU_InitStruct.Size = MPU_REGION_SIZE_128MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 区域6:SDRAM(如果使用),完全访问,不可执行 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER6;
MPU_InitStruct.BaseAddress = 0xD0000000; // SDRAM Base Address
MPU_InitStruct.Size = MPU_REGION_SIZE_64MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
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;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 区域7:系统和核心外围设备,仅限特权访问 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER7;
MPU_InitStruct.BaseAddress = 0xE0000000; // System Peripherals Base
MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 第三步:使能 MPU
/* Enable the MPU with privileged default access */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
未有知而不行者,知而不行,只是未知。-- 《传习录》
更多推荐



所有评论(0)