Pfad: Home => AVR-Übersicht => Anwendungen => Schranke => Quellcode
Schranke

Quellcode für die Schrankensteuerung mit ATtiny24



;
; **********************************************
; * Schrankensteuerung mit ATtiny24            *
; * V1, 19.07.2014                             *
; * (C)2014 by http://www.avr-asm-tutorial.net *
; **********************************************
;
; Include-Datei fuer den betreffenden AVR-Typ
.NOLIST
.INCLUDE "tn24def.inc" ; Headerdatei fuer ATTINY24
.LIST
;
; Debugschalter (fertig: alle gleich Null)
.equ debugparam=0
;
; ============================================
;   H A R D W A R E I N F O R M A T I O N E N
; ============================================
;
;             ______________
;            /   ATtiny24   |
;      +U o--|VCC        GND|--o -U
;            |              |
;   + LED o--|PB0       ADC0|--o Unten
;            |              |
;   - LED o--|PB1       ADC1|--o Oben
;            |              |
;   RESET o--|RESET     ADC3|--o Geschwindigkeit
;            |              |
;     DWN o--|INT0       PA4|--o
;            |              |
;      UP o--|PCINT7    USC0|--o SCK
;            |              |
; PWM-SIG o--|OC1A/MOSI MISO|--o MISO
;  & MOSI    |______________|
;
;
; ============================================
;      P O R T S   U N D   P I N S
; ============================================
;
; Ports fuer Leuchtdiodensteuerung
.EQU pLedAus = PORTB
.EQU pLedIn = PINB
.EQU pLedDir = DDRB
.EQU bLedPlus = PB0
.EQU bLedMinus = PB1
; Ports fuer Tastensteuerung
.EQU pDwnOut = PORTB
.EQU pDwnDir = DDRB
.EQU pDwnIn = PINB
.EQU bDwn = PB2
.EQU pUpOut = PORTA
.EQU pUpDir = DDRA
.EQU pUpIn = PINA
.EQU bUp  = PA7
; Ports fuer PWM-Signal
.EQU pPwmOut = PORTA
.EQU pPwmIn = PINA
.EQU pPwmDir = DDRA
.EQU bPwm = PA6
;
; ================================================
; K O N S T A N T E N   Z U M  E I N S T E L L E N
; ================================================
;
.EQU cMinMin = 900 ; us unterste PWM-Dauer
.EQU cMaxMax = 2100 ; us oberste PWM-Dauer
.EQU cMinMed = 1000 ; us untere Regelgrenze
.EQU cMaxMed = 2000 ; us obere Regelgrenze
.EQU cMid = (cMaxMax+cMinMin) / 2 ; Mittelstellung
.EQU cPwm = 20000 ; us Gesamt-PWM-Dauer
.EQU cSpeedHigh = 1000000 ; us Dauer schnelles Schliessen
.EQU cSpeedLow = 7000000 ; us Dauer langsames Schliessen
;
; =======================================================
;  F E S T E + A B G E L E I T E T E  K O N S T A N T E N
; =======================================================
;
.EQU cMaxMin = cMaxMax - 511 ; Untergrenze oberste Stellung
.EQU cRundenSchnell = cSpeedHigh / cPwm
.EQU cRundenLangsam = cSpeedLow / cPwm
.EQU cAdderSchnell = (cMaxMed - cMinMed) / cRundenSchnell
.EQU cAdderLangsam = 1 ; langsamster Wert
.EQU cSpeedMulti = cAdderSchnell - cAdderLangsam
;
; =============================
;  P R O G R A M M A B L A U F
; =============================
;
; Alle Ablaeufe sind fuer eine Taktfrequenz von 1 MHz
; vorgesehen, d.h. der ATtiny24 laeuft mit seiner werksseitig
; voreingestellten Geschwindigkeit (8 MHz, CLKPR = 8).
;
; ADC-Werte
; Die Messung der Potis erfolgt mit einem Vorteiler von 128.
; Jeweils 64 Messungen werden in den Registern R0..R5
; aufsummiert und damit gemittelt.
; Aus dem ADC-Ablauf ergibt sich eine Messfrequenz von
; 1 MHz / 128 / 13 Takte pro Wandlung / 3 Kanaele / 64 Messungen
; = ca. 3 Hz.
; Die drei Messwerte werden folgendermassen umgerechnet:
; Poti 1: Summenwert / 128 + cMinMin(900) ==> rUnten (Minimumwert)
; Poti 2: cMaxMax(2100) - Summenwert / 128 ==> rOben (Maximalwert)
; Poti 3: (Summenwert / 256 * cSpeedMulti) / 256 + 1 ==> rDelta
;   (Geschwindigkeit), Wert variiert je nach Poti-Stellung zwischen
;   1 und 23 (1 entspricht 20 Sekunden Schliessdauer, 23 einer
;   Sekunde).
;
; Timer1-Ablauf (Erzeugung PWM-Signal)
; Der Timer TC1 wird ohne Vorteiler mit dem Systemtakt von 1 MHz
; getaktet, die Taktdauer des Timers betraegt daher 1 us.
; TC1 ist als CTC eingestellt und erzeugt das Ausgangssignal am
; Output Pin. Dieser kippt bei Erreichen des Compare-Wertes im
; Compare-Register A, der Timer wird daraufhin neu gestartet und
; ein Compare-Match Interrupt ausgeloest. Bei diesem wird abhaengig
; vom Zustand des Ausgangs-Pins abwechselnd die Zeitdauer im High-
; Zustand in rCtcAct bzw. die Zeitdauer im Low-Zustand in rCtcIna
; in das Compare-Register A geschrieben. Das Signal sieht so aus:
;
;       _______                   _______                   ______
; _____|       |_________________|       |_________________|
;       rCtcAct    rCtcIna        rCtcAct      rCtcIna
;      |<======= 20.000 us =====>|<======= 20.000 us =====>|
;
; rCtcAct = 900 .. 2.100 us
; Untergrenze mit Poti zwischen 900 und 1410 us einstellbar
; Obergrenze mit Poti zwischen 1590 und 2100 us einstellbar
; rCtcIna = 20.000 - rCtcAct
;
; rCtcAct plus rCtcIna zusammen ergeben immer 20.000 us, entsprechend
; 50 Hz. Beim Timer-Compare-Match-Interrupt nach Ruecksetzen des OCRA-
; signals (nach dem Schreiben von rCtcIna) wird die bPwm-Flagge gesetzt.
; Diese Flagge wird ausserhalb der Interrupt-Service-Routine ausgewertet
; und der Programmteil PwmRdy ausgefuehrt. Dieser unterscheidet zunaechst
; danach, ob sich die Schranke gerade bewegt oder nicht.
; Bei inaktiver Schranke werden noch 25 nachfolgende PWM-Signale mit
; der aktuellen Stellung abgegeben (0,5 s Nachlauf), dann werden die
; PWM-Signale abgeschaltet (Nullsetzen des Ausgangs-Pins bei Erreichen
; von Compare-Match).
; Bei aktiver Schranke wird die Dauer des PWM-Signals um den Betrag
; rDelta verlaengert (Schranke oeffnet sich) bzw. verkuerzt (Schranke
; schliesst sich). Ist beim Oeffnen die obere Grenze in rOben ueber-
; schritten wird die LED auf gruenes Dauerlicht geschaltet und der
; Nachlauf eingeleitet. Ist beim Schliessen die untere Grenze unter-
; schritten wird die LED auf rotes Dauerlicht geschaltet und der
; Nachlauf eingeleitet.
; Ist weder die Unter- noch die Obergrenze erreicht, blinkt die LED
; im 0,2-Hz-Rhythmus rot.
;
; Oeffnen/Schliessen einleiten
; Wird der Taster 1 geschlossen, wird ein INT0-Interrupt ausgeloest.
; Dieser loescht das Aufwaerts-Flag.
; Wird der Taster 2 geschlossen, wird ein PCINT0-Interrupt ausgeloest.
; Dieser setzt das Aufwaerts-Flag.
; Nach beiden Interrupts wird bei inaktivem Toggle-Bit der rCtcIna-
; Wert in das Compare-Register geschrieben, das Toggle-Bit fuer den
; Ausgang gesetzt, der Compare-Match-A-Interrupt enabled und das
; Schranke-Aktiv-Flag gesetzt.
;
; Justieren
; Wird die Stellung des Unten- oder Oben-Trimmers geaendert, wird der
; neue Wert unmittelbar angewendet. Findet waehrend der Aenderung kein
; aktiver Bewegungszyklus statt (bActive-Flagge aus), dann wird die
; Schranke nur dann nachgefuehrt, wenn sie ganz offen ist (letzte
; Schrankenbewegung nach oben) oder ganz zu ist (letzte Schrankenbewe-
; gung nach unten).
; Aenderungen bei der Geschwindigkeit machen sich nur bei Schranken-
; bewegungen bemerkbar.
;
; Startalgorithmus
; Zu Beginn wird die Schranke in die Mitte gestellt und dann ganz hoch-
; gefahren.
;
; ============================================
;   R E G I S T E R D E F I N I T I O N E N
; ============================================
;
; R0 .. R5 : ADC-Summenwerte
.DEF rCtcActL = R6 ; Aktive Signaldauer
.DEF rCtcActH = R7
.DEF rCtcInaL = R8 ; Inaktive Signaldauer
.DEF rCtcInaH = R9
.DEF rUntenL = R10 ; Untergrenze PWM-Signal
.DEF rUntenH = R11
.DEF rObenL = R12 ; Obergrenze PWM-Signal
.DEF rObenH = R13
.DEF rDelta = R14 ; Schrittlaenge fuer Schrankenbewegung
.DEF rSreg = R15 ; fuer SREG-Zwischenspeicherung
.DEF rmp = R16 ; Vielzweckregister
.DEF rimp = R17 ; Vielzweckregister bei Interrupts
.DEF rimp2 = R18 ; 16-Bit-Addierregister bei Interrupts
.DEF rAdcCnt = R19 ; Zaehler fuer ADC-Runden
.DEF rFlg = R20 ; Flag-Register
	.equ bActive = 0 ; Schranke faehrt
	.equ bHigh = 1 ; Schranke faehrt aufwaerts
	.equ bAdc = 2 ; ADC eine Runde durch, bearbeite Flagge
	.equ bPwmIna = 3 ; Inaktives Pwm-Signal, bearbeite Flagge
	.equ bOffen = 4 ; Schranke ist offen
	.equ bZu = 5 ; Schranke ist zu
	.equ bToggle = 6 ; CTC-Toggle ist aktiv
