;******************************************************************************************************************** ;* T24 Lastgeregelter DCC dekoder 8MHz int RC 060209 * ;* * ;* ************ *********** * ;* 1 * VCC *** GND * 14 * ;* Motor Richtung 2 * PB0 PA0 * 13 Generator ADC * ;* Motor PWM 3 * PB1 PA1 * 12 F1 * ;* Reset 4 * Reset PA2 * 11 F2 * ;* DCC Eingang 5 * PB2/INT0 PA3 * 10 F3 * ;* ADC Schalter 6 * PA7 PA4 * 9 F4 * ;* Licht vorwärts 7 * PA6 PA5 * 08 Licht rückwärts * ;* ************************** * ;* * ;* * ;* * ;* * ;* * ;* * ;* * ;* * ;******************************************************************************************************************** .include "tn24def.inc" ;******************************** ;* FUSE info: für 8MHz int RC * ;* * ;* SELFPRGEN: nein(1) * ;* RSTDISBL: nein(1) * ;RESET Pin RESET Funktion ausgeschaltet ;* DWEN: nein(1) * ;Debug eingeschaltet ;* WDTON: nein(1) * ;WDT beim Start an ;* SPIEN: ja(0) * ;serial programmieren erlaubt ;* EESAVE: nein(1) * ;eeprom wird bei chiperase auch gelöscht ;* BODLEVEL: 100 * ;BOD bei ca 4,3V VCC ;* SUT1: ja(0) * ;SUT 00 = BOD ein ;* SUT0: ja(0) * ;* CKDIV8: nein(1) * ;kein CK vorteiler ;* CKOUT: nein(1) * ;PB2 CK out ;* CKSEL3: ja(0) * ;CKSEL 0010 = int 8 MHz RC ;* CKSEL2: nein(1) * ;* CKSEL1: ja(0) * ;* CKSEL0: ja(0) * ;******************************** ;************************************************************************************************ ;* Konstantendef. * ;************************************************************************************************ ;Ports .equ MotR = 0 ; PB0 Motor Richtung Ausgang .equ MotPWM = 1 ; PB1 Motor PWM Ausgang .equ LichtV = 6 ; PA6 Licht vorwärts .equ LichtR = 5 ; PA5 Licht rückwärts .equ F1 = 1 ; PA1 Funktionsausgang 1 .equ F2 = 2 ; PA2 Funktionsausgang 2 .equ F3 = 3 ; PA3 Funktionsausgang 3 .equ F4 = 4 ; PA4 Funktionsausgang 4 .equ F5 = 7 ; PA7 Funktionsausgang 5 .equ fosc = 8000000 ; Systemtakt in Hz ;Flagregister .equ resetbit = 0 ; neues Byte abgelegt .equ frei1 = 1 .equ frei2 = 2 .equ frei3 = 3 .equ bit = 4 .equ neuesbit = 5 .equ ctcein = 6 ; zum wechseln der Low/High zeit im Timer1 ctc interrupt .equ keinDCC = 7 ; für analogbetrieb hier nicht aktiv ;DCCregister .equ Richtung_inv= 0 .equ Fahrstufen = 1 .equ analog = 2 .equ Vmerken = 3 .equ nutzeKennl = 4 .equ FL = 5 .equ erwAdresse = 6 .equ Richtung = 7 ;************************************************************************************************ ;* Registerdef. * ;************************************************************************************************ .def Adresse = r1 ; eigene Adresse bzw bei langen Adressen der high wert .def V_IST = r2 ; momentane Geschwindigkeit .def V_SOLL = r3 ; Soll Geschwindigkeit .def PWMein = r4 ; zeit in der motor pwm high (low=255-PWMein) .def reg5 = r5 ; Register .def reg6 = r6 ; Register .def reg7 = r7 ; Register .def reg8 = r8 ; Register .def reg9 = r9 ; Register .def byteA = r10 ; Empfangsbytespeicher .def byteB = r11 .def byteC = r12 .def byteD = r13 .def byteE = r14 .def byteF = r15 .def bitzaehler = r16 ; Bitzaehler beim speichern .def w = r17 ; Arbeitsregister .def flag = r18 ; Flagregister ; Bit0 = 1 -> resetbit ; Bit1 = 1 -> frei ; Bit2 = 1 -> frei ; Bit3 = 1 -> Vsoll im EEProm merken ; Bit4 = 1 -> das neue bit ; Bit5 = 1 -> neues bit da ; Bit6 = 1 -> PWM Pin ein im ctc interrupt ; Bit7 = 1 -> länger als 10000us keine bits eingetroffen .def DCCReg = r19 ; DCC Register für Funktionen und Fahrtrichtung ; Bit7 = 1 -> 28 Fahrstufen (0 = 14 Fahrstufen) ; Bit6 = 1 -> Fahrtrichtung reversiert ; Bit5 Fahrtrichtung 1=vorwärts, 0=rückwärts ; Bit4 ; Bit3 ; Bit2 ; Bit1 ; Bit0 FL Licht ein/aus .def Lowpegel = r20 ; Speicher für Lowpegel .def Highpegel = r21 ; Speicher für Highpegel .def eead = r22 ; eepromaddresse .def reg23 = r23 ; Register .def reg24 = r24 ; Register .def reg25 = r25 ; Register ;.def xl = r26 ; Register ;.def xh = r27 ; Register ;.def yl = r28 ; Register ;.def yh = r29 ; Register ;.def zl = r30 ; zum speichern der ADC Werte im Ram ;.def zh = r31 ; ;******************************** ;* Daten def. * ;******************************** .DSEG Generator: .byte 8 ; 8 Byte für die letzten ADC Werte reservieren AdresseLow: .byte 1 ; für lange Adresse LowByte 1 Byte im SRAM reservieren V_start: .byte 1 ; für V_start 1 Byte im SRAM reservieren IncWert: .byte 1 ; für Beschleunigungswert 1 Byte im SRAM reservieren DecWert: .byte 1 ; für Verzögerungswert 1 Byte im SRAM reservieren V_max: .byte 1 ; für V_max 1 Byte im SRAM reservieren V_mitte: .byte 1 ; für V_mitte 1 Byte im SRAM reservieren Dimmer: .byte 1 ; für Licht - Dimmer 1 Byte im SRAM reservieren Kickstart: .byte 1 ; für Kickstart 1 Byte reservieren Kennline: .byte 28; für Fahrstufen Kennline 28 Byte reservieren ;************************************************************************************************ ;* ab hier Programmcode * ;************************************************************************************************ .CSEG ;************************************ ;* Reset- und Interrupt-Vektoren * ;************************************ .ORG $0000 rjmp RESET ; Reset Handler reti; EXT_INT0 ; IRQ0 Handler reti; PCINT0 ; PCINT0 Handler reti; PCINT1 ; PCINT1 Handler reti; WATCHDOG ; Watchdog Interrupt Handler reti; TIM1_CAPT ; Timer1 Capture Handler rjmp TIM1_COMPA ; Timer1 CompareA Handler reti; TIM1_COMPB ; Timer1 CompareB Handler reti; TIM1_OVF ; Timer1 Overflow Handler reti; TIM0_COMPA ; Timer0 Compare A Handler reti; TIM0_COMPB ; Timer0 Compare B Handler rjmp TIM0_OVF ; Timer0 Overflow Handler reti; ANA_COMP ; Analog Comparator Handler rjmp ADC_fertig ; ADC Conversion Handler reti; EE_RDY ; EEPROM Ready Handler reti; USI_STR ; USI STart Handler reti; USI_OVF ; USI Overflow Handler ;************************************************************************************************ ;* Interruptprogramme * ;************************************************************************************************ ;************************************ ;* Timer_0_interrupt * ;************************************ ; wird ca 10us ausgelöst und liest dann bit von int0/d2 ein ; dazu teiler auf1 (8tackte=1us) -> 10us=80-9 Tackte bis zum neuladen von tcnt TIM0_OVF: push w ; w sichern in w,sreg ; sreg holen push w ; und sichern ldi w,184 ; Zähler auf 184 neu laden out tcnt0,w ; in w,pinb ; port einlesen sbrc w,2 ; wenn empfangenes Bit=0 1x springen inc Highpegel ; sonst Highpegel inc sbrs w,2 ; wenn empfangenes Bit=1 1x springen inc Lowpegel ; sonst Lowpegel inc cpi Lowpegel,3 ; wenn mindestens 3x Low eingetroffen brne PC+2 ; dann 1x springe clr Highpegel ; sonst erst Highpegel löschen cpi Highpegel,3 ; ist jetzt Highpegel = 3 brne TIM0_OVF_ENDE ; wenn ungleich, dann fertig cbr flag,(1< 7812,5 Tackte/sec. /256 = 30,5 Hz PWM Frequenz ; Teiler 256 -> 31250 Tackte/sec. /256 = 122 Hz PWM Frequenz ; Teiler 64 -> 125000 Tackte/sec. /256 = 488 Hz PWM Frequenz ; Teiler 8 -> 1000000 Tackte/sec. /256 = 3906 Hz PWM Frequenz -> GEHT MIT 125 kHz ADC TACKT NICHT MEHR ! ; dabei steht die steht die Zeit wo der Impuls an ist in "PWMein" ; es wird das Compare Register im Wechsel mit der Low oder der High Zeit geladen ; vor den setzen des Impuls (high Zeit) wird erst noch der AD Wandler gestartet um die aktuelle ; Generatorspannung einzulesen, die Wandlung dauert ca. 960 cpu Tackte, danach setzt der ADC Interrupt ; den PWMImpulse dadurch wird der erste (von 256 möglichen) PWM Schritten ca 12% kürzer TIM1_COMPA: push w ; w sichern in w,sreg ; sreg holen push w ; und sichern sbrs flag,ctcein ; wenn PWM Pin ein war 1x springen rjmp PWM_Pin_war_aus PWM_Pin_war_ein: mov w,PWMein ; PWM Low Zeit ermitteln com w ; 255-PWMein bzw com PWMein out OCR1AL,w ; und Compare Register setzen cbi portb,MotPWM ; Motor PWM Pin ausschalten tst PWMein ; ist PWMein = 0 breq TIM1_COMPA_ende ; wenn gleich gehe zum ende -> beim nächsten Aufruf wird PWM nicht eingeschaltet cbr flag,((1< beim nächsten Aufruf wird PWM eingeschaltet rjmp TIM1_COMPA_ende ; gehe zu.. PWM_Pin_war_aus: out OCR1AL,PWMein ; Compare Register setzen sbi ADCSRA,ADSC ; ADC Wandlung starten sbr flag,((1< beim nächsten Aufruf wird PWM ausgeschaltet TIM1_COMPA_ende: pop w ; sreg widerherstellen out sreg,w ; pop w ; w widerherstellen reti ; und fertig ;************************************ ;* ADC Conversion Complete * ;************************************ ; AD Wandlung fertig, dauert ca 960 cpu Tackte ; die Wandlung wird unmittelbar vor dem Einschalten des PWM Impuls durchgeführt damit ; hat die Generatorspannung nach dem Abschalten des letzten Impulse ausreichend Zeit sich zu beruhigen ADC_fertig: push w ; w sichern in w,sreg ; sreg holen push w ; und sichern sbi portb,MotPWM ; zuerst Motor PWM Pin einschalten ; dann neue Generatorspannung holen ; in w, ADCL ; immer zuerst LOW Byte lesen (bei neuen AVRs nicht nötig) in w, ADCH ; danach das mittlerweile gesperrte High Byte ; beides in w einlesen, da nur 8 bit nötig st z+,w ; wert im ram ablegen cpi zl,low(generator+8) ; zeiger nach 8 wandlungen wieder zurücksetzen ; (zh wird bei 128 Byte Ram nicht verändert) brne PC+2 ; wenn ungleich 1x springen ldi zl,low(generator) ; ansonsten wieder auf ersten speicherplatz pop w ; sreg widerherstellen out sreg,w ; pop w ; w widerherstellen reti ; und fertig ;************************************************************************************************ ;* Unterprogramme * ;************************************************************************************************ ;************************************ ;* EEprom byte lesen * ;************************************ ;liest ein Byte aus Addresse eead in w ein zum CVregister lesen eepromlesen: sbic EECR,EEPE ; wird noch geschrieben ? EECR Bit1(EEPE)=1 rjmp PC-1 ; dann Schleife out EEARL,eead ; eeprom addresse low setzen bei kleinen eeprom eear bei großem eearl sbi EECR,EERE ; Setze EEProm-Lesebefehl EECR Bit0(EERE)=1 in w,EEDR ; daten einlesen ret ;************************************ ;* EEprom byte schreiben * ;************************************ ;schreibt ein Byte aus w in Addresse eead zum CVregister schreiben eepromschreiben: sbic EECR,EEPE ; wird noch geschrieben ? EECR Bit1(EEPE)=1 rjmp PC-1 ; dann Schleife out EEARL,eead ; Adresse setzen out EEDR,w ; Daten setzen ldi w,(0< beim nächsten Aufruf von timer1 wird PWM ausgeschaltet rjmp Hauptschleife erw_Operation: rjmp Hauptschleife ;************************************ ;* Funktionsgruppe1 setzen * ;************************************ ; hier werden neue Funktionsbefehle gesetzt ; die neuen Daten kommen in w hier an Fgruppe1: ; Befehle für FG1 in F1 bis F4 setzen cbi porta,F1 ; F1 ausschalten sbrc w,0 ; wenn F1 aus 1x springen sbi porta,F1 ; wenn an, Port wieder einschalten cbi porta,F2 ; F2 ausschalten sbrc w,1 ; wenn F2 aus 1x springen sbi porta,F2 ; wenn an, Port wieder einschalten cbi porta,F3 ; F3 ausschalten sbrc w,2 ; wenn F3 aus 1x springen sbi porta,F3 ; wenn an, Port wieder einschalten cbi porta,F4 ; F4 ausschalten sbrc w,3 ; wenn F3 aus 1x springen sbi porta,F4 ; wenn an, Port wieder einschalten sbrs DCCReg,fahrstufen ; 1=28 Fahrstufen 1x springen rjmp Hauptschleife ; bei 14 fertig bst w,4 ; bei 28 noch Lichtbit sichern bld dccreg,FL rjmp Hauptschleife ; und dann zurück ;************************************ ;* CV Manipulation setzen * ;************************************ ;hier werden neue CV Manipulationsbefehle gesetzt CV_edit: rjmp Hauptschleife ;************************************ ;* CV Progmodus * ;************************************ ;hier CV Progmodus bearbeiten progmode: cpi w,0b01111100 ; prüfe Funktionscode auf CV Write Byte Schreiben breq PC+3 ; wenn richtig 2x springen cbr flag,(1< beim nächsten Aufruf von timer1 wird PWM ausgeschaltet clr PWMein ldi zl,LOW(Generator) ; den Z-Pointer mit dem Start der Generator Bytes laden ldi zh,HIGH(Generator) ;* Timer0 auf 10us Overflow Interrupt setzen * ; Timer/Counter Control Register A ldi w,0b00000000 ; +------+------+------+------+------+------+------+------+ out TCCR0A,w ; |COM0A1|COM0A0|COM0B1|COM0B0| - | - | WGM01| WGM00| ; +------+------+------+------+------+------+------+------+ ; Bits 7/6 COM0A1/0: Compare Match Output A Mode ; Bits 5/4 COM0B1/0: Compare Match Output B Mode ; Bits 1/0 WGM01/0: Waveform Generation Mode ; Timer/Counter0 Control Register B ; timer mit Teiler auf ck/1 starten ldi w,0b00000001 ; +------+------+------+------+------+------+------+------+ out TCCR0B,w ; |FOC0A |FOC0B | - | - |WGM02 | CS02 | CS01 | CS00 | ; +------+------+------+------+------+------+------+------+ ; Bit 7 FOC0A: Force Output Compare A ; Bit 6 FOC0B: Force Output Compare B ; Bit WGM02: Waveform Generation Mode ; Bits 2-0 CS02-0: Clock Select ; CS02 CS01 CS00 ; 0 0 0 No clock source (Timer/Counter stopped) ; 0 0 1 clkI/O/(No prescaling) ; 0 1 0 clkI/O/8 (From prescaler) ; 0 1 1 clkI/O/64 (From prescaler) ; 1 0 0 clkI/O/256 (From prescaler) ; 1 0 1 clkI/O/1024 (From prescaler) ; 1 1 0 External clock source on T0 pin. Clock on falling edge. ; 1 1 1 External clock source on T0 pin. Clock on rising edge. ; Timer/Counter0 Register ; Zähler setzen (255-80)= 10us ldi w,175 out tcnt0,w ; Timer/Counter0 Interrupt Mask Register TIMSK0: ldi w,0b00000001 ; +------+------+------+------+------+------+------+------+ out TIMSK0,w ; | - | - | - | - | - |OCIE0B|OCIE0A| TOIE0| ; +------+------+------+------+------+------+------+------+ ; OCIE0B: Timer/Counter0, Output Compare B Match Interrupt Enable ; OCIE0A: Timer/Counter0, Output Compare A Match Interrupt Enable ; TOIE0: Timer/Counter0, Overflow Interrupt Enable ; Timer/Counter0 Interrupt Flag Register TIFR0: lösche Timer0 Interupt ldi w,0b00000001 ; +------+------+------+------+------+------+------+------+ out TIFR0,w ; | - | - | - | - | - |OCF0B |OCF0A | TOV0 | ; +------+------+------+------+------+------+------+------+ ; OCF0B: Timer/Counter, Output Compare B Match Flag ; OCF0A: Timer/Counter0, Output Compare A Match Flag ; TOV0: Timer/Counter0, Overflow Flag ;* Timer1 auf CTC Interrupt setzen * ; Timer/Counter Control Register TCCR1A ldi w,0b00000000 ; +------+------+------+------+------+------+------+------+ out TCCR0A,w ; |COM1A1|COM1A0|COM1B1|COM1B0| - | - | WGM11| WGM10| ; +------+------+------+------+------+------+------+------+ ; Bits 7/6 COM1A1/0: Compare Match Output A Mode ; Bits 5/4 COM1B1/0: Compare Match Output B Mode ; Bits 1/0 WGM11/0: Waveform Generation Mode ; Timer/Counter Control Register TCCR1B ; timer auf mode4 wgm12=1 ctc setzen ; Teiler 1024 -> 7812,5 Tackte/sec. /256 = 30,5 Hz PWM Frequenz ; Teiler 256 -> 31250 Tackte/sec. /256 = 122 Hz PWM Frequenz ; Teiler 64 -> 125000 Tackte/sec. /256 = 488 Hz PWM Frequenz ; Teiler 8 -> 1000000 Tackte/sec. /256 = 3906 Hz PWM Frequenz ldi w,0b00001100 ; +------+------+------+------+------+------+------+------+ out TCCR1B,w ; |ICNC1 |ICES1 | - | WGM13|WGM12 | CS12 | CS11 | CS10 | ; +------+------+------+------+------+------+------+------+ ; Bit 7 ICNC1: Input Capture Noise Canceler ; Bit 6 ICES1: Input Capture Edge Select ; Bits 4/3 WGM12/13: Waveform Generation Mode ; Bits 2-0 CS12-0: Clock Select ; CS12 CS11 CS10 ; 0 0 0 No clock source (Timer/Counter stopped) ; 0 0 1 clkI/O/(No prescaling) ; 0 1 0 clkI/O/8 (From prescaler) ; 0 1 1 clkI/O/64 (From prescaler) ; 1 0 0 clkI/O/256 (From prescaler) ; 1 0 1 clkI/O/1024 (From prescaler) ; 1 1 0 External clock source on T1 pin. Clock on falling edge. ; 1 1 1 External clock source on T1 pin. Clock on rising edge. ; Timer/Counter Control Register TCCR1C ldi w,0b00000000 ; +------+------+------+------+------+------+------+------+ out TCCR1C,w ; |FOC1A |FOC1B | - | - | - | - | - | - | ; +------+------+------+------+------+------+------+------+ ; Bit 7 FOC1A: Force Output Compare A ; Bit 6 FOC1B: Force Output Compare B ; Timer/Counter Register löschen clr w out TCNT1H,w out TCNT1L,w ; Output Compare Register OCR1AH and OCR1AL ; mit PWM Low zeit laden (bei stop $00FF) und Flag auf CTCein löschen ;cbr Flag,(1<ca 15 Tackte jede Wandlung * 64 = 960 cpu Tackte = eine Wandlung ldi w,0b10001110 ; +------+------+------+------+------+------+------+------+ out ADCSRA,w ; | ADEN | ADSC |ADATE | ADIF | ADIE |ADPS2 |ADPS1 |ADPS0 | ; +------+------+------+------+------+------+------+------+ ; Bit 7 ADEN: 1 = ADC Enable ; Bit 6 ADSC: 1 = ADC Start Conversion ; Bit 5 ADATE: 1 = ADC Auto Trigger Enable ; Bit 4 ADIF: ADC Interrupt Flag 1 = Wandlung fertig ; Bit 3 ADIE: ADC Interrupt Enable ; Bits 2-0 ADPS2-0: ADC Prescaler Select Bits ; ADPS2 ADPS1 ADPS0 Division Factor ; 0 0 0 2 ; 0 0 1 2 ; 0 1 0 4 ; 0 1 1 8 ; 1 0 0 16 ; 1 0 1 32 ; 1 1 0 64 ; 1 1 1 128 ; ADC Control and Status Register B ADCSRB: Ergebnis linksbündig in ADCH ldi w,0b00010000 ; +------+------+------+------+------+------+------+------+ out ADCSRB,w ; | BIN | ACME | - | ADLAR| - | ADTS2| ADTS1|ADTS0 | ; +------+------+------+------+------+------+------+------+ ; Bit 7 BIN: Bipolar Input Mode = 1 ; Bit 6 ACME: Analog Comparator Multiplexer Enable ; Bit 4 ADLAR: ADC Left Adjust Result = 1 ; Bits 2-0 ADTS2-0: ADC Auto Trigger Source ; ADTS2 ADTS1 ADTS0 Trigger Source ; 0 0 0 Free Running mode ; 0 0 1 Analog Comparator ; 0 1 0 External Interrupt Request 0 ; 0 1 1 Timer/Counter0 Compare Match A ; 1 0 0 Timer/Counter0 Overflow ; 1 0 1 Timer/Counter1 Compare Match B ; 1 1 0 Timer/Counter1 Overflow ; 1 1 1 Timer/Counter1 Capture Event ; Digital Input Disable Register 0 DIDR0: ldi w,0b00000001 ; +------+------+------+------+------+------+------+------+ out DIDR0,w ; | ADC7D|ADC6D |ADC5D |ADC4D |ADC3D |ADC2D |ADC1D |ADC0D | ; +------+------+------+------+------+------+------+------+ ; Bits 7-0 ADC7D-ADC0D: ADC7-0 Digital Input Disable sei ; Interrupts einschalten ; ab hier normaler Dekoderschleife Hauptschleife: clr Adresse inc Adresse sbr DCCreg,(1< nichts machen bei diesem befehl) rjmp Hauptschleife cp byteA,Adresse ; wenn eigene Adresse breq an_mich ; dann test OK rjmp Hauptschleife ; sonst Hauptschleife an_alle: ; alle Decoder sind angesprochen an_mich: ; eigene Adresse ist angesprochen ;hier fehlt noch auf lange Adresse testen und verzweigen ;hier je nach Datenformat verzweigen mov w,byteB ; Daten sichern cpi w,0b00000000 ; Vergleich auf dcc reset brne PC+2 ; wenn ungleich 1x springen rjmp Reset ; sonst Reset machen andi w,0b11100000 ; Datenbits ausblenden cpi w,0b00100000 ; Vergleiche auf erweiterte Operation (enthält befehl für 128 Fahrstufen) brne PC+2 ; wenn ungleich 1x springen rjmp erw_Operation ; wenn gleich gehe zu.. cpi w,0b01000000 ; Vergleiche mit V Rückwärts einfaches Format brne PC+2 ; wenn ungleich 1x springen rjmp V_Calc ; wenn ja gehe zu cpi w,0b01100000 ; Vergleiche mit V Vorwärts einfaches Format brne PC+2 ; wenn ungleich 1x springen rjmp V_Calc ; wenn ja gehe zu.. cpi w,0b10000000 ; Vergleiche mit Funktionsgruppe F1-F4 brne PC+3 ; wenn ungleich 2x springen mov w,byteB ; w mit Funktionsdaten laden rjmp Fgruppe1 ; und dann gehe zu.. cpi w,0b11100000 ; Vergleich auf CV reg Manipulation brne PC+2 ; wenn ungleich 1x springen rjmp CV_edit ; wenn gleich gehe zu.. ; alles andere wird nicht unterstüzt -> verwerfen und zurück rjmp Hauptschleife ; Endlosschleife