In this installment I will share the MSP430 code for a traffic light sequencer, but before I do, I believe a few words need to be said about powering the Launch Pad.
TI has designed the Launch Pad to be powered in two ways. The most straightforward is via the USB connection. This is how most experimenters will use it. Your computer puts out regulated 5V on the USB cable. The Launch Pad converts this 5V to 3V to power the microcontrollers on board (there are more than the one that you are programming).
If you want to power the Launch Pad on your layout this way I would suggest that you get a USB powered hub with a lot of output ports. Then get some USB "Male A" to "Female A" extension cables (sometimes the dollar store has these, if not try All Electronics). Use the extensions to connect the hub to your Launch Pad's USB cable. Now your Launch Pads are powered with no muss, fuss or bother and the cost is not great. Non-powered hubs can be cascaded from your powered hub to feed more Launch Pads. Not a bad option if your layout is not too large.
If your layout is really large or the distances between Launch Pads is long, you may want to run a power bus for the Launch Pads. This bus must be clean, pure, regulated DC - the output from a spare power pack will not do. The voltage on this bus must be between 3V and 3.3V and cannot exceed 3.6V.
The Launch Pad documentation tells you which jumpers to disconnect to use external power. A good power source would be a converted PC power supply. You may be able to find a wall-wart that puts out 3VDC, but it has to be regulated. Another inexpensive option, that I have not tried, may be the Philmore MW122A power supply (about $25 but available from MCM Electronics for $19 and when on sale for as little as $15). This looks like their old battery replacement unit but they claim full regulation and low ripple. Whatever you use, maintaining regulation over very long runs can be a problem. I have purchased some inexpensive regulator boards off of e-bay that could be used to take clean but unregulated DC and regulate it to 3V right at the Launch Pads. I'll let you know how my experiments turn out.
Using external power unfortunately prevents you from using the USB port for a future network of Launch Pads or from potentially controlling your Launch Pads from a central location.
Enough about power, on to the traffic light sequencer.
Traffic Light Sequencer
This program uses timing loops just like the alternate flasher, but I have used some different coding structures and the timing loops are of uneven durations.
First, how do traffic lights operate? Traffic lights control two intersecting bidirectional streets. Lets refer to one route as East/West (E/W) and the other as North/South (N/S). A regular 3-color traffic light then has four states:
E/W - Red N/S - Green
E/W - Red N/S - Yellow
E/W - Green N/S - Red
E/W - Yellow N/S - Red
And then the cycle repeats. The green and the yellow cycles will have different durations and the red cycle will be the sum of the green and yellow cycles - so the three signal aspects have three different durations. My observation of the traffic lights outside of my office indicates that the yellow cycle lasts for about 1/8 as long as the green.
This is all of the information that we need to know to program our traffic light sequencer. The table above tells us which lights to turn on or off for each cycle. We need to define two timing loop counters - which we will call 'green' and 'yellow'. We do not need to define one for red since it is the sum of green and yellow.
Refer to the listing at the end of this post as I walk through the code. This is not an attempt to teach you how to program in "C" (I'm hardly qualified for that); but simply to explain the workings of the program.
After the declaration of the main program (main.c) come several statements that tell the compiler - the program that executes on your PC and builds the program for the microcontroller - some things that it should do. One of these is which, of several hundred TI microcontrollers, we want to build a program for (i.e. target).
Then a number of variables are declared. Variables are numbers whose values, as their name implies, can be changed. In this program, almost all of them are used as constants; only t's value changes. The comments after each variable declaration explains its use. A value of 900,000 in 'green' causes a delay of about 16 seconds for the green lights and the value 112,500 in 'yellow' a delay of about 2+ sec. for the yellow lights.
Under the banner "BEGIN MAIN PROGRAM" the part of the code that actually executes on the microcontroller begins. The first job is to turn off the watchdog timer (it's important to do, but I won't explain why now).
Next we set up the pins on the microcontroller. This involves two operations for each pin. First, the pin has to be defined as an output; followed by setting the output "on" (that is, telling the microcontroller to put 3V on the pin). The comments tell you which pin is being set and what it is used for. Inside of the parentheses is the internal port and bit number that is being used (more on this in another post).
Next under the banner "DELAY WITH ALL SIGNALS ILLUMINATED FOR LAMP CHECK" I do something that is not necessary, but is nice to have. All of the signals have been turned on by previous code, so I introduce a delay loop of a couple of seconds with all of the lights so that you can do a visual lamp check.
A word about delay loops. It is possible to get precise timing out of the microcontroller; but it's an involved process and not required for this prigram. Approximate times can be established by using loops to count up or down to/from a number and the time it takes the microcontroller to do this counting produces the time delay. This loop tells the microcontroller to count using the variable 't' as a counter from t=0 to the value of 'start_up_delay', which has been set to a value of 50,000. After the loop is finished, 't' is set back to zero so that it can be used as a counter in the next delay loop.
Under the banner "BEGIN MAIN LOOP" we begin the part of the program that will execute indefinitely so long as power is applied. The "while" statement begins an infinite loop; once execution reaches this point everything between this statement and the end of the of the "while" statement will run indefinately.
The first sequence of setting the traffic lights begins after the banner "Begin Sequence E/W - Red N/S - Green"; like the first state in the table above. Since the first time through this loop all of the lights will be on after the lamp check, we have to not only turn on the appropriate Red and Green signals, but turn the others off as well.
After the lights have been set, the program enters a delay loop that counts up to the value in the variable "green". The microcontroller will take about 16 seconds to count up to 900,000; "t" is then reset to zero and we go on to the next sequence.
Here's where this program differs from the alternate flasher program. In the alternate flasher, the instructions turning the LEDs on were inside of the loop. So the LEDs were turned on several thousand times while the loop executed. In this program we set LEDs once and then just idle in the loop while we count up.
The next sequence "E/W - Red N/S - Yellow" begins with the comment that we do not have to set the E/W signal to red because it's red already; but we're going to do it anyway. This makes the code clearer and more self-contained, but could be eliminated if we were running out of code space or time - which we're not. At the end of this sequence, we count up to the value in the variable "yellow", which produces a delay of a little over 2 seconds.
The next two sequences operate in much the same way as the first two. After the timing loop of the last sequence, the right hand curly brackets closes the "while" statement infinite loop and the program returns to the first sequence.
Here's a video of the circuit in action. I've made a daughter board to connect six LEDs and resistors (270-300 ohm) to the Launch Pad's pins that I have defined as output. The daughter board obscures the Launch pad which is below the daughter board. The layout of the LEDs follows the layout of the microcontroller's pins and not the arrangement of a traffic light signal head, but you'll see the correct pattern of lights in red, yellow and green.
You can download the text file of the code here:
Traffic Light Code
If you want to try your hand at modifying this program here are a couple of suggestions. If you want to have more than three indications on the signal, to add turning arrows for instance, you would add additional states to the state table. You'd have to define more output pins for the turning arrow LEDs, then add a couple of states where both main signals are red and the E/W then the N/S arrows are illuminated in turn before returning to the green-yellow-red sequence.
If you want to add a third route through the intersection, you will need to add a column to the state table then re-sequence the lights to accommodate the three routes. New output pins will have to be defined for the additional signals for the third route.
For the neurotic, who want the maximum attention to prototype detail, I have two suggestions. At most intersections one route through the intersection is usually more heavily traveled and is allowed more time on green than the other route. This would be an easy modification. Simply define two green and two yellow loop counters - a longer one for the superior route and a shorter one for the inferior route. Substitute them into the code where appropriate for 'green' and 'yellow' and the timing loops will adjust accordingly.
The other suggestion is for the (exceedingly neurotic) advanced programmer. Build in a 'soft start' and 'gradual fade' for the LEDs to make them look more like incandescent lights. This could be accomplished in two ways. Build a nested timing loop where the LED is on for a short while followed by an off period, followed by a longer on period, shorter off period..., etc. until full brightness is reached. Full brightness is held for a the duration of the signal and then the process is reversed. The soft start and gradual fade would be much less than a second each and the loops would probably be only hundreds of counts long at the beginning and end. What you are doing is creating a crude Pulse Width Modulation (PWM).
The other apporach would be to use the on-chip PWM generator to do this function for you. I'll be doing PWM for a future project, so we'll see how this comes out.
These options demonstrate the versatility of the microcontroller. You can buy a traffic light circuit kit, but it's a fixed green-yellow-red sequence without any ability to add additional lights or routes or modify the timing.
This has been a long post with a lot of information. The code for the basic traffic light sequencer follows. Don't cut and paste this into Code Composer Studio as the embedded HTML that allows it to display properly will mess up the compiler. Use the link above to get a text file of the program. (BTW Blogger has really honked-up the indentation this time, and I cannot seem to get it fixed, sorry)
//
//
//
// Traffic Light Sequencer
// COPYRIGHT 2012 T. TERRANCE
//
// Provided under a Creative Commons Attribution, Non-Commercial
// Share-Alike,3.0 Unported License
//
// TARGETED TO MSP430 LANUCHPAD W/MSP430G2553 PROCESSOR
//
//
/*
* main.c
*/
#include <msp430g2553.h> //This instruction tells the compiler what microcontroller you are compiling this for.
volatile long t=0; //Define loop counter t and set it to 0
volatile long green = 900000; //Define loop counter green and set it to 900,000
volatile long yellow = 112500; //Define loop counter yellow and make it 1/8 of green
volatile long delay=15000; //Define delay waiting period used to latch the signal indication and set it to 15000
volatile long start_up_delay = 50000; //Define delay waiting period used to flash all of the signals at start-up and set it to 50000
////////////////////////////////////////////////////////////////////////////////////////////
//
// BEGIN MAIN PROGRAM
//
////////////////////////////////////////////////////////////////////////////////////////////
void main(void){
WDTCTL = WDTPW + WDTHOLD; //Stop Watchdog Timer
P1DIR |= BIT0; //sets E/W RED SIGNAL pin #2 (Port 1, Bit 0) to output and set to on
P1OUT |= BIT0;
P1DIR |= BIT4; //sets E/W YELLOW SIGNAL pin #6 (Port 1, Bit 4) to output and set to on
P1OUT |= BIT4;
P1DIR |= BIT5; //sets E/W GREEN SIGNAL pin #7 (Port 1, BIT 5) to output and set to on
P1OUT |= BIT5;
P2DIR |= BIT0; //sets N/S RED SIGNAL pin #8 (Port 2, BIT 0) to output and set to on
P2OUT |= BIT0;
P2DIR |= BIT1; //sets N/S YELLOW SIGNAL pin #9 (Port 2, Bit 1) to output and set to on
P2OUT |= BIT1;
P2DIR |= BIT2; //sets N/S GREEN SIGNAL pin #10 (Port 2, BIT 2) to output and set to on
P2OUT |= BIT2;
/////////////////////////////////////////////////////////////////////////////////////////////
//
// DELAY WITH ALL SIGNALS ILLUMINATED FOR "LAMP CHECK"
//
/////////////////////////////////////////////////////////////////////////////////////////////
for (t; t < start_up_delay; t++) //delay loop
{
}
t=0;
/////////////////////////////////////////////////////////////////////////////////////////////
//
// BEGIN MAIN LOOP
//
/////////////////////////////////////////////////////////////////////////////////////////////
while (1) // while (1) is always true, so this is an infinite loop
{
///////////////////////////////////////////////////////////////////////////////////
// Begin Sequence - E/W Red, N/S Green
//
///////////////////////////////////////////////////////////////////////////////////
P1OUT |= BIT0; //turn on E/W RED SIGNAL
P1OUT &= ~BIT4; //turn off the E/W YELLOW SIGNAL
P1OUT &= ~BIT5; //turn off the E/W GREEN SIGNAL
P2OUT &= ~BIT0; //turn off the N/S RED SIGNAL
P2OUT &= ~BIT1; //turn off the N/S YELLOW SIGNAL
P2OUT |= BIT2; //turn on the N/S GREEN SIGNAL
for (t; t < green; t++) //delay loop for one green time period
{
}
t=0;
///////////////////////////////////////////////////////////////////////////////////
// Begin Sequence - E/W Red, N/S Yellow
//
///////////////////////////////////////////////////////////////////////////////////
//the E/W signals should not have to be reset
//but do it anyway
P1OUT |= BIT0; //turn on E/W RED SIGNAL
P1OUT &= ~BIT4; //turn off the E/W YELLOW SIGNAL
P1OUT &= ~BIT5; //turn off the E/W GREEN SIGNAL
//the N/S red signal should not need to be reset
//but do it anyway
P2OUT &= ~BIT0; //turn off the N/S RED SIGNAL
P2OUT |= BIT1; //turn on the N/S YELLOW SIGNAL
P2OUT &= ~BIT2; //turn off the N/S GREEN SIGNAL
for (t; t < yellow; t++) //delay loop for one yellow time period
{
}
t=0;
///////////////////////////////////////////////////////////////////////////////////
// Begin Sequence - E/W Green, N/S Red
//
///////////////////////////////////////////////////////////////////////////////////
P1OUT &= ~BIT0; //turn off E/W RED SIGNAL
P1OUT &= ~BIT4; //turn off the E/W YELLOW SIGNAL
P1OUT |= BIT5; //turn on the E/W GREEN SIGNAL
P2OUT |= BIT0; //turn on N/S RED SIGNAL
P2OUT &= ~BIT1; //turn off the N/S YELLOW SIGNAL
P2OUT &= ~BIT2; //turn off the N/S GREEN SIGNAL
for (t; t < green; t++) //delay loop for one green time period
{
}
t=0;
///////////////////////////////////////////////////////////////////////////////////
// Begin Sequence - E/W Yellow, N/S Red
//
///////////////////////////////////////////////////////////////////////////////////
//should not have to reset E/W red signal
//but do it anyway
P1OUT &= ~BIT0; //turn off E/W RED SIGNAL
P1OUT |= BIT4; //turn on E/W YELLOW SIGNAL
P1OUT &= ~BIT5; //turn off the E/W GREEN SIGNAL
//should not have to reset any N/S signal
//but do it anyway
P2OUT |= BIT0; //turn on N/S RED SIGNAL
P2OUT &= ~BIT1; //turn off the N/S YELLOW SIGNAL
P2OUT &= ~BIT2; //turn off the N/S GREEN SIGNAL
for (t; t < yellow; t++) //delay loop for one yellow time period
{
}
t=0;
} //close while statement, back to top of infinite loop
} // End Main