.DEF rPwmCtr = R21 ; Abschaltzaehler fuer PWM-Signal
; R22 .. R25 unbenutzt
; X = XH:XL Multiplikation fuer Geschwindigkeitsermittlung
; Y = YH:YL ADC-Zeiger in SRAM
; Z = ZH:ZL Vielzweck-Doppelregister ausserhalb Interrupts
; ============================================
;       S R A M   D E F I N I T I O N E N
; ============================================
;
.DSEG
.ORG  0X0060
sAdc: .Byte 6
;
; ==============================================
;   R E S E T   U N D   I N T   V E K T O R E N
; ==============================================
;
.CSEG
.ORG $0000
	rjmp Main ; Reset-Vektor
	rjmp IntDwn ; Int Vektor INT0
	rjmp IntUp ; Int Vektor PCINT0
	reti ; Int Vektor PCINT1
	reti ; Int Vektor WDT
	reti ; Int Vektor TC1 Capture
	rjmp IntPwmA ; Int Vektor TC1 Compare A
	reti ; Int Vektor TC1 Compare B
	reti ; Int Vektor TC1 OVF
	reti ; Int Vektor TC0 Compare A
	reti ; Int Vektor TC0 Compare B
	reti ; Int Vektor TC0 OVF
	reti ; Int Vektor Ana_Comp
	rjmp IntAdc ; Int Vektor ADC
	reti ; Int Vektor EEPROM Ready
	reti ; Int Vektor USI START
	reti ; Int Vektor USI Overflow
