Main Line Program Code




This page contains the program code portion of the Basic Stamp 2 program that controls the main line automated functions. The complete code and documentation can be downloaded using this link:  ReverseLoop.zip

' ======================================================================
' ReverseLoop.bs2                                             2-05-2004

'{$STAMP BS2}

' Output bit definitions.
T1Close        CON  $FE00   ' 74154 pin 1
T1Open         CON  $FE01   ' 74154 pin 2
T2Close        CON  $FE02   ' 74154 pin 3
T2Open         CON  $FE03   ' 74154 pin 4
T3Close        CON  $FE05   ' 74154 pin 6   This turnout is mounted
T3Open         CON  $FE04   ' 74154 pin 5   backwards on the layout.
T4Close        CON  $FE06   ' 74154 pin 7   Spare
T4Open         CON  $FE07   ' 74154 pin 8   Spare
T5Close        CON  $FE09   ' 74154 pin 10  This turnout operates
T5Open         CON  $FE08   ' 74154 pin 9   normally open.
T6Close        CON  $FE0A   ' 74154 pin 11
T6Open         CON  $FE0B   ' 74154 pin 13
T7Close        CON  $FE0C   ' 74154 pin 14
T7Open         CON  $FE0D   ' 74154 pin 15
T8Close        CON  $FE0E   ' 74154 pin 16  Spare
T8Open         CON  $FE0F   ' 74154 pin 17  Spare

' Input bit definitions
Sensor1        CON  $EF00  ' pin 4
Sensor2        CON  $EF01  ' pin 3
Sensor3        CON  $EF02  ' pin 2
Block1         CON  $EF03  ' pin 1
Block2         CON  $EF04  ' pin 15
T1Pos          CON  $EF05  ' pin 14
T2Pos          CON  $EF06  ' pin 13
T3Pos          CON  $EF07  ' pin 12
T5Pos          CON  $DF00  ' pin 4
T6Pos          CON  $DF01  ' pin 3
T7Pos          CON  $DF02  ' pin 2
Sensor5        CON  $DF03  ' pin 1
Sensor6        CON  $DF04  ' pin 15
Sensor7        CON  $DF05  ' pin 14
Sensor8        CON  $DF06  ' pin 13
Sensor9        CON  $DF07  ' pin 12
T5Req          CON  $BF00  ' pin 4   Manual T5 toggle (active low)
T6Req          CON  $BF01  ' pin 3   Manual T6 toggle (active low)
T7ReqOpen      CON  $BF02  ' pin 2   Manual T7 Open (active low)
T7ReqClose     CON  $BF03  ' pin 1   Manual T7 Close track (active low)
Sensor10       CON  $BF04  ' pin 15
Sensor11       CON  $BF05  ' pin 14
Sensor12       CON  $BF06  ' pin 13
Sensor13       CON  $BF07  ' pin 12

ChipReset      CON  $FF    ' Inactive chip enable bits
RelayLed       CON  $FD    ' 74HC259 chip select

' Other program constants
DefaultDir     CON  %1111111101101111
LedPin         CON  5
SoundPin       CON  6
ModePin        VAR  IN7

' Retrigger timeout constants and variables
RetrigValue    CON  $1A0
LongRetrig     CON  $FF
ShortRetrig    CON  $80
OneSecond      CON  $40
RetrigCount    VAR  WORD  ' S1 retriggerable timeout variable
S5Count        VAR  BYTE  ' S5 siding retriggerable timeout variable
S6Count        VAR  BYTE  ' S6 siding retriggerable timeout variable
WyeCount       VAR  BYTE  ' Wye retriggerable timeout variable

' Define other working variables
BitValue       VAR  BIT           ' Set by ReadBit
BitAddr        VAR  WORD          ' I/O bit address
BitAddrHigh    VAR  BitAddr.HIGHBYTE
BitAddrLow     VAR  BitAddr.LOWBYTE
LedCount       VAR  BYTE          ' Countdown time between Led state toggle
TurnoutSel     VAR  WORD          ' Set to turnout control output bit
TurnoutSelHigh VAR  TurnoutSel.HIGHBYTE
TurnoutSelLow  VAR  TurnoutSel.LOWBYTE
TurnoutPos     VAR  WORD          ' Set to turnout sensor input bit
TurnoutPosHigh VAR  TurnoutPos.HIGHBYTE
TurnoutPosLow  VAR  TurnoutPos.LOWBYTE
TurnoutTst     VAR  BIT           ' Used to verify turnout position
TurnoutLast    VAR  WORD          ' Last commanded turnout
TurnoutCount   VAR  NIB           ' Count for TurnoutLast
SoundCount     VAR  BYTE          ' Countdown time between sound "beeps"
Control        VAR  WORD          ' Program control bits
S1NoCheck      VAR  Control.BIT0  ' 0 = normal, 1 = ignore S1 input
LastB1B2       VAR  Control.BIT1  ' 0 = Block2, 1 = Block1
TrainWreck     VAR  Control.BIT2  ' 0 = normal, 1 = both sidings occupied
TurnoutBad     VAR  Control.BIT3  ' 0 = normal, 1 = turnout mis-position
T5ReqState     VAR  Control.BIT4  ' 0 = normal, 1 = request processed
T6ReqState     VAR  Control.BIT5  ' 0 = normal, 1 = request processed
T7ReqCState    VAR  Control.BIT6  ' 0 = normal, 1 = request processed
T7ReqOState    VAR  Control.BIT7  ' 0 = normal, 1 = request processed
B6Inbound      VAR  Control.BIT8  ' 0 = normal, 1 = B6 train inbound at S7
T5Locked       VAR  Control.BIT9  ' 0 = normal, 1 = inhibit auto-reposition
T6Locked       VAR  Control.BIT10 ' 0 = normal, 1 = inhibit auto-reposition
T5LockCount    VAR  BYTE          ' Time between button depressions
T6LockCount    VAR  BYTE          ' Time between button depressions
Relay          VAR  BYTE          ' Relay and hidden position control bits
HiddenBits     VAR  Relay.HIGHNIB
P1Relay        VAR  Relay.BIT0    ' 74HC259 pin 4
P2Relay        VAR  Relay.BIT1    ' 74HC259 pin 5
P3Relay        VAR  Relay.BIT2    ' 74HC259 pin 6
P4Relay        VAR  Relay.BIT3    ' 74HC259 pin 7
LedPos1a       VAR  Relay.BIT4    ' 74HC259 pin 9
LedPos1b       VAR  Relay.BIT5    ' 74HC259 pin 10
LedPos1c       VAR  Relay.BIT6    ' 74HC259 pin 11
LedPos1d       VAR  Relay.BIT7    ' 74HC259 pin 12
RelayCnt       VAR  NIB
RelayWork      VAR  BYTE

'--------------------------------------------------------------------------

ProgramStart:
   DIRS = DefaultDir                ' Set default I/O direction bits
   OUTH = ChipReset                 ' Reset chip enable bits
   IF ModePin = 0 THEN Init0        ' Jump if not in manual mode
ExerciseLoop:
   GOSUB Exercise                   ' Exercise turnouts and relays
   IF ModePin = 1 THEN ExerciseLoop

'--------------------------------------------------------------------------
' Set initial turnout positions based on current sensor states.

Init0:
   GOSUB ResetAll                   ' Reset all program variables
   BitAddr = Sensor1                ' Select sensor S1
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN Init1       ' Jump if sensor is inactive
   GOSUB Sen1Sub
Init1:
   BitAddr = Sensor2                ' Select sensor S2
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN Init2       ' Jump if sensor is inactive
   GOSUB Sen2Sub
Init2:
   BitAddr = Sensor3                ' Select sensor S3
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN Init3       ' Jump if sensor is inactive
   GOSUB Sen3Sub
Init3:
   BitAddr = Sensor5                ' Select sensor S5
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN Init4       ' Jump if sensor is inactive
   GOSUB SidingSetS5                ' Set T5 position
Init4:
   BitAddr = Sensor6                ' Select sensor S6
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop    ' Jump if sensor is inactive
   GOSUB SidingSetS6                ' Set T6 position

