Analogwerte kontinuierlich messen mit ATmega328 (Uno, Nano, Pro Mini u.s.w.)

Hallo,

ich habe immer wieder das Problem, dass ich mit dem Atmega328P fortlaufend Analogwerte auslesen muss, und der Arduino fast nur damit beschäftigt ist, auf die Werte zu warten, während er besseres zu tun hätte. Jetzt lese ich immer wieder mal was von einer kontinuierlichen Betriebsart des ADCs, wo ständig gemessen wird, und der aktuelle Wert dann immer aus einem Register gelesen werden kann.

Beispiel https://www-user.tu-chemnitz.de/~heha/hsn/ATmegaX8.chm/24.htm:

"Der Analog-Digital-Wandler kann in zwei Arten betrieben werden, der kontinuierlichen oder der einzelnen Wandlung. Bei der Einzelwandlung wird jede Wandlung durch das Programm einzeln gestartet. Bei der kontinuierlichen Wandlung werden die Eingänge ständig ausgewertet und die Daten im ADC Data Register aktualisiert. Das ADFR-Bit im ADCSR-Register wählt zwischen diesen beiden Möglichkeiten aus."

Das möchte ich so machen, dass der ADC ständig im Hintergrund einen Eingang ausliest, während im Programmablauf was anderes gerechnet wird. Ich find hierzu aber leider keine genaue Anleitung und komme da nicht weiter.

Zum Beispiel finde ich zum "ADC Control and Status Register A":

Bit 6: ADSC (ADC Start Conversion)

"Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.
Wenn das Bit nach dem Setzen des ADEN-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0."

Das verstehe ich z.B. nicht. Wenn für die kontinuierliche Messung dieses Bit gesetzt sein muss, warum setzt es sich dann nach der Messung zurück? Das ergibt doch keinen Sinn!? Oder wird nur bei Einzelmessung zurückgesetzt?

Weiß jemand genau wie es geht? Wäre super wenn mir jemand eine komplette Anleitung liefern könnte, mir allem was nötig ist, um beispielsweise den Eingang A0 kontinuierlich auszulesen, also alle nötigen Anweisungen im Setup und wie man die Werte dann liest. Weiter würde mich noch interessieren, ob und woran ich dann erkennen kann, dass ein neuer Wert da ist. (Sonst lese ich ja, wenn der zeitliche Abstand kurz ist, mehrmals den selben Messwert.) Gibt es dazu vielleicht einen Zähler, der die Messungen zählt, und den ich abfragen kann. (Ein eventuell möglicher Interrupt bei jedem neuen Wert würde mir allerdings nicht helfen, da der restliche Programmablauf stellenweise sehr zeitkritisch ist.)

Dein Titel passt nicht zu deiner Fragestellung, den solltest du anpassen.

Und wenn wir helfen sollen, brauchen wir deinen Sketch, poste den bitte in Code-Tags hier im Forum.

Sorry, wie kann ich denn noch passender formulieren, was ich suche? Ich möchte gerne wissen, wie man einen Arduino Uno, oder Nano (also mit Atmega328) dazu bringt, kontinuierlich Analog-Messwerte zu erstellen. Wie kann ich denn das im Titel noch passender formulieren?

Und wie soll ich denn einen Sketch posten, wenn es um ein generelles Problem geht, das ich in vielen Projekten habe, und zu dem ich erst mal sowas wie einen Grund-Sketch (ein Grund-Beispiel) benötige? Wenn ich jetzt meinen aktuellen Sketch schicke, wäre das sehr irritierend, weil der vor allem auch tausend andere Sache macht.

Evtl. mal darüber nachdenken, worin der Unterschied zwischen Analogwerte erstellen und Analogwerte messen besteht?

Gruß Tommy

Du möchtest die nicht erstellen, sondern messen und anzeigen.
Erstellen geht mit dem Atmega328 nicht direkt.

Du kannst den 1. Beitrag im Titel direkt ändern.

Und wieso ist es ein generelles Problem ?

Hast Du schon mal Fast sampling from analog input gelesen und probiert?

OK, sorry für diese nicht ganz richtige Formulierung.
Es ist insofern ein generelles Problem, weil ich häufig das Problem habe, dass der Programm-Ablauf durch das Warten auf die Analogwerte sich zu stark verzögert, da das messen ja immer erst mit dem analogRead() ausgelöst wird und dann dauert. Mir ist schon klar, dass das analoge messen entsprechend dauert. Aber wenn währenddessen im Programm-Ablauf was anderes gemacht werden kann, wäre das in vielen Fällen sehr hilfreich.

