Hello everyone,
I am currently porting ChibiOS (version 21.11) to a custom board based on the T-Motor H743VIT6 flight controller. I am encountering an issue where the board immediately goes into what appears to be a HardFault or kernel panic (indicated by a solid red LED) right after flashing and booting.
Hardware Status:
The hardware is 100% functional. I have tested a baremetal application (.elf), and it runs perfectly on the board (blue LED stays on, USB CDC correctly enumerates on COM4).
Current Configuration & Checks Performed:
HSE Clock: Configured to 8 MHz (#define STM32_HSECLK 8000000U).
Power Supply: LDO enabled (PWR_CR3_LDOEN is set), as this board does not use an external SMPS.
Memory/Linker: The original bootloader was removed. I am flashing the ChibiOS .bin directly to 0x08000000 via STM32CubeProgrammer. The linker script has been adjusted accordingly: flash0 (rx) : org = 0x08000000, len = 2M.
Observations:
Flashing the .bin file to 0x08000000 is successful, but booting results in an immediate lockup (solid red LED).
Attempting to flash the .elf file via CubeProgrammer throws a "Failed to download the file" error, likely due to how STM32CubeProgrammer handles the DTCM/RAM sections defined in the ChibiOS linker for the H7.
Due to forum attachment limits, I have attached a zip file containing my specific project and board setup:
RT-TMOTOR-H743VIT6 (Project / demo source files and mcuconf.h, halconf.h, .ld script)
TMOTOR_H743VIT6 (Board configuration files: board.c, board.h, board.mk)
Could anyone point out what I might be missing in my PLL/clock configuration or the H7 startup sequence?
Thank you in advance for your help!
Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
- Attachments
-
- TMOTOR_H743VIT6.zip
- (13.13 KiB) Downloaded 55 times
-
- RT-TMOTOR-H743VIT6.zip
- (112.87 KiB) Downloaded 41 times
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
Hi, and thanks for the detailed report and the attached project.
The clock configuration in the attached mcuconf.h is internally inconsistent, and that is almost certainly the cause. Two settings fight each other:
In fact, when I build the attached project as-is, it does not compile — it stops at:
because the USB clock resolves to 0. So the .bin that locks up on your board cannot have been built from this exact mcuconf.h. Which ChibiOS 21.11.x point release are you on (21.11.4 / .5 / .6 / ...), and is the attached mcuconf.h the one that actually produced the flashed binary, or was it edited afterwards? That matters — the USB clock check and a few clock-tree details differ slightly between point releases.
The fix for the inconsistency is straightforward:
Your PLL1 dividers are otherwise fine for what you intend (HSE 8 MHz, DIVM1 1, DIVN1 60 -> 480 MHz VCO, DIVP1 2 -> 240 MHz SYSCLK, DIVQ1 10 -> 48 MHz for USB), and VOS scale 1 is correct for 240 MHz. Rather than debugging the hand-edited file, I'd strongly suggest starting from the stock mcuconf.h of the ST_NUCLEO144_H743ZI board (or a testhal H7 demo) and changing only what your board actually needs — it is a known-good H743 clock tree.
A few more things to check / try:
Regards
The clock configuration in the attached mcuconf.h is internally inconsistent, and that is almost certainly the cause. Two settings fight each other:
- STM32_PLL1_ENABLED is set to FALSE
- STM32_SW is set to STM32_SW_HSI_CK
In fact, when I build the attached project as-is, it does not compile — it stops at:
Code: Select all
#error "the USB USBv1 driver requires a 48MHz clock"The fix for the inconsistency is straightforward:
Code: Select all
#define STM32_PLL1_ENABLED TRUE
#define STM32_SW STM32_SW_PLL1_P_CKA few more things to check / try:
- HSE: crystal or external clock? Your board.h has STM32_HSECLK 8000000 and does not define STM32_HSE_BYPASS, i.e. it expects a crystal on OSC_IN/OSC_OUT. Many flight-controller boards instead feed an external clock/TCXO into OSC_IN — in that case you must add #define STM32_HSE_BYPASS, otherwise HSE never starts and the clock init spins forever (which looks exactly like an immediate lockup). Match whatever your working baremetal build uses.
- Isolate clock vs USB. Temporarily disable USB (HAL_USE_USB and HAL_USE_SERIAL_USB = FALSE in halconf.h, and remove the USB calls from main.c), enable PLL1 as above, and just blink an LED. If it blinks, the clock tree is good and the remaining issue is on the USB side; if it is still dead, the problem is HSE / power / clock.
- LED pin. Your main.c toggles PAL_LINE(GPIOE, 3U), but board.h defines LED1 = PE5 and LED2 = PE4 — PE3 is not one of the board LEDs, so even once it boots you may be driving the wrong pin.
Regards
Re: Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
Hi Giovanni,
You are absolutely right. I apologize for the confusing mcuconf.h. I forgot to revert it back from HSI to HSE before zipping the folder, because I was attempting a "Safe Mode" (bypassing the PLL entirely) just to see if it would boot, which obviously caused the USB clock errors you noticed.
I have reverted the clock configuration back to normal:
Important Note on Flashing & Debugging:
Before continuing, I should mention my flashing method. I am uploading the .bin via USB DFU using STM32CubeProgrammer. The reason is that this flight controller board does not have exposed SWDIO and SWCLK pads, meaning I cannot use an ST-Link. Because of this lack of SWD access, I am completely blind during the boot process and cannot simply attach a hardware debugger to step through hal_init() to see exactly where it hangs.
To answer your questions and share the results of the tests you suggested:
1. HSE: crystal or external clock?
I was initially confused about this. My working baremetal code used RCC_OscInitStruct.HSEState = RCC_HSE_ON; (which implies a regular crystal). However, as you suggested, I generated a new board.h via chcfg with HSEBypass="true" to explicitly add #define STM32_HSE_BYPASS. Unfortunately, whether bypass is defined or not, the result is the exact same.
2. Isolate clock vs USB
I completely disabled USB to isolate the issue:
Set HAL_USE_USB and HAL_USE_SERIAL_USB to FALSE in halconf.h.
Removed all USB-related calls and threads from main.c.
It compiled perfectly with no errors. However, after flashing the .bin, the board still results in an immediate lockup (solid red LED). This confirms your suspicion: the remaining issue is definitely on the HSE / power / clock initialization side, not the USB.
3. LED pin
You were spot on about PE3. I checked the ArduPilot hwdef.dat for the TMOTORH743 and found this:
There are 3 LEDs on the board. The red one seems to be a hardware/boot fault indicator, while the blue one is programmable. I changed main.c to toggle PAL_LINE(GPIOE, 4U) and 5U (even tried setting them constantly ON). But since the board locks up with the Red LED immediately on boot, it never reaches main() anyway.
Version Info:
I am currently on ChibiOS 21.11.1 (based on the readme.txt).
Since isolating the USB and fixing the PLL still results in a lockup during the clock initialization phase, do you have any other insights on what might be causing the clock tree/power init to spin forever on this specific H7 board?
I have attached the updated project (RT-TMOTOR-H743VIT6, Project / demo source files) and (TMOTOR_H743VIT6, Board configuration files) which includes the corrected mcuconf.h & board.h, along with the modified halconf.h and main.c (USB disabled, LED pins updated) so you have the exact current state.
Thank you so much for your time and guidance!
You are absolutely right. I apologize for the confusing mcuconf.h. I forgot to revert it back from HSI to HSE before zipping the folder, because I was attempting a "Safe Mode" (bypassing the PLL entirely) just to see if it would boot, which obviously caused the USB clock errors you noticed.
I have reverted the clock configuration back to normal:
Code: Select all
#define STM32_PLL1_ENABLED TRUE
#define STM32_SW STM32_SW_PLL1_P_CK
Before continuing, I should mention my flashing method. I am uploading the .bin via USB DFU using STM32CubeProgrammer. The reason is that this flight controller board does not have exposed SWDIO and SWCLK pads, meaning I cannot use an ST-Link. Because of this lack of SWD access, I am completely blind during the boot process and cannot simply attach a hardware debugger to step through hal_init() to see exactly where it hangs.
To answer your questions and share the results of the tests you suggested:
1. HSE: crystal or external clock?
I was initially confused about this. My working baremetal code used RCC_OscInitStruct.HSEState = RCC_HSE_ON; (which implies a regular crystal). However, as you suggested, I generated a new board.h via chcfg with HSEBypass="true" to explicitly add #define STM32_HSE_BYPASS. Unfortunately, whether bypass is defined or not, the result is the exact same.
2. Isolate clock vs USB
I completely disabled USB to isolate the issue:
Set HAL_USE_USB and HAL_USE_SERIAL_USB to FALSE in halconf.h.
Removed all USB-related calls and threads from main.c.
It compiled perfectly with no errors. However, after flashing the .bin, the board still results in an immediate lockup (solid red LED). This confirms your suspicion: the remaining issue is definitely on the HSE / power / clock initialization side, not the USB.
3. LED pin
You were spot on about PE3. I checked the ArduPilot hwdef.dat for the TMOTORH743 and found this:
Code: Select all
PE5 LED1 OUTPUT LOW GPIO(91)
PE4 LED2 OUTPUT LOW GPIO(92)
Version Info:
I am currently on ChibiOS 21.11.1 (based on the readme.txt).
Since isolating the USB and fixing the PLL still results in a lockup during the clock initialization phase, do you have any other insights on what might be causing the clock tree/power init to spin forever on this specific H7 board?
I have attached the updated project (RT-TMOTOR-H743VIT6, Project / demo source files) and (TMOTOR_H743VIT6, Board configuration files) which includes the corrected mcuconf.h & board.h, along with the modified halconf.h and main.c (USB disabled, LED pins updated) so you have the exact current state.
Thank you so much for your time and guidance!
- Attachments
-
- TMOTOR_H743VIT6 (2).7z
- (7.31 KiB) Downloaded 55 times
-
- RT-TMOTOR-H743VIT6 (2).7z
- (5.29 MiB) Downloaded 129 times
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
I will follow up, in the meanwhile consider updating to the latest 21.11.5, that version is very old.
Giovanni
Giovanni
Re: Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
Hi Giovanni,
Quick update covering everything since my last post:
Re: the STM32_VOS_SCALE0 question — I dug into hal_lld_type1.h and hal_lld.c directly and I think I've answered my own question, so no need to spend time on that. Overdrive turns out to be fully automatic in this driver, not a manual setting:
written unconditionally via SYSCFG->PWRCR = STM32_ODEN; in init_pwr(). At my 240 MHz target this correctly resolves to DISABLED, so VOS1 (unchanged from your original review) is correct and Overdrive isn't relevant here. I also checked init_pwr()'s sequencing against RM0433 (PWR_CR3 lower byte -> wait ACTVOSRDY -> full CR1/CR2/CR3/CPUCR -> D3CR=VOS -> ODEN -> wait D3CR VOSRDY) and it matches the required order, and PLL1RGE/VCOSEL/flash-latency are all auto-derived from the actual computed frequencies rather than manual macros. I don't see a bug in the driver logic against my config.
Given that, I tried to at least bisect where the lockup happens using raw register writes (no SWD available — this board has no exposed debug pads). As the very first instruction in __early_init(), before stm32_gpio_init() or stm32_clock_init() run at all:
(addresses/bit math double-checked against the H743 memory map, they're correct). Even with this at the absolute earliest possible point, before any HAL/clock code runs, the board still shows solid red with no visible change.
The complication: this is the VIT6 (LQFP100) package, so I can't practically probe individual MCU pins with a multimeter to verify the GPIO write actually happened versus the CPU never reaching that code at all — 0.5mm pitch, no easy access.
Given no SWD and a package that's impractical to hand-probe, what would you suggest as the lowest-effort way to get some signal out of the board at this point? I have a working USB CDC path in my baremetal build and accessible UART pins on connectors (not just LEDs) if either of those would be more useful as a diagnostic than continuing to guess blind.
Thanks for bearing with this one, it's been a stubborn bug.
Quick update covering everything since my last post:
Re: the STM32_VOS_SCALE0 question — I dug into hal_lld_type1.h and hal_lld.c directly and I think I've answered my own question, so no need to spend time on that. Overdrive turns out to be fully automatic in this driver, not a manual setting:
Code: Select all
#if STM32_SYS_CK > STM32_SYSCLK_MAX_NOBOOST // 400,000,000
#define STM32_ODEN STM32_ODEN_ENABLED
#else
#define STM32_ODEN STM32_ODEN_DISABLED
#endif
Given that, I tried to at least bisect where the lockup happens using raw register writes (no SWD available — this board has no exposed debug pads). As the very first instruction in __early_init(), before stm32_gpio_init() or stm32_clock_init() run at all:
Code: Select all
*((volatile uint32_t *)0x580244E0) |= (1U << 4); // RCC_AHB4ENR |= GPIOEEN
*((volatile uint32_t *)0x58021000) &= ~(0xFU << 8);
*((volatile uint32_t *)0x58021000) |= (0x5U << 8); // PE4,PE5 -> output
*((volatile uint32_t *)0x58021018) = (3U << (4 + 16)); // drive PE4,PE5 low (active-low LED test)
The complication: this is the VIT6 (LQFP100) package, so I can't practically probe individual MCU pins with a multimeter to verify the GPIO write actually happened versus the CPU never reaching that code at all — 0.5mm pitch, no easy access.
Given no SWD and a package that's impractical to hand-probe, what would you suggest as the lowest-effort way to get some signal out of the board at this point? I have a working USB CDC path in my baremetal build and accessible UART pins on connectors (not just LEDs) if either of those would be more useful as a diagnostic than continuing to guess blind.
Thanks for bearing with this one, it's been a stubborn bug.
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: Immediate HardFault/Crash on Boot - Custom STM32H743VIT6 Board
Hi,
Without board it is hard, I gave a try to the latest GTP6 (Max), this is what it has to say:
-------------
Hi,
I'm Giovanni's AI assistant. He asked me to review this discussion and suggest some targeted checks. I reviewed the August 30 attachments, including the ELF; the observations below are based on that review, not on testing your hardware.
Your conclusion about overdrive is correct: it is handled automatically, and it is not needed for your 240 MHz configuration.
There are two configuration points worth checking before going further.
1. LSE is still enabled
Your mcuconf.h enables LSE, and board.h declares a 32768 Hz oscillator. Does this board actually have a 32.768 kHz crystal?
This matters because stm32_clock_init() calls init_bkp_domain(), which waits indefinitely for LSERDY when LSE is enabled. This happens before HSE/PLL initialization and also happens with HAL_USE_RTC = FALSE. The wait is present in your attached ELF.
If the board does not have an LSE crystal, try:
This is separate from the 8 MHz HSE oscillator.
2. HSE bypass is still defined in the attached board file
Since your working bare-metal firmware uses RCC_HSE_ON, I would retain that same crystal configuration and remove STM32_HSE_BYPASS. Giovanni's earlier suggestion about bypass was conditional on an external clock actually being fitted, not a recommendation to enable it generally.
Remove the definition entirely; defining it as FALSE would still select bypass because the driver checks whether the macro exists.
Regarding the LED test, a solid red LED does not by itself establish a HardFault. The attached exception handler simply loops; it does not implement a red fault indication. Also, the normal GPIO initialization resets and reconfigures the ports, and already drives PE4/PE5 low. Your additional writes therefore do not provide an unambiguous indication of where execution stopped.
I would suggest the following test sequence:
1. First validate the LED pin and its operation using your working bare-metal project.
2. Put a continuous GPIO blink loop at the beginning of __early_init(), using that known-working pin setup. Do not continue into either initialization function. Use a simple volatile busy-wait delay, not SysTick or an RTOS sleep. Include a readback of the RCC clock-enable register before accessing GPIO registers.
3. If that blinks, move the diagnostic loop after stm32_gpio_init(), then after stm32_clock_init(), testing each position separately.
4. If it stops across clock initialization, narrow it down around power initialization, backup-domain initialization, and the oscillator/PLL ready waits.
Note that __early_init() is early, but it is not the reset entry point: stack/VTOR setup and __cpu_init() precede it. If the first blink test fails, verify the flashed image and boot mode, and test after a full power cycle with the boot button released.
For another diagnostic channel, a polled UART clocked from HSI would be useful. It can emit checkpoint characters without interrupts or RTOS services. I would leave USB CDC aside until startup is working. An accessible UART connector pin can also serve as a temporary GPIO test point, avoiding any need to probe the MCU legs.
Please confirm the ChibiOS version you are now using and attach the current sources and matching ELF/BIN after a clean rebuild. The available attachments predate your latest diagnostic changes.
Regards,
Giovanni's AI assistant
Without board it is hard, I gave a try to the latest GTP6 (Max), this is what it has to say:
-------------
Hi,
I'm Giovanni's AI assistant. He asked me to review this discussion and suggest some targeted checks. I reviewed the August 30 attachments, including the ELF; the observations below are based on that review, not on testing your hardware.
Your conclusion about overdrive is correct: it is handled automatically, and it is not needed for your 240 MHz configuration.
There are two configuration points worth checking before going further.
1. LSE is still enabled
Your mcuconf.h enables LSE, and board.h declares a 32768 Hz oscillator. Does this board actually have a 32.768 kHz crystal?
This matters because stm32_clock_init() calls init_bkp_domain(), which waits indefinitely for LSERDY when LSE is enabled. This happens before HSE/PLL initialization and also happens with HAL_USE_RTC = FALSE. The wait is present in your attached ELF.
If the board does not have an LSE crystal, try:
Code: Select all
#define STM32_LSE_ENABLED FALSE
#define STM32_RTCSEL STM32_RTCSEL_NOCLK
#define STM32_CECSEL STM32_CECSEL_DISABLE
2. HSE bypass is still defined in the attached board file
Since your working bare-metal firmware uses RCC_HSE_ON, I would retain that same crystal configuration and remove STM32_HSE_BYPASS. Giovanni's earlier suggestion about bypass was conditional on an external clock actually being fitted, not a recommendation to enable it generally.
Remove the definition entirely; defining it as FALSE would still select bypass because the driver checks whether the macro exists.
Regarding the LED test, a solid red LED does not by itself establish a HardFault. The attached exception handler simply loops; it does not implement a red fault indication. Also, the normal GPIO initialization resets and reconfigures the ports, and already drives PE4/PE5 low. Your additional writes therefore do not provide an unambiguous indication of where execution stopped.
I would suggest the following test sequence:
1. First validate the LED pin and its operation using your working bare-metal project.
2. Put a continuous GPIO blink loop at the beginning of __early_init(), using that known-working pin setup. Do not continue into either initialization function. Use a simple volatile busy-wait delay, not SysTick or an RTOS sleep. Include a readback of the RCC clock-enable register before accessing GPIO registers.
3. If that blinks, move the diagnostic loop after stm32_gpio_init(), then after stm32_clock_init(), testing each position separately.
4. If it stops across clock initialization, narrow it down around power initialization, backup-domain initialization, and the oscillator/PLL ready waits.
Note that __early_init() is early, but it is not the reset entry point: stack/VTOR setup and __cpu_init() precede it. If the first blink test fails, verify the flashed image and boot mode, and test after a full power cycle with the boot button released.
For another diagnostic channel, a polled UART clocked from HSI would be useful. It can emit checkpoint characters without interrupts or RTOS services. I would leave USB CDC aside until startup is working. An accessible UART connector pin can also serve as a temporary GPIO test point, avoiding any need to probe the MCU legs.
Please confirm the ChibiOS version you are now using and attach the current sources and matching ELF/BIN after a clean rebuild. The available attachments predate your latest diagnostic changes.
Regards,
Giovanni's AI assistant