; Ver 1.0 ; ; Date: April, 3, 2005 ; This is a simple single switch DCC decoder with the 12F629 ; It uses the internal osc. Execution cycle is 1us. ; No CVs to fiddle with, programmable directly from switch control device: ; decoder address and switch impulse duration (with constant and re-trigerrable output options) ; It incorporates ideas from D.Probst DCC decoder project ; Copyright (C) 2005 Alex Lagutin ; Parts copyright (C) 2003 Heiko Schroeter ; Parts copyright (C) Dean Probst ; ; How to program this decoder (using Roco RouteControl Keyboard as an example) ; ; Accessory address: ; Press and hold ADDRESS button on a decoder ; type in desired number and press OK or ESC on a keyboard ; Release addr_btn ; Possible address ranges: 1-255, default address - 1 ; ; Switch impulse duration: ; Press and hold TIME button on a decoder ; type in desired number and press OK or ESC on a keyboard ; Release time_btn ; Possible duration ranges: 1-255, default duration - 1 (20ms) ; duration calculated by decoder as Value*20ms ; Duration value of 255 has special meaning: Constant Output ; i.e. output pin remains on until switched to another position ; If duration value is even number, then output becomes re-trigerrable ; i.e. same command repeated will trigger output again and again ; LIST P=PIC12F629 #INCLUDE __CONFIG _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _MCLRE_ON & _INTRC_OSC_NOCLKOUT ;========================================================================== #define acc_addr_ee 0 ; location to store ACC_ADDR in EEPROM #define acc_time_ee 1 ; location to store ACC_ON_TIME in EEPROM #define out_A GP0 ; GP0 is out_A #define out_B GP1 ; GP1 is out_B #define dcc_in GP2 ; GP2 is DCC IN #define addr_btn GP4 ; GP4 is address programming button #define time_btn GP5 ; GP5 is switch on-time programming button #define nopreamb D'19' ; No. of preamble bits = 20 ; Bit Positions in TEST_BITS #define bitrec 0 ; What value has just rec. bit #define byte4 1 ; 4 or 5 byte packet received cblock 0x20 ACC_ADDR ; our accessory address ACC_ON_TIME ; time to keep output pin up ACTIVE_PAIR ; last pair active ON_TIMER_CTRL ; on-time counter control ON_TIMER_HI ; on-time counter hi-part ON_TIMER_LO ; on-time counter low-part WR_EEDATA ; register used to pass data to write into EEPROM TEST_BITS ; Contains various bits to test ENDVAL ; Offset to byte check routines PRECOUNT ; Preamble counter SAMPLES ; How many samples taken for one or zero STATE ; Where are we in the DCC package DATA1 ; First transm. byte DATA2 ; Second trans. byte DATA3 ; Third transm. byte DATA4 ; Fourth transm. byte DATA5 ; Scratch Register BTN_STATE_PRE ; State of buttons at the beginning of decoding BTN_STATE_POST ; State of buttons at the end of decoding TEMP TEMP1 endc ; Macro for generating the Output switching timing switch macro jumpto local keep_counting,set_ctrl_bit,exit_18,exit_19 movf ON_TIMER_HI,F ;8 btfss STATUS,Z ;9 goto keep_counting ;10,11 bcf GPIO,out_A ;11 time to drive bcf GPIO,out_B ;12 pins down bsf ON_TIMER_HI,7 ;13 disable clrf ON_TIMER_CTRL ;14 timer nop ;15 goto exit_18 ;16,17 keep_counting: btfss ON_TIMER_CTRL,0 ;12 flip scaler bit goto set_ctrl_bit ;13,14 bcf ON_TIMER_CTRL,0 ;14 decfsz ON_TIMER_LO,F ;15 wrapped? goto exit_18 ;16,17 not yet decf ON_TIMER_HI,F ;17 goto $+1 ;18,19 goto jumpto ;20,21 set_ctrl_bit: btfss ON_TIMER_CTRL,7 ;15 check if timer enabled goto exit_18 ;16,17 not enabled, leave scaler clear bsf ON_TIMER_CTRL,0 ;17 exit_18: nop ;18 exit_19: nop ;19 goto jumpto ;20,21 endm ;************* Here we start ! ****************************************************************************** org 0 bsf STATUS,RP0 ; Bank 1 call 0x3ff ; Get calibration value movwf OSCCAL ; Set calibration bcf STATUS,RP0 ; Bank 0 goto start ;************************************************************************************************************ ;Value checks for correct bit and jumps to where we are in DCC package ;Computed goto has to reside in lower half of page, that's why it is here Value: clrf SAMPLES ; 6 movf STATE,W ; 7 addwf PCL,F ; 8 ; Jump table to routines from where we do a RETURN ! ; cycl. to here -> cycl for rout. goto waitn ; 9,10 goto waitlo goto testlo ; First byte goto bitset goto lastv goto bitset goto lastv goto bitset goto lastv goto bitset goto lastv goto bitset goto lastv goto bitset goto lastv goto bitset goto lastv goto bitset goto lastx ; Byte received goto end11 ; Check first half bit of byte goto end12 ; Check second half bit of byte goto end21 goto end22 goto end31 goto end32 goto end41 goto end42 goto end51 goto end52 ;************************************************************************************ start: clrf GPIO ; Init GPIO movlw 0x7 ; turn digital i/o movwf CMCON ; on GP0/1/2 bsf STATUS,RP0 ; Bank 1 bcf OPTION_REG,NOT_GPPU ; enable pull-ups bcf TRISIO,out_A ; configure outputs bcf TRISIO,out_B bsf TRISIO,dcc_in ; configure inputs bcf WPU,dcc_in ; disable pull-up for dcc_in bsf TRISIO,addr_btn bsf WPU,addr_btn bsf TRISIO,time_btn bsf WPU,time_btn bcf STATUS,RP0 ; Bank 0 ; clear RAM movlw 0x20 ; bottom of RAM movwf FSR Next1: clrf INDF incf FSR,F movlw 0x5f ; end of ram subwf FSR,W btfss STATUS,C goto Next1 clrf INDF movlw nopreamb ; Preamble = 20 half bits movwf PRECOUNT call check_power_up ;************************************************************************************************************ ; High Bit Level Section starthi: call Value ;4,5 conthi: btfss GPIO,dcc_in ;1 HighLevel ? goto startlo ;2,3 No incf SAMPLES,F ;3 Increment SAMPLES Counter bcf TEST_BITS,bitrec;4 clear BITREC movlw 0xFD ;5 two or more samples are a ZERO addwf SAMPLES,W ;6 add the received pulses btfss STATUS,C ;7 bsf TEST_BITS,bitrec;8 less then two SAMPLES is a one switch conthi ;---------------------------------------------------------------------------------------------------- ; Low Level Bit Section startlo: call Value ;4,5 contlo: btfsc GPIO,dcc_in ;0 goto starthi ;1,2 incf SAMPLES,F ;2 bcf TEST_BITS,bitrec;3 movlw 0xFD ;4 addwf SAMPLES,W ;5 btfss STATUS,C ;6 bsf TEST_BITS,bitrec;7 switch contlo ;---------------------------------------------------------------------------------------------------- ; Frame error in packet frameerr: movlw nopreamb movwf PRECOUNT clrf STATE clrf ENDVAL clrf DATA1 clrf DATA2 clrf DATA3 clrf DATA4 clrf DATA5 clrf SAMPLES movlw 0xF0 ; cut of low nibble in TEST_BITS Byte andwf TEST_BITS,F return ;************************************************************************************************************ ; waitn waits for 20 half bits of the preamble waitn: goto $+1 ;11,12 btfss TEST_BITS,bitrec;13 there are only ones in preamble goto waitn1 ;14,15 decfsz PRECOUNT,F ;16 goto ret4 ;16,17 movlw nopreamb ;17 movwf PRECOUNT ;18 incf STATE,F ;19 retlw 0 ;20,21 ret4: goto $+1 ;18,19 retlw 0 ;20,21 waitn1: movlw nopreamb ;16 movwf PRECOUNT ;17 goto $+1 ;18,19 retlw 0 ;20,21 ;************************************************************************************************************ ; waitlo waits for low half bit after preamble waitlo: goto $+1 ;11,12 nop ;13 waitlo1: goto $+1 ;14,15 nop ;16 btfsc TEST_BITS,bitrec;17 must be a zero goto ret5 ;18 might be long preamble incf STATE,F ;19 ret5: retlw 0 ;20,21 ;************************************************************************************************************ ; testlo test second half bit of start bit testlo: goto $+1 ;11,12 goto $+1 ;13,14 goto $+1 ;15,16 btfsc TEST_BITS,bitrec;17 must be a zero goto frameerr ;18 incf STATE,F ;19 retlw 0 ;20,21 ;************************************************************************************************************ ; bitset takes first half bit of a bit in the byte bitset: incf STATE,F ;11 bcf STATUS,C ;12 btfsc TEST_BITS,bitrec;13 bsf STATUS,C ;14 rlf DATA5,F ;15 goto $+1 ;16,17 goto $+1 ;18,19 retlw 0 ;20,21 ;************************************************************************************************************ ; lastv checks that first half bit = second half bit lastv: goto $+1 ;11,12 nop ;13 incf STATE,F ;14 btfss TEST_BITS,bitrec;15 goto lastv1 ;16,17 btfss DATA5,0 ;17 goto frameerr ;18,19 error if not equal nop ;19 retlw 0 ;20,21 lastv1: btfsc DATA5,0 ;18 goto frameerr ;19 error if not equal retlw 0 ;20,21 ;************************************************************************************************************ ; lastx checks that first half bit = second half bit ; and sets offset to byte check routine lastx: incf ENDVAL,F ;11 movf ENDVAL,W ;12 addwf STATE,F ;13 nop ;14 btfss TEST_BITS,bitrec;15 goto lastx1 ;16,17 btfss DATA5,0 ;17 goto frameerr ;18 error if not equal nop ;19 retlw 0 ;20,21 lastx1: btfsc DATA5,0 ;18 goto frameerr ;19 error if not equal retlw 0 ;20,21 ;************************************************************************************************************ ; end11 end of first byte there must be zero ; end11 first half bit. end12 second half bit end11: btfsc TEST_BITS,bitrec;11 goto frameerr ;12,13 incf STATE,F ;13 incf ENDVAL,F ;14 goto $+1 ;15,16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end12: btfsc TEST_BITS,bitrec;11 goto frameerr ;12 movf DATA5,W ;13 movwf DATA1 ;14 movlw 0x03 ;15 point STATE to beginning of jump table movwf STATE ;16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 ;************************************************************************************************************ ; end21 end of second byte there must be zero ; end22 first half bit. end22 second half bit end21: btfsc TEST_BITS,bitrec;11 goto frameerr ;12 incf STATE,F ;13 incf ENDVAL,F ;14 goto $+1 ;15,16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end22: btfsc TEST_BITS,bitrec;11 goto frameerr ;12 movf DATA5,W ;13 movwf DATA2 ;14 movlw 0x03 ;15 point STATE to beginning of jump table movwf STATE ;16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 ;************************************************************************************************************ ; end31 end of third byte there must be a one ; end32 first half bit. end32 second half bit end31: incf STATE,F ;11 incf ENDVAL,F ;12 goto $+1 ;13,14 goto $+1 ;15,16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end32: movf DATA5,W ;11 movwf DATA3 ;12 btfsc TEST_BITS,bitrec;13 if 0 other bytes will follow goto end32x ;14,15 movlw 0x03 ;15 point STATE to beginning of jump table movwf STATE ;16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end32x: clrf STATE ;16 reset STATE for next preamble clrf ENDVAL ;17 clrf DATA4 ;18 clrf DATA5 ;19 bcf TEST_BITS,byte4 goto decode ;21,22 no need here for precise cycle counting, because we decode now ;************************************************************************************************************ ; end41 end of third byte there must be a one ; end42 first half bit. end42 second half bit end41: incf STATE,F ;11 incf ENDVAL,F ;12 goto $+1 ;13,14 goto $+1 ;15,16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end42: movf DATA5,W ;11 movwf DATA4 ;12 btfsc TEST_BITS,bitrec;13 if 0 other bytes will follow goto end42x ;14,15 movlw 0x03 ;15 point STATE to beginning of jump table movwf STATE ;16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end42x: bsf TEST_BITS,byte4 clrf STATE ;16 reset STATE for next preamble clrf ENDVAL ;17 clrf DATA5 ;18 goto decode ;20,21 no need to count, because we decode now ;************************************************************************************************************ ; end51 end of third byte there must be a one ; end52 first half bit. end52 second half bit end51: btfss TEST_BITS,bitrec;11 goto frameerr ;12,13 incf STATE,F ;13 incf ENDVAL,F ;14 goto $+1 ;15,16 goto $+1 ;17,18 nop ;19 retlw 0 ;20,21 end52: btfss TEST_BITS,bitrec;11 test correct ending goto frameerr ;12,13 clrf STATE ;13 reset STATE for next preamble clrf ENDVAL ;14 bsf TEST_BITS,byte4 goto decode ;*************DECODING******************************* decode: clrf BTN_STATE_PRE ; get buttons state btfss GPIO,addr_btn bsf BTN_STATE_PRE,addr_btn btfss GPIO,time_btn bsf BTN_STATE_PRE,time_btn movf DATA1,W ; Exclusive or check xorwf DATA2,W xorwf DATA3,W xorwf DATA4,W xorwf DATA5,W btfss STATUS,Z goto exit_decode ; error in packet call decode_basic_acc_addr iorlw 0 ; check if address is valid (!=0) btfsc STATUS,Z goto exit_decode movf BTN_STATE_PRE,F ; were any buttons pressed? btfsc STATUS,Z goto normal_operation movwf TEMP clrf BTN_STATE_POST ; get buttons state again btfss GPIO,addr_btn bsf BTN_STATE_POST,addr_btn btfss GPIO,time_btn bsf BTN_STATE_POST,time_btn movf BTN_STATE_PRE,W ; check if buttons state is the same xorwf BTN_STATE_POST,W btfsc STATUS,Z goto program_acc ; jump to programming movf TEMP,W normal_operation: xorwf ACC_ADDR,W ; test if address is ours btfss STATUS,Z goto exit_decode movf DATA2,W ; extract output pair andlw 1 call do_switch goto exit_decode program_acc: clrf ACTIVE_PAIR ; reset active pair movf TEMP,W btfsc BTN_STATE_POST,addr_btn goto program_acc_addr btfss BTN_STATE_POST,time_btn goto exit_decode ; program switch on-time movwf ACC_ON_TIME movwf WR_EEDATA movlw acc_time_ee call write_eeprom goto exit_decode program_acc_addr: movwf ACC_ADDR call store_acc_addr exit_decode: clrf TEMP clrf DATA1 clrf DATA2 clrf DATA3 clrf DATA4 clrf DATA5 clrf ENDVAL bcf TEST_BITS,byte4 return decode_basic_acc_addr: ; returns decoded accessory address in W, 0 - means invalid ; we support addresses up to (64*4)-1 btfss DATA1,7 ; check if address is basic accessory one retlw 0 btfsc DATA1,6 ; 10AAAAAA retlw 0 btfss DATA2,7 ; 1AAACDDD retlw 0 swapf DATA2,W ; check if address is in our supported range andlw b'00001111' sublw b'00001111' btfss STATUS,Z retlw 0 rlf DATA1,W ; make 8-bit address movwf TEMP rlf TEMP,W andlw b'11111100' movwf TEMP rrf DATA2,W andlw b'00000011' iorwf TEMP,W addlw 1 return ;***************************************************************************** check_power_up: movlw acc_time_ee ; Load call read_eeprom ; switch on-time movwf ACC_ON_TIME movlw acc_addr_ee ; Load call read_eeprom ; accessory address movwf ACC_ADDR ; incfsz ACC_ADDR,F ; address stored is actual acc_addr minus one goto exit_power_up ; 0x0 means first time run (invalid address) movlw 1 ; Default accessory address movwf ACC_ADDR call store_acc_addr movlw 1 ; Default switch on time movwf ACC_ON_TIME movwf WR_EEDATA movlw acc_time_ee call write_eeprom exit_power_up: return store_acc_addr: ; accessory address in ACC_ADDR decf ACC_ADDR,W movwf WR_EEDATA movlw acc_addr_ee call write_eeprom return read_eeprom: ; Address in W, read byte in W bsf STATUS,RP0 ; Bank 1 movwf EEADR bsf EECON1,RD movf EEDATA,W bcf STATUS,RP0 ; Bank 0 return write_eeprom: ; Address in W, data byte in WR_EEDATA bsf STATUS,RP0 ; Bank 1 movwf EEADR bcf STATUS,RP0 ; Bank 0 movf WR_EEDATA,W bsf STATUS,RP0 ; Bank 1 movwf EEDATA ; begin EEPROM write required sequence bsf EECON1,WREN ; Enable EEPROM write bcf INTCON,GIE ; Disable interrupts movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; end sequence btfsc EECON1,WR ; wait till write completes goto $-1 bsf INTCON,GIE ; Enable interrupts bcf STATUS,RP0 ; Bank 0 return ;********************************************************************** do_switch: ; W is pair id btfsc ACTIVE_PAIR,7 ; test if active pair is valid goto test_active_pair btfsc ACC_ON_TIME,0 ; is output re-triggerable bsf ACTIVE_PAIR,7 ; disable re-trigger goto activate_pair ; skip active pair test test_active_pair: movwf TEMP ; save W xorwf ACTIVE_PAIR,W andlw b'01111111' btfsc STATUS,Z return ; asked to activate already active pair movf TEMP,W ; restore W activate_pair: iorlw 0 btfsc STATUS,Z goto activate_out_B activate_out_A: bcf GPIO,out_B bsf GPIO,out_A bsf ACTIVE_PAIR,0 goto set_up_timer activate_out_B: bcf GPIO,out_A bsf GPIO,out_B bcf ACTIVE_PAIR,0 set_up_timer: clrf ON_TIMER_LO movf ACC_ON_TIME,W movwf ON_TIMER_HI clrf ON_TIMER_CTRL incf ON_TIMER_HI,W ; test if on-time=255 btfss STATUS,Z ; this means constant output bsf ON_TIMER_CTRL,7 ; enable timer return end