2023-12-03 08:21 PM - edited 2023-12-03 08:23 PM
I'm trying to get multiple timers with interrupts working on an STM32L462 using the provided HAL libraries in CubeIDE 1.14, but every time the chip comes out of reset, it crashes after starting the second timer. Here is the relevant code:
int main(void) {
MX_TIM15_Init();
MX_TIM2_Init();
...
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim15);
...
}
// TIMER Interrupts
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) {
events.HEARTBEAT = 1;
}
if (htim->Instance == TIM15) {
events.DEBOUNCE = 1;
}
}
I can't this to work. Only when I disable the second interrupt or start the timer without interrupt will the chip start. I tried stepping through and it just gets stuck stepping indefinitely. This other topic seems to indicate it is possible, but I don't know how. Thinking about how the handler is set up, how could htim->Instance be two different values. That's impossible. So after more reading, I did try enabling 'Register Callbacks' configuration for timers, and then calling HAL_TIM_RegisterCallback before calling HAL_TIM_Base_Start_IT for each timer. It still didn't work, and stops when starting the second timer still. I don't understand what could be going on. Anyone ever get this setup? Thanks for any advice.
Solved! Go to Solution.
2023-12-04 05:38 AM - edited 2023-12-04 05:39 AM
There are no issues with the code you presented. The problem lies elsewhere.
> I tried stepping through and it just gets stuck stepping indefinitely.
> Anyway, it still doesn't explain why it locks up as it doesn't even make it past the init phase before the main while loop.
Perhaps your timers are running too fast and swamping the chip with interrupts. "Stuck stepping" through where exactly? Interrupts?
2023-12-03 09:25 PM
htim will depend on the value you pass in to the HAL via the IRQHandler
2023-12-03 09:46 PM
Yes, understood. That seems to contradict this statement though. So you should have no trouble with if/else or even a switch statement. Anyway, it still doesn't explain why it locks up as it doesn't even make it past the init phase before the main while loop. I don't know if there's some other setting needed I overlooked.
2023-12-04 05:38 AM - edited 2023-12-04 05:39 AM
There are no issues with the code you presented. The problem lies elsewhere.
> I tried stepping through and it just gets stuck stepping indefinitely.
> Anyway, it still doesn't explain why it locks up as it doesn't even make it past the init phase before the main while loop.
Perhaps your timers are running too fast and swamping the chip with interrupts. "Stuck stepping" through where exactly? Interrupts?
2023-12-04 02:56 PM
Yes, that was it. I misconfigured it for us instead of ms. Very cool. Seems that the stepping issue indicates an overloaded cpu. I'll have to remember that in further debugging. I thought it was an issue with the board layout to the programming header.
I did not realize there's quite a bit of overhead in the IRQ handler using the HAL library. Are there any good examples of using interrupts without the HAL overhead? I think interrupts should call directly to my own handler without all those callback type checks to keep interrupt processing very quick. Of course, I'd still want to use HAL for higher-level complex operations such as SDIO, SPI, etc. Thanks in advance.
2023-12-04 04:05 PM
The checks are necessary to call the right callbacks. There's really not a ton of overhead in the handlers, there's just a lot of potential cases. Look at the callback for update:
/* TIM Update event */
if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
{
if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
htim->PeriodElapsedCallback(htim);
#else
HAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
}
}
This is exactly how it should be written. If UIE and UF, clear the flag and call the callback. That's it. There is some small overhead for getting the timer instance from htim, but that's the price you pay to make it more general. If you knew beforehand that UIE would be set, maybe you don't have to check for that, but HAL can't make that assumption.
If you want to make your own, take only the relevant parts from the HAL code.
2023-12-04 06:25 PM
Ya you're right, it's not a huge difference, but looking at HAL_TIM_IRQHandler there are an extra 5 unnecessary if statements before calling the update callback since I'm only using the update callback. Do you know of a simple way to override HAL_TIM_IRQHandler or even TIM1_UP_TIM16_IRQHandler so I could call my handler directly (without having to replace code every time I run CubeMX etc)? Looking to do this still within the HAL environment.
Coming from working Microchip MCU's this is very different, where you have direct control over the interrupt handler functions. The HAL libraries are a huge benefit, but at least for interrupt logic, I'd personally rather sacrifice flexibility for performance, especially at lower clocks.
2023-12-04 07:39 PM
You have direct control over TIM1_UP_TIM16_IRQHandler. It's part of user code. If you don't want HAL_TIM_IRQHandler, hijack it there, check your flags, do whatever, and skip out on calling HAL_TIM_IRQHandler.
2023-12-04 10:09 PM
Ya, nice, got it. I had to surround the generated code with my own macro conditional like this:
void TIM1_UP_TIM16_IRQHandler(void)
{
/* USER CODE BEGIN TIM1_UP_TIM16_IRQn 0 */
// ADDED CODE
#if DIRECT_TIMER_CALLBACKS
__HAL_TIM_CLEAR_IT(&htim16, TIM_IT_UPDATE);
TMR_Debounce_Callback(&htim16);
#else
/* USER CODE END TIM1_UP_TIM16_IRQn 0 */
HAL_TIM_IRQHandler(&htim16);
/* USER CODE BEGIN TIM1_UP_TIM16_IRQn 1 */
#endif
/* USER CODE END TIM1_UP_TIM16_IRQn 1 */
}
:thumbs_up: