Forum: Mikrocontroller und Digitale Elektronik Arduino ADC + Digital Poti -> Problem mit gleitendem Mittelwert


von Franky (Gast)


Lesenswert?

Guten Abend zusammen,

ich bin gerade über ein Problem bei mir gestolpert, bei dem ich nicht 
weiß wie ich das lösen kann. Das Problem liegt vermutlich an bei der 
Bildung eines gleitenden Mittelwerts.

Und zwar habe ich einen Sollwert 0-1023 welcher mit dem map Funktion vom 
Arduino auf einen 8 Bit Wert 0-255 abbilden soll.

Den Sollwert gebe ich vor und die 0-255 gehen an ein Digitales Poti.
Der Wert vom Digitalen Poti wird dann über den ADC eingelesen und dann 
in bestimmten Zeitintervallen um 1 erhöht. Das Problem was ich nun habe 
ist das meine Erhöhung um 1 ab einem Sollwert von ~400 im rauschen des 
Digi Potis untergeht und dann über meinen gleitenden Mittelwert 
rausgemittelt wird.

Das Poti wird dann nicht mehr erhöht.
in der Variable "DigitalPoti" steckt der über 16 Werte gemittelte 
eingelesen wert vom Digital Poti. Wenn ich das +1 auf +2 ändere dann 
funktioniert es, aber ich hätte es gerne mit der +1 Erhöhung 
hinbekommen, aber ich stehe da irgendwie auf dem Schlauch.
1
Das ist die map Funktion
2
digitalPotWrite(((map(DigitalPoti, 0, 1023, 0, 256)) + 1));

Könnte mir da jemand auf die Sprünge helfen was ich da falsch mache?
Hier ein Ausschnitt aus dem relevanten Code:

1
const int RunningAverageCount = 16;               // Anzahl der in den Laufenden-Mettelwert aufgenommenen Messungen
2
int RunningAverageBufferA1[RunningAverageCount];
3
int NextRunningAverage;
4
5
if (millis() > myTimeout8ADC + myTimer8ADC ) { /7 Alle 100ms
6
      myTimer8ADC = millis();  
7
    ADC_Werte_konvertieren();
8
}
9
if (DigitalPoti < (Sollwert - GrenzeUnten)) { //Licht heller machen
10
    
11
      if (millis() > myTimeout1 + myTimer1 ) {
12
        myTimer1 = millis();
13
        digitalPotWrite(((map(DigitalPoti, 0, 1023, 0, 256)) + 1));
14
      }
15
}
16
17
if (DigitalPoti > (Sollwert + GrenzeOben)) {
18
19
    if (millis() > myTimeout2 + myTimer2 ) { //Licht dunkler machen
20
      myTimer2 = millis();
21
      digitalPotWrite( ((map(DigitalPoti, 0, 1023, 0, 256)) - 1));
22
    }
23
}
24
25
26
27
void ADC_Werte_konvertieren(){
28
  int RawA0 = analogRead(A0);
29
30
  RunningAverageBufferA0[NextRunningAverage++] = RawA0;
31
32
  if (NextRunningAverage >= RunningAverageCount){
33
    NextRunningAverage = 0; 
34
  }
35
  int RunningAverageA0 = 0;
36
37
  for(int i=0; i< RunningAverageCount; ++i){
38
    RunningAverageA0 += RunningAverageBufferA0[i];
39
  }
40
  RunningAverageA0 /= RunningAverageCount;
41
42
  DigitalPoti = RunningAverageA0;
43
}

von Wolfgang (Gast)


Lesenswert?

Franky schrieb:
> if (millis() > myTimeout8ADC + myTimer8ADC ) { /7 Alle 100ms
>       myTimer8ADC = millis();
>     ADC_Werte_konvertieren();
> }

Schon hier geht es nach einiger Zeit schief. Da hat jetzt nichts mit 
deinem ADC zu tun, aber guck dir mal an, wie man millis() verwenden 
muss, damit soetwas nicht plötzlich scheinbar hängt.
Stichwort: überlaufsichere Verwendung von millis()

von Stefan F. (Gast)


Lesenswert?

