GPIO Interrupt on STM32F4 causes FreeRTOS timer go haywire

aarbee wrote on Wednesday, April 11, 2018:

I want to use tickless idle to save power and at the same time I want to use the GPIO interrupts. Can I use both? Currently it seems if I enable tickless Idle, I can’t use GPIO interrupt as it causes the timers to go nuts.

rtel wrote on Wednesday, April 11, 2018:

Yes you can use both - tickless idle is meant to allow the CPU to sleep
until either an interrupt or a timeout occurs.

How have you implemented the tickless mode?

Remind me which FreeRTOS version you are using.

aarbee wrote on Wednesday, April 11, 2018:

Richard,
I am using 8.2.3 version
For tickless mode, I have just enabled
configUSETICKLESSIDLE to 1 and configUSE_TICK_HOOK to 1 and application tickhook function is doing nothing:

void vApplicationTickHook(void) {
}

aarbee wrote on Wednesday, April 11, 2018:

The STM32 port uses processor’s STOP (low power) mode. This code to put the device in STOP mode is called in vPortSuppressTicksAndSleep function. In this mode, RTC Wakeup timer is used to get out of the stop mode.

This is the code that is causing all the trouble.
If I keep Tickless Idle ON but disable STOP Mode, it works but then the device obviously wouldn’t go to low power mode.

richard_damon wrote on Thursday, April 12, 2018:

My guess is that the RTC_Wakeup timer is running at a different rate than what FreeRTOS thinks it is. That can cause all kind of havok when the processor wakes back up from the interrupt, as FreeRTOS may think more time has passed than really has.

aarbee wrote on Thursday, April 12, 2018:

What rate does FreeRTOS thinks it is running? I mean what variable?

rtel wrote on Thursday, April 12, 2018:

There is probably one of two issues here:

  1. The version you are using is old and some original versions of
    tickless idle didn’t allow the simultaneous use of software timers. I’m
    not sure without checking the version history when that was changed, but
    it’s probably worth updating the FreeRTOS kernel files (including header
    files and port layer files) to the latest version in case that is your
    issue.

  2. From your description it is clear you are not using the ‘generic’
    tickless idle mode, which cannot enter a very deep sleep as it uses the
    SysTick timer, but a chip specific version, which is using a low power
    timer and entering a much deeper sleep mode. It could be the
    implementation is not re-calculating the time correctly when low power
    mode is exited because of an interrupt - and that is making it appear
    that time has moved on past where the timers should have expired, and
    that the timers are catching up with where they should have been if the
    time were correct.

aarbee wrote on Friday, April 13, 2018:

I am suspecting #2 i.e., STM32 port not returning correct ticks after getting out of sleep mode.

aarbee wrote on Thursday, April 19, 2018:

The problem was in STM32 port code. For STM32 there is mode called STOP mode which is used in conjunction with TicklessDelay feature. When Stop mode is used to put device in sleep, vPortSuppressTicksAndSleep function (in file port.c) makes call to function xPortSTM32StopMode (in file freertos_stopmode.c). This function puts the device in stop mode and returns the amount of time it slept, after it wakes up.

The existing code uses RTC Wakeup Interrupt to wake up the device and the interrupt timer is set to expire at the end of the duration for which the device is supposed to sleep (i.e., the argument to the functon xPortSTM32StopMode). This function always returns the same value (=value it was supposed to sleep). This works as long as only the Wakeup interrupt wakes it up. If GPIO interrupt wakes it up, it doesn’t re-calculate how long it slept. It just returns (the incorrect) value which it was supposed to return if Wakeup interrupt woke it up. That means it returns a value much larger thatn the time for which it actually slept. This further means the freeRTOS is provided incorrect info and the timers will now expire fast.

Fix for this problem is to read the RTC minutes/seconds/SubSeconds register before and after the device goes to sleep and on wakeup and then calculate the elapsed time and return. This fixed it for me.

Make sure to :

  1. Change resolution of the subseconds register by changing the prediv_a and prediv_s registers
  2. Disable shadow registers update
  3. Read the subseconds register twice, compare and if it doesn’t match read again.

aarbee wrote on Thursday, April 19, 2018:

Thanks Richard Barry and Richard Damon for helping me out.

rtel wrote on Thursday, April 19, 2018:

Thanks for reporting back.