Files
cbb 46ef19d882 feat(build): 优化构建配置并引入日志分级系统
- 将优化级别从balanced调整为size,减少Flash占用
- 添加hardfault.cpp文件以增强故障诊断能力
- 关闭RTTI和异常处理以移除libcppabi运行时(节省约2.5KB Flash)
- 添加链接器参数输出未引用段与总量报告用于Flash尺寸分析

refactor(logging): 统一日志输出并实现编译时日志分级

- 将printf调用替换为模块化的日志宏(ECAT_LOGI、CAN_LOGI等)
- 实现CONFIG_*_LOG_ENABLE编译开关控制各模块INFO级日志
- 错误和关键事件使用LOG_ERR恒定保留,其他日志可编译时裁剪
- 保持原有日志内容不变,但支持链接器自动回收未使用的格式字符串
2026-07-27 01:49:18 +08:00

153 lines
5.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file hardfault.cpp
* @brief HardFault 诊断处理——通过 USART0 输出故障寄存器信息。
*
* 当 APP 在 BOOT 跳转后立即死机时,标准 printf 不可用,
* 所以使用直接寄存器操作输出。
*/
#include "gd32e50x.h"
#include <stdint.h>
/**
* @brief 通过 USART0 直接发送一个字节(纯寄存器操作,不依赖库函数/宏)。
*/
static void rawPutc(char ch) {
volatile uint32_t *stat0 = (volatile uint32_t *)0x40013800U;
volatile uint32_t *data = (volatile uint32_t *)0x40013804U;
while ((*stat0 & (1U << 7)) == 0U) {} /* 等待 TBE */
*data = (uint32_t)ch;
}
/**
* @brief 通过 USART0 直接发送字符串。
*/
static void rawPuts(const char *s) {
while (*s) { rawPutc(*s++); }
}
/**
* @brief 输出 32 位十六进制值。
*/
static void rawHex32(uint32_t val) {
const char hex[] = "0123456789ABCDEF";
rawPuts("0x");
for (int i = 28; i >= 0; i -= 4) {
rawPutc(hex[(val >> i) & 0xF]);
}
}
/**
* @brief 检测当前实际 APB2 时钟频率(Hz)。
*
* 通过读取 RCU_CFG0 的 SCSS / AHBPSC / APB2PSC 字段,
* 实时计算 USART0 所在的 APB2 总线时钟频率,
* 而不依赖可能已过时的 SystemCoreClock 全局变量。
*
* @param stack_frame 指向异常栈帧的指针(由汇编桩压入)。
* 包含: R0, R1, R2, R3, R12, LR, PC, xPSR
* @param lr_value EXC_RETURN 值,用于判断使用 MSP 还是 PSP。
*/
static uint32_t detectApb2Clock(void) {
volatile uint32_t *rcu_cfg0 = (volatile uint32_t *)0x40021000U;
uint32_t cfg0 = *rcu_cfg0;
/* 1. 确定 SYSCLK 频率 */
uint32_t scss = (cfg0 >> 2) & 0x3U;
uint32_t sysclk;
if (scss == 0x2U) {
sysclk = 180000000U; /* PLL = 180MHz(当前 PLL 目标频率) */
} else {
sysclk = 8000000U; /* IRC8M = 8MHz 或 HXTAL(未倍频时视为 8MHz) */
}
/* 2. AHB 预分频 */
uint32_t ahbpsc = (cfg0 >> 4) & 0xFU;
uint32_t ahb_div;
switch (ahbpsc) {
case 0x8: ahb_div = 2; break;
case 0x9: ahb_div = 4; break;
case 0xA: ahb_div = 8; break;
case 0xB: ahb_div = 16; break;
case 0xC: ahb_div = 64; break;
case 0xD: ahb_div = 128; break;
case 0xE: ahb_div = 256; break;
case 0xF: ahb_div = 512; break;
default: ahb_div = 1; break;
}
uint32_t ahb_clk = sysclk / ahb_div;
/* 3. APB2 预分频 */
uint32_t apb2psc = (cfg0 >> 11) & 0x7U;
uint32_t apb2_div;
switch (apb2psc) {
case 0x4: apb2_div = 2; break;
case 0x5: apb2_div = 4; break;
case 0x6: apb2_div = 8; break;
case 0x7: apb2_div = 16; break;
default: apb2_div = 1; break;
}
return ahb_clk / apb2_div;
}
extern "C" void HardFault_Handler_C(uint32_t *stack_frame, uint32_t lr_value) {
/* 确保 USART0 时钟可用 */
volatile uint32_t *rcu_apb2en = (volatile uint32_t *)0x40021018U;
*rcu_apb2en |= (1U << 2) | (1U << 14); /* RCU_GPIOA | RCU_USART0 */
/* 确保 PA9 处于 AF_PP 50MHz 模式(可能被其他代码覆盖) */
volatile uint32_t *gpioa_ctl1 = (volatile uint32_t *)0x40010804U;
uint32_t ctl = *gpioa_ctl1;
ctl &= ~(0xFU << 4);
ctl |= (0xBU << 4); /* PA9: AF_PP 50MHz */
*gpioa_ctl1 = ctl;
/* 根据实际 APB2 时钟计算波特率分频值 */
uint32_t apb2_clk = detectApb2Clock();
uint32_t baud_div = apb2_clk / 115200U;
if (baud_div == 0U) baud_div = 1U;
volatile uint32_t *usart_baud = (volatile uint32_t *)0x40013808U;
volatile uint32_t *usart_ctl0 = (volatile uint32_t *)0x40013800U;
*usart_baud = baud_div;
*usart_ctl0 = (1U << 3) | (1U << 13); /* TE | USARTEN */
rawPuts("\r\n!!! HardFault !!!\r\n");
rawPuts("EXC_RETURN="); rawHex32(lr_value); rawPuts("\r\n");
/* EXC_RETURN 解析:bit3=0(MSP)/1(PSP), bit2=0(handler mode)/1(thread mode) */
if (stack_frame) {
uint32_t pc = stack_frame[6];
uint32_t lr = stack_frame[5];
uint32_t psr = stack_frame[7];
rawPuts("PC="); rawHex32(pc); rawPuts("\r\n");
rawPuts("LR="); rawHex32(lr); rawPuts("\r\n");
rawPuts("PSR="); rawHex32(psr); rawPuts("\r\n");
}
rawPuts("CFSR="); rawHex32(SCB->CFSR); rawPuts("\r\n");
rawPuts("HFSR="); rawHex32(SCB->HFSR); rawPuts("\r\n");
rawPuts("MMFAR="); rawHex32(SCB->MMFAR); rawPuts("\r\n");
rawPuts("BFAR="); rawHex32(SCB->BFAR); rawPuts("\r\n");
/* 死循环 */
while (1) {}
}
/**
* @brief HardFault_Handler 汇编桩——保存栈帧后调用 C 诊断函数。
* @note 必须 extern "C":否则被 C++ 名字修饰,无法覆盖启动文件的 WEAK 符号。
*/
extern "C" __attribute__((naked)) void HardFault_Handler(void) {
__asm volatile(
"tst lr, #4 \n" /* 检查 EXC_RETURN 位 2 */
"ite eq \n"
"mrseq r0, msp \n" /* 位 2 = 0: 使用 MSP */
"mrsne r0, psp \n" /* 位 2 = 1: 使用 PSP */
"mov r1, lr \n" /* 保存 EXC_RETURN */
"b HardFault_Handler_C \n"
);
}