Forum: Mikrocontroller und Digitale Elektronik Atmega 1284P ADC-Multiplexer


von Heinz W. (bibermann)


Angehängte Dateien:

Lesenswert?

Guten Tag und hallo,
ich benötige Hilfe. Ich nutze bei einem Atmega 1284P alle 8 ADC-Kanäle. 
An ADC 0 und ADC 1 habe ich jeweils einen Sharp IR-Distanzsensor 
geschaltet. Die Daten vom ADC 1 sind plausibel, von ADC 0 werden die 
Werte addiert und laufen somit in meinen vorgegebenen Grenzwert. Ich 
habe für Testzwecke den Sensor einmal auf ADC 5 gelegt, dann sind alle 
Ergebnisse plausibel. Leider geht diese Lösung nur temporär. Ich muss 
die Sensoren auf ADC 0 und ADC 1 einlesen. Ich habe die wichtigen 
Codeausschnitte beigefügt. Wo mache ich einen Gedankenfehler.? Mir ist 
bekannt das der MUX 65µs Schaltzeit hat. Eine eingebaute Verzögerung 
hinter der Kanalwahl hat aber nicht die erwarteten Erfolgt gebracht.

von Dietrich L. (dietrichl)


Lesenswert?

Heinz W. schrieb:
> Mir ist
> bekannt das der MUX 65µs Schaltzeit hat. Eine eingebaute Verzögerung
> hinter der Kanalwahl hat aber nicht die erwarteten Erfolgt gebracht.

Als erten Versuch würde ich probieren, nach Kanalwechsel statt warten 
das erste Wandelergebniss wegzuwerfen.
Ich habe mir den Code nicht angeschaut, daher weiß ich nicht, ob du das 
eventuell schon machst.

von Oliver S. (oliverso)


Lesenswert?

1. Woher weißt du, dass die Wandlung fertig ist?
2. Liefert dein Sensor genug Strom?

Oliver

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Das erste ADC-Ergebnis wegschmeißen bringt nichts, das ist nur sinnvoll 
wenn es das allererste Ergebnis nach dem Start des ADC-Moduls ist. Der 
Multiplexer hat eine nachgeschaltete Sample&Hold-Stufe, elektrisch ist 
also nur der Beginn der Wandlung interessant. Ich vermute einen 
Programmierfehler, aber µC-C ist nicht so meines. Ich schaue es mir 
trotzdem mal an.

Meine AlarmSau verwendet den gleichen µC und liest alle 8 Kanäle 
nacheinander ein und da habe ich keine solchen Probleme.

Edit:
Ich denke ich hab es, Du schaltest tatsächlich den ADC ein und danach 
wieder aus. Musst du das aus Stromspargründen machen?

Besser wäre es, den ADC einmal zu initialisieren (Modul einschalten, 
Referenz wählen, die wechselst Du ja auch nicht und eine dummy-Messung 
zum Wegschmeißen) und es dann durchlaufen zu lassen, nur noch die 
Messungen anzustoßen ohne das ADC-Modul immer wieder ein- und 
auszuschalten.

von S. Landolt (Gast)


Lesenswert?

Weshalb wird der ADC zwischendrin abgeschaltet? Ich würde ihn 
durchlaufen lassen, zumindest jetzt mal als Schnellschuss zur Probe.

von Oliver S. (oliverso)


Lesenswert?

Ben B. schrieb:
> Das erste ADC-Ergebnis wegschmeißen bringt nichts, das ist nur
> sinnvoll
> wenn es das allererste Ergebnis nach dem Start des ADC-Moduls ist.

Das ist sinnvoll und erforderlich in den Situationen, die explizit im 
Datenblatt aufgeführt werden. Der Start des ADC-Moduls gehört nicht 
dazu.

Oliver

von c-hater (Gast)


Lesenswert?

Heinz W. schrieb:

> ich benötige Hilfe. Ich nutze bei einem Atmega 1284P alle 8 ADC-Kanäle.
> An ADC 0 und ADC 1 habe ich jeweils einen Sharp IR-Distanzsensor
> geschaltet. Die Daten vom ADC 1 sind plausibel, von ADC 0 werden die
> Werte addiert und laufen somit in meinen vorgegebenen Grenzwert. Ich
> habe für Testzwecke den Sensor einmal auf ADC 5 gelegt, dann sind alle
> Ergebnisse plausibel. Leider geht diese Lösung nur temporär. Ich muss
> die Sensoren auf ADC 0 und ADC 1 einlesen. Ich habe die wichtigen
> Codeausschnitte beigefügt. Wo mache ich einen Gedankenfehler.?

> Mir ist
> bekannt das der MUX 65µs Schaltzeit hat.

Nein, das ist nicht so. Wie es wirklich ist, steht im Datenblatt.

Grundsätzlich machst du aber einen Fehler: Viel zuviel 
Verwaltungsaufwand und das auch noch verteilt über main() und die ISR 
und dann auch noch ohne die nötigen Schutzmaßnahmen.

Das kann man viel einfacher und effizienter lösen. Die Grundidee für 
eine effiziente Nutzung ist: ADC durchlaufen lassen (also FreeRunning 
oder Auto-Trigger). Die gesamte Verwaltung der Wandlungen läßt du in der 
ISR passieren (du wirst sehen, dass sich der Verwaltungsaufwand dadurch 
insgesamt dramatisch verringert).

Ist das soweit erledigt, brauchst du zum Abrufen eines Messwertes 
einfach nur noch auf das Array zugreifen und kannst (nach den ersten 9 
Messungen) jederzeit sicher sein, dass der abgerufene Wert erstens 
korrekt ist und zweitens nicht älter als einen kompletten Meßzyklus. Das 
einzige was dann noch zu beachten ist: der Zugriff auf den Messwert im 
Array muß unter Interruptsperre erfolgen. Also mit Atomic absichern.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Free Running Mode mit Interrupts würde ich nur machen wenn es nicht 
anders geht bzw. ADMUX nicht geändert wird. Ansonsten muß man wirklich 
den ADC abschalten während man ADMUX ändert.

> Der Start des ADC-Moduls gehört nicht dazu.
Könntest Du sogar Recht haben, ich weiß nicht mehr genau welche 
Controller mit denen ich gebastelt habe das "in Hardware machen" bzw. 
die erste Wandlung nach dem Einschalten des ADC doppelt so lange dauert 
und welche nicht.

von Oliver S. (oliverso)


Lesenswert?

Ben B. schrieb:
> Ansonsten muß man wirklich
> den ADC abschalten während man ADMUX ändert.

Auch das muß man nicht. Siehe Datenblatt.

Oliver

von Stefan F. (Gast)


Lesenswert?

Ben B. schrieb:
> Ansonsten muß man wirklich
> den ADC abschalten während man ADMUX ändert.

Nein, muss man nicht. Es ist sicher, den Multiplexer in der ISR 
umzuschalten. Man muss nur wissen, dass zu diesem Zeitpunkt bereits die 
nächste Messung läuft. Der durch den Multiplexer gewählte Kanal wird 
erst danach gemessen.

Oder anders gesagt: Man hat immer zwei Positionen Versatz zwischen dem 
in der ISR eingestellten Kanal und dem gerade geliefertem Ergebnis.

Für die erste Messung nach dem Einschalten des ADC bzw Umschalten des 
Multiplexers gilt zu beachten, dass der S&H Kondensator eine Weile zum 
Umladen braucht. Im Free Running Modus sorgt der ADC von alleine dafür, 
dass das Timing eingehalten wird.

von c-hater (Gast)


Lesenswert?

Ben B. schrieb:
> Free Running Mode mit Interrupts würde ich nur machen wenn es nicht
> anders geht bzw. ADMUX nicht geändert wird. Ansonsten muß man wirklich
> den ADC abschalten während man ADMUX ändert.

Nein, Unsinn. Das geht wunderbar, auch ohne den ADC abzuschalten. Das 
ist die Regel.

Wie bei jeder Regel gibt es allerdings auch bei dieser Ausnahmen. Die 
rühren immer her von zu geringer Quell-Impedanz. Bei den externen 
ADC-Quellen hat man das ja selber in der Hand (durch entsprechende 
Auslegung der Hardware), Probleme kann es aber bei internen Quellen 
geben. Das betrifft bestimmte Quellen für die Referenzspannung und 
teilweise auch die internen Temperatursensoren. Details dazu gibt es im 
jeweiligen DB.

Hier kann man auf zweierlei Art tricksen:
1) man verringert einfach die Wandlerrate
2) man gestaltet die Verwaltung der Messungen so, dass man ein und 
dieselbe Meßaufgabe auch mehrmals hintereinander erledigen kann, fügt 
also ein paar Dummykanäle ein.

Beides führt letztlich dazu, dass auch die leicht behinderten Quellen 
noch folgen können. 2) ist besser, denn es erhöht die Zeit für den 
gesamten Meßzyklus nur in dem unbedingt nötigen Maß, hat aber den 
Nachteil, dass ein paar Byte Speicher für die (nutzlosen) Ergebnisse der 
Dummykanäle verschwendet werden.

Aber, bei dem konkreten Problem des TO spielt diese Sache überhaupt 
keine Rolle (sollte es zumindest nicht). Der schaltet ja nur zwischen 
externen single-ended Quellen um. Wenn es doch zu Problemen kommt, kann 
man allerdings mit exakt denselben Maßnahmen auch hier für Abhilfe 
sorgen.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Im free running mode ist nicht erkennbar wo der ADC mit seiner Wandlung 
gerade ist bzw. wann der nächste Interrupt ausgelöst wird. Sollte also 
einmal aus welchem Grund auch immer die ADC-Interruptroutine verzögert 
ausgeführt werden, kann es schnell passieren, daß der ADC seine nächste 
Messung startet während das Programm gerade an ADMUX herumbastelt.

von c-hater (Gast)


Lesenswert?

Ben B. schrieb:

> Im free running mode ist nicht erkennbar wo der ADC mit seiner Wandlung
> gerade ist bzw. wann der nächste Interrupt ausgelöst wird.

Das ist richtig.

> Sollte also
> einmal aus welchem Grund auch immer die ADC-Interruptroutine verzögert
> ausgeführt werden, kann es schnell passieren, daß der ADC seine nächste
> Messung startet während das Programm gerade an ADMUX herumbastelt.

Das kann erst dann passieren, wenn die ISR mindestens um ca. einen 
ganzen Wandlerzyklus verzögert wurde. Wenn das passiert, stimmt aber 
grundsätzlich mit dem Gesamtprogramm etwas nicht.

ADC-Zyklen sind normalerweise vergleichsweise endlos lang, weil die ADC 
scheisselangsam ist.

Aber ja, wenn man als Systemtakt nur einen Uhrenquarz hat oder eine 
128kHz-ULP-Quelle, dann muss man sich natürlich wirklich auch um sowas 
kümmern. Ändert aber auch dann nichts Grundsätzliches: Es stimmt dann 
was mit dem Gesamtprogramm (also dessen Konzept) nicht...

von Stefan F. (Gast)


Lesenswert?

Ben B. schrieb:
> Im free running mode ist nicht erkennbar wo der ADC mit seiner Wandlung
> gerade ist

Das ist richtig. Man weiß aber, dass er das Ergebnis von dem Kanal 
liefert, der beim vorherigen Aufruf der ISR eingestellt war. Das geht 
so:
1
int adc_werte[8]; // 8 Werte für die 8 Kanäle
2
3
beginn der ISR:
4
   daten[last_channel]=ADC;
5
   last_channel=ADMUX;
6
   next_channel=(last_channel+1) & 7;
