Yard Program Code |
This page contains the source code portion of the Basic Stamp 2 program that controls the yard and industrial spur turnouts. The complete code and documentation can be downloaded using this link: YardRoute.zip
' ====================================================================== ' YardRoute.bs2 9-02-2003 '{$STAMP BS2} DefaultDir CON %1111101011110000 ' Basic Stamp pin definitions ' ' Pin 0 = address 0 ' Pin 1 = address 1 ' Pin 2 = address 4 ' Pin 3 = address 8 ' Pin 4 = Heartbeat Led ' Pin 5 = KeyPad 1 first entry Led ' Pin 6 = KeyPad 2 first entry Led ' Pin 7 = Sound pin ' Pin 8 = KeyPad 1 data available ' Pin 9 = KeyPad 1 chip enable ' Pin 10 = KeyPad 2 data available ' Pin 11 = KeyPad 2 chip enable ' Pin 12 = Turnouts 1-8 chip enable ' Pin 13 = Turnouts 9-16 chip enable ' Pin 14 = Available ' Pin 15 = Available ' Turnout bit definitions. Upper nibble used for 74154 chip enable. ' Lower nibble used for 74154 address input. Y1Close CON $E0 Y1Open CON $E1 Y2Close CON $E2 Y2Open CON $E3 Y3Close CON $E4 Y3Open CON $E5 Y4Close CON $E6 Y4Open CON $E7 Y5Close CON $E8 ' Also causes Y6 to close Y5Open CON $E9 ' Also causes Y6 to open Y6Close CON $E8 ' Also causes Y5 to close Y6Open CON $E9 ' Also causes Y5 to open Y7Close CON $EA ' Also causes Y8 to close Y7Open CON $EB ' Also causes Y8 to open Y8Close CON $EA ' Also causes Y7 to close Y8Open CON $EB ' Also causes Y7 to open Y9Close CON $EC Y9Open CON $ED Y10Close CON $EE Y10Open CON $EF Y11Close CON $D0 Y11Open CON $D1 Y12Close CON $D2 Y12Open CON $D3 Y13Close CON $D4 Y13Open CON $D5 Y14Close CON $D6 Y14Open CON $D7 Y15Close CON $D8 Y15Open CON $D9 Y16Close CON $DA Y16Open CON $DB Y17Close CON $DC Y17Open CON $DD Y18Close CON $DE Y18Open CON $DF Reset CON $FA ' Chip enable bits ' Define working variables Tracks VAR BYTE ' User entered route FromTrack VAR Tracks.HIGHNIB ToTrack VAR Tracks.LOWNIB LastTracks VAR BYTE ' Previous user entry RouteIndex VAR BYTE ' Index of validated route number Route VAR WORD ' Location of route to use EntryCount VAR BYTE ' Number of bytes in route entry (excluding count byte) Index VAR BYTE ' Variable used to read turnout addresses from route TurnoutAddr VAR BYTE ' Turnout address AddrHigh VAR TurnoutAddr.HIGHNIB AddrLow VAR TurnoutAddr.LOWNIB InactCount1 VAR BYTE ' Keypad 1 inactivity counter InactCount2 VAR BYTE ' Keypad 2 inactivity counter LedCount VAR BYTE ' Countdown time between Led state toggle KeyPad1Ent1 VAR NIB ' Keypad 1 first number KeyPad2Ent1 VAR NIB ' Keypad 2 first number KeyFlags VAR NIB ' Program control flags KeyPressed1 VAR KeyFlags.BIT0 KeyPressed2 VAR KeyFlags.BIT1 MemArray VAR NIB(8) ' Turnout position array. See Turnout routine. Value VAR NIB Addr VAR NIB Mask VAR NIB '-------------------------------------------------------------------------- ProgramStart: DIRS = DefaultDir ' Set default I/O direction bits OUTH = Reset ' Reset chip enable bits OUTB = 1111 ' Reset Led's KeyPressed1 = 0 ' Reset control bit KeyPressed2 = 0 ' Reset control bit InactCount1 = 0 ' Reset inactivity timer InactCount2 = 0 ' Reset inactivity timer '-------------------------------------------------------------------------- ' Begin main program loop. Each iteration decrements the LedCount variable. ' Calls are made to subroutines for processing of keypad input and route setting. MainLoop: LedCount = LedCount - 1 ' Decrement heartbeat indicator timer IF LedCount <> 0 THEN MainLoop2 ' Jump if no Led change yet TOGGLE 4 ' Toggle Led state. IF InactCount1 = 0 OR KeyPressed1 = 1 THEN MainLoop1 ' Jump if no timeout or ' if key still down InactCount1 = InactCount1 - 1 ' Decrement inactivity counter IF InactCount1 <> 0 THEN MainLoop1 ' Jump if not zero OUT5 = 1 ' Turn off 1st entry Led MainLoop1: IF InactCount2 = 0 OR KeyPressed2 = 1 THEN MainLoop2 ' Jump if no timeout or ' if key still down InactCount2 = InactCount2 - 1 ' Decrement inactivity counter IF InactCount2 <> 0 THEN MainLoop2 ' Jump if not zero OUT6 = 1 ' Turn off 1st entry Led MainLoop2: IF IN8 = 0 THEN MainLoop3 ' Jump if no keypad 1 input IF KeyPressed1 = 1 THEN MainLoop4 ' Jump if key is still down GOSUB KeyPad1In ' Read key number and save KeyPressed1 = 1 ' Set key pressed flag GOTO MainLoop4 MainLoop3: KeyPressed1 = 0 ' Reset key pressed flag MainLoop4: IF IN10 = 0 THEN MainLoop5 ' Jump if no keypad 2 input IF KeyPressed2 = 1 THEN MainLoop6 ' Jump if key is still down GOSUB KeyPad2In ' Read key number and save KeyPressed2 = 1 ' Set key pressed flag GOTO MainLoop6 MainLoop5: KeyPressed2 = 0 ' Reset key pressed flag MainLoop6: GOTO MainLoop '-------------------------------------------------------------------------- ' This code is run to process a KeyPad 1 input. The first entry ("from" track ' number) is stored in a KeyPad 1 specific variable. The "first entry" Led ' doubles as a program control flag. The second entry ("to" track number) ' is combined with the first entry and stored in the Tracks variable. A call ' is then made to the SetRoute routine. KeyPad1In: OUT9 = 0 ' Enable keypad 1 data read IF IN5 = 0 THEN KeyPad1In1 ' Jump if 2nd number KeyPad1Ent1 = INA ' Read keypad value (1st entry) OUT9 = 1 ' Disable keypad 1 data read OUT5 = 0 ' Turn on 1st entry Led InactCount1 = 10 ' Set inactivity timer RETURN KeyPad1In1: ToTrack = INA ' Read keypad value (2nd entry) OUT9 = 1 ' Disable keypad 1 data read OUT5 = 1 ' Turn off 1st entry Led InactCount1 = 1 ' Reset inactivity timer FromTrack = KeyPad1Ent1 GOSUB SetRoute ' Process KeyPad 1 input RETURN '-------------------------------------------------------------------------- ' This code is run to process a KeyPad 2 input. The first entry ("from" track ' number) is stored in a KeyPad 2 specific variable. The "first entry" Led ' doubles as a program control flag. The second entry ("to" track number) ' is combined with the first entry and stored in the Tracks variable. A call ' is then made to the SetRoute routine. KeyPad2In: OUT11 = 0 ' Enable keypad 2 data read IF IN6 = 0 THEN KeyPad2In1 ' Jump if 2nd number KeyPad2Ent1 = INA ' Read keypad value (1st entry) OUT11 = 1 ' Disable keypad 2 data read OUT6 = 0 ' Turn on 1st entry Led InactCount2 = 10 ' Set inactivity timer RETURN KeyPad2In1: ToTrack = INA ' Read keypad value (2nd entry) OUT11 = 1 ' Disable keypad 2 data read OUT6 = 1 ' Turn off 1st entry Led InactCount2 = 1 ' Reset inactivity timer FromTrack = KeyPad2Ent1 GOSUB SetRoute ' Process KeyPad 2 input RETURN '-------------------------------------------------------------------------- ' Set the turnouts corresponding to the specified From and To track numbers. ' ' Find the route to be used for the entered combination; made up from a "From" ' track number and a "To" track number (Tracks variable). Invalid combinations ' are not included and will result in the initialized value being unchanged. ' Though the user enters track numbers 1 - 16, the corresponding values are ' 0 - 15 in the Tracks variable. ' ' The LOOKDOWN statement matches the contents of the Tracks variable with one ' of the entries in the list of valid combinations. If a match is found, the ' LOOKDOWN statement will set the RouteIndex variable to the offset position ' of the matching entry. For example, if the user entered track 1 to track 3 ' ($02 in the Tracks variable), RouteIndex will be set to $00 (offset from ' start of list in LOOKDOWN statement. If track 1 to track 13 was entered ' ($0C in the Tracks variable), RouteIndex would retain its initialized ' value ($FF) and a warning tone would be output. ' ' We have to handle a couple of special cases here. For a track 4 to 5 or ' track 5 to 4 request, the turnouts used are train direction dependent. Since ' the train direction is not known, the code initially sets turnouts 5 and 6 ' for track 4 to 5 and turnouts 7 and 8 for track 5 to 4. If the same track ' command is consecutively entered, the code inverts turnouts 5/6 and 7/8. ' Entries (3E and 3F) have been added to the lookup lists for these cases. SetRoute: RouteIndex = $FF ' Initialize to invalid route. IF Tracks <> $34 AND Tracks <> $43 THEN SetRoute2 IF LastTracks <> Tracks THEN SetRoute2 IF Tracks <> $34 THEN SetRoute1 Tracks = $3E GOTO SetRoute2 SetRoute1: Tracks = $3F SetRoute2: LastTracks = Tracks LOOKDOWN Tracks, [$02,$03,$04,$05,$06,$07,$08,$09,$0A,$12,$13,$14,$20, $21,$22,$30,$31,$33,$34,$3E,$3F,$40,$41,$43,$44,$45, $46,$47,$48,$49,$4A,$50,$54,$55,$5B,$5C,$5D,$60,$64, $66,$70,$74,$77,$80,$84,$88,$90,$94,$99,$A0,$A4,$AA, $B5,$BB,$BE,$C5,$CC,$CE,$D5,$DD,$DE,$EB,$EC,$ED,$EE, $FF], RouteIndex IF RouteIndex <> $FF THEN SetRoute3 FREQOUT 7, 100, 660 PAUSE 100 FREQOUT 7, 100, 660 PAUSE 500 RETURN SetRoute3: ' Now use the validated RouteIndex to access the list of turnout addresses ' to be used to set the route. It is important that the entries in the LOOKUP ' statement correspond to the entries in the LOOKDOWN statement. Otherwise, ' the wrong route list entry will be selected. ' ' The LOOKUP statement uses the contents of the RouteIndex variable to select ' the starting address from the list of route entries. Each route is made up of ' a count entry (specifying the number of bytes in the route) and a list of ' turnout control addresses. ' ' The first location in the route is read and stored in the EntryCount variable. ' The EntryCount variable tells the code when to exit the FOR loop below. Each ' iteration of the FOR loop reads two bytes from the route and stores them in ' the TurnoutAddr variable. A call is then made to the Turnout routine which ' causes the addressed turnout to be positioned. LOOKUP RouteIndex, [R02,R03,R04,R05,R06,R07,R08,R09,R0A,R12,R13,R14,R20, R21,R22,R30,R31,R33,R34,R3E,R3F,R40,R41,R43,R44,R45, R46,R47,R48,R49,R4A,R50,R54,R55,R5B,R5C,R5D,R60,R64, R66,R70,R74,R77,R80,R84,R88,R90,R94,R99,RA0,RA4,RAA, RB5,RBB,RBE,RC5,RCC,RCE,RD5,RDD,RDE,REB,REC,RED,REE, RFF], Route READ Route, EntryCount ' Now step through the route and set the turnouts. FOR Index = 1 TO EntryCount READ Route+Index, TurnoutAddr GOSUB Turnout NEXT RETURN '-------------------------------------------------------------------------- ' Check the current position of the turnout and exit if already in position. ' This serves to minimize the number of commands sent to the turnouts by ' remembering their commanded positions. This will improve program response ' time and reduce switch machine coil heating. ' ' MemArray contains two bits for each turnout based upon its address. Bit ' meanings are: 00 = Unknown, 01 = Closed, 10 = Open. The bits are updated ' after each turnout position change. At power up, all turnout positions are ' unknown. ' ' If a position change is required, this routine will set the turnout who's ' address is specified by TurnoutAddr. The routine must also preserve the ' heartbeat and data entry led states. The low turnout address byte is output ' first so bits 3-0 will be stable prior to the chip select bits 12 and 13. ' ' The PAUSE statement value should be adjusted for a balance between reliable ' turnout operation and minimum switching noise. Longer than required time ' will also generate unnecessary heat and wear on the switch machine. 200 ' milliseconds seems to work well when using 12 volts for the switch machine ' coil circuit. For higher voltages, this value can be lowered which will ' improve overall program response time. Turnout: Value = %0001 << (AddrLow // 4) ' Value = 1000, 0100, 0010, or 0001 Addr = AddrLow / 4 ' Potential array location IF AddrHigh = $E THEN Turnout1 Addr = Addr + 4 ' Adjust array location Turnout1: IF MemArray(Addr) & Value <> 0 THEN Turnout3 ' Jump if in position DIRA = %1111 ' Set bits 3 - 0 to output mode OUTA = AddrLow ' Select the turnout coil OUTD = AddrHigh ' Energize it PAUSE 200 OUTH = Reset ' De-energize the turnout coil DIRA = %0000 ' Set bits 3 - 0 back to input mode Mask = %0011 ' Set mask for bits 2 and 3 IF Value > 2 THEN Turnout2 Mask = Mask << 2 ' Set mask for bits 0 and 1 Turnout2: MemArray(Addr) = MemArray(Addr) & Mask ' Reset current contents MemArray(Addr) = MemArray(Addr) | Value ' Save new position data Turnout3: RETURN '-------------------------------------------------------------------------- ' The following data statements define the turnout positions to set for all ' valid routes. Valid routes were identified using the track diagram. Each ' route is made up of a count entry (specifying the number of bytes in the ' route) and a list of turnout control addresses (16 bit values). The DATA ' statement information is stored in the Basic Stamp EEPROM by the Basic ' Stamp editor. The label associated with each route (R02, R03, etc) is used ' to store the staring address of each route in the LOOKUP statement list. R02 DATA 2, Y1Close, Y2Open R03 DATA 4, Y1Close, Y2Close, Y6Close, Y7Close R04 DATA 5, Y1Open, Y5Close, Y9Close, Y10Close, Y8Close R05 DATA 5, Y1Open, Y5Close, Y9Open, Y12Open, Y16Close R06 DATA 5, Y1Open, Y5Close, Y9Open, Y11Close, Y12Open R07 DATA 5, Y1Open, Y5Close, Y9Open, Y11Close, Y12Close R08 DATA 6, Y1Open, Y5Close, Y9Close, Y10Open, Y13Open, Y14Open R09 DATA 6, Y1Open, Y5Close, Y9Close, Y10Open, Y13Open, Y14Close R0A DATA 5, Y1Open, Y5Close, Y9Close, Y10Open, Y13Close R12 DATA 1, Y4Close R13 DATA 4, Y4Open, Y3Close, Y7Close, Y6Close R14 DATA 6, Y4Open, Y3Open, Y8Close, Y10Close, Y9Close, Y5Close R20 DATA 2, Y2Open, Y1Close R21 DATA 1, Y4Close R22 DATA 3, Y1Close, Y2Open, Y4Close R30 DATA 4, Y7Close, Y6Close, Y2Close, Y1Close R31 DATA 4, Y3Close, Y4Open, Y7Close, Y6Close R33 DATA 6, Y1Close, Y2Close, Y6Close, Y7Close, Y3Close, Y4Open R34 DATA 1, Y5Open R3E DATA 2, Y5Close, Y7Open ' Special entry. See SetRoute routine. R3F DATA 2, Y7Close, Y5Open ' Special entry. See SetRoute routine. R40 DATA 5, Y8Close, Y10Close, Y9Close, Y5Close, Y1Open R41 DATA 6, Y8Close, Y10Close, Y9Close, Y5Close, Y3Open, Y4Open R43 DATA 1, Y7Open R44 DATA 4, Y5Close, Y9Close, Y10Close, Y8Close R45 DATA 4, Y9Open, Y12Open, Y11Open, Y16Close R46 DATA 3, Y9Open, Y12Open, Y11Close R47 DATA 2, Y9Open, Y12Close R48 DATA 5, Y5Close, Y9Close, Y10Open, Y13Open, Y14Open R49 DATA 5, Y5Close, Y9Close, Y10Open, Y13Open, Y14Close R4A DATA 4, Y5Close, Y9Close, Y10Open, Y13Close R50 DATA 5, Y16Close, Y12Open, Y9Open, Y5Close, Y1Open R54 DATA 5, Y16Close, Y11Open, Y12Open, Y9Open, Y5Close R55 DATA 3, Y16Close, Y11Open, Y12Open R5B DATA 3, Y16Open, Y15Close, Y17Close R5C DATA 4, Y16Open, Y15Close, Y17Open, Y18Close R5D DATA 4, Y16Open, Y15Close, Y17Open, Y18Open R60 DATA 5, Y12Open, Y11Close, Y9Open, Y5Close, Y1Open R64 DATA 5, Y16Close, Y11Open, Y12Open, Y9Open, Y5Close R66 DATA 2, Y12Open, Y11Close R70 DATA 5, Y12Close, Y11Close, Y9Open, Y5Close, Y1Open R74 DATA 3, Y12Close, Y9Open, Y5Close R77 DATA 2, Y12Close, Y11Close R80 DATA 6, Y14Open, Y13Open, Y10Open, Y9Close, Y5Close, Y1Open R84 DATA 5, Y14Open, Y13Open, Y10Open, Y9Close, Y5Close R88 DATA 2, Y14Open, Y13Open R90 DATA 6, Y14Close, Y13Open, Y10Open, Y9Close, Y5Close, Y1Open R94 DATA 5, Y14Close, Y13Open, Y10Open, Y9Close, Y5Close R99 DATA 2, Y14Close, Y13Open RA0 DATA 5, Y13Close, Y10Open, Y9Close, Y5Close, Y1Open RA4 DATA 4, Y13Close, Y10Open, Y9Close, Y5Close RAA DATA 1, Y13Close RB5 DATA 3, Y17Close, Y15Close, Y16Open RBB DATA 2, Y17Close, Y15Close RBE DATA 2, Y17Close, Y15Open RC5 DATA 4, Y18Close, Y17Open, Y15Close, Y16Open RCC DATA 3, Y18Close, Y17Open, Y15Close RCE DATA 3, Y18Close, Y17Open, Y15Open RD5 DATA 4, Y18Open, Y17Open, Y15Close, Y16Open RDD DATA 3, Y18Open, Y17Open, Y15Close RDE DATA 3, Y18Open, Y17Open, Y15Open REB DATA 2, Y15Open, Y17Close REC DATA 3, Y15Open, Y17Open, Y18Close RED DATA 3, Y15Open, Y17Open, Y18Open REE DATA 1, Y15Open RFF DATA 2, Y5Open, Y8Open
Navigation: Yard D&B Home Buczynski.com Index
Copyright © 2006 Don Buczynski
San Diego, California