title ace_1b.asm LIST P = 16C84, R = HEX ;Prosessortype, radix INCLUDE "p16c84.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 PIC 15|-- XTAL ; Vss(GND)--|5 16C 14|-- Vdd (+5V) ; ROW 1 PORTB.0 ->|6 84 13|-> PORTB.7 ROW 8 (W) ; ROW 2 PORTB.1 <-|7 12|-> PORTB.6 ROW 7 (W) ; ROW 3 PORTB.2 <-|8 11|-> PORTB.5 ROW 6 ; ROW 4 PORTB.3 <-|9 10|-> PORTB.4 ROW 5 ; +---------+ ; ; +---+_+---+ ; ST -|1 24|- Vdd (+5V) ; D1 -|2 23|- Inh ; D2 -|3 22|- D4 ; S7 -|4 21|- D3 ; S6 -|5 20|- S10 ; S5 -|6 4515 19|- S11 ; S4 -|7 18|- S8 ; S3 -|8 17|- S9 ; S1 -|9 16|- S14 (W) ; S2 -|10 15|- S15 (W) ; S0 -|11 14|- S12 (W) ; Vss(Gnd) -|12 13|- S13 (W) ; +---------+ ; ;********************************************************************************************** ; 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!) ;********************************************************************************************** ; ; 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- ; ... ; ; Also added a possibility to change the address window, or base address ; of the decoders the system is to operate. the system will only operate 15 ; decoders, the controls of decoder 16 are controlling the window. ; these buttons are replaced with jumpers, and at least one of them has ; to be in place to get other than idle packets out! ; Jumper positions correspond to R1:C1 decoder (DEC) or R1:C1 turnout (W): ; ; COL13 COL14 COL15 COL16 ; pin14 pin13 pin16 pin15 pin of 4515 ; ------ ----- ----- ----- ----- ; ROW 7: DEC 1 DEC 8 DEC16 DEC24 ; pin12 (W1) (W33) (W65) (W97) ; ; ROW 8: DEC32 DEC40 DEC48 DEC56 ; pin13 (W129) (W161) (W193) (W225) ; ; pin of ; PIC ; --------------------------------------------------------------------- ; ; 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 ; ;********************************************************************* ; ; Registers used predefined ; ; working registers CBLOCK H'0C' 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 win_tmp ;used for calculating the address window window ;the turnout address window's origo row in_use ;is the panel in use (non zero=yes) pktcnt ;number of packets pbitcnt ;preamble bit count rowcnt ;row counter bitnum ;sets bit in row (shift register) adrcnt ;address counter k_byte1 ;killer_byte1 k_byte2 ;killer_byte2 k_row ;killer_row x_row ;tmporary row data table_1 ;The table! table_2 ; table_3 ; table_4 ; table_5 ; table_6 ; table_7 ; table_8 ; table_9 ; table_A ; table_B ; table_C ; table_D ; table_E ; table_F ; ENDC #DEFINE DCC_OUT porta,4 ;DCC signal out #DEFINE PRE_BITS .18 ;Number of preamble bits ;************************************************************************* ; 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 ; movlw b'00010000' ;dcc_out set, row=0 movwf porta ;set out bsf STATUS, RP0 ; movlw b'00001000' ;PullUp=on, int. clk, PreScaler on TMR0=1:1 movwf OPTION_REG ; bcf STATUS, RP0 ; clrf colcnt ; clrf window ; clrf win_tmp ; clrf row ; movlw PRE_BITS ;we'll need predefined movwf pbitcnt ;amount of preamblebits ; end of setup goto init ; ;---------------------------------------------------------------- ; SYNC: Waits for TMR0, resets, loads new seed and returns ; Set DCC_OUT in calling code ;SYNC_1 for ones, SYNC_0 for zeros! ;---------------------------------------------------------------- sync_1 btfss intcon, T0IF ;wait loop goto $-1 btfss TMR0,2 ;Jitter routine from goto $+1 ;Bob Ammerman [Piclist] btfsc TMR0,0 ; goto $+1 ; bcf intcon, T0IF ;clear interrupt flag movlw 0xD2 ; movwf TMR0 return sync_0 btfss intcon, T0IF ;wait loop goto $-1 btfss TMR0,2 ;Jitter routine from goto $+1 ;Bob Ammerman [Piclist] btfsc TMR0,0 ; goto $+1 ; bcf intcon, T0IF movlw 0xE2 ;reset TMR0 movwf TMR0 return ;---------------------------------------------------------- ;The turnout (or decoder!) base address is picked from here ;---------------------------------------------------------- getbase addwf pcl,f ;and jump to return value retlw .0 ;0: retlw .8 ;1: retlw .16 ;2: retlw .24 ;3: retlw .32 ;4: retlw .40 ;5: retlw .48 ;6: retlw .56 ;7: retlw .64 ;8: (it should never get this far ;) ;****************************************************************** ; ; Initialise sequence ; [This is original Bolton code!] ;---------------------------------------------------------------- init movlw .10 movwf pktcnt ;send 10 reset packets call reset movlw .10 movwf pktcnt ;send 10 reset packets call idle clrf row ; clrf colcnt ; clrf rowcnt ; clrf in_use ; clrf win_tmp ; clrf window ; scan ;------------------------------------------------------- ; Here is the beginning of operations for each column ; entrance DCC HIGH! ;------------------------------------------------------- call sync_1 ;get sync (58 us) and then bcf DCC_OUT ;output DOWN! decf pbitcnt, f ;decrement preamble bit count at ;falling edge of preamble byte comf portb, w ;get row data movwf row ;save in row ;---------------------------------------------------------- ; For shifting the decoder base address (window) ; we have to check the buttons/switches C12..15:R6..7! ; These will be handled here and cleared so as ; to prevent the change-routine from halting ; to these in case they are set. Win_tmp is used for ; searching the new base decoder number (returned in window ; and added to base=0 decoder address) and also as ; temp flag to set/clear in_use at end of col scan! ;---------------------------------------------------------- comf colcnt, w ;get the column number and andlw b'00001100' ;check if we are within skpz ;window columns (i.e Col:0..3) goto scan2 ;nope -- continue to scan2 ;----------------------------------------------------- ; Now in possible column for base address shifting bits ;----------------------------------------------------- movfw row ;and now test the row for andlw b'11000000' ;any window bits? skpnz ;are bits 6 or 7 set? goto scan2 ;no, continue as normal ;Now we know that either bit 6 or 7 ;is set (only one "allowed") so it's ;enough to test only the other ;----------------------------------------------------- ; Found base address shifting bits ;----------------------------------------------------- movfw colcnt ;yes, get the col again andlw b'00000011' ;get the low bits only movwf win_tmp ;and save it btfsc row,7 ;We'll test bit 7 bsf win_tmp, 2 ;if set we'll set w_lookup bit 2 ; bcf row, 6 ;and clear both possible window bits ; bcf row, 7 ;from row incf win_tmp, f ;win_tmp is used also as flag ;to tell that window was changed ;we add one as it is possible that we found ;jumper at C1,R1, i.e. zero! scan2 call sync_1 bsf DCC_OUT ;Output UP! ;----------------------------------------------------- ; Test if panel in use ;----------------------------------------------------- tstf in_use ;Check if the local panel is in use skpnz ;if 0 then skip row interpretations goto scan4 ;and go reading other columns ;----------------------------------------------------- ; Here if panel in use, lets test for buttons! ;----------------------------------------------------- movlw table_1 ;get pointer to tables 1st item addwf colcnt, w ;add column number to this movwf FSR ;and point INDF to this table item movfw INDF ;get the old row data (from table_n) movwf x_row ;copy to safety movfw row ;and replace table data movwf INDF ;with new data (to table_n) and xorwf x_row, w ;compaire it with the previous read andwf row, f ;and check if new buttons or switches down. ;So: x_row had old value copied from table and ;that is now xored with new value (to get ;changed bits) and this is anded with new value ;to find out which changed bits have moved ;from 0 -> 1. "row" has now these bits set! skpz ;any new buttons down (note: window bits not cleared!)? goto change ;yes, out with it ;----------------------------------------------------- ; Here if panel in use, but column has no buttons down ;----------------------------------------------------- scan4 call sync_1 ;syncronize to 58us bcf DCC_OUT ;output DOWN! decf pbitcnt, f ;decrement preamble bit count at ;falling edge of preamble byte ;----------------------------------------------------- ; Now at the end of this column, get to next column ;----------------------------------------------------- incf colcnt, w ;increment column andlw B'00001111' ;clear the top nibble movwf colcnt ;to prevent going over the top! skpz ;are we at the end of cols (last column)? goto scan6 ;no, skip the end routine ;------------------------------------------------------------------- ; Here if this is the last column! ;------------------------------------------------------------------- clrf in_use ;if no window jumpers: not in use! tstf win_tmp ;test the temp-file, in case we skpnz ;have found a window jumper? goto scan6 ;no, the panel is not in use ;----------------------------------------------------- ; If win_tmp is non zero then the panel is in use! ;----------------------------------------------------- incf in_use, f ;yes, we are in business decf win_tmp, f ;decrement to get rid of the extra set for label! movfw win_tmp ;get the w_lookup call getbase ;make the lookup to find out window base movwf window ;and move it in action! incf window, f ;add one to get LENZ addressing; ;W. LENZ the decoder numbers start from 1! clrf win_tmp ;clear the win_tmp as it is used as a flag ;during next round scan6 ;------------------------------------------------------------------- ; Here after all the column stuff is completed! ;------------------------------------------------------------------- ; movfw 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 movfw colcnt ;get column count movwf porta ;and put it out ;(DCC signal is always down here!) call sync_1 ;syncronize to 58 us bsf DCC_OUT ;output UP! incf pktcnt,f ;one packet call idle ;to give delay goto scan ;return to beginning of column scanning ;with output HI ;============================================================ ; END OF MAIN SCAN LOOP ;============================================================ change ;------------------------------------------------------- ; Here if new switches were closed ; Will send all newly found switches, so all bits ; may be set and must be sent before returning to scan! ;------------------------------------------------------- ; 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 ;------------------------------------------------------- ;Enter with output UP! ;------------------------------------------------------- movlw .8 ;8 rows to search, movwf x_row ;x_row now row's bit counter incf x_row, f ; clrf rowcnt ;row's bit pos denotes decf rowcnt, f ;rowcnt value comf colcnt, w ;get the column number and andlw b'00001100' ;check if we are within skpz ;window columns (i.e Col:0..3) goto c_loop ;nope -- continue to scan2 decfsz x_row, f ;Yep! decrement x_row so that the decfsz x_row, f ;window setting bits won't be noticed c_loop incf rowcnt, f ;value converted to binary w. decfsz x_row, f ;test if all rotated out goto c0 goto scan4 ;return to scan more columns c0 rrf row, f ;rotate right throuh carry skpc ;if carry is not found goto c_loop ;we'll rotate more ;--------------------------------------------------- ; Create accessory decoder packet data ;--------------------------------------------------- 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 call sync_1 ;need to syncronize again bcf DCC_OUT ;output DOWN decf pbitcnt, f ;decrement preamble bit count at ;falling edge of preamble byte 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 btfss rowcnt, 0 ;Test row bit 0 (X) [inverted for SMJ] bsf byte2, 0 ;yes, set X ;------------------------------------------------------ ; Now, as the window is added there is a possibility of ; messing the 2 MSB markers of byte1: if they are not ; '10' we'll fix em and adjust the byte2 inverted ; address bits accordingly ;------------------------------------------------------ movfw window ;get the window addwf byte1, f ;and add it to byte1 btfss byte1, 6 ;check if address gone over 63 goto c2 ;no, continue bcf byte1, 6 ;yes, clear "wrong" bit bcf byte2, 4 ;and set (clear) correct bit c2 btfsc byte1, 7 ;check if addres overrun in byte1 goto c3 ;no, continue bsf byte1, 7 ;yes, correct by settng bit 7 bcf byte2, 5 ;and set (clear) correct bit c3 call sync_1 ;syncronize again bsf DCC_OUT ;output UP! movfw byte1 ;--------------------------- movwf k_byte1 ;make backups of byte1 and 2 movfw byte2 ;to create the killer packet movwf k_byte2 ;Data bit = byte2, bit 3 movlw 3 ;total 3 bytes (E inclusive) movwf bytcnt ;to bytecount ;Recall: enter preamb w. output UP! call preamb ;send preamble call packet ;send the packet proper movlw 3 ;and then send 2 movwf pktcnt ;idle packets to give call idle ;time for decoder to operate ;---------------------------------------------------------- ; We'll send second activating packet just to make sure ;---------------------------------------------------------- movfw k_byte1 ;yes still down! Let's send more movwf byte1 ;of those turnout activating movfw k_byte2 ;commands by using the movwf byte2 ;copies of previously calculated movlw 3 ;byte1 and byte 2 copies! movwf bytcnt ;set the bytecount (E inclusive) call preamb ;send preamble call packet ;and send the packet proper! movlw 3 ;and then send 3 movwf pktcnt ;idle packets to give call idle ;time for decoder to operate ;------------------------------------------------------------------- ; Sending killer! ;------------------------------------------------------------------- bcf k_byte2, 3 ;modify the backup to create killer! movfw k_byte1 ;take the backups in use movwf byte1 ;for the packet sending movfw k_byte2 ;routine movwf byte2 ;and send the killer_packet movlw 3 ;total 3 bytes (E inclusive) movwf bytcnt ;to bytecount call preamb ;send preamble call packet ;send the killer packet proper movlw 3 ;and then send 3 movwf pktcnt ;idle packets to give call idle ;time for decoder to operate ;------------------------------------------------------------------- ; Sending second killer! ;------------------------------------------------------------------- movfw k_byte1 ;take the backups in use movwf byte1 ;for the packet sending movfw k_byte2 ;routine movwf byte2 ;and send the killer_packet movlw 3 ;total 3 bytes (E inclusive) movwf bytcnt ;to bytecount call preamb ;send preamble call packet ;send the killer packet proper ;------------------------------------------------------ movlw 3 ;and then send 2 idle packets movwf pktcnt ;to give the decoder time to digest call idle ;the new situation. goto c_loop ;and start looping again ;NOTE: We will re-enter to the same column! ;***************************************************** ; ; subroutines ; ; [Mainly original Bolton code!] ;****************************************************** ; ; 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 ; When arriving here, TMR0 has seed for "one"! ; ; preamble>|sep >| ; +---+ +---+--+ +--- ; | | | | | ... ; | +---+ +--+---+ ; ; sync1 1 0 0 1 n ; ; At sep.bit (0) the original seed is consumed ; and then extra sync with special seed is run ; before changing output. ; ; At change of output the seed is always that of "one" ; ; The same idea is used throughout the packet. ;----------------------------------------------------------------- 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 call sync_0 ;lets consume the "one" ;and insert seed for "zero" movlw .9 movwf bitcnt ;set bitcount (8 bits) call sync_1 ;and consume the "zero" ;and start with seed for "one" bcf DCC_OUT ;output lo (first half of start bit complete) ; ; second half of start bit ; ploop2 call sync_0 ;consume the "one" and insert ;seed for "zero" ; ; setup for a byte before end of start bit ; ;----------------------------------------------------------- ; BIT ROUTINES: enter with output LOW and with unknown seed ;----------------------------------------------------------- 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 ;----------------------------------------------------------- ;BIT IS ONE ;----------------------------------------------------------- call sync_1 ;sync to "one" bsf DCC_OUT call sync_1 ;sync to "one" bcf DCC_OUT goto nxt1 ;----------------------------------------------------------- ; BIT IS ZERO ;----------------------------------------------------------- zero call sync_1 ;consume "zero" and seed to "one" bsf DCC_OUT ;output HI call sync_0 ;consume "one" and ;insert seed for "zero" call sync_1 ;consume "zero" load "one" bcf DCC_OUT ;output LO call sync_0 ; goto nxt1 ; ; get next byte ; nxtbyt incf FSR,f movlw .9 movwf bitcnt call sync_1 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 14 cycles ;arrive with output hi ;leaves with output hi (ready for packet) preamb call sync_1 ;syncronize to 58 us pr1 bcf DCC_OUT ;output down decfsz pbitcnt,f goto pr2 call sync_1 ;syncronize again bsf DCC_OUT ;output up movlw PRE_BITS ;get the pre-defined number of movwf pbitcnt ;preamble bits as seed return ;end of preamble pr2 call sync_1 ;syncronize again to 58us bsf DCC_OUT ;output up goto preamb ;and again to beginning of preamb routine end