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

Quellcode Version 2 für die Schrankensteuerung mit ATtiny24



;
; ***********************************
; * Schrankensteuerung mit ATtiny24 *
; * Version 2 vom Maerz 2018        *
; * (C)2018 by avr-asm-tutorial.net *
; ***********************************
;
.nolist
.include "tn24adef.inc" ; Typdefinition ATtiny24A
.list
;
; **********************************
;    D E B U G   S C H A L T E R
; **********************************
;
; Alle Schalter auf 0 in der Endversion!
;
; Teste die ADC-Umwandlungsroutinen
.equ DebugAdc = 0 ; Die ADC-Routinen testen
  .equ cAdcChannel = 1 ; Kanal (0..2)
  .equ cAdcResult = 65535 ; Ergebnis der ADC-Summe, 0 bis 6535
  .equ cOpen = 2200 ; Offenposition, 1500 bis 2,200
  .equ cClose = 800 ; Geschlossenposition, 1500 bis 800
.equ DebugPosition = 0 ; Folge der gemessenen Position
  .equ DebugOpen = 0 ; Folge der Position am Offenpoti
  .equ DebugClose = 0 ; Folge der Position am Geschlossenpoti
.equ DebugOpenClose = 0 ; Oeffne und schliesse die Schranke an dauernd
;
; Ueberpruefe Fehler
.if (DebugPosition == 1) && (DebugOpen == 0) && (DebugClose == 0)
  .error "Keine Positionierung ausgewaehlt!"
  .endif
.if (DebugPosition == 1) && (DebugOpenClose == 1)
  .error "Positionierung und Offen/Geschlossen-Einstellung!"
  .endif
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Typ: ATtiny24A, Packung: 14-pin-PDIP_SOIC
;
;            _________
;         1 /         |14
; + 5 V o--|VCC    GND|--o 0 V
; DuoLED  2|          |13
; An rt o--|PB0   ADC0|--o Trim 1
;         3|          |12
; An gn o--|PB1   ADC1|--o Trim 2
;         4|          |11
; RESET o--|RESET ADC2|--o Trim 3
;         5|          |10
;   Auf o--|INT0   PA3|--o NC
;         6|          |9
;    Zu o--|PCINT7 SCK|--o ISP6-SCK
;         7|          |8
;   PWM o--|OC1A  MOSI|--o ISP6-MOSI
; ISP6-MISO|__________|
;
;
; **********************************
;  P O R T S   U N D   P I N S
; **********************************
; Duo LED Steuerung
.equ pLedO = PORTB ; LED Outputport
.equ pLedD = DDRB  ; LED Richtungsport
.equ pLedI = PINB  ; LED Inputport
.equ bLedAnRdO = PORTB0 ; Rote Anode Ausgangspin
.equ bLedAnRdD = DDB0 ; Rote Anode Richtungspin
.equ bLedAnGnO = PORTB1 ; Gruene Anode Ausgangspin
.equ bLedAnGnD = DDB1 ; Gruene Anode Richtungspin
;
; Taste Zu Steuerung
.equ pBtnDO = PORTB ; Taste Zu Outputport
.equ pBtnDD = DDRB ; Taste Zu Richtungsport
.equ pBtnDI = PINB ; Taste Zu Inputport
.equ bBtnDO = PORTB2 ; Taste Zu Ausgangspin
.equ bBtnDD = DDB2 ; Taste Zu Richtungspin
.equ bBtnDI = PINB2 ; Taste Zu Inputpin
;
; Taste Auf Steuerung
.equ pBtnUO = PORTA ; Taste Auf Outputport
.equ pBtnUD = DDRA ; Taste Auf Richtungsport
.equ pBtnUI = PINA ; Taste Auf Inputport
.equ bBtnUO = PORTA7 ; Taste Auf Ausgangspin
.equ bBtnUD = DDA7 ; Taste Auf Richtungspin
.equ bBtnUI = PINA7 ; Taste Auf Inputpin
;
; PWM-Signal
.equ pPwmO = PORTA ; PWM Ausgangsport
.equ pPwmD = DDRA ; PWM Richtungsport
.equ bPwmO = PORTA6 ; PWM Ausgangspin
.equ bPwmD = DDA6 ; PWM Richtungspin
;
; **********************************
;      E I N S T E L L B A R
; **********************************
;
; PWM Signaleigenschaften
.equ cPwmMin = 800 ; Minimumdauer, ms
.equ cPwmMax = 2200 ; Maximaldauer, ms
.equ cFpwm = 50 ; Frequenz PWM signal
.equ cPost = 25 ; Postperiode Dauer, 0,5 Sek.
; Auf/ab Geschwindigkeit
.equ cSpeedHigh = 1 ; Maximalgeschwindigkeit, eine Sekunde
.equ cSpeedLow = 7 ; Minimalgeschwindigkeit, sieben Sekunden
;
; **********************************
;  F E S T  &  B E R E C H N E T 
; **********************************
;
; Kontrollertakt = Default
.equ clock=1000000 ; Taktfrequenz
;
; PWM-Signaldauer, default = 20.000 us
.equ cPwm = clock / cFpwm - 1 ; in us
;
; Abgeleitete PWM-Signaleigenschaften
; Mittenposition des Servomotors
; Default: 1.500
.equ cPwmMid = (cPwmMax + cPwmMin + 1) / 2
; Max. Auslenkung fuer Auf- und Zu-Bewegung
; Default: +/- 500
.equ cPwmDelta = (cPwmMax - cPwmMin +1) / 2
; Startwerte
.equ cPwmCloseDef = cPwmMid - 50 ; Leicht abwaerts
.equ cPwmOpenDef = cPwmMid + 50 ; Leicht aufwaerts
.equ cPwmSpeedDef = 1 ; Kleinste Geschwindigkeit
;
; Geschwindigkeitsumrechnung aus Trimmer 3
.equ cCycleHigh = cFpwm*cSpeedHigh ; Default: 50
.equ cCycleLow = cFpwm*cSpeedLow ; Default: 350
.equ cSpdMul = cCycleLow - cCycleHigh ; Multiplikator
.equ cSpdAdd = cCycleHigh ; Addierer zum Ergebnis
;
; ***************************************************
;  W I E   D I E   S O F T W A R E   A R B E I T E T
; ***************************************************
;
; Relevante Register:
;   rClose: 16-Bit-Register mit der PWM-Zeitdauer in
;     Geschlossenposition, kann zwischen 1.500 und
;     800 liegen, wird aus Trimmer 1 berechnet
;   rOpen: 16-Bit-Register mit der PWM-Zeitdauer in
;     Offenstellung, kann zwischen 1.500 und 2.200
;     liegen, wird aus Trimmer 2 berechnet
;   rDelta: 8-Bit-Register um das sich die Position bei
;     jedem PWM-Signal erhoeht (Oeffnen) bzw. absinkt
;     (Schliessen), kann zwischen 1 (langsam) und 28
;     (schnell) liegen
;
; PWM-Signal und Motorstellung:
;   Die kurze PWM-Periode steuert das Servo nach rechts
;   in die Geschlossen-Stellung.
;   Die lange PWM-Periode faehrt den Motor nach links
;   in die Offen-Stellung.
;   Trimmer 1 senkt mit zunehmender Aussteuerung die
;   Geschlossen-Stellung von der Mittenposition aus.
;   Die PWM-Periode variiert dadurch zwischen 1,500
;   (Trimmer P1 am linken Anschlag) und 800 us (Trim-
;   mer 1 am rechten Anschlag). Der Geschlossen-Wert
;   ist im Registerpaar rClose.
;   Trimmer P2 steigert die Offen-Stellung von der
;   Mitte aus nach oben und variiert den Geschlossen-
;   Wert der PWM von 1.500 (linker Anschlag) bis auf
;   2.200 (rechter Anschlag). Der Offen-Wert ist im
;   Registerpaar rOpen.    
;
; PWM-Signalerzeugung:
;   Der 16-Bit-Timer TC1 arbeitet im Fast-PWM-Modus
;   #14. Er wird mit einem Prescaler von 1 getaktet
;   (1 us pro Tick). Das ICR1-Doppelregister wird
;   dazu benutzt, um TC1 nach Ablauf der PWM-Periode
;   auf 0 zu setzen (CTC, Clear timer on compare).
;   Die beiden Portregister ICR1H:ICR1L werden auf
;   19,999 gesetzt um eine PWM von 50 Hz zu erzeugen.
;   Der Ausgangspin OC1A (an Pin PA6) wird automatisch
;   auf High gesetzt wenn der PWM-Zyklus beginnt. Er
;   wird beim Erreichen des Wertes im Vergleicher A
;   (Compare-Portregisterpaar A) automatisch auf Low
;   gesetzt (nicht-invertierende PWM). Der Vergleicher
;   A wird jeweils auf die gewunschte Motorposition
;   eingestellt, geaenderte Werte werden aber erst
;   nach Erreichen des PWM-Zyklusendes in den Ver-
;   gleicher uebernommen, wenn der TC1 auf 20.000
;   ueberlaeuft und bei 0 neu startet.
;   Beim Erreichen der Vergleichswerts A wird der
;   Compare-Match-A-Interrupt ausgeloest (siehe im
;   Folgenden).
;
; TC1 Compare Match A Interrupt:
;   Wird der Vergleichsregister-A-Wert erreicht wird
;   der Interrupt TC1COMPA ausgeloest. Dieser setzt
;   die Flagge bTC1 im Flaggenregister rFlag, die
;   weitere Bearbeitung dieses Erreignisses erfolgt
;   nach der Bearbeitung der Interrupt-Service-Routine
;   (nach dem Aufwachen des Prozessors) ausserhalb
;   des Interrupts.
;
;   Abwaertsbewegung der Schranke:
;   Wenn die Richtungsflagge T im Statusregister 0
;   ist, bewegt sich mit jedem Compare Match die
;   Schranke abwaerts. Dazu wird die aktuelle Po-
;   sition im Vergleichsregister A mit der Geschlos-
;   senposition rClose verglichen. Ist die Endposi-
;   tion erreicht, wird die Post-Periode ausgefuehrt
;   (siehe unten).
;   Wenn sich die Position unterscheidet, wird der
;   Wert im Register rDelta abgezogen. Unterschreitet
;   der neue Wert den Wert in rClose, wird der rClose-
;   Wert ins Vergleichsregister geschrieben,
;   anderfalls der neu berechnete Wert. Die Ueber-
;   nahme des neuen Werts erfolgt aber erst am Ende
;   des PWM-Zyklusses.
;   Die Dauer der Post-Periode wird auf ihren Aus-
;   gangswert cPost gesetzt (in Register rPost). Die
;   LED wird auf rotes Blinken gestellt.
;
;   Aufwaertsbewegung der Schranke:
;   Ist die T-Flagge im Statusregister gesetzt, be-
;   wegt sich die Schranke aufwaerts. Dazu wird der
;   Vergleichswert A mit der Offen-Stellung in rOpen
;   verglichen. Ist die Endstellung erreicht, wird
;   wieder die Post-Periode durchgefuehrt (siehe
;   unten).
;   Unterscheidet sich der Vergleichswert vom rOpen-
;   wert, wird zur aktuellen Position der Wert im
;   Register rDelta dazugezaehlt. Liegt die Summe
;   ueber dem rOpen-Wert, wird der rOpen-Wert in das
;   Vergleichsregister geschrieben, andernfalls der
;   Summenwert. Der neue Vergleichsregister-Wert
;   wird aber erst zum Ende des PWM-Zyklusses ange-
;   wendet.
;   Auch nach dieser Aktion wird der Post-Perioden-
;   Zaehler rPost wieder auf cPost gesetzt und die
;   rote LED blinkt.
;
;   Post-Periode bei Gleichheit:
;   Wenn entweder der Vergleichswert im Compare
;   Match A gleich rOpen ist (T-Flagge gesetzt)
;   oder rClose (T-Flagge nicht gesetzt), wird
;   dieser Programmteil ausgefuehrt.
;   Zuerst wird geprueft, ob der Zaehler rPost
;   schon auf Null ist. Wenn dies der Fall ist,
;   passiert nichts weiter.
;   Falls er nicht bei Null ist, wird er um Eins
;   verringert. Erreicht er dabei Null, wird die
;   LED dauerhaft auf Gruen (T-Flagge gesetzt) oder
;   auf Rot (T-Flagge nicht gesetzt) geschaltet. 
;   Der Ausgang OC1A wird vom TC1 abgekoppelt und
;   ausgeschaltet, so dass keine weiteren PWM-Sig-
;   nale erzeugt werden.
;   Ist der Zaehler rPost nach dem Abwaertszaehlen
;   nicht Null, blinkt die LED weiter rot.
;
; Messen und Umrechnen der Trimmerstellungen:
;   Die drei AD-Wandlerkanaele ADC0, ADC1 und
;   ADC2 werden nacheinander jeweils 64 mal vom
;   AD-Wandler gemessen, die Messwerte im rADC-
;   Registerpaar aufsummiert und beim Erreichen
;   der 64sten Messung durch Setzen der Flagge
;   bAdc im Flaggenregister rFlag signalisiert,
;   dass einer der Kanaele vollstaendig gemessen
;   ist. Die weitere Umrechnung erfolgt ausser-
;   halb der Interrupt-Service-Routine des ADC.
;
;   Schliessen-Trimmer-Position an P1:
;   Ist der ADMUX-Kanal 0 gemessen worden, wird
;   das MSB der erzielten Summe im rAdc-Register-
;   paar mit dem Wert von cPwmDelta (default:
;   700) malgenommen, welches eine 8*16-Bit-
;   Multiplikation ist. Byte 3 und 2 des Ergeb-
;   nisses werden vom Mittenwert cPwmMid abge-
;   zogen und das Ergebnis im rClose abgelegt.
;
;   Oeffnen-Trimmer-Position an P2:
;   Wurde auf Kanal 1 gemessen, wird das MSB des
;   Summenergebnisses mit cPwmDelta multipliziert
;   und Byte 3 und 2 des Ergebnisses zum Wert
;   cPwmMid hinzu addiert und ins Registerpaar
;   rOpen geschrieben.
;
;   Geschwindigkeitseinstellung am Trimmer P3:
;   Wurde Kanal 2 des ADC gemessen, wird das
;   MSB des Ergebnisses invertiert (Zweier-
;   komplement). Der Wert wird mit cSpdMul
;   multipliziert (default: 300, 8*16-Bit-
;   Multiplikation) und cSpdAdd (default: 50)
;   hinzuaddiert. Die Differenz aus rOpen
;   und rClose wird durch Byte 3 und 2 des
;   Ergebnisses geteilt (16/16-Bit-Division).
;   Ist das Ergebnis 0 wird es auf 1 gesetzt.
;   Das Ergebnis wird in rDelta abgelegt.
;
;   Nach jedem Messzyklus wird der nachfol-
;   gende Kanal eingestellt, der Messzaehler
;   rAdcC auf 64 eingestellt und mit der
;   Messung neu begonnen.
;
; Einstellungen beim Programmstart:
;   1. Der Stapelzeiger wird auf RAMEND gesetzt.
;   2. Ist der Debug-Schalter DebugAdc gesetzt,
;      wird dieser ausschliesslich bearbeitet.
;   3. Fall der Zu-Taster gedrueckt ist, wird
;      die T-Flagge geloescht, andernfalls ge-
;      setzt.
;   4. rOpen wird auf 1.550, rClose auf 1.450
;      und rDelta auf 1 gesetzt (langsam).
;   5. Der Zaehler TC1 wird im Fast-PWM-Modus
;      mit ICR als CTC gestartet, als Ver-
;      gleichswert wird der Mittenwert cPwmMid
;      eingestellt, der OC1A-Pin wird auf
;      Clear-On-Compare-Match eingestellt und
;      der Prescaler auf 1. Der Compare-Match
;      Interrupt OCIE1A wird eingeschaltet.
;   6. Der ADC wird mit Kanal 0 als ersten
;      Messkanal, mit der Betriebsspannung
;      als Referenzspannung und ohne ADLAR
;      eingestellt. Die erste Wandlung wird
;      mit gesetztem Interrupt-Enable-Bit
;      gestartet. Der Zaehler rAdcC wird auf
;      64 gesetzt und die Digitaltreiber an
;      den drei Analogeingaengen abgeschaltet.
;   7. Die Ausgabepins PB0 und PB1 der LED
;      werden als Ausgaenge konfiguriert und
;      die LED abgeschaltet.
;   8. Die Tasteneingangspins werden als Ein-
;      gaenge konfiguriert und die Pull-Up-
;      Widerstaende eingeschaltet. Am INT0-
;      Eingang werden Interrupts bei fallen-
;      den Flanken, am PCINT7-Eingang die
;      Maske bei Bit 7 gesetzt und PCINT0
;      eingeschaltet.
;   9. Interrupts werden ermoeglicht und der
;      Schlafmodus Idle wird eingestellt.
;
; Interruptbearbeitung:
;   Der Controller wird nach der Initiierung
;   schlafen gelegt. Alle weitere Bearbeitung
;   erfolgt durch ausgeloeste Interrupts und,
;   nach dem Aufwecken durch den Interrupt,
;   in Routinen ausserhalb der Interrupts.
;   - Der Compare match A Interrupt setzt die
;     bTC1-Flagge, was die Bearbeitung von
;     Schrankenbewegungen und die LED-Steuer-
;     ung ausloest.
;   - Die externen Interrupts INT0 und PCINT0
;     aendern das T-Flag im Statusregister
;     (PCINT0 nur bei fallenden Flanken).
;   - Der ADC-Ready-Interrupt addiert die
;     Messergebnisse auf, zaehlt die Messungen
;     und startet entweder die naechste Messung
;     oder setzt die bAdc-Flagge. Die weitere
;     Bearbeitung bei gesetzter bAdc-Flagge er-
;     folgt nach dem Aufwecken.
;
; **********************************
;         R E G I S T E R
; **********************************
;
; Benutzt: R2:R1:R0 als 24-Bit fuer Multipl./Div.
; Benutzt: R5:R4:R3 als 24-Bit fuer Multipl./Div.
; Frei: R6 bis R9
.def rBlink = R10 ; Blinkzaehler
.def rPost = R11 ; Post-Periodenzaehler
.def rDelta = R12 ; Delta Geschwindigkeit
.def rAdcL = R13 ; ADC-Summe LSB
.def rAdcH = R14 ; dto., MSB
.def rSreg = R15 ; Sichern/Wiederherstellen Statusregister
.def rmp = R16 ; Multipurposeregister ausserhalb Ints
.def rimp = R17 ; Multipurpose innerhalb von Ints
.def rFlag = R18 ; Flaggenregister
  .equ bAdc = 0 ; ADC conversion complete Flagge
  .equ bTC1 = 1 ; TC1 compare match int eingetreten
.def rAdcC = R19 ; ADC Zaehler
.def rOpenL = R20 ; Offen-Stellung, us, LSB
.def rOpenH = R21 ; dto., MSB
.def rCloseL = R22 ; Geschlossen-Stellung, us, LSB
.def rCloseH = R23 ; dto., MSB
; frei: R24 bis R29
; benutzt: R31:R30 = Z fuer diverse Zwecke ausserhalb Ints
;
; **********************************
;           S R A M
; **********************************
;
.dseg
.org SRAM_START
; (SRAM-Nutzung nur fuer Stapel)
;
; **********************************
;     C O D E  - S E G M E N T
; **********************************
;
.cseg
.org 000000
;
; *************************************
; R E S E T  &  I N T - V E K T O R E N
; *************************************
  rjmp Main ; Reset Vektor
  rjmp Int0Isr ; EXT_INT0, Zu-Taste
  rjmp Pcint0Isr ; PCI0, Oeffnen-Taste
  reti ; PCI1, nicht benutzt
  reti ; WATCHDOG, nicht benutzt
  reti ; ICP1, nicht benutzt
  rjmp OC1AIsr ; OC1A Compare Match A
  reti ; OC1B, nicht benutzt
  reti ; TC1OVF, nicht benutzt
  reti ; OC0A, nicht benutzt
  reti ; OC0B, nicht benutzt
  reti ; OVF0, nicht benutzt
  reti ; ACI, nicht benutzt
  rjmp AdcIsr ; AD Conversion Complete
  reti ; ERDY, nicht benutzt
  reti ; USI_STR, nicht benutzt
  reti ; USI_OVF, nicht benutzt
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; INT0 interrupt
; Fallende Flanken am Tasteneingang Schliessen
; aktivieren diesen Interrupt. Das T-Flag wird
; geloescht und die Schranke bewegt sich
; abwaerts
Int0Isr:
  clt ; Abwaertsrichtung, schliesse Schranke
  reti
;
; PCINT0 interrupt
; Bei jedem Flankenwechsel am Schliessen-Eingang
; wird dieser Interrupt ausgeloest
Pcint0Isr:
  sbis pBtnUI,bBtnUI ; Ueberspringe wenn Schliessen Eingang high
  set ; Aufwaertsrichtung, Schranke oeffnen
  reti
;
; OC1AIsr Compare Match A Interrupt
; wird pro PWM-Zyklus (default 20 ms) einmal aus-
; geloest, loest Positionsanpassung aus
OC1AIsr:
  in rSreg,SREG ; Sichern SREG
  sbr rFlag,1<<bTC1 ; Setze T-Flagge
  out SREG,rSreg ; Stelle SREG wieder her
  reti
;
; ADC Interrupt
; AD conversion complete: addiere Ergebnis,
; zaehle Anzahl Messungen abwaerts und starte
; die naechste Wandlung oder setze die bAdc-Flagge
AdcIsr:
  in rSreg,SREG ; Sichere SREG
  in rimp,ADCL ; Lese LSB Ergebnis
  add rAdcL,rimp ; Addiere zur Summe LSB
  in rimp,ADCH ; Lese MSB Ergebnis
  adc rAdcH,rimp ; Addiere zur Summe MSB mit Uebertrag
  dec rAdcC ; Zaehler abwaerts
  breq AdcIsrEnd ; Kanalzyklus zu Ende
  ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  out ADCSRA,rimp ; Starte naechste Wandlung
  out SREG,rSreg ; Stelle SREG wieder her
  reti
AdcIsrEnd:
  sbr rFlag,1<<bAdc ; Setze ADC-Flagge
  out SREG,rSreg ; Stelle SREG wieder her
  reti
;
; **************************************
;  P R O G R A M M S T A R T ,  I N I T
; **************************************
;
Main:
  ; Den Stapel initiieren fuer Interrupts/Calls
  ldi rmp,Low(RAMEND)
  out SPL,rmp ; Initiiere LSB Stapelzeiger
  ; Debug-Schalter DebugAdc
  .if DebugAdc == 1 ; Debug der ADC-Routinen
    ldi rmp,cAdcChannel ; Kanal (0..2)
    out ADMUX,rmp
    ldi rmp,High(cAdcResult) ; Summenwert setzen
    mov rAdcH,rmp
    ldi rmp,Low(cAdcResult)
    mov rAdcL,rmp
    ldi rmp,High(cOpen) ; Oeffnenwert setzen
    mov rOpenH,rmp
    ldi rmp,Low(cOpen)
    mov rOpenL,rmp
    ldi rmp,High(cClose) ; Schliessenwert setzen
    mov rCloseH,rmp
    ldi rmp,Low(cClose)
    mov rCloseL,rmp
    rcall AdcRdy ; Adc-Routine aufrufen
    DebugLoop:
      rjmp DebugLoop
    .endif
  ; Initiiere LED-Ausgabepins
  sbi pLedD,bLedAnRdD ; LED-Pins als Ausgaenge
  sbi pLedD,bLEDAnGnD
  cbi pLedO,bLedAnRdO ; Rote LED aus
  cbi pLedO,bLedAnGnO ; Gruene LED aus
  ; Initiiere PWM-Ausgabepin
  cbi pPwmO,bPwmO ; Loesche PWM-Ausgabepin
  sbi pPwmD,bPwmD ; Setze PWM-Ausgabepin als Ausgang
  ; Initiiere Tastenpins
  cbi pBtnDD,bBtnDD ; Schliessen-Eingang als Eingang
  sbi pBtnDO,bBtnDO ; Pull-up am Schliessen-Eingang an
  cbi pBtnUD,bBtnUD ; Oeffnen-Eingang als Eingang
  sbi pBtnUO,bBtnUO ; Pull-Up am Oeffnen-Eingang an
  ; Schliessen-Eingang beim Start gedrueckt:
  ; T-Flagge auf 0
  set ; Aufwaerts als default
  sbis pBtnDI,bBtnDI ; Ueberspringe wenn Eingang gesetzt
  clt ; Abwaerts wenn Eingang auf Null
  ; Flaggenregister loeschen
  clr rFlag
  ; Setze Startparameter
  ldi rmp,High(cPwmOpenDef) ; Leicht aufwaerts
  mov rOpenH,rmp
  ldi rmp,Low(cPwmOpenDef)
  mov rOpenL,rmp
  ldi rmp,High(cPwmCloseDef) ; Leicht abwaerts
  mov rCloseH,rmp
  ldi rmp,Low(cPwmCloseDef)
  mov rCloseL,rmp
  ldi rmp,cPwmSpeedDef ; Langsamste Geschwindigkeit
  mov rDelta,rmp
  ; Initiiere TC1 als PWM
  ldi rmp,High(cPwm) ; Setze ICR auf PWM-Dauer, MSB
  out ICR1H,rmp
  ldi rmp,Low(cPwm) ; dto., LSB
  out ICR1L,rmp
  ldi rmp,High(cPwmMid) ; Motor in Mittelposition
  out OCR1AH,rmp
  ldi rmp,Low(cPwmMid)
  out OCR1AL,rmp
  ldi rmp,(1<<COM1A1)|(1<<WGM11) ; OC1A bei Compare Match loeschen, Fast-PWM mit ICR
  out TCCR1A,rmp
  ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10) ; Prescaler=1, Fast PWM mit ICR
  out TCCR1B,rmp
  ldi rmp,1<<OCIE1A ; Ermoegliche Interrupts bei Compare Match A
  out TIMSK1,rmp
  ; Initiiere ADC
  clr rAdcL ; Loesche Summenergebnis
  clr rAdcH
  ldi rAdcC,64 ; 64 Messungen
  ldi rmp,(1<<ADC2D)|(1<<ADC1D)|(1<<ADC0D) ; Digitale Eingangsstufen abschalten
  out DIDR0,rmp
  ldi rmp,0 ; Starte mit Kanal 0, Referenz = Betriebsspannung
  out ADMUX,rmp
  out ADCSRB,rmp
  ; Starte erste Wandlung
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  out ADCSRA,rmp
  ; Schlafmode idle, externe Interrupts INT0 bei fallenden Flanken
  ldi rmp,(1<<SE)|(1<<ISC01)
  out MCUCR,rmp
  ldi rmp,1<<PCINT7 ; PCINT am Pin PA7 in die Maske
  out PCMSK0,rmp
  ldi rmp,(1<<INT0)|(1<<PCIE0) ; INT0 und PCINT0 Interrupts einschalten
  out GIMSK,rmp
  ; Interrupts ermoeglichen
  sei ; Interrupt-Flagge setzen
