A disco effect: Looping and wait() in mbed OS
This post was originally published on Mbed Developer Blog.
Let’s say I have a tri-color LED, and I want to make a small disco effect by toggling the three channels every few hundred milliseconds, and stopping after ten iterations.
Easy enough when doing traditional embedded development:
In mbed OS, the approach above is not allowed:
- It blocks main, which is bad, because no other operations can happen at this point.
- The microcontroller needs to be awake to honor the
waitcall, and therefore cannot go into deep sleep in between toggling LEDs.
Instead, mbed OS works with an event loop and a scheduler (MINAR), that manages the timing of function calls (similar to Node.js or Ruby’s EventMachine). The idea is that the operating system is a lot better at managing time than the developer, especially when applications get more complex. So rather than switching between modules or handling deep sleep yourself, you tell the scheduler ‘wake me up in half a second’, ‘activate me when this interrupt is triggered’ or ‘let me know when a Bluetooth connection comes in’. The scheduler then switches contexts when necessary, and lets the microcontroller sleep when possible.
We always need to be able to call MINAR, so we must not block the microcontroller with a long-running or infinite function. So instead of using
wait(0.2) we tell the OS: “hey! in 200 milliseconds I’d like to do something again, can you wake me up?”. That means that we need to rewrite this code a bit to use events rather than a blocking
In this approach:
- We create a function
discothat takes two arguments.
turns_leftis the number of times the function should run after the current iteration, and
delay_msis the delay until the next iteration. This function is invoked every 200 ms.
turns_leftis greater than zero, we create a function pointer:
- Its return type is
voidand it takes an
- Surprise, this is the same function signature as the function we’re currently in (
- We initialize the function pointer with a reference to the
- Its return type is
- We tell MINAR (the event scheduler) that we want to execute the function pointer with the arguments
turns_left - 1, and our
- We also tell MINAR that we want to delay the function call by
- When our application starts we kick off the sequence.
Relatively easy, and now our code is non-blocking!
Calling a member function
If the function
disco is a class member, we can use
FunctionPointer by passing in the object reference as the first argument: