So jetzt wo der Sommer kommen sollte, dieser doch auf sich warten lässt,
habe ich meinen ATmega 644 vom letzten Jahr mal wieder rausgekramt und
mich der für mich unverständlichen Anleitung von Atmel durch das
Datenblatt erneut zugewandt.
Ich wollt eig nur mal schaun, ob ich das mit ein paar Handgriffen sofort
hin bekomme, doch hat mein Netzteil das 1 Jahr Lagerung mit einem
Raucher kritisiert.
Da ich beim Manual zu 50% nur Bahnhof verstehe und mal hier mal da was
von einem Prescaler lese, doch alle Happen verstreut sind, ist doch vll.
was falsch.
Ich nutze nicht meinen externen 16MHz Oszi, da ich keine hohen
Frequenzen brauche. Somit läuft der @ default bei 1MHz.
Ich möchte ein PWM von 100Hz generieren. Soll dann damit ein LED Treiber
gespeist werden. Doch wie setzte ich den Timer 0 durch einen Prescaler
von 1MHz auf 100Hz?
Laut Handbuch gilt die Formel: f_PWM = f_clock / (N*256)
Doch wie setze ich N? Wird das N durch CS0_2:0 aus dem Reg. TCCR0B
gesetzt?
Bitte korrigiert mich, wenn in Folgendem Fehler sind ;)
So wie ich das sehe muss ich den "Fast PWM Mode" wählen.
Für den Timer 0 (TCNT0) bedeutet das also OCR0A und OCR0B als Output
Comp. Reg.
So wie ich das lese gibt es hier die 2 Reg. weil der Timer0 2 PWM
Outputs befeuern kann. Wenn ich also nur den einen Output brauche,
reicht mit zBsp der OCR0A?
Die obere Grenze des Counters wird mit WGM2_2:0 definiert, was jetzt
hier dann bei mir über das Reg OCR0A definiert werden soll. Also muss
bei mir WGM2_2:0 = 7b sein?
Ohne das der Timer auf 100Hz läuft würde das wohl zunächst so
aussschauen:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
// Register definieren
5
6
TCCR0A = (1<<WGM00) | (1<<WGM01)| (1<<COM0A1);
7
TCCR0B = (1<<WGM02) | (1<<CS01) | (1<<CS00);
8
9
// - ich will 100Hz -> CS0_2:0 = 7b -> ~1kH -> zuviel :((
Hallo Michael,
also ich hab' noch kein PWM verwendet, aber Timer und Prescaler.
Also zum PWM gibt es in meinem Verständnis 2 Schritte:
- mit dem Prescaler teilst Du die Prozessorfrequenz herunter um dann mit
einem 8- oder 16 Bit Counter arbeiten zu können
- danach musst Du für's PWM einerseits über einen Counter die genaue
Frequenz definieren, das wäre TCNT0, das bestimmt dann den gesamten
PWM-Zyklus, d.h. die Länge desselben
- und im PWM-Zyklus brauchst Du dann den Duty Cycle, wann soll das
Signal angehen und wann wieder aus, das sind OCR0A und OCR0B
- über Register-Flags kannst Du dann setzen, ob zum OCR0A und OCR0B
bestimmte Output-Ports geschaltet werden, damit das Signal auch aus dem
AVR rauskommt
- hier gibt's bestimmte Modi, ob nur Sägezahn-mässig (in bezug auf den
Counter) geschaltet wird, also Hochzählen und dann auf "0" (Fast PWM)
oder ob der Counter dann bei Max wieder runterzählt (Normal PWM)
Ansonsten sind die die WGM02 etc. nur Flags, d.h. gesetzte oder nicht
gesetzte Bits, keine Counter-Werte.
Beispiel für das Ganze:
- Dein Prozessortakt ist 1 Mhz
- Du möchtest auf 100 Hz als Basisfrequenz für PWM kommen
- welchen Teiler würdest Du benötigen?
1.000.000 / 100 = 10.000
das ist ein 16-bit-Wert, der Timer0 ist aber 8 Bit, also musst Du per
Prescaler runterteilen, schauen wir, ob Faktor 8 reicht, also:
TCCR0B |= _BV( CS01 ) (unabhängig davon ob Du hier noch andere
Bits brauchst)
dann kommst also raus bei:
1.000.000 / 8 = 125.000 als Basisfrequenz
jetzt kommt der TCNT0:
125.000 / 100 = 1.250 das ist immer noch 16 Bit, also noch eins
mehr,
den Teiler 64, also:
TCCR0B |= _BV( CS00) | _BV ( CS01 )
und die Basisfrequenz ist dann:
1.000.000 / 64 = 15.625,
wenn wir das nun mit dem TCNT0 weiter teilen wollen, brauchen wir
folgenden Wert für TCNT0:
15625 / 100 = 156 das ist ein 8-bit wert und funktioniert
Jetzt bestimmen innerhalb des Zyklus von 156 Takten die OCR0A und OCR0B
wann das Signal an und wieder ausgeht.
Soweit ich das verstehe, wie gesagt noch nicht gemacht, aber schon
genügend mit Prescaler und TCNT0 etc. gearbeitet, nur PWM (also OCR0A
und OCR0B) noch nicht verwendet.
VG,
Konrad
Halt, falsch, da hattest Du Recht, dass oder Ocr0a und b für 2 Outputs
sind. Für den Duty Cycle von A braucht man per Ocr0a nur den Punkt
setzen, wo das Signal gesetzt wird und bei 0 wird es wieder
zurückgesetzt.
Und dann gibt Fast Pwm und normal pwm ob der tcnt0 beim erreichen von
ocr0a auf Null gesetzt wird oder wieder runter zählt.
Das weiß ich jetzt aber wirklich nicht mehr, das ist jetzt geraten ohne
das Datasheet nochmal zu lesen.
Michael M. schrieb:> Ich möchte ein PWM von 100Hz generieren.
Warum genau 100Hz?
> Soll dann damit ein LED Treiber> gespeist werden.
Den LED ist das doch völlig wurscht, ob das 100Hz oder auch mehr sind.
Für dich als Mensch darf die PWM Frequenz nicht zu gering sein, weil es
dann zu flimmern beginnt. Ist die Frequenz aber höher, dann stört das
keinen.
> Doch wie setzte ich den Timer 0 durch einen Prescaler> von 1MHz auf 100Hz?
gar nicht.
> So wie ich das sehe muss ich den "Fast PWM Mode" wählen.
Aus deinen Betrachtungen hast du schon gesehen: nur mit der voegegebenen
PWM und nur dem Vorteiler kriegst du keine exakten 100Hz.
2 Möglichkeiten
* du akzeptierst, dass du die 100Hz nicht exakt erreichst und nimmst
einfach die nächst höhere Frequenz, die sich aus Takt und geeignetem
Vorteiler ergibt
* oder aber du benutzt nicht den Fast-PWM Modus, der immer bis zu einer
vorgegebenen Bitzahl arbeitet, sondern suchst dir einen der beiden
Fast-PWM Modi aus, bei denen du in einem Register die Zählgrenze
vorgeben kannst, wenn dein µC dies erlaubt
Da es sich bei LEDs um nichts frequenzkritisches handelt, würde ich den
ersten Weg gehen. Aber die Entscheidung liegt bei dir.
> Für den Timer 0 (TCNT0) bedeutet das also OCR0A und OCR0B als Output> Comp. Reg.>> So wie ich das lese gibt es hier die 2 Reg. weil der Timer0 2 PWM> Outputs befeuern kann.
Jein. Das ist eine Folge. Dieser Timer hat halt einfach 2 Compare Match
Register.
> Wenn ich also nur den einen Output brauche,> reicht mit zBsp der OCR0A?
Zum Beispiel
> Die obere Grenze des Counters wird mit WGM2_2:0 definiert, was jetzt> hier dann bei mir über das Reg OCR0A definiert werden soll. Also muss> bei mir WGM2_2:0 = 7b sein?
Im Datenblatt, beim Timer unter "Register Summary" gibt es eine schöne
Tabelle mit allen Modi, die der Timer kann. Da suchst du dir die
Belegung der WGM Bits raus für den Modus, den du haben willst.
> Ohne das der Timer auf 100Hz läuft würde das wohl zunächst so> aussschauen:> #include <avr/io.h>> #include <avr/interrupt.h>>> // Register definieren>> TCCR0A = (1<<WGM00) | (1<<WGM01)| (1<<COM0A1);> TCCR0B = (1<<WGM02) | (1<<CS01) | (1<<CS00);>> // - ich will 100Hz -> CS0_2:0 = 7b -> ~1kH -> zuviel :((
Wieso der :((
Für LEDS ist das doch völlig wurscht, ob 1kHz oder 100Hz
> // OC0A als PWM-Port definieren>> DDRB |= (1 << PB3);>> OCR0A = 128; //50% PWM Bsp.>>> int main (void)>> while( 1 ) {}
Alles schön und gut. Aber das ist kein C Programm. Das ist eine
Anhäufung von "Worten", die so auch in C gibt, aber C-Programme haben
auch sowas wie eine Grammatik an die du dich halten musst.
Konrad G. schrieb:> Beispiel für das Ganze:> - Dein Prozessortakt ist 1 Mhz> - Du möchtest auf 100 Hz als Basisfrequenz für PWM kommen> - welchen Teiler würdest Du benötigen?> 1.000.000 / 100 = 10.000> das ist ein 16-bit-Wert, der Timer0 ist aber 8 Bit, also musst Du per> Prescaler runterteilen, schauen wir, ob Faktor 8 reicht, also:> TCCR0B |= _BV( CS01 ) (unabhängig davon ob Du hier noch andere> Bits brauchst)> dann kommst also raus bei:> 1.000.000 / 8 = 125.000 als Basisfrequenz> jetzt kommt der TCNT0:> 125.000 / 100 = 1.250 das ist immer noch 16 Bit, also noch eins> mehr,> den Teiler 64, also:> TCCR0B |= _BV( CS00) | _BV ( CS01 )> und die Basisfrequenz ist dann:> 1.000.000 / 64 = 15.625,
Ohne das jetzt nachgerechnet zu haben: Bis hier her ist die
Grundüberlegung in Ordnung.
> wenn wir das nun mit dem TCNT0 weiter teilen wollen, brauchen wir> folgenden Wert für TCNT0:> 15625 / 100 = 156 das ist ein 8-bit wert und funktioniert
Hier hast du einen entscheidenden Fehler. Du kannst nicht mit TCNT0
weiterteilen. TCNT0 ist das Register in dem gezählt wird. Das ist der
eigentliche Timer-Kern.
Es gibt aber Timer-Modi, bei denen dieses TCNT0 Register zb mit dem
OCR0A verglichen wird und wenn beide übereinstimmen, dann wird TCNT0
wieder auf 0 gesetzt. Man nennt das den CTC-Modus.
Und. Dasselbe gibt es auch in der PWM-Version. D.h. deine 156 kommen zb
ins OCR0A Register, so dass sich in TCNT0 wieder die Zählesequenz 0, 1,
2, ... , 154, 155, 0, 1, 2, ... ergibt.
Nachteil: Damit fällt das OCR0A Register zur Erzeugung der PWM aus und
man kann nur noch mit OCR0B eine PWM erzeugen.
FAQ: Timer
Da werden die Zusammenhänge vielleicht etwas klarer.
Denkt nicht zu komplizert. Das ganze ist in Wirklichkeit extrem einfach.
Muss es auch sein, wenn es in Silizium gegossen werden kann :-)
Karl Heinz Buchegger schrieb:> Michael M. schrieb:> Alles schön und gut. Aber das ist kein C Programm. Das ist eine> Anhäufung von "Worten", die so auch in C gibt, aber C-Programme haben> auch sowas wie eine Grammatik an die du dich halten musst.
Scheint ja so, als wäre meine Überlegung aus dem Manual zum µC mal
richtig.
Also kann ich wirklich nur den Takt mit einem Prescaler von max. 1024
herabsetzen, was 1kHz entspricht.
Ich habe noch gelernt, dass LEDs frequenzkritische Bauteile sind und mit
zunehmender Frequenz schneller altern. Gerade bei Hochleistungs-LEDs.
Ob da jetzt schon meine 1W LEDs dazu zählen weiß ich nicht.
Was meinst du jetzt wegen C?
Versteht der Compiler das nicht?
Hi
>Also kann ich wirklich nur den Takt mit einem Prescaler von max. 1024>herabsetzen, was 1kHz entspricht.
Nein, da kommt eine PWM-Frequenz von ca. 3,8 Hz raus (1MHz/1024/256).
Einen Wert in der Nähe von 100 bekommst du z.B. wenn du den Controller
mit 8MHz (CKDIV8-Fuse löschen) laufen lässt und einen Prescaler von 256
nimmst. Das gibt dann 122 Hz.
MfG Spess
Hi
>Wo kommen denn jetzt die 256 her? Hab ich nirgends eingestellt.
Das ist ein 8-Bit-Timer. Und der braucht für eine PWM-Periode genau 256
Takte vom Vorteiler.
> Da ich kein Oszi hab kann ich das leider nicht prüfen.
3,8 Hz sieht man deutlich als Blinken.
MfG Spess
Ah ja verdammt stimmt. Na dann passt das ja doch ganz nett :).
Jetzt muss ich nur noch schaun, dass der Syntax stimmt und dann wirds
mal getestet. Hol heute noch ein neues Netzteil.
> Hier hast du einen entscheidenden Fehler. Du kannst nicht mit TCNT0> weiterteilen. TCNT0 ist das Register in dem gezählt wird. Das ist der> eigentliche Timer-Kern.>> Es gibt aber Timer-Modi, bei denen dieses TCNT0 Register zb mit dem> OCR0A verglichen wird und wenn beide übereinstimmen, dann wird TCNT0> wieder auf 0 gesetzt. Man nennt das den CTC-Modus.
Hast Recht! Bisher hab ich nur CTC verwendet...