About breathing and real-time performance
I recently attended a hands-on workshop meant to introduce a group of “millennials,” who already had a foundation of computer science, to the art of embedded programming. Sure enough, one of the earliest example projects turned out to be the proverbial “Hello (Embedded) World” that of course meant: blinking a Light-Emitting Diode (LED).
The instructor did a pretty good job of avoiding being side-tracked by the many details and temptations. He guided the class through setting up a simple I/O port and went straight to using the simplest possible solution. This meant a pair of blocking (waiting) loops and two assignments that, as he explained, were sure to be turned into a single microcontroller (MCU) instruction (bit-set and bit-clear) by the clever C-compiler. Soon, the room was filled by the intermittent light of many red LEDs. But, much to the presenter’s disappointment, the crowd did not seem too enthusiastic about the result. From the back of the room I started listening to the grumble. Soon the source of the group’s malcontent was revealed: there is blinking, and then there is pulsing or breathing as many modern laptops and mobiles do when in standby. As is often the case, it was a matter of incorrectly set expectations.
Running Short on MIPS
From there on the class took an unexpected turn as the presenter tried to please the audience and started off on a tangent to introduce them to the relative complexity that the new task entailed. A timer had to be introduced and with it a Pulse-Width Modulation (PWM) could be arranged to adjust the visible luminous output of the LED by controlling the duty cycle. Next he ventured into an explanation of how this had to be changed gradually, through a number of steps up and down a ramp defined in a table. The timing of each step required yet another (slower) timer, which produced an interrupt. The MCU would respond to this interrupt and compute (or consult a lookup table) to assign a new duty cycle value to the PWM.
This was already too much for the audience on their first foray into the world of embedded, but things got worse as he tried to impress them with a quick back of the envelope calculation.
Here is the math as I recall it:
Choose a PWM period such that we can count on the human eye persistence effect: 30~120Hz.
Let’s multiply that value as to provide a smooth dimming effect with say 256 steps. This gives ~ 32 kHz. So far so good. There is an extremely low-power internal oscillator available inside each PIC® microcontroller at exactly that frequency!
Now if we want to produce the simplest breathing effect (using a triangular shaped profile) with a period between ½ second and 2 seconds, we will need to ramp up and down 512 (total) steps for each period.
This translates roughly to the need to update the PWM approximately each millisecond. In MCU instruction cycle terms, at the 32 kHz clock frequency, we would receive one interrupt every eight instruction cycles.
Even to the most experienced embedded designer that sounded like an impossible proposition. The clock had to be increased, or in other words: we were running short of MIPS!
This was quite a realization for some in the audience, as for the first time they understood that the term performance (as in MIPS and MHz) had a very different meaning in this context. We needed to push the microcontroller faster even though we were not even attempting to make any calculations; we just needed to respond timely to an event!
This was their first encounter with the concept of real-time performance.
Feel the Beat
The exercise resonated with me as well, which is the reason you are reading about it here. I had been looking for simple ways to illustrate the limitations of the traditional MCU/core-centric approach to embedded applications. Today we are kind of obsessed with performance, MIPs, megahertz and megabytes, yet it is often the wrong type of performance we are focusing on. This became very clear to me sitting in this classroom.
Sometimes stepping back and looking at a problem from a new angle can reveal a hidden, more elegant and balanced solution. For example, when approaching the breathing LED problem presented above, let’s take a minute and try to free our mind from the chains of the MCU/core-centric culture and stop thinking about interrupts and lookup tables.
If you focus on the original problem requirements, squint a bit and look at the square wave of a gradually varying duty cycle you might happen to notice a striking similarity with a phenomenon, often experienced as an acoustic effect, produced by the “beating” of two periodic signals of close frequency as they add and cancel each other out. The sum of the two reaches our ears with an envelope that is recognizably periodic and with a frequency equal to the difference between the two. That is not difficult to implement with just a couple of logic gates and can be done within a microcontroller with the right type of peripherals.
A Core Independent Solution
The first thing we need is to find a way to generate two periodic signals (square waves are okay) but whose frequency is only 0.5 to 2Hz apart. A pair of digital timers with a reload register (in other words basic PWM peripherals) can be used to create the two signals. We will need to choose carefully the two reload values to be close but different between the two, so to produce the desired beat frequency. For example, if we use a PIC microcontroller with a clock at 32 kHz, two 8-bit timers and the connected PWM modules we can generate a 60.5Hz frequency and a 61.5Hz output. We can then use one of the Configurable Logic Cells (CLC), a small programmable logic blocks similar to FPGA/PLD macro blocks, to perform the logic AND of the two signals. Eventually we can publish the output directly to any of the I/O pins where an LED will be connected.
This will give us a visible breathing effect of 1Hz. Faster breathing will be achieved by increasing the gap between the two frequencies (making the second PWM period shorter) and vice versa slower breathing will be achieved by reducing the difference between the two all the way to 0.1Hz when the two reload registers are only one count apart.
The Configurable Logic Cell is just the most basic of the Core Independent Peripherals (CIP) found inside modern (PIC) microcontrollers, and for the rest of the solution we have only used pretty standard issue timers and PWMs.
You might wonder if, at this point, we had any use anymore for the MCU itself? The microcontroller core will in fact be used during the power up of the application but just to configure the peripherals. Perhaps even more interestingly, we reached our solution using an oscillator with very low power consumption and we still have 100% of the microcontroller performance (MIPS, MHz no matter how you want to measure it) available for use in the “other” hopefully more interesting tasks of our application.
Look ‘ma, no code!
If this has peaked your interest, you will be even more pleased to learn that putting this into practice doesn’t require opening a datasheet, or writing a single line of code! You need to see it to believe it, so I encourage you to follow along.
For simplicity I will use one of the inexpensive MPLAB® Xpress evaluation boards. I will use the PIC16F18855, which is featured on the board, although any of the recent Core Independent Peripheral (CIP)-enabled PIC16F1 microcontrollers can be used.
Let’s start with the MPLAB X Integrated Development Environment (IDE) “New Project” wizard to create a new project with the selected PIC model. We will also use the MPLAB Code Configurator (MCC), a free MPLAB X IDE plugin, to help us initialize and connect all the peripherals. We can simply pick them out of the list of “Device Resources” by double-clicking on their names. In our case, we can double-click on TMR4, TMR6, PWM6, PWM7, and one of the CLC modules – I picked CLC1 for this example.