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

combie:
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?

Ja warum nicht? Weil's leider nicht geht. Ich versuch's mal zu erklären:

Ich betreibe mit dem Arduino eine Matrix von 8 mal 8 RGB-LEDs. Da muss ich für jede der 8 Zeilen 24 Bit in ein Schiebregister takten, und zu einem genau definierten Zeitpunkt (möglichst auf die Microsekunde genau) freischalten. Wenn einzelne Farben der 8 Felder (also einzelne der 24 LEDs einer Zeile) unterschiedliche Helligkeiten haben, muss ich die 24 Bit mehrmals erneut durchtakten und wieder zu jeweils einem genau definierten Zeitpunkt freischalten, damit jede LED genau so lange leuchtet, wie es der gewünschten Helligkeit entspricht.

Das war soweit nun erst der Vorgang für eine einzelne Zeile. Das alles darf aber nur etwa eine Millisekunde dauern, denn es gibt ja 7 weitere Zeilen, und in weniger als 10 Millisekunden muss alles durch sein, so dass die Bildwiederholfrequenz für das gesamte LED-Feld (alle 8 * 8 * 3 LEDs) über 100 Hz liegt, so dass nichts flimmert.

Das funktioniert soweit auch alles wunderbar, und im beschriebenen Ablauf ist auch immer wieder kurz Zeit für andere Aufgaben übrig, aber ich kann da nicht jedes mal, wenn ich einen Messwert brauche, auf den ADC warten, und ich kann den Ablauf auch nicht durch einen Interrupt unterbrechen lassen, wenn gerade vom Zeitpunkt her ein Helligkeitswert erreicht ist und die nächsten 24 Bit freigeschaltet werden müssen. Ich verwende da Zeiteinheiten von 4 Microsekunden, die ich genau treffen muss. Das würde mit einem Interrupt, der da immer wieder dazwischenkommt nie funktionieren, sondern würde dann völlig unregelmäßig flimmern.

Ich hab's jetzt mal versucht auf Basis dieses "fast-sampling-from-analog-input" Programms, nur eben ohne Interrupt, aber irgendwas mach ich noch falsch. (Das ADIF Flag verwende ich noch gar nicht, sondern lese den Messwert nur im Sekundentakt aus.) Dabei bekomme ich immer wieder und wieder den selben Wert. Hier mein Test-Sketch:

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

pinMode(A0, INPUT);

// ADCSRA = 0; // clear ADCSRA register
ADCSRB = 0; // clear ADCSRB register
//ADMUX |= (0 & 0x07); // set A0 analog input pin
//ADMUX |= (1 << REFS0); // set reference voltage
//ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
ADMUX = 64; // Reference VCC, rechtsbuendig, Eingang A0
// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
//ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz
//ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
//ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz
ADCSRA = 5; // prescaler 32 (ADC-Geschwindigkeit vervierfachen)

ADCSRA |= (1 << ADATE); // enable auto trigger
//ADCSRA |= (1 << ADIE); // enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); // enable ADC
ADCSRA |= (1 << ADSC); // start ADC measurements
}

void loop()
{
delay(1000);
Serial.println(256*ADCH+ADCL);
}

You could also look at my PollingAnalogRead code which wraps it up on a library.

Ich bin erst jetzt dazugekommen, das seltsame Verhalten am lebenden Patienten zu untersuchen.
Das Problem scheint in der Optimierung des Compilers zu liegen, der das Lesen von ADCH irgendwie unterbuttert und das fortgesetzte Laden des Ergebnisses verhindert.

So geht's bei mir:

  //Serial.println(256*ADCH+ADCL);
  int val = ADCL;
  val |= (ADCH << 8);
  Serial.println(val);

Kann das jemand bestätigen bzw. genauer erklären?

BTW pinmode(A0...) habe ich rausgeworfen.

Alternative Erklärung: Das Lesen von ADCL blockiert ein Update von ADCH, was ja Sinn macht wenn man nicht Bytes aus zwei Messungen bekommen möchte. Dreht man also die Abfrage um, funktioniert es auch so:

  Serial.println(ADCL + 256*ADCH);

