;************************************************************************ ; TaPRK IR-LINK-RECEIVER (PC-ELIMINATOR;) VER 2007-02-24 ;************************************************************************ LIST P = PIC16F84, R = HEX ;Prosessortype, radix INCLUDE "p16f84.inc" ;w,f,porta,portb etc. ERRORLEVEL -302 ;prevents warnings from ;page changes __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 ; RESET/Vdd --|4 15|-- XTAL ; Vss(GND)--|5 14|-- Vdd (+5V) ; TMW_REQ PORTB.0 ->|6/INT 13|<- PORTB.7 N/C ; 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 loosly 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 form is Mancester code (like 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: CCC0AAAAAAAP CCC =transmitter ID ; 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: CCC11DsSSSSP CCC =transmitter ID ; 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: CCC10MFFFFFP CCC =transmitter ID ; 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 12 bit messages: ; three 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! ; [2007-01-17] Added request for two identical packets, and increased ; the number of throttles to eight. ; [2007-01-28] Changing to address 0 and switching off a cab may cause ; speed set to address 0 thus other cabs could not be set ; to cab 0 before switching off. ;************************************************************************ ; 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 temp ;long call assist victim ;the cab that will have the loco stolen from 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 expay_4 ;previous data packet speed_4 ;cab 4 speed (4 bits) func_4 ;cab 4 functions addr_4 ;cab 4 address expay_5 ;previous data packet speed_5 ;cab 5 speed (4 bits) func_5 ;cab 5 functions addr_5 ;cab 5 address expay_6 ;previous data packet speed_6 ;cab 6 speed (4 bits) func_6 ;cab 6 functions addr_6 ;cab 6 address expay_7 ;previous data packet speed_7 ;cab 7 speed (4 bits) func_7 ;cab 7 functions addr_7 ;cab 7 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 RX_CAB_ID ;IR-transmitter ID RX_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 clrf porta ;clear Port A (Shift reg output) clrf portb ;Port B on (LED test) BSF STATUS,RP0 ;RAM bank 1 movlw b'11100001' ;RB0/INT=in, 1..7=out movwf trisb ;Output-> O -> 0 movlw b'00010001' ;RA0=in, RA1..3=out movwf trisa ;Output-> O -> 0 BCF STATUS,RP0 ;RAM bank 0 ;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 movlw .18 ;new seed to idle2: -> movwf idle2 ;approx. 2 sec 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 F 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 F ;else the counter is decremented ;----------------------------------------------------; decf cab_sel,w ;change the number andlw b'00001111' ;clear top movwf cab_sel ;and return ;----------------------------------------------------; ;Now the bit 3 needs to be examined: ;1=speed, 0=function ;----------------------------------------------------; btfss cab_sel, 3 ; goto send_func ; send_speed ;----------------------------------------------------; ;Speed branch! ;----------------------------------------------------; ;The three 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'00000111' ;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'00000111' ;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 three 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'00000111' ;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'00000111' ;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 nop bsf S_CLOCK ;clock hi nop ;wait for slow C-MOS bcf S_CLOCK ;clock lo goto sob2 so1 bsf S_DATA ;data hi nop 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'00000111' ;clear top bits and make this as if movwf RX_CAB_ID ;just received packet from this throttle btfss idle_cab, 3 ;every second idle round for addr/step goto idle_more ;and every second to speed/func btfss idle_cab, 4 ;split the speed/func goto idle_speed ;to speed goto idle_func ;or to func idle_more btfss idle_cab, 4 ;and split the other (addr/step goto idle_step ;to speed step mode goto idle_address ;or to address idle_step call lookup_ir_func movwf FSR movfw INDF movwf RX_DATA goto disp_step ;display cab speed step status 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 RX_DATA goto disp_speed ;display cab speed status idle_func call lookup_ir_func movwf FSR movfw INDF movwf RX_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. 4 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 ;(three bits) ;----------------------------------------------------------- movlw 3 ;read 3 bits (eight 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'00000111' ;and clear top (safety matter!) movwf RX_CAB_ID ;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 RX_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 call tmw_request ;TMW-DCC requesting new data? ;----------------------------------------------------- ; 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 RX_CAB_ID ;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 RX_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 RX_DATA ;nay, fetch the new payload again movwf INDF ;and make it the next ex-payload ;------------------------------------------------------ ; Comment the next line to request double packets ; only for address changing packets ;------------------------------------------------------ btfss RX_DATA, 7 ;test if payload was address? goto m1 ;yes, forget this packet Parse_payload ;---------------------------------------------------- ;Got full packet, start parsing: ; ; 0XXXXXXX => Address ; 10XXXXXX => Function ; 11XXXXXX => Speed ;------------------------------------------------------ movfw RX_CAB_ID ;lets get the cab ID to w ;to be handy while working with payload btfss RX_DATA, 7 ;test if payload was address? goto got_address ;Yes (clear), go with it btfss RX_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 ;------------------------------------------------------------- ; First let's test that the speed of this cab is zero ; (SSSS = 0000) ;------------------------------------------------------------- movfw RX_CAB_ID ;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 ;------------------------------------------------------------- ; Next check if the new address is zero, and bypass stealing ;------------------------------------------------------------- movfw RX_DATA ;get new address skpnz ;if it is zero goto stealing_done ;no need to steal anything change_addr ;------------------------------------------------------------- ; 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!) ;------------------------------------------------------------- call tmw_request ;TMW-DCC requesting new data? movlw .8 ;circulate all 8 cabs (1..8) movwf victim steal_cab decf victim, f ;victim cab counter now 0..7 movfw victim ;temp is victim cab counter now call lookup_ir_address;Load address of the victim to w movwf FSR ;and make it the indirect register, movfw INDF ;get the addres byte xorwf RX_DATA, w ;and compare with would-be address. skpz goto steal_cab1 ;nay, lets try next cab movfw victim ;yes, get victim cab number again call lookup_ir_speed ;looking up the memory locatoin movwf FSR ;and loading it into the indirect register movfw INDF ;to get the speed value andlw b'00001111' ;is zero before stealing! skpz goto disp_err ;no, forget it! movfw victim ;yes, get victim cab number again call lookup_ir_address;Load address of the victim to w movwf FSR ;and make it the indirect register, clrf INDF ;and clear it! steal_cab1 tstf victim ;test victim: skpz ;was it the last one (zero) goto steal_cab ;no, check the next one stealing_done movfw RX_CAB_ID ;get the cab number call lookup_IR_address ;find corresponding addr_x movwf FSR ;and make it the indirect register, movfw RX_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 RX_CAB_ID ;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 RX_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 RX_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 RX_DATA, w ;get to w, but shifted left andlw b'00011110' ;leave the 4 LSB speed bits btfsc RX_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 RX_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 RX_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 RX_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 RX_DATA, 5 ;test direction bit (set=fwd) movlw b'11101111' ;no - light '-' instead (clear=rev) call so_byte ;and shift out byte movfw RX_CAB_ID ;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 ;---------------------------------------------------- ; Apart from storing the function, only changed ; function action will be displayed, so that ; if speed notch count is changed it will be displayed ; as the bit 5 changes. The next time the function packet ; with exactly the same data would arrive it would jump ; into showing func settings, so the speed notch would ; be only displayed for the split second. ; ; So function packets are displayed only in case something ; had changed! ;--------------------------------------------------------- call lookup_IR_func ;Lets get the cab's address register movwf FSR ;and make it the indirect register, movfw INDF ;let's get the previous func byte movwf temp ;temporarily in safe movfw RX_DATA ;load payload to accumulator movwf INDF ;and further to correct cab's addr. reg! xorwf temp, f ;compair previous to this: skpnz ;test if anything changed? goto m1 ;Nope! Back to square one! btfss temp, 5 ;so, did the step change? goto disp_func ;No, so it was functions changing! disp_step btfsc RX_DATA, 5 ;Skip if speed mode = 14 step goto disp_28st ;display symbol of 28 speed step goto disp_14st ;display symbol of 14 speed step disp_func btfsc RX_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 RX_DATA, 0 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc RX_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 RX_DATA, 2 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc RX_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 RX_DATA, 0 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc RX_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 RX_DATA, 2 ;testing F1 andlw b'01111111' ;clear (light) right top bar btfsc RX_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 RX_CAB_ID ;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_14st movlw b'01001011' ; 4 call so_byte ; movlw b'01111011' ; 1 call so_byte ; goto disp_st disp_28st movlw b'00001000' ; 8 call so_byte ; movlw b'00101100' ; 2 call so_byte ; goto disp_st disp_st movlw b'01101110' ; / call so_byte ;and shift out byte movfw RX_CAB_ID ;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 0x07 ;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 retlw expay_4 ;point to cab 4's previous payload retlw expay_5 ;point to cab 5's previous payload retlw expay_6 ;point to cab 6's previous payload retlw expay_7 ;point to cab 7'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 0x07 ;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 retlw addr_4 ;point to cab 4's DCC address storage retlw addr_5 ;point to cab 5's DCC address storage retlw addr_6 ;point to cab 6's DCC address storage retlw addr_7 ;point to cab 7'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 0x07 ;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 retlw speed_4 ;point to cab 4's DCC speed storage retlw speed_5 ;point to cab 5's DCC speed storage retlw speed_6 ;point to cab 6's DCC speed storage retlw speed_7 ;point to cab 7'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 0x07 ;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 retlw func_4 ;point to cab 4's DCC func storage retlw func_5 ;point to cab 5's DCC func storage retlw func_6 ;point to cab 6's DCC func storage retlw func_7 ;point to cab 7'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