Warum liest du die Einstellung des digitalen Potis analog zurück?

Du kannst dir doch einfach den zuletzt eingestellten Wert merken und 
dann dazu die 1 addieren (anstatt auf den zurück gelesenen Wert).

von Franky (Gast)


Lesenswert?

Wolfgang schrieb:
> Stichwort: überlaufsichere Verwendung von millis()

oh das Checke ich gleich mal, ich hatte da schonmal etwas drüber 
gelesen, aber ich dachte das wäre nur interessant wenn man lange 
laufzeiten mit millis "messen" möchte. bei mir sinds nur 0,1s bis 3s.

Stefan ⛄ F. schrieb:
> Warum liest du die Einstellung des digitalen Potis analog zurück?

mhm, das verstehe ich nicht ganz, ich wollte mit dem einlesen und 
mitteln des Potiwerts eine "saubere" Kurve hinbekommen, weil ich 
wirklich den realen Wert dann für die weitere Verwendung habe. Oder ist 
das keine gute Idee?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Warum liest du die Einstellung des digitalen Potis analog zurück?
Ein Dejavu: Beitrag "Re: Arduino -> Digital Poti -> Wert über ADC wieder einlesen"

von Franky (Gast)


Lesenswert?

Lothar M. schrieb:
> Ein Dejavu: Beitrag "Re: Arduino -> Digital Poti -> Wert über ADC wieder
> einlesen"

Ja, aus diesem Beitrag hatte ich teile von meinem Code hergenommen vor 
Allem die Mittelwertbildung war da ganz gut erklärt, das schien mir da 
ein ähnliches Problem zu sein, Aber ein Lösungsansatz konnte ich da 
nicht herauslesen :(

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Franky schrieb:
> das schien mir da ein ähnliches Problem zu sein
Die Lösung war damals schon bitterer Murks. Nochmal dranrum murksen 
macht das nicht besser.

> im rauschen des Digi Potis
Sei dir sicher: das Ding rauscht in deiner Schaltung sicher am 
wenigsten.

Mein Vorschlag: du zeigst deine Hardware mit Bauteilnamen und 
Tybezeichnungen in einem Schaltplan und beschreibst, was da passieren 
soll. Und darauf basierend schauen wir zusammen, wie eine brauchbare 
Lösung aussehen würde.

: Bearbeitet durch Moderator
von Franky (Gast)


Lesenswert?

Lothar M. schrieb:
> Mein Vorschlag: du zeigst deine Hardware mit Bauteilnamen und
> Tybezeichnungen in einem Schaltplan und beschreibst, was da passieren
> soll. Und darauf basierend schauen wir zusammen, wie eine brauchbare
> Lösung aussehen würde.

Okay, vielen Dank, das mache ich parat. :-)

von Wolfgang (Gast)


Lesenswert?

Franky schrieb:
> oh das Checke ich gleich mal, ich hatte da schonmal etwas drüber
> gelesen, aber ich dachte das wäre nur interessant wenn man lange
> laufzeiten mit millis "messen" möchte.

Nein, das Problem tritt genauso bei kurzen Zeiten auf - allerdings erst 
einige Zeit nach dem letzten Reset des µC.

von MaNi (Gast)


Lesenswert?

[c]
map(DigitalPoti, 0, 1023, 0, 256)) - 1);
[\c]

Mal 2 Fragen:

Ist es Absicht dass so ein krummer Faktor (3,984435797665) verwendet 
wird?

Und wenn nein, warum nimmt man dafür eine map Funktion die aus 4 
Additionen, 1 Multiplikation und 1 Division besteht anstatt 2 Mal rechts 
schieben?

von Franky (Gast)


Lesenswert?

MaNi schrieb:
> Ist es Absicht dass so ein krummer Faktor (3,984435797665) verwendet
> wird?

Tatsache, das war ein Tippfehler. da muss natürlich 255 stehen!

Stefan ⛄ F. schrieb:
> Warum liest du die Einstellung des digitalen Potis analog zurück?

Ich habe das nun angepasst und Speicher mir den wert in eine Variable ab 
und erhöhe diese dann, das scheint zu funktionieren. Ich weiß nicht 
genau warum ich in meinem Kopf gedacht habe das es mit dem rückgelesenen 
wert besser wäre :D