agmue:
Hast Du schon mal Fast sampling from analog input gelesen und probiert?

Das sieht sehr interessant aus. Der untere Sketch auf der Seite scheint das zu sein was ich suche. Das werde ich mir genauer anschauen.

Das mit der erhöhten Abtastrate (was da zuerst beschrieben ist) wende ich auch meistens an. Aber ich traue mich da immer nur um den Faktor 2 oder allenfalls 4 zu beschleunigen, weil ich befürchte dass die Messwerte sonst sehr ungenau werden. Kann man da wirklich bis Faktor 8 gehen?

Unterscheide zwischen Einzelmessung und Freilauf.

Eine Einzelmessung wird durch Setzen von ADCS gestartet, und wenn das Bit wieder gelöscht ist kann der gemessene Wert abgeholt werden. Dieses Bit läßt sich also pollen, so daß das restliche Programm nicht gestört wird. Sobald es wieder gelöscht ist kann der Wert abgeholt und durch Setzen die nächste Messung gestartet werden.

Im Freilauf (siehe ADATE und ADTS) wird im Interrupt-Flag ADIF angezeigt, wenn ein neues Ergebnis verfügbar ist. Sobald es gesetzt ist, kann man den neuen Wert abholen, wobei das Flag automatisch wieder gelöscht wird, auch bei abgeschaltetem Interrupt. Man bekommt also stets den neuesten Stand, auch wenn man zwischendrin ein paar Werte wegen anderer Abläufe verpaßt hat.

Ob sich das Signal geändert hat, muß man selbst prüfen. Meist zappelt aber der ADC Wert auch wenn das Eingangssignal gleich geblieben ist. Man muß dann eine Differenz festlegen und erst dann eine Signaländerung annehmen, wenn diese Schwelle überschritten wird.

Hier ein Bespiel für den FreeRunningMode: (aus meiner geheimen Wühlkiste)

#include <CombieAdc.h>

#include <util/atomic.h>

#ifndef ADC_MODELL_WITH_THERMO 
  #error Dieser AVR hat keinen eingebauten Temperatursensor
#endif

/**
 * 
 * Den eingebauten Temperatursensor im Interruptbetrieb lesen
 * 
 * Beachte:
 * Die CallBack Funktion läuft im Interrupt Kontext.
 * Also müssen alle genutzten Variablen als volatile 
 * deklariert werden, und aus dem Hauptprogramm muss darauf
 * Atomar zugegriffen werden
 * 
 * Setup:
 * ADC Einstellungen 
 * Interrupt ettablieren
 *  
 * In loop: 
 * Messwert lesen
 * Ausgabe
 * 
 * Tipp:
 * http://www.atmel.com/Images/Atmel-8108-Calibration-of-the-AVR%27s-Internal-Temperature-Reference_ApplicationNote_AVR122.pdf
 * 
*/

using Combie::Adc;
Adc adc;


volatile unsigned long irqCount;
volatile int tempWert;



void adcCallBack(int value)
{
  irqCount++;
  tempWert = value;
}




void setup() 
{
   Serial.begin(9600);

   adc  .enable()
        .setReference(Adc::REF_11)
        .setClockDivisor(Adc::DIV_128)
        .setSource(Adc::MUX_THERMO)
        .setCallBack(adcCallBack)
        .enableAutoTrigger()
        .setTrigger(Adc::TRIG_FREERUNNING)
        .enableIrq()
        .startConversion();
}

void loop() 
{
  unsigned long count;
  int wert;
  
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
  {
    count = irqCount;
    wert  = tempWert;
    irqCount = 0;
  }
 
  Serial.print("Zuletzt gelesener Temperatur Wert ");
  Serial.println(wert);
  Serial.print("Anzahl Messungen pro Sekunde ");
  Serial.println(count);
  Serial.println("---");
  delay(1000);
}

DrDiettrich:
Im Freilauf (siehe ADATE und ADTS) wird im Interrupt-Flag ADIF angezeigt, wenn ein neues Ergebnis verfügbar ist. Sobald es gesetzt ist, kann man den neuen Wert abholen, wobei das Flag automatisch wieder gelöscht wird, auch bei abgeschaltetem Interrupt.

