; ************************************************************************** ; * * ; * Railway Controller Module for the PICAXE-18X PICAXETW.BAS * ; * * ; ************************************************************************** ; .------------------------------------------------------------------------. ; | | ; | Version 1.0 | ; | Last Update 2004-08-26 | ; | | ; | Target PICAXE-18X | ; | Compiler Used Revolution Programming Editor 4.1.4 | ; | Memory Used 605 bytes of 2048 | ; | | ; | Author The Happy Hippy | ; | Web Site www.hippy.freeserve.co.uk/picaxerw.htm | ; | Email hippy@psynet.net | ; | | ; | Copyright (C) 2004, The Happy Hippy | ; | | ; `------------------------------------------------------------------------' ; ************************************************************************** ; * * ; * Controller Module Circuit Diagram * ; * * ; ************************************************************************** ; +5 ---------------.-------------------. ; | | ; RX >--.----. | | ; .|. .|.22K .|. 4K7 | ; 10K | | | | | | PICAXE-18X | ; |_| |_| |_| .---------. | ; | `------|--->| A2 A1 | | ; TX <---|-----------|----| SO A0 | | ; }-----------|--->| SI I7 | | .---------. ; } `--->| RST I6 | | | | ; }----------------| 0V +V |----{ .-^--. | ; CX <---|----------------| O0 O7 |----|--------->| 14 | D7 | ; | | O1 O6 |----|--------->| 13 | D6 | ; | .-----| O2 O5 |----|--------->| 12 | D5 | ; | | .--| O3 O4 |----|--------->| 11 | D4 | ; | | | `---------' | | 10 | D3 | ; | `--|-----------------|------. | 9 | D2 | ; | `-----------------|---. | | 8 | D1 | ; | | | | | 7 | D0 | ; | .------{ | `-->| 6 | E | ; | .|. | | .-->| 5 | /WR | ; | 10K | |<-. | `--|-->| 4 | RS | ; | Pot |_| `--|------|-->| 3 | Vo | ; | | `------|-->| 2 | Vcc | ; 0v ---^------------------------^-------------^-->| 1 | Vdd | ; `-.--' | ; 100nF Power Supply | | ; decoupling not shown `---------' ; ************************************************************************** ; * * ; * Network Configuration * ; * * ; ************************************************************************** ; 9600 2400 ; baud baud ; PSU Controller Receiver ; .------. .--------. .--------. ; | +V |---.------------>| +V 5V |----.--------->| 5V |---> OUT1 ; | 0V |---|--. .------>| RX CX |----|--.------>| RX |---> OUT2 ; `------' | | | .----| TX | | | | |---> OUT3 ; | }--|--|--->| 0V 0V |----|--|--.--->| 0V |---> OUT4 ; | | | | `---.----' | | | `--------' ; PC | | | | | : : : ; .------. | | | | .--^--. : : : Receiver ; | TX |---|--|--{ | | LCD | | | | .--------. ; | RX |<--|--|--|--{ `-----' }--|--|--->| 5V |---> OUT1 ; | 0V |---|--{ | | | }--|--->| RX |---> OUT2 ; `------' | | | | | | | | |---> OUT3 ; | | | | | | }--->| 0V |---> OUT4 ; KEYPAD | | | | | | | `--------' ; .------. | | | | : : : ; | +V |<--' | | | : : : Receiver ; | TX |------|--' | | | | .--------. ; | RX |<-----|-----' `--|--|--->| 5V |---> OUT1 ; | 0V |<-----' `--|--->| RX |---> OUT2 ; `--.---' | | |---> OUT3 ; | `--->| 0V |---> OUT4 ; .--^---. `--------' ; |[][][]| ; |[][][]| ; |[][][]| ; `------' ; ************************************************************************** ; * * ; * Constant Definitions * ; * * ; ************************************************************************** SYMBOL RX_BAUD = N4800 ; 9600 at 8MHz SYMBOL CX_BAUD = N2400 SYMBOL PULSE_LENGTH = 20 ; ************************************************************************** ; * * ; * Define Input Pin Usage * ; * * ; ************************************************************************** SYMBOL I0 = 0 ; Unused SYMBOL I1 = 1 ; Unused SYMBOL RX = 2 ; RX from PC SYMBOL I6 = 6 ; Unused SYMBOL I7 = 7 ; Unused ; ************************************************************************** ; * * ; * Define Output Pin Usage * ; * * ; ************************************************************************** SYMBOL CX = 0 ; TX to Receivers SYMBOL O1 = 1 ; Unused SYMBOL E = 2 ; 0 = Idle 1 = Active SYMBOL RS = 3 ; 0 = Command 1 = Data SYMBOL D4 = 4 ; LCD Data Line 4 SYMBOL D5 = 5 ; LCD Data Line 5 SYMBOL D6 = 6 ; LCD Data Line 6 SYMBOL D7 = 7 ; LCD Data Line 7 SYMBOL RSCMDmask = %00000000 SYMBOL RSDATmask = %00001000 ; ************************************************************************** ; * * ; * Variable Definitions * ; * * ; ************************************************************************** SYMBOL byte = b0 ; b0 SYMBOL outBits = b1 ; b1 SYMBOL matchAdr = w1 ; b3:b2 SYMBOL matchAdrLsb = b2 SYMBOL matchAdrMsb = b3 SYMBOL adr = w2 ; b5:b4 SYMBOL adrLsb = b4 SYMBOL adrMsb = b5 SYMBOL action = b6 ; b6 SYMBOL echoChar = b7 ; b7 SYMBOL adrCount = w4 ; b9:b8 SYMBOL bitMask = b10 ; b10 SYMBOL digit = b11 ; b11 SYMBOL chkBitMask = b12 ; b12 SYMBOL rsBit = b13 ; b13 ; ************************************************************************** ; * * ; * Power-On Reset Initialisation * ; * * ; ************************************************************************** PowerOnReset: ;-- SETFREQ M8 ; Run at 8MHz adrCount = $1000 InitialiseLcd: FOR action = 0 TO 5 READ action,byte GOSUB SendInitCmdByte NEXT ' Nibble commands - To initialise 4-bit mode EEPROM 0,( $33 ) EEPROM 1,( $32 ) ' Byte commands - To configure the LCD EEPROM 2,( $28 ) ; %00101000 %001LNF00 Display Format EEPROM 3,( $0C ) ; %00001100 %00001DCB Display On EEPROM 4,( $06 ) ; %00000110 %000001IS Display Shift ; L : 0 = 4-bit Mode 1 = 8-bit Mode ; N : 0 = 1 Line 1 = 2 Lines ; F : 0 = 5x7 Pixels 1 = N/A ; D : 0 = Display Off 1 = Display On ; C : 0 = Cursor Off 1 = Cursor On ; B : Cursor Flash ; I : 0 = Dec Cursor 1 = Inc Cursor ; S : 0 = Cursor Move 1 = Display Shift EEPROM 5,( $01 ) ; Clear Screen EEPROM (%11011) ; --#-- EEPROM (%10011) ; -##-- EEPROM (%11011) ; --#-- EEPROM (%11011) ; --#-- EEPROM (%11011) ; --#-- EEPROM (%11011) ; --#-- EEPROM (%10001) ; -###- EEPROM (%11111) ; ----- EEPROM (%10001) ; -###- EEPROM (%01110) ; #---# EEPROM (%11110) ; ----# EEPROM (%10001) ; -###- EEPROM (%01111) ; #---- EEPROM (%01111) ; #---- EEPROM (%00000) ; ##### EEPROM (%11111) ; ----- EEPROM (%10001) ; -###- EEPROM (%01110) ; #---# EEPROM (%11110) ; ----# EEPROM (%10001) ; -###- EEPROM (%11110) ; ----# EEPROM (%01110) ; #---# EEPROM (%10001) ; -###- EEPROM (%11111) ; ----- EEPROM (%11111) ; ---## EEPROM (%11111) ; --#-# EEPROM (%11111) ; -#--# EEPROM (%11111) ; #---# EEPROM (%11111) ; ##### EEPROM (%11111) ; ----# EEPROM (%11111) ; ----# EEPROM (%00000) ; ----- ; ************************************************************************** ; * * ; * * ; * * ; ************************************************************************** ; The controller is designed to be controlled directly from a PC or ; from another PICAXE which is scanning a 18-digit keypad ( 0-9, A-F, ; # and * ) and sending each key as its ASCII character equivalent. ; All communicatiosn with the PC or PICAXE keypad controller is done ; at 9600 baud, 1 stop bit and no parity. ; ; # Precedes the setting of an address. Once '#' has been sent ; it must be followed by a four digit address code. When the ; '#' is entered, the address is reset to 0000, and the ; command is set to 'no change'. ; ; 0-9 If the four digit address code is being entered, the digit ; is set in the address code. Once the four digit address ; code has been entered, the number is used to indicate which ; output bits are to be adjusted in the receiver; 1, 2, 3 and ; 4 select OUT1, OUT2, OUT3 and OUT4 bits respectively. 0 will ; select all output bits. ; ; A-F If the four digit address code is being entered, the digit ; is set in the address code. Once the four digit address ; code has been entered, the number is used to indicate what ; action should be performed on the previously selected output ; bits. 'A' sets the bit, 'B' pulses the bit and 'C' clears the ; bit. ; ; * Sends the currently specified command to the receivers. If ; used during entry of the four digit address code, any digits ; unentered are set to '0' ( wildcards ). This allows settings ; of large groups quickly; '#*0C*' clears all output bits for ; the whole network. ; ; Note that there are some special bit setting commands which can follow ; the four digit address code entry ... ; ; For a three light rail signal controller - ; ; 5 Will set the Red light 0001 1110 ; 7/8 Will set the Top Yellow light 0010 1101 ; 9 Will set the Green light 1000 0111 ; ; For a four light rail signal controller - ; ; 5 Will set the Red light 0001 1110 ; 7 Will set the Top Yellow light 0010 1101 ; 8 Will set both Yellow lights 0110 1001 ; 9 Will set the Green light 1000 0111 ; ; For a three light road signal controller - ; ; 5 Will set the Red light 0001 1110 ; 6 Will set the Red and Yellow light 0011 1100 ; 7/8 Will set the Yellow light 0010 1101 ; 9 Will set the Green light 1000 0111 ; For a single points controller - ; ; E Will set the through points 0101 1011 ; F Will set the turned points 1010 0111 ; ; When using a PC to control the controller, the following additional ; key press characters can be used - ; ; Acts as '*' ; S Acts as 'A' Set Bit ; P Acts as 'P' Pulse Bit ; ; These characters should only be sent from the PC at appropriate ; times and as applicable to the command being entered. ReadyForCommand: ; Send the Ready For Command prompt to the PC SERTXD(CR,LF) echoChar = ">" ; ************************************************************************** ; * * ; * Build up a command * ; * * ; ************************************************************************** GetCommandChar: GOSUB UpdateLcd SERTXD(echoChar) SERIN RX,RX_BAUD,byte echoChar = byte IF byte = CR THEN SendCommandCrLf IF byte = "*" THEN IssueCommand IF byte = "#" THEN EnterAddress IF byte >= "0" AND byte <= "9" THEN EnterDigitDec IF byte < "a" THEN CharIsUpper byte = byte & $5F ; Dirty convert to uppercase CharIsUpper: IF byte >= "A" AND byte <= "F" THEN EnterDigitHex GOTO EnterActionDigit EnterAddress: adr = $0000 adrCount = $1000 bitMask = %0000 action = %00000000 GOTO GetCommandChar EnterDigitHex: IF adrCount = $0000 THEN EnterActionDigit EnterAddressDigitHex: byte = byte - "A" + "0" + 10 EnterDigitDec: byte = byte - "0" IF adrCount = $0000 THEN EnterBitDigit EnterAddressDigit: adr = byte * adrCount | adr adrCount = adrCount / 16 GOTO GetCommandChar EnterBitDigit: ' 0=All 1 2 3 4 5 6 7 8 9 LOOKUP byte,(%1111,%0001,%0010,%0100,%1000,%00011110,%00111100,%00101101,%01101001,%10000111),bitMask IF byte <= 4 THEN GetCommandChar SetAction: action = bitMask bitMask = %0000 GOTO GetCommandChar EnterActionDigit: IF byte = "A" OR byte = "S" THEN SetBit IF byte = "B" OR byte = "P" THEN PulseBit IF byte = "C" THEN ClearBit IF byte = "D" THEN DiscardBit IF byte = "E" THEN SetActionE IF byte = "F" THEN SetActionF GOTO GetCommandChar SetActionE: bitMask = %01011011 ; Points 1 GOTO SetAction SetActionF: bitMask = %10100111 ; Points 2 GOTO SetAction SetBit: action = bitMask * 16 | action action = bitMask ^ $FF & action GOTO GetCommandChar PulseBit: action = bitMask * 16 | bitMask | action GOTO GetCommandChar ClearBit: action = bitMask * 16 ^ $FF & action | bitMask GOTO GetCommandChar DiscardBit: action = bitMask * 16 | bitMask ^ $FF & action GOTO GetCommandChar IssueCommand: IF adrCount = $0000 THEN SendCommand FinishedAdress: adrCount = $0000 GOTO GetCommandChar SendCommandCrLf: IF adrCount <> $0000 THEN GetCommandChar ; ************************************************************************** ; * * ; * Send the Command * ; * * ; ************************************************************************** SendCommand: ; Drop down to 4MHz to match PICAXE-08 speed. Although we can execute ; the SEROUT at 8MHz using 1200 baud to get an output baud rate of ; 2400, running at 8MHz may shrink the inter-byte gaps. To make sure ; we are compatible with the PICAXE-08 we drop the speed now. SETFREQ M4 SEROUT CX,CX_BAUD,(adrMsb,adrLsb,action) ; ************************************************************************** ; * * ; * Wait until the sent command has been executed * ; * * ; ************************************************************************** ; This code emulates what is happening inside a receiver which is ; obeying the command sent; when we have finished executing the code ; then so will all receivers which have received it. The code is very ; nearly identical to that in each receiver but doesn't update any of ; the output pins. ; ; For a detailed explanation of the code, please see the PICAXERW.BAS ; source code wich is the program downloaded into all the receivers. matchAdr = adr IF adrMsb > $0F THEN NoWildCardFirstDigit matchAdrMsb = matchAdrMsb & $0F NoWildCardFirstDigit: byte = adrMsb & $0F IF byte <> $00 THEN NoWildCardSecondDigit matchAdrMsb = matchAdrMsb & $F0 NoWildCardSecondDigit: IF adrLsb > $0F THEN NoWildCardThirdDigit matchAdrLsb = matchAdrLsb & $0F NoWildCardThirdDigit: byte = adrLsb & $0F IF byte <> $00 THEN NoWildCardFourthDigit matchAdrLsb = matchAdrLsb & $F0 NoWildCardFourthDigit: IF matchAdr <> adr THEN CommandCompleted IF action <= $0F THEN DoClearBits action = action / 16 & %0000 | action outBits = action / 16 | outBits byte = byte ^ $FF bit4 = bit3 byte = byte byte = action / 16 ^ action & action IF byte = $0 THEN DoClearBits PAUSE PULSE_LENGTH DoClearBits: outBits = action & $0F ^ $0F & outBits byte = outBits ^ $FF bit4 = bit3 byte = byte CommandCompleted: ;-- SETFREQ M8 ; Back to fastest speed GOTO ReadyForCommand ; ************************************************************************** ; * * ; * LCD Display Update * ; * * ; ************************************************************************** ; The controller uses a 1 line, 16 digit display to indicate the ; command which is being constructed. The display format i is as ; follows ... ; ; .-------------------. ; | abcd 1x 2x 3x 4x | ; `-------------------' ; ; The 'abcd' is the address the command will be sent to. ; ; The '1x', '2x', '3x' and '4x' indicates the action to be performed ; with OUT1, OUT2, OUT3 and OUT4 on the receivers which respond to the ; command sent, where 'x' indicates ... ; ; - No change ; S Set bit ; P Pulse bit ; C Clear bit UpdateLcd: byte = adrMsb / $10 + "0" IF byte <= "9" THEN ShowAdrMsbHighNibbleDec byte = byte - "0" - 10 + "A" ShowAdrMsbHighNibbleDec: IF adrCount < $1000 THEN ShowAdrMsbHighNibble byte = "-" ShowAdrMsbHighNibble: GOSUB SendDataByte byte = adrMsb & $0F + "0" IF byte <= "9" THEN ShowAdrMsbLowNibbleDec byte = byte - "0" - 10 + "A" ShowAdrMsbLowNibbleDec: IF adrCount < $0100 THEN ShowAdrMsbLowNibble byte = "-" ShowAdrMsbLowNibble: GOSUB SendDataByte byte = adrLsb / $10 + "0" IF byte <= "9" THEN ShowAdrLsbHighNibbleDec byte = byte - "0" - 10 + "A" ShowAdrLsbHighNibbleDec: IF adrCount < $0010 THEN ShowAdrLsbHighNibble byte = "-" ShowAdrLsbHighNibble: GOSUB SendDataByte byte = adrLsb & $0F + "0" IF byte <= "9" THEN ShowAdrLsbLowNibbleDec byte = byte - "0" - 10 + "A" ShowAdrLsbLowNibbleDec: IF adrCount < $0001 THEN ShowAdrLsbLowNibble byte = "-" ShowAdrLsbLowNibble: GOSUB SendDataByte IF adrCount <> $0000 THEN SkipActionBits chkBitMask = %0001 FOR digit = "1" TO "4" byte = " " GOSUB SendDataByte byte = bitMask & chkBitMask IF byte <> %0000 THEN ShowSelectedBitNumber byte = digit GOTO ShowBitNumber ShowSelectedBitNumber: byte = digit ;-- byte = digit - "0" ShowBitNumber: GOSUB SendDataByte byte = 0 bit8 = chkBitMask & action MAX 1 bit9 = chkBitMask * 16 & action MAX 1 LOOKUP byte,("-CSP"),byte GOSUB SendDataByte chkBitMask = chkBitMask * 2 NEXT SkipActionBits: RETURN ; ************************************************************************** ; * * ; * Send an initialisation byte to the LCD * ; * * ; ************************************************************************** ; According to the data sheets for standard-style LCD displays; the ; initialisation codes to put an LCD into 4-bit mode should be sent as ; 4-bit nibbles, each with RS set to 0, with E pulsed after each code ; is sent. Analysing this, shows that sending two nibbles is the same ; as sending a byte in 4-bit mode to the LCD Command Register. Pairs ; of nibbles are therefore sent as single bytes. ; ; The data sheets also indicate that there must be a delay after the ; LCD is first powered up and between initialisation codes. Although ; the delays reduce as initialisation proceeds, it is simpler to use ; a single, long, delay; this does not increase the initialisation of ; the LCD by any appreciable amount. ; ; According to the data sheet; there should be delays between every ; initialisation code, however, the method used here only puts delays ; between every two initialisation codes. This works perfectly for the ; LCD being used to test this project, but may prove problematic for ; other LCD models. SendInitCmdByte: PAUSE 15 ; Delay 15mS ; ************************************************************************** ; * * ; * Send a command byte to the LCD * ; * * ; ************************************************************************** ; The only difference between sending an LCD Control Command and an ; LCD Data Byte is that the RS line on the LCD needs to be 0 not 1. ; We use the 'rsbit' variable to store the bit which is sent to the ; LCD when we output to the LCD to save having to have two separate ; routines. We have plenty of variables free so this is a compromise ; which is perfectly acceptable. ; ; Whenever a byte is sent to the LCD, 'rsbit' is reset so a subsequent ; call to 'SendDataByte' will be sent as data. We need to set 'rsbit' ; whenever we need to send a Control Command. Because we always send ; Control Commands to the LCD during initialisation, it is unneccesary ; to initialise 'rsbit' before the first call to 'SendCmdByte', nor ; before a call to 'SendDataByte'. SendCmdByte: rsbit = RSCMDmask ; Send to Command register ; ************************************************************************** ; * * ; * Send a data byte to the LCD * ; * * ; ************************************************************************** ; Note that if you change the pin allocations for D4..D7 then this ; routine must be rewritten to cater for the changes made. SendDataByte: pins = byte & %11110000 | rsbit ; Put MSB out first PULSOUT E,1 ; Give a 10uS pulse on E pins = byte * %00010000 | rsbit ; Put LSB out second PULSOUT E,1 ; Give a 10uS pulse on E rsbit = RSDATmask ; Send to Data register next RETURN ; ************************************************************************** ; * * ; * End of Railway Controller Module for the PICAXE-18X * ; * * ; **************************************************************************