STM32H5 系列开启 ICACHE 后,读取温度传感器校准值(TS_CAL1/TS_CAL2)会触发 HardFault,核心原因是 OTP/RO 区域默认被配置为 cacheable,而该区域不支持缓存。通过 MPU(内存保护单元)将 OTP/RO 区域配置为 none-cacheable,即可在保留 ICACHE 提升效率的同时,正常读取校准值及 UID、Flash 容量等信息。本文基于 ST 官方 LAT1340 应用笔记,以 NUCLEO-H563ZI 为例,详解问题根源与分步解决流程。
1. 核心背景与问题定位
1.1 关键信息
- 温度传感器校准值地址:TS_CAL1(30℃校准值)为 0x08FFF814-0x08FFF815,TS_CAL2(130℃校准值)为 0x08FFF818-0x08FFF819;
- 区域属性:校准值所在的 RO 区域(0x08FFF800-0x08FFFFFF)与 OTP 区域(0x08FFF000-0x08FFF7FF)均为 Read-only,通过 AHB 总线访问;
- 触发条件:仅开启 ICACHE 时出现 HardFault,关闭 ICACHE 可临时规避,但会降低代码执行效率。
1.2 问题根源
- STM32H5 的 AHB 内存默认属性为 cacheable,但 OTP/RO 区域(含校准值、UID、Flash 信息)不支持缓存,直接访问会导致缓存冲突;
- 需通过 MPU 手动将该区域配置为 none-cacheable,禁止缓存机制介入,确保总线访问正常。
2. 关键前提:OTP/RO 区域地址范围
OTP 与 RO 区域地址连续,合并配置即可覆盖所有相关区域:
- 合并地址范围:0x08FFF000 - 0x08FFFFFF(共 2KB OTP + 2KB RO,合计 4KB);
- 包含内容:温度传感器校准值、唯一设备 ID(UID)、Flash 容量、封装信息等只读数据。
3. 解决方案:MPU 配置实操
通过 STM32CubeMX 图形化配置 + 代码补充,快速完成 MPU 设置,无需手动操作寄存器。
3.1 STM32CubeMX 配置步骤
- 打开工程,进入 “Pinout & Configuration”→“Configuration”→“Cortex-M55 MPU”;
- 启用 MPU:勾选 “MPU Enable”,设置 “MPU Control Mode” 为 “Background Region Privileged accesses only”;
- 配置区域 0(覆盖 OTP/RO):
- MPU Region Base Address:0x08FFF000(区域起始地址);
- MPU Region Limit Address:0x08FFFFFF(区域结束地址);
- MPU Access Permission:ALL READS(只读权限,匹配 RO/OTP 属性);
- MPU Instruction Access:DISABLE(禁止指令访问,仅允许数据读取);
- MPU Cacheable Permission:NOT CACHEABLE(核心配置,禁用缓存);
- MPU Shareability Permission:NOT SHAREABLE;
- MPU Attributes:DEVICE nGnRnE(设备属性,适配 AHB 总线访问);
- 点击 “Project Manager”→“Generate Code”,生成包含 MPU 基础配置的代码。
3.2 补充 MPU 初始化代码
生成代码后,在
main.c中添加完整 MPU 配置函数(确保在系统初始化后、校准值读取前调用):void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
MPU_Attributes_InitTypeDef MPU_AttributesInit = {0};
/* 禁用MPU,配置前必须执行 */
HAL_MPU_Disable();
/* 配置OTP/RO区域(0x08FFF000-0x08FFFFFF) */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x08FFF000;
MPU_InitStruct.LimitAddress = 0x08FFFFFF;
MPU_InitStruct.AttributesIndex = MPU_ATTRIBUTES_NUMBER0;
MPU_InitStruct.AccessPermission = MPU_REGION_ALL_RO; // 只读权限
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; // 禁止指令执行
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置内存属性:none-cacheable + 设备属性 */
MPU_AttributesInit.Number = MPU_REGION_NUMBER0;
MPU_AttributesInit.Attributes = INNER_OUTER(MPU_DEVICE_nGnRnE | MPU_NOT_CACHEABLE | MPU_TRANSIENT | MPU_NO_ALLOCATE);
HAL_MPU_ConfigMemoryAttributes(&MPU_AttributesInit);
/* 启用MPU,使用特权模式默认配置 */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
3.3 调用 MPU 配置函数
在
main()函数中,系统初始化后、读取校准值前调用MPU_Config():int main(void)
{
/* 复位所有外设,初始化Flash和Systick */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 初始化所有配置的外设 */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* 关键步骤:配置MPU,禁用OTP/RO区域缓存 */
MPU_Config();
/* 读取温度传感器校准值,此时不会触发HardFault */
uint16_t TS_CAL1_Val = *(uint16_t *)0x08FFF814;
uint16_t TS_CAL2_Val = *(uint16_t *)0x08FFF818;
/* 后续业务代码... */
while (1)
{
// 业务逻辑
}
}
4. 验证与注意事项
4.1 验证方法
- 开启 ICACHE,运行程序,观察是否触发 HardFault;
- 打印 TS_CAL1_Val 和 TS_CAL2_Val,若数值非 0xFFFF(默认未初始化值),说明读取成功。
4.2 关键注意事项
- 地址范围不可错:必须覆盖完整 OTP/RO 区域(0x08FFF000-0x08FFFFFF),遗漏会导致部分数据读取异常;
- 权限配置:仅设置为 “只读”,避免误写 OTP 区域(OTP 为一次性可编程,写操作不可逆);
- 调用时机:MPU 配置必须在读取校准值、UID 等数据前执行,否则仍会触发 HardFault;
- 兼容性:该配置适用于所有 STM32H5 系列芯片(H563/H573 等),无需修改地址范围。
STM32H5 读取温度传感器校准值触发 HardFault 的核心是缓存冲突,通过 MPU 将 OTP/RO 区域配置为 none-cacheable 即可彻底解决。该方案既保留了 ICACHE 对代码执行效率的提升,又确保了只读区域的正常访问,同时适用于 UID、Flash 容量等其他只读数据的读取。
阅读全文
89