cancel
Showing results for 
Search instead for 
Did you mean: 

How can I reduce the time for interrupt callback function execution?

ki01
Associate II

I have this timer that is configured to generate an interrupt after every 15.625 µs, i.e., when it’s period elapses. The timer elapsed callback function contains the code below. The requirement is that new data be transmitted via SPI every 15.625 µs but a single iteration of this callback function is taking 17.95 µs currently. The clock speeds are at the maximum frequency I can achieve with the internal clock. I’ve also tried to reduce the length of the code as much as I can (There’s no code in while (1)). I believe if I use an external oscillator to achieve a higher SYSCLK frequency, the callback execution time will reduce. I wanted to know if there’s anything else I can try before redesigning the board.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

       if (htim == &htim2)  {

              DAC_EN;

              HAL_SPI_Transmit(&hspi1, &wave_data[index], 3, 1);

              DAC_DIS;

              index += 3;

              if (index >= 1535)

              {

              index = 0;

              }

}

}
18 REPLIES 18

I did end up writing my own function, but not from scratch, just a modified version of the HAL function. It got the job done. Reduced the time by 75%.

The interrupt and DMA versions of the HAL SPI command have an even longer execution time. I realized that with my time requirements there was no option but to write my own function instead of using the HAL function.

This was my first question on the form so I'm not too familiar with the etiquettes.  Also, to be honest, I forgot I even posted this question 😬.


@ki01 wrote:

The interrupt and DMA versions of the HAL SPI command have an even longer execution time. I realized that with my time requirements there was no option but to write my own function instead of using the HAL function.


And did you implement your custom function, which I assume you still call from an ISR context, blocking?

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

Yes, it's in blocking mode.

BarryWhit
Senior III

Yes, it's in blocking mode.

Then you're throwing away most of the benefits of using an interrupt in the first place.

 

You might as well just read the timer in a tight loop inside the main while loop. It would probably even be (slightly) faster, since you're avoiding the overhead involved with invoking and returning from an ISR. 

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

This is my first STM project so I am not very familiar with all the possibilities it offers. Do you mind elaborating on how I can use interrupts more efficiently?


@ki01 wrote:

This is my first STM project so I am not very familiar with all the possibilities it offers. Do you mind elaborating on how I can use interrupts more efficiently?


Sure. The point of using interrupts is to reduce the load on the CPU by handling events as needed instead of the MCU polling for them. You're doing that. But you've also placed very time-consuming blocking code in your callback. That means the MCU spends virtually all its time inside the interrupt callback - which is upside-down. While the MCU is inside an interrupt callback, lower-priority interrupts are not serviced. That means response times to other interrupts will suffer. In the worst case, other interrupts will not be serviced at all. Also, if you ever need your MCU to do any work except send this data over SPI, this way of doing it is very limiting.

 

In general, you want to keep interrupt callbacks as short as possible. In your case, the standard way to accomplish your goal would be to use the interrupt callback to fire off a DMA transfer from memory into SPI, and then immediately return. On the next callback, verify that the previous transfer has completed (it must, unless you've designed your system poorly - see my previous reply), and then fire off the next chunk transfer to the SPI peripheral. 

 

That way, instead of near 100% MCU load, The MCU will have something around 0% load. Instead of wasting MCU cycles waiting for the SPI transfer to complete so you can send the next byte, you offload that work to dedicated hardware (the DMA peripheral). Your MCU will now just be sitting around in the main while() loop doing nothing most of the time. This allows your MCU to do other work that needs to be done while the transfer is ongoing or, if there isn't any. you could have it switch to a low-power mode.

 

But OK,  in this project you may not need your MCU to do anything but transfer data to the SPI. As this things go, you'll probably want to add do more eventually even if you don't think so now,, but say you don't. Say that, as long as it works, you don't care if the MCU spends 100% of it's time just doing block SPI calls. In that case, there was no need to use interrupts - it may even be (slightly) less efficient. You could have just done your blocking sends inside the main loop, and then polled with a tight loop, waiting for the moment when you want to start sending over SPI (preferably still using a timer peripheral in time-base mode and reading its registers, but just polling on the global tick counter which has 1ms resolution may even be enough).

 

If this is your first project, getting anything to work is an accomplishment. But if you plan to do more projects, you might as well use this one to learn how to use the hardware efficiently, and how to design your software properly to do so. If you don't, and you plan to do more projects in the future, you'll soon find that your current approach, while being simplest, is also a dead-end. And once you have several more interrupts sources active, you might end up creating surprising and difficult-to-debug issues bugs (like lower-priority interrupt callbacks not getting called).

 

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

Thank you very much for the detailed response. I understand what you're saying. I will give both the DMA and the main loop approach a try.