Forum: Mikrocontroller und Digitale Elektronik AVR PWM Verständnis


von Johannes (joeto)


Lesenswert?

Liebe Community,

ich versuche gerade PWM bei den AVRs zu verstehen.
Bis jetzt habe ich immer nur in Arduino die ``analogWrite()`` funktion 
benutzt.

Was ich bisher verstanden habe ist das Grundprinzip von PWM und dass 
dies über timer realisiert wird die immer hochzählen bis zu einem 
bestimmten Wert und dann den Ausgang setzten. \
Meine Fragen:

1. Die meisten AVRs haben aber doch nur wenige Timer/"Timer-Pins".
Wie macht es dann der Arduino, dass er zig PWM Ausgänge hat und diese so 
einfach nur mit einer Funktion ansteuerbar sind?

2. Wie macht ihr das in der Praxis wenn ihr PWM mit einem AVR nutzt?
Jedes Mal aufwendig über diese ganzen Register oder gibts da ne 
einfache, schnelle und übersichtliche Variante?

3. Was genau macht das``analogWrite()``? Setzt das die Register und 
alles im Hintergrund?

Viele Grüße

Johannes

: Bearbeitet durch User
von Rainer W. (rawi)


Lesenswert?

Johannes schrieb:
> Wie macht es dann der Arduino, dass er zig PWM Ausgänge hat und diese so
> einfach nur mit einer Funktion ansteuerbar sind?

Es sind lange nicht alle Pins für PMW-Ausgabe nutzbar. Hier eine 
Übersicht:
https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/

von Rahul D. (rahul)


Lesenswert?

Johannes schrieb:
> dass dies über timer realisiert wird die immer hochzählen bis zu einem
> bestimmten Wert und dann den Ausgang setzten. \

Das ist vom eingestellten Modus abhängig. Es gibt auch den 
Phasecorrect-Mode bei dem der Timer hoch- und wieder runterzählt.

Johannes schrieb:
> 1. Die meisten AVRs haben aber doch nur wenige Timer/"Timer-Pins".
> Wie macht es dann der Arduino, dass er zig PWM Ausgänge hat und diese so
> einfach nur mit einer Funktion ansteuerbar sind?

Wie viele brauchst du? Man kann auch per Software mehr Pins verwenden; 
allerdings schlägt sich das auf die allgemeine Performance nieder, da 
Software-Lösungen - imm Gegensatz zu Hardwarelösungen - immer den 
Prozessor verwenden

> 2. Wie macht ihr das in der Praxis wenn ihr PWM mit einem AVR nutzt?
> Jedes Mal aufwendig über diese ganzen Register oder gibts da ne
> einfache, schnelle und übersichtliche Variante?

Es gibt auch (Online-)Tools zur Berechnung der Einstellungen.
Wenn man sich allerdings mit den Beschreibungen im Datenblat 
auseinandersetzt, würde sich deine erste Frage erledigen.

> 3. Was genau macht das``analogWrite()``? Setzt das die Register und
> alles im Hintergrund?
Sowas kann man dadurch nachvolziehen, indem man sich den 
Arduino-Quellcode der Timer anguckt.

von Rainer W. (rawi)


Lesenswert?

Rahul D. schrieb:
> Sowas kann man dadurch nachvolziehen, indem man sich den
> Arduino-Quellcode der Timer anguckt.

Ich würde dazu eher in den Quellcode der Funktion analogWrite() in 
wiring_analog.c gucken oder welche Stelle im Arduino-Quellcode meinst du 
speziell genau. In wiring_analog.c steht das Setzen der Register für die 
PWM-Ausgabe. Die Timer müssen natürlich laufen, aber das passiert 
unabhängig von der PWM-Ausgabe.

Beim Arduino Due werden für analogWrite() allerdings bei den Kanälen 0 
und 1 direkt die DACs benutzt, so dass PWM und Timer dabei außen vor 
sind, ist aber natürlich nicht AVR.

