Hallo,
habe folgendes Problem:
Möchte mit einem AVR ATmega16 8 Spannungen überwachen.
Mit einer Spannung funktioniert es, aber sobald ich es mit Multiplexing
versuche, um auch die anderen 7 ADC-Kanäle auslesen zu können, gibt es
Probleme.
Meine Vermutung ist, dass ich die Bits für die Kanalwahl im
ADMUX-Register möglicherweise zum falschen Zeitpunkt update.
Im Datenblatt steht dazu:
"If these bits [MUX0 bis 3] changed during conversion, the change will
not take effect until this conversion is complete (it means while the
ADIF bit in ADCSRA register is set)."
Die AD-Konvertierung ist ja eigentlich abgeschlossen, wenn das ADSC
-Flag im ADCSRA-Register gelöscht wurde?!!!
Vielleicht also am besten erst mal die Frage, woran man nun das
Konvertierungsende "besser" erkennt:
an ADSC = 0
oder
an ADIF = 1
???
Hi
>Mit einer Spannung funktioniert es, aber sobald ich es mit Multiplexing>versuche, um auch die anderen 7 ADC-Kanäle auslesen zu können, gibt es>Probleme.
Welche?
>Vielleicht also am besten erst mal die Frage, woran man nun das>Konvertierungsende "besser" erkennt:>an ADSC = 0>oder>an ADIF = 1
An dem Wechsel von 1 nach 0.
MfG Spess
Hi,
ich hab mal was ähnliches gemacht mit 4 Kanälen,
Hab einfach eine Einzelmessung gestarted, und im ADC Interrupt den Kanal
weitergeschaltet und die nächste Messung gestartet.
Funktioniert so problemlos.
spess53 schrieb:>>Probleme.>> Welche?
Es gibt nur jeder zweite Kanal ein Ergebnis (das aber selber richtig zu
sein scheint).
spess53 schrieb:>>an ADIF = 1>> An dem Wechsel von 1 nach 0.
Also ADSC setzen, um die Konvertierung im Single Conversion Mode zu
starten und warten, bis ADSC von der Hardware wieder gelöscht ist, um
das Ende der Konvertierung zu erkennen?
@nobi: Danke für den Code!!!
Zurück zur Hauptfrage:
frederik_u schrieb:> Also ADSC setzen, um die Konvertierung im Single Conversion Mode zu> starten und warten, bis ADSC von der Hardware wieder gelöscht ist, um> das Ende der Konvertierung zu erkennen?
Wann ist denn eigentlich der günstigste Zeitpunkt, den neuen ADC-Kanal
durch Update der MUX-Register zu definieren?
Hi
>Es gibt nur jeder zweite Kanal ein Ergebnis (das aber selber richtig zu>sein scheint).
Dann musst du halt mal dein Programm zeigen.
>Also ADSC setzen, um die Konvertierung im Single Conversion Mode zu>starten und warten, bis ADSC von der Hardware wieder gelöscht ist, um>das Ende der Konvertierung zu erkennen?
Ja. Oder einfach den ADC Complete Interrupt benutzen. Dann entfällt die
Warterei.
MfG Spess
spess53 schrieb:> Ja. Oder einfach den ADC Complete Interrupt benutzen. Dann entfällt die> Warterei.
Wobei man eigentlich nur das Flag pollen muss, der ganze Overhead für
die ISR lohnt meist nicht, wenn man nur den MUX eins weiter setzt und
den Wert abholt...
flo schrieb:> Ingo schrieb:>> Wobei man eigentlich nur das Flag pollen muss>> was bedeutet "flag pollen"?
Flagsoolange abfragen bis eine Änderung eingetreten ist und dann
entsprechend reagieren.
Hi
>Wobei man eigentlich nur das Flag pollen muss, der ganze Overhead für>die ISR lohnt meist nicht, wenn man nur den MUX eins weiter setzt und>den Wert abholt...
Und das ist keine Warterei? Bei z.B. 8MHz sind das zwischen 500 und 2000
Takte.
MfG Spess
Der Code ist leider auf einem anderen Rechner, könnte ihn aber heute
Nachmittag übertragen.
Ich würde auch ganz gerne einzelne ADC-Kanäle gezielt auslesen können,
also nicht immer nur der Reihe nach.
Flag pollen ist eine gute Idee, das Programm kann seine anderen Aufgaben
erledigen und dazwischen wird hin und wieder das Flag gepollt.
Wobei mit "Flag pollen" das Pollen des ADSC-Flags auf Null gemeint ist,
nehme ich an... !??
spess53 schrieb:> Und das ist keine Warterei? Bei z.B. 8MHz sind das zwischen 500 und 2000> Takte.
Nein, nicht wenn man das Flag in einem festen Zeitraster pollt, man muss
ja nicht unbedingt darauf warten das er fertig wird, sondern man kann
das auch geschickter machen, wundert mich das du das nicht kennst...
Ingo
spess53 schrieb:> Hi>>>Wobei man eigentlich nur das Flag pollen muss, der ganze Overhead für>>die ISR lohnt meist nicht, wenn man nur den MUX eins weiter setzt und>>den Wert abholt...>> Und das ist keine Warterei? Bei z.B. 8MHz sind das zwischen 500 und 2000> Takte.
natürlich ist das Warterei.
*) die Frage ist, wie weh das in einer bestimmten Situation tut
*) weiters ist es oft möglich, das Programm etwas umzugestalten, so
dass der ADC sampelt, während das Programm etwas anderes macht.
Wichtig ist ja nur, dass das Ergebnis vorliegt, wenn es benötigt
wird, bzw. alle Zwischenarbeiten erledigt wurden.
Speziell letzteres:
Man kann ja den ADC bereits mit dem neuen Kanal schon wieder starten,
während noch das vorhergehende Ergebnis aufgearbeitet wird.
also nicht (Prinzipskizze)
while( 1 ) {
starte ADC
warte auf Ergebnis
bearbeite Ergebnis
Ergebnis ausgeben
}
sondern
starte ADC
while( 1 ) {
x = warte auf Ergebnis
starte ADC
bearbeite x
x ausgeben
}
oder irgendein anderes Schema um das Pollen durchzuführen.
>Wann ist denn eigentlich der günstigste Zeitpunkt, den neuen ADC-Kanal>durch Update der MUX-Register zu definieren?
Unmittelbar vor dem Starten einer neuen Wandlung?
AdcVoltage(x) ist ein Makro in meinem Headerfile definiert, dass mir
fuer den jeweiligen Kanal die Spannung berechnet, unter Berücksichtigung
eines Fehlerkorrekturfaktors, der die Hardwaretolleranzen meiner
speziellen Eingangsbeschaltung berücksichtigt.
Da Dies aber zur Auslesung der ADC Werte nichts zur Sache tut, hab ich
das Makro hier nicht gepostet.
nobi schrieb:> AdcVoltage(x) ist ein Makro
Dann halte dich an die einzige freiwillge Konvention, die es tatsächlich
schafft, dass Millionen Programmierer weltweit sie einhalten:
Makronamen werden ausschliesslich in Grossbuchstaben geschrieben und
auch umgekehrt sind Namen komplett in Grossbuchstaben ausschliesslich
für Makros reserviert.
Schreibst du
val = ADCVOLTAGE(0);
dann kann jeder C-Programmierer im Dschungel des Amazonas erkennen, dass
ADCVOLTAGE (oder ADC_VOLTAGE um es lesbarer zu machen) ein Makro ist.
Im übrigen sehe ich nicht, was dir hier ein Makro bringen würde. Ich
sehe aber, dass dir ein Makro unter Umständen eine Menge Mehrarbeit
anstelle einer Funktion aufbürdet.
Also, habe den Fehler gefunden. Es muss nach jedem ADC-Multiplexing
(ADC-Kanal-Umschaltung) eine "Leerlauf"-ADC-Konvertierung durchgeführt
und verworfen werden, bevor die eigentliche Messung stattfinden kann.
Viele Grüße
Hi
>Also, habe den Fehler gefunden. Es muss nach jedem ADC-Multiplexing>(ADC-Kanal-Umschaltung) eine "Leerlauf"-ADC-Konvertierung durchgeführt>und verworfen werden, bevor die eigentliche Messung stattfinden kann.
Habe ich noch nie gemacht. Ging immer ohne. Da ist etwas anderes faul.
MfG Spess
Ingo schrieb:> Spess hat recht, nur der XMega hat (oder hatte?) dieses Problem, normale> Tinys und Megas eigentlich nicht. Wie hochohmig gehst du an den ADC ran?
10k gegen Masse. Werde es noch mal genauer untersuchen...
Hallo Ingo,
das war nicht gemeint, der Innenwiderstand deiner Stromquelle soll <10kΩ
betragen.
Ich habe deshalb bei einem Messgerät einen präzisions OPA im SOT-23
Gehäuse mit Vu=1 eingesetzt.
Die externe Spannugsreferenz hat VRef = 2,500V.
Hi
>10k gegen Masse.
Das ist nicht die Quellimpedanz.
> Werde es noch mal genauer untersuchen...
Dann schreib gleich noch dazu, wie hoch dein ADC-Takt ist. Also die ADPS
Bits.
MfG Spess
Danke für die Antworten!
Kleine Zwischenfrage, wie müssen eigentlich die I/O-Pins für ADC-Betrieb
definiert sein?
Sie sind momentan als Eingänge konfiguriert per
DDRX &= ~(1<<DDXy)
wobei X = Port und y = jeweiliger Pin
An den ADC-Ports sitzt ein Spannungsteiler:
Messspannung (max. 8,4V)
|
10k
|
ADC-Pin
|
10k
|
GND
ADC-Spannungsteiler so wie im Buch "AVR" von Florian Schäffer
Hi
>Kleine Zwischenfrage, wie müssen eigentlich die I/O-Pins für ADC-Betrieb>definiert sein?>Sie sind momentan als Eingänge konfiguriert per>DDRX &= ~(1<<DDXy)>wobei X = Port und y = jeweiliger Pin
So wie du es gemacht hast. Allerdings solltest du auch vermeiden den
internen Pull-Up-Widerstand einzuschalten.
>ADC-Spannungsteiler so wie im Buch "AVR" von Florian Schäffer
wenn du keine schnellen Spannungsänderungen messen willst, kann ein 100n
Kondensator zwischen AD-Pin und GND nicht schaden.
MfG Spess
spess53 schrieb:>>DDRX &= ~(1<<DDXy)>>>wobei X = Port und y = jeweiliger Pin>> So wie du es gemacht hast. Allerdings solltest du auch vermeiden den> internen Pull-Up-Widerstand einzuschalten.
Selbstverständlich!
spess53 schrieb:> wenn du keine schnellen Spannungsänderungen messen willst, kann ein 100n> Kondensator zwischen AD-Pin und GND nicht schaden.
Gute Idee! Die Spannungen könnten im Prinzip sogar im Sekundentakt oder
noch langsamer ausgelesen werden...
Hi
>Selbstverständlich!
Nicht ganz. Andere IOs, wie z.B. USART oder TWI, übernehmen die volle
Kontrolle über die entsprechenden Pins. Da kannst du die Portregister
setzen wie du lustig bist.
MfG Spess
spess53 schrieb:> Hi>>>Selbstverständlich!>> Nicht ganz. Andere IOs, wie z.B. USART oder TWI, übernehmen die volle> Kontrolle über die entsprechenden Pins. Da kannst du die Portregister> setzen wie du lustig bist.>> MfG Spess
Aber nur, wenn sie aktiviert werden, schätze ich... ?!!
Ansonsten könnte man vielleicht hinter das
DDRX = 0x00
zur Sicherheit noch ein
PORTX = 0x00
setzen, damit die internen Pullups wirklich deaktiviert sind...
(wenn das wirklich Sinn macht)
Hi
>Aber nur, wenn sie aktiviert werden, schätze ich... ?!!
Natürlich, nur wen die passenden Enable-Bits gesetzt sind.
>(wenn das wirklich Sinn macht)
Mcht nur Sinn wenn du vorher schon mal an dem Portregister rumgefummelt
hast. Nach einem Reset/Power on ist das Register automatisch mit 0x00
initialisiert.
MfG Spess