Wolfgang schrieb:
> Stichwort: überlaufsichere Verwendung von millis()

So jetzt begebe ich mich mal diesem Problem, das war mir irgendwie nicht 
bewusst das das ein Problem gibt. Aber ich schaue mir mal an was da das 
Problem ist. Vielen Lieben Dank auf Jeden fall schon einmal

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Franky schrieb:
> So jetzt begebe ich mich mal diesem Problem, das war mir irgendwie nicht
> bewusst das das ein Problem gibt.
Es dauert aber lang, bis du dieses spezielle Problem hast. Und dessen 
Behebung wird deinen Fehler nicht beheben. Dadurch wird nur dein 
Programm an irrelevanter Stelle besser.

Zeichne lieber mal den Schaltplan und mach ein paar Fotos vom Aufbau, 
statt deine Zeit mit solchen Übersprungshandlungen zu verplempern.

https://dorsch.hogrefe.com/stichwort/uebersprung-uebersprungshandlung

: Bearbeitet durch Moderator
von Wolfgang (Gast)


Lesenswert?

Lothar M. schrieb:
> Es dauert aber lang, bis du dieses spezielle Problem hast. Und dessen
> Behebung wird deinen Fehler nicht beheben.
Nichts ist lästiger als Fehler, die nur selten unter unklaren 
Bedingungen auftreten - insbesondere, wenn ein Programm tagelang 
(scheinbar) problemlos läuft und plötzlich seinen Dienst versagt, obwohl 
es vorher doch sooh gut lief.

von Franky (Gast)


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
> Zeichne lieber mal den Schaltplan und mach ein paar Fotos vom Aufbau,
> statt deine Zeit mit solchen Übersprungshandlungen zu verplempern.

Den Schaltplan habe ich schonmal einscannen können. Ich hoffe das es 
dadurch etwas klarer wird. Ein Foto vom Aufbau ist gerade nicht möglich, 
keine Kamera/Smartphone zur Hand.

Aber es ist ganz rudimentär auf einer Lochraster Platine aufgebaut. Der 
Ausgang des OPs geht eine Dimmbare LED Beleuchtung an meiner Bar, diese 
möchte im Eingang 0-5V sehen.

Lothar M. schrieb:
> Es dauert aber lang, bis du dieses spezielle Problem hast. Und dessen
> Behebung wird deinen Fehler nicht beheben. Dadurch wird nur dein
> Programm an irrelevanter Stelle besser.

Wenn ich das richtig verstanden habe dann tritt dort ein Problem nach 
ca. 50 Tagen des Programmstarts auf, weil der Timer dann überläuft. Mir 
ist noch nicht ganz bewusst warum das bei mir ein Problem ist, weil ich 
ja nur ganz kurze intervalle habe.

von MaNi (Gast)


Lesenswert?

Du willst also anhand einer Potistellung eine Spannung von 0-5V 
ausgeben?

Dafür reicht ein Poti alleine.
Wenn du noch mehr Leistung benötigst von mir aus ein OP o.ä. 
Leistungsstufe.
Aber für was genau brauchst du einen uC und einen digitalen Poti?

von Franky (Gast)


Lesenswert?

MaNi schrieb:
> Aber für was genau brauchst du einen uC und einen digitalen Poti?

Guten Abend,

Das "Sollwert"-Poti ist momentan nur zum Manuellen spielen. Später würde 
ich gerne die LEDs Faden lassen und für das hoch und runterfahren würde 
ich gerne den Arduino benutzen. =)

von MaNi (Gast)


Lesenswert?

Franky schrieb:
> Später würde ich gerne die LEDs Faden lassen

Dann mach es doch wie alle anderen auch.
Nimm eine PWM und steuere entweder die LEDs über einen MOSFET oder 
Transistor an.

von Franky (Gast)


Lesenswert?

MaNi schrieb:
> Dann mach es doch wie alle anderen auch.

Der Rest existiert doch schon ;-) ich möchte mir nur mit dem Digital 
Poti eine Spannung von 0V-5V erzeugen.

Es läuft ja auch schon größtenteils alles. Ich hatte halt das Problem 
mit dem gleitenden Mittelwert.

von Manfred (Gast)


Lesenswert?

Franky schrieb:
> Das "Sollwert"-Poti ist momentan nur zum Manuellen spielen. Später würde
> ich gerne die LEDs Faden lassen und für das hoch und runterfahren würde
> ich gerne den Arduino benutzen. =)

0..5V bekommt man mit einen D/A-Wandler hin. MCP4725 gibt es günstig als 
Chinaplatinchen, die passende A*-Library ist ebenfalls kalglos 
auffindbar.

von MaNi (Gast)


Lesenswert?

Franky schrieb:
> ich möchte mir nur mit dem Digital Poti eine Spannung von 0V-5V
> erzeugen.

Das kannst du aber mit einer PWM und einem Tiefpass um Welten einfacher 
und weniger fehleranfällig.

Franky schrieb:
> Es läuft ja auch schon größtenteils alles.

Das bezweifle ich.
Du schreibst die ganze Zeit vom Mittelwert, aber bist du dir zu 100% 
sicher dass jeder Wert den du in das Poti schreiben möchtest auch 
wirklich an dessen Ausgang so umgesetzt wird?

Was für einen Wert bekommt denn digitalPotWrite, wenn DigitalPoti == 0 
ist:
1
digitalPotWrite( ((map(DigitalPoti, 0, 1023, 0, 255)) - 1));

Franky schrieb:
> Ich hatte halt das Problem mit dem gleitenden Mittelwert.

Ich verstehe ehrlich gesagt nicht warum du den überhaupt bildest.
Mach eine Steuerung.
Sollwert stellen. Und wenn nicht gerade die Welt untergeht, dann stimmt 
der Wert innerhalb der Toleranzen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Wolfgang schrieb:
> obwohl es vorher doch sooh gut lief.
Ja, daran arbeiten wir gerade. Und als "Abfallprodukt" kann man dann das 
mit den millis() auch noch richten.

Franky schrieb:
> Den Schaltplan habe ich schonmal einscannen können.
Mit Typbezeichnungen meinte ich die Bestellbezeichnungen der Bauteile, 
so dass man sich ggfs. mal das Datenblatt dazu ansehen kann.

MaNi schrieb:
> Dafür reicht ein Poti alleine.
Das sollte doch im Grunde nicht allzu schwierig zu verstehen sein: bei 
diesr Anwendung muss die Potistellung deshalb nicht "zurückgelesen" 
werden, weil man sie mit einem Stellwert eindeutig selber festlegen 
kann.

Franky schrieb:
> Den Sollwert gebe ich vor und die 0-255 gehen an ein Digitales Poti.
> Der Wert vom Digitalen Poti wird dann über den ADC eingelesen und dann
> in bestimmten Zeitintervallen um 1 erhöht.
Ein Programm, das genau das macht, sieht so aus:
1
initialisierung:
2
3
  millis_alt = millis();
4
  Potistellung = 0;    
5
6
mainloop:    
7
  :
8
  if (millis() - millis_alt > Wartezeit) { // überlaufsicher
9
     millis_alt = millis();
10
     digitalPotWrite(Potistellung);
11
     if (Potistellung<255) Potistellung++;
12
  }
13
  :
Hier wird also einfach jeweils nach der "Wartezeit" der nächsthöhere 
Wert zum Poti geschickt. Das wars, dafür ist kein ADC nötig, ich kenne 
die "Potistellung" ja ganz genau.

Manfred schrieb:
> 0..5V bekommt man mit einen D/A-Wandler hin.
Oder sogar mit der PWM-"Analogausgabe" des Andruiden. Wobei der ADC 
anerkanntermaßen das Mittel der Wahl ist.


BTW: diese map() Funktion im "Originalprogramm" kann ganz leicht duch 1 
Minute Nachdenken und ein "/4" ersetzt werden. Ich bekomm da fast die 
Krätze ans Auge, wenn man so eine einfache Rechung, die der Compiler in 
zwei Assemblerbefehlen abhandeln kann, in eine umständliche Funktion 
packt...

