Forum: Mikrocontroller und Digitale Elektronik PWM in C falsch eingestellt?


von Jonas (Gast)


Lesenswert?

Hallo zusammen,

ich bin im Moment noch in der Einarbeit-Phase, was das µC programmieren 
angeht. Daher möchte ich nun einfach Versuchsweise eine LED per PWM 
dimmen.

Ich arbeite mit einem ATMega88PA und habe in diesem Fall auf PD6 eine 
LED gegen Masse angeschlossen. Programmierung in C++.


Den Timer0 habe ich mal so konfigueriert, nach dem was ich im Datenblatt 
gelesen habe:
1
  DDRD |= (1<<6);                                 /* PORTD6 */    
2
  TCCR0A |= (1<<WGM00) | (1<<WGM01);              /* Fast PWM */
3
  TCCR0B |= (1<<WGM02);                           /* Signal auf Pin */
4
  TCCR0A |= (1<<COM0A0) | (0>>COM0A1);            /* Toggle on Compare Match */
5
  TCCR0B |= (0>>CS00) | (1<<CS01) | (0>>CS02);    /* prescaler 8 */

Anschließend schreibe ich den Vergleichswert in OCR0A.


Wenn ich das Programm nun in den µC übertrage, geht die LED an. Sie 
leuchtet nicht 100%ig so hell, wie eine gleich geschaltete LED, die ich 
über einen Port direkt anschalte. Diese Helligkeit meiner PWM-LED bleibt 
jedoch konstant, völlig unabhänging vom Wert in OCR0A.

Wo ist da mein Fehler...?!

Die Taktfrequenz und der Prescaler sind doch eigentlich unabhängig... 
oder?! Ich meine es geht doch um das Verhältnis der Dauer zwischen LED 
an und aus, oder hab ich da was falsch verstanden?!

Das Datenblatt will ich jetzt aufgrund der Größe nicht anhängen, es ist 
jedoch hier original virenfrei von Atmel zu finden ;):
http://www.atmel.com/Images/doc8271.pdf
Registerbeschreibung für Timer0 findet sich auf S. 108.

Vielen Dank schonmal ;)
Lg

von Benjamin U. (utzus)


Lesenswert?

Jonas schrieb:
1
> DDRD |= (1<<6);                                 /* PORTD6 */    
2
>   TCCR0A |= (1<<WGM00) | (1<<WGM01);              /* Fast PWM */
3
>   TCCR0B |= (1<<WGM02);                           /* Signal auf Pin */
4
>   TCCR0A |= (1<<COM0A0) | (0>>COM0A1);            /* Toggle on Compare Match */
5
>   TCCR0B |= (0>>CS00) | (1<<CS01) | (0>>CS02);    /* prescaler 8 */
Wo hast du denn diese Schreibweise 0>>XXXX her?? Was macht der Kompiler 
damit?
Die erste Zeile kannst du weglassen. Wenn der timer die Kontrolle über 
den Pin übernimmt, hat das DDRD nichts mehr zu melden.

Nur zur Sicherheit. Widerstände sind schon vor den LEDs oder?

Diese Zeile wird sein (vermutung)
1
  TCCR0B |= (1<<WGM02);                           /* Signal auf Pin */
Damit bringst du den Timer in Mode 7 und er zählt nur bis OCRA0 hoch. 
Also bringt dir das nix. Diese Zeile muss weg und der Timer muss im Mode 
3 arbeiten.

von Ingo (Gast)


Lesenswert?

Jonas schrieb:
> TCCR0A |= (1<<COM0A0) | (0>>COM0A1);            /* Toggle on Compare Match */

mach mal so:
1
TCCR0A |= (1<<COM0A1);

dass hier bewirkt im übrigen nichts:
1
TCCR0A |= (1<<COM0A0) | (0>>COM0A1);
2
                         ^^^^^^^^^

Da hast auch die falschen Bits gesetzt, COM0A0 bewirkt ein toggeln, 
dieses ändert aber nicht den Tastgrad, sondern nur die Frequenz. Ist 
eingentlich das selbe wie CTC so wie ich das sehe.


Ingo

von Ingo (Gast)


Lesenswert?

Richtig, du solltest auch Mode3 wählen, dann sollte es gehen.

Ingo

von spess53 (Gast)


Lesenswert?

Hi

>Die erste Zeile kannst du weglassen. Wenn der timer die Kontrolle über
>den Pin übernimmt, hat das DDRD nichts mehr zu melden.

Nein. Nur das Portregister hat nichts zu sagen. Der Pin muss auf Ausgang 
gesetzt sein.

MfG Spess

von Jonas B. (jibi)


Lesenswert?

>Wenn ich das Programm nun in den µC übertrage, geht die LED an. Sie
>leuchtet nicht 100%ig so hell, wie eine gleich geschaltete LED, die ich
>über einen Port direkt anschalte.

Das beschreibt eine korrekt funktionierende PWM an einer LED. Also wenn 
du so fader-mässiges Nightriderspektakel erwartest braucht's noch ein 
bißchen mehr an Code...

Gruß Jonas

von Jonas (Gast)


Angehängte Dateien:

Lesenswert?

Das bewirkt leider nichts...

Dass das Null setzen nichts bringt weiß ich, ich mache die Einstellungen 
der Verständlichkeit halber rein, damit ist mir dann auch später noch 
klar, welche Bits da mit reinspielen.

Ein toggeln ist doch eigentlich das, was ich erreichen will.
Nach dem Bild aus dem AVR-GCC-Tutorial (s. Anhang) soll der Pin ja immer 
getoggelt werden, wenn der Zähler den Vergleichswert erreicht hat. 
Oder?!

Lg

von Krapao (Gast)


Lesenswert?

Der Fehler steckt vmtl. in dem Fakt, dass du mit OCR0A die PWM Frequenz 
eingestellt hast, aber nirgends den Duty Cycle. Der ist hier immer 50% 
AN und 50% AUS.

Du musst 1-2 Register benutzen: Null bis eins, um die Frequenz 
festzulegen (0: Frequenz über TOP) und null bis eins, um den 
"Umschaltpunkt" auf dem Hochzählberg anzugeben, das definiert dann dein 
Duty Cycle (0: Softwareumschaltung in einer ISR vgl. LED-Fading).

von Jonas (Gast)


Lesenswert?

oha.. da war ich wohl zu langsam. :D
Letzter Beitrag bezieht sich auf Ingo. Den Rest muss ich erst mal kurz 
lesen ;)

von Jonas (Gast)


Lesenswert?

Benjamin Utz schrieb:
> Diese Zeile wird sein (vermutung)  TCCR0B |= (1<<WGM02); 
/* Signal auf Pin */
> Damit bringst du den Timer in Mode 7 und er zählt nur bis OCRA0 hoch.
> Also bringt dir das nix. Diese Zeile muss weg und der Timer muss im Mode
> 3 arbeiten.

Hmm der Text ist mit Datenblatt nachvollziehbar. Nur laut Datenblatt 
brauche ich auch eben dieses Bit, um die PWM überhaupt auf meinen 
OC0A-Pin (PD6) zu bringen.?!

>COM0A1: 0 COM0A0: 1
>       WGM02 = 0: Normal Port Operation, OC0A Disconnected.
>       WGM02 = 1: Toggle OC0A on Compare Match.
>(Datenblatt Table 15-3, S.108)

Und eben dies passiert auch, wenn ich die Zeile auskommentiere/lösche... 
Die LED geht komplett aus.

Die Schreibweise kommt übrigens auch aus dem AVR-GCC-Tutorial, der 
Compiler nimmt z.B. (1<<6) als Bit 6 wird gesetzt. Zusammen mit dem Or 
(|) wird quasi nur das bit x gesetzt, alles andere bleibt erhalten wie 
es ist. ;)

Hmm den Beitrag von Krapao habe ich nur halb verstanden... Sehe ich das 
richtig, dass es auf das gleiche Problem hinausläuft, dass der Zähler 
nicht, wie in der Grafik (s. Beitrag oben) durchläuft, sondern immer bei 
match zurückgesetzt wird, daher der toggle nur 50%/50% bewirkt... ?!
Beim zweiten Teil komme ich leider gar nicht mehr mit :/

von Benjamin U. (utzus)


Lesenswert?

Hey,

also: Das Bild stimmt mit dem Zähler so nicht überein, da dein Zähler 
nicht rückwärts zählen kann.

Du hast den Zähler, der von 0 bis 0xFF zählt. Dies tut er in einer 
bestimmten Frequenz, die du durch den Prescaler und den Quarz/Takt des 
MCs festlegst.