;
; ==========================================
;    I N T E R R U P T   S E R V I C E
; ==========================================
;
; Abwaerts-Taste
IntDwn:
	in rSreg,SREG ; SREG sichern
	cbr rFlg,1<<bHigh ; loesche Aufwaerts-Flagge
	rjmp IntUD
;
; Aufwaerts-Taste
IntUp:
	in rSreg,SREG ; SREG sichern
	sbic pUpIn,bUp ; abfallende Flanke?
	reti
	sbr rFlg,1<<bHigh ; setze Aufwaerts-Flagge
IntUD:
	; starte Bewegungszyklus
	sbrc rFlg,bActive ; schon aktiv?
	rjmp IntUD1 ; schon aktiv
	sbr rFlg,1<<bActive ; setze Schranke-Aktiv-Flagge
	sbrc rFlg,bToggle ; springe wenn nicht auf Toggle-Modus
	rjmp IntUD1 ; Toggle-Modus ist an
	ldi rPwmCtr,1 ; Blinkzaehler auf Anfang
	out OCR1AH,rCtcInaH ; Compare-Match-Inaktiv-Dauer
	out OCR1AL,rCtcInaL
	ldi rimp,1<<COM1A0 ; Toggle einschalten
	out TCCR1A,rimp
	ldi rimp,1<<OCIE1A ; Interrupt einschalten
	out TIMSK1,rimp
	sbr rFlg,1<<bToggle
