Files
DCU_APP/GD32E508_DCU_APP/sys/hardfault.cpp
T

152 lines
4.9 KiB
C++
Raw Normal View History

/**
* @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");
2026-07-15 02:49:37 +08:00
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 诊断函数。
*/
__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"
);
}