/** * @file hardfault.cpp * @brief HardFault 诊断处理——通过 USART0 输出故障寄存器信息。 * * 当 APP 在 BOOT 跳转后立即死机时,标准 printf 不可用, * 所以使用直接寄存器操作输出。 */ #include "gd32e50x.h" #include /** * @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) { (void)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"); 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 诊断函数。 */ __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" ); }