Forum: Mikrocontroller und Digitale Elektronik ADC des Atmega 8 auslesen


von Sven F. (skfink)


Lesenswert?

Hallo,
ich wollte als ersten Anwendung mit dem Atmega8 ein Lauflicht machen
dessen Geschwindigkeit von einer Poti-Stellung abhängt. Poti hängt
zwischen Vcc und Maße und der Schleifer ist am ADC0 Port.
Hier der Code der vom AVR Studio 4 fehlerfrei compiliert wird.

.include "m8def.inc"       ;Definitionsdatei einbinden, ggf. durch
                             ;2333def.inc ersetzen
         ldi r16,high(ramend)        ;Stackpointer auf
         out sph,r16                 ;Ende des SRAM
         ldi r16,low(ramend)         ;setzen
         out spl,r16
         ldi r16, 0xFF       ;0xFF ins Arbeitsregister r16 laden
         out DDRB, r16       ;Inhalt von r16 ins IO-Register DDRB
;initialisierung des ADC
     sbi ADEN, 1    ;ADC aktivieren
     sbi ADFR, 1    ;ADC auf Free Run Mode
     sbi ADPS0, 0    ;Takteiler..
     sbi ADPS1, 1    ;..auf 64..
     sbi ADPS2, 1    ;..stellen
     sbi REFS1, 0    ;AVcc with..
     sbi REFS0, 1    ;..external Capacitor at AREF
     sbi ADLAR, 1    ;Result right adjusted
     sbi MUX0, 0    ;ADC...
     sbi MUX1, 0    ;..auf..
     sbi MUX2, 0    ;..Port ADC0...
     sbi MUX3, 0    ;..stellen
     sbi ADSC, 1    ;Wandlung starten
anfang:
     ldi r18,0
     ldi r17,0
         rcall LED1an
schleife:
     inc r18
     brne schleife
     inc r17
     ldi r19, ADCH
     CP r19, r17
     brlo schleife


     ldi r18,0
     ldi r17,0
     rcall LED2an
schleife2:
     inc r18
     brne schleife2
     inc r17
     ldi r19, ADCH
     CP r19, r17
     brlo schleife2

     ldi r18,0
     ldi r17,0
     rcall LED3an
schleife3:
     inc r18
     brne schleife3
     inc r17
     ldi r19, ADCH
     CP r19, r17
     brlo schleife3
     rjmp anfang

LED1an:  ldi r16, 0b00000010 ;0b11111100 in r16 laden
         out PORTB, r16      ;r16 ins IO-Register PORTB
     ret
LED2an:  ldi r16, 0b00000100
     out portb, r16
     ret
LED3an:  ldi r16, 0b00001000 ;0b11111100 in r16 laden
         out PORTB, r16      ;r16 ins IO-Register PORTB
     ret

Alle drei LEDs leuchten schwach unabhängig von der Poti Stellung. An
jedem Pin sind 1,6V was ja ein drittel von 5 ist und somit nur der RMS
Wert, da jeder Pin nur ein drittel der Zeit 5V hat. Frequenz gibt mein
Multimeter mit 400Hz an. Unabhängig von der Poti Stellung.
Wenn ich den ADC Part entferne und stattdessen eine fixe zweite
schleife rein mache läuft das Licht auch...ist aber nicht Sinn der
Übung.

Was ist am auslesen falsch?

von Karl H. (kbuchegg)


Lesenswert?

Ich empfehle dir mal ein Studium der Datenblaetter
das ATMega8. Insbesondere welches Steuerbit in welchem
Steuerregister was genau macht.
Weiters empfehle ich dir die Befehlsreferenz von Atmel in
der du mal nachlesen solltest, wie eigentlich der sbi
Befehl funktioniert.

von Karl H. (kbuchegg)


Lesenswert?

Zur Not kannst du auch den Abschnitt ueber ADC im
AVR-GCC Tutorial auf dieser Site studieren.
Aber um das Studium der Befehlsreferenz (wegen sbi)
kommst du nicht herum. Der wird naemlich voellig
anders verwendet als du dir das vorstellst.

von Rahul (Gast)


Lesenswert?

Ich habe zwar (noch) nicht viel Ahnung vom AVR-Assembler, aber du
solltest irgendwo abfragen, ob der Messungvorgang des ADC beendet ist.
Der Hinweis von Karl-Heinz bzgl. Tutorium ist auch eine Hilfe.
Und wie immer gilt: Das Datenblatt ist dein Freund.

von Matthias (Gast)


Lesenswert?

Nachdem du das mit dem sbi behoben hast, würde ich dir empfehlen den
AD-Wandler erst auszulesen wenn die Wandlung fertig ist. Also entweder
auf das entsprechende Flag zu pollen oder noch besser mit interrupt.
Noch dazu mit ADLAR=1 Left Adjusted
Mfg Matthias

von AR. (Gast)


Lesenswert?