Den Compiler trifft also keine Schuld :slight_smile:
Kleine Ursache - große Wirkung!

VORSICHT!
Teile dieses Beitrags können Verstimmungen verursachen.

Wer dafür empfindlich ist sollte ihn besser überlesen


Die Reihenfolge ist wichtig!

int val = ADCL;
  val |= (ADCH << 8);

Eine recht unsinnige Konstruktion, aus meiner Sicht.
Ebenso wie

Serial.println(ADCL + 256*ADCH);
int val = ADC;

Ist vollkommen ausreichend und tuts unter allen Bedingungen perfekt, solange nicht ADLAR gesetzt ist.

Ist ADLAR gesetzt, dann ist die Absicht, nur 8Bit aus ADCH zu lesen
Also byte val = ADCH;

Warum hast Du das nicht gleich gesagt, und uns viel Zeit mit Probieren erspart?

Aus der Referenz ist jedenfalls nicht ersichtlich, daß und wie ADC definiert ist. Was ist denn an meiner Konstruktion so unsinnig?

BTW Code bitte mit Code Tags markieren, nicht als unsinnigen Quote :-]

Warum hast Du das nicht gleich gesagt, und uns viel Zeit mit Probieren erspart?

Mich hat ja keiner Gefragt!
Zusätzlich war ich durch die Abläufe in diesem Thread leicht genervt.

Aus der Referenz ist jedenfalls nicht ersichtlich, daß und wie ADC definiert ist.

Und?
Man kennt doch sein Werkzeug, oder nicht?

Tipp:
Das Datenblatt ist keine C/C++ Referenz.
Also kann/muss es da gar nicht drin stehen, wenn es ein GCC Feature ist.

Was ist denn an meiner Konstruktion so unsinnig?

Einfach erklärt:
Einmal gab es ein besseres Verfahren, und das andere mal keine kontrollierte Sequenzierung.
Die Addition ist kein sequenzierender Operator.
Stichwort: "C++ sequence point"

Die Folge:
Der Compiler darf die Auswertung der Summanden nach belieben würfeln.

Das führt zu "mal tuts, andermal tuts nicht" Fehlern.

ADC ist weder in der Sprache noch im Compiler definiert, wo also hast Du diesen Bezeichner her?

Meine Konstruktion enthält weder einen Additionsoperator noch in einem einzigen Ausdruck. Solche unfundierten Spitzen sind dem Betriebsklima nicht besonders zuträglich :frowning:

Hallo,

@ TO:

eine Einstellungen passt noch nicht ganz, die der Samplefrequenz. Die muss laut Manual für 10Bit zwischen 50kHz und 200kHz liegen. Kapitel 24.4. Abhängig vom CPU Takt muss dafür der Prescaler gewählt werden. Taktet der ADC zu hoch wird er dir dennoch Werte raushauen, nur müssen die dann nicht immer stimmen, der ADC benötigt eine bestimmte Wandlungszeit.

Wenn du ohne Interrupt programmierst, dann musst du auf das Interrupt Flag pollen und manuell löschen. Automatisch gelöscht wird das Flag nur, wenn man die Interrupt Routine verwendet, also wenn der entsprechende Interrupt Vector aufgerufen wird.

Die Kanalwahl erfolgt mit den MUX Bits. Die sind vertreut auf Register ADMUX und ADCSRB. Das 5. Bit benötigen wir hier für Single Ended Messung nicht. Deswegen muss für die Eingangswahl nur ADMUX konfiguriert werden.
Edit:
Das 5. MUX Bit gibts nur bei meinem ATmega2560. Beim ATmega328P kannste den Satz ignorieren.
Ansonsten sind die verwandt und damit gleich aufgebaut.

Wenn der Prescaler angenommen 64 wäre, dann würde der ADC mit 16MHz/64 = 250kHz beaufschlagt, liegt über dem Spec. Also nimm lieber Prescaler 128. Nur im 8Bit Modus darf der ADC mit bis 1000kHz beaufschlagt werden.

