Newbie verzweifelt: PWM-Ausgangswert berechnen

Hallo Leute, ich kriege das einfach nicht hin…. :slightly_frowning_face:

Der Ausgangswert am PWM-pin11 soll sich für eine LED-Helligkeitsteuerung nach folgender Formel berechnen:

Ausgang = Eingang x (190/1023) +10

Das der Versuch mit dem Befehl “ analogWrite(11, A0 * (190/1023) +10 );“ konstant zum Ergebnis 10 am Ausgang - unabhängig vom Eingangswert - führt hat mich nicht weiter erstaunt, da das Zwischenergebnis 190/1023 für den Arduino vermutlich = 0 und nicht = 0.18572825 ist.

Ich habe das Programm also immer weiter „aufgebohrt“, immer mehr Variablen definiert, immer mehr als float deklariert, aber es funzt immer noch nicht. Der Wert am Ausgang ist immer 10 unabhängig von der Potentiometer Einstellung.

Was mache ich falsch? Wo ist der Fehler?

// LED Helligkeit mit Poti steuern und Wert des Ausgangs am Laptop Serial Monitor anzeigen,
// Berechnung Ausgangswert unter Berücksichtigung einer min. und max.-Intesität
// Potentiometer -> pin A0
// LED bzw. MOSFET -> pin 11 (PWM)

float val = 0; // variable für Eingangswert an pin A0
float m = 190 / 1023; // Variable "m" (lineare Steigung) als float setzten, da sonst Ergebnis der Division = 0
float Ausgabe = 0; // variable für Ausgangswert an pin 11

void setup()
{
pinMode(11, OUTPUT); // pin 11 als Ausgang deklarieren
Serial.begin(9600); // initialize serial communications at 9600 bps:
}
void loop()
{
val = analogRead(A0); // Wert Analogeingang A0 an die Variable "val" übergeben
Ausgabe = ((val*m)+10); // Ausgabeswert berechnen
analogWrite(11, Ausgabe); // Ausgabeswert an pin11 ausgeben

Serial.print("Ausgang = " ); Serial.print(Ausgabe); // Gib den Wert von "val" am Serial Monitor aus
delay(200); // Warte 0,2 Sekunden
}

Danke vorab für Eure Mithilfe!
Gruß Wühlmaus

Versuche mal die Variablen mit Dezimalpunkt zu initialisieren.
Also so:

  float val = 0.0;            // variable für Eingangswert an pin A0
  float   m = 190.0 / 1023.0;     // Variable "m" (lineare Steigung) als float setzten, da sonst Ergebnis der Division = 0
  float Ausgabe = 0.0;        // variable für Ausgangswert an pin 11

und unten die Berechnung auch mit Dezimalpunkt.

 Ausgabe = ((val*m)+10.0);         // Ausgabeswert berechnen

Laut der Referenz müssen float Variablen mit Dezimalpunkt gefüttert werden, sonst werden sie als int behandelt. Aus https://www.arduino.cc/en/Reference/Float:
"If doing math with floats, you need to add a decimal point, otherwise it will be treated as an int. See the Floating point constants page for details. "

Wuehlmaus:
Was mache ich falsch? Wo ist der Fehler?

Wenn Du einen Quotienten als Gleitkommazahl berechnen möchtest, dann darfst Du keine reine Ganzzahldivision machen.

Falsch eGanzzahldivision:

  float m = 190 / 1023;

Das Ergebnis ist null (Rest 190)und das sieht man auf den ersten Blick.

Mache entweder aus dem Dividenden oder aus dem Divisor eine Gleitkommazahl.

  float m = 190.0 / 1023; // Dividend als Gleitkommazahl

oder

  float m = 190 / 1023.0; // Divisor als Gleitkommazahl

Dann wird es eine Gleitkommadivision.

hi,

ich hab’ noch nie mit analog inputs gearbeitet. analog = böse, digital = nett. hab’ den post auch nach etwa 20 sekunden gelöscht, aber natürlich war wer schneller… :confused:

gruß stefan

Versuch es mal mit einem UL Zwischenergebnis:

unsigned int val;         // variable für Eingangswert an pin A0
byte Ausgabe;             // variable für Ausgangswert an pin 11

void setup()
{
  Serial.begin(115200);
  Serial.println("Anfang");
  pinMode(11, OUTPUT);   // pin 11 als Ausgang deklarieren
}
void loop()
{
  val = analogRead(A0);           // Wert Analogeingang A0 an die Variable "val" übergeben
  Ausgabe = ((val * 190UL / 1023) + 10);   // Ausgabeswert berechnen
  // analogWrite(11, Ausgabe);       // Ausgabeswert an pin11 ausgeben

  Serial.print("val = " ); Serial.print(val);
  Serial.print("  Ausgang = " ); Serial.println(Ausgabe);
  delay(200);                                              // Warte 0,2 Sekunden
}

Da Du letztlich einen 8-Bit-Wert ausgeben willst, ist float nicht optimal. Da kann man in der Ganzzahlenarithmetik bleiben. Standard für Berechnungen ist int (gilt für den UNO), außer es wird anders festgelegt. Ich mache das hier durch UL Eine Konstante mit Punkt (190.0) setzt auf float.

Eisebaer:
... aber natürlich war wer schneller... :confused:

Hallo,
wenn jurs aktiv ist, kann ich gemütlich Kuchen essen :slight_smile:

Zu Deinem 20 Sekunden-Beitrag: Nach dem Reset sind IOs Eingänge, auch die analogen, muß man nichts machen.

@Eisebaer:
Hatte nach meinem Post gesehen das Du Deinen Post wegen analogem Eingang gelöscht hast. Daraufhin habe ich meinen Post nach 5 Sperrminuten editiert und den Kommentar zu den analogen Inputs entfernt. Aber Du warst schneller mit der Antwort... :slight_smile:

Ach und wegen

Eisebaer:
analog = böse, digital = nett.

Das stimmt schon irgendwie. Vor allem wenn dann noch eine Schaltung, die analog arbeitet, dazu kommt. Mit analog stehe ich auch ein wenig auf Kriegsfuß. Digital ist schon einfacher.

Danke für die Hilfe !!!

´habe Eurem Ratschlag folgend wie ein Verrückter überall Dezimalpunkte nachgerüstet, auch bei +10 als +10.0

-> Es funktioniert!!!!!

Jetzt muss ich mal probieren ob sich das auch wieder vereinfachen lässt.

Also die Variablen "val" und "Ausgabe" sparen.

Es müsste doch möglich sein die Berechnung direkt
im Befehl " analogWrite(11, ......) durchzuführen?

P.S.: ´habe mir für meine Arduino Tätigkeit 3 deutsche Tutorials ausgedruckt. Nirgends findet sich ein Hinweis auf diese Dezimalpunktsache :o

Wuehlmaus:
Es müsste doch möglich sein die Berechnung direkt
im Befehl " analogWrite(11, …) durchzuführen?

Ja, warum nicht.
Zum Bleistift:

 analogWrite(11, (val * 190.0 / 1023.0) + 10.0);       // Ausgabeswert an pin11 ausgeben

oder ganz extrem

analogWrite(11, (analogRead(A0) * 190.0 / 1023.0) + 10.0);       // Ausgabeswert an pin11 ausgeben

Wird aber irgendwann unübersichtlich.

Wuehlmaus:
Es müsste doch möglich sein die Berechnung direkt
im Befehl " analogWrite(11, ......) durchzuführen?

Das geht auch mit UL und ist die m. E. beste Lösung, float ein Umweg:

Der anloge Wert ist unsigned int, Du willst einen 8-Bit-Wert (byte) haben. Da ist der Weg über unsigned long näherliegend als der über float.

analogWrite(11, (analogRead(A0) * 190UL / 1023) + 10);

Ist zumindest meine Meinung :slight_smile:

Ein Kabarettist sagte:
"Was der Bauer nicht kennt frisst er nicht, was der Chinese nicht kennt frisst er erst mal"

Heißt: Was der Newbie nicht kennt oder versteht das benutzt er erst mal nicht.
Erst wenn er zu viel Zeit zum rumspielen und experimentieren hat und das Programm nicht endlich fertig werden muss probiert er völlig neue Sachen.

Unsigned? ´habe im Tutorial nachgeschaut aber schlau bin ich daraus auch nicht geworden.

So wie ich das verstehe sind das Datentypen mit einem größeren Wertebereich aber leider auch keine Dezimalzahlen. !?!

Bin gespannt ob die Vorschläge von Katsu funktionieren.
Werde ich nachher mal probieren. Das Wäre dann ja richtig einfach....

Unsigned? ´habe im Tutorial nachgeschaut aber schlau bin ich daraus auch nicht geworden.

Sign = Vorzeichen. Also ohne Vorzeichen. Nur positive Zahlen.

So wie ich das verstehe sind das Datentypen mit einem größeren Wertebereich aber leider auch keine Dezimalzahlen

Ob das Dezimal ist oder was anders liegt nur daran wie du als Mensch die Daten interpretierst. Es sind aber Integer und keine Gleitkommazahlen

Hallo Leute!
Funktioniert mit Euern Tipps super!

Auch die extrem kurze Version läuft:

"......
analogWrite(11, (analogRead(A0) * 190.0 / 1023.0) + 5.0);
Serial.print("Ausgang = " ); Serial.print((analogRead(A0) * 190.0 / 1023.0) + 5.0);
......"

Dem besseren Verständnis halber verwende ich aber diese Version mit nur einer float-Variablen.
Das Ganze ist ja schließlich nur ein kleiner Teil des geplanten "Gesamtkunstwerkes" für die Sternwarte.

// LED Helligkeit mit Poti steuern und Wert des Ausgangs am Laptop Serial Monitor anzeigen
// Berechnung Ausgangswert unter Berücksichtigung einer min. und max.-Intesität
// Potentiometer    -> pin A0
// LED bzw. MOSFET  -> pin 11 (PWM)
// Anteuerung LED: max. Wert 195, min. Wert 5

 float val = 0.0;            // variable für Eingangswert an pin A0
 
void setup()
{
 pinMode(11, OUTPUT);   // pin 11 als Ausgang deklarieren
 Serial.begin(9600);    // initialize serial communications at 9600 bps:
}
void loop()
{
 val = analogRead(A0);             // Wert Analogeingang A0 an die Variable "val" übergeben
 val = val*(190.0 / 1023.0)+5.0;   // Ausgabeswert berechnen
 analogWrite(11, val);             // Ausgabeswert an pin11 ausgeben

 Serial.print("Ausgang = " ); Serial.print(val);      // Gib den Wert von "val" am Serial Monitor aus
 delay(200);                                          // Warte 0,2 Sekunden
}

Warum machst du das ganze nicht nur mit integer?

void setup()
{
  pinMode(11, OUTPUT);   // pin 11 als Ausgang deklarieren
  Serial.begin(115200);    // initialize serial communications at 9600 bps:
}
void loop()
{
  analogWrite(11, (analogRead(A0) * 190UL) / 1023 + 5);             // Ausgabeswert an pin11 ausgeben

  Serial.print("Ausgang = " );  Serial.println((analogRead(A0) * 190UL) / 1023 + 5);      // Gib das gleiche am Serial Monitor aus, wenn das wirklich gebraucht wird, besser mit variablen arbeiten
  delay(200);                                          // Warte 0,2 Sekunden
}

Kommen Ergebnisse von 5 bis 195, wie gewünscht. Und keine float Berechnungen

Wuehlmaus:
Erst wenn er zu viel Zeit zum rumspielen und experimentieren hat und das Programm nicht endlich fertig werden muss probiert er völlig neue Sachen.

Auch ein Spruch: "Ich habe keine Zeit, die Säge zu schärfen, ich muß sägen!"

Die Problematik mit der Darstellung von Zahlen im Computer wird Dir immer wieder auf die Füße fallen, weshalb ich Dir die Muße wünsche, dich damit beschäftigen zu können.

Der Maximalwert vom analogen Meßwert ist 1023, das mit 190 multipliziert ist 194370 oder 0x2F742, das sind drei Byte. Der Variablentyp, wo drei Byte reinpassen, ist vier Byte groß und wird long (L) oder unsigned long genannt. Da analogRead() einen Integerwert zurückgibt und die Konstante 190 (beim 8-Bit-Prozessor des UNO) ebenfalls als Integer verstanden wird, gibt es einen Überlauf, also Fehler. Durch 190L oder 190UL wird die Konstante als long oder unsigned long interpretiert und die Berechnung entsprechend mit vier Byte durchgeführt, wodurch ein Überlauf vermieden wird.

ElEspanol:
Warum machst du das ganze nicht nur mit integer?

Weil er es nicht versteht, aber wir arbeiten dran :slight_smile:

Wuehlmaus:
Das Ganze ist ja schließlich nur ein kleiner Teil des geplanten "Gesamtkunstwerkes" für die Sternwarte.

Du machst mich neugierig, wo ich Dich unter oder im Auto vermutet habe.

Ich glaub´s ja gar nicht!

Das funktioniert auch !!!!!

Aber warum zum Teufel?

-> agmue (habe geschrieben während Du gepostet hast)

"Wenn ich keine Zeit habe meine (Motor)-Säge zu schärfen und ich muss dringend sägen kauf ich mir halt eine neue scharfe Kette"

-> Man muss immer Plan B im Sack haben !!!!

Danke für deine Erklärung aber das ist mir heute Abend nach 3 Bier viel zu hoch.

(´habe die letzten Stunden damit vereiert die LED´s nach Ablauf eines Timers auszuschalten. Ich wollte schon im Forum nachfragen als ich endlichen den blöden Fehler in meiner Logik entdeckt habe. Aber so ist das halt als Newbie...)

Im Sketch von #4 habe ich ja Werte ausgeben lassen. Das könntest Du noch erweitern und dann mal mit 190 und 190UL vergleichen. Letztlich habe ich das auch nicht anders gemacht. Am analogen Eingang habe ich ein 10k-Poti zum Probieren. :slight_smile:

Wuehlmaus:
"Wenn ich keine Zeit habe meine (Motor)-Säge zu schärfen und ich muss dringend sägen kauf ich mir halt eine neue scharfe Kette"

Ja, das habe ich auch schon gemacht. Inzwischen habe ich so viele Ketten, daß ich eine Saison damit durchkomme :slight_smile:

Wuehlmaus:
Aber so ist das halt als Newbie...)

Wenn ich Dir was verraten darf: Das bleibt so und wird sich nie ändern! Nur die Probleme werden komplexer.

Prosit!

agmue:
Nur die Probleme werden komplexer.

Da hast du allerdings recht.

analogWrite(11, (analogRead(A0) * 190UL) / 1023 + 5);

Das funktioniert weil zuerst die Multiplikation gemacht wird.
Ich weiß jetzt nicht ob die Berechnung von 190 / 1023 in der ersten Version bereits als Optimierung im Compiler vorgenommen wird, da das Konstanten sind die während der ausführung des Sketches sich nicht ändern.. Da Integer keine Nachkommastellen haben können, ist das Ergebnis null.

bei dieser Version

analogWrite(11, (analogRead(A0) * 190UL) / 1023 + 5);

wird durch die Klammern erzwungen daß der gemessene Wert zu Ausführungszeit des Sketches mit 190 als 4 Byte Zahl multipliziert wird. Das damit das Ergebnis sicher nicht zu groß wird und durch überlauf eines Int Wertes (2Byte) nicht ein falsches Ergebnis rauskommt. Erst danach wird die Division gemacht bei der die Kommastellen abgeschnitten werden und dann die Kostante dazugezählt. Daß hierbei dei Kommastellen abgeschnitten werden hat keinen Einfluß auf das Ergebnis da analorWrite sowieso nur einen Byte-Wert annimmt.

Grüße Uwe

Hallo Uwe,
der Sketch in #4 ist getestet und funktioniert. Im Umkehrschluß wird 190UL / 1023 nicht vom Compiler zu 0 optimiert. Gute Compilerprogrammierer :slight_smile: