PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 0% Duty Cycle bei PWM nicht möglich?



root2
10.04.2007, 12:33
Nachdem dies mein erster Post hier ist ein herzliches "Hallo Forum" an alle.

Nun gleich zu meinem Problem:

Ich möchte mit einem atmega48 (@ 8MHz) per Timer1 eine PWM am Ausgang PortB.1 erzeugen, die eine LED in der Helligkeit steuert (in meinem Fall ein zyklisches An- und Abschwellen).
Die PWM hat folgende Kenngrößen: f=80Hz, Duty Cycle variabel von 0..100%

Der Duty Cycle der PWM wird dabei durch einen zweiten Timer alle 10ms neu gesetzt (der Wert wird aus einem uchar array gelesen und dann auf das passende Maß umgerechnet). Nachdem das Array einmal durchlaufen ist, wird eine Zeit lang gewartet (der Ausgang soll während der Wartezeit auf 0 sein).

Die PWM funktioniert auch schon recht gut. In der Wartezeit ist der Pegel am Ausgang auch konstant 0. Nur habe ich während des Array Durchlaufs - selbst wenn das ganze Array mit 0x00 (0% Duty Cycle) gefüllt ist - das Problem, dass im Oszi immernoch in unregelmäßigen Abständen sehr schmale Peaks zu erkennen sind und der Ausgang nicht konstant abgeschaltet bleibt.

Nachtrag: Ich habe gerade am Oszi bemerkt, auch bei 100% Duty Cycle ist die Einschaltzeit nicht konstant auf 1, sondern fällt in unregelmäßigen Abständen immer mal wieder auf 0 ab. Das stört mich nicht so arg, aber der Wert bei 0% Duty Cycle sollte wirklich dauernd auf 0 sein.

Hier ein (etwas vereinfachter) Auszug aus dem Code (ich verwende IAR Embedded Workbench als IDE/Compiler):



void UpdateDutyCycle(void)
{
if(bArrayFinished < 0x01) // we're not at the end of the array, so update the duty cycle
{
OCR1A = ucDutyCycleArray[ucDutyIndex] * 39 / 10; // duty cycle of PWM for LED calculated via uchar array
ICR1 = 0x0188; // TOP value: 0x0188 = 125us period @ prescaler 256 and 8MHz
TCCR1A = (1<<COM1A1 | 1<<WGM11); // Clear OC1A on match, set OC1A on BOTTOM / fast PWM, ICR1 used for TOP
TCCR1B = (1<<WGM13 | 1<<WGM12 | 1<<CS12); // fast PWM, ICR1 used for TOP / timer prescaler 256
PRR = (1<<PRTW1)|(1<<PRTIM0)|(1<<PRUSART0); // Turn Timer1 on as we need it running here
}
else // we're at the end of the array, now wait some time
{
SET_LED_OFF(); // Reset LED periodically via #DEFINE to ensure it stays turned off during wait time
TCCR1B = (1<<WGM13 | 1<<WGM12 | 0<<CS12 | 0<<CS11 | 0<<CS10); // Turn off Timer1
/* wait routine goes here... */
}
}


Mache ich hier einen Denkfehler, was die Benutzung von ICR1 und OCRA1 angeht?

Danke schon jetzt für euere Hilfe.

Bernd

askazo
10.04.2007, 13:36
Den genauen Grund für diese Peaks kann ich Dir nicht nennen. Ich denke mal, dass der Controller zwischenzeitlich beim Vergleichen von TCNT und OCR einfach mal zu langsam ist und dadurch bis 1 zählt bevor der Überlauf erkannt wird. Bei 100% wird das ähnlich sein.

Als Lösung würde ich Dir vorschlagen, die Werte 0% und 100% als Ausnahme abzufangen und in diesen Fällen den PWM-Ausgang einfach als normalen Port zu schalten.

Gruß,
askazo

root2
10.04.2007, 14:52
... Als Lösung würde ich Dir vorschlagen, die Werte 0% und 100% als Ausnahme abzufangen und in diesen Fällen den PWM-Ausgang einfach als normalen Port zu schalten.
Vielen Dank, das hat geholfen. Ich habe allerdings (aus Speicherplatzgründen) nur die 0% Werte abgefangen, was mir auch ausreicht.

Was mir noch aufgefallen ist: Wenn ich den Controller in einen Sleep Mode versetze (er läuft dann nur noch mit 4MHz im IDLE mode) werden die Pulse (mit der von Dir angebrachten Verbesserung) korrekt wiedergegeben - sowohl bei 100%, als auch bei 0% Duty Cycle. Im Active Mode (8MHz) sind die 100% Duty Cycle Pulse weiterhin unterbrochen durch einzelne, sehr schmale Pulse.

Aber ich denke, mit der Lösung kann ich gut leben.

Falls jemand trotzdem noch weitere Informationen zu dem Effekt hat, kann er/sie sich aber immernoch gern melden ;-)

Gruß, Bernd