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.
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.
1. Woher weißt du, dass die Wandlung fertig ist? 2. Liefert dein Sensor genug Strom? Oliver
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.
Weshalb wird der ADC zwischendrin abgeschaltet? Ich würde ihn durchlaufen lassen, zumindest jetzt mal als Schnellschuss zur Probe.
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
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.
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.
Ben B. schrieb: > Ansonsten muß man wirklich > den ADC abschalten während man ADMUX ändert. Auch das muß man nicht. Siehe Datenblatt. Oliver
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.
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.
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.
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...
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.
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.
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.
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; |
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...
> Dabei entsteht kein Fehler, wenn man es richtig programmiert.
Wenn das Wörtchen wenn nicht wäre...
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.
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...
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?
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.
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.
MWS schrieb: > ADMUX = (1<<REFS0); > ADMUX = (uint8_t)(ADMUX |(kanal & 0x07)); > Deine zweite Zuweisung zerschmeisst Dir die erste. Warum?
Stefan ⛄ F. schrieb: > Warum? Mit der ersten wird der Multiplexer single ended auf ADC0 geschaltet, mit der zweiten auf ADC<kanal> ;-)
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.
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.
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...)
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.
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 :)
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?
Stefan ⛄ F. schrieb: > Nicht mehr als du. Na, das wollen wir doch mal hoffen... > Wie man in den Wald hinein ruft .... ???
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.