;
; **********************************
;  P R O G R A M M S C H L E I F E
; **********************************
;
Loop:
  sleep ; Schlafen legen
  nop ; Dummy beim Aufwachen
  sbrc rFlag,bTC1 ; Ueberspringe wenn bTC1-Flagge aus
  rcall TC1Ctrl ; Behandle TC1 Interrupt
  sbrc rFlag,bAdc ; Ueberspringe wenn bADC-Flagge aus
  rcall AdcRdy ; Behandle End des ADC-Messzyklusses
  rjmp loop ; Zurueck zum Schlafen
;
; Behandle TC1 Interrupt Flagge
; in jedem PWM-Zyklus nach dem Compare Match A
TC1Ctrl:
  cbr rFlag,1<<bTC1 ; Loesche Flagge
.if DebugPosition == 1 ; Wenn Positionsfolge eingeschaltet ist
  ret
  .endif
  in ZL,OCR1AL ; Lese aktuelle Position aus Vergleichsport nach Z
  in ZH,OCR1AH
  brts TC1CtrlUp ; Oeffnen-Modus aktiv
  ; Schranke abwaerts?
  cp ZL,rCloseL ; Pruefe LSB gleich?
  brne Tc1CtrlDwn1 ; Nein
  cp ZH,rCloseH ; Pruefe MSB gleich?
  breq TC1Post ; LSB+MSB gleich, Post-Periode
Tc1CtrlDwn1:
  ; Schranke abwaerts
  sub ZL,rDelta ; Subtrahiere Delta
  ldi rmp,0
  sbc ZH,rmp
  cp ZH,rCloseH ; MSB gleich?
  brcs Tc1CtrlDwn2 ; MSB kleiner, Setze Geschlossen-Wert
  brne Tc1SetPos ; MSB groesser, Setze Position in Z
  ; MSB ist gleich
  cp ZL,rCloseL ; LSB gleich?
  brcc Tc1SetPos ; LSB groesser, Setze Position in Z
Tc1CtrlDwn2:
  mov ZL,rCloseL ; Endposition Schliessen-Wert in Z
  mov ZH,rCloseH
  rjmp Tc1SetPos ; Schreibe Position in Z in OCR1A
TC1CtrlUp:
  ; Schranke aufwaerts?
  cp ZL,rOpenL ; Vergleiche LSB Offen-Wert
  brne TC1CtrlUp1 ; Ungleich, aufwaerts
  cp ZH,rOpenH ; Vergleiche MSB Offen-Wert
  breq TC1Post ; Endposition erreicht, Post-Periode
TC1CtrlUp1:
  add ZL,rDelta ; Addiere Schrittwert rDelta
  ldi rmp,0
  adc ZH,rmp
  cp ZH,rOpenH ; Vergleiche MSB mit Offenwert-MSB
  brcs Tc1SetPos ; MSB kleiner, Setze Position in Z
  brne Tc1CtrlUp2 ; MSB groesser, Setze Offenwert nach Z
  ; MSB gleich
  cp ZL,rOpenL ; Vergleiche LSB
  brcs Tc1SetPos ; LSB kleiner, Setze Position in Z
TC1CtrlUp2:
  mov ZL,rOpenL  ; Kopiere Offenposition nach Z
  mov ZH,rOpenH
; Schreibe Position in Z nach OCR1A
TC1SetPos:
  out OCR1AH,ZH
  out OCR1AL,ZL
  ldi rmp,cPost ; Startwert Postperiode
  mov rPost,rmp ; in rPost schreiben
  in rmp,TCCR1A ; Pruefe ob COM1A1 an
  andi rmp,1<<COM1A1 ; Pruefe COM1A1
  brne TC1Blink ; Ist schon aktiv
  ldi rmp,(1<<COM1A1)|(1<<WGM11) ; Schalte COM1A1 an
  out TCCR1A,rmp ; Setze OC1A-Pin aktiv
TC1Blink:
  ; Schranke bewegt sich, blinke die rote LED
  sbi pLedO,bLedAnRdO ; Rote LED an
  inc rBlink ; Blinkzaehler erhoehen
  mov rmp,rBlink
  sbrc rmp,2 ; Blinke mit Bit 2
  cbi pLedO,bLedAnRdO ; Rote LED aus
  cbi pLedO,bLedAnGnO ; Gruene LED aus
  ret
; Schranke hat Endposition erreicht, Postperiode
TC1Post:
  tst rPost ; Postperiode vorbei?
  breq TC1Ret ; Ja, fertig
  dec rPost ; Zaehle Postperiode abwaerts
  brne TC1Blink ; Noch nicht vorbei, blinke rot
  ; Postperiode vorbei, OC1A-Pin abschalten
  ldi rmp,1<<WGM11 ; Nur das WGM Bit
  out TCCR1A,rmp ; OC1A abschalten
  cbi pPwmO,bPwmO ; PWM-Pin low
  brts TC1UpLed ; Aufwaerts gewaehlt
  ; Schranke in Geschlossen-Position, rote LED an
  sbi pLedO,bLedAnRdO ; Rote LED an
  cbi pLedO,bLedAnGnO ; Gruene LED aus
.if DebugOpenClose == 1 ; Oeffnen/Schliessen eingeschaltet?
  set ; Wenn ja, jetzt aufwaerts
  .endif
  ret
TC1UpLed:
  ; Schranke in Offen-Position, gruene LED an
  cbi pLedO,bLedAnRdO ; Rote LED aus
  sbi pLedO,bLedAnGnO ; Gruene LED an
.if DebugOpenClose == 1 ; Oeffnen/Schliessen eingeschaltet?
  clt ; Wenn ja, jetzt abwaerts
  .endif
TC1Ret:
  ret
;
; ADC-Messzyklus beendet, behandle Ergebnis
AdcRdy:
  cbr rFlag,1<<bAdc ; Loesche ADC-Flagge
  in rmp,ADMUX ; Lese Kanal
  cpi rmp,1 ; Kanal 1?
  breq AdcRdy1 ; Ja
  brcc AdcRdy2 ; Kanal 2
  ; Kanal = 0
  rcall MultiplyPos ; Multipliziere Position
  sub ZL,R0 ; Subtrahiere Ergebnis von der Mittelposition
  sbc ZH,R1
  mov rCloseL,ZL ; Schreibe Ergebnis in Geschlossen-Stellung
  mov rCloseH,ZH
.if DebugClose == 1 ; Folge der Schliessen-Position eingeschaltet?
  out OCR1AH,ZH ; Ja, schreibe direkt in Vergleichsregister
  out OCR1AL,ZL
  .endif
  ldi rmp,1 ; Weiter mit Kanal 1
  rjmp AdcRst
AdcRdy1:
  ; Kanal = 1
  rcall MultiplyPos ; Multipliziere Position
  add ZL,R0 ; Addiere Ergebnis zur Mittenposition
  adc ZH,R1
  mov rOpenL,ZL ; Schreibe Ergebnis in Offen-Stellung
  mov rOpenH,ZH
.if DebugOpen == 1 ; Folgen der Oeffnen-Position eingeschaltet?
  out OCR1AH,ZH ; Ja, schreibe direkt ins Vergleichsregister
  out OCR1AL,ZL
  .endif
  ldi rmp,2 ; Weiter mit Kanal 2
  rjmp AdcRst
AdcRdy2:
  ; Kanal = 2
  com rAdcH ; Invertiere MSB ADC-Summe
  ; Konvertiere MSB des ADC in
  ;   50..350 (1 bis 7 Sekunden entspricht
  ;   50..350 Compare A interrupts
  ;   Register: R2:R1:R0 = 300
  ;             R5:R4:R3 = Ergebnis
  ; Dividiere (rClose-rOpen) durch Ergebnis
  ;   ergibt Delta pro Schritt
  ;   Register: R2:R1 als Teiler
  ;             R5:R4:R3 als Dividend
  ;             rmp als Ergebnis
  ; Beispiele:
  ;   rClose = 2000, rOpen = 1000
  ;   MSB ADC = 0xFF:
  ;     Multiplikationsergebnis = 300*255/256 = 299
  ;     Divisionsergebnis = 1000 / 299 = 3
  ;   MSB ADC = 0x00
  ;     Multiplikationsergebnis = 50
  ;     Division result = 1000 / 50 = 20
  ;
  ; Lade Multiplikator (Default 300) in R2:R1:R0
  ldi rmp,LOW(cSpdMul)
  mov R0,rmp
  ldi rmp,HIGH(cSpdMul)
  mov R1,rmp
  clr R2
  ; Lade Ergebnis mit Addierer in R5:R4:R3
  clr R3
  ldi rmp,Low(cSpdAdd)
  mov R4,rmp
  ldi rmp,High(cSpdAdd)
  mov R5,rmp
; Multipliziere rAdcH mit R1:R0
;   Ergebnis nach R5:R4:R3
AdcRdy2a:
  lsr rAdcH ; Schiebe niedrigstes Bit in Carry
  brcc AdcRdy2b ; Bit=0, nicht addieren
  add R3,R0 ; Addiere den Multiplikator zum Ergebnis
  adc R4,R1
  adc R5,R2
AdcRdy2b:
  lsl R0 ; Schiebe Multiplikator eins links
  rol R1
  rol R2
  tst rAdcH ; Multiplikation fertig?
  brne AdcRdy2a ; Nein
  lsl R3 ; LSB des Ergebnisses zum Aufrunden
  brcc AdcRdy2c
  inc R4 ; Runde LSB auf
  brne AdcRdy2c
  inc R5 ; Runde auch MSB auf
  ; Multiplikationsergebnis ist in R5:R4
AdcRdy2c:
  ; Berechne Difference zwischen Offen- und Zu-Stellung
  ; in R2:R1:R0
  mov R1,rOpenH
  mov R0,rOpenL
  sub R0,rCloseL
  sbc R1,rCloseH
  clr R2
  ; Teile Differenz in R1:R0 durch R5:R4
  ; rmp ist 8-Bit-Ergebnis
  ldi rmp,1 ; 8 mal schieben
AdcRdy2d:
  lsl R0 ; Schiebe Dividend links
  rol R1
  rol R2
  brcs AdcRdy2e ; Ueberlauf, eine Eins ins Ergebnis schieben
  cp R1,R4 ; Vergleiche mit Divisor
  cpc R2,R5
  brcc AdcRdy2e ; Schiebe eine 1 ins Ergebnis
  clc ; Schiebe eine 0 ins Ergebnis
  rjmp AdcRdy2f
AdcRdy2e:
  sub R1,R4 ; Subrahiere den Divisor
  sbc R2,R5
  sec ; Schiebe eine Eins in das Ergebnis
AdcRdy2f:
  rol rmp ; Schiebe Carry-Bit in das Ergebnis
  brcc AdcRdy2d ; Noch nicht fertig
  lsl R0 ; Schiebe Dividend nochmals links zum Runden
  rol R1
  rol R2
  brcs AdcRdy2g ; Wenn eine Eins herausrollt: aufrunden
  cp R1,R4 ; Vergleiche LSB
  cpc R2,R5 ; und MSB
  brcs AdcRdy2h ; Kleiner, nicht aufrunden
AdcRdy2g:
  inc rmp ; Aufrunden
AdcRdy2h:
  tst rmp ; Ergebnis = 0?
  brne AdcRdy2i ; Nein
  inc rmp ; Addiere Eins zu der Null in rmp
AdcRdy2i:
  mov rDelta,rmp ; Kopiere Ergebnis in rDelta
  ldi rmp,0 ; Starte wieder mit Kanal 0
AdcRst:
  out ADMUX,rmp ; in ADMUX
  clr rAdcL ; Summe auf Null
  clr rAdcH
  ldi rAdcC,64 ; 64 Messungen
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  out ADCSRA,rmp ; Wandlung starten
  ret
;
; Multiplikation des MSB-ADC-Ergebnisses
MultiplyPos:
  ldi rmp,Low(cPwmDelta) ; Default: 700, in R2:R1:R0
  mov R0,rmp
  ldi rmp,High(cPwmDelta)
  mov R1,rmp
  clr R2
  clr R3 ; Loeschen Ergebnis
  clr R4
  clr R5
MultiplyPos1:
  lsr rAdcH ; Schiebe Bit ins Carry
  brcc MultiplyPos2 ; Nicht Eins
  add R3,R0 ; Zum Ergebnis addieren
  adc R4,R1
  adc R5,R2
MultiplyPos2:
  lsl R0 ; Multipliziere mit zwei
  rol R1
  rol R2
  tst rAdcH ; Alles multipliziert?
  brne MultiplyPos1
  lsl R3 ; Aufrunden?
  brcc MultiplyPos3 ; Nein
  ldi rmp,0 ; Runde auf
  adc R4,rmp
  adc R5,rmp
MultiplyPos3:
  mov R0,R4 ; Kopiere Ergebnis nach R1:R0
  mov R1,R5
  ldi ZH,High(cPwmMid) ; Mittenposition in Z
  ldi ZL,Low(cPwmMid)
  ret
;
; Ende des Quellcodes
;



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