Hallo!
Ich bin noch relativ neu in der Welt der Mikrocontroller und hänge
gerade an einem kleinen ASM-Programm, das das Bitmuster eines
eingelesenen analogen Wertes auf 8 LEDs ausgeben soll.
Bei dem Programm im Anhang bekomme ich beim Kompilieren mit AVR Studio
immer den Fehler:
D:\Elektronik\ADC\ADC.asm(32): error: Overlap in .cseg: addr=0x20
conflicts with 0x20:0x21
Das macht der Compiler erst, nachdem ich die Sprungadresse für den
Timer0 mit .org angegeben habe.
Und nochwas:
Ich lasse ja momentan den ADC im Free Running Mode laufen und ich wollte
eigentlich, dass bei jedem fertig konvertierten Messwert ein Interrupt
ausgelöst wird. Ich habe aber hier im Forum gelesen, dass das nicht
sonderlich optimal wäre, weil ja dann alle paar Zyklen ein Interrupt
ausgelöst werden würde was bei umfangreichen Programmen zu Problemen
führen würde. Habe ich das so richtig verstanden?
Dann wäre also die optimale Lösung, den ADC über einen Timer zu
triggern, der z.B. alle paar ms ein Interrupt auslöst und damit den ADC
anstößt, oder?
Ich bedanke mich schon mal!
Grüße,
André
> D:\Elektronik\ADC\ADC.asm(32): error: Overlap in .cseg: addr=0x20> conflicts with 0x20:0x21
Das bedeutet, dass du dem Assembler gesagt hast, er soll zwei
verschiedene Instruktionen an derselben Adresse ablegen.
> .org ADCCAddr> rjmp ADC_finished
Das heißt, das rjmp ADC_finished soll an Adresse 0x20 abgelegt werden.
> .org OC0addr> rjmp Timer0_Compare
Und rjmp Timer0_Compare kommt an Adresse 0x14
> Timer0_Compare:> reti
Dann geht's einfach weiter. Das reti kommt an Adresse 0x15 und so
weiter. Irgendwann kommst du dann wieder bei 0x20 an, wo ja schon was
drin steht. Du mußt also am Schluß der Interruptvektortabelle nochmal
ein .org machen, das dafür sorgt, dass der eigentliche Code erst
dahinter anfängt.
> Ich lasse ja momentan den ADC im Free Running Mode laufen und ich> wollte eigentlich, dass bei jedem fertig konvertierten Messwert ein> Interrupt ausgelöst wird. Ich habe aber hier im Forum gelesen, dass das> nicht sonderlich optimal wäre, weil ja dann alle paar Zyklen ein> Interrupt ausgelöst werden würde
Nur, wenn dein Controller mit ziemlich niedrigem Takt läuft. Der ADC ist
im Normalfall deutlich langsamer.
> Dann wäre also die optimale Lösung, den ADC über einen Timer zu> triggern, der z.B. alle paar ms ein Interrupt auslöst und damit den ADC> anstößt, oder?
Es kommt drauf an, wie oft du überhaupt einen ADC-Wert brauchst.
Ok, das Problem mit dem Kompilieren konnte ich jetzt lösen.
Ich habe mal mit AVR Studio ein bisschen simuliert und in der Tat, der
ADC ist wirklich sehr langsam. Ein Problem, das ich mit dem Programm
oben im Anhang auch noch habe, ist, dass ein ADC-Interrupt nur 1x
ausgelöst wird und dann nie wieder. Sowohl im Simulator, als auch in der
fertigen Schaltung.
Ist das normal?
Naja, eigentlich bräuchte ich einen Messwert nur alle paar ms, von daher
ist eine Lösung mit einem Timer sicherlich angebrachter. Trotzdem würde
mich interessieren, warum nur 1x ein Interrupt ausgelöst wird.
Bit 5 – ADFR: ADC Free Running Select
When this bit is set (one) the ADC operates in Free Running mode. In
this mode, the
ADC samples and updates the Data Registers continuously. Clearing this
bit (zero) will
terminate Free Running mode.
Grüße
Walter
Dieses Bit gibts beim ATmega32 nur leider nicht (mehr). Stattdessen
wurde wohl das ADATE Bit und das Register SFIOR eingeführt. Aber das Bit
habe ich gesetzt und das Register ist so eingestellt, dass der Free
Running Mode aktiviert ist.
Ich habe nun mein Programm mal so umgebaut, dass der ADC auf Timer1
Overflow triggern soll. Aber auch das klappt nicht. Ein Interrupt wird
nur 1x aufgerufen, sowohl im Simulator als auch real.
Das Programm ist im Anhang.
Ich habe mir auch mal
http://www.mikrocontroller.net/attachment/12747/temp3.c angeguckt, aber
ich kann eigentlich keinen großen Unterschied erkennen.
> Ich habe nun mein Programm mal so umgebaut, dass der ADC auf Timer1> Overflow triggern soll. Aber auch das klappt nicht. Ein Interrupt wird> nur 1x aufgerufen, sowohl im Simulator als auch real.
Hä?
1
Timer1_Overflow:
2
reti
Wie soll da der ADC auf den TIMER1 OVERFLOW "triggern" ???
Im Datenblatt steht, dass es ein spezielles Register SFIOR gibt, mit dem
man die "ADC Auto Trigger Source" einstellen kann. Ich habe das Register
so eingestellt, dass "Trigger Source" auf Timer/Counter1 Overflow steht.
Also müsste doch eine Konvertierung ausgelöst werden, sobald Timer1
überläuft, oder?
Lass dich nicht beirren, du hast schon recht. Offensichtlich schaut
sonst keiner ins Datenblatt, bevor er antwortet, oder zumindest nicht
ins richtige.
Ich denke, folgender Auszug aus dem Datenblatt könnte relevant sein:
"Note that an Interrupt Flag will be set even if the specific interrupt
is disabled or the global interrupt enable bit in SREG is cleared. A
conversion can thus be triggered without causing an interrupt. However,
the Interrupt Flag must be cleared in order to trigger a new conversion
at the next interrupt event."
Zumindest, wenn du den Timer als Triggerquelle nutzt, mußt du also
dessen Interrupt-Flag löschen, damit eine neue Wandlung angestoßen
werden kann.
Genau, es ist essentiell wichtig, das Flag zu löschen. Achte auch
darauf, dass der Simulator im AVRStudio anscheinend den ADC-Auto-Trigger
nicht korrekt simuliert, obwohl alles korrekt eingestellt ist und es
auch in der Hardware tadellos funktioniert. Das Problem hatte ich mal.
Erstmal danke für die Antworten.
Welches Flag muss ich denn genau löschen? Das ADIF Flag? Oder das
Interrupt Flag vom Timer?
Ich hab jedenfalls gerade so ziemlich alles probiert, aber ich hatte
bisher immer noch keinen Erfolg.
Mein Programm sieht momentan so aus (siehe Anhang).
Ich habe doch richtig verstanden, dass wenn ich ein Flag löschen will,
es auf 1 setzen muss, oder?
Das TOV1 Flag wird aber (laut Datenblatt) beim Aufruf des
Interrupthandlers automatisch gelöscht, oder?
Das mit dem ADC Auto Trigger war mir bis dato unbekannt... aber man
lernt ja nie aus ;)
Gruß,
Magnetus
> Das TOV1 Flag wird aber (laut Datenblatt) beim Aufruf des> Interrupthandlers automatisch gelöscht, oder?
Ja, beim Aufruf des Timer-Interrupts. Dazu muss der aber auch aktiviert
sein.
In meinem Programm (siehe oben) habe ich das Timer-Interrupt aber
aktiviert und entsprechend eine Sprungroutine eingerichtet. Der Timer
funktioniert auch, aber der ADC will immer noch nicht funktionieren,
obwohl ich sogar das ADIF Flag gelöscht habe (und auch das TOV1-Flag).
Ok, ich habe nun das Problem gefunden.
In meinem Beispiel habe ich immer nach dem Lesen von ADCH auch noch ADCL
gelesen. Das darf man scheinbar nicht machen, weil der ADC dann
"gelockt" wird.
>Das darf man scheinbar nicht machen, weil der ADC dann>"gelockt" wird.
Das steht eigentlich auch im ADC-Abschnitt des Datenblattes ("Handling
16Bit-Registers" oder so...)
(Nur so als "Nachtret-Anmerkung" ;-)