cancel
Showing results for 
Search instead for 
Did you mean: 

Interrupts and I2C Peripherals

MMora.7
Associate II

Hello all! My name is Matt and I am a university student studying EE. For school, I recently designed a board with an STM32405RG MCU and BMI088 IMU. Part of the project is gathering data from the gyroscope and processing it every time step.

My original plan was to use the gyroscope in polling mode as fast as possible, and then use a timer interrupt to send out important data over serial at ~50 Hz. Before implementing this timer interrupt, the code worked perfectly fine and everything seemed ok. However, as soon as I enable the timer interrupt, my code only runs for a few milliseconds before something calls the Error_Handler().

I am unsure why this is happening. My intuition tells me that the interrupt may be interfering with stuff that HAL is doing to talk to the I2C peripheral. If I want to send out data periodically over serial, does this mean that I also must use the gyroscope in interrupt mode, instead of polling mode? Would I need to set interrupt priorities in some specific if I chose to do this?

A high level description of the code is:

while(1)

{

  1. Read gyroscope data (in polling mode)
  2. Process gyroscope data
  3. Update internal state

}

50Hz_Interrupt_Handler()

{

  1. Read internal state
  2. Send internal state over serial

}

Please forgive any amateur coding mistakes. As I said, I am a university student doing this for the first time 🙂

Thank you for any help!

Matt

1 ACCEPTED SOLUTION

Accepted Solutions

Cannot tell the root cause immediately, but some hints: BMI088_I2C_Read_Gyro probably uses a blocking HAL function. HAL relies on the SysTick interrupt which is also used by HAL_Delay. So, it is not advisable using those functions in an interrupt. You can put everything in the main loop. There is a HAL_GetTick() function providing the millisecond SysTick count which you could use to synchronize everything.

If you want to roll your own timer for that, use a volatile global variable to sync timer interrupt and main loop code.

Finally, set a breakpoint at error_Handler and find you how the code came there

hth

KnarfB

View solution in original post

8 REPLIES 8
KnarfB
Principal III

Don't see an interrupt handler in your code?

Hi. Thanks for the answer!

The interrupt handler is the PeriodElapsedCallback near the end of the file.

Since posting the question, I have changed my code so that there is no code in the main while loop and everything is handled by the GPIO EXTI interrupt which is connected to the sensor data_ready interrupt. I have set the GPIO interrupt to a higher priority than the serial update timer interrupt.

I have verified that the GPIO interrupt routine takes less time than the data period. However, for some reason increasing the data rate causes the Error_Handler to be called every now and then, seemingly sporadically. Now I am really confused because the problem is not consistent. What could be going on?

Cannot tell the root cause immediately, but some hints: BMI088_I2C_Read_Gyro probably uses a blocking HAL function. HAL relies on the SysTick interrupt which is also used by HAL_Delay. So, it is not advisable using those functions in an interrupt. You can put everything in the main loop. There is a HAL_GetTick() function providing the millisecond SysTick count which you could use to synchronize everything.

If you want to roll your own timer for that, use a volatile global variable to sync timer interrupt and main loop code.

Finally, set a breakpoint at error_Handler and find you how the code came there

hth

KnarfB

Would like to add a bit to the advise of KnarfB:

GCC compiler optimizes functions that don't return by optimizing out the return address.

This is why you don't see the caller or Error_Handler in the call stack. Besides of a breakpoint, add something more to foil the optimization, for example:

volatile int g_errorRet = 0;
void ErrorHandler()
{
  __BKPT(0);
 while (0 == g_errorRet ) {}
// compiler cannot know that it won't be set ever
}

Thanks for the replies!

@Pavel A.​  That is very interesting! Thanks for the info. I will keep this is mind when debugging in the future

@KnarfB​  Are you suggesting that instead of doing the processing in the interrupt handler, I put all the code in the main loop but only execute it if I set some global "data_ready" variable in the interrupt handler?

I am also wondering about the other HAL_I2C modes... The documentation says that HAL_I2C_Mem_Read_IT is non-blocking, but I never got this to work.

> The documentation says that HAL_I2C_Mem_Read_IT is non-blocking

Yes, for STM32F4 it looks non-blocking and almost OK to call from ISR , from the source. But since you could not make it work and time is pressing, this variant is safer:

> put all the code in the main loop but only execute it if I set some global "data_ready" variable in the interrupt handler

-- pa

Awesome. I rewrote the code this way and it all seems to be working. Sounds like good practice is to never put blocking code inside interrupt handlers!

Thank you to both of you for the fast replies and very useful information! 🙂

Matt

Just use this CMSIS defined macro for not optimizing away a function:

https://github.com/ARM-software/CMSIS_5/blob/0cf87995c207cce59458b30a783e1b3bde30b27a/CMSIS/Core/Include/cmsis_gcc.h#L56

> compiler optimizes functions that don't return

Most likely it's not because it has no return statement, but just because the function is small.