Forum: Mikrocontroller und Digitale Elektronik Wie funktioniert PWM beim ATmega328


von Marinus O. (marinus_o)


Lesenswert?

Hallo zusammen,

Ich setze mich nun schon seit einiger Zeit mit Mikrocontrollern 
auseinander, bin aber noch ein ziemlicher Noob, wenn es darum geht die 
Prozesse zu verstehen, welche im Hintergrund ablaufen. Eins stößt mir 
besonders vor den Kopf, Stichwort:PWM. Meines wissens besitz der Arduino 
keinen D/A-Wandler, sondern regelt dies über PWM. Nur gibt es bei der 
Sache einen kleinen Haken. Um das Signal aufrecht zu erhalten, muss doch 
der Controller ständig Regeln. Wenn ich aber in der Zwischenzeit etwas 
anderes mache, z.B. delay() hat er doch keine Zeit mehr das Signal 
aufrecht zu erhalten.
Bsp
1
loop(){
2
   analogWrite(3,255);//volle Leistung
3
   delay(1000);       //Immer noch volle Leistung
4
                      //sollte aber eigentlich von delay() blockiert sein.
5
 
6
   analogWrite(3,125);//halbe Leistung
7
8
}
Wo liegt mein Denkfehler?

: Bearbeitet durch Moderator
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

Marinus O. schrieb:
> sollte aber eigentlich von delay() blockiert sein.
Warum sollte das delay() irgendwas blockieren?
Die PWM ist in Hardware auf dem AVR vorhanden. Sie läuft quasi als 
"Coprozessor" nebenher.

BTW: bitte die [c] Tags zum formatieren nehmen.

: Bearbeitet durch Moderator
von Stefan K. (stefan64)


Lesenswert?

Die PWM erzeugt ein eigenständiger Timer. Die CPU muss nach dem 
(einmaligen) Festlegen des PWM-Verhältnisses nicht mehr tun, um die PWM 
aufrecht zu erhalten.

von Axel S. (a-za-z0-9)


Lesenswert?

Lothar M. schrieb:
> Die PWM ist in Hardware auf dem AVR vorhanden.

Stefan K. schrieb:
> ie PWM erzeugt ein eigenständiger Timer. Die CPU muss nach dem
> (einmaligen) Festlegen des PWM-Verhältnisses nicht mehr tun

Das dürfte speziell für Arduino auf AVR nicht stimmen, weil er mehr 
analoge Ausgänge bietet als er Hardware PWM-Kanäle hat. Es wird also 
eine Soft-PWM sein, wo der Timer nur noch als Interrupt-Quelle genutzt 
wird und die ISR die digitalen(!) Ausgänge passend zu den zugewiesenen 
Analogwerten ein- bzw. ausschaltet.

Aber natürlich funktioniert das auch dann noch, wenn man eine 
Verzögerungsschleife auf die harte Tour mit busy waiting macht.

von Dieter F. (Gast)


Lesenswert?

Axel S. schrieb:
> Es wird also
> eine Soft-PWM sein, wo der Timer nur noch als Interrupt-Quelle genutzt
> wird und die ISR die digitalen(!) Ausgänge passend zu den zugewiesenen
> Analogwerten ein- bzw. ausschaltet.

Ja, das kann man hier auch "erahnen"

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

...
The PWM outputs generated on pins 5 and 6 will have higher-than-expected 
duty cycles. This is because of interactions with the millis() and 
delay() functions, which share the same internal timer used to generate 
those PWM outputs. This will be noticed mostly on low duty-cycle 
settings (e.g 0 - 10) and may result in a value of 0 not fully turning 
off the output on pins 5 and 6.
...

von momo (Gast)


Lesenswert?

Axel S. schrieb:
> Das dürfte ...
> Es wird also ...

Auch was definitives?

von Einer K. (Gast)


Lesenswert?

momo schrieb:
> Axel S. schrieb:
>> Das dürfte ...
>> Es wird also ...
>
> Auch was definitives?

Ja!

Der AVR Arduino Core nutzt Hardware PWM.
Für den Uno (ATMega328P) gilt: 3 Timer 6 PWM Kanäle.