OK, das ADIF Bit zeigt mir an, dass ein neuer Messwert vorliegt. Aber wie wird es wieder gelöscht, also wodurch? Allein indem ich dieses Bit lese? Oder wenn ich den Messwert auslese? Einen Interrupt möchte ich ja nicht verwenden.

Im Internet finde ich dazu diesen Satz:
"Alternativ kann das ADIF-Bit auch gelöscht werden, indem man eine logische 1 in das Flag schreibt."

Das irritiert mich jetzt noch mehr. Ich schreibe eine 1 rein um es auf Null zurückzusetzen???

Ich schreibe eine 1 rein um es auf Null zurückzusetzen???

Ja, so stehts im Datenblatt zu deinem µC.
Dieses bitte lesen!
Tipp: Das Datenblatt sagt die Wahrheit.
Tutorials/Internet lügen, mit hoher Wahrscheinlichkeit.

rsont:
OK, das ADIF Bit zeigt mir an, dass ein neuer Messwert vorliegt. Aber wie wird es wieder gelöscht, also wodurch? Allein indem ich dieses Bit lese? Oder wenn ich den Messwert auslese?

Lies einfach nochmal, was ich dazu in #8 geschrieben habe, oder das Datenblatt. Ich hasse Wiederholungen!

DrDiettrich:
Lies einfach nochmal, was ich dazu in #8 geschrieben habe, oder das Datenblatt. Ich hasse Wiederholungen!

Ich hab nicht nur gelesen was Du geschrieben hast, ich hab's ja sogar bei meiner Nachfrage gequotet. Daher hiflt mir Dein Hinweis, ich soll's einfach lesen nicht wirklich weiter. Und der Hinweis "oder das Datenblatt" hilft auch nicht, solange da was völlig anderes steht. Ich kann im Datenblatt suchen so viel ich will, aber dass das ADIF- Flag automatisch gelöscht wird steht da genauso wenig, wie die Antwort auf meine Frage wodurch diese automatische Löschung ausgelöst wird.

Du hast geschrieben:
"Sobald es gesetzt ist, kann man den neuen Wert abholen, wobei das Flag automatisch wieder gelöscht wird, auch bei abgeschaltetem Interrupt."

Also wenn ich Dich jetzt richtig interpretiere, wird es beim Abholen des neuen Wertes (also beim Auslesen ADCH? ADCL? oder doch beim lesen des ADIF-Flags?) automatisch zurückgesetzt? Sorry, aber auch wenn ich Deinen Post 10 mal lese wird's mir nicht klarer, und im Datenblatt steht davon nichts drin.

Und der Hinweis "oder das Datenblatt" hilft auch nicht, solange da was völlig anderes steht.

Ein Streit mit DrDiettrich hilft dir auch nicht aufs Pferd.

Das Datenblatt schon.
Denn das sagt die Wahrheit.

Zudem ist der FreeRunningMode und damit auch das ADIF nur wirklich interessant, wenn du auch Interrupts nutzen möchtest.
Willst du aber nicht.

Warum eigentlich nicht?

Der Freilauf ist auch ohne Interrupt hilfreich, da man jederzeit ohne Warten den zuletzt gemessenen Wert abfragen kann.

Dann musst du aber auch das ADIF händisch löschen.
Automatisch passiert das dann nicht.

Und mit einer "kontinuierlichen Messung" hat das dann auch nicht mehr viel zu tun, wenn Berge an Messwerten zwischendurch im Nirvana verschwinden.

Erwischt, ich bitte um Entschuldigung :frowning:

ADIF wird nur bei Aufruf der ISR automatisch gelöscht, muß sonst explizit gelöscht werden. Auf den Freilauf soll das aber keinen Einfluß haben, der läuft egal ob ADIF zwischendrin gelöscht wird oder nicht. Das ist also anders als bei den übrigen Triggerquellen und anders als im Schaltbild in Figure 24-2 dargestellt.

Wenn es um "kontinuierliche Datenspeicherung" geht, muß natürlich jeder Meßwert abgespeichert werden. Wenn man aber nur die Wartezeit von analogRead() vermeiden möchte, reicht auch eine "kontinuierliche Messung" (Freilauf, Free Running Mode).

DrDiettrich:
Erwischt, ich bitte um Entschuldigung :frowning:

Schon gut.
Meine Antwort war ja auch etwas patzig.
Daher auch ein Sorry von mir.