Cmbacktrace的github目录为:GitHub - armink/CmBacktrace: Advanced fault backtrace library for ARM Cortex-M series MCU | ARM Cortex-M 系列 MCU 错误追踪库https://github.com/armink/CmBacktraceCmbacktrace的gitee目录为:CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库https://gitee.com/Armink/CmBacktrace.git下载后解压为一下目录
参考demos中的freertos示例,移植Cmbacktrace。示例中使用的是F103系列,但我使用的是STM32F429系列芯片。
添加源码文件。cmbacetrace.c在cm_backtrace下,cmb_fault.s在CmBacktrace-master\cm_backtrace\fault_handler\keil下。
包含头文件。
在使用了cmbacetrace库提供的 cmb_fault.s 汇编文件时,因为该汇编文件内部已经定义了 HardFault_Handler ,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler 被重复定义的错误。注释/删除其他文件中定义的 HardFault_Handler
函数,仅保留 cmb_fault.s 中的.
调用cm_backtrace_init函数进行库初始化。
参数 | 描述 |
---|---|
firmware_name | 固件名称,需与编译器生成的固件名称对应 |
hardware_ver | 固件对应的硬件版本号 |
software_ver | 固件的软件版本号 |
以上入参将会在断言或故障时输出,主要起了追溯的作用。
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* © Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define HARDWARE_VERSION "V1.0.0"
#define SOFTWARE_VERSION "V0.1.0"
/* 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);
void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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 */
cm_backtrace_init("CmBacktrace", HARDWARE_VERSION, SOFTWARE_VERSION);
/* USER CODE END 2 */
/* Call init function for freertos objects (in freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* 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};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 360;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != 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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
#pragma import(__use_no_semihosting)
//±ê×¼¿âÐèÒªµÄÖ§³Öº¯Êý
struct __FILE
{
int handle;
};
FILE __stdout;
//¶¨Òå_sys_exit()ÒÔ±ÜÃâʹÓðëÖ÷»úģʽ
void _sys_exit(int x)
{
x = x;
}
//Öض¨Òåfputcº¯Êý
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï
USART1->DR = (uint8_t) ch;
return ch;
}
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM1 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @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 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
配置文件名: cmb_cfg.h
,针对不同的平台和场景,用户需要自自行手动配置,常用配置。
配置名称 | 功能 | 备注 |
---|---|---|
cmb_println(...) | 错误及诊断信息输出 | 必须配置 |
CMB_USING_BARE_METAL_PLATFORM | 是否使用在裸机平台 | 使用则定义该宏 |
CMB_USING_OS_PLATFORM | 是否使用在操作系统平台 | 操作系统与裸机必须二选一 |
CMB_OS_PLATFORM_TYPE | 操作系统平台 | RTT/UCOSII/UCOSIII/FREERTOS |
CMB_CPU_PLATFORM_TYPE | CPU平台 | M0/M3/M4/M7 |
CMB_USING_DUMP_STACK_INFO | 是否使用 Dump 堆栈的功能 | 使用则定义该宏 |
CMB_PRINT_LANGUAGE | 输出信息时的语言 | CHINESE/ENGLISH |
按照自己的需求配置参数如下:
/*
* This file is part of the CmBacktrace Library.
*
* Copyright (c) 2016, Armink,
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the configure head file for this library.
* Created on: 2016-12-15
*/
#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_
/* print line, must config by user */
#define cmb_println(...) printf(__VA_ARGS__);printf("\r\n")/* e.g., printf(__VA_ARGS__);printf("\r\n") */
/* enable bare metal(no OS) platform */
/* #define CMB_USING_BARE_METAL_PLATFORM */
/* enable OS platform */
#define CMB_USING_OS_PLATFORM
/* OS platform type, must config when CMB_USING_OS_PLATFORM is enable */
#define CMB_OS_PLATFORM_TYPE CMB_OS_PLATFORM_FREERTOS
/* cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M4 /* CMB_CPU_ARM_CORTEX_M0 or CMB_CPU_ARM_CORTEX_M3 or CMB_CPU_ARM_CORTEX_M4 or CMB_CPU_ARM_CORTEX_M7 */
/* enable dump stack information */
#define CMB_USING_DUMP_STACK_INFO
/* language of print information */
/* #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH(default) or CMB_PRINT_LANGUAGE_CHINESE */
#define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH
#endif /* _CMB_CFG_H_ */
因为 FreeRTOS 的 TCB 中没有 StackSize 信息,所以修改了其源码(基于 V10.3.1),在 FreeRTOS/tasks.c
中增加了 uxSizeOfStack
字段, 以及 vTaskStackAddr()
、 vTaskStackSize()
、 vTaskName()
函数。
/*-----------------------------------------------------------*/
/*< Support For CmBacktrace >*/
uint32_t * vTaskStackAddr()
{
return pxCurrentTCB->pxStack;
}
uint32_t vTaskStackSize()
{
#if ( portSTACK_GROWTH > 0 )
return (pxNewTCB->pxEndOfStack - pxNewTCB->pxStack + 1);
#else /* ( portSTACK_GROWTH > 0 )*/
return pxCurrentTCB->uxSizeOfStack;
#endif /* ( portSTACK_GROWTH > 0 )*/
}
char * vTaskName()
{
return pxCurrentTCB->pcTaskName;
}
/*-----------------------------------------------------------*/
在typedef struct tskTaskControlBlock添加
#if( portSTACK_GROWTH <= 0)
UBaseType_t uxSizeOfStack; /*< Support For CmBacktrace >*/
#endif
static void prvInitialiseNewTask添加
pxNewTCB->uxSizeOfStack = ulStackDepth; /*< Support For CmBacktrace >*/
在typedef struct xSTATIC_TCB中添加
#if(portSTACK_GROWTH <= 0)
UBaseType_t uxSizeOfStack; /*< Support For CmBacktrace >*/
#endif /* ( portSTACK_GROWTH > 0 )*/
addr2line (它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。
Linux 系统一般会集成这个工具,Windows 系统有很多在方式获取,本文提供两种方式。
安装 MinGW(网上教程很多,自行搜索),安装后在其安装目录的 bin
文件夹里会包含 addr2line.exe
,此时只用保证环境变量 path
中包含该路径即可;
(XP 平台除外):cmbacetrace库的 tools 文件夹中已存放 addr2line.exe ,可以将其直接拷贝至 C:\Windows 下,或者将 CmBacktrace 仓库的 tools 文件夹路径添加至到环境变量 path 中,这样都能保证命令行工具能正常使用 addr2line 命令。
使用 addr2line --help
可以看到如下介绍:
常用的参数:
-e
:指定可执行映像名称
-a
:显示函数地址
-f
:显示函数名称
例如命令 addr2line -e CmBacktraceTest.axf -a -f 080020ce 080011ba
0x080020ce 将会显示名称为 CmBacktraceTest.axf 的可执行映像,在地址为 080020ce 080011ba
0x080020ce对应的函数名称及源代码信息。执行结果如下:
C:\Users\41610\Desktop\CmbacktraceTest\MDK-ARM\CmbacktraceTest>addr2line -e CmBacktraceTest.axf -a -f 080020ce 080011ba
0x080020ce
fault_test
C:\Users\41610\Desktop\CmbacktraceTest\MDK-ARM/../Core/Src/freertos.c:153
0x080011ba
StartDefaultTask
C:\Users\41610\Desktop\CmbacktraceTest\MDK-ARM/../Core/Src/freertos.c:130
更多使用指南,请参考官方说明文档:https://sourceware.org/binutils/docs-2.27/binutils/addr2line.html#addr2line 。
完成上面4个步骤,即可完成了cmbacetrace库移植。下面进行测试,测试用例使用的是官方demo提供的用例。
编译,运行下载到开发板上。串口会输出以下信息
打开电脑上的命令行工具,进入项目工程的可执行文件所在路径(Keil一般在 Output
下,可执行文件后缀 .axf
;IAR一般在 Exe
下,可执行文件后缀 .out
),最后输出的 addr2line -e CmBacktrace.axf -a -f 080020ce 080011ba
拷贝至控制台,并执行,可看到类似如下,包含函数名称及代码行号的函数调用栈信息:
在命令行可以获取函数调用栈详细信息,并定位错误代码。
俗话说,工欲善其事,必先利其器。CmBacktrace 不仅可以将错误信息输出到控制台上,还可以将错误信息使用 EasyFlash 的 Log 功能保存至 Flash 中,设备死机后重启依然能够读取上次的错误信息。CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性。