If you're using mbed OS 5, you should be using RTOS mechanisms here.
By attempting to WFI/WFE in a thread, you are effectively hogging the CPU - using 100% of CPU time to sleep in this thread. Okay, you're hogging 100% of CPU time efficiently, but you're still hogging.
You should instead be using an RTOS call that waits for an event, to yield to other threads. Your interrupt handler could set an event flag or wait for a semaphore, and your thread could be waiting for that. Then the RTOS does all the sleeping stuff for you - but only when there are no other threads active either. The "idle thread" in the RTOS activates more HW-specific power-saving stuff, and will do better than any plain WFE/WFI you can do.
Aside from all that, on this discussion - WFE is a good general "wait for something to happen". It's designed to be safe to use from user applications, and may be a NOP. If you know you are waiting for a volatile flag or spinlock or something, then it's an appropriate mechanism. It will be woken automatically by any interrupt handler that runs on this CPU, and in an SMP system, you'd have to make to have SEV instructions to wake other CPUs. You do not disable interrupts when using it - there's no race because it's waiting for a sticky flag.
For your example WFE would work by:
- local event flag is clear
- if (serial.readable()) evaluates to false
- interrupt occurs and is handled - this sets the local event flag
- WFE is called - returns immediately because the local event flag is set.
WFI is for low-level "stop the CPU and other hardware". It's designed for OS use, and is a privileged instruction. Ths OS must disable interrupts before issuing it to avoid races - it will wake despite the interrupts being disabled, including if the interrupt is already pending. (If interrupts were enabled, there could be no pending interrupts, so there would be a race).
For your example WFI would work by:
- disable interrupts
- if (serial.readable()) evaluates to false
- interrupt occurs but is not handled due to masking
- WFI is called - returns immediately because the interrupt is pending
- enable interrupts
- interrupt is handled
But regardless, you should be using the RTOS
- wait for event flag to be set - if it isn't already, so RTOS sleeps your thread
- interrupt occurs and is handled - signals event flag
- RTOS wakes your thread
The RTOS provides a few primitives that are safe(ish) to signal from IRQ context - semaphore, event flag and some others. Although there are limitations: https://github.com/ARM-software/CMSIS_5/issues/283
In terms of waiting for conditions, you may also be interested in ConditionVariable, present since mbed OS 5.7, I think: https://github.com/ARMmbed/mbed-os/blob/master/rtos/ConditionVariable.h But that can't be signalled from interrupt.
I've been messing with my own "ConditionVariableCS" which could be signalled from interrupt, but it's somewhat contentious, and maybe not the ideal answer: https://github.com/kjbracey-arm/mbed-os/commits/cv_cs
I'm trying to make the micro sleep when not in use, and wake to process any UART or SPI data received, or InterruptIn. How do I do this?
Currently in main(), I have the code below (just for the UART, for starters). When I run it, the micro doesn't ever wake. What am I doing wrong or missing?
I'm using an Embedded Artists LPC4088QSB, if it matters.