
	title	dccace1.asm

	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   

;************************************************************************
; SCHEMATIC DIAGRAM
;************************************************************************
;
;	          		  +---+_+---+
;  	    Col addr.C	PORTA.2 <-|1      18|-> PORTA.1 Col addr.B
;	    Col addr.D	PORTA.3 <-|2      17|-> PORTA.0	Col addr.A
;	    DCC OUT	PORTA.4 <-|3      16|-- XTAL
;	    RESET	    Vdd --|4      15|-- XTAL
;			Vss(GND)--|5      14|-- Vdd (+5V)
;		ROW 1	PORTB.0 ->|6      13|-> PORTB.7	ROW 8
;		ROW 2	PORTB.1 <-|7      12|-> PORTB.6	ROW 7
;		ROW 3	PORTB.2 <-|8      11|-> PORTB.5	ROW 6
;		ROW 4	PORTB.3 <-|9      10|-> PORTB.4	ROW 5
;		    	          +---------+
;
;===========================================================================
; Original system has on-off switches, my attempt is to convert
; into (on)-off-(on) switches, first switch found off zero will will be sent
; until released. Must send some idles between packets and only
; idles if no switches are turned
;
; Original addressing scheme:
;
;		COL 1	COL2	COL3	...
;
;	ROW 1:	A0.1	A2.1	A4.1	...
;	ROW 2:	A0.2	A2.2	A4.2	...
;	ROW 3:	A0.3	A2.3	A4.3	...
;	ROW 4:	A0.4	A2.4	A4.4	...
;	ROW 5:	A1.1	A3.1	A5.1	...
;	ROW 6:	A1.2	A3.2	A5.2	...
;	ROW 7:	A1.3	A3.3	A5.3	...
;	ROW 8:	A1.4	A3.4	A5.4	...
;
;
; Revised scheme
;
;		COL 1	COL2	COL3	COL4	COL5	...	COL16
;
;	ROW 1:	A1.1+	A1.2+	A1.3+	A1.4+	A2.1+	...	A4.4+
;	ROW 2:	A1.1-	A1.2-	A1.3-	A1.4-	A2.1-	...	A4.4-
;	ROW 3:	A5.1+	A5.2+	A5.3+	A5.4+	A6.1+	...	A8.4+
;	ROW 4:	A5.1-	A5.2-	A5.3-	A5.4-	A6.1-	...	A8.4-
;	...
;
; Accessory decoder packet format:
; ================================
;	PPPPPP 0 10AA-AAAA 0 1aaa-DCCX 0 EEEE-EEEE 1 PPP...
;	
;	Where:
;	------
;	P	= Preamble min. 14 ones
;	A	= Address LSB bits
;	a	= Address MSB bits complemented (0=>1, 1=>0)
;	D	= Data, w. turnouts must be 1 (lenz requires no resetting to 0)
;	C	= Turnout number within decoder
;	X	= which coil within turnout
;	E	= Error detection byte
;
;**********************************************************************************************
;	Original written by Mike Bolton:
;
;	This program is for an accessory only encoder. It scans a matrix of
;	on / off switches, each with series diodes and detects changes in the 
;	switch position. Accessory packets are sent to set one of each
;	output pair dependent on the switch position. Only one packet is sent
;	for each change. If there has been no change, an idle packet is sent.
;	A 4 to 16 line multiplexer is used to drive 16 columns. 8 rows are 
;	sensed by the PIC input lines. This gives a maximum of 128 switches.
;	Each set of four switches has a unique DCC address. Column 1 rows 1 to 4
;	are address 0, column 1 rows 5 to 8 are address 1 etc. allowing for upto
;	32 separate decoders, each with 4 output pairs.
;	When the encoder is first switched on, all switches are interrogated and
;	their positions complemented. This gives the impression that each has changed
;	so the encoder sends packets to all outputs to set them to the switch positions
;	This is essential for solenoid point motors as they can be in any position
;	at switch-on. There is a manual reset button which also sets all points
;	correctly in case any have stuck in the wrong position.
;	The present program only sends 'activate' packets. It assumes the decoder will
;	'time out' quickly as for solenoid drives. It may be modified for true on / off
;	actions for lights.
;
;	No aspects of the program are copyright and it may be freely used
;	copied, modified or distributed in whole or part. Acknowledgement of the original
;	author would be appreciated. 
;
;	Hardware schematics are also freely available from the author
;	email bolton@fs1.with.man.ac.uk   
;
;	03/05/02	Corrections and timimg changes
;                       Working with one decoder  04/05/02
;			Not tested with any more (I only have one!)
;*********************************************************************

;
;	Registers used	predefined
;
;	working registers

temp	equ	0E	;temp
spare	equ	0F	;spare
colcnt	equ	10	;column counter
byte1	equ	11	;first bute of packet
byte2	equ	12	;second byte of packet
byte3	equ	13	;third byte of packet (for future use)
err1	equ	14	;error byte
bitcnt	equ	15	;bit count in packet
bytcnt	equ	16	;in packet
temp1	equ	17
temp2	equ	18
row	equ	19
fcrtmp	equ	1A
pktcnt	equ	1B	;number of packets
count4	equ	1C	;extra counter
rowcnt	equ	1D	;row counter
bitnum	equ	1E	;sets bit in row (shift register)
adrcnt	equ	1F	;address counter

#DEFINE DCC_OUT			porta,4 	;DCC signal out

;*************************************************************************
;
	org		0
	goto	setup	;reset vector
;
	org	04	;interrupt vector
	retfie		;return from interrupt
;
;************************************************************************
;
;start of program. Sets up ports etc.


	org	10
	
setup
	movlw	b'11111111'	;RB.1..7=in
	tris	portb		;

	movlw	b'00000000'	;RA.0..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'00000000'	;PullUp=on, int. clk, PreScaler on TMR0=1:2
	movwf	OPTION_REG	;
	bcf	STATUS, RP0	;

	clrf	colcnt

;	end of setup

	movlw	0	;goto init routine
	goto	loop

;***************************************************************
;
;	main jump table	

main	bcf	intcon,2	;clear TMR0 flag
main1	addwf	pcl,f
	goto	init		;0
	goto	p2          	;1	packet routine
	goto	one1		;2	-"-
	goto	zero1		;3	-"-
	goto	zero2		;4	-"-
	goto	one2		;5	-"-
	goto	nxtby1		;6	-"-
	goto	pr1		;7	preamble 
	goto	pr3		;8	-"-
	goto	pr4		;9 	-"-
	goto	scand		;10	scan
	goto	scanu		;11	-"-
	goto	c1		;12	change

;******************************************************************
;
;	timing loop. Waits for TMR0, resets and jumps
	
loop	btfss	intcon,2	;wait loop
	goto	loop
	movwf	temp		;save w
	movlw	0xE6		;reset TMR0
	movwf	TMR0
	movf	temp,w		;recover w
	goto	main
;
;******************************************************************
;
;	Initialise sequence
;	reads all switches and complements them
;	so it appears as if they have all changed
;	cycles through all outputs of all decoders
;	so starting settings are correct


init	movlw	.10
	movwf	pktcnt		;send 10 reset packets
	call	reset 
	movlw 	.10
	movwf	pktcnt		;send 10 reset packets
	call	idle
	clrf	pktcnt		;maybe not necessary
		
	
;***********************************************************
;
;	main read loop
;
	movlw	b'00010000'	;dcc_out set, row=0
	movwf	porta		;set out
	clrf	row		;clear row
	clrf	colcnt		;clear col count
	clrf	rowcnt		;clear row count
scan	movlw	.10             ;wait before scand
	goto	loop 		;in the loop

scand   bcf	DCC_OUT		;output down
	comf	portb, w	;get row data
	movwf	row		;save in row
	skpz			;no change
	goto	change		;yes, out with it

scan1	movlw	.11		;wait before scanu
	goto	loop          	;in the loop

scanu	bsf	DCC_OUT	       	;output up
	incf	colcnt,w	;increment column
	andlw	B'00001111'	;columns 0 to 15
	movwf	colcnt
	movf	porta,w		;get port a 
	andlw	B'11110000'	;save DCC_OUT, discard address
	iorwf	colcnt,w	;get new column address 
	iorlw	B'00010000'	;keep dcc output
	movwf	porta		;select new column
	
	
	incf	pktcnt,f	;one packet 
	call	idle		;to give delay
	goto	scan
				
;------------------------------------------------------------
; Here if switch set
; ------------------
; PPPPPP 0 10AA-AAAA 0 1aaa-DCCX 0 EEEE-EEEE 1
;
; Create packet from colcnt and rowcnt:
;
;	colcnt low nibble => A1, A0, C1, C0
;	rowcnt low nibble => A4, A3, A2, X
;-----------------------------------------------------------

change	clrf	rowcnt		;row's bit pos denotes
	decf	rowcnt, f	;rowcnt value
c_loop	incf	rowcnt, f	;value converted to binary w.
	rrf	row, f		;rotate right throuh carry
	skpc			;if carry is not found
	goto 	c_loop		;we'll rotate more

	movlw	B'10000000'	;Byte 1: 10AA-AAAA
	movwf	byte1		;--------------------
	btfsc	colcnt, 2	;Test column bit 2 (A0)
	bsf	byte1,  0	;yes set A0
	btfsc	colcnt, 3	;Test column bit 3 (A1)
	bsf	byte1,  1	;yes set A1

	btfsc	rowcnt, 1	;Test row bit 1 (A2)
	bsf	byte1,  2	;yes set A2
	btfsc	rowcnt, 2	;Test row bit 2 (A3)
	bsf	byte1,  3	;yes set A3

	incf	byte1, f	;shift to Lenz addressing

	movlw	.12		;wait before jumping to c1
	goto	loop		;inside loop