'--------------------------------------------------------------------------
' Begin main program loop. Each loop decrements the LedCount (heartbeat)
' variable. The call to RetrigCountdown decrements the RetrigCount variable.
' The call to SidingCheck decrements the S5Count and S6Count variables. The
' WyeCount variable inhibits manual change of the T7 turnout. Calls are
' made to subroutines for processing of specific sensor inputs, buttons,
' and program control flags.

MainLoop:
   IF LedCount <> 0 THEN MainLoop1  ' Jump if no Led change yet
   TOGGLE LedPin                    ' Toggle Led state.
   LedCount = OneSecond / 2
MainLoop1:
   LedCount = LedCount - 1          ' Heartbeat Led counter
   GOSUB RetrigCountdown            ' Process S1 time delay counter
   GOSUB SidingCheck                ' Check siding counters
   IF WyeCount = 0 THEN MainLoop2   ' Non-zero inhibits manual change
   WyeCount = WyeCount - 1          ' of turnout T7.
MainLoop2:
   BitAddr = Sensor1                ' Select sensor S1
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop3   ' Jump if Sensor S1 is inactive
   GOSUB S1Retrigger
MainLoop3:
   BitAddr = Sensor2                ' Select sensor S2
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop4   ' Jump if Sensor S2 is inactive
   GOSUB Sen2Sub
MainLoop4:
   BitAddr = Sensor3                ' Select sensor S3
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop5   ' Jump if Sensor S3 is inactive
   GOSUB Sen3Sub
MainLoop5:
   BitAddr = Sensor5                ' Select sensor S5
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop6   ' Jump if Sensor S5 is inactive
   GOSUB SidingSetS5
MainLoop6:
   BitAddr = Sensor6                ' Select sensor S6
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop7   ' Jump if Sensor S6 is inactive
   GOSUB SidingSetS6
MainLoop7:
   BitAddr = Sensor7                ' Select sensor S7
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop8   ' Jump if Sensor S7 is inactive
   GOSUB Sen7Sub
MainLoop8:
   BitAddr = Sensor8                ' Select sensor S8
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoop9   ' Jump if Sensor S8 is inactive
   GOSUB Sen8Sub
MainLoop9:
   BitAddr = Sensor9                ' Select sensor S9
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoopA   ' Jump if Sensor S9 is inactive
   GOSUB Sen9Sub
MainLoopA:
   RelayCnt = HiddenBits            ' Remember current states
   HiddenBits = 0                   ' Reset all bits
   BitAddr = Sensor10               ' Select sensor S10
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoopB   ' Jump if Sensor S10 is inactive
   LedPos1a = 1                     ' Illuminate indicator
MainLoopB:
   BitAddr = Sensor11               ' Select sensor S11
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoopC   ' Jump if Sensor S11 is inactive
   LedPos1b = 1                     ' Illuminate indicator
MainLoopC:
   BitAddr = Sensor12               ' Select sensor S12
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoopD   ' Jump if Sensor S12 is inactive
   LedPos1c = 1                     ' Illuminate indicator
MainLoopD:
   BitAddr = Sensor13               ' Select sensor S13
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 0 THEN MainLoopE   ' Jump if Sensor S13 is inactive
   LedPos1d = 1                     ' Illuminate indicator
MainLoopE:
   IF RelayCnt = HiddenBits THEN MainLoopF  ' Jump if no change
   GOSUB SetRelays                  ' Set hidden train position indicators
MainLoopF:
   GOSUB InputCheck            ' Check for manual turnout position input
   IF TrainWreck = 0 AND TurnoutBad = 0 THEN MainLoop
   GOSUB SoundTone
   PAUSE 100
   GOTO MainLoop

'--------------------------------------------------------------------------
' Run this code when train sensor S1 is active. If both blocks are not
' occupied, then the LastB1B2 variable controls which siding we use. This
' adds some variability to things.