: Bearbeitet durch User
Beitrag #7605641 wurde vom Autor gelöscht.
von Veit D. (devil-elec)


Lesenswert?

Hallo,

bezieht sich deine Frage auf konkrete Controller? Wenn ja welche?

von Johannes (joeto)


Lesenswert?

Veit D. schrieb:
> bezieht sich deine Frage auf konkrete Controller? Wenn ja welche?
Ne ganz allgemein :)

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

Rainer W. schrieb:
> Ich würde dazu eher in den Quellcode der Funktion analogWrite() in
> wiring_analog.c gucken oder welche Stelle im Arduino-Quellcode meinst du
> speziell genau. In wiring_analog.c steht das Setzen der Register für die
> PWM-Ausgabe. Die Timer müssen natürlich laufen, aber das passiert
> unabhängig von der PWM-Ausgabe.
>
> Beim Arduino Due werden für analogWrite() allerdings bei den Kanälen 0
> und 1 direkt die DACs benutzt, so dass PWM und Timer dabei außen vor
> sind, ist aber natürlich nicht AVR.

Johannes schrieb:
> Veit D. schrieb:
>> bezieht sich deine Frage auf konkrete Controller? Wenn ja welche?
> Ne ganz allgemein :)

Genau so meinte ich das auch: ganz allgemein.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johannes schrieb:
> Ne ganz allgemein :)
Rahul D. schrieb:
> ganz allgemein.

Dann gibts auch keine konkrete Antwort.
Wenn du konkretes Wissen möchtest, dann schaue dir konkrete µC an und 
auch ihren konkreten Arduino Core.

: Bearbeitet durch User
von Michael B. (laberkopp)


Lesenswert?

Johannes schrieb:
> dass dies über timer realisiert wird die immer hochzählen

Nein. Es gibt auch TimerModes die rückwärts zählen. Aber unwichtig wenn 
man noch am Anfang steht.

Bestimmte Timer können nur bestimmte Pins erreichen. Will man auch 
woanders PWM ausgeben, macht man das in Software, per timer-gesteuertem 
Interrupt. Ganz so hohe Frequenzen wie bei hardware-Timern schafft man 
dann nicht.

Johannes schrieb:
> Jedes Mal aufwendig über diese ganzen Register

Klar. Kinderkram bei AVR gegenüber echten Prozessoren wie ARM.

Johannes schrieb:
> Was genau macht dasanalogWrite

Schau es dir an
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c

von Rahul D. (rahul)


Lesenswert?

Arduino F. schrieb:
> Dann gibts auch keine konkrete Antwort.
> Wenn du konkretes Wissen möchtest, dann schaue dir konkrete µC an und
> auch ihren konkreten Arduino Core.

Da hast du was falsch zitiert...
ich bin durchaus in der Lage, auf einem AVR (, einem STM32 oder 
demnächst auf einem Pi Pico) eine PWM zu realisieren. ;)

von Rainer W. (rawi)


Lesenswert?

Johannes schrieb:
> Veit D. schrieb:
>> bezieht sich deine Frage auf konkrete Controller? Wenn ja welche?
> Ne ganz allgemein :)

Für mich hört sich die Frage ganz klar nach Microchip/Atmel AVRs an.

Johannes schrieb:
> ich versuche gerade PWM bei den AVRs zu verstehen.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Rainer W. schrieb:
> Johannes schrieb:
>> Veit D. schrieb:
>>> bezieht sich deine Frage auf konkrete Controller? Wenn ja welche?
>> Ne ganz allgemein :)
>
> Für mich hört sich die Frage ganz klar nach Microchip/Atmel AVRs an.

Natürlich bezieht sich die Frage auf Atmel/Microchip. Nur beim Begriff 
AVR könnten nur die neuen Controllerserien gemeint gewesen sein. 
Deswegen die Rückfrage.

von Veit D. (devil-elec)


Lesenswert?