Bei mir würde das so aussehen für ein Bsp. mit Eingang A3 und 10Bit Auflösung

void setup()
{  
  Serial.begin(9600);
  Serial.println(F("\nStart #### #### #### ####"));
  initADC();        // ADC Setting
}  

void loop()
{  
  readingADC();       // Polling auf ADIF Flag
  delay(500);         // nur wegen seriellen Monitor
}


// *** Funktionen *** //
void initADC ()
{
  ADMUX   = _BV(REFS0);             // AVcc externe Referenz 
//ADMUX  |= _BV(ADLAR);             // left adjusting (ist für 8Bit sinnvoll und nur ADCH auslesen)       
  ADMUX  |= _BV(MUX1) | _BV(MUX0);  // Input ADC3 select   
  DIDR0   = _BV(ADC3D);             // disable 'ADC3' Digital Input Buffer   
  ADCSRA  = _BV(ADPS2)| _BV(ADPS1)| _BV(ADPS0); // Prescaler 128 (Sample Frequenz 50kHz...200kHz)
  ADCSRA |= _BV(ADATE);             // Auto Trigger enable   
//ADCSRA |= _BV(ADIE);              // Interrupt enable    
  ADCSRA |= _BV(ADEN);              // ADC general enabled   
  ADCSRA |= _BV(ADSC);              // start conversion      
  ADCSRB  = 0;                      // free running
  
}

void readingADC (void)
{
  if (ADCSRA & _BV(ADIF))
  {
    const unsigned int reading = ADC;
    Serial.print("ADC2: "); Serial.print(reading); Serial.print(" Digits");
    // wenn man davon ausgeht das Ub 5.0V sind ...
    const unsigned long voltage = 5000UL * reading / 1024;
    Serial.print(" = "); Serial.print(voltage); Serial.println("mV");
    ADCSRA = ADCSRA | _BV(ADIF);      // clear result ready interrupt flag
  }
}

@ DrDiettrich:
ADC ist im Headerfile definiert. Solche Dinge sind schon lange Standard. Ist auch nur eine Registeradresse aber eine 16 Bittige.
Eine Hilfe zur Herangehensweise, manchmal verstehe ich die Logik und damit die Behauptung nämlich nicht.
Wir programmieren hier einen Controller auf Registerebene. Die Sprache C/C++ kann die Register irgendwelcher Controller nicht kennen. Der Compiler selbst kennt den Controller auch nicht direkt. Aber zur eigentlich verwendeten Toolchain gehört immer noch das Headerfile etc. des Controllers der programmiert wird. Ansonsten würde der Compiler auch die anderen Registernamen wie ADMUX, ADCSRA usw. und die Bitnamen REFS0, ADLAR usw. nicht kennen. Und all diese Definitionen können nur im spezifischen Controller Headerfile stehen.

Aber zur eigentlich verwendeten Toolchain gehört immer noch das Headerfile etc. des Controllers der programmiert wird.

So ist es.
iom328p.h Zeile 455 (zumindest bei meiner Toolchain)
Der ADC, oder ADCW, Zugriff ist also weder vom Datenblatt gefordert, noch Teil der Sprache C++.

Sorry, dass ich eine fehlerträchtige Registerbastelei als "sinnfrei" bezeichnet habe, nur weil es besseres gibt.
Wollte dich nicht damit ärgern oder gar beleidigen.

Serial.println(ADCL + 256*ADCH);

Das habe ich aus deinem Posting.
Und ich sehe da einen Additionsoperator, genau diesen möchte ich als "kritisch" bezeichnen. Begründung hatte ich mitgeliefert.
(woher er ursprünglich stammt, war mir dabei egal)


Wie auch immer, ich möchte nochmal um Verzeihung bitten, dass ich mich hier wieder mal eingemischt habe.
Kommt nicht nochmal vor.

