Höhere ADW-Auflösung - Oversampling?

Hi,

zum Glück habe ich jetzt wieder Zeit, um mich ein bisschen mit dem Programmieren zu beschäftigen.
Als nächstes Projekt möchte ich, abhängig von verschiedenen Messwerten, bestimmte Geräte steuern.

Erfasst werden soll: Die Temperatur, Luftfeuchte, Wind, ...
Bei der Temperatur und Luftfeuchte möchte ich analoge Sensoren verwenden, da ich solche noch in der Werkstatt habe.

Der Arduino UNO hat einen 10 Bit ADW - heißt also ich kann 0 -5 V mit 1024 Schritte auflösen. Bei der Feuchte passt's, die kann ich mit einer Dezimalstelle ausgeben. Bei der Temperatur wird's eng! Deshalb möchte ich die Auflösung des ADWs erhöhen.
Ohne einen externen ADW habe ich nur die Möglichkeit Oversampling zu betreiben. Nach meinen Recherchen ist das nichts anderes, als sehr sehr oft zu Messen (> 16 Hz).

Dazu habe ich folgenden Code zusammenkopiert:

#define BUFFER_SIZE 16 // For 12 Bit ADC data

volatile uint32_t result[BUFFER_SIZE];
volatile int i = 0;
volatile uint32_t sum=0;

/*ADC and Timer1 setup function
  Argument 1: uint8_t channel must be between 0 and 7
  Argument 2: int frequency must be integer from 1 to 600
    WARNING! Any value above 600 is likely to result in a loss
    of data and could result in a reduced accuracy of ADC
    conversions
*/
void setup()
{
  Serial.begin(9600);
  pinMode(A2, INPUT);
  setupADC(2, 16);
}

void loop()
{
  delay(1500);
  Serial.println("TEST TEST"); 
}

void setupADC(uint8_t channel, int frequency)
{
  cli();
  ADMUX = channel | _BV(REFS0);
  ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) | _BV(ADATE) | _BV(ADIE);
  ADCSRB |= _BV(ADTS2) | _BV(ADTS0);  //Compare Match B on counter 1
  TCCR1A = _BV(COM1B1);
  TCCR1B = _BV(CS11)| _BV(CS10) | _BV(WGM12);
  uint32_t clock = 250000;
  uint16_t counts = clock/(BUFFER_SIZE*frequency);
  OCR1B = counts;
  
  TIMSK1 = _BV(OCIE1B);
  ADCSRA |= _BV(ADEN) | _BV(ADSC);
  sei();
}

ISR(ADC_vect)
{
  result[i] = ADC;
  i=++i&(BUFFER_SIZE-1);
  for(int j=0;j<BUFFER_SIZE;j++)
  {
    sum+=result[j];
  }
   if(i==0)
  {
    sum = sum>>2;
    Serial.println(sum,DEC);
  }
  sum=0;
  TCNT1=0;
}
ISR(TIMER1_COMPB_vect)
{
}

Jetzt gibt der mir am Serial-Monitor sehr viele Werte aus, bis er mal das "TEST TEST" ausgibt - ich möchte aber doch nur einen. So wie man es von analogRead() gewöhnt ist.
Was mache ich da falsch? Wo muss ich mir den Messwert abknöpfen? Und wie müsste ich den Code modifizieren, dass nur z.B. alle 5 Sek. eine Messung gemacht wird?
Ich bin ehrlich gesagt ziemlich überfordert damit...... :~ :~

Problem 2: Der Feuchtesensor (Poti) ist nicht linear. Wie erhalte ich da "das beste" Ergebnis? Mit Excel kann man eine Nährungsformel erstellen, aber ist das auch genau?

Wäre schön, wenn Ihr bei meinen vielen Problemen helfen könntet.

Gruß
Jan

Hallo,

