New to FreeRTOS and liking it so far.
We (company I’m working for) decided to dive into the deeper end of the pool and go with using the the tickless approach and putting the CPU into Stop mode. This saves power by a significant amount and need to do this to meet the design criteria for the product.
The problem comes in where I have to wake up the CPU on an I2C message. The I2C Peripheral seems to hang fairly consistently (but not always) the first time around. Not quite sure what, but something appears to reset the peripheral after several seconds or it gives up or something and there on out it fairly consistently (but not always) works. There are other interrupts that happen on a periodic basis (every 10 seconds) and I suspect the peripheral comes to life and the error handling resets it.
I am using code out of an STM32 cortex-M0+ port that was available, here is how I get into STOP mode:
The original code was wrong, it disabled interrupts before going to stop mode which ensured the CPU would never wakeup.
In my ISR, I start and stop timers and have been told that I need to do special things because tasks that are higher priority can get scheduled in because of some action in the ISR.
So, it looks like I need to call portEND_SWITCHING_ISR(x) in the ISR. Does this have to be at the end of the ISR or can I call it right after I call the Start/Stop timer code? There are several places where I might manipulate the timers, which is why I ask if I it is ok to do the portEND_SWITCHING_ISR in the middle of the ISR.
Has anyone done the same thing (STM32 Cortex-M0+ in Stop mode and wake up on I2C message) successfully? Or something close? The difference may be that the I2C peripheral will stretch the clock line on the I2C START condition until the internal clock spins up from sleep and allows the processor to run at normal speeds. It then compares the address to makes sure the message has the right address and if so, wakes up the CPU core, otherwise it goes back to Stop mode as if nothing happened.
Any help or suggestions would be greatly appreciated.
Thanks in advance!
Re Q1, you can call
portEND_SWITCHING_ISR() anywhere inside the ISR. Presumably after you find out whether a higher priority task was awoken. If you make multiple fromISR() API calls that might awaken a higher priority task, you don’t have to call it after every one. But you can.
Re Q2, not really.
Your comment about disabling interrupts before WFI might not be right though. In Cortex M, if you don’t mask interrupts prior to WFI then the interrupt that should prevent you from going to sleep might not. I recommend delaying the use of STOP modes (instead using SLEEP) until everything seems to work correctly. Then introduce STOP modes.
Really appreciate the information!
Q1 - Great! Only one of them should happen in any one pass, but it is not a concern anyway.
Q2 - I spoke with the FAEs from ST and they also said that the interrupt should be enabled before the WFI. So, if I disable the interrupts, what would wake up the CPU? I am perplexed by this. Seems like the CPU would never wakeup, no? Perhaps I missed something in the docs? If you have some code you could share, that would be greatly appreciated.
Good idea on using Sleep until everything works correctly. The Stop mode disconnects the debugger and causes IAR to crash too. I even have the macros to prevent this, but it does not seem to be working. Will give this a try. However, in Sleep mode the HSI clock (used by everything) is still functioning, so this bug may not show up at all. Any suggestions on this?
Thanks again, your response was very helpful. You seem quite knowledgeable and really appreciate your insights. If it wasn’t for this COVID thing, I’d buy you a beer if you were ever in the Midwest
Cortex M will wake up (or not bother going to sleep at all) if there is an interrupt request, even if interrupts are masked. However it will not “take” the interrupt and execute the ISR until interrupts are unmasked. The FAE might not understand all the software implications of the race condition that comes with trying to go to sleep. Between the decision to go to sleep to and the actual WFI, interrupts must be masked in case the interrupt that should wake you up comes after the decision but before the WFI.
The code you should use is the basic CM0 port in the FreeRTOS distribution. It will get you up and running with tickless idle and sleep. Moving to the STOP modes will take a little customization but you can deal with that later. The forum can help with that when the time comes as there are FreeRTOS considerations there too.
By the way, your I2C strategy from question 2 is sound, I’ve just never done it myself. Depending on your MCU model, you may need to pick the right I2C peripheral, and then it can operate in Stop mode by temporarily enabling the HSI without the CPU even knowing. Just as you said.
All working now. Turns out somehow the I2C was using the SysClk instead of HSI. There were a couple of issues with the interrupts as well. But working great now.
Thanks for all your help!
Great! Glad you got it working. Did you use the official port? Just curious about the tickless code you ended up with.
I never did find the official port. I used something one of the other guys found on-line somewhere. Might be the same.
For what it’s worth, you can go to https://github.com/FreeRTOS/FreeRTOS-Kernel/releases and pick the release you want – latest is always recommended.
After you download and open the zip/tar, the main files are in the top directory, and the port files you would need are in portable/[compiler]/ARM_CM0/ and portable/MemMang.
Not read all this thread so maybe misfiring my reply, but there is tickless idle in the Cortex-M0 GCC port in the latest release: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V10.3.1-kernel-only/portable/GCC/ARM_CM0/port.c#L398