Sen1Sub:
   BitAddr = Block1                 ' Select Block1 detector bit
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 1 THEN Sen1Sub2    ' Jump if B1 siding is occupied
   BitAddr = Block2                 ' Select Block2 detector bit
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 1 THEN Sen1Sub1    ' Jump if B2 siding is occupied
   LastB1B2 = ~LastB1B2             ' Neither siding occupied.
   IF LastB1B2 = 0 THEN Sen1Sub3    ' Alternate B1 and B2 siding use
Sen1Sub1:
   TurnoutSel = T1Close
   TurnoutPos = T1Pos
   GOSUB Turnout                    ' Turnout T1 "straight"
   TurnoutSel = T2Close
   TurnoutPos = T2Pos
   GOSUB Turnout                    ' Turnout T2 "straight"
   TurnoutSel = T3Close
   TurnoutPos = T3Pos
   GOSUB Turnout                    ' Turnout T3 "straight"
   P1Relay = 0                      ' Inbound P1 relay direction
   GOSUB SetRelays                  ' Set polarity relays
   RETURN

Sen1Sub2:
   BitAddr = Block2                 ' Select Block2 detector bit
   GOSUB ReadBit                    ' Read the bit
   IF BitValue = 1 THEN Sen1Sub4    ' Jump if B2 siding is occupied
Sen1Sub3:
   TurnoutSel = T1Open
   TurnoutPos = T1Pos
   GOSUB Turnout                    ' Turnout T1 "open"
   TurnoutSel = T3Close
   TurnoutPos = T3Pos
   GOSUB Turnout                    ' Turnout T3 "straight"
   P2Relay = 0                      ' Inbound P2 relay direction
   GOSUB SetRelays                  ' Set polarity relays
   TurnoutSel = T2Close
   TurnoutPos = T2Pos
   GOSUB Turnout                    ' Turnout T2 "straight"
   RETURN

Sen1Sub4:
   SoundCount = 5
   TrainWreck = 1                   ' Both sidings are occupied!
   RETURN

'--------------------------------------------------------------------------
' Run this code when train sensor S2 is active.

Sen2Sub:
   P1Relay = 1                      ' Outbound P1 relay direction
   GOSUB SetRelays                  ' Set polarity relays
   TurnoutSel = T2Open
   TurnoutPos = T2Pos
   GOSUB Turnout                    ' Turnout T2 "open"
   TurnoutSel = T1Close
   TurnoutPos = T1Pos
   GOSUB Turnout                    ' Turnout T1 "straight"
   RetrigCount = 0                  ' Stop any inprogress timeout
   S1NoCheck = 1                    ' Ignore S1 signal
   RETURN

'--------------------------------------------------------------------------
' Run this code when train sensor S3 is active.

Sen3Sub:
   P2Relay = 1                      ' Outbound P2 relay direction
   GOSUB SetRelays                  ' Set polarity relays
   TurnoutSel = T3Open
   TurnoutPos = T3Pos
   GOSUB Turnout                    ' Turnout T3 "open"
   TurnoutSel = T1Open
   TurnoutPos = T1Pos
   GOSUB Turnout                    ' Turnout T1 "open"
   RetrigCount = 0                  ' Stop any inprogress timeout
   S1NoCheck = 1                    ' Ignore S1 signal
   RETURN

'--------------------------------------------------------------------------
' Run this code when train sensor S5 or S6 is active. Both turnouts are
' returned to their default positions when their respective siding count
' is 1.

SidingSetS5:
   IF S5Count <> 0 THEN SidingS5Exit
   TurnoutSel = T5Close
   TurnoutPos = T5Pos
   GOSUB Turnout                    ' Turnout T5 "closed"
SidingS5Exit:
   S5Count = ShortRetrig            ' Use short delay count
   RETURN

SidingSetS6:
   IF S6Count <> 0 THEN SidingS6Exit
   TurnoutSel = T6Open
   TurnoutPos = T6Pos
   GOSUB Turnout                    ' Turnout T6 "open"
SidingS6Exit:
   S6Count = ShortRetrig            ' Use short delay count
   RETURN