irgendwas verwechselst Du. Die Auflösung von 10Bit kann man durch nichts ändern. Außer Du nimmst einen externen ADC. Die Samplingfrequenz beeinflusst nur die Genauigkeit eines sich änderte analoge Eingangsspannung in ihrer Abbildung. Je höher die Samplingfrequenz desto weniger Datenlücken hat man. Das heißt Deine Datendichte pro Zeiteinheit wird höher.
Die Auflösung jedoch bleibt gleich.

Oversampling um künstliche die Auflösung zu erhöhen gibt es schon. Siehe Atmel Application Notes:
http://www.atmel.com/images/doc8003.pdf
Das hängt mit der Frequenz des Signals zusammen. Die Sampling-Rate muss mehrfach über der normalen Nyquist-Frequenz liegen. Und zwar um einen Faktor von 4 pro Bit.

Wenn ich das mal grob betrachte triggerst du den ADC jetzt wahrscheinlich über den Timer, so dass er ständig misst. Du hast einen Puffer mit 16 Werten der ständig beschrieben wird. Das könnte soweit passen

Das hier sieht aber nicht gut aus:

if(i==0)
{
    sum = sum>>2;
    Serial.println(sum,DEC);
}

Serial hat in einem Interrupt nichts verloren! Setze statt dessen einen boolean (auch als volatile deklarieren!) den du in ständig loop() abfragst. So kannst du aus der ISR der Loop mitteilen, dass sie was erledigen soll.

Alle 5 Sekunden eine Messung kannst du auch mit millis() machen. Indem du einfach den Wert (sum) nur alle 5 Sekunden ausliest.

void loop()
{
     static unsigned long lastMillis;

     if(millis() - lastMillis > 5000)
     {
          Serial.println(sum);
          lastMillis = millis();
     }
}

Gemessen wird dann immer noch ständig. Wenn du wirklich tatsächlich nur alle 5 Sekunden 16 Messungen machen willst kann man den ADC auch über das ADEN Bit deaktivieren. Oder man schaltet den Timer an und aus (über die CS Bits). Dann wird der ADC auch nicht mehr getriggert. Dann brauchst du aber noch Statusvariablen um zu wissen was gerade gemacht wird.

Hallo,

so wie ich ihn verstehe, denkt er jedoch er könnte mit höherer Samplingsfrequenz die 10Bit erhöhen auf künstliche 16 oder sowas. Das geht natürlich nicht. Die zeitliche Auflösung hängt von der Samplingfrequenz ab. Ja. Die Auflösung der Eingangsspannung in mV/Bit ist auf 10Bit festgenagelt. Ohne externen ADC ist da nichts zu machen.

Hast du dir das Dokument mal angelesen?

"Special signal processing techniques can be used to improve the resolution of the measurement. By using a method called ‘Oversampling and Decimation’ higher resolution might be achieved, without using an external ADC."

Funktioniert aber auch nur wenn man ein Rauschen von mindestens 1-2 LSB hat.

Hallo,

aus einem 10Bit ADC kann man keinen 16Bit ADC machen. Hab mir das mal genauer angeschaut. So wie ich das verstehe. Das AVR Oversampling ist keine echte Erhöhung der Auflösung. Wie auch. Das ist auch nur Beschiss. :wink: Denn die machen im Extremfall, was mit 16Bit Auflösung beschrieben wird, nur ein 4096 faches Oversampling und bilden den Mittelwert. Das kann man auch zu Fuß machen, man addiert 4096 Einzelwerte und teilt diese durch 4096. Ich persönlich halte die Methode für nicht praktikabel. Bei den 4096 notwendigen Einzelwerten geht viel Zeit flöten und man hat dennoch nur irgendeinen Mittelwert. Irgendwelche stark abweichende Min/Max gehen verloren. Wenn dann sollte man einen ADC mit echter höherer Auflösung verwenden.

Hallo,

alles klar! Dann werde ich diese Tipps gleich mal umsetzten. (Danke für die Tipps!) :slight_smile:

Serenifly:
[..] Gemessen wird dann immer noch ständig. Wenn du wirklich tatsächlich nur alle 5 Sekunden 16 Messungen machen willst kann man den ADC auch über das ADEN Bit deaktivieren. Oder man schaltet den Timer an und aus (über die CS Bits). Dann wird der ADC auch nicht mehr getriggert. Dann brauchst du aber noch Statusvariablen um zu wissen was gerade gemacht wird.

Wobei wenn ich alle 5 Sekunden messe macht es doch keinen Sinn den ADW zu deaktivieren, oder wie seht Ihr das?
Hättest Du vielleicht einen Link oder ein Beispiel, in welchem der Timer an und aus geschaltet wird?

Sinnvoll ist es meines Erachtens, wenn man den Mittelwert der 16 Messungen bildet?!

Gruß
Jan

Hättest Du vielleicht einen Link oder ein Beispiel, in welchem der Timer an und aus geschaltet wird?

Einfach die zwei verwendeten CS Bits im TCCR1B Register auf 0 setzen:

TCCR1B &= ~(_BV(CS11) | _BV(CS10));

Eventuell noch das Timer/Counter Register TCNT1 auf 0 setzen. Zum aktivieren die zwei Bits wieder setzten.

Siehe Datenblatt Seite 136f

Sinnvoll ist es meines Erachtens, wenn man den Mittelwert der 16 Messungen bildet?!

Das filtert Unebenheiten heraus, aber ist was anderes. Das geht auch einfach mit analogRead() bei sowas langsamen wie einer Temperaturmessung.

Hallo!

Das ganze sieht irgendwie ganz kompliziert aus....

So ganz habe ich das Prinzip/ den Sktech nicht verstanden. Der ADW wird ganz oft ausgelesen -> die Auflösung wird künstlich erhöht (Warum das? Und wie geht das?). Also werden die 0-5 V am analogen Eingang nicht mehr mit 1024 Schritten sondern viel mehr Schritten aufgelöst (je nach Frequenz)?

Warum spuckt mir der Sketch 16 einzelne Messwerte aus? Sind diese (einzelne) Messwerte dann höher aufgelöst?

Das Thema denke ich, kann als erledigt angesehen werden.

Gruß,
Jan

@Doc_Arduino: Serenify hat recht. Auch wenn Du die Theorie nicht verstehen willst funktioniert das trotzdem. Die Einschränkung, daß man für n bits mehr mit mindestens 4^n der Nyquist Frequenz samplen muß hört sich nur harmlos an. In der Praxis ist das eine ganz massive Einschränkung.

Was auch gerne vergessen wird: 10 bit Auflösung heisst noch lange nicht 2^-10 Genauigkeit. Unter Umständen muß man noch viel mehr Aufwand investieren. DESHALB ist ist es in der Regel oft einfacher einen externen ADC zu verwenden.

Hallo,

bei einem externen 24 Bit-ADW schreckt mich zum Einen die analog Technik ab, die ich brauchte um das Rauschen halbwegs zu entfernen. Außerdem brauche ich noch einen MUX davor, .... Ich möchte halt mit Möglichkeit wenig neue Komponenten an den Arduino anschließen.

Gruß...

Muss es denn gleich 24 bit sein? "Einfache" ADCs gibt's bis 16 bit, das wär in deinem Fall auch schon ein großer Fortschritt. Der ADS1115 hat z. B. 4 Eingänge (interner MUX) und wird über I²C angesprochen, das geht mit dem Arduino dann ohne großen Aufwand.

Gruß,
Marv

Warum müssen es überhaupt mehr als 10 bit sein?
Du willst Temperatur und Luftfeuchte messen, bei 10 bit Auflösung hast du ca. 0,1 Grad bzw. 0,1 Prozent Genauigkeit.
So genaue Sensoren wirst du überhaupt nicht bekommen.

Das stimmt natürlich. Hochauflösende ADCs gibt es sicherlich, aber bei so einfachen Sensoren die nur auf 0,25-0,5° genau sind, bringt das nicht wirklich was.

Hallo,