Johannes schrieb:
> Liebe Community,
>
> ich versuche gerade PWM bei den AVRs zu verstehen.
> Bis jetzt habe ich immer nur in Arduino die ``analogWrite()`` funktion
> benutzt.
>
> Was ich bisher verstanden habe ist das Grundprinzip von PWM und dass
> dies über timer realisiert wird die immer hochzählen bis zu einem
> bestimmten Wert und dann den Ausgang setzten.

Genau, der Timer zählt bis zum eingestellten TOP, welches die 
Periodendauer vorgibt, und wenn der Timerzähler unterwegs an einem 
eingestellten Compare-Wert (Duty) vorbeikommt passiert irgendwas, was 
der Anwender konfiguriert hat. Entweder schaltet ein Pin sein Pegel um 
oder die ISR wir aktiv ... Bei TOP passiert auch irgendwas je nach 
Timerkonfig.

> Meine Fragen:
>
> 1. Die meisten AVRs haben aber doch nur wenige Timer/"Timer-Pins".
> Wie macht es dann der Arduino, dass er zig PWM Ausgänge hat und diese so
> einfach nur mit einer Funktion ansteuerbar sind?

Es werden möglichst alle Compare Ausgänge eines Timers verwendet und 
möglichst alle vorhandenen Timer.
Ein Timer hat in der Regel, bei den "alten" ATmegas 2 bis 3 eigene 
Ausgänge OCnx je Timer.
Bei den neueren Serien gibt es noch einen Splitmode, dann hat man statt 
3 (16-Bit) plötzlich 6 (8-Bit) Compareausgänge zur Verfügung mit einem 
einzigen Timer.

Daran kannste sehen was alles hinter den Kulissen im Arduino Framework 
abläuft um es dem Anwender so einfach wie möglich zu machen, wenn dieser 
nur analogWrite() verwenden muss.

> 2. Wie macht ihr das in der Praxis wenn ihr PWM mit einem AVR nutzt?
> Jedes Mal aufwendig über diese ganzen Register oder gibts da ne
> einfache, schnelle und übersichtliche Variante?

Es gibt nur die eine Möglichkeit die Register entsprechend zu 
konfigurieren. Das macht das Arduino Framework so und man muss es selbst 
auch machen. Egal wie man es macht, man muss immer die Register 
konfigurieren und ggf. zur Laufzeit ändern.

> 3. Was genau macht das analogWrite()? Setzt das die Register und
> alles im Hintergrund?

Genau. Das Arduino Framework konfiguriert einige Register vor, auch wenn 
die Hardwareeinheit in dem Moment noch nicht genutzt wird oder nie 
genutzt wird. Deswegen ist es bspw. wichtig bei eigener Verwendung der 
Timer alle Register zu nullen bevor man selbst konfiguriert. Ohne 
Arduino Framework sind alle Register nach Reset genullt bzw. haben die 
Default Einstellung laut Manual.

Mach selbst einmal folgenden Test. Lasse dir von einem Timer deiner 
Wahl, wovon du weißt das Pins für analogWrite verfügbar sind, alle 
Registerwerte ausgeben. Danach nutzt du 1x analogWrite und lässt dir 
erneut alle Registerwerte ausgeben.

Wenn du wissen möchtest was beim Arduino Frameworkim Hintergrund 
passiert, dann:
Michael B. schrieb:
> 
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c

Liegt auch alles auf deinem Rechner im Arduino Installationsverzeichnis.

von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Johannes schrieb:
> 2. Wie macht ihr das in der Praxis wenn ihr PWM mit einem AVR nutzt?
> Jedes Mal aufwendig über diese ganzen Register oder gibts da ne
> einfache, schnelle und übersichtliche Variante?

