' Beispiel einer suzzesiven AD-Wandlung aller sechs Kanäle eines mega8, mega48,
' mega88 oder mega168 mittels Interruptroutine (asynchron und unabhängig vom
' Hauptprogramm). Die Interruptroutine wird immer dann wieder aufgerufen, wenn
' der AD-Wandler eine Wandlung durchgeführt hat.
' Interner RC-Oszillator bei 8MHz kann verwendet werden.

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 64
$swstack = 64
$framesize = 64

' Benötigte Vaiablen definieren
Dim Channel As Byte , Werte(6) As Byte , Index As Byte

' Port B Initialisierung
' Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
' State7 = P State6 = P State5 = P State4 = P State3 = P State2 = P State1 = P State0 = P
Portb = &HFF
Ddrb = &H00

' Port C Initialisierung
' Analogeingangspins auf In und hochohmigen Zustand, also Pullups ausschalten
' Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
' State6=P State5=T State4=T State3=T State2=T State1=T State0=T
Portc = &H40
Ddrc = &H00

' Port D Initialisierung
' Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
' State7 = P State6 = P State5 = P State4 = P State3 = P State2 = P State1 = P State0 = P
Portd = &HFF
Ddrd = &H00

' ADC Initialisierung
' ADC Clock Frequenz: 62.500 kHz -> ca. 208µs pro Wandlung pro Kanal
' ADC Referenzspannung: AREF pin (ADMUX) -> Am AREF Pin kann man die Spannung vorgeben, welche dem max. Wandlungswert entsprechen soll
' Werte linksbündig: so muss man für 8 Bit Auflösung nur ADCH lesen
' Einzelwandlung einstellen (keine Dauerwandlung)
' ADC Interrupt erlauben
' Kanal 0 wählen (ADMUX)
Admux = &H20
Adcsra = &H8F

On Adc Adc_isr Nosave                                       'interrupt service routine mit Nosave definieren (nicht automatisch alle Register auf dem Stack sichern)

Channel = 0                                                 'Kanal am Anfang auf 0 setzen

sei                                                         'Global: Interrupts erlauben (Assembler-Befehl)

Adcsra = Adcsra Or &H40                                     'ADC Wandlung starten ohne andere Einstellungen zu verändern

Do
' Ca. 800 mal in der Sekunde werden nun die Variablen Werte(1) bis Werte(6) mit neuen Wandlungswerten (0 - 255)
' der entsprechenden Analogeingänge upgedatet. Das sollte auch für eine gute Pegelanzeige reichen.
' In dieser Do ... Loop Schleife (Hauptprogramm) kann man nun diese Werte mit If Then oder Select Case etc.
' abfragen und aufgrund der Vergleiche bestimmte Aktionen (ev. Unterprogramme) ausführen.
' Man kann aber auch einen Wert direkt in LED-Zeilen-Information (praktisch als Wert-Anzeige) etc. umwandeln.
' Falls Aktionsunterprogramme aufgerufen werden, welche eine bestimmte Zeit dauern und man während dieser
' Zeit sowieso keine Werte abfragt, sollte man am Anfang des Unterprogramms mit CLI alle Interrupts blockieren,
' damit die Interruptroutine nicht unnötigerweise ausgeführt wird. Vor dem Ende des Unterprogrammes muss
' man dann mit SEI wieder die Interrupts erlauben.
Loop

' Hier folgt nun die Interrupt-Routine, welche suzzessive die Wandlungsergebnisse der Kanäle 0 bis 5 in die Variablen Werte(1) bis Werte(6) schreibt
Adc_isr:
' zunächst werden ein paar wenige Register (wir verändern in der Routine kaum welche) mittels Assembler-Befehlen auf dem Stack gespeichert
  push r26
  push r27
  push r24
  in r24,sreg
  push r24
  push r25

  Index = Channel + 1                                       'index ist Kanal + 1, da die Array-Indizes in BASCOM bei 1 anfangen
  Werte(index) = Adch                                       'Wandlungswert in die entsprechende Werte-Variable (mit dem korrekten Index) schreiben
  Incr Channel                                              'Kanal um 1 erhöhen
  If Channel > 5 Then Channel = 0                           'nach Kanal 5 wieder bei Kanal 0 beginnen
  Admux = &H20 Or Channel                                  'ADMUX auf neuen Kanal setzen ohne andere Einstellungen zu verändern
  Waitus 10                                                 '10 µs warten bis Spannungswert am Eingang nach Umschaltung valid
  Adcsra = Adcsra Or &H40                                   'neue ADC Wandlung starten

' vor der Rückkehr aus der Routine die gespeicherten Register wieder rücksetzen
  pop r25
  pop r24
  !out sreg,r24                                             'out ist auch ein BASCOM-Befehl, deshalb das Ausrufezeichen (weist Compiler an, dass Assembler-Befehl folgt) vor out
  pop r24
  pop r27
  pop r26
Return