PICs and Flashing LEDs

I firmly believe that every microprocessor project should have a flashing light - at least during the early development stages. It acts as an indication that the software's doing what you thought it would and is a very basic telltale for when things go catastrophically wrong. They're also handy for non-technical family members, as they show there is an outward manifestation for all the time spent writing the software; even if that leads to a comment like "is that all it does?".

When I start a new project, it comes about in one of two ways: either as a modification of an already existing device, or as something completely new, which starts with the bare metal and develops from there. If it's based on an existing project, then there's probably already an LED, or some other sign of live that I can use for debugging. With a new design, or a new PIC device, the very first thing I get it to do is light an LED. This at least tells me that the initialisation is correct and that the hardware is working as expected.

Generally, as the software develops I place the LED flashing code inside an interrupt routine: either to show that whatever has caused the interrupt has happened, or as part of a timer that is triggered at specific time intervals. Most of my projects contain a timer interrupt and I use this to flash an LED, generally at around 5 times a second: 5 switch onsand 5 switch offs, with code something like this:

if(tenths_of_a_second & 0x1) LED = 1;
else LED = 0;

However, there's so much more you can do with an LED.


Even with just a single LED, it's possible to convey more than a simple off/on status. I've recently started assigning a 16-bit variable to each LED and loading this up with a bit pattern I want the LED to display. So each time the interrupt routine is called, it shifts the word left and uses the most-significant bit to determine if the LED should be on or off:

if(led_pattern & 0x8000) LED = 1;
else LED = 0;
/* the next segment rotates the word by 1 bit */
if(led_pattern & 0x8000) {
    led_pattern = led_pattern     led_pattern != 0x1;
} else {
    led_pattern = led_pattern }

Obviously, this code isn't optimised - but by keeping the flashing instructions separate from the rotating instructions, you get to see what's happening. The reason the if statement is necessary is that the shift-left instruction clears the least significant bit of the word. So after 16 shifts, the word has become all 0's and the LED pattern is lost. (This can be a benefit itself, but you need to be aware of this point).


So, using this snippet to flash the LED, a bit pattern in the controlling word of, say:
0b1000000000000000
would cause the LED to be on for 1 period every 16. If the routine that flashes the LED is called every 1/10 seconds, this results in a short flash, followed by a long pause. Similarly, a pattern of
0b1010101010101010
Gives a fast, repeating flash pattern that's very noticeable.


There's more!The diagram below shows a project with 3 indicator LEDs (I had a lot of spare pins).

Here each of the LEDs is handled slightly differently.
The conventional flashing LED is the green one. This shows a repeating pattern that is assigned to a 16 bit (although it could be shorter, or longer depending on how complicated you want to get) variable. The pattern repeats until a new one is assigned - it uses the code segment shown above. The yellow LED is used as a sort of progress counter. The duration of it's flashes are determined by an external count and get increasingly longer turn-on times as the projects functions progresses. This does not retain the bit pattern, as the green LED does. So once the pattern has been shown fully the variable that held it becomes zero. A test in the main body of the code for

if(yellow_pattern == 0) {
    yellow_pattern = new_value;
}

So long as the main program loop which perform this test runs more frequently that the 1.6 seconds it takes for the existing pattern to become exhausted, there is no obvious gap in what the user sees.

The final LED: the red one is used for two functions: both to provide a short acknowledgment when the user presses a pushbutton and also to signify when an external interrupt has fired. The configuration is also unusual as the red and yellow LEDs are in series. This technique known as "charlieplexing" allows either LED to be lit, or for neither of them to be on - but not for both to be on at the same time. The way this works is by setting the "output" pin as a high impedance input when neither LED should be on. This leaves both LEDs with insufficient voltage across them to light up. When the driving pin is set as a "1", the lower LED lights with the current supplied from the pin. When the driving pin is "0", the top LED lights with current sunk through the pin.

By having the code that controls the red LED executed after the code that controls the yellow one, the flahing pattern of the red LED appears to over-ride the yellow LED. (The yellow LED is on for an extremely short time, but provided the red LED's code follows the yellow code in execution, that time is very, very short