: Bearbeitet durch Moderator
von EAF (Gast)


Lesenswert?

Lothar M. schrieb:
> Ich bekomm da fast die
> Krätze ans Auge, wenn man so eine einfache Rechnung, die der Compiler in
> zwei Assemblerbefehlen abhandeln kann, in eine umständliche Funktion
> packt...

Die Rechnung bzw. Wertebereich (0 bis 1023 / Zweierpotenz) ist ein 
Sonderfall.

Ansonsten hat map() schon seine Berechtigung. Implementiert sie doch die 
"Lineare Interpolation", welche im Grunde ein übliches/häufig 
verwendetes Pattern ist.

Es ist hier also nicht das Problem, dass man das "in eine umständliche 
Funktion packt", sondern dass es offensichtlich eine 
einfachere/schlichtere Möglichkeit gibt.

Die Funktion selber ist ok.
Das in eine Funktion zu packen auch wohl.
Das man es "besser" implementieren könnte, insbesondere, wenn man 
Annahmen über die Wertebereichen treffen kann, ist auch wohl war.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

EAF schrieb:
> Die Rechnung bzw. Wertebereich (0 bis 1023 / Zweierpotenz) ist ein
> Sonderfall.
Aber eben hier im Thread der einzig relevante "Normalfall". Und wenn man 
sich die Probleme manchmal näher anschaut, dann kann man mit solchen 
"Sonderfällen" viele Aufgaben lösen. Nur muss man dafür halt ein wenig 
"binär" und "maschinennah" denken.


> Das man es "besser" implementieren könnte
Mal angenommen, man möchte einen 8-Bit Wert mit dem ADC einlesen, dann 
muss man nur den ADC richtig verwenden, dann liefert der schon den 
passenden 8-Bit Wert zurück und es muß gar nichts skaliert werden. Dazu 
muss man allerdings die verwendete Hardware kennen. Und das sollte für 
einen Embedded-Programmierer, der ja unmittelbar an der Hardware 
programmiert, selbstverständlich sein.

von MaNi (Gast)


Lesenswert?

Franky schrieb:
> digitalPotWrite( ((map(DigitalPoti, 0, 1023, 0, 256)) - 1));

Doppelt ärgerlich ist dann noch, dass in dieser Zeile mindestens 2 
Fehler drin
 sind die durch einen einfachen >> 2 nicht aufgetreten wären:
* Falsche Skalierung (1024/257) habe ich oben ja schon erwähnt.
* "Unterlauf" bei DigitalPoti == 0

von EAF (Gast)


Lesenswert?

Lothar M. schrieb:
> Und das sollte für
> einen Embedded-Programmierer, der ja unmittelbar an der Hardware
> programmiert, selbstverständlich sein.

Ich halte deine Erwartungshaltung **hier** für überzogen.
Denn ganz offensichtlich ist Franky noch kein erfahrener 
Embedded-Programmierer.
Aber das kann ja noch werden....

Wenn ich da auch noch meine Zweifel habe, denn die Reaktion auf die 
Millis Problematik lässt schon auf hinderliches Beharrungsvermögen 
schließen.

von Stefan F. (Gast)


Lesenswert?

Lothar M. schrieb:
> Mal angenommen, man möchte einen 8-Bit Wert mit dem ADC einlesen, dann
> muss man nur den ADC richtig verwenden, dann liefert der schon den
> passenden 8-Bit Wert zurück und es muß gar nichts skaliert werden.

Ja, aber so einfach denkt heute kaum noch jemand. In der Firma werde ich 
immer für meine einfachen Lösungsansätze gefeiert.

Warum kann der Nachwuchs das nicht mehr? Vielleicht, weil die Menschen 
immer weniger mit einfachen Lösungen aufwachsen.

Manche können ohne Thermomix scheinbar nicht einmal mehr einen Eintopf 
kochen. Andere können sich nicht vorstellen, dass die Menschen schon vor 
hunderten Jahren Kleidung trugen, die der heutigen sehr ähnlich ist. Als 
ob man ohne App nur Bärenfelle und Kartoffelsäcke verarbeiten könnte.

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
Noch kein Account? Hier anmelden.