7
   ADMUX=next_channel;
8
ende der ISR

Wenn du das nach diesem Prinzip programmierst und einen Interrupt 
verpasst, dann fällt einfach eine Messung aus. Die darauf folgende wird 
trotzdem ins richtige Array-Element geschrieben. Selbst wenn du mehrere 
Interrupts verpasst, dann misst er hat ein paar mal den selben Kanal 
nacheinander. Das Ergebnis landet auch dann im richtigen Array-Element.

Bitte noch berücksichtigen, dass in dem ADMUX Register noch ein paar 
andere Bits stehen. Ich habe diesen Pseudocode vereinfacht dargestellt 
um seine Funktionsweise klar zu stellen.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Ihr habt beide Recht wenn ihr sagt, daß das bei einem korrekt 
geschriebenen Programm sehr unwahrscheinlich ist und die CPU im 
Vergleich zum ADC sehr schnell ist. Wenn man nun aber den ADC ausreizt 
(der liefert auch bei 1Mhz ADC-Takt noch ziemlich brauchbare 
8-Bit-Ergebnisse) und mit einem vergleichsweise langsamen CPU-Takt 
herangeht bzw. das Programm unsauber geschrieben ist (Klassiker wäre ein 
delay in einer ISR), dann steigt die Wahrscheinlichkeit für so einen 
Fehler beträchtlich.

Ich programmiere sowas lieber auf Nummer sicher anstatt hinterher 
monatelang einen selten auftretenden Fehler zu suchen, den man nicht mal 
zuverlässig reproduzieren kann.

von Carl D. (jcw2)


Lesenswert?

Im angehängten Code steht drin, daß alle Kanäle nacheinander gesampelt 
werden und dann der ADC ausgeschaltet wird. Also wird der Kanal 0 immer 
als "erster Wert nach dem ADC-Start" gesampelt wird und genau der ist 
nun so falsch, wie der Hersteller s verspricht. Also alles ok.

von Stefan F. (Gast)


Lesenswert?

Ben B. schrieb:
> dann steigt die Wahrscheinlichkeit für so einen Fehler beträchtlich.
> Ich programmiere sowas lieber auf Nummer sicher

Dabei entsteht kein Fehler, wenn man es richtig (so wie ich es gezeigt 
habe) programmiert.

Aber ich habe auch für dich einen Vorschlag:
1
static volatile uint16_t adc_values[8];
2
3
ISR(ADC_vect) {
4
    // ADC auslesen
5
    uint16_t value = ADC;
6
    uint8_t channel = ADMUX & 7;
7
    adc_value[channel]=value;
8
9
    // Es gibt nur 8 Kanäle
10
    if (++channel >= 8) {
11
        channel=0;
12
    }
13
14
    // Schalte zum nächsten Kanal
15
    ADMUX=(ADMUX & ~7) | channel;
16
17
    // Starte nächste Messung
18
    ADCSRA |= (1<<ADSC); 
19
}
20
21
// Starte den ADC
22
// Aktiviere ADC Konvertierung im Einzelschritt Modus mit F_CPU/128
23
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE) | (1<<ADSC); 
24
ADMUX = REF_VCC;

von c-hater (Gast)


Lesenswert?

Ben B. schrieb:

> Ich programmiere sowas lieber auf Nummer sicher

Dann bleibt nur: Das tun, was ich auch tue: Analyse der Runtime, 
insbesondere bezüglich der ISRs. Genauer: des darin exclusiv, also unter 
Interruptsperre, ausgeführten Codes (und auch des Codes, der in main() 
exclusiv ausgeführt wird bzw. werden muss)

Kein Ding, wenn man halt komplett in Asm programmiert. Genau deswegen 
bevorzuge ich Asm überall da, wo's eng werden könnte...

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Dabei entsteht kein Fehler, wenn man es richtig programmiert.
Wenn das Wörtchen wenn nicht wäre...

von Stefan F. (Gast)


