cancel
Showing results for 
Search instead for 
Did you mean: 

Program does not get into hard fault exception when expected to do so

PPopo
Senior

Hello,

If I understood correctly the a hard fault exception handler should catch all exceptions that do not have a specific handler that should execute when they are thrown. For example: if I enable button interrupts(say falling edge) and configure EXTI0 properly then I press the button(PA0) the cpu should try to find this ISR EXTI0_IRQHandler(). That works fine. But say I don't have that implemented. Shouldn't we end up in 'HardFault_Handler' then?

I'm using a STM32F446 controller from an STM32 nucleo board.

I wrote this code and tested if the code gets to 'EXTI0_IRQHandler' when I press the button. That works fine. I then commented out this ISR and expected the code to reach the hard fault handler isr when pressing the button. However that does not happen. The code stays stuck in the while loop. Please help me understand why that is happening and please correct me if I understood hard fault handlers in a wrong way. Thanks! This is the code:

#include "stm32f446xx.h"
 
#include "Board_LED.h"
 
#include "Board_Buttons.h"
 
 
 
// PA1(led) - output, push pull (SWITCH FROM BELOW(PA0) to PA1)
 
void leds_initialize()
 
{
 
  RCC->AHB1ENR |=  (1ul << 0);  /* Enable GPIOA clock         */
 
	// configure pa1(the led)
 
  GPIOA->MODER |= ( 1 << 2 ); 
 
  GPIOA->MODER &= ~( 1 << 3 );  
 
	GPIOA->OTYPER &= ~( 1 << 1 ); 
 
	// initially led is off
 
	GPIOA->ODR &= ~(1 << 1);
 
}
 
 
 
// button(pa0)
 
void buttons_initialize()
 
{
 
	// configure PA0(the button) - input/pull down
 
	GPIOA->MODER &= ~( 1 << 0 );
 
	GPIOA->MODER &= ~( 1 << 0 );
 
	
 
	
 
	GPIOA->OTYPER &= ~( 1 << 0 );
 
	
 
	GPIOA->PUPDR &= ~( 1 << 0 );
 
	GPIOA->PUPDR |= ( 1 << 1 );
 
	
 
	// configure the interrupt part
 
	SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PA; // enable PA0 as EXTI0(macro = 0x0000U)
 
	EXTI->IMR |= ( 1 << 0 );  // interrupt request from line 0 is NOT MASKED
 
	EXTI->RTSR &= ~( 1 << 0); // rising trigger disabled for external interrupt input line 0
 
	EXTI->FTSR |= ( 1 << 0 ); // falling trigger enabled for external interrupt input line 0
 
	
 
	/* ENABLE THE CORRESPONDING NVIC LINE WHERE EXTI0 LINE IS CONNECTED */
 
  NVIC->ISER[0] |= ( 1 << EXTI0_IRQn );
 
}
 
 
 
int main(void)
 
{
 
  leds_initialize();
 
	buttons_initialize();
 
		
 
	while(1)
 
	{
 
	 
 
	}
 
	
 
	return 0;
 
}
 
 
 
#if 0
 
void EXTI0_IRQHandler(void)
 
{
 
	// Check if the pending bit for exti0 is really set before processing the interrupt 
 
  if( (EXTI->PR) & 0x01 )
 
	{
 
	  EXTI->PR |= 1;              // clear the bit
 
		GPIOA->ODR ^=  ( 1 << 1 );  // toggle the LED
 
	}
 
	 
 
}
 
#endif 
 
 
 
#if 1
 
void HardFault_Handler(void)
 
{
 
    EXTI->PR |= 1;              // clear the bit
 
		GPIOA->ODR ^=  ( 1 << 1 );  // toggle the LED
 
}
 
#endif
 

Thank you for reading my post!

1 ACCEPTED SOLUTION

Accepted Solutions
David SIORPAES
ST Employee

Hello,

all handlers are defined as weak functions which default to an endless loop. If you do not override the handler this will default to it. This is usually in the toolchain specific assembler startup code.

These snippets for example come from the startup_stm32f030x6.s file for gcc created with CubeMX.

 0690X000008Ak5AQAS.png

0690X000008Ak5FQAS.png

View solution in original post

6 REPLIES 6
David SIORPAES
ST Employee

Hello,

all handlers are defined as weak functions which default to an endless loop. If you do not override the handler this will default to it. This is usually in the toolchain specific assembler startup code.

These snippets for example come from the startup_stm32f030x6.s file for gcc created with CubeMX.

 0690X000008Ak5AQAS.png

0690X000008Ak5FQAS.png

But I did overwrite the handler(please see my code line 133-143). The name of the function I overwritten is 'HardFault_Handler'. The code still doesn't go there. It stays in the main program loop even if the interrupt request was triggered.

Also about your remark about the infinite loop. The dummy handlers are indeed infinite loops. But if the hard fault exception triggers shouldn't the code go to there(in the startup code to the dummy handler)?

In lines 133-143 you overwrote the HardFault_Handler, not the EXTI0_IRQHandler (as stated, you commented it out).

Your assumption "If I understood correctly the a hard fault exception handler should catch all exceptions that do not have a specific handler" is not correct. If you do not implement a given handler, you will end up in the default handler of that given exception which happens to be a tight loop.

Thank you so much now all makes sense. One extra question if you don't mind :D.

In the code below I did the following:

  • overwritten HardFault handler
  • overwriten UsageFault handler

According to the documentation a division by 0 should be handled by a UsageFault(when trapping of division by 0 is allowed).

So in my code I created a function which attempts a division by 0. Now since I have not masked the UsageFault exception via 'Priority Mask Register'. Since UsageFault was not masked shouldn't the code get in that handler when I try to execute a division by 0? Instead it goes into the HardFault_Handler. From what I understood the code should first try to get into UsageFault_Handler and if that is not implemented or is masked it should only then go to HardFault_Handler. Why is that not the case in the following code:

#include "stm32f446xx.h"
 
int div(int a, int b)
 
{
 
	int result = a/b;
 
	
 
 return result;
 
}
 
 
 
int main(void)
 
{
 
 
 
	// trap divide by 0
 
	SCB->CCR |= ( 1 << 4 ); // trap divide by 0
 
	
 
	int c = div(10, 0);
 
	
 
	return 0;
 
}
 
 
 
#if 1
 
void HardFault_Handler(void)
 
{
 
  while(1);
 
}
 
 
 
void UsageFault_Handler(void)
 
{
 
  while(1);
 
}
 
 
 
#endif

Thank you again!

Not minding at all 🙂

UsageFault exception and friends (MemManage fault, Bus fault) need to be enabled beforehand otherwise they will escalate to HardFault.

To enable UsageFault:

SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk;

Thought they were enabled by default. Thank you!