Hallo allerseits,
ich habe eine Lampe gebaut, in der eine recht helle LED von einem
ATmega8 via MOSFET gedimmt wird.
Der Mega8 erzeugt das PWM-Signal mit Timer 1:
1
voidPWM_Init(void)
2
{
3
// Non-inverted PWM on OC1A, 16 Bit Fast PWM
4
TCCR1A=(1<<COM1A1)|(1<<WGM11);
5
TCCR1B=(1<<WGM13)|(1<<WGM12);
6
7
// Precaler of 1 -> ~122 Hz PWM frequency
8
TCCR1B&=~((1<<CS12)|(1<<CS11));
9
TCCR1B|=(1<<CS10);
10
11
// TOP for PWM, full 16 Bit
12
ICR1=0xFFFF;
13
}
Um die Helligkeit zu setzen gibt es folgende Funktion, die passend zur
Helligkeitsstufe einen 16-Bit-Wert aus dem Speicher lädt (Stichwort
lineare Helligkeit für's Auge durch exponentiellen PWM-Anstieg):
1
voidsetBrightness(uint8_tbright)
2
{
3
if(bright==0)
4
PWM_off();
5
else
6
PWM_on();
7
8
// Set the PWM for the brightness
9
OCR1A=pgm_read_word(PWM_TABLE+bright);
10
11
// Store the current brightness
12
brightness=bright;
13
}
Der Getter ist eher unbedeutend, soll der Vollständigkeit halber aber
erwähnt werden:
1
// Get the current brightness
2
uint8_tgetBrightness(void)
3
{
4
returnbrightness;
5
}
Da merkwürdigerweise bei OCR1A=0 die LED noch minimal leuchtet, habe ich
zwei Zusatzfunktionen PWM_off() und PWM_on() geschrieben. PWM_off() soll
die Verbindung zum Pin OC1A kappen und somit den Pin PB1 auf GND ziehen.
Das funktioniert auch hervorragend. PWM_on() soll den Timer wieder mit
dem Pin verbinden und somit wieder das gepulste Signal ausgeben.
1
// Turn the PWM signal on
2
voidPWM_on(void)
3
{
4
// Reenabling the PWM
5
TCCR1A|=(1<<COM1A1);
6
}
7
8
// Turn the PWM signal off
9
voidPWM_off(void)
10
{
11
// Disabling the PWM
12
TCCR1A&=~(1<<COM1A1);
13
}
Nun zum Problem...
Wenn ich die Helligkeit auf 0 gesetzt habe und anschließend langsam die
Helligkeit anhebe (0, 1, 2, ...), dann gibt es ab und zu direkt nach 0
zuerst einen sehr hellen Blitz und dann wird erst gedimmt.
Ich konnte den Fehler auf PWM_on() bzw. somit auch PWM_off() einkreisen.
Könnt ihr mir vielleicht eine elegantere Möglichkeit nennen, wie man den
Pin vollständig auf GND ziehen kann, ohne dass ein "Blitz" beim
Wiedereinschalten auftaucht?
Es scheint ja so zu sein, dass der Pin für einen Bruchteil einer Sekunde
auf Vcc liegt...
Besten Dank für eure Hilfe!
Viele Grüße
Florian
Moin,
Ich würd tippen, dass du mit COM1A1 ein Clear on Compare-Match hast.
Das heißt, dein PWM ist AN, und geht AUS, wenn er den Compare-Wert hat.
Heißt, er ist AN, prüft, welche Zahl vorliegt, sieht eine null, und
schaltet dann AUS.
Kann sein, muss aber nicht.
Lösung: Du machst auch COM1A0 auf 1. Dann hast du ein Set on Compare
Match. Problem ist, dass du dann deine Tabelle einmal invertieren
musst... Eleganter fällts mir aber nicht ein.
Gerade noch eine Idee:
Wenn du den Pin kappst, bin ich mir gerad nicht sicher, was er mit dem
OCR-Register macht. Könnte sein, dass er da noch einen Wert gespeichert
hat, ab dem er dann weiterzählt, sobald du den Pin wieder "ranklemmst".
Somit ist die LED dann für einen PWM-Zyklus noch an.
also vielleicht das OCR-Register beim Ausschalten auch auf 0 setzen.
Hallo anfaenger (so würde ich Dich nicht bezeichnen ;) ),
Hallo Stefan,
vielen Dank für eure Hilfe!
Ich habe den Code nun folgendermaßen geändert, zum Einen habe ich den
invertierten Modus von Fast PWM verwendet und COM1A0 gesetzt:
1
voidPWM_Init(void)
2
{
3
// Inverted PWM on OC1A, 16 Bit Fast PWM, ICR1 is TOP value
4
TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<WGM11);
5
TCCR1B=(1<<WGM13)|(1<<WGM12);
6
7
// Precaler of 1 -> ~122 Hz PWM frequency
8
TCCR1B&=~((1<<CS12)|(1<<CS11));
9
TCCR1B|=(1<<CS10);
10
11
// TOP for Fast PWM
12
ICR1=0xFFFF;
13
14
// Turn the lamp off
15
setBrightness(BRIGHT_OFF);
16
}
Zum Anderen habe ich wie vorgeschlagen erst einmal OCR1A invertiert,
indem ich von 0xFFFF abgezogen habe:
1
voidsetBrightness(uint8_tbright)
2
{
3
/*if(bright == 0)
4
PWM_off();
5
else
6
PWM_on();*/
7
8
// Set the PWM for the brightness
9
OCR1A=(0xFFFF-pgm_read_word(PWM_TABLE+bright));
10
11
// Store the current brightness
12
brightness=bright;
13
}
Es funktioniert und meine Lösung mit PWM_on() und PWM_off() ist nicht
mehr notwendig...
Nun ist mir aber aufgefallen, wenn ich die Elektronik einschalte, dann
blitzt die LED bei jedem Reset erst einmal kurz auf.
Wie kann ich das verhindern?
Viele Grüße und nochmals besten Dank!
Florian
Florian schrieb:> Nun ist mir aber aufgefallen, wenn ich die Elektronik einschalte, dann> blitzt die LED bei jedem Reset erst einmal kurz auf.> Wie kann ich das verhindern?
Wenn du nach solchen Sachen suchst, dann musst du dir im Detail klar
machen, was jede einzelne Anweisung bewirkt und wie die Hardware darauf
reagieren wird.
zb hier
void PWM_Init(void)
{
// Inverted PWM on OC1A, 16 Bit Fast PWM, ICR1 is TOP value
TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<WGM11);
TCCR1B = (1<<WGM13) | (1<<WGM12);
// Precaler of 1 -> ~122 Hz PWM frequency
TCCR1B &= ~((1<<CS12) | (1<<CS11));
TCCR1B |= (1<<CS10);
// TOP for Fast PWM
ICR1 = 0xFFFF;
Der Code beginnt damit, dass du den Ausgabepin an den Timer übergibst,
und dass du den Timer in den PWM Modus schaltest.
Aber: Dadurch dass du den Timer in den PWM Modus schaltest fängt der
schon mit seiner PWM-Arbeit an. Die besteht darin, dass er den Inhalt
des Compare Registers mit dem Timer-Zählregister vergleicht und bei
Übereinstimmung schaltet der den Pin.
Das tun deine Register aber! Dein Compare Register hat seinen
Einschaltwert, der da ist: 0
Und das Zählregister hat auch noch seinen Einschaltwert, der da ist: 0
Das wiederrum bedeutet, dass du sofort einen Compare Match hast und die
Hardware macht das, was sie in so einem Fall machen muss: Sie schaltet
den Pin.
Danach gibst du den Vorteiler für den Timer frei. Und der Timer beginnt
mit seiner Zählung. Durch den PWM Modus wird ihm aufgetragen bis zum
Wert in ICR1 zu zählen. Wenn du den Prescaler frei gibst, welchen Wert
hat denn ICR1? Richtig. immer noch seinen Einschaltwert, der da war: 0
Und wieder greift die PWM-Hardware und macht, was sie machen muss: sie
setzt den Timer wieder auf 0 und schaltet den Pin.
Also: Darauf achten in welcher Reihenfolge die Dinge passieren! In
welcher Reihenfolge müssen welche Register befüllt werden, damit am
Anfang nichts schief geht. Wenn automatische Hardware Aktionen von
Registern abhängen, dann ist es eine ziemlich gute Idee erst einmal
dafür zu sorgen, dass die Register vernünftige Anfangswerte haben, ehe
man die Funktionalität aktiviert.
Und ja: Das kann manchmal aus dem Datenblatt etwas tricky herauszulesen
sein, weil oft nicht ganz klar ist, wann genau jetzt eine bestimmte
Hardwareaktion gemacht wird. Wird der Registervergleich gemacht, nachdem
der Timer um 1 hochgezählt hat und wird dieser Vergleich nur einmal
gemacht oder werden die beiden ständig miteinander verglichen?
Hallo Karl,
so scheint es gut zu funktionieren! :)
Besten Dank an alle!!!
Gibt es an dieser Reihenfolge noch Optimierungsvorschläge, oder ist die
Reihenfolge nun so korrekt?
1
voidPWM_Init(void)
2
{
3
// Inverted PWM on OC1A, 16 Bit Fast PWM, ICR1 is TOP value
4
TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<WGM11);
5
TCCR1B=(1<<WGM13)|(1<<WGM12);
6
7
// TOP for Fast PWM
8
ICR1=0xFFFF;
9
10
// Turn the lamp off
11
setBrightness(BRIGHT_OFF);
12
13
// Activate clock source and set precaler of 1 -> ~122 Hz PWM frequency