Mir war schon klar, daß ADC - wenn überhaupt - in einem Headerfile definiert sein muß, der zum jeweiligen Controller gehört. Aber welcher das ist, ist bei der verzweigten Arduino Bibliotheksstruktur nicht erkennbar.

Da es sich im konkreten Fall um 8 Bit Controller handelt, ist nicht unbedingt zu erwarten, daß es auch ein Makro zum Laden zweier Register gibt. Deshalb habe ich in #23 meine erstmals funktionierende Leseversion gepostet, mit definierter Reihenfolge der Zugriffe, die Combie in #25 zitiert und als unsinnig bezeichnet hat. Ich warte immer noch auf eine Begründung für "unsinnig". Auf eine Addition wäre ich nie selbst gekommen, da im Gegensatz zu OR ein Übertrag berücksichtigt werden muß, der hier nie auftreten kann. Entsprechend Shift und keine Multiplikation, obwohl der Compiler beide Varianten optimieren kann.

Danke für die weiterführenden Betrachtungen zur Programmierung des ADC, auch auf verschiedenen Controllern. Da jetzt ein funktionierender Beispielcode existiert, kann jeder auch ohne detaillierte Kenntnisse des ADC weiterexperimentieren.

Hallo,

das mit dem Free Running Mode, klappt jetzt prima.
Auch das Abfragen und zurücksetzen des ADIF Flags funktioniert.

Vielen Dank für Eure Hilfe. :slight_smile:

Ich hab da mal folgendes Testprogramm geschrieben:

unsigned int analog_16 = 0;
unsigned long start_time;
unsigned long duration;
byte n; // counter
byte m; // counter
float ref_voltage = 1.1; // interne Referenz
float vcc;
float frequency; // Messfrequenz

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

  ADCSRB = 0; // clear ADCSRB register

  //ADMUX = 64; // Reference VCC, rechtsbuendig, Eingang A0
  ADMUX = 64 + 14; // Reference VCC, rechtsbuendig, Eingang Ref 1,1V (so kann ich intern VCC messen)

  //ADCSRA = 5;             // prescaler auf 32 (ADC-Geschwindigkeit vervierfachen, 500k)
  ADCSRA = 6;             // prescaler auf 64 (ADC-Geschwindigkeit verdoppeln, 250k)
  //ADCSRA = 7;             // prescaler auf 128 (ADC-Geschwindigkeit einfach, 125k)

  ADCSRA |= (1 << ADATE); // enable auto trigger
  //ADCSRA |= (1 << ADIE);  // enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements
  unsigned long start_time = micros();
}

void loop()
{
  if (ADCSRA & 16) // wenn ADIF gesetzt ist, und somit neuer Messwert anliegt
  {
    analog_16 += ADC; // Messwert addieren
    ADCSRA |= 16; // ADIF Bit setzen um es zu loeschen
    n++;

    if (!(n&63)) // Endwert erreicht nach jeweils 64 Messungen
    {
      m++;
      if(!m) // nach 256 * 64 Messungen
      {
        duration = micros() - start_time;
        vcc = ref_voltage * 65536 / analog_16;
        frequency = (float)1000000 * 256 * 64 / duration;
        Serial.print("VCC = ");
        Serial.print(vcc,3); // mit 3 Nachkommastellen
        Serial.println(" Volt");
        Serial.print("Messfrequenz: ");
        Serial.print(frequency,2);
        Serial.println(" Hz");
        Serial.println("");
        start_time = micros();
      }
      analog_16 = 0; // zuruecksetzen
    }
  }
}

Kurz zur Erklärung:

Der ADC liest im Free Running Mode die interne Referenz-Spannung, um daraus (ohne externe Beschaltung)
die Betriebsspannung zu ermitteln. In der Variablen analog_16 werden die neuen Messwerte aufaddiert,
bis es 64 Werte sind, und somit ein 16_Bit Wert (statt 10 Bit) zur Verfügung steht.