IntUD1:
	out SREG,rSreg ; SREG wiederherstellen
	reti
;
; TC1 Compare-Match-A Interrupt-Service-Routine
IntPwmA:
	in rSreg,SREG ; rette SREG
	sbic pPwmIn,bPwm ; springe bei PWM-Signal=0
	rjmp IntPwmA1
    out OCR1AH,rCtcInaH ; lange Pause
	out OCR1AL,rCtcInaL
	sbr rFlg,1<<bPwmIna ; setze Flagge
	rjmp IntPwmA2
IntPwmA1:
	out OCR1AH,rCtcActH ; kurzes Signal
	out OCR1AL,rCtcActL
IntPwmA2:
	out SREG,rSreg ; stelle SREG wieder her
	reti
;
; AD-Wandler Interrupt Service Routine
IntAdc:
	in rSreg,SREG ; sichern SREG
	in rimp,ADCL ; lese LSB Ergebnis
	ld rimp2,Y ; lese LSB bisherige Summe
    add rimp,rimp2
    st Y+,rimp ; speichere Ergebnis im SRAM
    in rimp,ADCH ; lese MSB Ergebnis
    ld rimp2,Y ; lese MSB bisherige Summe
	adc rimp,rimp2 ; addiere mit Ueberlauf
	st Y+,rimp ; speichere MSB
	cpi YL,6 ; Ende Runde?
	brcs IntAdcRet ; nein
	clr YL ; neu starten
	dec rAdcCnt ; Runde zaehlen
	brne IntAdcRet
AdcCopy:
	ldi YL,LOW(sAdc) ; Kopiere ins SRAM
	st Y+,R0
	st Y+,R1
	st Y+,R2
	st Y+,R3
	st Y+,R4
	st Y,R5
	clr R0 ; alles loeschen
	clr R1
	clr R2
	clr R3
	clr R4
	clr R5
	sbr rFlg,1<<bAdc ; setze Flag
IntAdcRet:
	mov rimp,YL ; MUX-Kanal ermitteln
	lsr rimp ; durch zwei teilen
	out ADMUX,rimp ; MUX auf naechsten Kanal
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp
	out SREG,rSreg ; wiederherstellen SREG
	reti
;
; ============================================
;    H A U P T P R O G R A M M    I N I T
; ============================================
;
Main:
; Initiiere Stapel
	ldi rmp, LOW(RAMEND) ; Initiiere LSB Stapel
	out SPL,rmp
; Initiiere Ports
	; Initiiere LED-Port
	sbi pLedDir,bLedPlus ; LED-Ausgaenge auf Ausgang
	sbi pLedDir,bLedMinus
	sbi pLedAus,bLedPlus ; rote LED an
	cbi pLedAus,bLedMinus
	; Initiiere Tasten-Ports
	cbi pDwnDir,bDwn ; Schliessen-Taste auf Eingang
	sbi pDwnOut,bDwn ; Pull-Up-Widerstand einschalten
	cbi pUpDir,bUp ; Oeffnen-Taste auf Eingang
	sbi pUpOut,bUp ; Pull-Up-Widerstand einschalten
	; Initiiere PWM-Ausgang
	sbi pPwmDir,bPwm ; PWM-Ausgang auf Ausgang
	cbi pPwmOut,bPwm ; Ausgang auf Low
; Initiiere ADC
	; Einzelmessung zu Beginn
.if debugparam==1
	rjmp debugcalc
	nop
	.endif
	rcall AdcGet ; ersten Messzyklus holen
	ldi rmp,HIGH(cMid) ; Position auf Mitte
	mov rCtcActH,rmp
	ldi rmp,LOW(cMid)
	mov rCtcActL,rmp
	clr rFlg ; Flaggen loeschen
	sbr rFlg,(1<<bActive)|(1<<bHigh) ; Schranke faehrt zu Beginn hoch
	ldi rPwmCtr,1 ; LED-Blinkzaehler
	rcall PwmRdyO ; blinken und Korrektur Inaktivdauer
	; Vorbereitung ADC Loop
	ldi YH,HIGH(sAdc) ; Zeiger in SRAM
	ldi YL,LOW(sAdc)
	ldi rmp,0 ; MUX auf Kanal 0
	out ADMUX,rmp
	ldi rmp,(1<<ADC0D)|(1<<ADC1D)|(1<<ADC0D) ; Digital Input aus
	out DIDR0,rmp
	clr rmp ; Ergebnis nicht left adjust
	out ADCSRB,rmp
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
; Initiiere Timer 1
	out OCR1AH,rCtcInaH ; CTC auf Inaktivdauer
	out OCR1AL,rCtcInaL
	ldi rmp,1<<COM1A0 ; Toggle Output A on Compare Match
	out TCCR1A,rmp
	ldi rmp,(1<<WGM12)|(1<<CS10) ; CTC-Mode, Prescaler = 1
	out TCCR1B,rmp
	ldi rmp,1<<OCIE1A ; TC1 Compare Match A Interrupt Enable
	out TIMSK1,rmp
	sbr rFlg,1<<bToggle ; Toggle ist aktiv
; Init PCINT7
    ldi rmp,1<<PCINT7 ; Pin-Change Int Taste 2
	out PCMSK0,rmp
; Init external interrupts and Sleep mode 0
	ldi rmp,(1<<SE)|(1<<ISC01) ; Enable sleep und INT0
	out MCUCR,rmp
	; External Interrupts
	ldi rmp,(1<<INT0)|(1<<PCIE0) ; INT0 und PCINT0 aktivieren
	out GIMSK,rmp
	sei
;
; ============================================
;     P R O G R A M M - S C H L E I F E
; ============================================
;
Loop:
	sleep ; Schlafen legen
	nop ; Dummy fuer Aufwecken
	sbrc rFlg,bPwmIna ; PWM-Flag gesetzt?
	rcall PwmRdy ; ja, behandle PWM
	sbrc rFlg,bAdc ; ADC-Flag gesetzt?
	rcall AdcRdy ; ja behandle ADC
	rjmp loop ; Zurueck nach Loop
;
; PWM ready
PwmRdy:
	cbr rFlg,1<<bPwmIna ; loesche Flagge
	sbrc rFlg,bActive ; springe bei Schranke inaktiv
	rjmp PwmRdyA ; Schranke aktiv
	; Schranke ist nicht aktiv
	sbrs rFlg,bToggle ; springe bei Toggle
	ret
	; TC1-Toggle ist noch an
	dec rPwmCtr ; Nachlauf herunterzaehlen
	brne PwmRdy1 ; nicht abschalten
	ldi rmp,1<<COM1A1 ; Set TC-Output-Pin to clear
	out TCCR1A,rmp ; Toggle abschalten
	clr rmp ; Interrupt abschalten
	out TIMSK1,rmp
	cbr rFlg,1<<bToggle ; Toggle-Flagge aus
PwmRdy1:
	ret
PwmRdyA: ; Schranke ist aktiv
    sbrc rFlg,bHigh ; springe bei Schranke herunterfahren
	rjmp PwmRdyH ; Schranke aufwaerts
    ; Schranke abwaerts
	sub rCtcActL,rDelta ; PWM-Signaldauer verkuerzen
	brcc PwmRdyA1 ; Kein Unterlauf
	dec rCtcActH ; MSB abwaerts
PwmRdyA1:
	cp rCtcActL,rUntenL ; Vergleiche mit unterem Sollwert
	cpc rCtcActH,rUntenH
	brcc PwmRdyA2 ; Noch nicht unterschritten
	mov rCtcActL,rUntenL ; Unterschritten, setze unteren Sollwert
	mov rCtcActH,rUntenH
	cbr rFlg,1<<bActive ; Beende Schrankenbewegung
	sbr rFlg,1<<bZu ; Zu-Flagge setzen
    sbi pLedAus,bLedPlus ; LED rot dauernd an
	cbi pLedAus,bLedMinus
    ldi rPwmCtr,25 ; Wiederholungszaehler fuer Nachlauf setzen
	rjmp PwmRdyK ; korrigiere Dauer PWM
    ; Noch nicht unterschritten
PwmRdyA2:
	cbr rFlg,1<<bZu ; Zu-Flagge aus
	rjmp PwmRdyO ; blinke und korrigiere Dauer PWM
PwmRdyH:
    ; Schranke aufwaerts
	add rCtcActL,rDelta ; Signaldauer aufwaerts
	brcc PwmRdyH1 ; kein Ueberlauf
	inc rCtcActH ; Ueberlauf
PwmRdyH1:
	cp rCtcActL,rObenL ; Vergleiche mit oberem Sollwert
	cpc rCtcActH,rObenH
	brcs PwmRdyH2 ; Noch nicht ueberschritten
	mov rCtcActL,rObenL ; Ueberschritten, setze oberen Sollwert
	mov rCtcActH,rObenH
	cbr rFlg,1<<bActive ; Beende Schrankenbewegung
	sbr rFlg,1<<bOffen ; Schranke offen
	cbi pLedAus,bLedPlus ; Led dauernd auf gruen
	sbi pLedAus,bLedMinus
    ldi rPwmCtr,25 ; Wiederholungszaehler fuer Nachlauf
	rjmp PwmRdyK ; Korrektur Dauer PWM Inaktiv
PwmRdyH2:
	cbr rFlg,1<<bOffen ; Offen-Flagge aus
	rjmp PwmRdyO ; LED-Blink und CTC-Ina-Korrektur
PwmRdyO:
	; LED-Blink
	cbi pLedAus,bLedMinus
	dec rPwmCtr ; Zaehler fuer Blink-Led
	brne PwmRdyK ; korrigiere Inaktivdauer PWM
    ldi rPwmCtr,12 ; starte Zaehler neu
	sbic pLedIn,bLedPlus ; Springe bei Led aus
	rjmp PwmRdyO1 ; Led ist an
	sbi pLedAus,bLedPlus ; Led an
	rjmp PwmRdyK
PwmRdyO1:
	cbi pLedAus,bLedPlus ; Led aus
PwmRdyK:
    ; Korrigiere Inaktivdauer PWM
	ldi rmp,LOW(cPwm) ; Gesamtdauer - Aktivdauer PWM
	sub rmp,rCtcActL
	mov rCtcInaL,rmp ; in Inaktivdauer uebertragen
	ldi rmp,HIGH(cPwm)
	sbc rmp,rCtcActH
	mov rCtcInaH,rmp
	ret
;
; ADC-Runde beendet
;
AdcRdy:
	cbr rFlg,1<<bAdc
	push rUntenL
	push rUntenH
	push rObenL
	push rObenH
	rcall CalcSpeed
	pop XH
	pop XL
	pop ZH
	pop ZL
	sbrc rFlg,bActive ; Schranke noch aktiv
	ret
	sbrc rFlg,bToggle ; Toggle noch aktiv
	ret
	ldi rmp,LOW(cMid) ; unter oder ueber Mitte
	cp rCtcActL,rmp
	ldi rmp,HIGH(cMid)
	cpc rCtcActH,rmp
	brcc AdcRdyU
	; Schranke ist unten
	mov rCtcActH,rUntenH
	mov rCtcActL,rUntenL
	rjmp AdcRdyS ; starte Zyklus
AdcRdyU:
	; Schranke ist oben
	mov rCtcActH,rObenH
	mov rCtcActL,rObenL
AdcRdyS:
    ; starte Anpassung
	rcall PwmRdyK ; Berechne Ina
	ldi rPwmCtr,10 ; starte Nachlaufzaehler
	ldi rmp,1<<COM1A0 ; starte toggle
	out TCCR1A,rmp
	ldi rmp,1<<OCIE1A ; starte Interrupts
	out TIMSK1,rmp
	sbr rFlg,1<<bToggle
	ret
;
; =============================================
;  A S Y N C H R O N E   S U B R O U T I N E N
; =============================================
;
; Hole ADC-Ergebnisse zu Beginn
AdcGet:
	ldi rmp,(1<<ADC0D)|(1<<ADC1D)|(1<<ADC2D) ; Digital Input aus
	out DIDR0,rmp
	clr rmp ; Ergebnis nicht left adjust
	out ADCSRB,rmp
	ldi YH,HIGH(sAdc) ; Kanalzaehler im SRAM
	ldi YL,LOW(sAdc)
GetAdc1:
	clr R0 ; loesche Ergebnis
	clr R1
	ldi rAdcCnt,64 ; 64 Messungen
	mov rmp,YL ; MUX-Kanal
	lsr rmp
	andi rmp,0x03 ; Kanalbits isolieren
	out ADMUX,rmp
GetAdc2:
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
GetAdc3:
	sbic ADCSRA,ADSC ; warte auf Ergebnis
	rjmp GetAdc3
	in rmp,ADCL ; hole LSB Ergebnis
	add R0,rmp
	in rmp,ADCH ; hole MSB Ergebnis
	adc R1,rmp
	dec rAdcCnt
	brne GetAdc2 ; noch eins holen
	st Y+,R0 ; LSB speichern
	st Y+,R1 ; MSB speichern
	cpi YL,sAdc+6 ; alle drei gelesen?
	brcs GetAdc1
;
; Ergebnis fuer Geschwindigkeit umrechnen
CalcSpeed:
	lds rmp,sAdc+5 ; ADC-MSB Ergebnis Kanal 3
	clr XH ; Multiplikator MSB
	ldi XL,cSpeedMulti ; LSB = Multiplikator fuer Geschwindigkeit
	clr ZH ; Ergebnis
	clr ZL
CalcSpeed1:
	lsr rmp ; Bit = 1?
	brcc CalcSpeed2 ; nein, nicht addieren
	add ZL,XL ; Multiplikator LSB addieren
	adc ZH,XH ; MSB addieren
CalcSpeed2:
	lsl XL ; Multiplikator mal zwei
	rol XH
	tst rmp ; schon fertig?
	brne CalcSpeed1 ; nein, noch ein Bit
	ldi rmp,cAdderLangsam
	add rmp,ZH ; addiere MSB Ergebnis
	mov rDelta,rmp
;
; Unteres und oberes Poti auswerten
AdcConv:
	lds rmp,sAdc ; lese LSB unteres Poti
	lds ZL,sAdc+1 ; lese MSB unteres Poti
	clr ZH
	lsl rmp ; oberstes Bit LSB in unterstes Bit LSB schieben
	rol ZL ; oberstes Bit LSB in ZL verschieben
	rol ZH ; oberstes Bit LSB in ZH verschieben
	ldi rmp,LOW(cMinMin) ; Minimum-Dauer Untergrenze
	add ZL,rmp ; zu Ergebnis addieren
	ldi rmp,HIGH(cMinMin)
	adc ZH,rmp
	andi ZL,0xFC ; unterstes Bit loeschen
	mov rUntenH,ZH ; in Speicher schreiben
	mov rUntenL,ZL
	lds rmp,sAdc+2 ; lese LSB oberes Poti
	lds ZL,sAdc+3 ; lese MSB oberes Poti
	clr ZH
	lsl rmp ; oberstes Bit LSB in unterstes Bit LSB schieben
	rol ZL ; oberstes Bit LSB in ZL verschieben
	rol ZH ; oberstes Bit LSB in ZH verschieben
	ldi rmp,LOW(cMaxMin) ; LSB Maximum-Dauer Obergrenze
	add ZL,rmp ; LSB addieren
	ldi rmp,HIGH(cMaxMin)
	adc ZH,rmp ; MSB addieren
	andi ZL,0xFC ; unterstes Bit loeschen
	mov rObenL,ZL
	mov rObenH,ZH
	ret
.if debugparam==1
debugcalc:
	ldi rmp,0xFF
	ldi ZH,HIGH(sAdc)
	ldi ZL,LOW(sAdc)
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	st Z+,rmp
	rjmp CalcSpeed
	.endif
;
; Ende Quellcode
; Copyright
.db "(C)2014 by Gerhard Schmidt  " ; menschenlesbar
.db "C(2)10 4ybG reahdrS hcimtd  " ; wortgerecht
;



©2014 by http://www.avr-asm-tutorial.net