cancel
Showing results for 
Search instead for 
Did you mean: 

Keil debugger(ST-Link Debugger) question

PPopo
Senior

Hello,

I wrote an application in which I used an STM32F446 controller(from an STM-32 nucleo board) to configure an external interrupt using a push button.

This is my configuration:

  • enabled EXTI0 line and I'm using my push button connected to PA0 to generate falling edge interrupts
  • I have implemented the 'EXTI0_IRQHandler' function and confirmed that the code goes there and the interrupt gets processed properly.

However I still have one thing about the debugger which bugs me :). I have put few two breakpoints in the endless while(1) loop to see the flow of the code like this: one on line 47(int a = 1) one on line 49(in c = 3). Then one breakpoint in the ISR to make sure the code goes there when I press the button. If I cycle through the breakpoints by 'run'(f5) then everything works fine. However if I just step through the code in the while loop(by pressing f10) and then I press the button to trigger the falling edge interrupt I would expect the code to go to line 56(beginning of the ISR). However that does not happen. The interrupt request gets processed, the isr gets pendet however the code keeps cycling through the while loop instead of going to the ISR when I press f10. Why is this happening? Below is my 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)
	{
	 int a = 1;
	 int b = 2;
	 int c = 3;
	}
	
	return 0;
}
 
#if 1
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
	}
	 
}

Thank you for reading!

1 ACCEPTED SOLUTION

Accepted Solutions

Hi

Between two breakpoints, F10(step over) works as F11 (one line step) because every step is a single line and not a call to a function.

At end of each execution line step, debugger halts the mcu core.

All interrupts are asynchronous to instruction execution, and except reset interrupt the mcu can execute other instructions between a triggered interrupt and the execution of theISR.

So ISR will never take control if walk the code by one line step.

View solution in original post

6 REPLIES 6

Hi

Between two breakpoints, F10(step over) works as F11 (one line step) because every step is a single line and not a call to a function.

At end of each execution line step, debugger halts the mcu core.

All interrupts are asynchronous to instruction execution, and except reset interrupt the mcu can execute other instructions between a triggered interrupt and the execution of theISR.

So ISR will never take control if walk the code by one line step.

PPopo
Senior

Thanks!

Sorry I need to come back to this because I want to make sure I understood it correctly.

I'll state what I understood and please correct me if I'm wrong:

  • when single stepping in debugging the CPU is halted after every line of code
  • if I press the button to trigger an interrupt while the CPU is halted it would basically not see it because the CPU is in idle mode(so in theory if I could perfectly time the moment I press the button to trigger the interrupt with the moment I press F11 to single step through the code the program would go into the ISR routine).
  • if the two above are true then something doesn't make sense to me: why when I press the button to trigger the external interrupt in debug mode the pending bit is set(I checked that it is set with an if instruction in the main code) ? How can the CPU set a register to some value while it is in the halted(idle) state? This is how I checked the pending bit:

if( (EXTI->PR) & 0x01 )
	{
	  EXTI->PR |= 1;              // clear the bit
	}

I halted the processor(stopped at some line in the code) pressed button to trigger external interrupt and then single stepped further into the code where I had the above snippet. The code went inside the while and I was expecting it to not go as the interrupt button was pressed while CPU was halted.

Also one more thing:

  • are software triggered exception asynchronous to the program execution as well? shouldn't they work as normal call to functions and therefore shouldn't we be able to single step into them while debugging?

Thanks!

PPopo
Senior

Sorry I need to come back to this because I want to make sure I understood it correctly.

I'll state what I understood and please correct me if I'm wrong:

  • when single stepping in debugging the CPU is halted after every line of code
  • if I press the button to trigger an interrupt while the CPU is halted it would basically not see it because the CPU is in idle mode(so in theory if I could perfectly time the moment I press the button to trigger the interrupt with the moment I press F11 to single step through the code the program would go into the ISR routine).
  • if the two above are true then something doesn't make sense to me: why when I press the button to trigger the external interrupt in debug mode the pending bit is set(I checked that it is set with an if instruction in the main code) ? How can the CPU set a register to some value while it is in the halted(idle) state? This is how I checked the pending bit:

if( (EXTI->PR) & 0x01 )
	{
	  EXTI->PR |= 1;              // clear the bit
	}

I halted the processor(stopped at some line in the code) pressed button to trigger external interrupt and then single stepped further into the code where I had the above snippet. The code went inside the while and I was expecting it to not go as the interrupt button was pressed while CPU was halted.

Also one more thing:

  • are software triggered exception asynchronous to the program execution as well? shouldn't they work as normal call to functions and therefore shouldn't we be able to single step into them while debugging?

Thanks!

Hi.

All these are really difficult questions when you use SWD . (SWD communication manipulated by the host)

SWD host decides when to read a register, when to halt the core by stopping the PC. And don't forget that the clock is limited to 4MHZ for STink v2 (on board) to catch or manage everything . .

Also, in some cases by reading a register (eg a SR register) is enough to change this register. (invasive).

Registers can change even the core is halted.(the clocks are not halted although there are some options about it).

There are debuggers using ETM that are capable to run the program cycle by cycle but are very expensive to used for learning purpose.

"are software triggered exception asynchronous to the program execution as well?"

if you mean SVCall exception , this is synchronous.

I suggest to read the cortexM manuals to understand how the interrupt mechanism works instead of trying to understand by using a SW Debugger.

allright. thanks very much for your answers!