Damit (bis zur Ausgabe) Zeit vergeht, wiederhole ich das ganze sinnlos 256 mal, Und gebe dann die Werte aus.

Ich bekomme eine VCC von 5,097 Volt angezeigt. Ich hab auch mal testweise 3,3 Volt als VCC drangehängt
(was ja bei 16 MHz eigentlich gar nicht geht, hat aber funktioniert) und hat etwa 3,4 Volt angezeigt.

eine Einstellungen passt noch nicht ganz, die der Samplefrequenz. Die muss laut Manual für 10Bit zwischen 50kHz und 200kHz liegen. Kapitel 24.4. Abhängig vom CPU Takt muss dafür der Prescaler gewählt werden. Taktet der ADC zu hoch wird er dir dennoch Werte raushauen, nur müssen die dann nicht immer stimmen, der ADC benötigt eine bestimmte Wandlungszeit.

Mit Prescaler 64 (250kHz) und somit etwa 19000 Messungen pro Sekunde komme ich in meiner geplanten Anwendung aus. Das liegt knapp außerhalb der Spezifikation. 8 Bit wären mir aber zu wenig. Die Frage ist nun, ob es nicht trotzdem besser ist 10 Bit zu verwenden, auch wenn sich die Ungenauigkeit leicht erhöht, oder wie wirkt sich das genau aus, wenn ich Prescaler 64 (oder sogar 32) bei 10 Bit verwende? Hat da jemand Erfahrungen? Ich hab mal irgendwo gelesen, dass die Ungenauigkeit da noch gering sein soll, aber ja, ich weiß doch: Nur das Datenblatt zählt! Nur das schweigt sich darüber leider aus. :confused:

Hallo,

ich komme mit der Aussage nicht klar.

Mit Prescaler 64 (250kHz) und somit etwa 19000 Messungen pro Sekunde komme ich in meiner geplanten Anwendung aus.

Was bedeutet "man kommt aus"?
Für mich bedeutet es, unnötig überhöhte Samplerate. Zudem diese noch verfälscht werden, weil der ADC außerhalb seiner Spec betrieben wird. Warum macht man sowas überhaupt und bewußt noch dazu?

Zur 2. Frage. Es gibt immer einen Unterschied zwischen Auflösung und Genauigkeit. Die Auflösung kann man wählen zwischen 8 und 10 Bit. Die Messgenauigkeit, Linearitätsfehler usw. findet man im elektrischen Teil des Datenblattes weiter hinten. Ich kann dir jedoch gleich vorweg sagen, dass das alles für deine Messung keine Rolle spielt, da sich bei deiner Messung alles auf die ungenaue interne Vref bezieht. Da musste dir um die Messgenauigkeit vom ADC keine Sorgen machen.

Und selbst wenn das alles passen würde, ggf. mit gleitender Mittelwertbildung, haste ja immer noch die überhöhte Samplerate die dir die Genauigkeit versaut. Also überleg nochmal in aller Ruhe was du wirklich möchtest. Mit dem was onboard vorhanden ist das Beste rausholen oder nur sinnlos drauflos messen. Frei nach dem Motto Hauptsache schnell messen, völlig egal ob die Meßwerte stimmen.

Desweiteren solltest du dir überlegen wie und ob du überhaupt deine x Messungen / s auch verarbeiten kannst. Die Antwort sagt dir ob dies grundlegend so wie du denkst sinnvoll ist.

Doc_Arduino:
ich komme mit der Aussage nicht klar.
Was bedeutet "man kommt aus"?
Für mich bedeutet es, unnötig überhöhte Samplerate.

Mit der Formulierung, dass ich damit auskomme, meinte ich dass es meinen Anforderungen genügt. Aber schön, dass Du viel besser weißt, was ich brauche, und was unnötig ist, ohne überhaupt zu wissen was ich mache. :confused:

Sorry, ist jetzt nicht bös' gemeint, aber ich fühle mich von Deinen Post gerade so ein bisschen zurechtgewiesen, wie ein kleines dummes Kind.

