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