; ********************************************************************** ; * Blocksignal-DCC-Decoder/Rangiersperrsignal-DCC-Decoder * ; * Version 1.01 * ; * Last change: 12/30/2004 * ; * Microcontroller: PIC12F629 * ; * Status: Tested * ; ********************************************************************** ; ********************************************************************** ; * (C) Copyright Gorden Griem, 2004 * ; * parts copyright by Dean Probst, Ingo Schroeter * ; ********************************************************************** ; ********************************************************************** ; * Diese Software ist freie Software, sofern es sich um den privaten * ; * Gebrauch handelt. Kommerzieller Gebrauch erst nach expliziter, * ; * schriftlicher Genehmigung des Autors erlaubt. * ; * Dieses Programm wird trotz erfolgreicher Tests ohne Garantie ver- * ; * teilt. Insbesondere existiert KEINE implizite Garante für irgend- * ; * eine Funktion. * ; ********************************************************************** ; ********************************************************************** ; * Funktionsbeschreibung: * ; * Dies ist ein Decoder für ein Blocksignal/Rangiersperrsignal * ; * Die negative Seite der Leuchtdioden wird direkt an den Mikro- * ; * prozessor angeschlossen. * ; * Der Decoder kennt zwei Zustände: Hp0 bzw. Sh0 und Hp1 bzw. Sh1 * ; * Es wird eine Adresse verwendet. Hp0/Sh0 wird durch "-" (gerade), * ; * Hp1/Sh1 durch "+" (Abzweig) eingestellt. Der aktuelle Zustand wird * ; * im EEPROM gespeichert, so dass auch nach einem Stromausfall der * ; * Zustand erhalten ist. Ebenfalls im EEPROM wird die Adresse * ; * abgelegt. * ; * Die Adresse besteht aus zwei Teilen: ADRESSEH und ADRESSEL. * ; * Die tatsächliche Adresse errechnet sich aus der Formel * ; * ADRESSEH*256 + ADRESSEL * ; * Zustätzliche Features: * ; * - Überschreibschutz der Adresse im EEPROM in writeEE-Routine * ; * (seit Version 1.01) * ; * - internes Umrechnen von Adresse auf zu empfangendes Paket * ; ********************************************************************** ;----- Processor Definition------------------------------------------------ LIST p=12F629 ; target processor ;----- Configuration Bits ------------------------------------------------- #define _CPD_ON H'30FF' #define _CPD_OFF H'31FF' #define _CP_ON H'317F' #define _CP_OFF H'31FF' #define _BOD_EN_ON H'31FF' #define _BOD_EN_OFF H'31BF' #define _MCLRE_ON H'31FF' #define _MCLRE_OFF H'31DF' #define _PWRTE_ON H'31EF' #define _PWRTE_OFF H'31FF' #define _WDT_ON H'31FF' #define _WDT_OFF H'31F7' #define _IntRC_OSC H'31FC' ; ----- Current configuration: ; ----- Code Protect Data : Off ; ----- Code Protect : Off ; ----- Brown Out Detect : On ; ----- Power Up Timer : On ; ----- Watchdog Timer : Off ; ----- Master-Clear : Internal ; ----- Oscillator : Internal RC, no clock to outside __CONFIG _CPD_OFF & _CP_OFF & _BOD_EN_ON & _PWRTE_ON & _WDT_OFF & _MCLRE_OFF & _IntRC_OSC ; ********************************************************************** ; IO-Belegung #define gp0 0 ; Ausgang 1: steuert die rote LED #define gp1 1 ; Ausgang 2: steuert die grüne LED #define gp2 2 ; n.c. #define gp3 3 ; data in #define gp4 4 ; n.c. #define gp5 5 ; n.c. #define nopreamb D'17' ; No. of preamble bits = 20; aber es kann ja nicht schaden, schon vorher zu reagieren ; ********************************************************************** ; User Variables / Benutzervariablen ; contains unused variables cblock 0x20 CONFIG ; Contains various bits to test MOTMASK ; Motormask for direction PWM ; How long is motor on ENDVAL ; Offset to byte check routines PRECOUNT ; Preamble counter 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 SAMPLES ; How many samples taken for one or zero TEMP ; Temporarly scratch reg ; the following are for EEPROM routines PC_OFFSET ; PC offset register (low order 4 bits), ; value based on operating mode of EEPROM. ; Also, bit 7 used for EE_OK flag TIMEOUTCOUNT WHOIS ; Welcher Ausgang wird beim Timer kontrolliert? EOFFS ; Adreßoffset EADR1 ; Adresse für BITZUS, Bit 0 = 0 EADR2 ; Adresse für BITZUS, Bit 0 = 1 EETADR ; Adresse, wenn ins EEPROM geschrieben oder daraus gelesen werden soll endc ; Bit Positions in config #define bitrec 0 ; What value has just rec. bit #define CVaccessBit 1 ; Two consecutive packets to be send #define resetflag 6 ; Signal reset for service mode ;========================================================================== ; ; Register Definitions ; ;========================================================================== #define W H'0000' #define F H'0001' ;----- Register Files ----------------------------------------------------- INDF EQU H'0000' TMR0 EQU H'0001' PCL EQU H'0002' STATUS EQU H'0003' FSR EQU H'0004' GPIO EQU H'0005' OPTION_REG EQU H'0081' TRISIO EQU H'0085' OSCCAL EQU H'0090' EEDAT EQU H'009A' EEADR EQU H'009B' EECON1 EQU H'009C' EECON2 EQU H'009D' ;----- STATUS Bits -------------------------------------------------------- #define GPWUF H'0007' #define RP0 H'0005' #define NOT_TO H'0004' #define NOT_PD H'0003' #define Z H'0002' #define DC H'0001' #define C H'0000' ;----- OPTION Bits -------------------------------------------------------- #define NOT_GPWU H'0007' #define NOT_GPPU H'0006' #define T0CS H'0005' #define T0SE H'0004' #define PSA H'0003' #define PS2 H'0002' #define PS1 H'0001' #define PS0 H'0000' ;----- OSCCAL Bits -------------------------------------------------------- #define OSCFST H'0003' #define OSCSLW H'0002' ;----- EECON1 Bits -------------------------------------------------------- #define WRERR H'0003' #define WREN H'0002' #define WR H'0001' #define RD H'0000'; ;************* Hier geht es los! +********************** ; org 0 bsf STATUS,RP0 call 3FFh ; load calibration value movwf OSCCAL ;put calibration value into OSCCAL bcf STATUS,RP0 goto dccsetup ; go to init-setup ; ;****************************************************** ;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 ; Platz für Updates org 10 ; TimerInc wird von Value aus etwa alle 4 ms aufgerufen. Dann wird ; je nachdem, wer dran ist, einer der Ausgänge oder die "Löschprozedur" ; aufgerufen. TimerInc: clrf TMR0 ; "02" incf WHOIS,F movf WHOIS,W addwf PCL,F ; "05" nop ; Darf nicht auftauchen goto TimTi1 ; "07" goto TimEnde ; "07" TimTi1: nop nop nop nop nop goto TiDon ; "14" TimTa1: nop ; "13" goto TiDon ; "14" TimEnde: clrf WHOIS ; "09" goto TimE11 ; "10" TimE11: goto TimE12 ; "12" TimE12: goto TiDon ; "14" ; Die Routine, die ein neues Bit bearbeitet Value: ; movf SAMPLES,W ;use only when needed to check for 0 samples ; btfsc STATUS,Z ; goto nosample ; nop btfsc TMR0,6 ; Wenn Bit 6 gesetzt ist, ist etwa 4ms vergangen goto TimerInc ; "00" nop ;26us sample nop nop nop nop nop nop nop nop nop nop nop nop nop nop TiDon: clrf SAMPLES movf STATE,W addwf PCL,F ;cycl to here ;Jump table to routines from where we do a RETURN ! goto waitn ;cycl. to here -> cycl for rout. 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 ;****************************************************** ;************* setup sets all what is ness. *********** dccsetup: movlw EEZUSTAND ; load last state (red or green light on) movwf EETADR call readEE bsf STATUS,RP0 andlw B'00000011' ; make sure only the two bits for light can be turned on iorlw B'11111100' ; (good in case of an invalid value in EEPROM) movwf TRISIO bcf STATUS,RP0 movlw B'00000000' ; im Grundzustand ist das Signal auf rot movwf GPIO ;****************************************************** ;*** Einlesen der Adresse. Wenn die Adresse folgendermaßen gegeben ist ;*** High: 00000abc Low: defghikm ;*** dann muss dies in zwei Bytes folgendermaßen umgestellt werden: ;*** EOFFS: 10defghi ;*** EADRx: 1abc1kmx wobei abc invertiert sind ;*** Hinweis: Adresse "x" dient der Unterscheidung ob gerade (=0) oder Abzweig (=1) ;*** Außerdem ist Adresse "1" nicht, wie man schätzen würde, die mit defghi=0, sondern es ;*** sind bereits defghi=000001. Daher muss "3" zur im EEPROM gespeicherten Adresse hinzu- ;*** addiert werden, damit die korrekt Adresse angesprochen wird. Für Adresse 257 etc hingegen ;*** ist dann defghi=000000 erlaubt. movlw ADRESSEH ; Einlesen der oberen drei Adressbits für Licht 1 movwf EETADR call readEE ; Aus dem EEPROM lesen movwf TEMP rlf TEMP,f ; Die Bits viermal nach links verschieben rlf TEMP,f rlf TEMP,f rlf TEMP,w andlw B'01110000' ; andere Bits (Müll) ausblenden xorlw B'01110000' ; Bits umdrehen, weil die oberen drei Adreßbits invertiert sind iorlw B'10001000' ; Diese Bits sind Standard movwf EADR1 ; Obere 3 Bits für rotes Licht speichern iorlw B'00000001' ; die letzte 1 wird nur für grünes Licht benötigt movwf EADR2 ; Pbere 3 Bits für grünes Licht speichern; movlw ADRESSEL ; Einlesen der unteren 8 Adressbits für Licht 1 movwf EETADR call readEE ; aus dem EEPROM lesen addlw D'3' ; Add 3 to address because bitfields start at address "4" which is "1" for Lenz movwf TEMP rlf TEMP,w ; nach links schieben andlw B'00000110' ; nur die untersten 2 Adressbits speichern iorwf EADR1,f rlf TEMP,w ; nach links schieben andlw B'00000110' ; nur die untersten 2 Adressbits speichern iorwf EADR2,f rrf TEMP,f ; die unteren zwei Adreßbits rausschieben rrf TEMP,w andlw B'00111111' ; ggf. hereingeschobene Bits löschen iorlw B'10000000' ; oben eine "1" setzen movwf EOFFS ; dies ist der zu empfangende Offset für Licht 1 ; clear RAM now movlw 0x7 ; bottom of RAM movwf FSR movlw nopreamb ; Preamble = 20 half bits movwf PRECOUNT clrf TIMEOUTCOUNT ; clear packet time out ;****************************************************** ; call master init ; Laden der default-Werte MasterInit: hard_reset_jump: clrf STATE clrf ENDVAL clrf DATA1 clrf DATA2 clrf DATA3 clrf DATA4 clrf DATA5 clrf WHOIS ; den Timer so setzen, daß er alle 64us inkrementiert movlw B'11000101' bsf STATUS,RP0 movwf OPTION_REG bcf STATUS,RP0 goto starthi org 0xA0 ;****************************************************** ; Main loop is here. Track is sampled every 40us the first time and then ; every 14us. Therefore: ; 0-2 samples (26-81) -> one received ; > 2 samples (69-...) -> zero received starthi: call Value conthi: btfss GPIO,gp3 goto startlo btfss SAMPLES,7 incf SAMPLES,F bcf CONFIG,bitrec bcf STATUS,C movlw 0xFC addwf SAMPLES,W btfss STATUS,C bsf CONFIG,bitrec nop nop goto conthi startlo: call Value contlo: btfsc GPIO,gp3 goto starthi btfss SAMPLES,7 incf SAMPLES,F bcf CONFIG,bitrec bcf STATUS,C movlw 0xFC addwf SAMPLES,W btfss STATUS,C bsf CONFIG,bitrec nop nop goto contlo ;****************************************************** nosample: ; movlw nopreamb ; movwf PRECOUNT ; clrf STATE ; clrf SAMPLES ; clrf ENDVAL ; return ;****************************************************** ; 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 andwf CONFIG,F retlw 0 ;****************************************************** ; waitn waits for 20 half bits of the preamble waitn: nop nop nop btfss CONFIG,bitrec ; there are only ones in preamble goto waitn1 decfsz PRECOUNT,F goto ret4 movlw nopreamb movwf PRECOUNT incf STATE,F retlw 0 ; 9 cycl. ret4: nop nop retlw 0 ; 9 cycl. waitn1: movlw nopreamb movwf PRECOUNT nop nop retlw 0 ;****************************************************** ; waitlo waits for low half bit after preamble waitlo: ; decfsz TIMEOUTCOUNT,F nop goto waitlo1 ; goto dccsetup waitlo1: nop nop nop nop btfsc CONFIG,bitrec ; must be a zero goto ret5 ; might be long preamble incf STATE,F ret5: retlw 0 ; 9 cycl ;****************************************************** ; testlo test second half bit of start bit testlo: nop nop nop nop nop nop nop btfsc CONFIG,bitrec ; must be a zero goto frameerr incf STATE,F retlw 0 ; 9 cycl ;****************************************************** ; bitset takes first half bit of a bit in the byte bitset: incf STATE,F bcf STATUS,C btfsc CONFIG,bitrec bsf STATUS,C rlf DATA5,F nop nop nop nop nop retlw 0 ; 9 cycl. ;****************************************************** ; lastv checks that first half bit = second half bit lastv: nop nop nop nop incf STATE,F btfss CONFIG,bitrec goto lastv1 btfss DATA5,0 goto frameerr ; error if not equal nop retlw 0 ; 9 cycl. lastv1: btfsc DATA5,0 goto frameerr ; error if not equal retlw 0 ; 9 cycl. ;****************************************************** ; lastx checks that first half bit = second half bit ; and sets offset to byte check routine lastx: incf ENDVAL,F movf ENDVAL,W addwf STATE,F nop nop btfss CONFIG,bitrec goto lastx1 btfss DATA5,0 goto frameerr ; error if not equal nop retlw 0 ; 9 cycl. lastx1: btfsc DATA5,0 goto frameerr ; error if not equal retlw 0 ; 9 cycl. ;****************************************************** ; end11 end of first byte there must be zero ; end11 first half bit. end12 second half bit end11: btfsc CONFIG,bitrec goto frameerr incf STATE,F incf ENDVAL,F nop nop nop nop nop nop retlw 0 ; 9 cycl. end12: btfsc CONFIG,bitrec goto frameerr movf DATA5,W movwf DATA1 movlw 0x03 ; point STATE to beginning of jump table movwf STATE nop nop nop nop retlw 0 ; 9 cycl. ;****************************************************** ; end21 end of second byte there must be zero ; end22 first half bit. end22 second half bit end21: btfsc CONFIG,bitrec goto frameerr incf STATE,F incf ENDVAL,F nop nop nop nop nop nop retlw 0 ; 9 cycl. end22: btfsc CONFIG,bitrec goto frameerr movf DATA5,W movwf DATA2 movlw 0x03 ; point STATE to beginning of jump table movwf STATE nop nop nop nop retlw 0 ; 9 cycl. ;****************************************************** ; end31 end of third byte there must be a one ; end32 first half bit. end32 second half bit end31: incf STATE,F incf ENDVAL,F nop nop nop nop nop nop nop nop retlw 0 ; 9 cycl. end32: movf DATA5,W movwf DATA3 btfsc CONFIG,bitrec ; if 0 other bytes will follow goto end32x movlw 0x03 ; point STATE to beginning of jump table movwf STATE nop nop nop nop retlw 0 end32x: clrf STATE ; reset STATE for next preamble clrf ENDVAL clrf DATA4 clrf DATA5 goto decode ;****************************************************** ; end41 end of third byte there must be a one ; end42 first half bit. end42 second half bit end41: incf STATE,F incf ENDVAL,F nop nop nop nop nop nop nop nop retlw 0 ; 9 cycl. end42: movf DATA5,W movwf DATA4 btfsc CONFIG,bitrec ; if 0 other bytes will follow goto end42x movlw 0x03 ; point STATE to beginning of jump table movwf STATE nop nop nop nop retlw 0 end42x: clrf STATE ; reset STATE for next preamble clrf ENDVAL clrf DATA5 goto decode ;****************************************************** ; end51 end of third byte there must be a one ; end52 first half bit. end52 second half bit end51: btfss CONFIG,bitrec goto frameerr incf STATE,F incf ENDVAL,F nop nop nop nop nop nop retlw 0 ; 9 cycl. end52: btfss CONFIG,bitrec goto frameerr clrf STATE ; reset STATE for next preamble clrf ENDVAL ;****************************************************** ; now we can start decoding the package ;***************************************************************** ; Consist Group ConsistGroup: bcf CONFIG,CVaccessBit ; clear CVaccess flag bcf CONFIG,resetflag ; Clear resetflag movf DATA1,W btfsc STATUS,Z ; If not zero then hard reset packet goto soft_reset hard_reset: goto hard_reset_jump soft_reset: bsf CONFIG,resetflag ; set resetflag for SM mode detection ; clrf PWM ; clrf SPEEDNEW ; bsf GPIO,gp1 ; turn off motor ; bsf GPIO,gp2 exit_consist: goto exit_bank1 ;***************************************************************** exit_bank1: clrf TEMP clrf DATA1 clrf DATA2 clrf DATA3 clrf DATA4 clrf DATA5 clrf ENDVAL goto starthi ; we dont return, we goto decode: ;*************DECODING******************************* decodebank1: xorlw 0xFF movf DATA1,W ; Exclusive or check xorwf DATA2,W xorwf DATA3,W xorwf DATA4,W xorwf DATA5,W btfss STATUS,Z goto exit_bank1 ; error in packet clrf TIMEOUTCOUNT ; reset packet time out adressdecode: movf EOFFS,W xorwf DATA1,W btfsc STATUS,Z goto DecodeAcc ; For us! goto exit_bank1 ; Not for us ; Decodieren des Accessory-Befehls DecodeAcc: movf EADR1,W xorwf DATA2,W btfss STATUS,Z goto DecAcc1 ; Ist nicht der erste Ausgang, vielleicht der zweite? movlw B'11111110' ; nur die rote LED ein bsf STATUS,RP0 movwf TRISIO bcf STATUS,RP0 movlw B'00000000' movwf GPIO movlw EEZUSTAND ; Zustand im EEPROM speichern movwf EETADR movlw B'11111110' call writeEE goto exit_bank1 DecAcc1: movf EADR2,W ; Vergleich mit Daten für zweites Byte xorwf DATA2,W btfss STATUS,Z goto exit_bank1 ; Auch nicht gleich zweitem Byte movlw B'11111101' ; nur die grüne LED an bsf STATUS,RP0 movwf TRISIO bcf STATUS,RP0 movlw B'00000000' movwf GPIO movlw EEZUSTAND ; Zustand im EEPROM speichern movwf EETADR movlw B'11111101' call writeEE goto exit_bank1 ; Read from the EEPROM into W from EEPROM address readEE: movf EETADR,W bsf STATUS,RP0 movwf EEADR bsf EECON1,RD movf EEDAT,W bcf STATUS,RP0 return ; Writing whatever is in the W to the EEPROM at address EETADR. If the address is >0, no write ; occurs (this ensures that no errenous writes can occur due to an invalid EEPROM address, ; maybe erasing the NMRA-address writeEE: bsf STATUS,RP0 movwf EEDAT bcf STATUS,RP0 movf EETADR,w sublw D'1' btfss STATUS,C ; Carry is set if no borrow, i.e. if EETADR greater than 0 return ; Address is equal to or greater 1, DO NOT WRITE! movf EETADR,w bsf STATUS,RP0 movwf EEADR bsf EECON1,WREN movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR bcf STATUS,RP0 return ; Hier kommt an eine der letzten Stellen ein Sprung an die Setup-Adresse. ; Nur für den Fall, daß sich der Program Counter verlaufen hat org 0x3FC bcf STATUS,5 ; Auf Bank 0 wechseln goto dccsetup ; zur Initialisierung ;#include 12c7.eep ; load eeprom routines ; /** org 0x2100 EEZUSTAND de B'11111110' ; State represents the value of the TRISIO register ; In this case, the red LED is turned on, i.e. the state ; is Hp0/Sh0 when the signal is new... ADRESSEH de 0 ADRESSEL de D'3' END