Wechselblinker + PWM unsauber, Drehencoder zählt nur vorwärts

Hi,
in einem Projekt soll ein LED-Wechselblinker realisiert werden, wobei die Helligkeit einer LED verstellt werden soll.

Zwei LEDs blinken also abwechselnd, mindestens eine wird per PWM angesteuert.

Erstes Problem: Wenn zu dem Blinken noch die Helligkeit eingestellt werden soll, wird das Blinken unsauber bei höheren Frequenzen (~20 Hz). Eigentlich sollte man bei diesen Frequenzen das Blinken kaum mehr wahrnehmen können, die LEDs (bzw. mindestens eine) flackern jedoch unschön. Schließt man einen Piezo Buzzer statt LED an, hört man auch deutlich ein "wah-wah".
Lasse ich beide LEDs mit maximaler Helligkeit wechselblinken, ist alles einwandfrei sauber.

Kommt es bei solchen (ich denke für den Arduino eher geringen) Frequenzen schon zu Schwebungs-Erscheinungen? Kann man diese ausbügeln?
Der Effekt tritt sowohl bei delay basierten Blinken als auch per Millis auf.

Zweites Problem: [Erledigt] Ein Drehencoder mit Drehknopf soll eingesetzt werden, um Werte zu verstellen. Wir haben einen STEC16B01 Inkrementalgeber von Alps mit 24 Impulsen/U.
Sämtlicher Beispielcode, den ich bisher getestet habe, um mit dem Encoder primitiv vorwärts oder rückwärts zu zählen und das Ergebnis anzuzeigen, scheitert darin, dass der Encoder nur in eine Richtung zählt. Ich kann auch Pin A und B vertauschen, es geht nur nach oben. Beim Rückwärts drehen springt der Wert dann nur immer zwischen zwei Zahlen hin und her, bleibt aber im Endeffekt gleich, etwa: 1, 2, 3, 4, 5, 6, 5, 6, 5, 6, 5, 6, ....

Getestet mit Codes von hier:
https://www.pjrc.com/teensy/td_libs_Encoder.html
http://arduinoprx.de/?page=4&obj=35

Der Encoder wird selbst mit den internen PullUp-Widerständen betrieben und ist noch nicht debounced, hat also hardware seitig keine Widerstände oder Kondensatoren verlötet. Liegt hier der Hund begraben? Warum funktioniert dann eine Drehrichtung einwandfrei? Es ist auch egal, ob schnell oder extrem langsam gedreht wird.

Danke und viele Grüße,
Stephan

Die Fehler sind offensichtlich. Eine PWM mit 20Hz ist falsch! Die sollte deutlich höherliegen. Bitte Sketch anhängen.

Schaltung und Sketch wären gut.
Ich mag nicht auf eriner Seite suchen welchen Sketch Du vieleicht verwendest hast.

Schwebung tritt immer auf wenn 2 Frequenzen gemischt werden.

Grüße Uwe

Hi,
danke für die schnellen Antworten.

Beispiel-Sketch für Problem 1 (blinken) mit delay. Blinken ohne delay (millis) verhält sich genauso.

 int Pinblau = 6;
 int Pinrot= 3;
 
 int frequenz = 70;
 
void setup() 
{
  
  pinMode(Pinblau, OUTPUT);
  pinMode(Pinrot, OUTPUT); 
   
}

void loop() 
{
  
int blinkzeitabstand = 500/frequenz;
analogWrite(Pinblau, 120);
delay(blinkzeitabstand);
analogWrite(Pinblau, 0);
analogWrite(Pinrot, 120);
delay(blinkzeitabstand);
analogWrite(Pinrot, 0);
}

Bei 70 Hz sollte man ein Blinken nicht mehr wahrnehmen können. Stimmt auch, trotzdem wird die rote LED mit geschätzt 5Hz ungleichmäßig Heller/dunkler. 70 Hz sind ein Extrembeispiel. Wie gesagt soll sich das später in Frequenzen abspielen, die man gerade noch wahrnehmen kann (also etwa 20-40 Hz und weniger).

Schaltplan: Einfach nur zwei LEDs mit Vorwiderstand an den entsprechenden Pins.

Code für Problem 2:

////////////////////////////////////////////////////////////////////////
///// Beispiel Quellcode 4 Rotary Drehimpulsgeber im Loop abfragen /////
////////////////////////////////////////////////////////////////////////

// Variable Encoder 1
int encoder0PinA = 11;
int encoder0PinB = 10;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
int n = LOW;

// Variable Encoder 2
int encoder1PinA = 9;
int encoder1PinB = 8;
int encoder1Pos = 0;
int encoder1PinALast = LOW;
int n1 = LOW;


void setup() { 
// Declare Pins as Input 
  pinMode (encoder0PinA,INPUT);
  pinMode (encoder0PinB,INPUT);
  pinMode (encoder1PinA,INPUT);
  pinMode (encoder1PinB,INPUT);
  digitalWrite(encoder0PinA, HIGH);
  digitalWrite(encoder0PinB, HIGH);
  digitalWrite(encoder1PinA, HIGH);
  digitalWrite(encoder1PinB, HIGH);
  
// Start Serial to check up Encoders  
  Serial.begin (9600);
} 

void loop() { 
// Read current Encoder pin A Positions to nX
  n = digitalRead(encoder0PinA);
  n1 = digitalRead(encoder1PinA);
    


  if ((encoder0PinALast == LOW) && (n == HIGH)) {   // Check last State of Encoder pin A = 0  && n = 1
    if (digitalRead(encoder0PinB) == LOW) {         // Check current Encoder pin B = 0
      encoder0Pos--;                                // Count down Encoder value -1
    } 
    else {
      encoder0Pos++;                                // Count up Encoder value +1
    }
    Serial.print ("Position 1: ");                  // Write Encoder value to Serial
    Serial.print (encoder0Pos);
    Serial.println (" ");
  } 

  if ((encoder1PinALast == LOW) && (n1 == HIGH)) {   // Check last State of Encoder pin A = 0  && n = 1
    if (digitalRead(encoder1PinB) == LOW) {         // Check current Encoder pin B = 0
      encoder1Pos--;                                // Count down Encoder value -1
    } 
    else {
      encoder1Pos++;                                // Count up Encoder value +1
    }
    Serial.print ("Position 2: ");                  // Write Encoder value to Serial
    Serial.print (encoder1Pos);
    Serial.println (" ");
  } 




// Write n to last state of Encoder pin A
  encoder1PinALast = n1;
  encoder0PinALast = n;
}

Das ist ein Beispiel-Code von hier: http://arduinoprx.de/?page=4&obj=35
Modifziert auf zwei Drehencoder und Pullup-Widerstände aktiviert.

Im Serial-Monitor zählt er egal welche Drehrichtung immer nach oben bei beiden Encodern.

Schaltplan entspricht folgendem Bild, aber mit zwei Encodern an entsprechenden Pins:

Also beides erstmal äußerst grundlegend.

Grüße, Stephan

Wuiz:
Beispiel-Sketch für Problem 1 (blinken) mit delay. Blinken ohne delay (millis) verhält sich genauso.

 int Pinblau = 6;

int Pinrot= 3;

Dein Problem-1 dürfte damit zu tun haben, dass Du in Deinem Sketch zwei Pins mit unterschiedlicher PWM-Frequenz verwendest.

Auf dem UNO haben
Pin-5 und -6: PWM-Frequenz ca. 980 Hz
Pin-3, -9, -10, und -11: PWM-Frequenz ca. 490 Hz

Wie verhält es sich, wenn Du zwei Pins mit derselben PWM-Frequenz verwendest, also beispielsweise Pin-5 und Pin-6? Oder Pin-3 und Pin-9?

Wuiz:
Code für Problem 2:
...
Modifziert auf zwei Drehencoder

Wenn der Original-Code aus dem Internet für einen Encoder korrekt funktioniert und Dein von Dir selbst modifizierter Code nach der Modifizierung falsch funktioniert, dann wird wohl bei der Modifizierung irgendwas falsch gelaufen sein.

Beispielsweise kann ich nirgends in Deinem Code erkennen, dass Du Dir den letzten Zustand des B-Anschlusses an den Encodern irgendwo merkst. Ohne kannst Du am B-Pin überhaupt keine State-Change-Erkennung hinbekommen.

Hi,
das werde ich später oder morgen mal testen. Die LEDs sind momentan auf einem Vallemann RGB Shield, da sind die Pins fix. Werde aber die Option mal mit einer RGB-LED ohne Shield testen. Die soll auch später genutzt werden.

Der Original Encoder-Code ist für vier Encoder gedacht. Habe lediglich alles gelöscht was mit den anderen Encodern zu tun hat. Soeben nochmal den wirklich originalen Code getestet (nur zwei von vier Encodern angeschlossen) und die internen Pullups aktiviert. --> Gleiches Resultat.

Kann natürlich sein dass dem Code von der genannten Seite noch was fehlt.
Werde ich auch in Ruhe nochmal durchgehen, wenn Zeit ist.

Grüße, Stephan

Hi,
mittlerweile etwas länger her, ich möchte das Thema aber nochmal hoch holen.

Die Drehencoder funktionieren mittlerweile wie gewünscht, das war wohl eine Kombination aus verkehrt herum angeschlossen (hat dann immer nur in eine Richtung gezählt) oder einem Code, der nicht gut zum Programm gepasst hat.
Erwartungsgemäß kann man den Drehknopf nur langsam drehen, da die Encoder nicht entprellt sind. Sollte vielleicht noch nachgeholt werden, ist aber erstmal zweitrangig.

Wichtiger ist eigentlich noch das Problem mit den LEDs.
Ich habe beide Farben nun an Pin 5 und 6 angeschlossen, also bei ~980Hz PWM-Frequenz.
Leider flimmern die LEDs auch so nicht sauber; es wird immer eine Art Störung überlagert (die würde ich mit meinen Grundkenntnissen als Schwebung / Interferenz o.ä. beschreiben).

Lässt sich sowas irgendwie filtern? Vielleicht haben wir demnächst die Möglichkeit, mal ein Oszi dran zu hängen.

Grüße, Stephan

Wuiz:

....

int blinkzeitabstand = 500/frequenz;
....
}




Bei 70 Hz sollte man ein Blinken nicht mehr wahrnehmen können. Stimmt auch, trotzdem wird die rote LED mit geschätzt 5Hz ungleichmäßig Heller/dunkler. 70 Hz sind ein Extrembeispiel. Wie gesagt soll sich das später in Frequenzen abspielen, die man gerade noch wahrnehmen kann (also etwa 20-40 Hz und weniger).

Du hast eine Schwebung. mathematisch kann ich das nicht korrekt ausdrücken. Da du bei 70Hz durch die Int-Umwandlung auf 14ms Periodendauer kommst und die 7-fache Periodendauer der 490Hz-PWM-Frequenz 14,28ms ist, kommt es zu dieser Schwebung.
Am sinnvollsten würdest du die PWM-Frequenz hochsetzen (Arduino Playground - HomePage).

Hi,
mathematisch ausgedrückt könnte ich damit auch nicht sofort viel Anfangen, das Prinzip ist mir aber klar.

Blöd ist tatsächlich, dass man den Effekt ab ca. 15-20 Hz wahrnimmt (in unterschiedlichen Frequenzabständen ~ 1/vielfaches der PWM Frequenz?), es aber für den Aufbau dann erst Interessant wird.

Wird eine Änderung des Timers das Problem wirklich beheben? Schwebung tritt dann doch sicher immer noch auf, nur bei anderen Frequenzen? Es sollte halt zwischen 15 und sagen wir 50 Hz möglichst sauber werden.

Wenn es tatsächlich zum Ziel führen könnte, werde ich das mal machen.. muss dann halt alle timer, sounds, etc.. anpassen.

Hi,
so, ich habe nun die LEDs Rot und Blau auf Pin 9 und 10 verlegt und deren Timer (Timer1) auf die Grundfrequenz gesetzt.

TCCR1B = TCCR1B & 0b11111000 | 0x01;   // Divisor 1    31372.55 Hz PWM-Frequenz

Timer 0 ist so nicht betroffen und millis, delay, etc. funktionieren wie gehabt.

Das Wechselblinken ist zwar minimal besser geworden, jedoch unabhängig vom gewählten Divisor immer noch nicht sauber. Die Schwebung tritt dann einfach bei anderen Blinkfrequenzen auf, leider auch bei den 31kHz immer noch sehr stark.

Gibt's noch andere Vorschläge?

Wenn es Software-Seitig nicht lösbar ist, bleibt nur noch der unschöne weg, die Helligkeit per Poti (= Vorwiderstand der LED) zu dimmen, und diesen Wert auch zu messen und auszugeben.
So können die LEDs zumindest ohne PWM (bzw. PWM=255 oder digitalWrite) angesteuert werden..

Wäre jedoch schöner, das Problem anders in den Griff zu bekommen.

Grüße, Stephan

Zeig doch bitte den ganzen jetzt verwendeten Sketch.

Theseus:
Zeig doch bitte den ganzen jetzt verwendeten Sketch.

Bitteschön:

int Pinblau = 10;
int Pinrot = 9;


 const int startpin = A4;
 const int enterpin = A5;


 int pwmblau;
 const int pwmrot = 255;

  unsigned long currentTime;
  unsigned long loopTime; 
  
  // Drehencoder1
  int H_fadeAmount = 5;
  const int H_pin_A = A2;  
  const int H_pin_B = A3;  
  unsigned char H_encoder_A;
  unsigned char H_encoder_B;
  unsigned char H_encoder_A_prev=0;
  
  // Drehencoder2 
  //int Frequenz = 120;   
  int F_fadeAmount = 1;
  const int F_pin_A = A0;  
  const int F_pin_B = A1;  
  unsigned char F_encoder_A;
  unsigned char F_encoder_B;
  unsigned char F_encoder_A_prev=0;
  
  long previousMillis = 0;
  int ledStateblau;
  int ledStaterot;
  
  void setup()
{ 
  pinMode(Pinblau, OUTPUT);
  pinMode(Pinrot, OUTPUT);
  
  pinMode(enterpin, INPUT);   
  digitalWrite(enterpin, HIGH); 
  pinMode(startpin, INPUT);     
  digitalWrite(startpin, HIGH); 

  pinMode(H_pin_A, INPUT);
  pinMode(H_pin_B, INPUT);
  digitalWrite(H_pin_A, HIGH);
  digitalWrite(H_pin_B, HIGH);
  pinMode(F_pin_A, INPUT);
  pinMode(F_pin_B, INPUT);
  digitalWrite(F_pin_A, HIGH);
  digitalWrite(F_pin_B, HIGH);  
  
  // Ändere PWM Frequenz für Pin 9 & 10:  
  TCCR1B = TCCR1B & 0b11111000 | 0x01;   // Divisor 1    31372.55 Hz PWM-Frequenz
//  TCCR1B = TCCR1B & 0b11111000 | 0x02; // Divisor 8    3921.16
//  TCCR1B = TCCR1B & 0b11111000 | 0x03; // Divisor 64   490.20  <-- default
//  TCCR1B = TCCR1B & 0b11111000 | 0x04; // Divisor 256  122.55
//  TCCR1B = TCCR1B & 0b11111000 | 0x05; // Divisor 1024 30.64
  
  
}

void loop()
{

  int demoperiodenlaenge = 50;
      pwmblau = 125;
  
  
  int startStatus = digitalRead(startpin);
    while (startStatus == HIGH)
    {
    // Drehencoder
       currentTime = millis();
        if(currentTime >= (loopTime + 5)) // 5ms Abtastzeit von Encoder = 200Hz Abtastfrequenz
        {   
          // Encoder Helligkeit
          
          H_encoder_A = digitalRead(H_pin_A);
          H_encoder_B = digitalRead(H_pin_B);   
          if((!H_encoder_A) && (H_encoder_A_prev)) 
          {
            // A geht von HIGH zu LOW 
            if(H_encoder_B) 
            {
              if(pwmblau + H_fadeAmount <= 255) pwmblau += H_fadeAmount;
            }   
            else 
            {
              if(pwmblau - H_fadeAmount >= 5) pwmblau -= H_fadeAmount;
            }   
          }   
          H_encoder_A_prev = H_encoder_A;
          F_encoder_A = digitalRead(F_pin_A);
          F_encoder_B = digitalRead(F_pin_B);   
          if((!F_encoder_A) && (F_encoder_A_prev))
          {
            if(F_encoder_B) 
            {
              if(demoperiodenlaenge + F_fadeAmount <= 100) demoperiodenlaenge += F_fadeAmount;
            }   
            else 
            {
              if(demoperiodenlaenge - F_fadeAmount >= 20) demoperiodenlaenge -= F_fadeAmount;
            }    
           }   
           F_encoder_A_prev = F_encoder_A;

          loopTime = currentTime;
          
        }

        Blinken(demoperiodenlaenge, pwmblau);
               
        previousMillis = millis();
          
        }
 
}


void Blinken(int periodendauer, int helligkeit)
{
 int periode=millis()%(periodendauer);
 if (periode<periodendauer/2) 
 {
   analogWrite(Pinrot, 0);
 analogWrite(Pinblau, helligkeit);
 }
 else 
 {
   analogWrite(Pinrot, pwmrot);
 analogWrite(Pinblau, 0);
 }
}

Erläuterung:
Zwei Drehencoder steuern die Blinkfrequenz des Wechselblinkers und die Helligkeit einer Farbe.
In der Funktion "Blinken" findet das eigentliche Blinken statt.
Das ganze steht in einer while-Schleife, die per Drucktaster verlassen wird, danach werden andere Teile des Programms abgearbeitet, die erstmal nichts mit dem Problem zu tun haben.
Die Frequenz lässt sich momentan momentan von 10 - 50 Hz einstellen, PWM der blauen LED von 5 bis 255.

Viele Grüße,
Stephan

Hallo,

das liegt dann aber nicht mehr am PWM. Ich habe mal dein obiges Beispiel modifiziert:

 int Pinblau = 9;
 int Pinrot= 10;
 
 int frequenz = 70;
 
void setup() 
{
  
  pinMode(Pinblau, OUTPUT);
  pinMode(Pinrot, OUTPUT); 
  TCCR1B = TCCR1B & 0b11111000 | 0x01;   // Divisor 1    31372.55 Hz PWM-Frequenz
}

void loop() 
{
  
double blinkzeitabstand = 500000/frequenz;
analogWrite(Pinblau, 5);
delayMicroseconds(blinkzeitabstand);
analogWrite(Pinblau, 0);
analogWrite(Pinrot, 5);
delayMicroseconds(blinkzeitabstand);
analogWrite(Pinrot, 0);
}

Sobald die PWM-Frequenz erhöht ist, tritt die Schwebung nicht mehr auf. Zum Vergleich kannst du ja die Zeile mal herauskommentieren. Für ein genaueres Timing habe ich auf Microseconds statt Millis umgestellt.

Ich schätze mal es gibt ein Problem mit dem Timing wo anders in deiner letzten Programmversion, so dass die Blinkfrequenz nicht ganz genau immer stimmt. Leider kann ich das mangels Schaltung nicht testen.

Hi,
wie verhält es sich, wenn du bspw. mal irgendeine beliebige Frequenz einstellst? Also mal 30, 31, 32, 33 Hz, oder irgendwas ganz krummes? Kann natürlich sein, dass 70 Hz jetzt zufällig passen.
Teste es morgen selbst mal, wenn ich wieder am Aufbau dran bin.

Könnte also auch was bringen, wenn ich micros() statt millis() nutze?
Hätte sowieso den Vorteil, dass ich die Frequenz feinfühliger verstellen kann. Bei 1ms Unterschied bei höheren Frequenzen (z.B. 20ms zu 21ms) macht er schon Sprünge von fast 3 Hz, was eh schon fast zu grob ist.

Zwischendrin werden noch in der Schleife Werte an ein LCD Display gesendet.. Aber da das Problem schon beim einfachen Wechselblinker mit delay schon auftrat, nahm ich an, der restliche Code beeinflusst nichts (da es genauso "schlecht" funktioniert hat.

Danke für die Hilfe und Grüße, Stephan

Hi,
so, mein Problem ist tatsächlich endlich gelöst!!

Es gab wohl mehrere Ursachen:

  1. Zu geringe PWM Frequenz hat Schwebung verursacht.

  2. LCD-Display im loop. Wenn das mit jedem Durchlauf neu beschrieben wird, kommt das Blinken durcheinander. Abhilfe habe ich geschaffen, indem das LCD jetzt nur alle 2 sek (per millis()) aktualisiert wird.

  3. Gleiches Problem mit serieller Übertragung. Wird nun auch alle 2 sek mit dem Display aktualisiert.
    Das erzeugt zwar alle 2 Sekunden ein kurzes Flackern, aber damit kann ich leben.

  4. Der Ansatz mit Mikrosekunden (micros()) ist gut, jedoch funktioniert delayMicroseconds erst ab einer Frequenz von etwa 62.5 Hz, da delayMicroseconds keinen größeren Wert als 16383 annehmen kann. Steht hier:

Currently, the largest value that will produce an accurate delay is 16383.

https://www.arduino.cc/en/Reference/DelayMicroseconds

Abhilfe hat geschaffen, das Blinken per micros() durchführen zu lassen. Hierbei aufpassen, dass man die Datentypen nicht als "int" festlegt, da das sonst schnell überläuft..

Ich bin jetzt erstmal zufrieden so und werde Anfang kommender Woche mal ein Oszi dran hängen, um den tatsächlichen Output zu verifizieren.

Danke für die Tipps!
Viele Grüße, Stephan