c1	bsf	DCC_OUT		;output up

	movlw	B'11111000'	;Byte 2: 1111-1CCX
	movwf	byte2		;--------------------
	btfsc	colcnt, 0	;Test column bit 0 (C0)
	bsf	byte2,  1	;yes set C0
	btfsc	colcnt, 1	;Test column bit 1 (C1)
	bsf	byte2,  2	;yes, set C1
	btfsc	rowcnt, 0	;Test row bit 0 (X)
	bsf	byte2,  0	;yes, set X

	movlw	3		;total 3 bytes (E inclusive)
	movwf	bytcnt		;to bytecount
	call	preamb		;send preamble
	call	packet		;send the packet proper
	goto 	scan		;loop again
	
;*****************************************************
;
;	subroutines
;
;	
	
;******************************************************
;
;	sends a complete reset packet
;	number of packets in pktcnt
;
reset	movlw	B'00000000'
	movwf	byte1
	movlw	B'00000000'
	movwf	byte2
	movlw	3
	movwf	bytcnt
	call	preamb
	call	packet
	decfsz	pktcnt,f
	goto	reset 
    	return 
;
;******************************************************
;
;	sends a complete idle packet
;	number of packets in pktcnt

idle	movlw	B'11111111'
	movwf	byte1
	movlw	B'00000000'
	movwf	byte2
	movlw	3
	movwf	bytcnt 
	call	preamb
	call	packet
	decfsz	pktcnt,f
	goto	idle
	return
;
;****************************************************************
;
;	main packet routine
;
;	sends a packet without preamble
;	data to be set in byte1 to byte3 etc
;	number of bytes in packet in bytcnt
;	routine works out the error byte itself
;	adds start bits 
;	must arrive with the DCC output high
;
;			 
packet	movlw	byte1    	;point FSR to start of bytes
	movwf	FSR
	movf	bytcnt,w	;get no of bytes
	movwf	temp
	decf	temp,f
	decf	temp,f
	movf	INDF,w		;get 1st byte
	incf	FSR,f 
p1	xorwf	INDF,w		;work out error
	incf	FSR,f 
	decfsz	temp,f
	goto	p1 
	movwf	INDF		;error byte
	movlw	byte1 		;reset index
	movwf	FSR
	
	

		
ploopa	btfss	intcon,2  	;wait for TMR0
	goto	ploopa 
	

	movlw	0xED		;sets zero for 100 usec.
	movwf	TMR0
	bcf	intcon,2

	movlw	.9
	movwf	bitcnt		;set bitcount (8 bits)
	movlw	.1		;wait for TMR0 in main loop
	goto	loop		;jump to p2
p2	bcf	DCC_OUT		;output lo (first half of start bit complete)
;
;	second half of start bit
;
		 
ploop2	btfss	intcon,2
	goto	ploop2 
	movlw	0xED		;reset TMR0
	movwf	TMR0 
	bcf	intcon,2
	
;
;	setup for a byte before end of start bit
;
nxt1	decfsz	bitcnt,f	;last bit?
	goto	nxtbit		;no so next bit
	goto	nxtbyt		;yes so next byte
		
nxtbit	rlf	INDF,f		;roll the data
	skpc
	goto	zero		;wait for TMR0 then jump to 
				;a one or a zero

	movlw	.2		;one1
	goto	loop
;
;	bit is a zero
;
zero	movlw	.3		;zero1
	goto	loop
	
zero1	bsf	DCC_OUT		;output up 

z1	btfss	intcon,2  	;miss a count as it is a zero
	goto	z1
	movlw	0xED		;reset TMR0 
	movwf	TMR0		;value to make zero = 100usec.
	bcf	intcon,2

	movlw	.4 		;wait again then goto zero2
	goto	loop
	
zero2  	bcf	DCC_OUT		;output down (end of first half)
;
;	second half of a zero
;
    	
z2	btfss	intcon,2  	;miss a count
	goto	z2
	movlw	0xED		;reset TMR0
	movwf	TMR0
	bcf	intcon,2
	
z3	goto	nxt1
;
;	bit is a one	
;

one1	bsf	DCC_OUT		;output up
	movlw	.5       	;one2
	goto	loop 
;
;	second half of a one
; 
one2	bcf	DCC_OUT	   	;output down 
	goto	nxt1
;
;	get next byte
;
	
nxtbyt	incf	FSR,f
	movlw	.9
	movwf	bitcnt
	movlw	.6 		;nxtby1
	goto	loop
nxtby1	bsf	DCC_OUT		;output up
	decfsz	bytcnt,f	;last byte	
	goto	ploopa		;start bit of next byte

	return			;no more bytes in packet


;*************************************************************
;
;sends a passive preamble of 10 cycles
;arrive with output hi
;leaves with output hi  (ready for packet)


preamb	movlw	.14		;14 preamb cycles
	movwf	count4
	movlw	.7	     	;pr1
	goto	loop
pr1 	bcf	DCC_OUT		;output down
	decfsz	count4,f
	goto	pr2
	movlw	.8        	;pr3
	goto	loop
pr3	bsf	DCC_OUT	      	;output up
	return            	;end of preamble

pr2	movlw	.9       	;pr4
	goto	loop
pr4	bsf	DCC_OUT	       	;output up
	movlw	.7		;pr1
	goto	loop 

	end

