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.
In the top window, where the selected “Project Resources” are listed, see Figure 1, we can now click on each of them and proceed to inspect their configuration dialog windows. It is here that we can learn about the specific options available for each peripheral.
Figure 1 – MCC project resources window (Source: Microchip)
The top of the project resources list also contains the “System” group. The “System Module” in particular represents the essential elements of the microcontroller such as: the oscillators and configuration bits selections.
Set the oscillator to the “31kHz_LF” mode (the lowest power of all) as in figure 2.
Figure 2 – MCC System configuration window, internal oscillator detail (Source: Microchip)
Next, click on the PWM6 resource, see figure 3 Select a timer as its time base — Timer 6 will be our choice. All other options are already set as needed by default. These include: a duty cycle of 50% and a non-inverted output polarity.
Figure 3 – PWM6 configuration window (Source: Microchip)
Clicking on TMR6, see figure 4, we are once more presented with a number of sensible default values, let’s make the period to be 16.2 ms.
Figure 4 – TMR6 configuration window (Source: Microchip)
Click on PWM7 now, see figure 5, and set its time base to Timer 4, so that we can select a different period.
Figure 5 – PWM7 configuration window (Source: Microchip)
Click on TMR4 now and change the period value 16ms as in figure 6.
Figure 6 – TMR4 configuration window (Source: Microchip)
Finally, click on the CLC1 module and configure the first two input signals to be connected to the PWM6 and PWM7 outputs respectively, see figure 7. Connect them to GATE1 and GATE2 and ensure the “AND-OR” function is selected.
Figure 7 – CLC1 configuration window (Source: Microchip)
Next, use the “Pin Manager:Grid” window to access the I/O configuration grid where you will need to assign one or more pins to the CLC1 output. Thanks to the Peripheral Pin Select feature more than one LED can be driven simultaneously by the CLC output. Select RA0-2 in our case, which are physically connected to the four LEDs of the MPLAB XPRESS evaluation board, see figure 8.
Figure 8 – MCC pin manager grid window (Source: Microchip)
Back to the system group of resources, you can select the “Pin Module” and in its configuration window you will be able to verify that all I/O pins used are properly configured see figure 9.
Figure 9 – Pin module configuration window (Source: Microchip)
Pressing the “Generate Code” button will trigger the MCC to action producing a set of six small source files (written in C) providing all the necessary peripheral initialization code. In fact MCC will also volunteer to create a main file. We will accept gladly the offer and this will add a “main.c” file, containing calls to the peripheral initialization and an empty main loop.
Don’t be surprised if, at this point, I tell you to simply ask MPLAB X IDE to build the project and program the Curiosity board with a single last click on the “Make and Program” button. After a few seconds for the compiler, linker and programmer to do their job, you will be happy to see the little XPRESS board LEDs breathe!
In Less Than 10 (Binary) Lines of Code
You might want to know that I have recently written a book titled “In 10 lines of code,” offering this among 20 similar projects, although in this particular case we did not have to “manually” type a single line of code. By simply combining a few CIPs together to build the “breathing function,” completed everything we set out to accomplish and we still have 100% of our MCU performance available for use in the rest of our application.
You will find the project containing the MCC configuration and all code generated for this article inside the book repository on GitHub.
Interestingly, since I have published and started using these short examples I have noticed how even experienced embedded developers, much like the students in the class above, are surprised by the “real time” performance boost the core independent peripherals can deliver. Their use requires stepping out of the “core-centric” comfort zone but, once you experience it, there is no turning back.
Lucio Di Jasio joined Microchip Technology in 1995 as a Field Application Engineer. In 2000, he moved to Chandler, (AZ) where he worked for various Microchip product divisions including Analog, 16-bit MCU and 32-bit MCU. Since 2011 Lucio has returned to Europe (Germany) where he travels extensively as a business development manager for Microchip 8-bit MCU division.