SidingCheck:
   If S5Count = 0 THEN SidingChk1   ' Jump of no timeout inprogress
   S5Count = S5Count - 1
   IF S5Count <> 0 THEN SidingChk1  ' Jump if S5Count still not 0.
   IF T5Locked = 1 THEN SidingChk1  ' Jump if locked
   TurnoutSel = T5Open
   TurnoutPos = T5Pos
   GOSUB Turnout                    ' Turnout T5 "open"
SidingChk1:
   If S6Count = 0 THEN SidingChk2   ' Jump of no timeout inprogress
   S6Count = S6Count - 1
   IF S6Count <> 0 THEN SidingChk2  ' Jump if S6Count still not 0.
   IF T6Locked = 1 THEN SidingChk2  ' Jump if locked
   TurnoutSel = T6Close
   TurnoutPos = T6Pos
   GOSUB Turnout                    ' Turnout T6 "closed"
SidingChk2:
   IF T5LockCount = 0 THEN SidingChk3 ' Jump T5 lock timeout not in progress
   T5LockCount = T5LockCount - 1
SidingChk3:
   IF T6LockCount = 0 THEN SidingExit ' Jump T6 lock timeout not in progress
   T6LockCount = T6LockCount - 1
SidingExit:
   RETURN

'--------------------------------------------------------------------------
' Run this code when train sensor S7, S8 or S9 is active. This code controls
' the track polarity of the lead and yard tracks. The position of turnout 7
' is used to set the polarity relay.

Sen7Sub:
   GOSUB SetP3Relay
   RETURN

Sen8Sub:
   TurnoutSel = T7Close
   TurnoutPos = T7Pos
   GOSUB Turnout                    ' Turnout T7 "closed"
   GOSUB SetP3Relay
   RETURN

Sen9Sub:
   TurnoutSel = T7Open
   TurnoutPos = T7Pos
   GOSUB Turnout                    ' Turnout T7 "open"
   GOSUB SetP3Relay
   RETURN

SetP3Relay:
   BitAddr = T7Pos                  ' Select T7 position bit
   GOSUB ReadBit                    ' Read the bit
   P3Relay = BitValue               ' Set P3 control bit
   GOSUB SetRelays                  ' Set polarity relays
   WyeCount = ShortRetrig
   RETURN

'--------------------------------------------------------------------------
' Special processing for S1 sensor. Call this routine anytime Sensor S1
' detects the presence of a train. Sensor S1 has a flag bit and RetrigCount
' associated with it. S1NoCheck when set (1) indicates that the code should
' ignore acting on an active S1 signal. It is set in Sen2Sub or Sen3Sub by
' an outbound train leaving a siding. It is reset (0) when the first active
' S1 signal is detected indicating an outbound train is present.
'
' RetrigCount is loaded when a train is detected by sensor S1. RetrigCount
' is decremented in each cycle of the MainLoop. Additional S1 detection
' signals within one second of the initial one cause the countdown to
' restart. After one second, a closely following train will be detected and
' directed to the free reverse loop siding (if any).
'
' Once RetrigCount reaches zero (approximately 5 seconds of S1 inactive),
' a train is assumed to be no longer in transit across the turnouts. The
' RetrigCountDown code will reset the turnouts and power polarity relays.
'
' S1 is also used as a trigger point for S1Outbound trains. Turnout T5 is
' set closed at the Midway siding if B5 is occupied.

S1Retrigger:
   IF RetrigCount > (RetrigValue - OneSecond) THEN S1Retrigger2
   IF S1NoCheck = 0 THEN S1Retrigger1  ' Inbound train
   S1NoCheck = 0                       ' Outbound train
   GOTO S1Retrigger2
S1Retrigger1:
   GOSUB Sen1Sub
S1Retrigger2:
   RetrigCount = RetrigValue
   RETURN

'--------------------------------------------------------------------------
' Run this code for processing the countdown timing for Sensor S1. When
' countdown equals 1, time delay is over. Reset turnouts and power relays
' to inbound settings.

RetrigCountdown:
   IF RetrigCount = 0 THEN RetrigExit   ' Jump if no retrigger operation
   RetrigCount = RetrigCount - 1
   IF RetrigCount <> 1 THEN RetrigExit  ' Jump if RetrigCount not 1.
