Speziell für Andy: AD-Wandler eines Mega8 nutzen (Interrupt)

Fragen zu Schaltungen, Elektronik, Elektrik usw.

Moderator: T.Hoffmann

Antworten
synvox
Mega-User
Mega-User
Beiträge: 147
Registriert: Fr, 27.04.07, 04:40
Wohnort: Schweiz

Mo, 11.06.07, 15:53

Eigetlich ist das gar kein vollständiges HowTo, also bitte verschieben, falls es hier nicht reinpasst. Ich wusste einfach nicht wohin sonst damit.

Für Andy und alle anderen die's interessiert, sorry dass es doch so lange gedauert hatte (ich hatte letzte Woche kaum Zeit), aber jetzt habe ich einen kleinen Beispielcode geschrieben, wie man bei einem ATMEL AVR Mega8 den AD-Wandler anspricht und in einer Interruptroutine alle sechs Eingänge des Wandlers durchscannt :wink: . Im Hauptprogramm kann man dann beliebig die gespeicherten Werte abfragen und aufgrund der Werte entsprechende Aktionen resp. Anzeigen steuern (diesen Teil überlasse ich aber eurer Phantasie :wink: ).

Wie üblich konfiguriere und verwende ich in diesem Beispiel die entsprechenden Register des Chips selbst und verwende nicht die AD-Wandler-Befehle von BASCOM (dies bietet mehr Optionen und ist sicherer, wenn man sich mit der Architektur eines AVR entsprechend auskennt :wink: ).

Alle weiteren Erklärungen sind im Code selbst.

Gruss
Neni

Code: Alles auswählen

' 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
Zuletzt geändert von synvox am Di, 12.06.07, 04:00, insgesamt 1-mal geändert.
Andy
Hyper-User
Hyper-User
Beiträge: 1284
Registriert: Mi, 17.05.06, 13:03

Mo, 11.06.07, 16:28

Na das nenne ich doch mal einen Service. :lol:

Danke Synvox, das du dir die Mühe gemacht hast, die ersten schritte mal so aus zu führen.
Ich wäre nie im Leben selbst auf diesen Code gekommen, weil ich mich wohl nicht Intensiv genug damit befasse.
Wer weiß, vielleicht reicht mein Verständnis dafür auch nicht so wirklich. :wink:
Aber supe, ich Danke dir vielmals.

Habe dir auch dafür ´n paar Credits überwiesen. :wink:
Benutzeravatar
Doc_McCoy
Hyper-User
Hyper-User
Beiträge: 1962
Registriert: Sa, 03.06.06, 15:49
Wohnort: Neualbenreuth
Kontaktdaten:

Mo, 11.06.07, 17:13

synvox hat geschrieben: Wie üblich konfiguriere und verwende ich in diesem Beispiel die entsprechenden Register des Chips selbst und verwende nicht die AD-Wandler-Befehle von BASCOM (dies bietet mehr Optionen und ist sicherer, wenn man sich mit der Architektur eines AVR entsprechend auskennt :wink: ).
Ist das nun Bascom oder nicht? Weil irgendwie siehts schon danach aus?
Kann eigentlich wer µController in C oder in Assembler programmieren? :roll:
KB
Super-User
Super-User
Beiträge: 92
Registriert: Fr, 15.09.06, 21:37
Kontaktdaten:

Mo, 11.06.07, 20:20

Ich programmiere eigentlich nur in C hinundwieder mal auch ASM. Dort lässt sich der AD Wandler ebenfalls einfach benutzen.
physikphreak
Mega-User
Mega-User
Beiträge: 341
Registriert: Mi, 17.01.07, 17:01

Mo, 11.06.07, 20:29

was für Software benutzt du denn zum kompilieren und flashen und so?
Benutzeravatar
Volker K.
Site Admin
Site Admin
Beiträge: 746
Registriert: Mo, 14.08.06, 16:01

Mo, 11.06.07, 22:06

Was ist denn das fuer eine Sprache bzw. was fuer ein Assembler Dialekt? Die Zuweisungen erinnern mich an C die kommentare an Basic und der eigentliche Code an Assembler *g*


Ich habe meinen Atmel immer mit Forth und C programmiert war irgendwie am komfortabelsten *g*
synvox
Mega-User
Mega-User
Beiträge: 147
Registriert: Fr, 27.04.07, 04:40
Wohnort: Schweiz

Di, 12.06.07, 00:01

Hihi :wink: ,

also genau genommen ist es Basic (BASCOM ist ja ein Basic-Compiler), aber BASCOM erlaubt es eben auch, Inline-Assembler-Befehle zu benutzen (wie im Übrigen die meisten C-Compiler auch), nur muss man bei BASCOM keine spezifische Direktive dafür benutzen (man kann aber man muss nicht), solange die Assembler Mnemonics nicht genau so heissen, wie ein BASCOM-Basic-Befehl (bei OUT ist das z.Bsp. so, darum muss man vor OUT ein Ausrufezeichen setzen, damit BASCOM dann den Assembler-Befehl OUT einsetzt und nicht seinen eigenen Basic-Befehl Out).

Etwas anderes ist noch die Sache, mit den 'Registern selbst Beschreiben'. Bascom hat zwar für die Konfiguration der AD-Wandler-Register einen eigenen, einfachen Befehl "CONFIG ADC = single, PRESCALER = AUTO, REFERENCE = opt" mit ein paar Parametern, nur muss Bascom mittlerweile so viele verschiedene Chips unterstützen, dass manchmal diese Config-Befehle nicht die optimalen Einstellungen bei allen Chips vornehmen, oder nich alle möglichen Optionen unterstützen. Deshalb arbeite ich da lieber mit Datenblatt und gesundem Menschenverstand :wink: und schreibe die Werte für die entsprechende Konfiguration direkt in die betreffenden Register.
Dasselbe gilt auch für das Auslesen des AD-Wandlers. Das ist so einfach durch direkten Zugriff auf die entsprechenden Register zu erreichen, dass ich lieber so mehr Kontrolle über den generierten Code habe, als den BASCOM-Befehl "var = GETADC(channel)" zu benutzen.
Bei BASCOM-Befehlen, welche praktisch ein ganzes Unterprogramm ersetzen, greife ich natürlich gerne darauf zurück, ich will ja nicht das Rad zweimal erfinden :wink: .

Vielleicht konnte ich jetzt ja ein paar Konfusionen aufklären :wink: .

Hier noch ein Link zum Highlighted Code (wie im BASCOM-Editor zu sehen aber mit etwas anderen Farben als bei den Default-Einstellungen), damit sollte die Übersicht viel besser sein :) .

PS: Ein kleiner aber entscheidender Fehler hat sich noch bei mir eingeschlichen gehabt.
Es darf nicht "ADMUX = ADMUX Or Channel" heissen sondern "ADMUX = &H20 Or Channel".
Ist oben und im Link bereits korrigiert. Damit sollte's dann fehlerlos laufen.

Gruss
Neni
Antworten