;initialisierung des ADC
     sbi ADEN, 1    ;ADC aktivieren
     sbi ADFR, 1    ;ADC auf Free Run Mode
     sbi ADPS0, 0    ;Takteiler..
     sbi ADPS1, 1    ;..auf 64..
     sbi ADPS2, 1    ;..stellen
     sbi REFS1, 0    ;AVcc with..
     sbi REFS0, 1    ;..external Capacitor at AREF
     sbi ADLAR, 1    ;Result right adjusted
     sbi MUX0, 0    ;ADC...
     sbi MUX1, 0    ;..auf..
     sbi MUX2, 0    ;..Port ADC0...
     sbi MUX3, 0    ;..stellen
     sbi ADSC, 1    ;Wandlung starten

Hmm,
sbi ADCSRA, ADEN
sbi ADCSRA, ADFR
sbi ADCSRA, ADPS0
...
...
na usw.
oder ldi R16,(1<<ADEN)|(1<<ADFR)|...
und dann mit  "out" den Wert von R16 dem IO-Register ADCSRA zuweisen


sbi ADMUX, REFS1
sbi ADMUX, REFS0
...
...

sbi ADCSRA, ADSC zum Schluss. um die Wandlung zu starten

soviel zum Thema sbi.
Ich hoffe, meine Antwort hat Dir mehr geholfen...

@kbuchegg
etwas mehr Kooperation wäre hier angebracht gewesen, oder?
Das Datenblatt hat er sich angesehen, sbi sieht er sich gerade an, jede
Wette. ;-))

AxelR.

von Karl H. (kbuchegg)


Lesenswert?

@AR

Sorry AR.
Aber ich bin der Meinung, dass man einem Anfaenger durchaus
zumuten kann, dass er sich die Grundlagen selbst erarbeitet.
Dass man ihn nicht einfach machen laesst, ist klar. Ein
Hinweis wo etwas falsch ist und wo man Information darueber
bekommt wie's richtig geht, ist auch OK. Aber man muss nicht
alles auf einem Silbertablett servieren. Zumal der Lerneffekt
bei 'Selbst-Rausgefundenem' wesentlich groesser ist. Dauert
zwar fuer den Lernenden etwas laenger, als wenn er alles
mundgerecht zum abtippen vorgeworfen bekommt, dafuer vergisst
er es nie wieder.

Das ist zumindest meine Erfahrung der letzten 20 Jahre in denen
ich Leuten Programmieren beigebracht habe.

von AxelR. (Gast)


Lesenswert?

@Karl Heinz
Ja, Du hast wahrscheinlich Recht!
@Sven
dranbleiben!

GRuß
AxelR.

von Hannes L. (hannes)


Lesenswert?

Karl Heinz hat recht...
(schade, dass ich nicht zu den Glücklichen gehören durfte, denen er
Programmieren beigebracht hatte)

Sven, du arbeitest doch mit AVR-Studio. Setz doch mal den Cursor auf
einen ASM-Befehl und drück mal die F1-Taste.

Trotzdem solltest du dir die Tabellen "instruction set" und
"I/O-registers" (oder so ähnlich heißend) aus dem Datenblatt
(ziemlich weit unten) ausdrucken. Das sind Infos, die der Neuling immer
parat haben muss.

...

von Sven F. (skfink)


Lesenswert?

@Axel
thx, ich weiss solche Antworten zu schätzen. Und ich denke auch nicht
dass ich es so eher vergessen werde wenn ich es denn mal richtig weiss
als wenn ich es mir über Tage und Wochen des suchens irgendwo doch noch
gefunden hätte, da das ja etwas ist was man doch in einer Tour braucht.
Ich denke wie sehr man Dinge behält hängt mehr davon ab wie oft man sie
braucht als wie man sie lernt. Hilfe zur Selbsthilfe ist natürlich gut,
bloss habe ich im Vorfeld bereits im GCC Tut und im Datenblatt
nachgeschlagen (daher hab ich die Register und Bits). Bloss das GCC Tut
ist für C und ich will das erstmal in Assambler machen. Im Datenblatt
ist kein Codebeispiel und im Instruction Set ist das einzige Beispiel
zu sbi:
out $1E,r0 ; Write EEPROM address
sbi $1C,0 ; Set read bit in EECR
in r1,$1D ; Read EEPROM data

keine große Hilfe.

aber zu dem code nochmal, muss ich zuerst
sbi ADEN, 1 ;und dann noch
sbi ADCSRA, ADEN ;setzen oder letzten Befehl statt des ersten?

Eigentlich könnte ich die beiden Register (ADCSRA und ADMUX) doch
direkt mit byte per ldi laden, nicht sehr debug freundlich für dritte,
aber kurz und handlich.

von Hannes L. (hannes)


Lesenswert?

> Eigentlich könnte ich die beiden Register (ADCSRA und ADMUX) doch
> direkt mit byte per ldi laden, nicht sehr debug freundlich für
> dritte,
> aber kurz und handlich.