Natürlich gibt es auch Software PWM Libs.
Aber die haben nichts mit dem analogWrite() des Arduino Core zu tun.

von Axel S. (a-za-z0-9)


Lesenswert?

momo schrieb:
> Axel S. schrieb:
>> Das dürfte ...
>> Es wird also ...
>
> Auch was definitives?

Wer es genau wissen will, soll halt die Quellen der Arduino-Lib lesen. 
Da ich diese Lib nicht verwende, werde ich es sicher nicht tun. 
Andererseits sind es definitiv mehr "analoge" Ausgänge als PWM-Kanäle. 
Es kann also nur Soft-PWM sein.

Für die Frage des TE ist das ein unerhebliches Detail. Sowohl Hardware- 
als auch Software-PWM funktionieren im Hintergrund.

von Einer K. (Gast)


Lesenswert?

Axel S. schrieb:
> Andererseits sind es definitiv mehr "analoge" Ausgänge als PWM-Kanäle.
> Es kann also nur Soft-PWM sein.
Nein!

Siehe:
> PWM Digital I/O Pins  6
In: https://store.arduino.cc/arduino-uno-rev3

von Zwei Meinungen (Gast)


Lesenswert?

Arduino F. schrieb:
> Natürlich gibt es auch Software PWM Libs.
> Aber die haben nichts mit dem analogWrite() des Arduino Core zu tun.

Axel S. schrieb:
> Es kann also nur Soft-PWM sein.

Wer hat recht?

von Einer K. (Gast)


Lesenswert?


von Wolfgang (Gast)


Lesenswert?

Marinus O. schrieb:
> Meines wissens besitz der Arduino keinen D/A-Wandler, sondern
> regelt dies über PWM.

So kann man sich täuschen. Es hängt davon ab, welcher Prozessor auf dem 
Arduino sitzt. Der Arduino Due hat zwei echte D/A-Wandler.

von M. K. (sylaina)


Lesenswert?

Zwei Meinungen schrieb:
> Wer hat recht?

Der Fanboy hat recht und der Axel nicht. Beim analogWrite() sollte man 
halt auch mal bedenken, dass es mehr als nur das Arduino Uno Board gibt 
;)

von Le X. (lex_91)


Lesenswert?

Lothar M. schrieb:
> Marinus O. schrieb:
>> sollte aber eigentlich von delay() blockiert sein.
> Warum sollte das delay() irgendwas blockieren?

Natürlich blockiert delay() "irgendwas", nämlich die CPU.

Die ist zwar nicht für die Erzeugung des PWM-Signals zuständig, das 
macht die jeweilige Peripherie.
Trotzdem ist deine polemische Scheinfrage mehr als unangebracht.

von Stefan F. (Gast)


Lesenswert?

