1. 问题现象客户反馈在 STM32H5 中通过代码读取 UID 时,会触发 Hardfault.
2. 问题重现及分析我手动通过 STM32CubeMx 生成一个基于 STM32H563 的测试工程。运行在 NUCLEOH563RE 板上。在代码中写了一个读取 UID 函数 :
- uint32_t deviceserial0, deviceserial1, deviceserial2;
- void ReadUIDTest(void)
- {
- deviceserial0 = HAL_GetUIDw0();
- deviceserial1 = HAL_GetUIDw1();
- deviceserial2 = HAL_GetUIDw2();
- }
然后在 main 函数中调用这个 ReadUIDTest()函数,发现正如客户所说,触发了hardfault。Keil 的 Call Stack 如下所示 :
这个问题能 100%重现问题,于是在 Keil 调试下查看故障报告窗口 :
上图信息解析如下 :当前触发了 Bus Fault 中断,由于此中断在代码中并未使能,所以才升级为 hardfault.触发 Bus Fault 中断的原因是数据访问导致的,此数据地址为 : 0x08FFF800.通过代码中的宏定义 :#define UID_BASE (0x08FFF800UL) /*!< Unique device ID register baseaddress */可知,这个地址 0x08FFF800 刚好是芯片的 UID 地址。那么问题来了,为什么读取 UID 地址就会触发 hardfault 呢?查看芯片对应的参考手册 RM0481(Rev 4), 在第 7.3.2 节看到如下内容
‘UID 所在 FLASH 区域为 RO 区域,它默认不支持 Cacheable 特性的,而在代码中若激活了 ICache(通过 STM32CubeMx 生成的工程默认会默认提示用户开户 I-Cache 以提高代码效率),那么代码访问 AHB 存储范围内的所有地址默认都是缓存的,也就是说,这种缓存机制跟 UID所在特殊区域的缓存特性冲突。要解决这种冲突,得借助 MPU 来改变局部区域(UID 的在地址)的缓存策略才行。知道了问题所在,那么对应的解决代码如下所示:
- /**
- * [url=/u/brief]@brief[/url] Configures the main MPU regions.
- * @param None
- * @retval None
- */
- void MPU_Config(void)
- {
- MPU_Region_InitTypeDef MPU_InitStruct;
- MPU_Attributes_InitTypeDef MPU_AttributesInit;
- /* Disable MPU */
- HAL_MPU_Disable();
- /* Define cacheable memory via MPU */
- MPU_AttributesInit.Number = 0;
- MPU_AttributesInit.Attributes = MPU_NOT_CACHEABLE;
- HAL_MPU_ConfigMemoryAttributes(&MPU_AttributesInit);
- /* Configure UID region as Region Number 0 */
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.BaseAddress = 0x08FFF800;
- MPU_InitStruct.LimitAddress = 0x08FFFFFF;
- MPU_InitStruct.AccessPermission = MPU_REGION_ALL_RO;
- MPU_InitStruct.AttributesIndex = MPU_ATTRIBUTES_NUMBER0;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER0;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- /* Enable MPU (any access not covered by any enabled region will cause a fault) */
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
- }
在代码访问 UID 之前,先用 MPU 将 UID 所在地址区域的缓存属性关闭,如此,即可解决问题。除 UID 之外,EData, OTP 区域也有类似问题,且这种问题在以前传统的 MCU 中并不会遇到的,这也是 STM32H5 比较特别的地方之一,大家需要注意这一点。
’
|