RetrigNoCount:
   TurnoutSel = T1Close
   TurnoutPos = T1Pos
   GOSUB Turnout                        ' Turnout T1 "straight"
   TurnoutSel = T2Close
   TurnoutPos = T2Pos
   GOSUB Turnout                        ' Turnout T2 "straight"
   TurnoutSel = T3Close
   TurnoutPos = T3Pos
   GOSUB Turnout                        ' Turnout T3 "straight"
   P1Relay = 0                          ' Inbound P1 relay direction
   P2Relay = 0                          ' Inbound P2 relay direction
   GOSUB SetRelays                      ' Set polarity relays
RetrigExit:
   RETURN

'--------------------------------------------------------------------------
' Run this code to check for manual turnout position requests. Set turnouts
' T5, T6, or T7 appropriately. Request are ignored if the respective
' retriggerable timeout counter is non-zero. These inputs are active low.

InputCheck:
   BitAddr = T5Req                      ' Select T5 toggle request input
   GOSUB ReadBit                        ' Read the bit
   IF BitValue = T5ReqState THEN InpChk4  ' Jump if no current request
   IF BitValue = 1 THEN InpChk2         ' Jump if inactive
   IF S5Count <> 0 THEN InpChk3         ' Ignore input if inprogress operation
   IF T5LockCount = 0 THEN InpChk1      ' Jump lock timeout not in progress
   T5Locked = 1                         ' Lock turnout position
   T5LockCount = 0                      ' Reset timer
   FREQOUT SoundPin, 25, 2092
   T6Locked = 0                         ' Unlock T6
   TurnoutSel = T6Close
   TurnoutPos = T6Pos
   GOSUB Turnout                        ' Make sure T6 is "closed"
   GOTO InpChk3
InpChk1:
   GOSUB ToggleT5
   T5ReqState = 0
   T5Locked = 0                         ' Unlock turnout position
   T5LockCount = 8                      ' Set count for double press detection
   GOTO InpChk3
InpChk2:
   T5ReqState = 1
InpChk3:
   PAUSE 100                            ' Button debounce
InpChk4:
   BitAddr = T6Req                      ' Select T6 toggle request input
   GOSUB ReadBit                        ' Read the bit
   IF BitValue = T6ReqState THEN InpChk8  ' Jump if no current request
   IF BitValue = 1 THEN InpChk6         ' Jump if inactive
   IF S6Count <> 0 THEN InpChk7         ' Ignore input if inprogress operation
   IF T6LockCount = 0 THEN InpChk5      ' Jump lock timeout not in progress
   T6Locked = 1                         ' Lock turnout position
   T6LockCount = 0                      ' Reset timer
   FREQOUT SoundPin, 25, 2092
   T5Locked = 0                         ' Unlock T5
   TurnoutSel = T5Open
   TurnoutPos = T5Pos
   GOSUB Turnout                        ' Make sure T5 is "open"
   GOTO InpChk7
InpChk5:
   GOSUB ToggleT6
   T6ReqState = 0
   T6Locked = 0                         ' Unlock turnout position
   T6LockCount = 8                      ' Set count for double press detection
   GOTO InpChk7
InpChk6:
   T6ReqState = 1
InpChk7:
   PAUSE 100                            ' Button debounce
InpChk8:
   BitAddr = T7ReqClose                 ' Select T7 close request input
   GOSUB ReadBit                        ' Read the bit
   IF BitValue = T7ReqCState THEN InpChkB ' Jump if no current request
   IF BitValue = 1 THEN InpChk9         ' Jump if inactive
   IF WyeCount <> 0 THEN InpChkA        ' Ignore input if inprogress operation
   GOSUB Sen8Sub
   WyeCount = 0                         ' No timeout for manual operation
   T7ReqCState = 0
   GOTO InpChkA
InpChk9:
   T7ReqCState = 1
InpChkA:
   PAUSE 20                             ' Button debounce
