title ace3a.asm ;********************************************************************************************** ; 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 sequence 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. Only the first 6 bits of ; the accessory decoder address range are used. The top 3 bits are zero. ; 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. ; This program first sends a'deactivate' packet for the opposite of a pair, ; an intermediate idle and then an 'activate'. ; This gives a true bistable on / off action for 'tortoises' or lights ; but is equally suitable for CDU operated solenoids where the decoder ; 'on' time is short. (The deactivate is irrelevant here). ; ; ; 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. ; ; ; ; 27/05/02 Rewritten for 16F628 ; dccace2 working 28/05/02 ; 06/10/02 dccace3 is dccace2 with deactivate ; for tortoises, lights etc. ; 12/10/02 Tested and seems to work ; 27/01/03 Added comparator turnoff ; 10/01/06 Now renamed ace3a for 16F628A ; ;********************************************************************* processor 16F628A include "p16f628a.inc" __CONFIG h'3F61' ; PUTimer on, WDT off, XT clock ; 4 Mhz Crystal ; ; working registers CBLOCK 0x0020 temp ;temp spare ;spare colcnt ;column counter byte1 ;first bute of packet byte2 ;second byte of packet byte3 ;third byte of packet (for future use) err1 ;error byte bitcnt ;bit count in packet bytcnt ;in packet temp1 temp2 temp3 fcrtmp count0 ;number of packets count4 ;extra counter rowcnt ;row counter bitnum ;sets bit in row (shift register) adrcnt ;address counter byttmp ;temp for byte in deactivate adrtmp ;temp for address table0 ;table for row data (each column) table1 table2 table3 table4 table5 table6 table7 table8 table9 tableA tableB tableC tableD tableE tableF ENDC ;************************************************************************* ; org 0 goto setup ;reset vector ; org 04 ;interrupt vector retfie ;return from interrupt ; ;************************************************************************ ; ;start of program. Sets up ports etc. org 10 setup clrf PORTA bsf STATUS,RP0 ;set to page 1 movlw B'00000000' ;set up TMR0 (divide by 2) movwf OPTION_REG movlw B'11100000' ;set port a (pa4 is data out) ;pa0 column 0 ;pa1 column 1 ;pa2 column 2 ;pa3 column 3 ;pa4 DCC data out to decoders movwf TRISA movlw B'11111111' ;set up port b ;reads row data movwf TRISB bcf STATUS,RP0 ;back to page 0 movlw B'00000111' ; movwf CMCON ;turn comparators OFF movlw B'00010000' ;set port a movwf PORTA clrf colcnt ; ; end of setup movlw 0 ;goto init routine goto loop ; ;*************************************************************** ; ; main jump table main bcf INTCON,T0IF ;clear RTCC 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 rollu ;12 goto putd ;13 goto set2 ;14 goto h1 ;15 goto h2 ;16 ;****************************************************************** ; ; timing loop. Waits for RTCC, resets and jumps loop btfss INTCON,T0IF ;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 count0 ;send 10 reset packets call reset movlw .10 call idle ;send 10 idle packets clrf count0 ; init1 movlw table0 ;point to table addwf colcnt,W movwf FSR movf PORTB,W ;get row data movwf INDF ;put in table incf colcnt,W ;increment column andlw B'00001111' ;columns 0 to 15 movwf colcnt movf PORTA,W ;get port a andlw B'11110000' ;mask address iorwf colcnt,W ;get new column address movwf PORTA ;select new column comf INDF,F ;complement row data incf count0,F ;one packet call idle ;to give delay movf colcnt,F ;last column? btfss STATUS,Z goto init1 clrf adrcnt ;set address to zero ;*********************************************************** ; ; main read loop ; scan movlw table0 ;point to table addwf colcnt,W movwf FSR movf PORTB,W ;get row data movwf temp3 ;save movlw .10 ;scand goto loop ;wait scand bcf PORTA,4 ;output down movf temp3,W ;recover row data xorwf INDF,W ;compare with old data btfss STATUS,Z ;no change goto change scan1 movlw .11 ;scanu goto loop ;wait scanu bsf PORTA,4 ;output up incf colcnt,W ;increment column incf adrcnt,F ;address goes in twos incf adrcnt,F andlw B'00001111' ;columns 0 to 15 movwf colcnt movf PORTA,W ;get port a andlw B'11110000' ;mask address iorwf colcnt,W ;get new column address iorlw B'00010000' ;keep dcc output movwf PORTA ;select new column incf count0,F ;one packet call idle ;to give delay movf colcnt,F ;last column? btfss STATUS,Z goto scan clrf adrcnt ;reset address goto scan ; ; here if row data is different from table ; change clrf bitnum movwf temp1 ;difference data movwf temp2 addlw B'00001111' ;is it lower nibble btfss STATUS,DC ;yes goto higher bsf STATUS,Z ;low nibble call sort call set1 ;set up packet movlw .16 goto loop h2 bcf PORTA,4 ;output down ; check for higher nibble higher movlw B'11110000' ;any difference andwf temp1,W btfsc STATUS,Z ;yes goto scan1 bcf STATUS,Z ;hi nibble call sort swapf bitnum,F call set1 ;set up packet movlw .15 goto loop h1 bcf PORTA,4 ;output down goto scan1 ;loop again ;***************************************************** ; ; subroutines ; ; ;****************************************************** ; ; sends a complete reset packet ; number of packets in count0 ; reset movlw B'00000000' movwf byte1 movlw B'00000000' movwf byte2 movlw 3 movwf bytcnt call preamb call packet decfsz count0,F goto reset return ; ;****************************************************** ; ; sends a complete idle packet ; number of packets in count0 idle movlw B'11111111' movwf byte1 movlw B'00000000' movwf byte2 movlw 3 movwf bytcnt call preamb call packet decfsz count0,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,0 ;get no of bytes movwf temp decf temp,F decf temp,F movf INDF,0 ;get 1st byte incf FSR,F p1 xorwf INDF,0 ;work out error incf FSR,F decfsz temp,F goto p1 movwf INDF ;error byte movlw byte1 ;reset index movwf FSR ploopa btfss INTCON,T0IF ;wait for RTCC goto ploopa movlw 0xED ;sets zero for 100 usec. movwf TMR0 bcf INTCON,T0IF movlw .9 movwf bitcnt ;set bitcount (8 bits) movlw .1 ;wait for RTCC in main loop goto loop ;jump to p2 p2 bcf PORTA,4 ;output lo (first half of start bit complete) ; ; second half of start bit ; ploop2 btfss INTCON,T0IF goto ploop2 movlw 0xED ;reset TMR0 movwf TMR0 bcf INTCON,T0IF ; ; setup for a byte before end of start bit ; nxt1 decfsz bitcnt,1 ;last bit? goto nxtbit ;no so next bit goto nxtbyt ;yes so next byte nxtbit rlf INDF,1 ;roll the data btfss STATUS,C goto zero ;wait for RTCC 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 PORTA,4 ;output up z1 btfss INTCON,T0IF ;miss a count as it is a zero goto z1 movlw 0xED ;reset TMR0 movwf TMR0 ;value to make zero = 100usec. bcf INTCON,T0IF movlw .4 ;wait again then goto zero2 goto loop zero2 bcf PORTA,4 ;output down (end of first half) ; ; second half of a zero ; z2 btfss INTCON,T0IF ;miss a count goto z2 movlw 0xED ;reset TMR0 movwf TMR0 bcf INTCON,T0IF z3 goto nxt1 ; ; bit is a one ; one1 bsf PORTA,4 ;output up movlw .5 ;one2 goto loop ; ; second half of a one ; one2 bcf PORTA,4 ;output down goto nxt1 ; ; get next byte ; nxtbyt incf FSR,1 movlw .9 movwf bitcnt movlw .6 ;nxtby1 goto loop nxtby1 bsf PORTA,4 ;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 14 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 PORTA,4 ;output down decfsz count4,F goto pr2 movlw .8 ;pr3 goto loop pr3 bsf PORTA,4 ;output up return ;end of preamble pr2 movlw .9 ;pr4 goto loop pr4 bsf PORTA,4 ;output up movlw .7 ;pr1 goto loop ; ;********************************************************** ; ; sort out bit to change in accessory packet ; depending on change of matrix switch ; sort btfsc STATUS,Z ;arrives with w 0 or 1 goto lownib swapf temp1,F movf temp1,W movwf temp2 swapf temp3,F ;was high nibble movf adrcnt,W ;set address addlw 1 ;odd address movwf byte1 goto nibset lownib movf adrcnt,W ;set address movwf byte1 nibset movlw B'00001111' ;mask nibble andwf temp2,F ;get difference movlw 3 ;no of bits in nibble clrf count4 clrf bitnum incf bitnum,F ;starts with 1 movlw .12 goto loop ;wait rollu bsf PORTA,4 ;output up roll rrf temp2,F ;which bit btfsc STATUS,C goto putbit rlf bitnum,F incf count4,F incf count4,F goto roll putbit movlw .13 goto loop putd bcf PORTA,4 ;output down movlw table0 ;reset index addwf colcnt,W movwf FSR movf temp3,W ;get new row data andlw B'00001111' ;mask andwf bitnum,W ;what is new bit btfsc STATUS,Z ;is it a 1 goto azero movf count4,W addlw 1 ;odd number movwf byte2 bcf STATUS,Z return azero movf count4,W movwf byte2 bsf STATUS,Z return ; ;************************************************************ ; ; ; set1 sets new bit in table and then sends ; a deactivate packet to opposite of a pair, ; sends an intermediate idle and then an ; activate packet to the required output set1 btfsc STATUS,Z ;skip if data a zero goto chzero movf bitnum,W ;get bit to change iorwf INDF,F ;put in table goto setadr chzero comf bitnum,W ;get bit to change andwf INDF,F ;put in table setadr bsf byte1,7 bcf byte1,6 ;accessory address movf byte1,W movwf adrtmp ;save movlw B'11110000' iorwf byte2,W ;accessory deactivate movwf byttmp movlw .14 goto loop set2 bsf PORTA,4 ;output up movlw B'00000001' xorwf byttmp,W movwf byte2 call preamb movlw 3 movwf bytcnt call packet ;send deactivate previous movlw 1 movwf count0 ;one idle packet call idle movf adrtmp,W ;recover address movwf byte1 movf byttmp,W ;recover original activate movwf byte2 bsf byte2,3 ;activate call preamb movlw 3 movwf bytcnt call packet ;send change last return end