' 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