InpChkB:
   BitAddr = T7ReqOpen                  ' Select T7 open request input
   GOSUB ReadBit                        ' Read the bit
   IF BitValue = T7ReqOState THEN InpChkE ' Jump if no current request
   IF BitValue = 1 THEN InpChkC         ' Jump if inactive
   IF WyeCount <> 0 THEN InpChkD        ' Ignore input if inprogress operation
   GOSUB Sen9Sub
   WyeCount = 0                         ' No timeout for manual operation
   T7ReqOState = 0
   GOTO InpChkD
InpChkC:
   T7ReqOState = 1
InpChkD:
   PAUSE 20                             ' Button debounce
InpChkE:
   RETURN

ToggleT5:
   BitAddr = T5Pos                      ' Select T5 position bit
   GOSUB ReadBit                        ' Read the bit
   TurnoutSel = T5Close
   IF BitValue = 0 THEN ToggleT5_1
   TurnoutSel = T5Open
ToggleT5_1:
   TurnoutPos = T5Pos
   GOTO ToggleT6_2

ToggleT6:
   BitAddr = T6Pos                      ' Select T6 position bit
   GOSUB ReadBit                        ' Read the bit
   TurnoutSel = T6Open
   IF BitValue = 0 THEN ToggleT6_1
   TurnoutSel = T6Close
ToggleT6_1:
   TurnoutPos = T6Pos
ToggleT6_2:
   GOSUB Turnout                        ' Set Turnout position
ToggleExit:
   RETURN

'--------------------------------------------------------------------------
' Set turnout based upon values in TurnoutSel and TurnoutPos. This routine
' takes advantage of the fact that an 'open' command is an odd number and
' an open turnout sensor reports a 1.
'
' The TurnoutLast and TurnoutCount variables are used to protect aganst
' switch machine burnout in the event of a malfunctioning turnout position
' sensor. A maximum of 5 tries to position a turnout will be attempted.

Turnout:
   IF TurnoutLast <> TurnoutSel THEN Turnout1 ' Jump if not the same turnout
   IF TurnoutCount > 4 THEN Turnout3       ' Jump if more than 4 attempts
   TurnoutCount = TurnoutCount + 1         ' Increment counter
   GOTO Turnout2
Turnout1:
   TurnoutCount = 0                        ' Reset attempt counter
   TurnoutLast = TurnoutSel                ' Remember last commanded turnout
Turnout2:
   TurnoutTst = TurnoutSelLow // 2         ' Compute desired position value
   BitAddr = TurnoutPos                    ' Select turnout sensor bit
   GOSUB ReadBit                           ' Read the bit
   IF TurnoutTst = BitValue THEN Turnout3  ' Return if already positioned
   OUTA = TurnoutSelLow                    ' Select the turnout coil; bits 3-0
   OUTH = TurnoutSelHigh                   ' Energize it
   PAUSE 200                       ' Adjust for reliable turnout operation
   OUTH = ChipReset                        ' De-energize the turnout coil

' Verify the turnout moved to the commanded position.

   PAUSE 50                                ' Make sure TurnoutPos bit is stable
   BitAddr = TurnoutPos                    ' Select turnout sensor bit
   GOSUB ReadBit                           ' Read the bit
   IF TurnoutTst = BitValue THEN Turnout3
   SoundCount = (TurnoutSelLow / 2) + 1
   TurnoutBad = 1
Turnout3:
   RETURN

'--------------------------------------------------------------------------
' Set power polarity relays and hidden train position indicators based upon
' the current value of the Relay variable.

SetRelays:
   RelayWork = Relay
   FOR RelayCnt = 0 TO 7
      RelayCnt.BIT3 = RelayWork.BIT0
      OUTA = RelayCnt                      ' Set address and data
      OUTH = RelayLed                      ' Store data into selected latch
      OUTH = ChipReset                     ' Deselect 74HC259
      RelayCnt.BIT3 = 0                    ' Reset data bit
      RelayWork = RelayWork >> 1
   NEXT
   RETURN

'--------------------------------------------------------------------------
' Sound warning tone. SoundCount specifies the number of "beeps" to sound.
' TrainWreck and TurnoutBad specify which tone frequency to use. The TrainWreck
' and TurnoutBad flags are reset once the required tone count is sounded.