Lesenswert?

c-hater schrieb:
> Kein Ding, wenn man halt komplett in Asm programmiert

Das ist auch in C kein Ding, du weißt das ganz genau. Hör' doch mal auf 
mit diesem albernen Theater.

von c-hater (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:

> Das ist auch in C kein Ding

Sehr viel umständlicher, sehr viel mehr Arbeitsaufwand.

Und: Die Analyse ist ja nur der erste Teil. Danach muß man dann ja auch 
noch irgendwas am Problem ändern, um zu einer funktionierenden Lösung zu 
kommen...

von Joachim B. (jar)


Lesenswert?

c-hater schrieb:
> Wie bei jeder Regel gibt es allerdings auch bei dieser Ausnahmen. Die
> rühren immer her von zu geringer Quell-Impedanz.

???

meintest du "zu hohe Quell-Impedanz" welche dazu führt das der SH 
Kondensator nicht schnell genug umgeladen wird?

von c-hater (Gast)


Lesenswert?

Joachim B. schrieb:

> c-hater schrieb:
>> Wie bei jeder Regel gibt es allerdings auch bei dieser Ausnahmen. Die
>> rühren immer her von zu geringer Quell-Impedanz.
>
> ???
>
> meintest du "zu hohe Quell-Impedanz" welche dazu führt das der SH
> Kondensator nicht schnell genug umgeladen wird?

Ähemm... Ja, natürlich.

von MWS (Gast)


Lesenswert?

1
     ADMUX    = (1<<REFS0);        //Referenzspannung ist AVCC
2
     ADMUX     = (uint8_t)(ADMUX |(kanal & 0x07));  //Kanal wählen, nur single ended
Deine zweite Zuweisung zerschmeisst Dir die erste.

von Stefan F. (Gast)


Lesenswert?

MWS schrieb:
> ADMUX    = (1<<REFS0);
> ADMUX    = (uint8_t)(ADMUX |(kanal & 0x07));
> Deine zweite Zuweisung zerschmeisst Dir die erste.

Warum?

von Wolfgang (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Warum?

Mit der ersten wird der Multiplexer single ended auf ADC0 geschaltet, 
mit der zweiten auf ADC<kanal> ;-)

von c-hater (Gast)


Lesenswert?

Wolfgang schrieb:

> Stefan ⛄ F. schrieb:
>> Warum?
>
> Mit der ersten wird der Multiplexer single ended auf ADC0 geschaltet,
> mit der zweiten auf ADC<kanal> ;-)

Ja klar, temporär (für sehr wenige Takte) wird dem IO-Register 
tatsächlich ein falscher Wert zugewiesen. Das spielt aber normalerweise 
keine Rolle, weil dieses IO-Register eh' nur ein Bufferregister ist. 
Entscheidend ist der Inhalt, den es zu dem Zeitpunkt hat, wenn die ADC 
im Rahmen ihres Zyklus den Inhalt dieses Bufferregisters auf die 
Hardware anwendet. Wann das ist, steht im DB.

von Peter D. (peda)


Lesenswert?

c-hater schrieb:
> Das kann erst dann passieren, wenn die ISR mindestens um ca. einen
> ganzen Wandlerzyklus verzögert wurde. Wenn das passiert, stimmt aber
> grundsätzlich mit dem Gesamtprogramm etwas nicht.

Das muß nicht sein. Es kann durchaus längere Interruptsperren geben, die 
nur selten eintreffen.
Man bricht sich ja nichts dabei ab, die Messung nach dem Muxen zu 
starten. Das hat außerdem den Vorteil, daß der Arrayindex gleich dem 
Eingang ist. Bei free Running muß der Mux schon vor dem Auslesen 
umgeschaltet werden, d.h. das Ergebnis auf dem vorigen Arrayindex 
gespeichert werden.

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Das muß nicht sein. Es kann durchaus längere Interruptsperren geben, die
> nur selten eintreffen.