Ich kann dir jedoch gleich vorweg sagen, dass das alles für deine Messung keine Rolle spielt, da sich bei deiner Messung alles auf die ungenaue interne Vref bezieht.

Hier verwechselst Du mein kleines (zugegebenermaßen völlig sinnloses) Testprogramm mit dem eigentlichen Projekt an dem ich dran bin. Siehe #20.

Für eine simple Messung der Betriebsspannung wäre das alles tatsächlich Quatsch. Ich habe dieses Beispiel für mein kleines Testprogramm nur gewählt, weil es die einfachste (und wahrscheinlich auch einzige) Art ist, um ohne jegliche externe Beschaltung schnell etwas sinnvolles zu messen. (Interne Temperaturmessung wäre vielleicht noch eine Alternative gewesen.) Aber im Testprogramm ging's mir allein darum den Free Running Mode zu testen.

Dass Auflösung und Genauigkeit zwei paar Stiefel sind, das ist mir schon klar, denn genau darauf bezog sich ja meine Frage. Ob jemand Erfahrungen damit hat, ob man da z.B. nur eine kleine Störung reinbekommt (z.B. so +/- 1 Digit) oder ob man dann tatsächlich ein besseres Ergebnis hat, wenn man nur 8 Bit verwendet.

Ich kann Dir auch gerne kurz erklären, was ich machen möchte. In #20 hab ich beschrieben, weshalb das alles so Zeitkritisch ist, dass ich nicht auf den ADC warten kann und dafür auch keinen Interrupt verwenden kann. Das ganze gibt so eine Art Leuchttisch, eine krasse Version von so einem "Daft Punk Table", und über ein verstärktes Mikrofonsignal am Analog-Eingang soll dieser soundgesteuert sein.

Dafür allein bräuchte man nun noch nicht unbedingt eine hohe Samplingrate, aber in einem Betriebsmodus soll der Tisch wie ein VU-Meter 8 Lautstärke-Balken anzeigen, und als Spektrum-Analyzer fungieren. Dazu sollte der Tisch im Idealfall auch höhere Frequenzen bis 8 oder 9 kHz auswerten können. Deshalb die gewählte DAC-Geschwindigkeit. Die Qualität der Daten muss dafür nicht besonders gut sein, aber eine leicht beeinträchtigte 10-Bit-Auflösung ist wahrscheinlich immer noch besser als grundsätzlich nur 8 Bit zu verwenden!? Außer wenn ich bei 10 Bit eine Beeinträchtigung bekomme, die noch unterhalb des 8-Bit-Betriebs liegt. Darum ging's mir in meiner Frage.

Und bevor Du mir jetzt antwortest, dass das doch eh nicht geht in der kurzen Zeit die Daten zu verarbeiten: Doch, es geht. Ich hab sowas vor Jahren schon mal gemacht (Tisch mit Audio Spektrum-Analyzer), allerdings als Monochrom-Version, aber dafür noch ohne die komfortable Möglichkeit des Free Running ADC-Modus zu nutzen.

In meinem Test habe ich exakt gleiche Werte bekommen, ohne Zappeln, wenn ich Gnd, 5V und 3,3V gemessen habe. Das dürfte hauptsächlich daran liegen, daß immer der selbe Kanal abgetastet wird, so daß keine Spannungssprünge wie beim Kanalwechsel vorliegen. Dann kann sich die Eingangsschaltung (Sample and Hold) auf den exakten Wert aufladen und dort bleiben.

Bei einem Audio-Signal ist das natürlich anders, da zappelt das Signal ständig hin und her, Mittelung über mehrere Messungen ist sinnlos. Wer es genauer wissen möchte, darf einen Signalgenerator dranhängen und Rechteck, Sinus, Sägezahn und Dreieck messen und aufzeichnen. Dann ggf. die Abtastrate (prescaler) ändern und nachschauen, welche Änderungen sich daraus ergeben. Dabei kann auch herauskommen, daß die Abfrage nicht äquidistant erfolgt, besonders schön am Sägezahn und Dreieck mit konstanter Steigung zu sehen.