Mit dem OCR bestimmst du den Zeitpunkt, zu dem geschaltet wird. (Sofern 
der Timer im Mode 3 läuft).
Nun die Frage, willst du wirklich ein Toggeln? Dein Zähler läuft ja 
immer von 0 bis 255. Da würde ich den Ausgang bei 0 auf high setzten und 
bei erreichen deiner Zahl (ORC) auf low.
Dies machst du mit dieser Einstellung:
1
TCCR0A |= (1<<COM0A1);
Laut Datenblatt:
Clear OC0A on Compare Match, set OC0A at BOTTOM,
(non-inverting mode).

Also Timer auf Mode 3 und das TCCR0A Register anpassen.

Grüße

von Jonas (Gast)


Lesenswert?

Ok... So funktioniert es jetzt. :) Dankeschön!!

Ich bin nach dem obigen Bild gegangen, daher wollte ich das mit dem 
toggeln realisieren...

Rein interessenhalber dazu noch: Der Timer kann gar nicht rückwärts 
zählen??! oO Was soll denn dann das Bild?!

von spess53 (Gast)


Lesenswert?

Hi

>Rein interessenhalber dazu noch: Der Timer kann gar nicht rückwärts
>zählen??! oO Was soll denn dann das Bild?!

Klar kann er das. Passiert bei Phase Correct/Phase&Frequency Correct 
PWM.

MfG Spess

von Benjamin U. (utzus)


Lesenswert?

spess53 schrieb:
>>Rein interessenhalber dazu noch: Der Timer kann gar nicht rückwärts
>>zählen??! oO Was soll denn dann das Bild?!
>
> Klar kann er das. Passiert bei Phase Correct/Phase&Frequency Correct
> PWM.

Oh genial. Wieder was gelernt ;) Das Brauch ich ja unbedingt!! Danke!

von spess53 (Gast)


Lesenswert?

Hi

>Oh genial. Wieder was gelernt ;) Das Brauch ich ja unbedingt!! Danke!

Wozu? Bisher habe ich noch keine sinnvolle Verwendung dafür gehabt. 
Außerdem solltest du beachten, das dann die PWM nur mit der halben 
Frequenz läuft.

MfG Spess

von Benjamin U. (utzus)


Lesenswert?

Ja, war ein schnellschuss, hilft mir doch nicht. Ist mir dann nach dem 
Posten wieder eingefallen :D
Hab folgendes Problem. Ich dimme mit SSRs. Dazu hab ich einen Timer, den 
ich Netzsynchron starte. T = 20ms. Per OCR werden die SSRs 
eingeschalten. Nur muss ich den OCR wieder wegbekommen, bevor eine neue 
Halbwelle kommt. Werd es also nicht direkt per OCR machen, sondern per 
Interrupt vom OCR und dann nur nen kurzen Impuls geben.
Dachte durch den PhaseCorrect-Mode löst sich das Problem, aber dann kann 
ich ja maximal bis 50% dimmen :D

Grüße

von Jonas (Gast)


Lesenswert?

Hmmm also irgendwie sind die Einstellungen etwas wirr^^. Ich wollte die 
Sache jetzt im Phase-Correct Mode mit Toggle mal ausprobieren, um es 
quasi nach der Grafik zu machen... Aber hier ist wieder der gleiche 
Witz:
Um das Signal auf den Pin zu bekommen muss ich WGM02 setzen, wodurch ich 
wieder in einen Modus komme, der als TOP wieder OCRA hat.
Also selbst wenn der Timer jetzt rückwärts zählen kann, kann ich durch 
sich widersprechende Einstellungen das nicht so einstellen... :/
Nuja...

von Benjamin U. (utzus)


Lesenswert?

Ja, du wirst das OCRA-Register für den TOP-Wert opfern müssen, also 0xFF 
reinschreiben (oder je nach gewünschter Frequenz).

und die LED muss dann über OCRB gedimmt werden. => Also anderer PIN.

von spess53 (Gast)


Lesenswert?

Hi

>Ja, du wirst das OCRA-Register für den TOP-Wert opfern müssen, also 0xFF
>reinschreiben (oder je nach gewünschter Frequenz).

Oder den 16-Bit-Timer opfern. Der hat 16 PWM-Modi. Darunter auch Phase 
Correct mit 8, 9 und 10-Bit.

MfG Spess

von Jonas (Gast)


Lesenswert?

stimmt...!!
Das müsste funktionieren... Da es nur Test ist, ist mir der Pin 
ziehmlich egal ;) ich probiers morgen mal :D
Danke!!

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.