Man setzt sich einmal gründlich auf Registerebene – z.B. in klassischer 
C-Sprache unter 'Atmel/Microchip Studio', ATMEGA328P, µC-Datenblatt, 
Oszilloskop und ohne irgendwelche ArduinoUnterprogramme oder -interrupts 
im Hintergrund – damit auseinander und fängt dabei mit dem Einfachsten 
an, beispielsweise Fast-PWM mit einem Kanal/Output. Schritt für Schritt 
entstehen dann automatisch eine Initialisierungsroutine, die Befehle zum 
Ein-, Ausschalten oder Ändern der Parameter des Timers, die man 
wahlweise als eigene Macros oder Unterprogramme schreiben kann, man 
erstellt quasi eine kleine, eigene Bibliothek – das muss man dann nicht 
immer wieder neuschreiben, sondern greift in nächsten Projekten auf das 
schon Vorhandene, Gestestete und Funktionierende zurück – gegebenenfalls 
mit entsprechenden Anpassungen. Es gibt aber natürlich Leute, die sich 
dumm anstellen und diese Arbeit bei jedem neuen Projekt immer wieder von 
vorne anfangen zu machen.

Auf der Registerebene sollte man sich von Anfang an die richtige 
Nomenklatur mit der AND/OR/XOR-Verkettung (Beispiele unten) angewöhnen, 
nicht mit reinen Zahlen arbeiten, denn das ist auch – sofern nicht 
gezielt zum Testen gedacht – ziemlich dumm und obendrein noch reine 
Zeitverschwendung und Zeit ist kostbar, programmtechnisch ist es dann 
eigentlich auch eine Sackgasse. Bei den AVRs ist das mit den Registern 
und Peripherie relativ moderat und überschaubar – die STM32 z.B. sollte 
man sich nicht als absoluter Anfänger am Anfang antun; wenn man das 
relativ Einfache der AVRs begriffen hat und anzuwenden vermag, kann man 
später immer noch auf komplexere µController übergehen, sofern jemand 
dazu gewillt ist und nicht bei den AVRs mental steckenbleibt, was man 
aber leider auch oft beobachten kann – diese Fraktion meint dann auch 
naiv und selbstbelügend, mit ihrem AVR alles bauen zu können. Arduino 
ist übrigens sehr gut für Leute, die nie von einem µC gehört haben, aber 
mit einem projektmäßig etwas doch „erreichen” möchten; auch für Schüler 
in der Unter- und Mittelstufe ganz gut geeignet, um erste Erfahrungen 
mit µC zu sammeln und relativ schnell Ergebnisse für den Unterricht zu 
haben – eine Ampelschaltung ist so auf µC-Ebene mit Arduinocode in dem 
Alter zumutbar, mit einem „nackten” µC und auf Registerebene könnte das 
zu einem Desaster, Frustration und negativer Erfahrung führen. Erfolge – 
auch kleine – und positive Erfahrungen sind in der Hinsicht für weitere 
Entwicklungen ausschlaggebend.

_
1
PORTD=(1<<PORTD0)|(1<<PORTD1)|(1<<PORTD2)|(1<<PORTD3)|(1<<PORTD4)|(1<<PORTD5)|(1<<PORTD6)|(1<<PORTD7);
1
PORTD=(1<<PORTD7)|(1<<PORTD6)|(1<<PORTD5)|(1<<PORTD4)|(1<<PORTD3)|(1<<PORTD2)|(1<<PORTD1)|(1<<PORTD0);
1
TCCR0A=(3<<WGM00)|(3<<COM0B0)|(2<<COM0A0);

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Johannes schrieb:
> 1. Die meisten AVRs haben aber doch nur wenige Timer/"Timer-Pins".
> Wie macht es dann der Arduino, dass er zig PWM Ausgänge hat und diese so
> einfach nur mit einer Funktion ansteuerbar sind?

Du brauchst nicht für jeden PWM-Ausgang einen separaten Timer. Es genügt 
im Grunde völlig, wenn die Control-Register unterschiedlich sind. Bei 
Motoren ist häufig aber nicht einmal das nötig ;)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.