Written by Elecia White.
The first thing that most people do when getting a new board is to blink an LED. Embedded engineers treat it as though it is the simplest, most basic operation. We call it “hello world” to show how friendly and software-similar it is.
Blinking an LED implies a working cross compiler and a programming tool, not your run of the mill high-level software concepts at all. Sure, a software developer may have a compiler (or at least an interpreter) but the code they write is builds and runs on the same operating system on similar hardware.
One of the hallmarks of an embedded system is that we develop the code on one computer then run it on a different system targeted to a particular function. A cross-compiler runs on the development system but generates code for the target. A programming tool copies the code from the development system to the target's memory (often flash).
This requires specialized software and hardware. The whole “blinking an LED is the hello world of the embedded space” is a bedtime story we use to make embedded seem simpler than it is.
And yet, that is the first thing most of us do because it means our compiler and programmer are working. So, rant over, let’s talk about LEDs. And since I’ve still got a crush on my robot buddy, BB8 can help demonstrate.
I don’t work for this toy’s creator (Sphero) so I don’t know what code they use to control the LED. However, the STM32 processor has example code (via the STM32Cube for F3). The sample code initializes the hardware abstraction layer, configures the system clock, enables the GPIO clock, configures the GPIO as output and finally toggles the LED. For our GPIO, the last step would look like
HAL_Delay(100); // milliseconds
Ignoring the setup code, this is pretty straightforward. Looking at the hardware, using my voltmeter in resistance mode to beep out the signals (that is obviously the technical term), I can see the connection is not quite as simple as the LED connected to the processor.
The GPIO B8 processor pin is connected to a FET (Q13), which acts like a switch. When the processor outputs high, the LED gets access to VBatt. Exceeding the 3.3V of the processor, VBatt is the sum of the two LiPos, about 8V depending on charge level. The FET also provides as much current as the LED can use (though it does have a 200 ohm current-limiting resistor so the LED doesn’t explode).
If you prefer schematic form, the LED circuit looks like this:
BB8 also has RGB LEDs, a synchronized pair. Ohm’ing the signals out showed them to be directly electrically connected.
Note that the RGB signals do not go directly to the processor pins indicated, instead they go through current-limiting resistors and FETs, just as the blue LED did above.
RGB LEDs are neat because you can simulate nearly any color. Want yellow? Turn on green and red. Want orange? Turn on red and half of green. The mixing makes them powerful. But getting “half of green” means we can’t control them as simple on-off outputs any longer, we have to move to something that lets us choose how much of each color to mix. (Cree, maker of many RGB LEDs, has an extensive Application Note on color mixing to help you figure out what colors, though Wikipedia’s web colors page is a more pragmatic start.)
This leads us to pulse width modulation (PWM). We saw this same technique with the motor subsystem post: the processor outputs a digital signal very quickly, one with uneven highs and lows. If it is entirely high, it is on; entirely low, off. However, when it is mostly high (say 75% high), then the motor is slower (and the LED is dimmer) than when it is fully 100% on. The percentage of high signal is called the duty cycle.
The speed of switching back and forth is the switching frequency. A slow switching frequency can make an LED seem like it is blinking when it should be steady at a dim level (especially true in peripheral vision where the flicker fusion threshold is higher so you can detect motion more easily). (For a motor, a too-slow switch frequency can make it vibrate instead of move as it gets an uneven amount of oomph.)
While duty is usually given as a percentage, the granularity available is another term: you can set PWM up so that the duty cycle options are 10%, 20%, 30%, etc. or so that they are 0.1%, 0.2%, 0.3%, etc.
STM provides example code for PWM output control. They start by setting up the basis timer. The code uses a 24MHz clock as the basis for the PWM. The timer counts 665 (PERIOD_VALUE) of these clocks and then resets. Therefore, the switching frequency is 24MHz/PERIOD_VALUE = 24MHz/665 = 36,090Hz. This is fast. It depends on the FET and the LED but 20khz is usually a pretty good switching frequency. (Around 8kHz, you might start being able to hear some circuits sing.)
The code next configures the duty cycle of each channel (the example outputs four PWM signals or channels, our RGB LED would need three channels). The 665 PERIOD_VALUE also sets the possible granularity: there are 666 possible steps (0 counts): 0%, 0.15%, 0.30%, etc. If you want 50% duty cycle, then the pulse value (the pulse width) should be set to 333 counts (since 333 is 50% of 666).
Finally, the code starts the PWM signal generation. It stays in a steady loop, leaving each channel at the initial settings. This is pretty boring. I’d want to change PWM colors (or dim and brighten one color) which means changing the duty cycle while the timer is running. Ideally, we don’t want to interrupt the PWM system to make this change; that might cause a flicker in the LED (or jitter in a motor). This processor allows the pulse value (which translates to the duty cycle) to be preloaded into the auto-reload register (ARR) and updated the next time an event happens (such as the timer gets to PERIOD_VALUE and resets). Some processors will instead have an interrupt where your software can load the correct value when needed, it is nice that the STM32 takes care of this for us.
I started this post with a mini-rant about how blinking an LED isn’t trivial. Few things in the embedded space are. As I looked at STM’s code for the PWM, they made it easy to get started with: examples are wonderful and I appreciate them very much. On the other hand, their hardware abstraction layer (HAL) turns the channel off when it configures the duty cycle; it doesn’t use the auto-reload register that would make the PWM control that little bit nicer for the user. Instead, it is nicer to the developer, making a consistent, not-too-complex interface that solves most problems. The HAL code is available, you can use their code to kickstart your own but for efficient, precise code, their example is not going to get you out of reading the reference manual.
I suppose now is a good time to mention that the blue LED is also PWM’d. I didn’t notice at first since it is not obvious at a reasonable distance. However, trying to hold the probes (one on each side) and turn it on using the phone app with my nose, well, let’s just say I got really close to the board.