[STM32U3] 【STM32U385RG 测评】——4.移植开源cmbacktrace识别hardfault故障诊断

[复制链接]
 楼主| 龙鳞铁碎牙 发表于 2025-7-10 15:33 | 显示全部楼层 |阅读模式
本帖最后由 龙鳞铁碎牙 于 2025-7-12 10:14 编辑

CmBacktrace是一个叫朱天龙的人发明的定位Cortex M内核的开源软件模组。它是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下:
支持的错误包括:
断言(assert)
故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
故障原因 自动诊断 :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
输出错误现场的 函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈;
支持 裸机 及以下操作系统平台:
RT-Thread
UCOS
FreeRTOS(需修改源码)
根据错误现场状态,输出对应的 线程栈 或 C 主栈;
故障诊断信息支持多国语言(目前:简体中文、英文);
适配 Cortex-M0/M3/M4/M7 MCU;
支持 IAR、KEIL、GCC 编译器;


对于从 C51 、MSP430 等简单单片机转而使用更加复杂的 ARM 新人来说,时不时出现的 "hard falut" 死机会让新人瞬间懵掉。定位错误的方法也往往是连接上仿真器,一步步 F10/F11 单步,定位到具体的错误代码,再去猜测、排除、推敲错误原因,这种过程十分痛苦。
慢慢的大家知道可以通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。虽然连接仿真器可以查看到的函数调用栈,但故障状态下是无法显示的,所以还是得一步步 F10/F11 单步去定位错误代码的位置。另外,还有两种场景,
  • 1、很多产品真机调试时必须断开仿真器
  • 2、问题确实存在,但是极难被重现
所以定位这类问题就显得难上加难。

使用CmBacktrace库可以将错误信息输出到控制台上,还可以将错误信息使用 [color=var(--fgColor-accent, var(--color-accent-fg))]EasyFlash 的 Log 功能保存至 Flash 中,设备死机后重启依然能够读取上次的错误信息。CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性。
俗话说,工欲善其事,必先利其器。有时候做事效率低的原因是你会用的工具种类太少。

如何使用CmBacktrace库。
首先进入CmBacktrace的github网站下载
https://github.com/armink/CmBacktrace
76825686f6dcfeed87.png
下载后进行接下来的移植操作
1.先将CmBacktrace添加到KEIL目录和工程
26408686f6e182d8ad.png 76207686f6e235de5a.png
2.修改核心测试代码
/*
* fault_test.c
*
*  Created on: 2016/12/25
*      Author: Armink
*/

#include <stdio.h>
#include <stdint.h>

void fault_test_by_unalign(void) {
    volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
    volatile int * p;
    volatile int value;

    *SCB_CCR |= (1 << 3); /* bit3: UNALIGN_TRP. */

    p = (int *) 0x01;
    value = *p;
    printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);

    p = (int *) 0x04;
    value = *p;
    printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);

    p = (int *) 0x03;
    value = *p;
    printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
}

void fault_test_by_div0(void) {
    volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
    int x, y, z;

    *SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */

    x = 10;
    y = 0;
    z = x / y;
    printf("z:%d\n", z);
}




void func_3(int a, int b, int c, int d, int e, int f)
{
    *(volatile uint32_t *)0xffffffff = a + b + c + d + e + f;
}

void func_2(int a, int b, int c, int d, int e, int f)
{
    func_3(a, b, c, d, e, f);
}

void func_1(int a, int b, int c, int d, int e, int f)
{
    func_2(a, b, c, d, e, f);
}

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "coremark.h"
#include "hal_systick.h"
#include <cm_backtrace.h>
#include <stdio.h>

#define HARDWARE_VERSION               "V1.0.0"
#define SOFTWARE_VERSION               "V0.1.0"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */


/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void coremark_main(void);

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern void fault_test_by_unalign(void);
extern void fault_test_by_div0(void);

extern void func_1(int a, int b, int c, int d, int e, int f);
extern void func_2(int a, int b, int c, int d, int e, int f);
extern void func_3(int a, int b, int c, int d, int e, int f);


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
  uint8_t _continue;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  hal_systick_init();

  /* USER CODE END 2 */

  /* CmBacktrace initialize */
  cm_backtrace_init("CmBacktrace", HARDWARE_VERSION, SOFTWARE_VERSION);

  fault_test_by_unalign();
  //fault_test_by_div0();


  //printf("STM32U385RG评估板 coremark跑分:\r\n");
  //coremark_main();
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
//  while (1)
//  {
//    /* USER CODE END WHILE */

//    /* USER CODE BEGIN 3 */
//  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Enable Epod Booster
  */
  if (HAL_RCCEx_EpodBoosterClkConfig(RCC_EPODBOOSTER_SOURCE_MSIS, RCC_EPODBOOSTER_DIV1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_PWREx_EnableEpodBooster() != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Set Flash latency before increasing MSIS
  */
  __HAL_FLASH_SET_LATENCY(FLASH_LATENCY_2);

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSIS;
  RCC_OscInitStruct.MSISState = RCC_MSI_ON;
  RCC_OscInitStruct.MSISSource = RCC_MSI_RC0;
  RCC_OscInitStruct.MSISDiv = RCC_MSI_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSIS;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


编译后烧录到板子,打开串口助手
58824686f6e74eb9a0.png
上面就是打印的故障信息,进行故障定位,需要用到addr2line.exe。
先复制CmBacktrace 目录下的 addr2line.exe 到
53698686f6ec02f98d.png
打开cmd命令行
5737686f6ed46019c.png
按照提示,输入如下指令
.\addr2line -e USART1.axf -afpiC 08002a52 08002ed8 080006a2
72311686f6ef14f601.png
提示是fault_test.c第19行
69873686f6f20df825.png
原因:企图执行非对齐访问



附件是我的工程

  
12922686f6df91df27.png
55989686f6e4568696.png

USART1.zip

6.62 MB, 下载次数: 3

小小蚂蚁举千斤 发表于 2025-7-28 23:00 | 显示全部楼层
移植开源cmbacktrace识别hardfault故障诊断
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

47

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

17

主题

47

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部