Go Down

Topic: Höhere ADW-Auflösung - Oversampling? (Read 9556 times) previous topic - next topic

-Jan-

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:
Code: [Select]

#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

Doc_Arduino

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.
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Serenifly

#2
May 21, 2014, 04:40 pm Last Edit: May 21, 2014, 04:52 pm by Serenifly Reason: 1
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:
Code: [Select]

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.

Code: [Select]

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.

Doc_Arduino

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.

Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Serenifly

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.

Doc_Arduino

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.  ;)  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.

Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

-Jan-

Hallo,

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


[..] 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

Serenifly

Quote

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:
Code: [Select]

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

Quote

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.

-Jan-

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

udoklein

@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.
Check out my experiments http://blog.blinkenlight.net

-Jan-

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ß...

MGOS

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

olf2012

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.

Serenifly

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.

-Jan-

#14
May 24, 2014, 04:07 pm Last Edit: May 24, 2014, 04:11 pm by -Jan- Reason: 1
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!  :)

Go Up