;************************************************************************ ; TaPRK IR-LINK-RECEIVER (PC-ELIMINATOR;) VER 2004-01-18 ;************************************************************************ LIST P = 16F84A, R = HEX ;Prosessortype, radix INCLUDE "p16f84A.inc" ;w,f,porta,portb etc. ERRORLEVEL -224, -302 ;prevents error from ;"tris"-command __CONFIG _PWRTE_ON & _HS_OSC & _WDT_OFF ;************************************************************************ ; SHEMATIC DIAGRAM ;************************************************************************ ; +---+_+---+ ; TMW-RL1 PORTA.2 <-|1 18|-> PORTA.1 TMW_Strobe ; TMW-RL2 PORTA.3 <-|2 17|<- PORTA.0 TSOP1738 act.lo ; TMW-ACK PORTA.4 ->|3 16|-- XTAL ; Vdd --|4 15|-- XTAL ; Vss(GND)--|5 14|-- Vdd (+5V) ; TMW_REQ PORTB.0 ->|6/INT 13|-> PORTB.7 LED ; Strobe_Disp PORTB.1 <-|7 12|<- PORTB.6 N/C ; Comm.Ser.data PORTB.2 <-|8 11|<- PORTB.5 N/C ; Comm.Ser.clock PORTB.3 <-|9 10|<- PORTB.4 LED1 ; +---------+ ;************************************************************************ ; MAIN IDEA ;************************************************************************ ; ; RC5: ; based on Holger Klabunde's design ; Binary to BCD: ; directly from Microchip Embedded control Handbook ; Sending data to TMW: ; based on M.Brandt's idea ; Clearing memory: ; J. Niinikoski (based on PIC16F84 data sheet) ; Timing ; ====== ; 26us cycle of IR-LED on/off gives 38,5kHz ; 16 cycles for 1/2 bit -> 16 x 26us= 416 us ; Prescaler of 1:8 makes TMR0 advance every 8us (@4MHz crystal) ; 1/2 bit needs 416us / 8 = 52 ticks of TMR0 ; 1/4 bit needs 52 ticks / 2 = 26 ticks of TMR0 ; ; This is loosely following Philips RC5, exept: ; ; -- Bit length 2 X 416us! ; -- Only one start bit, and it is "0" ; -- Only 10 data bits: ; - 2 for transmitter ID and ; - 8 for payload ; -- Added parity bit ; -- Total of 11 bits ; ; This is presumably the same as Lorell Joiner's ; setup, done with hardwired UART some 10 years ago, but instead ; of using 250 kHz carrier and on/off bits, I use the ; Bit formation same as RC5 at 36...38 kHz carrier: ; ; +---+ +---+ ; | | =0 | | =1 ; + +---+ +---+ + ; ; TSOP1738 receiver module inverts the above ; ; Serial output to 7-segment display and LPT-connector to ; be connected to TMWDCC hardware (PC-eliminator!) ; ; 4094 serial-in-parallel-out will be used. Two branches for ; address/func/speed step display and other to mimic ; LPT-port for TMW-hardware. Both branches may use ; common data and clock, strobe sent only to correct branch. ; ; To operate TMW-DCC hardware directly the following assumptions ; are made: ; The IR-transmitters shall send three types of packets; ; ; (1) DCC Address byte packets ; (2) DCC Speed byte packets ; (3) DCC Functions group 1 packets ; ; These will be stored in respective registers within PIC ; The output of this device will be 8 bit parallel data ; suitable to mimic the LPT output connected to TMW-hardware. ; The output will always be 8 bits wide, and the DCC packet ; therefore needs to be broken into 8 bit sections including ; preamble and byte separation bits following the idea of ; M.Brandt of the DOS TSR driver: ; ; byte0: PPPPPPPP Preamble 8 first bits ; byte1: PPPPPPPP Preamble 8 next bits ; byte2: PPPPP0AA Preamble 5 bits, sep, 2 addr.bits ; byte3: AAAAAA0D 6 addr. bits, sep 1 data bit ; byte4: DDDDDDD0 7 data bits, sep ; byte5: EEEEEEEE Error byte ; ; This gives correct preamble according to old NMRA specs! ; (The 2002 version requests command stations to send at least ; 14 preamble bits, so a byte0: PPPPPPPP is added later) ; ; The above 6 bytes must be sent in sequence when demanded by ; TMW-DCC hardware. The time allowed to deliver the byte is ; close to the time the previous byte took (900us minimum) ; This means that the request signal needs to be polled at minimum ; 900us frequency. If polled at half IR bit frequency (416 us) ; we are in safe waters! ; ; As the Request signal is low only briefly (active low) ; the pulse is best recognized from the interrupt flag of ; RB0, the INTF flag of INTCON. ; ; Note, that there is no need for actual interrupt ; nor any interrupt handler: the INTF (Interrupt Flag) ; will be set irrespective of interrupts being generally ; allowed or not! The INTF must be made to set on falling ; edge of RB0, thus INTEDG (interrupt Edge Select) must ; be "0" ; ; As an added task it would be benfical to have the code also ; set the unused loco address speed settings to zero. ; This becomes the 5th cab. (not implemented yet! ; The system will send all speed packets followed by ; all Function packets and return. This may be too much for the ; decoders, so propably the frequency of function packets will ; be reduced into one out of four... ; ; ============================================================= ; The IR transmitters will send the packets as follows: ; ; ADDRESS: CC0AAAAAAAP CC =transmitter code ; A..A =address ; P =packet parity ; ; [Strip transmitter number and parity bits to get the ; proper NMRA-DCC 1..127 address data byte "0AAAAAAA"] ; ------------------------------------------------------------- ; 14/28SPEED: CC11DsSSSSP CC =transmitter code ; D =directon bit ; s =F0 / LSB speed ; S =speed bits ; P =packet parity ; ; [Strip transmitter number and parity bits and replace first ; 1 w. 0 to get the proper NMRA-DCC 14/28 speed step speed ; instruction data byte "01DsSSSS"] ; ------------------------------------------------------------- ; GROUP 1 FUNC: CC10MFFFFFP CC =transmitter code ; M =14/28 step Mode ; F =function status bits ; P =packet parity ; ; [Strip transmitter number and parity bits and clear M ; to get the proper NMRA-DCC Multi-Function Group 1 instruction ; data byte "100FFFFF". 14/28 mode is sent separately here to make ; sure speed=0 is interpreted correctly: address change should ; only allowed if speed is zero!] ; ------------------------------------------------------------- ; ; This means that the IR transmitters will send 11 bit messages: ; two bits for IR throttle identification, 8 bits for payload ; and one bit parity. The RC5-type code in itself contributes ; much of the intregity check of the data, so 1 bit seems to ; be enough! ; ; The system must also be capable of generating NMRA-DCC ; IDLE and Reset packets at startup! ; ; The above mentioned IR-packet's payload's are stored as is ; in resp. registers and the modified bits are cleared only ; at the time the byte is to be sent. This way the 14/28 mode ; may be stored in Func byte ;) ; ; ============================================================== ; 5-PIN STEREO DIN DISP. UNIT ; -------------------------------------------------------------- ; 1 CK Orange [Biltema ; 4 GND Green keyboard ; 2 DATA Blue extension ; 5 +5V White cord ; 3 STROBE Black colours] ; ============================================================== ; 3.5mm STEREO IR-MODULE CONNECTOR ; -------------------------------------------------------------- ; Tip Data ; Ring + ; Sleeve GND ;************************************************************************ ; UPDATES AND BUGFIXES ;************************************************************************ ; [2003-11-19] Added constant display of address, speed, and functions ; in case no new packets from IR remotes for 2 seconds ; [2004-01-08] Found error in speed display routines possibly causing ; stack overflows and probably the reason of occasionally ; loosing control of locos! ;************************************************************************ ; STATICS ;************************************************************************ #DEFINE TSOP PORTA, 0 ;IR receiver #DEFINE TMW_REQ PORTB, 0 ;TMW-REQUEST #DEFINE TMW_STROBE PORTA, 1 ;TMW data Strobe out #DEFINE S_STROBE PORTB, 1 ;7-SEG Strobe #DEFINE S_DATA PORTB, 2 ;7-SEG Data #DEFINE S_CLOCK PORTB, 3 ;7-SEG Clock #DEFINE RELAY1 PORTA, 2 #DEFINE RELAY2 PORTA, 3 #DEFINE ACK PORTA, 4 #DEFINE LED1 PORTB, 7 ;Pilot light #DEFINE HALFBIT .203 ;= 255-52 #DEFINE QUADBIT .229 ;= 255-26 ;Need to add the relay control and emg. button input! ;This will never handle programming! ;************************************************************************ ; VARIABLES ;************************************************************************ CBLOCK H'0C' Data1 ;Data from IR-receiver Data2 ;waiting for conversion to 7-seg... Parity ;Parity Err ;error flag from ReadBit bitcount ;bit counter count_gp ;freewheeling counter blink ;blink on/off temp ;long call assist expay_0 ;previous data packet speed_0 ;cab 0 speed (4 bits) func_0 ;cab 0 functions addr_0 ;cab 0 address expay_1 ;previous data packet speed_1 ;cab 1 speed (4 bits) func_1 ;cab 1 functions addr_1 ;cab 1 address expay_2 ;previous data packet speed_2 ;cab 2 speed (4 bits) func_2 ;cab 2 functions addr_2 ;cab 2 address expay_3 ;previous data packet speed_3 ;cab 3 speed (4 bits) func_3 ;cab 3 functions addr_3 ;cab 3 address ;------------------------------------------------ ; NOTE the speed, func,and address bytes must ; be kept in above mentioned order as the code ; may access those by just adjusting FSR up/down! ;------------------------------------------------ tmw_0 ;TMW data bytes containing tmw_1 ;the actual NMRA-DCC tmw_2 ;packets incorporating tmw_3 ;Preamble, Address, Data tmw_4 ;Error bytes and byte tmw_5 ;separation bits tmw_bytecount ;bytecount cab_sel ;cab to be served out s_BITCOUNT ;shifting out bit counter s_DATA1 ;shift out data power_on ;dcc-initializin packet count IR_ADDRESS ;IR-transmitter ID IR_DATA ;IR-transmitted payload MSD ;temp for bin-BCD-conversion LSD ;temp for bin-BCD-conversion idle1 ;idle precounter idle2 ;idle counter for intermediate add disp. idle_cab ;the cab to display while idle ENDC ;************************************************************************ ;RESTART/INT-START ;************************************************************************ ORG 0 ; Restart vector goto main ORG 4 ; INT vector retfie ;------------------------------------------------------------------------ ; Initialize I/O ports, Interrupts, Timers etc ;------------------------------------------------------------------------ init movlw b'11100001' ;RB0/INT=in, 1..7=out tris portb ; movlw b'00000001' ;RA0=in, RA1..4=out tris porta ; ;easy to recall: ;Input -> I -> 1 ;Output-> O -> 0 clrf porta ;clear Port A (Shift reg output) clrf portb ;Port B on (LED test) CLRWDT ; bsf STATUS, RP0 ; movlw b'01000010' ;PullUp=on, INTEDG=hi, PreScaler= 1:8 movwf OPTION_REG ; bcf STATUS, RP0 ; ramc movlw 0x0c ; start of 16F84 ram movwf fsr ramc1 clrf indf ; clear indirect location incf fsr, f ; next location btfss fsr, 7 ; cleans up to 0x7f goto ramc1 ; [From J.Niinikoski] ramdone movlw .30 ;NMRA-DCC system's movwf power_on ;initilaising packets movfw DATA1 ;clear w movlw b'11111111' ;preamble call so_byte ;to track bsf tmw_strobe ;strobe out nop bcf tmw_strobe ;done strobing movlw b'10001100' ; E call so_byte ; movlw b'11101111' ; - call so_byte ; movlw b'10011100' ; C call so_byte ; movlw b'00001110' ; P call so_byte ; bsf S_STROBE ;strobe out (display "PC-E") nop ;wait for slow C-MOS bcf S_STROBE ;strobing done bsf RELAY1 ;switch on power to rails return ;------------------------------------------------------------------------ ; Binary to BCD [Microchip: Embedded control Handbook, maths] ;------------------------------------------------------------------------ bin_to_BCD clrf MSD movwf LSD gtenth movlw .10 subwf LSD, w skpc return movwf LSD incf MSD, f goto gtenth ;------------------------------------------------------------------------ ; Read whole bit, set/clear error reset TMR0, count parity ;------------------------------------------------------------------------ ReadBit movlw 0xFF ;Set error state, movwf ERR ;will be cleared if bit correctly read call tmw_request ;TMW-DCC requesting new data? rb1 btfss INTCON, T0IF ;wait for timer to overflow goto rb1 ;not yet movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer bcf INTCON, T0IF ;clear overflow flag btfsc TSOP ;test 1st halfbit state goto read1 ;was high -> beginning of "1" read0 call tmw_request ;TMW-DCC requesting new data? r0 btfss INTCON, T0IF ;wait for timer to overflow goto r0 ;not yet movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer bcf INTCON, T0IF ;clear overflow flag call tmw_request ;TMW-DCC requesting new data? btfss TSOP ;test 2nd halfbit, must be high (set) return ;was not, error, return bcf STATUS, C ;have read a "0" rlf DATA1, f ;move it into DATA1 clrf ERR ;clear error state call tmw_request ;TMW-DCC requesting new data? return read1 call tmw_request ;TMW-DCC requesting new data? r1 btfss INTCON, T0IF ;wait for timer to overflow goto r1 ;not yet movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer bcf INTCON, T0IF ;clear overflow flag btfsc TSOP ;test 2nd halfbit, must be low (clear) return ;was not, error, return bsf STATUS, C ;have read a "1" rlf DATA1, f ;move it into DATA1 incf Parity, f ;add 1 to parity clrf ERR ;clear error state call tmw_request ;TMW-DCC requesting new data? return ;------------------------------------------------------------; ; MUX out TMW-DATA ; ;------------------------------------------------------------; tmw_request ;----------------------------------------------------; ;bytes needs to be sent always when the TMW-hardwre ;requests. If there is no request then return! ;----------------------------------------------------; btfss INTCON, INTF ;Has TMW-hw requested data? return ;no, return ;----------------------------------------------------; ;Bytes are sent in lots of 6. In case the ;tmw-bytecount is non-zero the sending of bytes of ;present packet (whatever it is) is still pending ;and must be finished until tmw-bytecount is zero. ;Jump directly to sending the byte! ;----------------------------------------------------; tstf tmw_bytecount ;Still pending packet bytes skpz ; goto send_packet ;Yes, go and send it ;----------------------------------------------------; ;Power-on routine needs to send at least 20 RESET and; ;10 IDLE packets. In case the power-on counter is ;zero, then we can go further, but else these packets ;need to be sent directly. Load the packets and jump ;to sending byte! ;----------------------------------------------------; tstf power_on ;Test if all power_on packets skpnz ;are sent goto tmw_cab_sel ;yes, lets load the cabs instead decf power_on, f ;decrement power_on-counter movlw .11 ;no. Lets see if RESET's are sent subwf power_on, w ;by subtracting 11 from power_on skpc ;no carry if power_up<=10 goto load_idle ;carry set, time for IDLEs goto load_reset ;no carry goto sending RESETS load_idle ;----------------------------------------------------------; ; IDLE ADDR: 11111111 ; ; DATA: 00000000 ; ; ERR: 11111111 ; ;----------------------------------------------------------; movlw b'11111111' ;preamble beginnings movwf tmw_0 ;preamble fills tmw_0 movwf tmw_1 ;and tmw_1 movlw b'11111011' ;rest of pre, sep, and start of address movwf tmw_2 ;to tmw_2 movlw b'11111100' ;rest of address, sep, and start of data movwf tmw_3 ;to tmw_3 movlw b'00000000' ;rest of data and sep movwf tmw_4 ;to tmw_4 movlw b'11111111' ;error byte movwf tmw_5 ;to tmw_5 movlw .6 ;six bytes to a packet movwf tmw_bytecount ; goto send_packet ;and out with it! load_reset ;----------------------------------------------------------; ; RESET ADDR: 00000000 ; ; DATA: 00000000 ; ; ERR: 00000000 ; ;----------------------------------------------------------; movlw b'11111111' ;preamble beginnings movwf tmw_0 ;preamble fills tmw_0 movwf tmw_1 ;and tmw_1 movlw b'11111000' ;rest of pre, sep, and start of address movwf tmw_2 ;to tmw_2 movlw b'00000000' ;rest of address, sep, and start of data movwf tmw_3 ;to tmw_3 movwf tmw_4 ;and rest of data and sep to tmw_4 movwf tmw_5 ;and error to tmw_5 movlw .6 ;six bytes to a packet movwf tmw_bytecount ; goto send_packet ;and out with it! tmw_cab_sel ;----------------------------------------------------; ;If we get here, it means that power-up is done and ;all the bytes of pending packet are sent! ;----------------------------------------------------; ;The cab counter runs from 8 to 0 (twice the number ;of cabs) if the counters top bit is high a speed ;packet needs to be sent, else functions packet is ;to be sent. ;First it must be checked if the cab counter is zero. ;if so, it must start again from 8 ;else the counter is decremented ;----------------------------------------------------; decf cab_sel,w ;change the number andlw b'00000111' ;clear top movwf cab_sel ;and return ;----------------------------------------------------; ;Now the bit 2 needs to be examined: ;1=speed, 0=function ;----------------------------------------------------; btfss cab_sel, 2 ; goto send_func ; send_speed ;----------------------------------------------------; ;Speed branch! ;----------------------------------------------------; ;The two LSB of cab counter will determine which ;addres byte is loaded for transferring into tmw_x ;If address byte is zero, load idle packet instead! ;If speed byte is zero, load idle packet instead! ; ; NOTE: this routine must clear the MSB of speed_x ; prior sending ;----------------------------------------------------; movfw cab_sel ;get the cab select andlw b'00000011' ;clear top to get cab number call lookup_IR_address movwf FSR ;get INDF point to address tstf INDF ;is it zero? skpnz ; goto load_idle ;yes, skip this cab! movlw b'11111111' ;preamble beginnings movwf tmw_0 ;fills tmw_0: 'PPPPPPPP' movwf tmw_1 ;and tmw_1:'PPPPPPPP' movlw b'11111000' ;rest of pre, sep, and start of address movwf tmw_2 ;to tmw_2: 'PPPPP0AA' btfsc INDF, 7 ;check address high end bytes bsf tmw_2, 1 ;in case set change resp. bit btfsc INDF, 6 ;check address high end bytes bsf tmw_2, 0 ;in case set change resp. bit movfw INDF ; movwf tmw_3 ; movwf tmw_5 ;move to err-byte also! rlf tmw_3, f ; rlf tmw_3, f ; bcf tmw_3, 0 ; bcf tmw_3, 1 ;tmw_3: 'AAAAAA0D' movfw cab_sel ;get the cab select andlw b'00000011' ;clear top to get cab number call lookup_IR_speed ; movwf FSR ;get INDF point to speed byte tstf INDF ;is it zero? (This is tested skpnz ;in case no speed has ever set!) goto load_idle ;yes, skip this cab! movfw INDF ; andlw b'01111111' ;clear top bit (IR flag!) movwf tmw_4 ;tmw_4 now has NMRA speed byte xorwf tmw_5, f ;set xor of w(speed) and tmw_5(addr) rlf tmw_4, f ;tmw_4= 'DDDDDDD0' bcf tmw_4, 0 ;and clear lsb (if carry was set!) movlw .6 ;six bytes to a packet movwf tmw_bytecount ; goto send_packet ;and out with it! send_func ;----------------------------------------------------; ;Func branch! ;----------------------------------------------------; ;The two LSB of cab counter will determine which ;address byte is loaded for transferring into tmw_x ;If address byte is zero, load idle packet instead! ;If func byte is zero, load idle packet instead! ; ; NOTE: this routine must clear bit 5 of func_x ; prior sending ;----------------------------------------------------; movfw cab_sel ;get the cab select andlw b'00000011' ;clear top to get cab number call lookup_IR_address movwf FSR ;get INDF point to address tstf INDF ;is it zero? skpnz ; goto load_idle ;yes, skip this cab! movlw b'11111111' ;preamble beginnings movwf tmw_0 ;fills tmw_0: 'PPPPPPPP' movwf tmw_1 ;and tmw_1:'PPPPPPPP' movlw b'11111000' ;rest of pre, sep, and start of address movwf tmw_2 ;to tmw_2: 'PPPPP0AA' btfsc INDF, 7 ;check address high end bytes bsf tmw_2, 1 ;in case set change resp. bit btfsc INDF, 6 ;check address high end bytes bsf tmw_2, 0 ;in case set change resp. bit movfw INDF ; movwf tmw_3 ; movwf tmw_5 ;move to err-byte also! rlf tmw_3, f ; rlf tmw_3, f ; bsf tmw_3, 0 ;NMRA G 1 func MSB =always set bcf tmw_3, 1 ;tmw_3: 'AAAAAA0D' movfw cab_sel ;get the cab select andlw b'00000011' ;clear top to get cab number call lookup_IR_func ; movwf FSR ;get INDF point to func byte tstf INDF ;is it zero? (This is tested skpnz ;in case no func's has ever set!) goto load_idle ;yes, skip this cab! movfw INDF ;w = IR func byte andlw b'11011111' ;clear IR 14/28 flag! movwf tmw_4 ;tmw_4= NMRA G1 func byte xorwf tmw_5, f ;set xor of w(func) and tmw_5(addr) rlf tmw_4, f ;tmw_4= 'DDDDDDD0' bcf tmw_4, 0 ;and clear lsb (if carry was set!) movlw .6 ;six bytes to a packet movwf tmw_bytecount ; goto send_packet ;and out with it! send_packet ;----------------------------------------------------; ;Sending byte ;decrement bytecount and so_byte and ;clear INTF and strobe out ;----------------------------------------------------; decf tmw_bytecount,w ;decrement bytecount movwf tmw_bytecount ;and save it call lookup_tmw_byte ;get the byte to be sent movwf FSR ;move to FTR to get movfw INDF ;correct data to call so_byte ;shift out the byte bsf tmw_strobe ;strobe out nop bcf tmw_strobe ;done strobing bcf INTCON, INTF ;clear the request flag return ;----------------------------------------------------; ;DONE! ;----------------------------------------------------; ;----------------------------------------------------------------------- ;shift out one byte from w (uses S_DATA1 and S_BITCOUNT) ;----------------------------------------------------------------------- so_byte movwf S_DATA1 ;put w put out for shifting movlw .8 ;all 8 bits movwf S_BITCOUNT ; sob1 rlf S_DATA1, F ;rotate through carry (MSB first) and btfsc STATUS, C ;in case of carry shift out 1 goto so1 ;carry=1 so0 ;carry=0 bcf S_DATA ;data lo bsf S_CLOCK ;clock hi nop ;wait for slow C-MOS bcf S_CLOCK ;clock lo goto sob2 so1 bsf S_DATA ;data hi bsf S_CLOCK ;clock hi nop ;wait for slow C-MOS bcf S_CLOCK ;clock lo sob2 decfsz S_BITCOUNT, F ;all bits sent? goto sob1 ;not yet, shift out more return ;yes, this byte shifted out ;------------------------------------------------------------------------ ; MAIN PROGRAMME ;------------------------------------------------------------------------ main call init ;intialize ports m1 ;---------------------------------------------------------------- ;First the TSOP is waited for a sign, and if seen, will jump to ;trying to make sense of it, else will loop here, and just check ;often enough if TMW requests data ;---------------------------------------------------------------- btfss TSOP ;wait for beginning of the broadcast goto m2 ;found, start interpreting btfss INTCON, T0IF ;No -- Has timer overflowed? goto m1 ;Not yet movlw HALFBIT ;Yes -- new timer base addwf TMR0, f ;add to just overflowed timer bcf INTCON, T0IF ;clear overflow flag call tmw_request ;TMW-DCC requesting new data? ;---------------------------------------------------- ;Idle 1 -> idle 2 is used for displaying the addresses ;of cabs if no IR commands have arrived recently (2 sec). ;This is for tracking the propable error of system ;loosing cab addresses! ;---------------------------------------------------- decfsz idle1, f ;change the number goto m1 ;nothing to display yet! decfsz idle2, f ;change the number goto m1 ;nothing to display yet! movlw .9 ;new seed to idle2 -> movwf idle2 ;approx. 2 sec incf idle_cab, w ;rotate the idle cab diplay number movwf idle_cab ;and return it to file andlw b'00000011' ;clear top bits and make this as if movwf IR_ADDRESS ;just received packet from this throttle btfss idle_cab, 2 ;every second idle round for address goto idle_address ;and every second to speed btfss idle_cab, 3 ;every second idle round for address goto idle_speed ;and every second either to speed goto idle_func ;or to func idle_address call lookup_ir_address movwf FSR movfw INDF goto disp_address ;display cab address status idle_speed call lookup_ir_speed movwf FSR movfw INDF movwf IR_DATA goto disp_speed ;display cab speed status idle_func call lookup_ir_func movwf FSR movfw INDF movwf IR_DATA goto disp_func ;display cab function status m2 ;-------------------------------------------------------------- ;TSOP has seen the light! Adjust timer so that we'll hit ;in the middle of first halfbit (quadbit) and try to ;read 0 for start bit ;-------------------------------------------------------------- bsf LED1 ;Some data, put LED off (set) clrf idle1 ;clear idle counter1 movlw .18 ;new seed to idle2: -> movwf idle2 ;approx. 2 sec movlw QUADBIT ;advance 1/4 bit to hit middle of movwf TMR0 ;1st half-bit of start-bit ("0") bcf INTCON, T0IF ;just in case: clear Timer overflow flag call ReadBit ;read whole start-bit btfsc ERR, 0 ;if error, return to square one goto m1 ; btfsc DATA1, 0 ;last read bit (LSB of DATA1) must goto m1 ;be "0" as it was the start bit IR_cab ;------------------------------------------------------------ ;Got the start bit, now read the IR throttles ID ;(two bits) ;----------------------------------------------------------- movlw 2 ;read 2 bits (four cabs) movwf BITCOUNT clrf DATA1 ;clear read DATA1 clrf Parity ;clear parity m3c call ReadBit ;read whole DATA1 bit btfsc ERR, 0 ;if error, return to square one goto m1 ; decfsz BITCOUNT, f ;All DATA1 bits read? goto m3c ;not yet, read more movfw DATA1 ;all DATA1 read andlw b'00000011' ;and clear top (safety matter!) movwf IR_ADDRESS ;move to safety IR_payload ;------------------------------------------------------- ;Time for receiving payload, read 8 bits ;------------------------------------------------------- movlw 8 ;read 8 bits movwf BITCOUNT clrf DATA1 ;clear read DATA1 ;not clearing parity! m3p call ReadBit ;read whole DATA1 bit btfsc ERR, 0 ;if error, return to square one goto m1 ; decfsz BITCOUNT, f ;All DATA1 bits read? goto m3p ;not yet, read more movfw DATA1 ;all DATA1 read movwf IR_DATA ;move to safety IR_parity ;----------------------------------------------------- ;All that remains is to get the Parity bit ;----------------------------------------------------- call ReadBit ;read parity bit btfsc ERR, 0 ;if error, return to square one goto m1 ; btfsc Parity, 0 ;Parity bit 0 must now be clear goto m1 ;if not return to square one bcf LED1 ;Sensible data, light the LED (clear bit) Check_previous goto Parse_payload ;----------------------------------------------------- ; All payloads of different cabs are stored in individual ; ex-payload-files and when next payload from this cab ; arrives it is compaired with the previous one. ; If they match we have good packets ;) ;----------------------------------------------------- movfw IR_ADDRESS ;lets get the cab ID to w call lookup_expay ;get previous payload address to w movwf FSR ;and make it the indirect register, movfw IR_DATA ;get the new payload xorwf INDF, w ;test if equal to old (from this cab!) btfsc status, z ;Was it equal? goto parse_payload ;yes, jump forward movfw IR_DATA ;nay, fetch the new payload again movwf INDF ;and make it the next ex-payload goto m1 ;and forget this packet Parse_payload ;---------------------------------------------------- ;Got full packet, start parsing: ; ; 0XXXXXXX => Address ; 10XXXXXX => Function ; 11XXXXXX => Speed ;------------------------------------------------------ movfw IR_ADDRESS ;lets get the cab ID to w ;to be handy while working with payload btfss IR_DATA, 7 ;test if payload was address? goto got_address ;Yes (clear), go with it btfss IR_DATA, 6 ;No (set), is payload a speed packet? goto got_func ;No (clear), so it must be func packet goto got_speed ;Yes (set) go for speed got_address call lookup_IR_address ;Lets get the cab's address register movwf FSR ;and make it the indirect register, movfw IR_DATA ;load payload to accumulator movwf INDF ;and further to correct cab's addr. reg! ;goto change_addr ;speed zero ;------------------------------------------------------------- ; First let's test that the speed of this cab is zero ; (SSSS = 0000) ;------------------------------------------------------------- movfw IR_ADDRESS ;lets get the cab ID to w call lookup_ir_speed ;and get the speed file address to w movwf FSR ;and make it the indirect register, movfw INDF ;get the speed byte andlw b'00001111' ;mask hi nibble away skpnz goto change_addr ;speed zero goto disp_err ;------------------------------------------------------------- ; This routine checks that no other cab has the same ; address, and in case it has it silently clears the address ; of the found other cab's equal address (steals the loco!) ; BUT only in case the speed of that cab is also set to zero! ; ; Another solution could be to warn and not capture, but this will ; propably cause more havoc as one should find which cab has the ; address in use (could be displayed instead of address!) ; but where the heck is this cab now (no wires!) ;------------------------------------------------------------- change_addr call tmw_request ;TMW-DCC requesting new data? steal_cab0 movfw IR_DATA ;Load new address to w xorwf addr_0, w ;and compare with addr_x. btfss status, z ;Was it equal? goto steal_cab1 ;nay, lets try next cab movfw speed_0 ;yes, let's see that speed andlw b'00001111' ;is zero before stealing! skpz goto disp_err ;no, forget it! clrf addr_0 ;yes, cleared steal_cab1 movfw IR_DATA ;Load new address to w xorwf addr_1, w ;and compare with addr_x. btfss status, z ;Was it equal? goto steal_cab2 ;nay, lets try next cab movfw speed_1 ;yes, let's see that speed andlw b'00001111' ;is zero before stealing! skpz goto disp_err ;no, forget it! clrf addr_1 ;yes, cleared steal_cab2 movfw IR_DATA ;Load new address to w xorwf addr_2, w ;and compare with addr_x. btfss status, z ;Was it equal? goto steal_cab3 ;nay, lets try next cab movfw speed_2 ;yes, let's see that speed andlw b'00001111' ;is zero before stealing! skpz goto disp_err ;no, forget it! clrf addr_2 ;yes, cleared steal_cab3 movfw IR_DATA ;Load new address to w xorwf addr_3, w ;and compare with addr_x. btfss status, z ;Was it equal? goto stealing_done ;nay, lets try next cab movfw speed_3 ;yes, let's see that speed andlw b'00001111' ;is zero before stealing! skpz goto disp_err ;no, forget it! clrf addr_3 ;yes, cleared stealing_done movfw IR_ADDRESS ;get the cab number call lookup_IR_address ;find corresponding addr_x movwf FSR ;and make it the indirect register, movfw IR_DATA ;load payload to accumulator movwf INDF ;and further to correct cab's addr. reg! call tmw_request ;TMW-DCC requesting new data? disp_address call bin_to_BCD ;make decimal movfw LSD ;get lower digit call lookup_7_seg ;get 7-seg of the low nibble call so_byte ;and shift out byte movfw MSD ;get higher digit call lookup_7_seg ;get 7-seg of the high nibble call so_byte ;and shift out byte movlw b'00001010' ;light 'A'! call so_byte ;and shift out byte movfw IR_ADDRESS ;get cab number addlw 0x1 ;increment to get laymen's numbers ;) call lookup_7_seg ;change to 7-segment call so_byte ;and shift out byte bsf S_STROBE ;strobe out nop ;wait for slow C-MOS bcf S_STROBE ;strobing done goto m1 ;and back to square 1 got_speed call lookup_IR_speed ;Lets get the cab's speed register movwf FSR ;and make it the indirect register, movfw IR_DATA ;and load it to accumulator movwf INDF ;and further to correct cab's speed. reg! ;------------------------------------------------------------- ; The FL should only be displayed as dp with speed/func. ; Functions F1..F4 should be displayed as short or long ; vertical bar at F-display like "2FiiIi." (cab 2, F2+FL set) ;------------------------------------------------------------- disp_speed incf FSR, f ;move FSR to point to func_x byte btfss INDF, 5 ;Skip if speed mode = 28 step goto disp_14s disp_28s ;------------------------------------------------------------ ; 28 speed steps, first test if SSSS of the speed is equal ; to zero (0000) and if so just get it out (little "s" ; makes no difference, 00000 and 00001 (SSSSs) are both "stop" ; second, test if its emg break (0001) and display "ES" ; and again little s makes no difference. ; Else decrement the speed by two, and put it out ;------------------------------------------------------------- movfw IR_DATA ;Load new address to w andlw b'00001111' ;leave the 4 LSB speed bits skpnz ;was speed zero? ;## call disp_28s2 ;yes, ready for bin_to BCD ## ERROR ## goto disp_28s2 ;yes, ready for bin_to BCD xorlw b'00000001' ;no, compare with Emg.Stop. skpz ;Was it Emg.Stop? goto disp_28s1 ;no, display value -2 movlw b'10001001' ;yes, (z set), load "S" btfsc INDF, 4 ;test the lights (INDF->func_x) andlw b'11110111' ;lights on, light (clear) the dp call so_byte ;and shift out byte movlw b'10001100' ;load "E" call so_byte ;and shift out byte goto disp_s1 ;and get the cab and dir out as well disp_28s1 rlf IR_DATA, w ;get to w, but shifted left andlw b'00011110' ;leave the 4 LSB speed bits btfsc IR_DATA, 4 ;test little "s" iorlw b'00000001' ;set the lsb of w addlw 0xFD ;add FD (equal to decrement 2) disp_28s2 call bin_to_BCD ;make decimal movfw LSD ;get lower digit call lookup_7_seg ;get 7-seg of the low nibble btfsc INDF, 4 ;Test Lights, FSR->func_x, dp on if on! andlw b'11110111' ;yes, light (clear) dp call so_byte ;and shift out byte movfw MSD ;get higher digit call lookup_7_seg ;get 7-seg of the high nibble call so_byte ;and shift out byte goto disp_s1 ;and get the cab and dir out as well disp_14s ;------------------------------------------------------------ ; 14 speed steps, first test if the speed is equal ; to zero (0000) and if so just get it out ; second, test if its emg break (0001) and display "ES" ; else decrement the speed by one, and put it out ;------------------------------------------------------------- movfw IR_DATA ;Load new address to w andlw b'00001111' ;leave the 4 LSB speed bits skpnz ;was speed zero? ;## call disp_14s2 ;yes, ready for bin_to BCD ## ERROR ## goto disp_14s2 ;yes, ready for bin_to BCD xorlw b'00000001' ;no, compare with Emg.Stop. skpz ;Was it Emg.Stop? goto disp_14s1 ;no, display value -1 movlw b'10001001' ;yes, (z set), load "S" btfsc IR_DATA, 4 ;test lights (little "s") andlw b'11110111' ;light the lights (clear) dp bit w call so_byte ;and shift out byte movlw b'10001100' ;load "E" call so_byte ;and shift out byte goto disp_s1 ;and get the cab and dir out as well disp_14s1 movfw IR_DATA andlw b'00001111' ;leave the 4 MSB speed bits addlw 0xFF ;add FE (equal to decrement 1) disp_14s2 call bin_to_BCD ;make decimal movfw LSD ;get lower digit call lookup_7_seg ;get 7-seg of the low nibble decf FSR, f ;get FSR to point to speed btfsc INDF, 4 ;Test for Lights (dp on if on!) andlw b'11110111' ;yes, light (clear) dp call so_byte ;and shift out byte movfw MSD ;get higher digit call lookup_7_seg ;get 7-seg of the high nibble call so_byte ;and shift out byte disp_s1 movlw b'11111111' ;show blank as msb of speed btfss IR_DATA, 5 ;test direction bit (set=fwd) movlw b'11101111' ;no - light '-' instead (clear=rev) call so_byte ;and shift out byte movfw IR_ADDRESS ;get cab number addlw 0x1 ;increment to get laymen's numbers ;) call lookup_7_seg ;change to 7-segment call so_byte ;and shift out byte bsf S_STROBE ;strobe out nop ;wait for slow C-MOS bcf S_STROBE ;strobing done goto m1 ;and back to square 1 got_func call lookup_IR_func ;Lets get the cab's address register movwf FSR ;and make it the indirect register, movfw IR_DATA ;load payload to accumulator movwf INDF ;and further to correct cab's addr. reg! disp_func btfsc IR_DATA, 5 ;Skip if speed mode = 14 step goto disp_28f disp_14f ;----------------------------------------------------- ; Leftmost 7-segment first: ; set bottom bars on (0), test for function ; bits and test F1 and F2 and light(0) resp top bars ; test the FL and set (0) dp ;------------------------------------------------------ movlw b'11111010' ;all but bottom bars off btfsc IR_DATA, 0 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc IR_DATA, 1 ;testing F1 andlw b'11011111' ;clear (light) left top bar decf FSR, f ;get INDF to point to speed btfsc INDF, 4 ;check the F5/FL is on andlw b'11110111' ;light dp call so_byte ;and shift out byte movlw b'11111010' ;all but bottom bars off btfsc IR_DATA, 2 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc IR_DATA, 3 ;testing F1 andlw b'11011111' ;clear (light) left top bar call so_byte ;and shift out byte goto disp_f1 ; disp_28f movlw b'11111010' ;all but bottom bars off btfsc IR_DATA, 0 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc IR_DATA, 1 ;testing F1 andlw b'11011111' ;clear (light) left top bar btfsc INDF, 4 ;check the F5/FL is on andlw b'11110111' ;light dp call so_byte ;and shift out byte movlw b'11111010' ;all but bottom bars off btfsc IR_DATA, 2 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc IR_DATA, 3 ;testing F1 andlw b'11011111' ;clear (light) left top bar call so_byte ;and shift out byte disp_f1 movlw b'10001110' ;light 'F'! call so_byte ;and shift out byte movfw IR_ADDRESS ;get cab number addlw 0x1 ;increment to get laymen's numbers ;) call lookup_7_seg ;change to 7-segment call so_byte ;and shift out byte bsf S_STROBE ;strobe out nop ;wait for slow C-MOS bcf S_STROBE ;strobing done goto m1 ;and back to square 1 disp_err movlw b'01110111' ; ! call so_byte ; movlw b'11101110' ; r call so_byte ; movlw b'11101110' ; r call so_byte ; movlw b'10001100' ; E call so_byte ; bsf S_STROBE ;strobe out (display "Err!") nop ;wait for slow C-MOS bcf S_STROBE ;strobing done goto m1 ;--------------------------------------------------------------- ;Lookup-table for 7-seg display, uses 4094 shift registers ;--------------------------------------------------------------- ORG 0x300 ; last page Lookup_7_seg movwf temp ;Make sure the movlw 0x03 ;jump of retlw movwf pclath ;gets to the correct movfw temp ;page in memory andlw 0x0F ;clear top to prevent going past addwf pcl,f ; retlw b'00011000' ; 0 retlw b'01111011' ; 1 retlw b'00101100' ; 2 retlw b'00101001' ; 3 77777 retlw b'01001011' ; 4 6 8 retlw b'10001001' ; 5 6 8 retlw b'10001000' ; 6 6 8 retlw b'00111011' ; 7 55555 retlw b'00001000' ; 8 1 3 retlw b'00001001' ; 9 1 3 retlw b'00001010' ; A 1 3 44 retlw b'11001000' ; b 22222 44 retlw b'10011100' ; C retlw b'01101000' ; d retlw b'10001100' ; E retlw b'10001110' ; F retlw b'11101111' ; - retlw b'10001111' ; P ;--------------------------------------------------------------- ;Lookup-tables for cab to be served ;--------------------------------------------------------------- lookup_expay movwf temp ;Make sure the movlw 0x03 ;jump of retlw movwf pclath ;gets to the correct movfw temp ;page in memory andlw 0x03 ;clear top to prevent going past addwf pcl,f ; retlw expay_0 ;point to cab 0's previous payload retlw expay_1 ;point to cab 1's previous payload retlw expay_2 ;point to cab 2's previous payload retlw expay_3 ;point to cab 3's previous payload lookup_IR_address movwf temp ;Make sure the movlw 0x03 ;jump of retlw movwf pclath ;gets to the correct movfw temp ;page in memory andlw 0x03 ;clear top to prevent going past addwf pcl,f ; retlw addr_0 ;point to cab 0's DCC address storage retlw addr_1 ;point to cab 1's DCC address storage retlw addr_2 ;point to cab 2's DCC address storage retlw addr_3 ;point to cab 3's DCC address storage lookup_IR_speed movwf temp ;Make sure the movlw 0x03 ;jump of retlw movwf pclath ;gets to the correct movfw temp ;page in memory andlw 0x03 ;clear top to prevent going past addwf pcl,f ; retlw speed_0 ;point to cab 0's DCC speed storage retlw speed_1 ;point to cab 1's DCC speed storage retlw speed_2 ;point to cab 2's DCC speed storage retlw speed_3 ;point to cab 3's DCC speed storage lookup_IR_func movwf temp ;Make sure the movlw 0x03 ;jump of retlw movwf pclath ;gets to the correct movfw temp ;page in memory andlw 0x03 ;clear top to prevent going past addwf pcl,f ; retlw func_0 ;point to cab 0's DCC func storage retlw func_1 ;point to cab 1's DCC func storage retlw func_2 ;point to cab 2's DCC func storage retlw func_3 ;point to cab 3's DCC func storage lookup_TMW_byte movwf temp ;Make sure the movlw 0x03 ;jump of retlw movwf pclath ;gets to the correct movfw temp ;page in memory andlw 0x07 ;clear top to prevent going past addwf pcl,f ; retlw tmw_5 ;point to tmw-byte 5 retlw tmw_4 ;point to tmw-byte 4 retlw tmw_3 ;point to tmw-byte 3 retlw tmw_2 ;point to tmw-byte 2 retlw tmw_1 ;point to tmw-byte 1 retlw tmw_0 ;point to tmw-byte 0 retlw tmw_0 ;in case overrun! retlw tmw_0 ;in case overrun! end