Selbstverständlich kannst du das.
Aber bitte nicht die Bits aus dem Datenblatt zusammenklauben und dann
selbst zur Hex-Zahl oder Binärzahl zusammensetzen, das versteht dann
keiner. Lieber die Namen aus dem Datenblatt verwenden, die kennt der
Assembler nämlich, da sie mit der "m8def.inc" bekannt gemacht
werden.

Willst du mehrere Bits setzen, dann könnte das so aussehen:

ldi wl,(1<<aden)|(1<<adsc)|(1<<adfr)|(1<<mux0)
out admux,wl

Dabei ist wl der selbst vergebene Name eines oberen Registers.
Da ldi (wie auch sbr und cbr) nicht die Bitnummern, sondern die
Bitmaske braucht, muss aus der jeweiligen Bitnummer (0..7) ein Wert
(1,2,4,8,16,32,64,128) gemacht werden. Dies geschieht durch
"Linksschieben einer 1" um den Wert der Bitnummer. Ist etwas
gewöhnungsbedürftig, aber dann sehr "sprechend".

Leider zeigt die Online-Hilfe des ACR-Studios die Operatoren und
Directiven nicht mit der F1-Taste an. Da müsstest du mal über das
Help-Menü (help, tools, avr-assembler2, directiven) hingehen und
nachlesen.

Wenn du dir ein paar ASM-Programmbeispiele ansehen willst, dann schau
mal hier, das ist recht üppig kommentiert:
http://www.hanneslux.de/avr/divers/index.html

Und hier geht es um den ADC, allerdings mit Mega48:
http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html

...

von Sven F. (skfink)


Lesenswert?

@hannes
hab mir deinen code mal angesehn von dem impulsgenerator und dann mit
meinem nun umgeschriebenen code verglichen. Bis auf meinen Fehler beim
einlesen des result registers ADCH, ldi statt lds zu verwenden sollte
es eigentlich stimmen, geht aber immer noch nicht.
Vorher haben die 3 LED gleichmäßig schwach geleuchtet, sprich r19 war
immer 0. Jetzt läuft das Lauflicht wieder so wie es gelaufen ist als
ich einfach nur zwei ineinandergeschachtelte 8bit schleifen hatte.
Sprich jetzt ist r19 immer 255, egal wie das poti steht.
ADLR hab ich mit 1 und 0 versucht sowie ADCH und ADCL ausgelesen, so
wie der code jetzt da steht müsste er während einer Poti drehung 4 mal
schneller und wieder langsam werden...passieren tut gar nix.

;initialisierung:
ldi r20,0
ldi r20,(1<<REFS0)
out ADMUX,r20
ldi r20,0
ldi r20,(1<<ADEN)|(1<<ADFR)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADSC)
out ADCSRA, r20

;auslesen:
schleife:
inc r18
brne schleife
inc r17
lds r19, ADCL
CP r19, r17
brlo schleife

irgendwelche Fehler ersichtlich? Wüsste nicht was ich noch
kontrollieren kann oder anders versuchen.

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Ja, ADCL und ADCH liegen beim Mega48 im Extended-I/O-Bereich, der wie
SRAM mit STS/LDS angesprochen werden muss. Beim Mega8 liegen alle
I/O-Register im normalen I/O-Bereich, die werden dann mit IN/OUT
angesprochen. Das ist beim Mega48 zwar blöd, aber ich kann es nicht
ändern.

Schau dir mal das Beispiel im Anhang an, ist schon etwas älter, ist
auch schonmal irgendwo hier im Forum veröffentlicht, ich finde es aber
nicht wieder.
Es ist für Mega8535, der ist bis auf die Anzahl der I/O-Pins mit dem
Mega8 vergleichbar.
Schau dir besonders den Timer-Interrupt an, eine solche Zeitbasis
braucht fast jedes Programm, mit Warteschleifen (wie in deinem
Beispiel) kommst du nicht weit. Das war auch der Grund, weshalb ich das
Testprogramm für dich mit einem Timer-Interrupt ausgestattet hatte. Ein
Timer-Interrupt im Programm erleichtert das Programmieren gewaltig.

Leg doch erstmal den (im Timer-Int) zyklisch eingelesenen ADC-Wert an
einen Port, an den du 8 LEDs (mit Vorwiderständen) angeschlossen hast.
Damit siehst du sofort jede Änderung des eingelesenen Wertes.

...

von Sven F. (skfink)


Lesenswert?

danke jetzt klappts :)

Dass ein Timer der warteschleife vorzuziehen ist, ist mir klar, nur
wollte ich nicht alles auf einmal machen sondern ein Feature des Atmega
nach dem anderen kennen lernen.
Und nun ist der Timer dran, hab ja dank dir jetzt genügend Material
mich damit eingehend damit zu beschäftigen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.