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区域初始化结构体及其参数来具体看下可配置哪些属性。
  1. uint8_t Enable : 启用或禁用指定区域的MPU配置
  • MPU_REGION_ENABLE: 启用
  • MPU_REGION_DISABLE: 禁用
  1. 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还低。
  1. uint8_t BaseAddress: 要配置区域的起始地址
  • H7地址范围是4GB(0x00000000~0xFFFFFFFF),起始地址只要在这个范围内且符合对齐标准就行,没有固定值
  • 也可以理解为必须能够被 size 的值整除
  1. uint8_t size: 从起始地址开始要保护的内存大小
  • MPU_REGION_SIZE_32B
  • MPU_REGION_SIZE_64B
  • MPU_REGION_SIZE_4GB
  1. uint8_t SubRegionDisable: 指定该区域中要禁用的子区域,禁用的区域不受规则保护
  • 值在 0x000xFF(0000000011111111),每个区域共8个子区域,1为禁用,0为启用。
  • 最低位代表子区域0,最高位代表子区域7
  1. uint8_t TypeExtField: 用于细化内存类型
  • MPU_TEX_LEVEL0: 普通内存,访问普通的数据和程序代码等,它的访问顺序可以被改变。访问该内存区域的数据会被缓存,并且可以使用通用的缓存策略,如写回、写直通等。
    • 此类型可以使CPU以最高效的方式加载和存储字节、半字和字,从而提高系统的性能。
  • MPU_TEX_LEVEL1: 设备内存,访问外设的数据缓冲区和寄存器等,它的访问顺序可以被改变。访问该内存区域的数据会被缓存,但缓存策略不同于普通的数据缓存。
    • 此类型可以确保外设的操作正确执行,防止出现数据错位或不完整的情况。
  • MPU_TEX_LEVEL2: 强序内存,访问特定的外设寄存器和共享内存区域等,它的访问顺序不能被改变。访问该内存区域的数据会直接被发送到总线上,而不会被缓存。
    • 此类型适用于对内存访问顺序有严格要求的场景,确保操作的正确性和可靠性。
  1. 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): 特权模式和用户模式均只读。
  1. uint8_t DisableExec: 是否禁止指令执行,防止从数据区域执行代码。
  • MPU_INSTRUCTION_ACCESS_ENABLE ((uint8_t)0x00): 允许
  • MPU_INSTRUCTION_ACCESS_DISABLE ((uint8_t)0x01): 禁止
  1. uint8_t IsShareable: 是否为可共享内存(多总线 / 多核心访问时的数据一致性控制)。
  • MPU_ACCESS_SHAREABLE ((uint8_t)0x01): 可共享
  • MPU_ACCESS_NOT_SHAREABLE ((uint8_t)0x00): 不可共享
  1. uint8_t IsCacheable: 是否可缓存(通过 CPU 缓存提升访问速度)。
  • MPU_ACCESS_CACHEABLE ((uint8_t)0x01):
  • MPU_ACCESS_NOT_CACHEABLE ((uint8_t)0x00):
  1. uint8_t IsBufferable: 是否可缓冲(通过写缓冲区优化写操作性能)。
  • MPU_ACCESS_BUFFERABLE ((uint8_t)0x01):
  • MPU_ACCESS_NOT_BUFFERABLE ((uint8_t)0x00):

配置步骤

  1. 禁用MPU,确保在配置期间不会产生任何干扰。
  • 函数:void HAL_MPU_Disable(void);
  1. 配置某个或多个区域的MPU保护参数,全部参数如下
  • 是否使能 - 区域编号 - 区域基址 - 区域大小 - 配置子区域 - 内存类型 - 访问权限 - 是否允许执行指令 - 是否允许共享 - 是否可缓存 - 是否可缓冲
  • 函数:void HAL_MPU_ConfigRegion(const MPU_Region_InitTypeDef *MPU_Init);
  1. 使用能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);
}

未有知而不行者,知而不行,只是未知。-- 《传习录》

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