Ich zittiere mal aus der Datei wiring_analog.c für AVR Mikrocontroller:
1
// Right now, PWM output only works on the pins with
2
// hardware support.  These are defined in the appropriate
3
// pins_*.c file.  For the rest of the pins, we default
4
// to digital output.
5
void analogWrite(uint8_t pin, int val)
6
{
7
  // We need to make sure the PWM output is enabled for those pins
8
  // that support it, as we turn it off when digitally reading or
9
  // writing with them.  Also, make sure the pin is in output mode
10
  // for consistenty with Wiring, which doesn't require a pinMode
11
  // call for the analog output pins.
12
  pinMode(pin, OUTPUT);
13
  if (val == 0)
14
  {
15
    digitalWrite(pin, LOW);
16
  }
17
  else if (val == 255)
18
  {
19
    digitalWrite(pin, HIGH);
20
  }
21
  else
22
  {
23
    switch(digitalPinToTimer(pin))
24
    {
25
      // XXX fix needed for atmega8
26
      #if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
27
      case TIMER0A:
28
        // connect pwm to pin on timer 0
29
        sbi(TCCR0, COM00);
30
        OCR0 = val; // set pwm duty
31
        break;
32
      #endif
33
34
      #if defined(TCCR0A) && defined(COM0A1)
35
      case TIMER0A:
36
        // connect pwm to pin on timer 0, channel A
37
        sbi(TCCR0A, COM0A1);
38
        OCR0A = val; // set pwm duty
39
        break;
40
      #endif
41
42
      #if defined(TCCR0A) && defined(COM0B1)
43
      case TIMER0B:
44
        // connect pwm to pin on timer 0, channel B
45
        sbi(TCCR0A, COM0B1);
46
        OCR0B = val; // set pwm duty
47
        break;
48
      #endif
49
50
      #if defined(TCCR1A) && defined(COM1A1)
51
      case TIMER1A:
52
        // connect pwm to pin on timer 1, channel A
53
        sbi(TCCR1A, COM1A1);
54
        OCR1A = val; // set pwm duty
55
        break;
56
      #endif
57
58
      #if defined(TCCR1A) && defined(COM1B1)
59
      case TIMER1B:
60
        // connect pwm to pin on timer 1, channel B
61
        sbi(TCCR1A, COM1B1);
62
        OCR1B = val; // set pwm duty
63
        break;
64
      #endif
65
66
      #if defined(TCCR1A) && defined(COM1C1)
67
      case TIMER1C:
68
        // connect pwm to pin on timer 1, channel B
69
        sbi(TCCR1A, COM1C1);
70
        OCR1C = val; // set pwm duty
71
        break;
72
      #endif
73
74
      #if defined(TCCR2) && defined(COM21)
75
      case TIMER2:
76
        // connect pwm to pin on timer 2
77
        sbi(TCCR2, COM21);
78
        OCR2 = val; // set pwm duty
79
        break;
80
      #endif
81
82
      #if defined(TCCR2A) && defined(COM2A1)
83
      case TIMER2A:
84
        // connect pwm to pin on timer 2, channel A
85
        sbi(TCCR2A, COM2A1);
86
        OCR2A = val; // set pwm duty
87
        break;
88
      #endif
89
90
      #if defined(TCCR2A) && defined(COM2B1)
91
      case TIMER2B:
92
        // connect pwm to pin on timer 2, channel B
93
        sbi(TCCR2A, COM2B1);
94
        OCR2B = val; // set pwm duty
95
        break;
96
      #endif
97
98
      #if defined(TCCR3A) && defined(COM3A1)
99
      case TIMER3A:
100
        // connect pwm to pin on timer 3, channel A
101
        sbi(TCCR3A, COM3A1);
102
        OCR3A = val; // set pwm duty
103
        break;
104
      #endif
105
106
      #if defined(TCCR3A) && defined(COM3B1)
107
      case TIMER3B:
108
        // connect pwm to pin on timer 3, channel B
109
        sbi(TCCR3A, COM3B1);
110
        OCR3B = val; // set pwm duty
111
        break;
112
      #endif
113
114
      #if defined(TCCR3A) && defined(COM3C1)
115
      case TIMER3C:
116
        // connect pwm to pin on timer 3, channel C
117
        sbi(TCCR3A, COM3C1);
118
        OCR3C = val; // set pwm duty
119
        break;
120
      #endif
121
122
      #if defined(TCCR4A)
123
      case TIMER4A:
124
        //connect pwm to pin on timer 4, channel A
125
        sbi(TCCR4A, COM4A1);
126
        #if defined(COM4A0)    // only used on 32U4
127
        cbi(TCCR4A, COM4A0);
128
        #endif
129
        OCR4A = val;  // set pwm duty
130
        break;
131
      #endif
132
      
133
      #if defined(TCCR4A) && defined(COM4B1)
134
      case TIMER4B:
135
        // connect pwm to pin on timer 4, channel B
136
        sbi(TCCR4A, COM4B1);
137
        OCR4B = val; // set pwm duty
138
        break;
139
      #endif
140
141
      #if defined(TCCR4A) && defined(COM4C1)
142
      case TIMER4C:
143
        // connect pwm to pin on timer 4, channel C
144
        sbi(TCCR4A, COM4C1);
145
        OCR4C = val; // set pwm duty
146
        break;
147
      #endif
148
        
149
      #if defined(TCCR4C) && defined(COM4D1)
150
      case TIMER4D:        
151
        // connect pwm to pin on timer 4, channel D
152
        sbi(TCCR4C, COM4D1);
153
        #if defined(COM4D0)    // only used on 32U4
154
        cbi(TCCR4C, COM4D0);
155
        #endif
156
        OCR4D = val;  // set pwm duty
157
        break;
158
      #endif
159
160
              
161
      #if defined(TCCR5A) && defined(COM5A1)
162
      case TIMER5A:
163
        // connect pwm to pin on timer 5, channel A
164
        sbi(TCCR5A, COM5A1);
165
        OCR5A = val; // set pwm duty
166
        break;
167
      #endif
168
169
      #if defined(TCCR5A) && defined(COM5B1)
170
      case TIMER5B:
171
        // connect pwm to pin on timer 5, channel B
172
        sbi(TCCR5A, COM5B1);
173
        OCR5B = val; // set pwm duty
174
        break;
175
      #endif
176
177
      #if defined(TCCR5A) && defined(COM5C1)
178
      case TIMER5C:
179
        // connect pwm to pin on timer 5, channel C
180
        sbi(TCCR5A, COM5C1);
181
        OCR5C = val; // set pwm duty
182
        break;
183
      #endif
184
185
      case NOT_ON_TIMER:
186
      default:
187
        if (val < 128) {
188
          digitalWrite(pin, LOW);
189
        } else {
190
          digitalWrite(pin, HIGH);
191
        }
192
    }
193
  }
194
}