Und dann wäre da noch das (analoge) Anti-Aliasing Filter hinzuzufügen, das die zu hohen Frequenzen herausfiltert bevor der ADC daraus falsche Werte und die Auswertung falsche Frequenzen bzw. Pegel erzeugt.

DrDiettrich:
Und dann wäre da noch das (analoge) Anti-Aliasing Filter hinzuzufügen, das die zu hohen Frequenzen herausfiltert bevor der ADC daraus falsche Werte und die Auswertung falsche Frequenzen bzw. Pegel erzeugt.

Ja, theoretisch sehr wichtig, hier aber ziemlich bedeutungslos. Was sich da am Analogsignal oberhalb der halben Abtastrate tut (also so knapp 10 kHz bis Ultraschall) ist gemessen am Gesamtsignal fast gar nichts. Und das gemessene Signal soll ja nicht weiterhin als Audiosignal zur Wiedergabe verwendet werden sondern nur dem optischen Effekt dienen, wobei die einzelnen Balken zwar schon den Frequenzbereich (jeweils eine Oktave) zeigen sollen, aber der ganze Spektrum-Analyzer ist ja kein Messgerät, sondern nur ein Effekt, der gut aussehen soll.

Hallo,

so, tief Luft geholt. Ich konnte mich nur auf das Beziehen was ich lesen konnte. Deswegen viel die Antwort eben so aus wie sie ausfiel. Ich kann ja schlecht irgendwelchen Pfusch empfehlen, wenn ich annehmen muss das jemand genau messen möchte. Zudem ich dein Vorwissen nicht kenne und du meines nicht. Egal, ich habe dich darauf hingewiesen, was du daraus machst ist dein Bier. Sieh es als Hinweis.

Zur Frage Auflösung und Genauigkeit. Dafür reicht folgende Überlegung. Was stellt der ADC Messwert eigentlich dar? Er stellt nicht den exakten Wert einer angelegten Spannung dar. Er stellt einen Spannungsbereich dar in dem sich die angelegte Spannung befindet. Das kannste dir wie ein Raster vorstellen in dem sich die angelegte Spannnung befindet. Je nach Wahl auf 8Bit oder 10Bit fallen die Abstufungen aus.

Welchem Spannungsbereich - nicht Spannungswert - entspricht bei 8Bit ein Digit?
Welchem Spannungsbereich - nicht Spannungswert - entspricht bei 10Bit ein Digit?

Ist dir der Spannungsbereich pro Raster/Digit mit 8Bit ausreichend? Oder willste doch 10Bit haben?
Überlegen wir weiter um bei der Frage zu bleiben. Wenn der ADC Messwert mit 10Bit an den Grenzen einer Stufe minimal wackelt, weil sich die angelegte Spannung ändert, dann wirst du das natürlich mit dem 8Bit Raster nicht mitbekommen. Wenn sich die angelegte Spannung innerhalb einer Stufe befindet und der ADC Messwert wackelt, dann haste in dem Fall den Wandlungsfehler gesehen der immer vorhanden ist. Das heißt im Endeffekt nichts weiter, dass du mit 8 Bit gröber misst und deswegen dein Messwert weniger wackelt, wenn sich dieser innerhalb einer Stufe befindet.
Wenn die Erklärung noch nicht ausreichend ist. Stell dir einen 1Bit ADC vor. Als Messwert bekommst du entweder 0 oder 1. 0 entspricht einer angelegten Spannung zwischen 0...2,5V und 1 einer zwischen 2,5...5V. Bei angenommen 5V Ub. Bei Messwert 0 kann die Spannung zwischen 0 und 2,5V zappeln wie sie möchte, bekommste nicht mit. Die Frage lautet, ist einem das genau genug oder nicht. Wenn dazu vielleicht noch ein Wandlungsfehler von 1 Digit dazukommt, dann zappelt der Messwert selbst in dem Fall. Soviel zur Veranschaulichung.

