Hallo! Ich habe ein Problem, ich habe ADC-Werte, die ich auf einem LCD anzeigen möchte. Da diese naturgemäss immer ein wenig flattern möchte icheinige Werte nehmen und daraus ein brauchbares Resultat machen. Nun mein Problem, wie mache ich das am besten? Ich möchte, wenn immer das geht, nicht die Werte aufaddieren, denn so bekomme ich zu grosse Zahlen, oder stimmt das nicht? Wie macht ihr das? Ich benutze einen Atmega8, hat jemand vielleicht Beispiel code, oder sonstige Tipps?? Vielen Dank für Tipps und alles andere nutzliche!
Hi, nicht Aufaddieren?! Das wirst Du bei einfachen Filtern aber zwangsweise immer müssen. Am schnellsten wird sich wohl ein gleitender Durschnitt realisieren lassen: eine bestimmte Anzahl von Werten (z.B. 20) in einem Ringbuffer ablegen, aufaddieren und dann durch die Anzahl der Werte dividieren. Kommt ein neuer Wert vom ADC, haust Du den ersten Wert raus usw... Grüße, Peter
Das klingt einleuchtend, nur wie kann ich einen solchen Ringbuffer realisieren? Bei 20 Werten von je 10bit, das ist bereits recht viel, so dass ich zwangsläufig aufs SRam zugreiffen muss, das kann ich (noch) nicht, oder komme ich darumherum? Könntest du mir das mit dem Ringbuffer ein wenig besser erklären? Nicht das Prinzip, sondern eher, wie das Softwaretechnisch zu lösen ist? Danke!
Hi Ste, der Vorschlag mit dem gleitenden Mittelwert wäre auf jeden Fall eine Lösung. Mit einem Ringpuffer verheizt du aber eine ganze Menge Zeit und Speicher. Ein einfacher und auch sehr schneller (wenig Rechenzeit) Weg wäre folgender: unsigned long value = 0; unsigned int avg = 0; for(;;){ value -= avg; // hier: warten auf neuen ADC-Wert value += 'ADC-Wert'; avg = value >> 3; // hier: Ausgabe von avg (= Mittelwert) } Ist vielleicht nicht auf den ersten Blick einzusehen wie das funktioniert, kann man sich aber recht einfach klar machen. Du kannst den Krempel natürlich auch in eine Funktion stecken, falls dich die for-Schleife stört (Variable müssen dann natürlich static sein). ... und jetzt erzählst du mir bestimmt, dass du den Kram in Assembler programmieren möchtest, oder? ;o) Gruß Olaf
Du programmierst anscheinend in Assembler!? Wenn du Dich nicht ans SRAM rantraust, könntest Du auch einfach über 20 (oder mehr) Werte aufsummieren und dann durch die Anzahl der Werte dividieren, also ohne Ringbuffer. in C-Pseudocode (hoffe, Du kennst Dich aus): while(1) { summe=0; for(i=0;i<=19;i++) { hole ADCwert; summe=summe+ADCwert; } mittelwert=summe/20; mittelwert ausgeben; } Sollte stimmen. Grüße!
@franz: ... das ist aber kein gleitender Durchschnitt. Und wenn du schon dividierst, addiere doch z. B. 16 oder 32 Werte; die kannst du wenigstens kommod durch schieben dividieren (man muss den kleinen Kerl ja nicht unnötig quälen). Gruß Olaf
Da hast Du natürlich recht, Olaf. Hab' ich vergessen, dazuzuschreiben. Grüße, Peter
Ich weiß ja nicht 1. wie oft Du mißt 2. wie schnell ein genauer Wert angezeigt werden soll Evtl. kannst Du ja das aktuelle Ergebnis mit dem gespeicherten von vorhin vergleichen. Ist das neue Ergebnis größer, so wird der gespeicherte Wert lediglich um 1 inkrementiert, ist er gleich bleibt er gleich, ist er kleiner wird der gespeicherte Wert um 1 dekrementiert. Lediglich wenn sich die beiden Werte um einen größeren Betrag unterscheiden (z.B. 20), wird sofort der aktuellste Wert übernommen und gespeichert. Den gespeicherten Wert verwendest Du für Deine Anzeigeroutine. Nur so als Idee! Du brauchst damit nur 2 Byte opfern (für Dein gespeichertes 10-Bit Ergebnis)
Tja, ich mache das ganze in Assembler...:) Muss mir aber langsam wirklich überlegen, ob ich nicht doch besser wechseln soll, wenn ich 20 werte aufaddieren will, dann erhalte ich eine riesen Zahl, falsch, ich weiss gar nicht wie ich eine so grosse Zahl erhalten kann in Assembler. Könnte mir da jemand mal auf die Sprünge helfen? Wie ich dann durch 16 teilen kann weiss ich (glaube ich zumindest ==> 4 mal schieben oder??) Ich brauche die Daten schon nicht so schnell, dass ich einen Ringbuffer basteln müsste (was sowieso nicht zu schaffen ist)!! Danke für eure Hilfe!
Stell Dir Rechnen mit Zahlen > 8Bit einfach so wie schriftliches Rechnen vor, nur daß man statt Ziffern Bytes nimmt. Außerdem kann man nicht mehrere Zahlen gleichzeit addieren, sondern muß das halt nacheinander machen. Wenn Du zwei 8Bit-Zahlen addierst, dann kann das Ergebnis auch 9 Bit haben, das 9. Bit ist dabei der Übertrag und wird als Carry-Bit bezeichnet. Dieses Carry-Bit mußt Du jetzt in einem extra Byte speichern. Was in der Schule die 10er-Stelle war, das ist hier halt das "obere" Byte einer 16Bit-Zahl. Nun braucht man praktischerweise keine Abfrage ob das Bit gesetzt ist und falls ja dann ein extra Add oder so, sondern es gibt einen Befehl "Add with carry". Vier 8Bit-Meßwerte zu addieren geht dann etwa so: R0: unteres Byte des Ergebnisses R1: oberes Bytes des Ergebnisses R2: Meßwert R3: 0 add r0, r2 Es kann noch keinen Übertrag geben, nächsten Meßwert lesen add r0, r2 adc r1, r3 Meßwert einlesen add r0, r2 adc r1, r3 Meßwert einlesen add r0, r2 adc r1, r3 bye Markus
@olaf hallo ich habe das dein programm : unsigned long value = 0; unsigned int avg = 0; for(;;){ value -= avg; // hier: warten auf neuen ADC-Wert value += 'ADC-Wert'; avg = value >> 3; // hier: Ausgabe von avg (= Mittelwert) } ein bisschen studiert aber komme nun nicht darauf wie das funktioniert. kannst du mir das vieleicht schnell erklären? auf jeden fall sieht diese lösung sehr elegant aus! besten dank tobias
Hi Tobias, einfach ausgedrückt: Im Normalfall ergibt sich der gleitende Mittelwert zum Zeitpunkt t0 z. B. aus der Summe der Werte von t-3 bis t0, geteilt durch die Anzahl der Werte (hier: 4 [t-3, t-2, t-1 und t0]). Bei der Berechnung des nächsten Mittelwertes fällt t-3 aus der Berechnung heraus und der neue Sample-Wert t1 kommt hinzu, usw. Dabei musst du immer auf vergangene Werte zurückgreifen. In erster Näherung kann man nun sagen, dass in avg bereits die Werte aus der Vergangenheit enthalten sind. Diese werden nun quasie als Rückkopplung wieder eingespeist. Du musst natürlich nicht unbedingt durch 8 teilen, kannst auch durch 2, 4, 16, 32, ... teilen, kommt natürlich immer auf dein Signal an (zu erwartende Signaländerung im vorgegebenen Zeitintervall). Ach ja, im obigen Beispiel ist die Variable value etwas überdimensioniert. Der Wert kann ja maximal 8192 erreichen (1024 [max. ADC-Wert] * 8 [Teiler] = 8192), somit würde auch ein unsigned int genügen. Ich habe mal eine Excel-Tab angehängt, mit der man sich den Output ansehen kann. Gruß, Olaf
Hallo Olaf, hab grad deinen Artikel hier entdeckt und bin sehr begeistert. Werds gleich mal ausprobieren und hoffentlich dann in meine Diplomarbeit einbauen. Vielen Dank fürs bereitstellen von dem Excell file!
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.