Thursday, January 17, 2013

Simple Signal Solution

The idea for this project came to me a couple of years ago to design a simple and inexpensive solution for three color, bi-directional signaling.  I was sure that microcontrollers could implement the code and drive the LEDs; the problem with this plan was the inexpensive part - that was until the LaunchPads came along. So this is what I call the Simple Signal Solution.

In a simple block signaling system, at the eastern entrance to a block there is a westbound signal, conversely at the western entry to the block there is an eastbound signal. When the block is unoccupied both signals are green.  However if the block is occupied both signals indicate red, the signals in the blocks immediately to the east and west,    in the direction heading towards the occupied block, indicate yellow and the blocks beyond that indicate green.

Hardware Required for this project;
  
  • LaunchPads (MSP430G2553 chip) - 1 for each block
  • LEDs - (2) Red, (2) Yellow, (2) Green for each block or (2) signals with built-in LEDs for each block.
  • Resistors - (6) 270-300 ohm min. (higher to reduce LED brightness) for each block.  
A note about LEDs and resistors.  I know that some folks do not use resistors when driving LEDs with the LaunchPad. I did this too when I first programmed one.  However I left an LED flashing for several days and eventually the LED started to act flaky.  Then I saw on the LaunchPad's schematic that TI uses resistors with the on-board LEDs. If TI thinks that it's necessary, that's good enough for me. 


This project requires several of the available pins on the LaunchPad to be assigned as inputs or outputs.  I made up a table to keep them all straight.

 
Pin Port Assignment Direction Sense





2 P1.0 (and LED1) Eastbound Red Indication Output Active High
6 P1.4 Eastbound Yellow Indication Output Active High
7 P1.5 Eastbound Green Indication Output Active High
8 P2.0 Westbound Red Indication Output Active High
9 P2.1 Westbound Yellow Indication Output Active High
10 P2.2 Westbound Green Indication Output Active High






Pin Port Assignment Direction Sense










15 P1.7 THIS_BLOCK_OCCUPIED Input Active low
14 P1.6 (and LED2) OCCUPANCY_DETECTION_TO_EAST_BLOCK  (Occupancy detection TO next east block) Output Active low
13 P2.5 EAST_BLOCK_OCCUPIED (Occupancy detection FROM next east block) Input Active low
12 P2.4 OCCUPANCY_DETECTION_TO_WEST_BLOCK (Occupancy detection TO next west block) Output Active low
11 P2.3 WEST_BLOCK_OCCUPIED (Occupancy detection FROM next west block) Input Active low



That's a lot of pins to keep straight, but not too bad once you get it all into your head.                                            

The schematic diagram below shows the pins on the LaunchPad  headers and how to interconnect the LaunchPads so that the signals to and from the east and west get to their appropriate pins.


Pin 15 is brought to ground by an external circuit (e.g. a train detector) to tell the LaunchPad that the block is occupied.  Pin 14 outputs the status of it's train detection to the LaunchPad to the east, status from the east is received on pin 11.  Similarly Pin 12 outputs the current block status to the LaunchPad to the west on pin13 and status from the west is received on pin 12.

[Author's post-script.  I designed this circuit for common cathode LEDs, because that's the way I like to wire mine.  If  you are using commercial signals/signal heads they may come wired as common anode.  If they are, this code will not work.  I'll modify this code for use with common anode signals at a future date.]

You'll notice that just four wires interconnect the LaunchPads; two going east and two going west.  Compare that to the interconnects between the components of the commercial signal systems.  All of the LaunchPads may have to be powered by the same source to make sure that you have a common ground between them OR you have to tie the grounds between the supplies together.

Here's a video of the circuit in action. 






If you are not a programmer, you now have all of the data that you need to grab the code and go.  So here is a link to the code.

Simple Signal Solution


Code Walkthrough

If you are inclined to program, read on as I will walk you through the code.  The code begins with the usual declaration that this will be targeted to the MSP430G2553 processor.  Some loop counters and delay variables are defined as are some short intergers that will be used as logicals (0 or 1) to hold the status of the block occupancies.

The program enters the main program and the watchdog timer is turned off.   Then the input pins for the block occupied status are initialized to input; have resistors connected to them and finally have the resistors set as 'pull-ups'.  The MSP430G2553has internal resistors that can be set as pull-up or pull down.  Since these inputs are active low, the resistors are used to 'pull-up' (make high) the input pins until an external signal pulls them down.  Otherwise the pins would 'float' between high and low and may give false readings.

These block occupied pins are connected to either the train detector for the current block, the output of the LaunchPad to the east or the output of the LaunchPad to the west.  Signals on these pins will be captured and internal variable set based on their state below.

The the output pins are set as outputs; the six pins driving the LEDs are set to high (on) and the two outputs of the current  block's train detection status - OCCUPANCY_DETECTION_TO_EAST_BLOCK and OCCUPANCY_DETECTION_TO_WEST_BLOCK are set to high (the comments mistakenly say 1) which is inactive since these pins are active low.

A delay loop follows for a 'lamp check'.  The LED outputs have been set to the 'on' state above, and the delay loop holds that state for about 2 seconds.


Next we clear the block occupancy variables by setting them to 0, which will be interpreted as a logical false.

This finishes the set up and the program enters an infinite loop that will execute until power is removed.



Next the block occupied status for the current block, the block to the east and the block to the west is determined. If a block is occupied it is set to '1' which will be interpreted as a logical true and if the block is unoccupied; the value is set to '0' which will be interpreted as a logical false.

Next the code signals the blocks to the east and the west whether this block is occupied so that they can set their signals accordingly.

Now the signal-setting logic of the code begins. You can read the comments in the code for yourself.  All combinations of the 3 block occupied variables are tested and the signals set accordingly.

Finally, the signal is latched for a while to provide for some debounce.  The infinite loop then begins again with the clearing of the block occupied variables and the reading of the block status, etc.



Modifications & Extensions


While this code is fully functional as a signal system, it can still benefit from some modifications.  Bashing this code together with Hoffy's IR detector would make for an interesting combination.  Similarly combining this with code for a reflective IR detector would also make a better product.  If you attempt either of these think about including a time delay to latch the 'train detected' signal to account for the inter-car dropout common to all optical detection systems.

You could also include code to control the signals on sidings adjacent to the main.  In fact, the most involved signal complex that you can imagine is probably not beyond the capabilities of this relatively simple microcontroller.  Of course, you may not have enough pins to drive all of those signals!

Enjoy!

In the code listing that follows, I've given up trying to keep proper formatting.  Do you'll have to follow along as best that you can or you can download the properly formatted code from the link above.  Do not try to copy and paste this code.  It will not work!

 


//////////////////////////////////////////////////////////////////////////////

//                                                                         ////

//                                                                         ////

//            SIMPLE SIGNAL SOLUTION                   ////

//                                                                          ////

//            THREE-COLOR, BI-DIRECTIONAL SIGNAL LOGIC        

//                                                                             ////

//            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>







volatile long t=0;                        //Define loop counter t and set it to 0

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





////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            ////

//            Initialize Logical Variables for Block Occupancy                                 ////

//                                                                                           ////

////////////////////////////////////////////////////////////////////////////////////////////



short int THIS_BLOCK_OCCUPIED;              //if this block is occupied the value is set to

//true (non-zero)

short int EAST_BLOCK_OCCUPIED;              //if the next block to the east is occupied the value

//is set to true (non-zero)

short int WEST_BLOCK_OCCUPIED;              //if the next block to the west block is occupied

//the value is set to true (non-zero)









////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            ////

//                                 BEGIN MAIN PROGRAM                                        ////

//                                                                                            ////

////////////////////////////////////////////////////////////////////////////////////////////





void main(void){

















WDTCTL = WDTPW + WDTHOLD;  //Stop Watchdog Timer















///////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            ///

//                   Initialize Ports AND Signal Indications                                  ///

//                                                                                            ///

///////////////////////////////////////////////////////////////////////////////////////////









P1DIR &= ~BIT7;               // sets THIS_BLOCK_OCCUPIED pin (Port 1, bit 7) to input

P2DIR &= ~BIT5;               // sets EAST_BLOCK_OCCUPIED pin (Port 2, bit 5) to input

P2DIR &= ~BIT3;               // sets WEST_BLOCK_OCCUPIED pin (Port 2, bit 3) to input

P1REN |= BIT7;                // sets pull-up resistor on THIS_BLOCK_OCCUPIED (Port 1, bit 7) input pin

P2REN |= BIT5;                // sets pull-up resistor on EAST_BLOCK_OCCUPIED (Port 2, bit 5) input pin

P2REN |= BIT3;                // sets pull-up resistor on WEST_BLOCK_OCCUPIED (Port 2, bit 3) input pin





P1OUT |= BIT7;                // sets pull-up resistor on THIS_BLOCK_OCCUPIED (Port 1, bit 7) to pull-up

P2OUT |= BIT5;                // sets pull-up resistor on EAST_BLOCK_OCCUPIED (Port 2, bit 5) to pull-up

P2OUT |= BIT3;                // sets pull-up resistor on WEST_BLOCK_OCCUPIED (Port 2, bit 3) to pull-up









P1DIR |= BIT0;                       //sets EASTBOUND RED SIGNAL pin #2 (Port 1, Bit 0) to output and set to on

P1OUT |= BIT0;



P1DIR |= BIT4;                       //sets EASTBOUND YELLOW SIGNAL pin #6 (Port 1, Bit 4) to output and set to on

P1OUT |= BIT4;



P1DIR |= BIT5;                       //sets EASTBOUND GREEN SIGNAL pin #7 (Port 1, BIT 5) to output and set to on

P1OUT |= BIT5;



P2DIR |= BIT0;                //sets WESTBOUND RED SIGNAL pin #8 (Port 2, BIT 0) to output and set to on

P2OUT |= BIT0;



P2DIR |= BIT1;                //sets WESTBOUND YELLOW SIGNAL pin #9 (Port 2, Bit 1) to output and set to on

P2OUT |= BIT1;



P2DIR |= BIT2;                //sets WESTBOUND GREEN SIGNAL pin #10 (Port 2, BIT 2) to output and set to on

P2OUT |= BIT2;



P1DIR |= BIT6;                //sets OCCUPANCY_DETECTION_TO_EAST_BLOCK pin to output and sets it to 1 (inactive)

P1OUT |= BIT6;



P2DIR |= BIT4;                //sets OCCUPANCY_DETECTION_TO_WEST_BLOCK pin to output and sets it to 1 (inactive)

P2OUT |= BIT4;





/////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            /////

//     DELAY WITH ALL SIGNALS ILLUMINATED FOR "LAMP CHECK"                                /////

//                                                                                            /////

/////////////////////////////////////////////////////////////////////////////////////////////





for (t; t < start_up_delay; t++)  //delay loop

              {



              }

        t=0;













/////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            /////

//                   Clear Block Occupancy Variables                                           /////

//                                                                                            /////

/////////////////////////////////////////////////////////////////////////////////////////////





THIS_BLOCK_OCCUPIED=0;

EAST_BLOCK_OCCUPIED=0;

WEST_BLOCK_OCCUPIED=0;







/////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            /////

//                   BEGIN MAIN LOOP                                                         /////

//                                                                                            /////

/////////////////////////////////////////////////////////////////////////////////////////////







while (1)

{













//////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            //////

//            Read Block Occupancy in This Block, Block to the East                    //////

//                          and Block to the West                                         //////

//                                                                                            //////

//////////////////////////////////////////////////////////////////////////////////////////////





if ((P1IN & BIT7) == 0)                                      //Test Port 1 input, bit 7 and if zero (active low)

{

       THIS_BLOCK_OCCUPIED = 1;                               // set THIS_BLOCK_OCCUPIED to a non-zero value (true)

}

else

{

       THIS_BLOCK_OCCUPIED = 0;                               // set THIS_BLOCK_OCCUPIED to zero (false)

}





if ((P2IN & BIT5) == 0)                                      //Test Port 2 input, bit 5 and if zero (active low)

{

       EAST_BLOCK_OCCUPIED = 1;                               // set EAST_BLOCK_OCCUPIED to a non-zero value (true)

}

else

{

       EAST_BLOCK_OCCUPIED = 0;                               // set EAST_BLOCK_OCCUPIED to zero value (false)

}





if ((P2IN & BIT3) == 0)                                      //Test Port 2 input, bit 3 and if zero (active low)

{

       WEST_BLOCK_OCCUPIED = 1;                               // set WEST_BLOCK_OCCUPIED to a non-zero value (true)

}

else

{

       WEST_BLOCK_OCCUPIED = 0;                               // set WEST_BLOCK_OCCUPIED to zero value (false)

}















///////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                                                                                               ///

//                          SEND LOCAL BLOCK OCCUPANCY STATUS TO BLOCKS EAST AND WEST                                  ///

//                                                                                                                                                               ///

///////////////////////////////////////////////////////////////////////////////////////////////





if (THIS_BLOCK_OCCUPIED > 0)         //local block occupied

{

       P1OUT &= ~BIT6;                   //set OCCUPANCY_DETECTION_TO_EAST_BLOCK to 0 (active)

       P2OUT &= ~BIT4;                   //set OCCUPANCY_DETECTION_TO_WEST_BLOCK to 0 (active)

}

else                                      //local block not occupied

{

       P1OUT |= BIT6;                    //set OCCUPANCY_DETECTION_TO_EAST_BLOCK to 1 (inactive)

       P2OUT |= BIT4;                    //set OCCUPANCY_DETECTION_TO_WEST_BLOCK to 1 (inactive)

}







///////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                                ///

//            Begin Main Signal Logic                                                          ///

//                                                                                                ///

///////////////////////////////////////////////////////////////////////////////////////////////











       if (THIS_BLOCK_OCCUPIED > 0)         //If this block is occupied then set all signals red, clear other indications



              {

                     P1OUT |= BIT0;             //turn on EASTBOUND RED SIGNAL

                     P1OUT &= ~BIT4;            //turn off the EASTBOUND YELLOW SIGNAL

                     P1OUT &= ~BIT5;            //turn off the EASTBOUND GREEN SIGNAL

                     P2OUT |= BIT0;             //turn on WESTBOUND RED SIGNAL

                     P2OUT &= ~BIT1;            //turn off the WESTBOUND YELLOW SIGNAL

                     P2OUT &= ~BIT2;            //turn off the WESTBOUND GREEN SIGNAL

              }



       else if ( (EAST_BLOCK_OCCUPIED > 0) & (WEST_BLOCK_OCCUPIED > 0)  ) //blocks east and west of here are occupied

                                                                             //east and west signals set to yellow

                                                                             //clear other indications

              {

                     P1OUT &= ~BIT0;            //turn off EASTBOUND RED SIGNAL

                     P1OUT |= BIT4;             //turn on EASTBOUND YELLOW SIGNAL

                     P1OUT &= ~BIT5;            //turn off the EASTBOUND GREEN SIGNAL

                     P2OUT &= ~BIT0;            //turn off WESTBOUND RED SIGNAL

                     P2OUT |= BIT1;             //turn on the WESTBOUND YELLOW SIGNAL

                     P2OUT &= ~BIT2;            //turn off the WESTBOUND GREEN SIGNAL

              }





       else if (EAST_BLOCK_OCCUPIED > 0)           //only the block east of here is occupied

                                                 //eastbound is yellow, westbound is green

                                                 //clear other indications

              {

                     P1OUT &= ~BIT0;            //turn off EASTBOUND RED SIGNAL

                     P1OUT |= BIT4;             //turn on EASTBOUND YELLOW SIGNAL

                     P1OUT &= ~BIT5;            //turn off the EASTBOUND GREEN SIGNAL

                     P2OUT &= ~BIT0;            //turn off WESTBOUND RED SIGNAL

                     P2OUT &= ~BIT1;            //turn off the WESTBOUND YELLOW SIGNAL

                     P2OUT |= BIT2;             //turn on the WESTBOUND GREEN SIGNAL

              }



       else if (WEST_BLOCK_OCCUPIED > 0)       //only the block west of here is occupied

                                                 //westbound is yellow, eastbound is green

                                                 //clear other indications

              {

                     P1OUT &= ~BIT0;            //turn off EASTBOUND RED SIGNAL

                     P1OUT &= ~BIT4;            //turn off the EASTBOUND YELLOW SIGNAL

                     P1OUT |= BIT5;             //turn on the EASTBOUND GREEN SIGNAL

                     P2OUT &= ~BIT0;            //turn off WESTBOUND RED SIGNAL

                     P2OUT |= BIT1;             //turn on the WESTBOUND YELLOW SIGNAL

                     P2OUT &= ~BIT2;            //turn off the WESTBOUND GREEN SIGNAL

              }



       else                                      //all blocks clear - all signals green

                                                 //clear other indications

              {

                     P1OUT &= ~BIT0;            //turn off EASTBOUND RED SIGNAL

                     P1OUT &= ~BIT4;            //turn off the EASTBOUND YELLOW SIGNAL

                     P1OUT |= BIT5;             //turn on the EASTBOUND GREEN SIGNAL

                     P2OUT &= ~BIT0;            //turn off WESTBOUND RED SIGNAL

                     P2OUT &= ~BIT1;            //turn off the WESTBOUND YELLOW SIGNAL

                     P2OUT |= BIT2;             //turn on the WESTBOUND GREEN SIGNAL

              }





/////////////////////////////////////////////////////////////////////////////////////////////

//                                                                                            /////

//     Latch the final signal indications for a time to prevent "chatter"               /////

//                                                                                            /////

/////////////////////////////////////////////////////////////////////////////////////////////









        for (t; t < delay; t++)  //delay loop

              {



              }

        t=0;

}



//end of main



head.