Meine Einschätzung für dein Audioprojekt ist ja, dass du mit 8Bit Auflösung locker hinkommst. Um das auszureizen müßte ja deine Anzeige eine Auflösung von 8Bit haben. Ist das der Fall?

Doc_Arduino:
Meine Einschätzung für dein Audioprojekt ist ja, dass du mit 8Bit Auflösung locker hinkommst. Um das auszureizen müßte ja deine Anzeige eine Auflösung von 8Bit haben. Ist das der Fall?

Ja, im Prinzip reicht die Auflösung locker, zumindest wenn die Lautstärke angepasst ist. Bei der Auflösung der einzelnen Felder jeder Reihe geht's um Dezibel. Das Problem ist eher dass sich der Effekt an jede Lautstärke (von dezenter Zimmerlautstärke bis höllisch Laut) automatisch anpassen können soll. Damit das überhaupt geht, verwende ich bei solchen Sachen immer zwei unterschiedliche Verstärkungsfaktoren für das Audiosignal (die um Faktor 10 auseinanderliegen) zwischen denen der Arduino umschalten kann, und da zeigt sich schon das Problem: An der Grenze, wo die Lautstärke so hoch ist, dass die Spitzen übersteuern, muss auf die schwächere Verstärkung umgeschaltet werden. Da hab ich dann bei 8 Bit nicht mehr einen Bereich von +/- 127 sondern nur noch +/- 12, und da bin ich dann für die zwei zusätzlichen Bits sehr dankbar. Ähnlich sieht es aus bei sehr leiser Lautstärke.

Nun ist ja meine Vermutung, dass der Hersteller einfach festgestellt hat, dass bei einem ADC-Takt über 200kHz die Präzision der Messwerte abnehmen kann (oder bei viel höherer Frequenz wahrscheinlich definitiv deutlich abnimmt), und dass er deshalb bei höherer Taktfrequenz die Anwendung in der Spezifikation auf 8 Bit begrenzt, weil da ein zusätzlicher Fehler von beispielsweise 1 Promille vom Gesamtbereich nicht so ins Gewicht fällt. In meinem Fall wäre es dann sicher sinnvoll, trotzdem die 10 Bit Auflösung zu verwenden, zumal ich mit 250kHz ja auch nicht so weit über 200kHz liege. Das ist aber - wie gesagt - reine Spekulation von mir. Ich weiß ja nicht wie die Auswirkungen tatsächlich sind. Ich bin nur bisher immer gut damit gefahren.

Hallo,

irgendwie musst und wirst du ja erkennen wie hoch die Lautstärke ist um überhaupt umschalten zu können. Ich würde vielleicht nicht die Auflösung runterteilen. Ich würde am Eingang des ADC einen Spannungsteiler umschalten. Dafür kann ein anderer ADC (Kanal) abgestellt werden. Das könnte man abstufen wie man lustig ist.

Vielleicht kann man auch eine Schaltung mit OPV entwerfen die einem das Eingangssignal immer in optimaler Amplitude hält. Nur so als Idee. Ob es genau möglich ist weiß ich nicht.

Müßte man sich alles im Detail durchdenken. Das Problem der Umschaltung haste in allen Fällen. Also kurz vor oder kurz nach Umschaltung sind die Pegel zu klein bzw. übersteuert. Die Umschaltung reagiert ja immer verzögert, man könnte nur die Triggerschwelle anpassen. Das Beste wäre du hast einen Audioabgriff woran sich der Spannungspegel nicht ändert. Ein freier Kopfhörerausgang o.ä.. Das wären so meine Gedanken zum Thema.

Edit:
Vergiss das mit dem OPV Regler. Wenn die Amplitude immer konstant hoch wäre brauchste ja nicht mehr messen. :o . Ich würde mir wie gesagt einen "konstanten" Audioabgriff suchen. Wenn das nicht möglich ist, dann kann man über die Umschaltung nachdenken, soweit meine Überlegungen.