Add a hardfault handler so it's more obvious when STM32 crashes. (#10071)

* Add hardfault handler so it's more obvious when STM32 crashes.

* thanks copilot
This commit is contained in:
Chloe Bethel
2026-04-03 14:50:39 +01:00
committed by GitHub
parent 726d539174
commit 2e6519bb98
2 changed files with 108 additions and 1 deletions

View File

@@ -0,0 +1,11 @@
.globl HardFault_Handler
.syntax unified
.thumb
.type HardFault_Handler, %function
HardFault_Handler:
tst lr, #4
ite eq
mrseq r0, msp
mrsne r0, psp
b HardFault_Handler_C

View File

@@ -1,5 +1,6 @@
#include "RTC.h"
#include "configuration.h"
#include <stdarg.h>
#include <stm32wle5xx.h>
#include <stm32wlxx_hal.h>
@@ -53,4 +54,99 @@ extern "C" void __wrap__tzset_unlocked_r(struct _reent *reent_ptr)
{
return;
}
#endif
#endif
// Taken from https://interrupt.memfault.com/blog/cortex-m-hardfault-debug
typedef struct __attribute__((packed)) ContextStateFrame {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t return_address;
uint32_t xpsr;
} sContextStateFrame;
// NOTE: If you are using CMSIS, the registers can also be
// accessed through CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk
#define HALT_IF_DEBUGGING() \
do { \
if ((*(volatile uint32_t *)0xE000EDF0) & (1 << 0)) { \
__asm("bkpt 1"); \
} \
} while (0)
static char hardfault_message_buffer[256];
// printf directly using srcwrapper's debug UART function.
static void debug_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
int length = vsnprintf(hardfault_message_buffer, sizeof(hardfault_message_buffer), format, args);
va_end(args);
if (length < 0)
return;
uart_debug_write((uint8_t *)hardfault_message_buffer, min((unsigned int)length, sizeof(hardfault_message_buffer) - 1));
}
// N picked by guessing
#define DOT_TIME 1200000
static void dot()
{
digitalWrite(LED_POWER, LED_STATE_ON);
for (volatile int i = 0; i < DOT_TIME; i++) { /* busy wait */
}
digitalWrite(LED_POWER, LED_STATE_OFF);
for (volatile int i = 0; i < DOT_TIME; i++) { /* busy wait */
}
}
static void dash()
{
digitalWrite(LED_POWER, LED_STATE_ON);
for (volatile int i = 0; i < (DOT_TIME * 3); i++) { /* busy wait */
}
digitalWrite(LED_POWER, LED_STATE_OFF);
for (volatile int i = 0; i < DOT_TIME; i++) { /* busy wait */
}
}
static void space()
{
for (volatile int i = 0; i < (DOT_TIME * 3); i++) { /* busy wait */
}
}
// Disable optimizations for this function so "frame" argument
// does not get optimized away
extern "C" __attribute__((optimize("O0"))) void HardFault_Handler_C(sContextStateFrame *frame)
{
debug_printf("HardFault!\r\n");
debug_printf("r0: %08x\r\n", frame->r0);
debug_printf("r1: %08x\r\n", frame->r1);
debug_printf("r2: %08x\r\n", frame->r2);
debug_printf("r3: %08x\r\n", frame->r3);
debug_printf("r12: %08x\r\n", frame->r12);
debug_printf("lr: %08x\r\n", frame->lr);
debug_printf("pc[return address]: %08x\r\n", frame->return_address);
debug_printf("xpsr: %08x\r\n", frame->xpsr);
HALT_IF_DEBUGGING();
// blink SOS forever
while (1) {
dot();
dot();
dot();
dash();
dash();
dash();
dot();
dot();
dot();
space();
}
}