Timer PWM

Timer PWM

Standardmäßig bietet der Arduino Uno mit dem Befehl "analogWrite(...)" die Möglichkeit, PWM-Signale mit einer Auflösung von 8 Bit und einer PWM-Frequenz von 490 Hz (Pin D3, D9, D10 und D11) bzw. 980 Hz (Pin D5 und D6) auszugeben. Wer für seine Anwendung eine höhere Auflösung und/oder eine höhere oder tiefere PWM-Frequenz haben möchte, muss zwangsläufig selbst diverse Bits in den entsprechenden Timer-/Counter-und Port-Registern setzen.

Die PWM-Pins des Arduino Uno werden von den Timern 0, 1 und 2 kontrolliert:

  • Pins D5 und D6: Timer0 (8 Bit)
  • Pins D9 und D10: Timer1 (16 Bit)
  • Pins D3 und D11: Timer2 (8 Bit)

Das folgende Beispiel zeigt die Verwendung des 16 Bit Timer1, wie ein PWM-Signal mit anderer Frequenz und Auflösung als standardmäßig vorgegeben, programmiert wird. Mit Timer1 können nur die Pins D9 und D10 als PWM-Pins verwendet werden!

Verwendete Register:

  • Timer Counter Control Register A: TCCR1A
  • Output Compare Register A: OCR1A (für PWM-Pin D9)
  • Output Compare Register B: OCR1B (für PWM-Pin D10)
  • Port B Data Direction Register: DDRB

Vorgehensweise:

  • 1. Modus wählen
  • 2. PWM-Frequenz festlegen
  • 3. Ausgangs-Aktion festlegen
  • 4. Pin als Ausgang definieren
  • 5. Setzen des Wertes der Pulsweite


1. Modus wählen:

Timer 1 erlaubt drei Modi: 8 Bit-, 9 Bit- und 10 Bit-PWM. Die Auswahl erfolgt über die Bits "Waveform Generation Mode" WGM10, WGM11 und WGM12 der "Timer/Counter Controll Register" A und B (TCCR1A und TCCR1B).
Die Bits WGM10 und WGM11 befinden sich im Timer/Counter Control Register A (TCCR1A), das Bit WGM12 im Timer/Counter Control Register B (TCCR1B).

TCCR1A - Timer/Counter1 Control Register A:
7 6 5 4 3 2 1 0
COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10


TCCR1B - Timer/Counter1 Control Register B:
7 6 5 4 3 2 1 0
ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10

Codebeispiel: Loeschen der Timer/Counter Control Register A und B
TCCR1A = 0;
TCCR1B = 0;


In der nachfolgenden Tabelle sind die Einstellungen der Bits für den gewünschten Modus ersichtlich:

Einstellungen der Bits
Modus WGM12 WGM11 WGM10
Fast PWM 8Bit 1 0 1
Fast PWM 9Bit 1 1 0
Fast PWM 10Bit 1 1 1

Codebeispiel: Den Modus Fast PWM-Mode 9 Bit einstellen

TCCR1A |= (0 << WGM10) | (1 << WGM11);
TCCR1B |= (1 << WGM12);

Modus Fast PWM-Mode 10 Bit einstellen
TCCR1A |= (1 << WGM10) | (1 << WGM11);
TCCR1B |= (1 << WGM12);


2. PWM-Frequenz festlegen

Die PWM-Frequenz ist abhängig von der Taktfrequenz des Prozessors, der Auflösung und der Einstellung des Vorteilers (Prescaler).

Vorgaben:

  • PWM-Auflösung 8, 9 oder 10 Bit
  • CPU-Frequenz Arduino Uno: 16.000.000 Hz
  • Mögliche Vorteiler: 1, 8, 64, 256 oder 1024


In Abhängigkeit der Auflösung wird die PWM-Frequenz [Hz] nach folgender Formel ermittelt:
8-Bit PWM: Frequenz = CPU Frequenz/Vorteilerx256
9-Bit PWM: Frequenz = CPU Frequenz/Vorteilerx512
10-Bit PWM: Frequenz = CPU Frequenz/Vorteilerx1024


Berechnungsbeispiel:
Z.B. Auflösung = 10 Bit, Vorteiler = 256:
PWM Frequenz = 16.000.000 / (256 * 1024) = 61 Hz
oder z.B. Auflösung = 10 Bit, Vorteiler = 8:
PWM Frequenz = 16.000.000 / (8 * 1024) = 1.953 Hz (Wird im untenstehenden Programmbeispiel verwendet)
oder z.B. Auflösung = 9 Bit, Vorteiler = 1:
PWM Frequenz = 16.000.000 / (1 * 512) = 31.250 Hz
Nachfolgende Tabelle zeigt alle möglichen PWM-Frequenzen in Abhängigkeit der Auflösung und des Vorteilers bei einer Taktfrequenz des Uno von 16 MHz. Wie man sieht, liegt die höchste PWM-Frequenzen bei 62,5 kHz, die tiefste Frequenz bei 15 Hz:

Mögliche PWM Frequenzen [Hz]
Vorteiler Auflösung
8 Bit
(256)
9 Bit
(512)
10 Bit
(1024)
162.500,031.250,015.625,0
87.812,53.906,31.953,1
64976,6488,3244,1
256244,1122,161,0
102461,030,515,3

Berechnung möglicher PWM Frequenzen [Hz] in Excel



Der gewünschte Vorteiler muss im Timer/Counter Control Register B (TCCR1B) mit den Bits "Clock Select" CS10, 11 und 12 gesetzt werden:
7 6 5 4 3 2 1 0
ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10


Die Bitkombination für den gewünschten Vorteiler ist in nachfolgender Tabelle ersichtlich:
Vorteiler CS12 CS11 CS10
Keine Taktquelle
(Timer/Counter Stopp)
0 0 0
Takt / 1 (keine Teilung) 0 0 1
Takt / 8 0 1 0
Takt / 64 0 1 1
Takt / 256 1 0 0
Takt / 1024 1 0 1

Codebeispiel: Vorteiler auf Takt / 8 setzen

TCCR1B |= (1 << CS11);

3. Ausgangsaktion festlegen
Hier legt man mit den "Compare Output Mode" - Bits des Timer/Counter1 Control Register A (TCCR1A) fest, ob das PWM-Signal nicht invertiert oder invertiert ausgegeben wird, oder ob das Signal abgeschaltet wird:
  • Für PWM-Pin D9 : Bit COM1A0 und COM1A1
  • Für PWM-Pin D10: Bit COM1B0 und COM1B1

7 6 5 4 3 2 1 0
COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10


Einstellungen der Bits für ein invertiertes/nicht invertiertes PWM Signal
PWM Ausgang COM1A1/
COM1B1
COM1A0/
COM1B0
PWM ausgeschaltet 0 0
PWM Ausgang nicht inventiert 1 0
PWM Ausgang inventiert 1 1

Codebeispiel: Nichtinvertiertes PWM-Signal setzen

TCCR1A |= (1 << COM1A1);


4. PWM-Pin als Ausgang definieren:
Die PWM-Pins D9 und D10 sind Pins des Port B und können durch direktes Setzen der entsprechenden Bits DDB1 oder DDB2 im Data Direction Register Port B (DDRB) als Binärausgang definiert werden.

PWM-Pin als Ausgang definieren:
Digitalpins Port B
7 6 5 4 3 2 1 0
DDB7 DDB6 DDB5 DDB4 DDB3 DDB2 DDB1 DDB0
- - D13 D12 D11 D10 D9 D8

Codebeispiel: PWM-Pin D9 als Ausgang definieren

DDRB |= (1 << DDB1);


5. Pulsweite setzen:
Die gewünschte Pulsweite kann nun, je nach verwendetem PWM-Pin, in das 16-Bit Output Compare Register A (OCR1A) für PWM-Pin D9 oder Output Compare Register B (OCR1B) für PWM-Pin D10 geschrieben werden. In Abhängigkeit der eingestellten Auflösung von 8, 9 oder 10 Bit kann der Wertebereich der Pulsweite 0 bis 255, 0 bis 511 oder 0 bis 1023 betragen. Bei einer Auflösung von z.B. 9 Bit und einer Pulsweite von 255 beträgt das Impuls-Pausenverhältnis somit 50:50.

Codebeispiel: Setzen des Impuls-Pausenverhältnis
//Potiwert einlesen mit einer Auflösung Analogeingang A2 = 10 Bit
potiWert = analogRead(potiPin);
OCR1A = potiWert;


Programmbeispiel:
Im folgenden Beispielprogramm wird die Helligkeit einer LED durch ein Potentiometer mit Pulsweitenmodulation gesteuert (PWM-Frequenz = 1.953 Hz, Auflösung 10 Bit = 1024 Stufen).

Verwendete Bauteile:
  • 1 Arduino Uno
  • 1 LED
  • 1 Widerstand 220 Ohm
  • 1 Poti 10 kOhm






C++ PWM Programm 1.953Hz

RAW code



C++ PWM Programm 31.250Hz Lüftersteuerung

RAW code



PWM Lüftersteuerung für Be- und Entlüftung




Das folgende ESP32 Codebeispiel startet einen seriellen Kommunikationsport, überprüft die CPU-Taktfrquenz, die XTAL Frequenz und den APB-Bustakt und gibt sie über die serielle Schnittstelle aus. Der GPIO-Pin 5 wird mit maximaler Geschwindigkeit getaktet.
Die XTAL Frequenz ist die periphere Takt-Frequenz und der APB-Busclock (APB_CLK) wird vom CPU_CLK abgeleitet.

C++ ESP32 CPU-Frequenz ermitteln



Die ESP32 CPU Frequenz, die periphere Takt-Frequenz XTAL und der APB-Busclock am Seriellen Monitor




Mit dem folgenden Codebeispiel wird der PWM-GPIO-Pin 5 definiert, dann erfolgt die Konfiguration der Frequenz und der Auflösung des PWM-Kanals. In der Hauptschleife erhöht sich allmählich das Tastverhältnis des PWM auf den maximalen Wert und reduziert sich allmählich wieder auf den Minimalwert.

C++ ESP32 PWM Beispiel 1



Das gleiche Programm wie das vorherige mit Ausnahme der Auflösung. Diese ist jetzt auf 4 Bit eingestellt und das Tastverhältnis im Bereich 0 - 15.

C++ ESP32 PWM Beispiel 2



Das nächste Programm ist ein ESP32 PWM Generator mit einem 1,3" OLED Display, der über den Seriellen Monitor konfigurierbar ist.

C++ ESP32 PWM Gemerator

RAW code



Der PWM Generator ist über den Seriellen Monitor kofigurierbar.
f = Frequenz; d = Ti/Tp 0-1023; r = Aufloesung 8-16 Bit; p = GPIO.






C++ ESP32 PWM mit Poti, LED und 1,3" OLED-Display

RAW code



ESP32 PWM mit Poti, LED und OLED-Display 1,3"




C++ ESP32 PWM 4 Kanal Multidimmer mit Potis, LEDs und OLED-Display 1,3

RAW code



PWM-Signal 1kHz 8Bit Auflösung


PWM-Signal 20kHz 12Bit Auflösung