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. |
Very cool Terry! Just wanted to point out that the signal to the east and west blocks that THIS block is occupied is the same signal and can be combined onto a single pin, thus freeing up another pin in case you are going to add detection onto this same launchpad.
ReplyDeleteIn fact the signal you are using to tell THIS launchpad that THIS block is occupied could go directly to the signalling to the east and west blocks, freeing up another pin!!!
ReplyDeleteToni:
ReplyDeleteYou are absolutely right! But that's the beauty of this LaunchPad effort vs. commercial products, you can customize the functionality to suit your situation.
I did have a couple of reasons for doing it this way. First, not every train detector is active low. I chose active low because I have a bunch of active low optical detectors (borrowed from the robotics community). By bringing the signal into the LaunchPad, you can accommodate detectors of either sense.
Second, I wanted this code to the the foundation for more complex signaling situations (e.g. junctions, crossings and interlockings) and in these cases this detector's state will have to be combined (logically) with other detectors to arrive at the final signal indication.
But as I said, customization is the name of the game so, as they say at Burger King - "have it your way."
Terry
really cool Terry. I am a newbie to all this.just got a couple of launchpads recently delivered to New Zealand freight free. I have managed to load the drivers and have installed code composer studio v5. It was quite exciting when i connected the launchpads to my PC for the first time . I want to have a fairly basic signalling system ,as you described, on my NZ120 scale modular layout. still getting modules set up with track,etc. I have no train detectors as yet for blocks, but am using optical detectors for my purchased railway crossing module. They seem to work great, so will probably try and get some more for blocks. Only one three light signal so far. I purchased a few 2mm LEDs in the 3 colours i need and was planning on building some more signals.we will see how that works out in NZ120.So many questions, First one is what are you using to power the launchpads and how do you combine 2 on the same power.I have an old desktop in my shed with a usb connection on the back that i have added a type of usb tree to it with about 5 connections available .would that work? My head hurts from all the input at present. In my 50's , and electronics is fascinating to me , but very challenging. Oh , I am running a NCE powercab.thanks again John
ReplyDeleteJohn:
DeleteEasiest answer first. Your USB 'tree' (called a 'hub' here in the US) will work. It will supply power to your LaunchPads. One variation that you may want to consider is to buy a 'powered' hub. This type of hub has its own power suppoy and you can then power your LaunchPads without your computer. There is a tab at the top of the LaunchPad blog labeled "Powering your LaunchPads"; click on it and you'll be taken to a page that explains all of this.
I have used this infrared reflective sensor under teh ties shining up to detect the train. It may be a little large for 1/120 narrow gauge.
http://www.goodluckbuy.com/infrared-ir-reflectance-sensor-module-for-smart-car-lm393-.html
One complication with this sensor is that it works on 5V, while the LaunchPad works on 3V, so a separate power supply has to be provided for the sensors. I noticed this other sensor that does work on 3V.
http://www.goodluckbuy.com/dc-3v-6v-reflect-infrared-ir-sensor-barrier-photoelectric-switch-switching.html
I will order one of these and if it works well with the LaunchPad I'll do a project with it.
Don't be daunted by the LaunchPads and your age. I have a few years on you and I can still work with these no problem. I intend to keep this blog simple for all to use.
Terry
Thanks Terry. i found the link to powering launchpad after i had finished my comments.duh!
ReplyDeleteI had a look at the links above and wondered how you would mount them under installed Atlas code 55 N scale track .For the crossing module ,it has optical sensors, but the tiny light unit is about 3mm in diameter with 2 wires running to the circuit board and you just drill a slightly smaller hole between sleepers so it sits level with the sleepers. hardly noticeable. I guess the units you mention are a different design or is there a way of using wires from these units to mount sensor under track. Like i said newbie!! John
Hi Terry,Its been a while .other projects have kept me busy. I was going to order a few of the 3Vsensors you mentioned above as well as a powered hub from the same site. Is this suitable
ReplyDeletehttp://www.goodluckbuy.com/10-ports-usb-2-0-hub-high-speed-with-power-adapter.html
I would have to provide an adapter plug to fit our electrical wall sockets. cheers John
I really enjoyed this site. This is such a Great resource that you are providing and you give it away for free. It gives in depth information. Thanks for this valuable information. Visit... cell phone detector
ReplyDelete