Das ist eindeutig kein Soft-PWM. Es zeigt auch eindeutig, dass die 
Anzahl der verfügbaren PWM Ausgänge vom konkreten Chip abhängt. Pervers 
finde ich, dass die Library einfach auf digitale LOW/HIGH Ausgabe 
wechselt, wenn man einen Ausgang analog zu verwenden versucht, bei dem 
das nicht geht.

von Axel S. (a-za-z0-9)


Lesenswert?

Arduino F. schrieb:
> Axel S. schrieb:
>> Andererseits sind es definitiv mehr "analoge" Ausgänge als PWM-Kanäle.
>> Es kann also nur Soft-PWM sein.
> Nein!

Aha. So kann man sich irren. Ich hatte angenommen, daß Arduino jeden Pin 
für analogwrite() erlaubt. Wie gesagt, ich nutze diese API nicht (ist 
mir zu fett, zu langsam und allgemein zu wenig nützlich). Da hatte ich 
denen einfach zu viel zugetraut.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Axel S. schrieb:
> Ich hatte angenommen, daß Arduino jeden Pin
> für analogwrite() erlaubt.

Erklärt aber im Nachhinein, warum der Arduino als PWM ausgewiesene Pinne 
hat.

Trotzdem schön zu sehen, wie 'das Gelump' dann doch zusammen gestrickt 
ist.
Gibt's da irgendwo eine Komplett-Übersicht, wo die ganzen Standard-Libs 
zusammen getragen sind?

MfG

von Georg M. (g_m)


Lesenswert?

Stefan U. schrieb:
> Das ist eindeutig kein Soft-PWM. Es zeigt auch eindeutig, dass die
> Anzahl der verfügbaren PWM Ausgänge vom konkreten Chip abhängt.

Auf Seite 2 steht:
– Six PWM Channels

( Atmel-42735B-ATmega328/P_Datasheet_Complete-11/2016 )

von Einer K. (Gast)


Lesenswert?

Patrick J. schrieb:
> Gibt's da irgendwo eine Komplett-Übersicht, wo die ganzen Standard-Libs
> zusammen getragen sind?

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

von M. K. (sylaina)


Lesenswert?

Patrick J. schrieb:
> Trotzdem schön zu sehen, wie 'das Gelump' dann doch zusammen gestrickt
> ist.

Das "Gelump" ist auch IMO recht gut dokumentiert was auch den Erfolg 
davon durchaus erklärt.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Arduino F. schrieb:
> https://www.arduino.cc/en/Reference/Libraries

Wer hätte Das gedacht - die Seite habe ich schon in den Lesezeichen 
*Kopf->Tisch* ... jetzt aber mit Schlagwörtern zum 'schneller finden'.

Danke für den Link, Folgende werden dort ja auch stöbern können.

MfG

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.