cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_HRTIM_IRQHandler duration is very long

victagayun
Senior III

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 */
}

1 ACCEPTED SOLUTION

Accepted Solutions
berendi
Principal

> 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 */
}

View solution in original post

12 REPLIES 12
berendi
Principal

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

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
 
}

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
Principal

> 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 */
}

TDK
Guru

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".

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!

victagayun
Senior III

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

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

berendi
Principal

​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

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?