Donnerstag, 15. März 2018

Arduino - variable PWM-Frequenz

Für meine Minieisenbahn (Diorama) habe ich mal einen Fahrregler mit einem Arduino gebaut, der mit einer variablen Gleichspannung und einem PWM-Signal mit variabler Impulsweite funktioniert.




Der Fahrregler funktioniert sehr gut.

Nun gab es den Vorschlag die Frequenz des PWM-Signal bei höheren Geschwindigkeiten zu erhöhen.

Um eine variable Frequenz des PWM-Signal zu testen, habe ich einen "frischen" Arduino Nano genommen und habe in Ermangelung eines Oszilloskops eine LED zur Visualsierung der variablen Frequenz genommen. Weil das menschliche Auge höhere Frequenzen nicht mehr als flackerndes Licht wahrnimmt, habe ich den Test für Frequenzen von ca. 30 bis 50 Hz durchgeführt. Der Fahrregler würde später von 30 bis 120 Hz betrieben werden.



Mann kann erkennen, dass das Licht zunehmend schneller flackert und dann dauernd leuchtet.

Die Grundlagen für den Code habe ich im www gefunden und auf meine Bedürfnisse angepasst.
Zu beachten ist, dass die verschiedenen PWM-Ports teilweise an unterschiedlichen Timern hängen und dass die Frequenzen unterschiedlich sind.
Der Code sollte ohne Änderungen auch auf einem UNO laufen.

Um einen größeren Frequenzbereich abzudecken muss man noch den Prescaler mit berücksichtigen und passend zur Frequenz setzen.




//----------------------------------------------------------------------
void setup()
{
  //untere (255 = ~30Hz) und untere (150 = ~50Hz) Frequenz
  f1 = 255;
  f2 = 150;
  
  //aufsteigend / vorwärts
  fwd = false;

  // ein paar Variablen für die Test-Zeitschleife
  time1 = millis();
  time2 = time1;
  

  //Initialize Timer2
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

  // Verwendung PIN 3 als Output
  // PIN3 und PIN11 gehen runter bis 30 Hz
  pinMode(3, OUTPUT);

  //setzen Register Timer2 (PIN3 wird von Timer2 bedient)
  bitSet(TCCR2A, COM2B1);
  
  // Setzen "Phase correct PWM"
  bitSet(TCCR2A, WGM20);
  bitSet(TCCR2B, WGM22);

  // Prescale auf max. Wert 1024 setzen um niedrige Frequenzen zu bekommen
  bitSet(TCCR2B, CS20);
  bitSet(TCCR2B, CS21);
  bitSet(TCCR2B, CS22);

  // Timer zählt hoch und runter (bei TOP 255 sind 510 Zyklen)
  // Zähler x Prescale x 0.0000625ms
  // (0.0000625 ms ist ein Zyklus bei 16MHz)
  // 510 x 1024 x 0.0000625 = 32.64ms      30.6 Hz   --> MIN
  //  2 x 1024 x 0.0000625 = 0.128 ms   7812.5 Hz   --> MAX
  // bei einem Prescale von 1024 sind PWM-Frequenzen 
  // von ca. 30 bis 7812 Hz möglich
  // und das ist so weit weg von stufenlos, 
  // aber ab ca. 122Hz kann man auf den 256er Prescale wechseln
  // somit ist der optimale Bereich bei einem Prescale 
  // von 1024 von ca. 30 bis 122Hz
  // dies entspricht TOP 255 - 64
  OCR2A = 255;

  //6% "duty cycle"  (zulässige Werte 1-155)
  OCR2B = 10;   

}//----------------------------------------------------------------------
void loop() {
    // Zeit seit Start Arduino abfragen
    time2 = millis();

    //Wenn mehr als 1 Sek. seit letzter Setzung der Frequenz vergangen
    if ((time2 - time1)>1000)
    {
      // neue Zeit merken
      time1 = time2;
      
      //wenn vorwärts / aufsteigend
      if (fwd==false){
        
        //Frequenzwert verkleinern (= Frequenz erhöhen)
        frq = frq - 10;

        //wenn aktuelle Frequenz kleiner gleich Minimalwert
        if (frq <= f2){
          //Richtung umkehren
          fwd = true;

          //Frequenz auf Minimum setzen
          frq = f2;
        }
      }else{
        //Frequenzwert erhöhen (= Frequenz verringern)
        frq = frq + 10;

        //wenn Frequenzwert größer gleich Maximum
        if (frq >= f1){
          //Richtung umkehren
          fwd = false;

          //Frequenz auf Maximum setzen
          frq = f1;
        }
      }
      
      //Frequenzwert dem Timer zuweisen
      OCR2A = frq;
    }
  }
//-----------------------------------------------------------------------