Skip to main content
victagayun
Senior III
May 10, 2020
Solved

HAL_HRTIM_IRQHandler duration is very long

  • May 10, 2020
  • 11 replies
  • 3325 views

Hello I have NUCLEO-G474RE running at 170Mhz (24Mhz HSE [div 6] [mul 85] [div 2]) .

I am using Master and TimA $ TimC hrtimer as PWM for SMPS running at 100kHz (10uS).

I want to check how much available time for me if I interrupt every period and update the duty cycle using compare unit.

I add an IO to check the duration, it took around 7.44uS??

So that means I have only left around 2.65 uS for my code?

void HRTIM1_Master_IRQHandler(void)
{
 /* USER CODE BEGIN HRTIM1_Master_IRQn 0 */
 
 GPIOC->BSRR = (1<<10); // up
 
 /* USER CODE END HRTIM1_Master_IRQn 0 */
 HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);
 /* USER CODE BEGIN HRTIM1_Master_IRQn 1 */
 
 GPIOC->BSRR = (1<<26); // down +16
 
 /* USER CODE END HRTIM1_Master_IRQn 1 */
}

This topic has been closed for replies.
Best answer by berendi

> While CH3 (dark pink) is 7.64uS

> CH4 (dark blue) is 3.7uS

So the overhead is about 4 μs. Merge the second function into HRTIM1_Master_IRQHandler(), remove the call to HAL_HRTIM_IRQHandler(), clear the interrupt flag in HRTIM->MICR, and replace the call to __HAL_HRTIM_SETCOMPARE() with a direct write to the register (see the macro definition in the header). Now all references to &hhrtim1 should be gone from the interrupt handler. Repeat the measurement.

Untested code:

void HRTIM1_Master_IRQHandler(void)
{
 /* USER CODE BEGIN HRTIM1_Master_IRQn 0 */
 GPIOC->BSRR = (1<<10); // up
#if 0 
 /* USER CODE END HRTIM1_Master_IRQn 0 */
 HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);
 /* USER CODE BEGIN HRTIM1_Master_IRQn 1 */
#endif
 int32_t misr;
 misr = HRTIM1->sMasterRegs.MISR; // read interrupt status
 HRTIM1->sMasterRegs.MICR = misr; // clear all active interrupts
 if(misr & HRTIM_MISR_MREP) {
 int phase; // is the type correct?
 GPIOC->BSRR = (1<<11); // up
 phase = my_PID_Controller();
 CurrentPhase = phase;
 GPIOC->BSRR = (1<<27); // down +16
 HRTIM1->sMasterRegs.MCMP1R = phase; // don't access the (hopefully) volatile global variable again 
 }
 GPIOC->BSRR = (1<<26); // down +16
 /* USER CODE END HRTIM1_Master_IRQn 1 */
}

11 replies

berendi
Principal
May 10, 2020

Yes, HAL is slow. Interrupt handling in HAL is excruciatingly slow.

Use the registers directly as documented in the reference manual.

If you can calculate the duty cycle values in advance, then use DMA to update the comparator.

I find your execution times somewhat longer than I would have expected, but still plausible.

Check the latest version of the reference manual for updated flash latency settings.

victagayun
Senior III
May 10, 2020

0693W000000X9YcQAK.jpg

Thank you for the reply.

As shown above TimerA1&2 are CH1-2 (10uS).

While CH3 (dark pink) is 7.64uS :

void HRTIM1_Master_IRQHandler(void)
{
 /* USER CODE BEGIN HRTIM1_Master_IRQn 0 */
 
 GPIOC->BSRR = (1<<10); // up
 
 /* USER CODE END HRTIM1_Master_IRQn 0 */
 HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);
 /* USER CODE BEGIN HRTIM1_Master_IRQn 1 */
 
 GPIOC->BSRR = (1<<26); // down +16
 
 /* USER CODE END HRTIM1_Master_IRQn 1 */
}

and CH4 (dark blue) is 3.7uS:

void HAL_HRTIM_RepetitionEventCallback(HRTIM_HandleTypeDef * hhrtim,
 uint32_t TimerIdx)
{
 /* Prevent unused argument(s) compilation warning */
 UNUSED(hhrtim);
 UNUSED(TimerIdx);
 
 GPIOC->BSRR = (1<<11); // up
 
 CurrentPhase = my_PID_Controller();
 __HAL_HRTIM_SETCOMPARE(&hhrtim1, HRTIM_TIMERINDEX_MASTER, HRTIM_COMPAREUNIT_1, CurrentPhase); // min = 16
 
 GPIOC->BSRR = (1<<27); // down +16
 
}

waclawek.jan
Super User
May 10, 2020

Cube is open source, you can look up yourself what's going on, or even copy the given functions/macros to your code and rewrite them wherever needed, possibly avoiding the unnecessary function calls.

Switch on the compiler optimalizations. As berendi said above, review FLASH waitstates. Move the critical code parts to RAM.

JW

berendi
berendiBest answer
Principal
May 10, 2020

> While CH3 (dark pink) is 7.64uS

> CH4 (dark blue) is 3.7uS

So the overhead is about 4 μs. Merge the second function into HRTIM1_Master_IRQHandler(), remove the call to HAL_HRTIM_IRQHandler(), clear the interrupt flag in HRTIM->MICR, and replace the call to __HAL_HRTIM_SETCOMPARE() with a direct write to the register (see the macro definition in the header). Now all references to &hhrtim1 should be gone from the interrupt handler. Repeat the measurement.

Untested code:

void HRTIM1_Master_IRQHandler(void)
{
 /* USER CODE BEGIN HRTIM1_Master_IRQn 0 */
 GPIOC->BSRR = (1<<10); // up
#if 0 
 /* USER CODE END HRTIM1_Master_IRQn 0 */
 HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);
 /* USER CODE BEGIN HRTIM1_Master_IRQn 1 */
#endif
 int32_t misr;
 misr = HRTIM1->sMasterRegs.MISR; // read interrupt status
 HRTIM1->sMasterRegs.MICR = misr; // clear all active interrupts
 if(misr & HRTIM_MISR_MREP) {
 int phase; // is the type correct?
 GPIOC->BSRR = (1<<11); // up
 phase = my_PID_Controller();
 CurrentPhase = phase;
 GPIOC->BSRR = (1<<27); // down +16
 HRTIM1->sMasterRegs.MCMP1R = phase; // don't access the (hopefully) volatile global variable again 
 }
 GPIOC->BSRR = (1<<26); // down +16
 /* USER CODE END HRTIM1_Master_IRQn 1 */
}

victagayun
Senior III
May 10, 2020

This code seems to work!

When just using this for HRTIM1_Master_IRQHandler:

void HRTIM1_Master_IRQHandler(void)
{
 /* USER CODE BEGIN HRTIM1_Master_IRQn 0 */
 
 GPIOC->BSRR = (1<<10); // up
 
 /* USER CODE END HRTIM1_Master_IRQn 0 */
 HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);
 /* USER CODE BEGIN HRTIM1_Master_IRQn 1 */
 
 GPIOC->BSRR = (1<<26); // down +16
 
 /* USER CODE END HRTIM1_Master_IRQn 1 */
}

I get this 1.8uS...

0693W000000X9irQAC.jpg

but when I did this :

void HRTIM1_Master_IRQHandler(void)
{
 /* USER CODE BEGIN HRTIM1_Master_IRQn 0 */
 GPIOC->BSRR = (1<<10); // up
//#ifdef 0
// /* USER CODE END HRTIM1_Master_IRQn 0 */
// HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);
// /* USER CODE BEGIN HRTIM1_Master_IRQn 1 */
//#endif
 int32_t misr;
 misr = HRTIM1->sMasterRegs.MISR; // read interrupt status
 HRTIM1->sMasterRegs.MICR = misr; // clear all active interrupts
 if(misr & HRTIM_MISR_MREP) {
 uint16_t phase; // is the type correct?
 GPIOC->BSRR = (1<<11); // up
 phase = my_PID_Controller();
 CurrentPhase = phase;
 GPIOC->BSRR = (1<<27); // down +16
 HRTIM1->sMasterRegs.MCMP1R = phase; // don't access the (hopefully) volatile global variable again
 }
 GPIOC->BSRR = (1<<26); // down +16
 
 /* USER CODE END HRTIM1_Master_IRQn 1 */
}

I got this faster int routine:

0693W000000X9imQAC.jpg

Thanks!

But I need to check if phase is still working.

Thanks again!

TDK
May 10, 2020

Turning on compiler optimizations should also help quite a bit.

If you're needing speed, I would suggest just rewriting HRTIM1_Master_IRQHandler yourself instead of calling HAL_HRTIM_IRQHandler. HAL has to handle all possible cases, so it'll be slower.

"If you feel a post has answered your question, please click ""Accept as Solution""."
victagayun
Senior III
May 23, 2020

Another question, would it be faster if a callback be used instead?

If so, what is the equivalent callback function for this?

berendi
Principal
May 23, 2020

​I don't know, what kind of callback do you have on your mind?

The interrupt handler is as fast as it gets except for​ the my_PID_Controller() function. From the falling edge of channel 2 which is I guess the event that triggers the interrupt to the rising edge of channel 4 maybe 350 ns, half of it is the hardware overhead, saving registers to the stack before entering the handler, the other half is testing and clearing the status register. Then the pulse on channel 4 surrounding the function call is 1670 ns long.

You can consider whether it is necessary to do that calculation on every cycle, or maybe it would be enough to call it e.g. in the 1 ms timer interrupt​, and set the duty cycle right there.

victagayun
Senior III
May 23, 2020

I am comparing between

HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);

and

HAL_HRTIM_RepetitionEventCallback

because HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER); had some 4uS overhead.

But if I were to use HAL_HRTIM_RepetitionEventCallback intead, will the overhead of 4uS disappear?

berendi
Principal
May 25, 2020

HAL_HRTIM_RepetitionEventCallback() is an empty function (unless you put some code in there), so it will execute very fast. Why would you call it?

victagayun
Senior III
May 25, 2020

To replace

HRTIM1_Master_IRQHandler(void)

which includes

HAL_HRTIM_IRQHandler(&hhrtim1,HRTIM_TIMERINDEX_MASTER);

which is around 4uS.

If I use the call back instead, which I haven't tested yet, would it be better, ie less overhead?