Sowas würde ich generell erstmal für einen ziemlich dramatischen Bug 
halten.

Aber egal. Liefere einfach mal ein konkretes Beispiel. Vielleicht lohnt 
es, das zu diskutieren (aus meiner Sicht kann das allerdings eigentlich 
nur darauf hinauslaufen: wie vermeidet man genau diese Situation, soviel 
sei vorab schon erwähnt...)

von Stefan F. (Gast)


Lesenswert?

c-hater schrieb:
> Sowas würde ich generell erstmal für einen ziemlich dramatischen Bug
> halten.

Die Uhr von Windows XP wurde langsam, wenn die CPU längere zeit hoch 
ausgelastet ist. Das war schon ätzend, vor allem weil wir damals eine XP 
Rechner als Server verwenden mussten, weil eine Anwendung nicht unter 
Windows 2000 laufen wollte.

Gottseidank ist das vorbei.

von Joachim B. (jar)


Lesenswert?

Stefan ⛄ F. schrieb:
> Die Uhr von Windows XP wurde langsam, wenn die CPU längere zeit hoch
> ausgelastet ist.

 und die Uhr von Raspian Buster auf dem PI3 steht seit gestern auf 21:17 
Uhr weil er immer noch am kompilieren ist, da meckere mal jemand über 
windows und lobe Linux :)

von c-hater (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:

> Die Uhr von Windows XP wurde langsam, wenn die CPU längere zeit hoch
> ausgelastet ist.

Tatsächlich? Ist mir nie aufgefallen. Und ich habe XP schon benutzt, 
bevor es das in Deutschland überhaupt zu kaufen gab und noch lange, 
nachdem Microsoft den Support dafür eingestellt hat.

Aber: Selbst wenn das tatsächlich so gewesen sein sollte: Was zum Teufel 
hat das jetzt mit einem AVR8 und dessen ADC zu schaffen?

Bist du stoned?

von Stefan F. (Gast)


Lesenswert?

c-hater schrieb:
> Bist du stoned?

Nicht mehr als du.

Wie man in den Wald hinein ruft ....

von c-hater (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:

> Nicht mehr als du.

Na, das wollen wir doch mal hoffen...

> Wie man in den Wald hinein ruft ....

???

von Peter D. (peda)


Lesenswert?

c-hater schrieb:
> Sowas würde ich generell erstmal für einen ziemlich dramatischen Bug
> halten.

Nö, das ist einfach nur defensives Programmieren. Ob eine AD-Wandlung 
mal etwas später erfolgt, spielt in der Regel keine Rolle. Wenn aber das 
Ergebnis auf dem falschen Arrayindex gespeichert wird, weil das free 
Running zu früh die nächste Wandlung startet, ist das ein schwerer 
Fehler.

von Stefan F. (Gast)


Lesenswert?

c-hater schrieb:
>> Wie man in den Wald hinein ruft ....
> ???

Wie man in den Wald hinein ruft, so schallt es heraus.

Das ist ein Sprichwort dass man seit dem Mittelalter Leuten sagt, die 
über ihren Umgang mit Mitmenschen nachdenken sollen. Es bedeutet, dass 
man mit Dir so umgeht, wie du mit anderen.

von Moby (Gast)


Lesenswert?

Peter D. schrieb:
> Wenn aber das
> Ergebnis auf dem falschen Arrayindex gespeichert wird, weil das free
> Running zu früh die nächste Wandlung startet

Deshalb nutzt man Free Running auch mit System. Zum Beispiel in einem 
(sowieso schon vorhandenen) Timer- Interrupt der regelmäßig umschaltet 
(ggf. sogar eine neue Referenz einstellt) und man sich beim nächsten 
Interrupt sicher sein kann ein gültiges Ergebnis auszulesen. Das lässt 
sich je nach Anforderungen prima im Timing steuern. Wenn auch nicht mit 
höchstmöglichem Speed- dafür aber ist das Prinzip schön einfach.

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.