SoundTone:
   IF TrainWreck = 0 THEN SoundTone2
   FREQOUT SoundPin, 100, 2092
   IF SoundCount = 0 THEN SoundTone1
   SoundCount = SoundCount - 1
   IF SoundCount <> 0 THEN SoundTone4
   PAUSE 2000
SoundTone1:
   TrainWreck = 0
   GOTO SoundTone4

SoundTone2:
   IF TurnoutBad = 0 THEN SoundTone4
   FREQOUT SoundPin, 100, 880
   IF SoundCount = 0 THEN SoundTone3
   SoundCount = SoundCount - 1
   IF SoundCount <> 0 THEN SoundTone4
   PAUSE 2000
SoundTone3:
   TurnoutBad = 0
SoundTone4:
   RETURN

'--------------------------------------------------------------------------
' Reads the bit specified by BitAddr and stores its value in BitValue.

ReadBit:
   OUTA = BitAddrLow       ' Set input selection bits 3-0
   OUTH = BitAddrHigh      ' Set chip enable bits 15-8
   BitValue = IN4          ' Read bit value from input pin
   OUTH = ChipReset        ' Deselect input bit
   RETURN

'--------------------------------------------------------------------------
' Reset chip enable bits so we don't damage the turnout coils. Then set the
' default turnout positions, power relay states, and program working
' variables.

ResetAll:
   GOSUB RetrigNoCount              ' T1, T2, and T3 "closed"
   TurnoutSel = T5Open
   TurnoutPos = T5Pos
   GOSUB Turnout                    ' Turnout T5 "open"
   TurnoutSel = T6Close
   TurnoutPos = T6Pos
   GOSUB Turnout                    ' Turnout T6 "closed"
   Relay = 0                        ' Initialize relay value
   GOSUB SetP3Relay                 ' Set P3 relay to T7 position.
   TurnoutLast = 0
   TurnoutCount = 0
   Control = $00F0                  ' Reset all program control bits
   RetrigCount = 0                  ' Reset all retrigger counts
   S5Count = 0                      ' Reset T5 delay counter
   S6Count = 0                      ' Reset T6 delay counter
   WyeCount = 0                     ' Reset T7 delay counter
   RETURN

'--------------------------------------------------------------------------
' This routine and the following data is used when in manual mode to exercise
' the turnouts. S5Count and S6Count are used for working variables.
'
Exercise:
   TOGGLE LedPin                    ' Toggle Led state.
   READ Taddr, S6Count
   FOR S5Count = 1 TO S6Count STEP 2
      READ Taddr+S5Count, TurnoutSelLow
      READ Taddr+S5Count+1, TurnoutSelHigh
      READ Tpos+S5Count, TurnoutPosLow
      READ Tpos+S5Count+1, TurnoutPosHigh
      GOSUB Turnout
      IF TurnoutBad = 0 THEN Exercise2
Exercise1:
      GOSUB SoundTone
      PAUSE 100
      IF SoundCount <> 0 THEN Exercise1
Exercise2:
      PAUSE 500
   NEXT
Exercise3:
   Relay = 1
Exercise4:
   GOSUB SetRelays
   PAUSE 500
   Relay = Relay << 1               ' Select next relay
   IF Relay <> 0 THEN Exercise4
   GOSUB SetRelays                  ' Reset last relay
   PAUSE 500
   RETURN

Taddr  DATA  24, WORD T1Open, WORD T1Close, WORD T2Open, WORD T2Close, 
                 WORD T3Open, WORD T3Close, WORD T5Close, WORD T5Open, 
                 WORD T6Open, WORD T6Close, WORD T7Open, WORD T7Close
Tpos   DATA  24, WORD T1Pos, WORD T1Pos, WORD T2Pos, WORD T2Pos, WORD T3Pos, 
                 WORD T3Pos, WORD T5Pos, WORD T5Pos, WORD T6Pos, WORD T6Pos, 
                 WORD T7Pos, WORD T7Pos
      

Color Bar

Navigation:   Main line   D&B Home   Buczynski.com Index

Copyright © 2006 Don Buczynski
San Diego, California