0
\$\begingroup\$

I've read a few documents on best practices for interrupt handling and thought I was doing things correctly, ensuring the variable set in the ISR was declared volatile. In my code the interrupt handler is operating properly, firing at the correct rate per the timer settings (checked by toggling an LED). The problem I'm encountering is that when I check the status of the flag set in the interrupt from my main loop, I get seemingly random updates at rate much much slower than expected. Some more specifics:

Timer ISR Period: 500ms
Main Loop Execution Period: 50us
Effective period of CheckPeriodicTimer() being set: anywhere from 2 - 45 seconds
MCU: STM32F030R8 48 MHz
IDE: STM32CubeIDE 1.3.1
I have tried compiling with and without optimization and am getting the same behavior.

tim.c


volatile uint16_t PeriodicTimerFlag = TIMER_FLAG_CLEAR;
volatile uint32_t TimerOverflow = 0;

... 

void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef *htim) {

    // check which timer
    if (htim->Instance == TIM1) {

        // check for stale flag - something is wrong (but also occurs when
        // debugger stops execution)
        if (PeriodicTimerFlag == TIMER_FLAG_SET) {
            TimerOverflow++;

        } else {        // set the flag and get out of here

            PeriodicTimerFlag = TIMER_FLAG_SET;

        }

    }
}
...
uint16_t CheckPeriodicTimer (void) {

    return PeriodicTimerFlag;
}

void ClearPeriodicTimerFlag (void) {

    PeriodicTimerFlag = TIMER_FLAG_CLEAR;

}

main.c

...
uint16_t mainPeriodicTimerFlag = TIMER_FLAG_CLEAR;
...
int main(void)
...
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    //timer check
    mainPeriodicTimerFlag = CheckPeriodicTimer();

    Task1(mainPeriodicTimerFlag);
    Task2(mainPeriodicTimerFlag);

    // Clear timer flag once done
    ClearPeriodicTimerFlag();                       // remote
    mainPeriodicTimerFlag = TIMER_FLAG_CLEAR;   //local
  }
```
\$\endgroup\$
2
  • \$\begingroup\$ If you have spare I/O, turn a pin on when mainPeriodicTimerFlag is set in the main loop. Turn another pin on right before you call Task1 and off after you exit. Ditto (with a third pin) for Task2. Then look at it all with an oscilloscope. \$\endgroup\$
    – TimWescott
    Commented Jun 19, 2020 at 16:58
  • \$\begingroup\$ Yeah, that's where my measures are coming from. While the main loop runs every 50us, the main flag is set at a period of 2-45 seconds, randomly. \$\endgroup\$
    – Kile
    Commented Jun 19, 2020 at 17:00

1 Answer 1

1
\$\begingroup\$

It's rather obvious - main loop continuously resets the flag every loop, even if the flag is not set. If the interrupt happens after reading the flag, then the tasks are not triggered, and the flag is cleared, so the tasks are not triggered on the next round even if the interrupt did trigger. So it only works if the interrupt happens after resetting the flag and before it is read, and thus at quite low propability.

\$\endgroup\$
2
  • \$\begingroup\$ D'oh -- I completely missed that. Best practice is to turn off interrupts, read the flag into local storage, clear the flag, turn on interrupts, and carry on. \$\endgroup\$
    – TimWescott
    Commented Jun 19, 2020 at 23:05
  • \$\begingroup\$ In addition, there is no atomic access nor re-entrancy protection in the code, so in worst case main() could be reading half the variable and ending up with corrupt data. Depending on what machine code you get and how those enums are defined. \$\endgroup\$
    – Lundin
    Commented Jun 24, 2020 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.