bei der rel. Feuchte kann ich mit 0,1 % auflösen. Das stimmt. Bei der Temperatur aber eben nicht. Denn ich benötige noch einen Widerstand vor meinem NTC und so verschenke ich viele von den 1024 Schritten. Sagen wir mal, ich habe dann noch 600 Schritte, dann kann ich einen Temperaturbereich von 120 °C mit "nur" noch 0,5 °C auflösen - das ist mir dann doch zu wenig.

Bei den Sensoren müsst Ihr euch keine Sorgen machen - Ich verwende sehr sehr gute Sensoren. (Standard Sensoren vom Deutschen Wetterdienst).

Gruß
Jan

PS: Ich werde mir mal zum Probieren einen paar 16 Bits-ADWs kaufen! :slight_smile:

Hallo,

ich beschäftige mich gerade mit einem MCP3550-50. Einkanal 22Bit ADC mit SPI Interface.
Ein MCP3428 liegt noch bereit, wäre ein 4 Kanal 16Bit ADC mit I2C Interface.

@ Udo und Serenifly.
Vielleicht reden wir aneinander vorbei. Wir sind uns doch einig das der Atmel µC hardwaremäßig nur eine 10Bit Auflösung mit seinem ADC hat. Oder nicht? Eine Erhöhung der Abtastfrequenz erhöht doch nur die Genauigkeit der zeitlichen Datendichte. Die Auflösung ändert sich dadurch nicht. Erst mit Sampling erhalte ich Zwischenwerte durch Mittelwertbildung. Das ist ja die übliche Methode um Rauschen oder Messwertzappeln zu mildern. Die hardwaremäßige Auflösung vom ADC wird jedoch daruch auch nicht erhöht. Wenn das nun auch falsch sein sollte, müßte ich mich sehr wundern.

Klar gibt die Hardware nur 10 bit her. Und selbstverständlich wird das nicht besser bei höherer Samplefrequenz sondern schlechter. Allerdings kann man durch Oversampling eben mehr als 10 bit Auflösung hinbekommen.

Hallo,

ganz ehrlich, die Antwort habe ich fast erwartet. :wink:
Ich möchte ja kein Krümmelkacker sein, jedoch nehmt ihr es beim programmieren ja auch sehr genau. :wink:
Deshalb muß ich der Aussage "Allerdings kann man durch Oversampling eben mehr als 10 bit Auflösung hinbekommen." widersprechen. Man erhält nichts weiter als Mittelwerte. Das hat jedoch in meinen Augen nichts mit einer Erhöhung der Messauflösung an sich zu tun. Die bleibt bei 10Bit. Man mittelt nur die Messwertschwankungen. Klar, das hier liegt im Auge des Betrachters. Nur da das 2 Paar verschiedene Schuhe sind, was vorn rein kommt und hinten wieder raus, muß man das gedanklich trennen, sonst kann man das falsch verstehen. Niemand kommt nämlich auf die Idee zu behaupten aus einem 1Bit ADC durch Oversampling eine 64Bit Auflösung herauszubekommen. Ich hoffe jetzt wird klarer wie ich das gemeint habe. :wink:

Mir ist glasklar wie Du das meinst. Du wirst persönlich weil Du glaubst, daß Du recht hast aber Du keine echten Argumente findest. Mit kann mit einem 1 Bit ADC sehr wohl mehr als 1 Bit Auflösung herausbekommen. 2-3 Bits sind gar kein Problem.
64 Bit aber schon, denn dazu muesste man ja mindestens 4^64 = 2^128 ~ 10^38 Messungen durchführen.

Voraussetzung ist natürlich wie schon erwähnt ein geeignetes Rauschen in der richtigen Größenordnung. Eine verwandte Methode zur erhöhung der Meßauflösung wird z.B. auch hier [physics/0201053] Speed of light measurement using ping breitgetreten.

Schlag mal "stochastic resonance" in Wikipedia nach. Vieleicht klingelt es dann.

Bei Delta-Sigma ADCs wird glaube ich auch sowas gemacht: