1.7 🚧 PWM (PulsWeitenModulation) Ausgabe

Synopsis: [de.wikipedia.org/wiki/Pulsdauermodulation 🔗] [www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/ 🔗]
Wieder so ein „explosives“ 🧨 Thema! Sobald man sich näher damit beschäftigt kann es spannend aber auch kompliziert werden…

  • Verwendung
    • Leistung steuern
    • Töne ausgeben
    • Servos ansteuern
  • Per Timer-Hardware an bestimmten Pins möglich
    • Arduino: analogWrite(…)…
      • analogWrite(…)
      • analogWriteResolution(…)
      • analogWriteFrequency(…)
      • einfach aber sehr eingeschränkt und ggfs. mit Nebenwirkungen.
    • STM32-API Befehle
      • setPWM(…)
      • setMode(…)
      • setOverflow(…)
      • setCaptureCompare(…)
    • µC-Register wissend selber einstellen
      • AVR
      • STM32
  • Per Software an allen Pins möglich

Arduino: analogWrite()

[docs.arduino.cc/learn/microcontrollers/analog-output/ 🔗]

Die Helligkeit der LED an PC7 soll durch den low-aktiven prellfreien UserButton an PC13 in den Stufen
0% -> 25% -> 50% -> 75% -> 100% -> 0% usw.
mit einer ISR isr_UserB() verändert werden.
Mit analogWrite(LED,analogWert) wird der Wert von analogWert zwischen 0..255 die Helligkeit von 0%..100% als PWM-Signal auf LED ausgegeben.
Vorgabe-Code:

#define USER_B PC13 // User-Button prellfrei low aktiv
#define LED PC7     // AusgabePin
volatile unsigned int helligkeit=0; // Globale Variable für Helligkeitswert in Prozent

void isr_UserB(){ // ISR für Helligkeit verändern
  int analogWert; // Ausgabewert PWM
  ...
  analogWert = helligkeit * 2; // erster Ansatz zur Umrechnung
  analogWrite(LED,analogWert); // Arduino-Funktion für PWM Ausgabe an bestimmten Port-Pins
  Serial.printf("Helligkeit %d%% Analogwert %d \n",helligkeit,analogWert);
}
void setup(){
  Serial.begin (9600); // Serielle kommunikation starten
  ...
}
void loop(){}

Vervollständigen Sie das Programm.

Lösungsvorschlag
#define USER_B PC13 // User-Button prellfrei low aktiv
#define LED PC7     // AusgabePin
volatile unsigned int helligkeit=0; // Globale Variable für Helligkeitswert in Prozent

void isr_UserB(){  // ISR für Helligkeit verändern
  int analogWert;  // Ausgabewert PWM
  if(helligkeit>=100){
    helligkeit=0;
  }
  else{
    helligkeit+=25;
  }
  analogWert = helligkeit * 2; // erster Ansatz zur Umrechnung
  analogWrite(LED,analogWert); // Arduino-Funktion für PWM Ausgabe an bestimmten Port-Pins
  Serial.printf("Helligkeit %d%% Analogwert %d \n",helligkeit,analogWert);
}
void setup(){
  Serial.begin (9600); // Serielle kommunikation starten
  pinMode(USER_B, INPUT);
  attachInterrupt (digitalPinToInterrupt (USER_B), isr_UserB, FALLING); // fallende Flanke
  pinMode(LED,OUTPUT);
}
void loop(){}

Umrechnung Prozentwert in Analogwert

Wie Sie sicher erkennen wird bei der Vorgabe der 100%-Wert nicht in analogWert=255 umgerechnet sondern in analogWert=200.
Problem: Erstelle eine Gleichung analogWert = … helligkeit … für die richtige Umrechnung.
Die typische Lösung: analogWert = helligkeit / 100 * 255 ist mathematisch korrekt, funktioniert aber nicht, weil mit ganzen Zahlen gerechnet wird und da gilt: 50 / 100 ist 0!
Dieser Ausdruck funktioniert wie gewünscht: analogWert = helligkeit*255/100; // es lebe die Numerik!

Gegen Kopfschmerzen map(…) Funktion?

Oft müssen Wertebereiche in andere Bereiche umgerechnet werden, es gibt bei Arduino eine Funktion dafür [www.arduino.cc/reference/en/language/functions/math/map/ 🔗] damit wäre die Lösung einfach:

analogWert = map(helligkeit,0,100,0,255); // Arduino-Umrechnungsfunktion

Falls diese Funktion in der Formelsammlung auftaucht, dürfen Sie sie auch problemlos verwenden…

Messen des Ausgabe-Signals mit Oszilloskop

Welches Signal geben wir aus, um die Helligkeit, bzw. die Leistung der LED zu steuern? Messen wir es mit einem Oszilloskop!

Oszillogramm für 25%
Oszillogramm für 25%
Oszillogramm für 50%
Oszillogramm für 50%

Die Frequenz ist 1 kHz, die Genauigkeit ist bedingt durch nur 256 Stufen von 0%..100% nicht besonders hoch.

PWM noch besser?

Es gibt zwei Einstellmöglichkeiten für die PWM-Ausgabe mit analogWrite(..):

analogWriteResolution(n); // n=8..16

Gibt die Bit-Auflösung des AnalogWertes an. Bei 8 Bit ist der Maximalwert 255, bei 16 Bit 65535 für 100%.
Bei 16 Bit Auflösung ist die Genauigkeit höher.

Testcode
#define USER_B PC13 // User-Button prellfrei low aktiv
#define LED PC7     // AusgabePin
volatile unsigned int helligkeit=0; // Globale Variable für Helligkeitswert in Prozent

void isr_UserB(){  // ISR für Helligkeit verändern
  int analogWert;  // Ausgabewert PWM
  if(helligkeit>=100){
    helligkeit=0;
  }
  else{
    helligkeit+=25;
  }
  //analogWert = helligkeit*255/100;
  analogWert = helligkeit*65535/100;
  //analogWert = map(helligkeit,0,100,0,255);
  analogWrite(LED,analogWert); // Arduino-Funktion für PWM Ausgabe an bestimmten Port-Pins
  Serial.printf("Helligkeit %d%% Analogwert %d \n",helligkeit,analogWert);
}
void setup(){
  Serial.begin (9600); // Serielle kommunikation starten
  pinMode(USER_B, INPUT);
  attachInterrupt (digitalPinToInterrupt (USER_B), isr_UserB, FALLING); // fallende Flanke
  pinMode(LED,OUTPUT);
  analogWriteResolution(16);
  //analogWriteFrequency(10000);
}
void loop(){}

analogWriteFrequency(10000);

Die Ausgabefrequenz kann damit angepasst werden. Wie ist der Bereich?
Nachteilig ist, dass die Anpassungen immer für alle analogWrite(..) gelten.

Oszillogramm für 25% mit 16Bit
Oszillogramm für 25% mit 16Bit

Welche Ausgänge können PWM?

Nicht mit allen Ausgängen kann per Hardware PWM betrieben werden. Hier ein Überblick welche Ausgänge geeignet sind: mezmedia.de/etc/hardware/stm32-nucleo-l152re/