processor 16F874A title "bc1a_2.asm Issue 1 Rev c 1/04/02" ;********************************************************************************************** ; ; bc1a is a rewritten version of dcc8g to use a 16F874 or 16F877 ; This gives essentially a 'single chip' command station with ; integrated booster ; ; Clock speed now 16MHz (plenty of time for expansion) ; Handset voltages converted by on chip ADC ; No external serial data MUX needed ; Chip has USART available for computer linking (not implemented yet) ; and RS485 interface for X bus etc. (no software) ; Loads of extra memory for expansion / future upgrades ; ; ; This program is for a NMRA standard DCC 'Command Station' ; Written by Mike Bolton, (May 1999 - initial veresion) ; 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 and PCB layout are also freely available from the author ; email bolton@fs1.with.man.ac.uk) ; ; The objective of this command station was to duplicate the ease and ; operability of a conventional analog system while maintaining the ; advantages of DCC. Keeping the cost low and using standard parts were ; also criteria. The system uses up to eight handsets giving a maximum of ; eight locos at the same time. Each handset has a potentiometer for speed control ; two, low cost thumbwheel switches for loco address selection (00 to 99), an on/off ; switch for direction, four function pushbuttons (simple normally open PBs), an ; optional handset deactivate switch (same as unplugging the handset), and ; two jumpers (or switches) to select the speed mode. Again optional if only one ; speed mode is required. Switching off a handset (or unplugging it) sends an emergency ; stop packet to its loco. ; ; Speed modes are 14 step, 28 step or 128 step. In 28 step mode, ; intermediate steps are filled in by adding a step on a 1 in 2 packet ; basis to create effectively 56 step control. This intermediate step mode can be selected ; by a jumper on PORTD,2. If D2 is lo, mode is standard 28 step. If hi (tied to D3), then ; the intermediate step mode is selected. (there are jumper pads on the PCB for this) ; Not all 28 step mode decoders were happy with the alternating packets, notably the Lenz ; LE130 feedback decoder. In 14 step mode, bit 4 controls F0(FL). ; ; Different handsets can be in different modes and modes can be changed whilst running. ; ; There is an address 00 analog function available on any one handset. This allows ; control of a conventional (non DCC) loco by the 'stretched zeros' method. Not ; advised for ironless rotor motors (Escap, Faulhaber, Minimotor etc). In analog mode, the ; waveform is similar to a conventional PWM controller with a PRF of about 40Hz. This gives ; excellent slow speed control but is a bit noisy. ; ; The hardware is based on a PIC 16F874 microcontroller chip with 8 channel ; 8 bit ADC. The speed information is sampled from the potentiometer voltage on each ; handset by the ADC. Function and address information is read serially from the handsets ; using a clock signal from the PIC. A 5 wire connection to the handsets is required. ; ; The program continually scans all handsets for information in rotation. The ; handset info. is read and 'parsed' during the packet preamble. If there is no ; handset on that channel or the address is 00, then an idle packet is sent. Function ; packets are sent every 4th speed packet. ; ; On startup, all handsets are deactivated until or unless the speed control is at ; zero. This avoids locos starting unexpectedly. Also, when a new address is dialled ; on the thumbwheels, it will not become valid until the speed is turned to zero. ; The 'old' loco remains under control until stopped, then the new one will start if ; the address is available. If not, there is an audible alarm. To change locos, select ; address of new loco. Turn speed to zero. Current loco stops and new loco is selected ; if not in use otherwise alarm sounds, new loco is not selected and old one deselected. ; ; System allows for upto 4 consists, each of upto 4 locos. Advanced consisting is ; not used. The primary address of each loco in a consist is stored in EEPROM. ; If a consist address is selected (90 to 93), the speed and direction info. is sent ; in successive packets to each loco in that consist. It is possible to have the same ; loco in more than one consist. Once in a consist, function data but not speed data ; can be sent to individual locos. Function data cannot be sent to all locos ; in a consist from the 'consist' handset. ; ; A consist is selected as if it was an individual loco by dialling in the consist ; address and turning the speed to zero. It is possible to use addresses 90 to 93 ; for individual locos but only those addresses can be used for consists. ; ; To set a loco into a consist, that loco must be 'active'. Hold in the consist button ; and then press a function button and release. Then release the consist button. ; To 'de-consist' repeat the process. The addresses of the locos in each consist are ; stored in EEPROM so will be remembered when the system is powered off. F0 sets the ; loco on that handset to consist 90, F1 to 91 etc. If you have forgotten which locos ; are in a consist, you can clear it by activating address 00 and putting this into the ; consist. It will clear one loco at a time on a first in, first out basis. To completely ; clear a consist, repeat with 00 four times. You cannot have an analog (00) loco in a ; consist. ; ; The function buttons have a toggle action. Press for on, press again for off. ; ; Zero stretching for analog control has 64 step resolution. A lookup table is used ; to create a set of 16 straight line approximations for the non-linear curve. ; This gives approximate correspondence between the speed setting and the mean DC level. ; ; The system output is a 5v logic level DCC bit stream. ; The 16F874 provides anti phase drives for booster MOSFETs with a variable delay between ; switching. An input senses overload and shuts down the drives. There is a timed retry ; and an audible alarm. ; ; Summary ; ; 8 channel 'command station' ; 8 channel 8 bit ADC ; 17 bit serial data input per channel ; 14, 28 and 128 speed steps ; 8 bit BCD address select (00 to 99 used) ; 4 function pushbuttons ; 1 consist set pushbutton ; 1 switch for direction ; 2 mode switches (4 modes) ; analog speed on address zero ; integrated booster drive ; ; ; Filename bc1a_2.asm uses 16F874/20 ; ; Date 3/12/00 ; Working with bc1a pcb. No serial interfaces yet ; 14/6/01 Mods for overload delay ; 01/04/02 Mod to remove speed step 1 ; ; ; Program methodology. ; ; ; All time intervals are derived by polling the real time clock (RTCC) ; overflow flag. No interrupts or timing loops are used. This gives simple ; but precise timing control and allows changes and mods. to be made without ; affecting the timing. This method entails breaking the program up into sections ; which take a relatively short time to execute - less than 58 microsecs. ; At the end of each section, the w register is loaded with a jump value pointing ; to the next section to be executed. The RTCC is polled for overflow, reset and a ; programmed jump is executed via the main 'goto' table. The exception to this ; is in the zero and stretched zero bits where the polling is internal to the ; routine and does not use the main jump table. The alarm sounds if an address is selected ; on one handset that is is use by another and the speed is turned to the ; zero position. Turning the speed off zero will silence the alarm as will selecting a ; valid address. Each bit in 'alarm'corresponds to one channel. The bit is set or cleared ; depending on the alarm state for that handset. Any bit set will sound the alarm. ; ; ; Programming ; ; HS on ; WDT off ; RST on ; debug disabled ; __CONFIG h'3F72' ; PUTimer on, WDT off, HS clock, no debug ; ;********************************************************************************************** ; ; Registers used predefined ; include "P16F874A.inc" ;registers file CBLOCK 0x0020 ;start of data block in RAM ; ; working registers ; fcount ;function counter conbit ;consist bit register adrtmp ;temp for address temp ;general temp reg byte1 ;first byte of packet byte2 ;second byte of packet byte3 ;third byte of packet (error on 3 byte packets) err1 ;error byte count0 ;counter 0 (number of packets) count1 ;counter 1 bytcnt ;byte counter in packet bitcnt ;bit counter in a byte count4 ;counter 4 alarm ;flag for alarm temp2 ;another temp chcnt ;channel counter zcount ;zeros counter adcset ;setup data for adc adcdat ;data from ADC (7 bit) chdata ;channel data (function and mode) curadr ;current loco address curfun ;current functions funflgs ;flag for function change - not used count ;local counter tcount ;count in adc setup / read FSRtmp ;temp for FSR consis ;flag tor consist setup mask ;used in consist direction loopt ;special temp in loop routine strch ;temp for stretch value div ;used in stretch used ;non zero if address in use ftoggl ;toggle for functions chadr ;channel address from handset temp1 ;another temp alarmc ;alarm counter (shift register) locos ;loco addresses against channel loco1a loco2a loco3a loco4a loco5a loco6a loco7a functs ;functions against channel func1a func2a func3a func4a func5a func6a func7a cons00 ;first loco in consist 90 cons01 cons02 cons03 cons10 ;first loco in consist 91 cons11 cons12 cons13 cons20 ;first loco in consist 92 cons21 cons22 cons23 cons30 ;first loco in consist 93 cons31 cons32 cons33 ENDC ; bits used predefined zbit equ 2 ;STATUS zero bit carry equ 0 ;carry bit ;RP0 equ 5 ;page bit rd equ 0 ;read bit for eeprom wr equ 1 ;write bit for eeprom wren equ 2 ;write enable f equ 1 w equ 0 dcc equ 7 ;dcc output bit alrm equ 1 ;alarm output clk equ 6 ;handset clock ph1 equ 2 ;H bridge phase 1 ph2 equ 3 ;H bridge phase 2 overld equ 4 ;overcurrent sense input alrovr equ 0 ;overload alarm ; ; ; end of definitions ;************************************************************************** ; ; start of program ; ;************************************************************************* ; org 0 goto setup ;reset vector org 4 goto setup ;in case of interrupt ; ; ;************************************************************************ ; ; Sets up ports etc. setup bsf STATUS,RP0 ;set to page 1 movlw B'00000001' ;set up TMR0 (divide by 4) movwf OPTION_REG movlw B'00111111' ;set port a ;low 5 lines of A/D (0 to 3) + O/L detect + A/D 4 ;sequence of channels is reversed ;bit 0 is input 8 etc movwf TRISA movlw B'0000111' ;set up port e ;high 3 lines of A/D (5 to 7) movwf TRISE movlw B'11111111' ;set port b to inputs movwf TRISB ;handset data inputs movlw B'10100000' ;set port c movwf TRISC ;bit 0,1 alarms ;bit 2,3 H bridge drivers ;bit 4 CTS out ;bit 5 RTS in ;bit 6 TX dat ;bit 7 RX dat movlw B'00001100' ;set port D ;bit 0 not used ;bit 1 not used ;bit 2 and 3 mode select ;bit 4 not RXE (RS485) ;bit 5 TXE (RS485) ;bit 6 HS clock ;bit 7 DCC out movwf TRISD clrf ADCON1 ;set ADC control bcf STATUS,RP0 ;back to page 0 movlw B'00000000' ;drives off (modify later) movwf PORTC movlw B'10011011' ;set port d (d7 is DCC out) ;d6 is serial clock ; movwf PORTD movlw B'00100001' ;timer 1 setup, prescale 1/4 movwf T1CON clrf chcnt movlw .16 ;clear tables movwf count0 movlw locos movwf FSR clear clrf INDF incf FSR,f decfsz count0,f goto clear movlw .16 ;read consist table movwf count0 movlw cons00 ;start of consist table movwf FSR clrf temp1 ;start of eeprom ldcon movf temp1,w bsf STATUS,RP1 ;page 2 movwf EEADR call eeread ;get consist info from eeprom movwf INDF ;put in RAM stack incf FSR,f incf temp1,f decfsz count0,f goto ldcon call brdown ;set output to zero clrf alarmc ;counter for alarm clrf alarm ;alarm bit register clrf consis ;consist setup flag clrf fcount ;counter for function packets clrf conbit ;bits show which handsets clrf strch ;stretch value movlw .29 ;goto initialise ; ; end of setup ; ; main jump table main bcf INTCON,2 ;clear RTCC flag main1 addwf PCL,f ; ; start of main goto table ; goto p2 ;0 packet routine goto one1 ;1 goto zero1 ;2 goto zero2 ;3 goto one2 ;4 goto nxtby1 ;5 goto setup ;6 dummy goto setup ;7 goto setup ;8 goto adcclk ;9 in ADC setup goto adcck2 ;10 goto adcck3 ;11 goto adc2 ;12 goto chr2 ;13 goto bcdout ;14 finish getting channel data goto nchng1 ;15 at end of function setup goto chrex2 ;16 goto nexld1 ;17 in conchk goto lid2 ;18 idle 2 half cycles goto lid1 ;19 idle 3 half cycles goto spdo1 ;20 before speed packet goto not0a ;21 part of loco goto chngz ;22 part of loco goto chngy ;23 part of loco goto nexlu1 ;24 in conchk goto inv1 ;25 inval first goto inv2 ;26 inval second cycle goto loc1 ;27 extra in loco goto noz ;28 nozero goto init ;29 initialise goto init1 ;30 second part of initialise goto kill1 ;31 kill old loco goto kill2 ;32 next stage of kill goto nexld2 ;33 goto nexlu2 ;34 goto setc1 ;35 in consist setup goto setc2 ;36 in consist setup ; ;****************************************************************** ; ; consist button convert ; conbut addwf PCL,f ;add button no. to pc retlw 0 ;0 is invalid combination retlw 1 ;returns with address +1 retlw 5 retlw 0 retlw 9 retlw 0 retlw 0 retlw 0 retlw .13 ; ; timing loop. Waits for RTCC, resets and jumps loop btfss INTCON,2 ;wait loop goto loop movwf loopt ;save w movlw 0xC8 ;reset TMR0. Adjust value for one bit period movwf TMR0 movf loopt,w ;recover w goto main ; ;****************************************************************** ; ; initialisation on startup ; init bsf PORTD,dcc ;output hi movlw B'00000111' ;set initial channel count movwf chcnt movlw 0xC8 ;timing value for 58 usec. movwf TMR0 movlw .30 ;init1 goto loop init1 call brup ;output hi movlw .20 movwf count0 ;20 reset packets call reset movlw .20 movwf count0 ;20 idle packets call idle ;******************************************************************* ; ; main continuous scan and send routine ; ; scan bcf INTCON,GIE ;just in case movf alarm,f ;test alarm btfsc STATUS,zbit goto alarm1 bsf PORTC,alrm ;alarm on goto scan1 alarm1 bcf PORTC,alrm ;alarm off scan1 incf chcnt,w ;next channel andlw B'00000111' ;eight channels only movwf chcnt call rolbit ;sets rolling bit for channel call adcgo ;set up adc call adcrd ;read adc call chread ;get channel data call setfun ;sets up functions call loco ;check for valid loco or consist ;and send packet(s) main3 movf chcnt,w ;send function packet andlw B'00000011' ;every 4th channel btfsc STATUS,zbit call sndchg ;send function main2 goto scan ;****************************************************** ; ; subroutines ; ;****************************************************** ; ; sends a complete reset packet ; reset clrf byte1 clrf byte2 movlw 3 movwf bytcnt call preamb call packet decfsz count0,f goto reset return ; ; sends a complete idle packet ; idle movlw .10 call lidle1 ;idle packet with preamble 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 and handles any stretches ; must arrive with the DCC output high ; ; packet movlw 1 ;add preamble call addpre 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 INDF movwf FSR ploop btfss INTCON,2 ;wait for RTCC goto ploop movlw 0xD8 ;sets non stretched zero for 100 usec. * movwf TMR0 bcf INTCON,2 incf zcount,f ;increment zeros counter btfss strch,7 ;check direction of DC call strtch ;nonlinear stretch of first half movlw .9 movwf bitcnt ;set bitcount (8 bits) movlw 0 ;wait for RTCC in main loop goto loop ;jump to p2 p2 call brdown ;output lo (first half of start bit complete) ; ; second half of start bit ; ploop2 btfss INTCON,2 goto ploop2 movlw 0xD7 ;reset TMR0 * movwf TMR0 bcf INTCON,2 btfsc strch,7 ;check direction of DC call strtch ; ; setup for a byte ; 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 btfss STATUS,carry goto zero ;wait for RTCC then jump to ;a one or a zero movlw 1 ;one1 goto loop ; ; bit is a zero ; zero movlw 2 ;zero1 goto loop zero1 call brup ;output up z1 btfss INTCON,2 ;miss a count as it is a zero goto z1 movlw 0xD7 ;reset TMR0 * movwf TMR0 ;value to make zero = 100usec. bcf INTCON,2 incf zcount,f ;increment zeros counter btfss strch,7 ;check direction of DC call strtch movlw 3 ;wait again then goto zero2 goto loop zero2 call brdown ;output down (end of first half) ; ; second half of a zero ; z2 btfss INTCON,2 ;miss a count goto z2 movlw 0xD7 ;reset TMR0 * movwf TMR0 bcf INTCON,2 btfsc strch,7 ;check direction of DC call strtch z3 goto nxt1 ; ; bit is a one ; one1 call brup ;output up movlw 4 ;one2 goto loop ; ; second half of a one ; one2 call brdown ;output down goto nxt1 ; ; get next byte ; nxtbyt incf FSR,f movlw .9 movwf bitcnt movlw 5 ;nxtby1 goto loop nxtby1 call brup ;output up decfsz bytcnt,f ;last byte goto ploop ;start bit of next byte return ;no more bytes in packet ;************************************************************ ; ; nonlinear stretch routine ; uses lookup table in eeprom to give 16 linear ; segment approximation for stretch curve ; also for each speed step (0 to 127) it stretches ; progressively more zeros. ; If stretch value is zero then no stretch ; Note. Bit 7 of strch is the DC direction bit. ; strtch btfsc zcount,6 ;if counter = 64 then reset clrf zcount movf strch,w ;get DC speed value andlw B'01111111' ;mask direction subwf zcount,w ;stretch this zero? btfsc STATUS,carry goto strend movf zcount,w ;get cycle number movwf div ;ready to divide by 4 rrf div,f rrf div,w andlw B'00001111' ;mask any carries addlw .48 ;table starts at 48 bsf STATUS,RP1 ;page 2 movwf EEADR call eeread movwf div ;use div again as loop counter loopy call dely ;delay loop decfsz div,f goto loopy movlw 0xF5 ;reset TMR0 * movwf TMR0 bcf INTCON,2 strend return dely movlw .110 ;seems OK movwf temp dely1 decfsz temp,f goto dely1 return ;************************************************************ ; ; MOSFET bridge switching routines ; Sets delay between phases ; Generates logic DCC output ; Uses timer 1 for overload delay ; set at 64K usec for now. (65.5 millisec) brup call over ;test for overload bcf PORTC,ph2 ;phase 2 off bsf PORTD,dcc ;dcc output up nop ;delay for switching nop ;250ns per nop nop nop nop nop nop nop ;nop ;nop bsf PORTC,ph1 ;phase 1 on return brdown call over ;test for overload bcf PORTC,ph1 ;phase 1 off bcf PORTD,dcc ;dcc output down nop ;delay for switching nop ;250ns per nop nop nop nop nop nop nop ;nop ;nop bsf PORTC,ph2 ;phase 2 on return over btfss PORTA,overld ;overload is low goto over1 clrf TMR1H ;reset overload timer clrf TMR1L return over1 btfsc PIR1,0 ;time out? goto overtm return overtm bcf PORTC,ph1 ;turn off bridge bcf PORTC,ph2 bsf PORTC,alrovr ;alarm on clrf temp clrf temp1 over3 call dely1 ;delay call dely1 call dely1 call dely1 call dely1 call dely1 call dely1 call dely1 call dely1 call dely1 decfsz temp1,f goto over3 bcf PORTC,alrovr ;alarm off bcf PIR1,0 ;clear timer flag over2 return ;try again ;************************************************************* ; ;sends a passive preamble of 10 cycles ;arrive with output hi ;leaves with output hi (ready for packet) preamb movlw .10 ;10 preamb cycles call addpre return ;************************************************************** ; ; set up ADC and start conversion ; ; ; adcgo movlw 9 goto loop adcclk call brdown ;output lo movf chcnt,w ;get channel count sublw 7 ;reverse sequence movwf adcset rlf adcset,f rlf adcset,f rlf adcset,w andlw B'00111000' ;mask unwanted bits iorlw B'10000001' ;set up for ADC movwf ADCON0 ;set channel for sample movlw .10 goto loop adcck2 call brup ;output up bsf ADCON0,2 ;start A to D movlw .11 goto loop ;allow for AD conversion adcck3 call brdown ;output down return ; ; get data from ADC ; adcrd btfsc ADCON0,2 ;conversion complete? goto adcrd movf ADRESH,w ;get answer movwf adcdat rrf adcdat,f ;7 bits only bcf adcdat,7 movlw 1 ;check for speed 1 subwf adcdat,w movlw 2 btfsc STATUS,zbit movwf adcdat movlw .12 goto loop adc2 call brup ;output up return ;ADC read complete ; ; read channel settings. two data bits for each half of ; preamble cycle ; chread clrf temp1 ;temp store for data byte movlw 2 movwf bytcnt ;two bytes (16 bits of data) chrr1 movlw 2 movwf count0 chrr movlw 4 movwf count4 ;bits per half output chrd2 bsf PORTD,clk ;clock down nop nop ;delay here for 16MHz clock nop call pad25 movf alarmc,w ;rolling bit andwf PORTB,w ;read data btfsc STATUS,zbit ;bit hi or lo goto dat0 bsf STATUS,carry goto roldat dat0 bcf STATUS,carry roldat bcf PORTD,clk ;clock up nop nop call pad30 ;more delay rlf temp1,f ;bit 7 = no handset if 1 ;bit 5 and 6 = 0 mode 0 ;bit 6 = 1 mode 2 ;bit 5 = 1 mode 1 ;bits 5 and 6 = 1 mode 3 ;bit 4 = fwd / rev ;functions lo nibble decfsz count4,f goto chrd2 ;next bit of 4 decfsz count0,f goto opdown movlw .16 ;chrex2 goto loop opdown movlw .13 ;goto chr2 goto loop chr2 call brdown ;output down goto chrr secbyt movf temp1,w ;store channel data movwf chdata goto chrr1 chrex2 call brup ;output up decfsz bytcnt,f ;last byte? goto secbyt movf temp1,w ;finished getting channel data movwf curadr ;store channel data hndtst btfss chdata,7 ;any handset? goto bcdcon ;yes goto notbcd ;don't do BCD conversion pad25 nop ;add 2.5 usec nop nop nop nop nop return pad30 nop ;add 3 usec nop nop nop nop nop nop nop return ; converts BCD address from thumbwheels ; uses jump table for speed bcdcon comf curadr,f ;recover address and complement movf curadr,w movwf temp1 ;BCD to binary swapf temp1,f movlw B'00001111' ;mask andwf curadr,f ;has low nibble andwf temp1,f ;has hi nibble movlw .10 ;start bcd conversion bcdlp movf temp1,f btfsc STATUS,zbit goto bcdfin addwf curadr,f ;add 10 decf temp1,f goto bcdlp ; sets bit in alarm counter ; works like a shift register ; bit moves along with each channel count ; rolbit movf chcnt,f ;roll the alarm counter bit btfss STATUS,zbit ;is it channel 0 goto aroll0 bsf STATUS,carry ;set carry rlf alarmc,f ;roll in a one goto aroll1 aroll0 bcf STATUS,carry ;clear carry rlf alarmc,f ;roll bit along aroll1 return bcdfin ;bsf PORTD,clk ;read consist switch movf alarmc,w andwf PORTB,w btfss STATUS,zbit ;test bit (0 = consist) goto conoff movf alarmc,w ;get handset counter andwf conbit,w ;test for zero btfsc STATUS,zbit bsf consis,4 goto conout conoff movf alarmc,w ;toggle consist set off andwf conbit,w btfsc STATUS,zbit goto conout comf alarmc,w andwf alarm,f ;clear alarm andwf conbit,f ;clear consist flag conout ;bcf PORTD,clk ;clock up movlw .14 ;bcdout goto loop bcdout call brdown ;output down return notbcd clrf curadr ;invalid address goto bcdfin ; ; ; ; ; set up function byte ; checks if there has been a change ; sets / clears toggle action ; setfun comf chdata,w ;hi is off on receipt movwf curfun ;current functions (lo nibble) btfsc consis,4 ;is it a consist set up goto nchng2 ;yes so leave alone movf alarmc,w ;check consist toggle andwf conbit,w btfss STATUS,zbit goto nchng2 movlw functs ;point to function table addwf chcnt,w ;offset by channel movwf FSR movlw B'00100000' ;counter for 28 step mode addwf INDF,f movlw B'00001111' ;toggle action for functions andwf curfun,w btfss STATUS,zbit ;are all buttons released? goto fchnge ;yes so set toggle bsf INDF,4 ;toggle is bit 4 goto nchng2 fchnge btfsc INDF,4 ;toggle set? goto fchng1 goto nchng2 fchng1 movlw B'00001111' ;mask functions andwf curfun,w ;get new functions to change xorwf INDF,f ;change funct. bit bcf INDF,4 ;clear toggle nchng2 movlw .15 ;wait for RTCC goto loop nchng1 call brup ;output up return ; ; checks loco address for in use ; a valid address has the top bit set ; loco movlw locos ;get table start addwf chcnt,w ;add channel count movwf FSR ;set indirection btfss chdata,7 ;handset off? goto okadr ;no so continue comf alarmc,w ;handset off so no alarm on this channel andwf alarm,f btfss INDF,7 ;old address valid? goto lidle5 ;no so idle bcf INDF,7 ;set address to invalid movf INDF,w ;get old address goto estop ;handset now off so stop ;loco that was assigned to it ;and set to invalid ; ; address is OK to check ; okadr bsf curadr,7 ;set valid bit on new one movf curadr,w ;get new address to w subwf INDF,w ;is it same as old one? btfss STATUS,zbit ;skip if same goto new1 ;new address different btfss consis,4 ;test for consist setup goto nocons ;no consist button goto setcon ;set / unset consist nocons goto speed ;put out speed packet to same address new1 movf adcdat,f ;is speed zero? btfsc STATUS,zbit goto change ;yes so change? comf alarmc,w ;speed not zero so no alarm on this channel andwf alarm,f btfss INDF,7 ;old address valid? goto lidle5 ;old one was not and speed not zero so idle ;old one was valid and speed not zero comf alarmc,w ;clear alarm bit andwf alarm,f movf INDF,w ;get old address movwf curadr goto speed ;send speed packet to old address ; ; speed is zero so see if address is in use ; interleaved with sending preamble bits ; if there is a match, the 'used' flag is set ; change clrf used ;clear flag for address in use movlw locos ;reset FSR movwf FSR movlw 2 ;loop 2 channels movwf count0 chng1 movf curadr,w ;get new address subwf INDF,w btfsc STATUS,zbit ;same? incf used,f incf FSR,f decfsz count0,f goto chng1 movlw .22 goto loop chngz call brdown ;output down movlw 4 ;loop another 4 movwf count0 chng2 movf curadr,w ;get new address subwf INDF,w btfsc STATUS,zbit ;same? incf used,f incf FSR,f decfsz count0,f goto chng2 movlw .23 goto loop ;wait for timer chngy call brup ;output up movlw 2 ;loop another 2 movwf count0 chng3 movf curadr,w ;get new address subwf INDF,w btfsc STATUS,zbit ;same? incf used,f incf FSR,f decfsz count0,f goto chng3 movf used,f ;test used flag btfss STATUS,zbit goto kill ;address in use so kill old loco movlw .27 ;wait for RTCC goto loop loc1 call brdown ;output down movlw locos ;OK so put new address movwf FSR ;in loco table movf chcnt,w addwf FSR,f ;now check if old address was 00 btfss INDF,7 ;is address valid goto nozero ;not valid so don't reset movf INDF,w ;test for zero andlw B'01111111' btfss STATUS,zbit goto nozero ;not zero clrf strch ;was zero so reset stretch nozero comf alarmc,w ;clear alarm bit andwf alarm,f ;mod to ensure old loco stops loc3 movlw .28 ;noz goto loop noz call brup ;output up call activ ;if new is a consist, activate movf curadr,w ;swap old and new addresses movwf temp1 ;save movf INDF,w movwf curadr movf temp1,w movwf INDF btfss curadr,7 ;was old one valid goto lidle5 clrf adcdat ;make sure old address is off call deact ;if old was a consist, then deactivate goto speed activ bcf curadr,7 ;clear valid flag movlw .90 ;sets consist active flag for speed check subwf curadr,w btfsc STATUS,zbit ;no match bsf consis,0 ;flag for consist 90 movlw .91 subwf curadr,w btfsc STATUS,zbit ;no match bsf consis,1 ;flag for consist 91 movlw .92 subwf curadr,w btfsc STATUS,zbit ;no match bsf consis,2 ;flag for consist 92 movlw .93 subwf curadr,w btfsc STATUS,zbit ;no match bsf consis,3 ;flag for consist 93 bsf curadr,7 ;restore valid flag return deact bcf curadr,7 ;clear valid flag movlw .90 ;clears consist active flag subwf curadr,w btfsc STATUS,zbit ;no match bcf consis,0 ;flag for consist 90 movlw .91 subwf curadr,w btfsc STATUS,zbit ;no match bcf consis,1 ;flag for consist 91 movlw .92 subwf curadr,w btfsc STATUS,zbit ;no match bcf consis,2 ;flag for consist 92 movlw .93 subwf curadr,w btfsc STATUS,zbit ;no match bcf consis,3 ;flag for consist 93 return ; ; address in use so stop old loco and set hansdet address to invalid ; also set alarm ; kill movlw .31 goto loop kill1 call brup ;output up movlw locos ;get old address movwf FSR ;in loco table movf chcnt,w addwf FSR,f ;now check if old address was 00 movf INDF,w ;test for zero andlw B'01111111' btfss STATUS,zbit goto kzero ;not zero clrf strch ;was zero so reset stretch kzero movf alarmc,w ;set alarm register iorwf alarm,f movlw .32 goto loop kill2 call brdown ;output down btfsc INDF,7 goto kill3 ;is old address valid? movlw 5 ;5 extra preambles goto lidle1 ;no so idle kill3 bcf INDF,7 ;set invalid movf INDF,w ;get old address movwf curadr call deact ;if a consist, deactivate clrf adcdat ;stop incf adcdat,f ;em stop goto speed ; ; ; sends an idle packet without preamble ; lidle3 movlw B'11111111' ;set idle bytes movwf byte1 movlw B'00000000' movwf byte2 movlw 3 movwf bytcnt call packet goto spdo2 ;check for part of consist lidle5 movlw 5 lidle1 call addpre ;add preamble cycles goto lidle3 ; ; subroutine to add extra preamble cycles ; number of extras in w ; arrives with output hi ; leaves with output hi ; addpre movwf count1 ;extra preamble cycles addpr1 movlw .19 goto loop lid1 call brdown ;output down movlw .18 goto loop lid2 call brup ;output up decfsz count1,f goto addpr1 return ; ; sends a speed packet ; checks on speed mode and sets accordingly ; mode for a consist is that of the consist handset ; cannot mix modes within a consist ; checks for address zero and sets stretches ; arrives with output high ; exits to packet with output high speed bcf curadr,7 ;clear valid flag movf curadr,f ;is address zero? btfss STATUS,zbit goto not0 ;no movf adcdat,w ;put adc data into stretch movwf strch rrf strch,f ;64 steps for DC btfss chdata,4 ;check direction bit goto fwd0 bsf strch,7 ;set direction bit (1 = reverse) goto speedy fwd0 bcf strch,7 speedy movlw 3 ;3 extra preambles goto lidle5 ;put out idle packet (address 00) not0 goto conchk ;is it a consist speedz movf curadr,f ;consist address is zero (no loco) btfsc STATUS,zbit goto speedy movlw .21 goto loop ;wait for RTCC not0a call brdown ;output down movlw B'01100000' ;test for current speed mode andwf chdata,w movwf temp1 btfsc STATUS,zbit goto spd3 ;mode 0 is 128 for now movlw B'01100000' subwf temp1,w btfsc STATUS,zbit goto spd3 ;128 step speed mode btfss temp1,5 goto spd2 ;28 step mode goto spd1 ;14 step mode ; set up speed bytes ; ; spd3 is 128 step mode ; spd3 movf curadr,w ;get address movwf byte1 bcf byte1,7 ;clear valid flag movlw B'00111111' ;128 bit speed movwf byte2 movf adcdat,w movwf byte3 btfsc chdata,4 goto revset fwdset bsf byte3,7 ;forward goto errset revset bcf byte3,7 ;reverse errset movlw 4 movwf bytcnt goto spdout ;send speed packet ; ; spd2 is 28 step mode ; spd2 movf curadr,w ;get address movwf byte1 bcf byte1,7 ;remove valid bit clrf byte2 ;clear speed byte decf adcdat,w ;em stop? btfsc STATUS,zbit goto spd2e movlw B'00000011' ;save last two bits andwf adcdat,w movwf div ;reuse div movf adcdat,w ;save adcdat movwf adcset ;reuse adcset as temp rrf adcset,f ;reduce speed to 4 bits rrf adcset,f movlw B'00011111' andwf adcset,f ;mask movlw 2 subwf adcset,w btfss STATUS,carry ;2 or more? goto nonout movlw functs ;point to function table addwf chcnt,w ;add channel movwf FSR ;indirection swapf INDF,w movwf temp rrf temp,w andlw B'00000011' ;mask except count subwf div,w btfss STATUS,carry ;count > div goto nojit btfsc STATUS,zbit ;count = div goto nojit btfss PORTD,2 ;no jitter mode? goto nojit movlw B'00011111' ;is it step 28 already andwf adcset,f ;mask subwf adcset,w btfss STATUS,zbit incf adcset,f ;add 1 nojit rrf adcset,w ;in 28 step mode btfsc STATUS,carry bsf byte2,4 iorwf byte2,f ;set speed spd2f call dirsub ;set direction goto spdout nonout clrf adcset ;stop goto nojit spd2e movf adcdat,w ;em stop movwf byte2 goto spd2f ; ; spd1 is 14 step mode. FO bit is set ; spd1 movf curadr,w ;get address movwf byte1 bcf byte1,7 decf adcdat,w ;em stop? btfsc STATUS,zbit goto spd1e rrf adcdat,f ;set for 14 steps only rrf adcdat,f ;reduce speed to 4 bits rrf adcdat,w movwf byte2 ;set speed bcf byte2,4 ;F0 bit movlw functs ;point to function table addwf chcnt,w ;add channel movwf FSR ;Indirection btfsc INDF,0 ;test F0 bit bsf byte2,4 ;F0 on spd1f call dirsub ;set direction spdout movlw .20 goto loop spdo1 call brup ;output up call packet ;send speed packet spdo2 btfss consis,6 ;a consist return btfss consis,7 goto lstcn1 movlw 5 call addpre ;add 5 preamble cycles goto nxtloc ;next in consist spd1e movf adcdat,w movwf byte2 goto spd1f lstcn1 bcf consis,6 ;last in consist return dirsub btfsc chdata,4 ;check direction goto revse1 fwdse1 bsf byte2,5 ;forward goto errse1 revse1 bcf byte2,5 ;reverse errse1 bcf byte2,7 bsf byte2,6 ;speed packet movlw 3 movwf bytcnt return confn1 btfss chdata,4 ;direction in consist goto revse1 goto fwdse1 ; sends a stop to current loco ; estop movwf curadr ;old address clrf adcdat ;speed to zero incf adcdat,f ;em stop call deact ;deactivate if a consist ;********************************* ; ; set up consist ; loco address in curadr ; consist number is function button ; ; setcon movf curfun,w ;get function button andlw B'00001111' movwf temp btfsc STATUS,zbit ;no button? goto noput movlw .9 ;too high? subwf temp,w btfsc STATUS,carry goto noput movf temp,w call conbut ;converts button number to address btfsc STATUS,zbit ;invalid combination? (only one button ;allowed) goto noput movwf temp ;for consist address movlw .35 ;wait goto loop setc1 call brdown ;output down decf temp,f ;less 1 lookup clrf temp1 clrf temp2 clrf count4 movf curadr,w ;get handset address movwf adrtmp bcf adrtmp,7 ;clear valid bit incf count4,f ;1 in count 4 movf temp,w addlw cons00 ;start of consist block movwf FSR looku1 movf INDF,w ;get loco btfsc STATUS,zbit ;blank? goto setblk looku2 andlw B'01111111' ;mask direction bit subwf adrtmp,w ;same? btfsc STATUS,zbit ;no goto looku3 movf adrtmp,f ;is address 00? btfss STATUS,zbit goto again ;no looku3 movf count4,w movwf temp2 ;number that matches again incf count4,f incf FSR,f movlw 5 subwf count4,w btfss STATUS,zbit goto looku1 goto check setblk movf count4,w movwf temp1 ;number that is a blank goto again check movlw 4 ;set FSR back to block start subwf FSR,f movf temp2,w ;was there a match? btfsc STATUS,zbit goto check1 ;no decf temp2,w ;get which one addwf FSR,f goto clrout ;clear loco check1 movf temp1,w btfsc STATUS,zbit ;was there a blank goto noput decf temp1,w addwf FSR,f putin movf adrtmp,w ;get address movwf INDF ;put in block btfsc chdata,4 ;direction for consist bsf INDF,7 ;top bit of address is direction movf INDF,w bsf STATUS,RP1 ;page 2 movwf EEDATA bcf STATUS,RP1 putcon movlw cons00 ;get eeprom address subwf FSR,w ;get block offset bsf STATUS,RP1 ;page 2 movwf EEADR bcf STATUS,RP1 movlw .36 call loop ;wait setc2 call brup ;output up bsf STATUS,RP0 ;page 3 bsf STATUS,RP1 bcf EECON1,EEPGD bsf EECON1,wren call eewrit ;write loco to eeprom ;end of write movf alarmc,w ;set toggle iorwf conbit,f iorwf alarm,f ;set alarm for acknowledge noput bcf consis,4 movlw 4 call addpre ;add 4 preambles goto speed clrout clrf INDF bsf STATUS,RP1 clrf EEDATA bcf STATUS,RP1 goto putcon ;clear table entry ;****************************************************** ; ; check if address is a consist and if so ; send speed packets to all in the consist ; else send speed packet as normal ; conchk movlw .90 ;consist address? subwf curadr,w ;90 or higher btfss STATUS,carry goto notcon ;not a consist sublw .3 btfss STATUS,carry goto notcon ;not a consist bsf consis,7 ;is a consist bsf consis,6 movf chdata,w ;save chdata movwf mask movlw .90 subwf curadr,f ;consist block address starts at 0 bcf STATUS,carry rlf curadr,f rlf curadr,f ;multiply by 4 movlw cons00 ;start of table addwf curadr,w movwf temp2 ;set index conck1 movlw .4 ;4 locos per consist movwf count4 ;4 in count4 loadu1 movf temp2,w movwf FSR movf INDF,w ;get table entry btfss STATUS,zbit ;is it blank goto spdin ;send packet to loco decfsz count4,f ;block end? goto nxtloc goto lstloc ;last in consist nxtloc incf temp2,f goto loadu1 ;next in block spdin movwf curadr ;loco address to curadr movf mask,w ;recover original chdata btfss curadr,7 ;consist direction goto norev ;not reverse xorlw B'00010000' ;direction for consist norev movwf chdata ;set for this loco bcf curadr,7 ;clear direction bit movlw 4 call addpre ;4 extra preamble cycles goto speedz lstloc bcf consis,7 ;last of consist goto spdin ;******************************************** ; ; checks if loco is in an active consist and if so ; does not send speed packet. As a loco may be in ; more than one consist which may or not be active, ; this is a bit messy. ; notcon clrf used movlw cons00 movwf FSR nexloc movlw .17 goto loop nexld1 call brdown ;output down call conloc sublw 0 btfsc STATUS,zbit goto nexlo1 ;no match in consist 90 btfsc consis,0 ;consist active? incf used,f nexlo1 movlw .24 goto loop nexlu1 call brup ;output up movlw 2 call addpre ;add 2 preamble cycles incf FSR,f call conloc sublw 0 btfsc STATUS,zbit goto nexlo2 ;no match in consist 91 btfsc consis,1 ;consist active? incf used,f nexlo2 movlw .33 goto loop nexld2 call brdown ;output down incf FSR,f call conloc sublw 0 btfsc STATUS,zbit goto nexlo3 ;no match in consist 92 btfsc consis,2 ;consist active? incf used,f nexlo3 movlw .34 goto loop nexlu2 call brup ;output up incf FSR,f call conloc sublw 0 btfsc STATUS,zbit goto nexlo4 ;no match in consist 93 btfsc consis,3 ;consist active? incf used,f nexlo4 movf used,f ;not zero if in a consist? btfsc STATUS,zbit goto speedz ;send speed packet to loco movlw 4 ;4 extra cycles goto lidle1 ;send an idle instead conloc movlw 4 movwf count conlo1 movf INDF,w ;get loco andlw B'01111111' ;mask direction subwf curadr,w btfsc STATUS,zbit ;not in a consist retlw 1 ;match decfsz count,f goto conlo2 retlw 0 ;no match conlo2 incf FSR,f goto conlo1 ;******************************************** ; sends a function packet if required ; sndchg incf fcount,w ;increment function counter andlw B'00000111' ;eight channels movwf fcount movf fcount,f ;is fcount zero btfss STATUS,zbit goto getfun movlw 1 movwf count0 call idle ;was zero so add an idle packet getfun movlw locos ;loco table addwf fcount,w ;add offset movwf FSR btfss INDF,7 ;invalid address? goto inval ;don't send a function movf INDF,w ;get address movwf byte1 bcf byte1,7 ;clear valid flag movf byte1,f ;is address zero? btfsc STATUS,zbit goto inval ;yes so dont send function movlw functs ;start of function stack addwf fcount,w ;offset movwf FSR ;set pointer movf INDF,w ;get function andlw B'00001111' ;mask mode bits movwf byte2 rrf byte2,f ;mod for f0 btfsc STATUS,carry ;F0 was zero bsf byte2,4 ;set F0 bsf byte2,7 ;set for function group 1 movlw 3 movwf bytcnt call preamb ;preamble call packet ;send function packet return inval movlw .25 ;no function packet goto loop ;but needs an extra ;preamble cycle to make up time inv1 call brdown ;output down movlw 8 addwf FSR,f ;point to function movlw B'11100000' andwf INDF,f ;clear any old function data movlw .26 goto loop inv2 call brup ;output up return ; ; eeprom write subroutine. Write EEDATA into EEADR ; does not wait for write complete as this would interfere ; with the DCC bit stream timing. ; can only write every 8th packet anyway. eewrit bcf INTCON,GIE movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,wr nop bcf EECON1,wren bcf STATUS,RP0 bcf STATUS,RP1 ;page 0 return eeread bsf STATUS,RP0 ;page 3 bcf EECON1,EEPGD bsf EECON1,rd ;set read bit bcf STATUS,RP0 ;back to page 2 movf EEDATA,w ;get data bcf STATUS,RP1 ;page 0 last return ;************************************************ ; ; clear consist EEPROM space ; org 0x2100 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 de 0 ; lookup table for speed curve in EEPROM ; not finalised yet ; org 0x2130 de .7 de .8 de .9 de .10 de .12 de .14 de .16 de .19 de .24 de .29 de .37 de .48 de .65 de .93 de .145 de .255 end