Guten morgen Jungs!
Ich bin recht neu in der Mikrocontroller-Welt und habe kleine
Verständnissprobleme. Desweiteren programmiere ich in der Sprache C und
arbeite mit dem CoderVisionAVR, dem STK500 Board und einem ATmega8.
Hierbei geht es um die beiden Timer 1 und Timer 2 die beide die PWM
Funktion haben.
Ich habe mich durch das Datenblatt gewurschtelt und versucht zu
verstehen.
Die Register zu schreiben ist mir denk ich ganz gut gelungen, mein
Problem liegt viel mehr bei dem Verständniss des mathematischen bzw.
physischen Teil.
Timer 2 ist relativ einfach wobei mir da der Prescaler Probleme
bereitet, da ich nicht verstehe, wie ich zu rechnen habe um auf meine
gewünschten 10Hz zu kommen. Ich zeig euch mal was ich bis jetzt mit den
Timer 2 erreicht habe.
1
// Timer/Counter 2 initialization
2
// Clock source: System Clock
3
// Clock value: 1000,000 kHz
4
// Mode: Fast PWM top=FFh
5
// OC2 output: Non-Inverted PWM
6
ASSR=0x00;
7
TCCR2=0x6F;
8
TCNT2=0x3C;
9
OCR2=0x50;
TCCR2=0b01111111 heißt:
- Bit 7: FOC2 aus
- Bit 6 und 3: Fast PWM Mode
- Bit 5 und 4: OC2 setzen bei Compare Match, OC2 bei Top löschen (die
Einstellung irritiert mich etwas, da ich mit dem Overflow Interrupt
arbeiten möchte)
- Bit 2 und 1 und 0: Prescaler 1024
Mein Problem ist nun, dass ich die Register TCNT2 und ORC2 nicht richtig
setzte. Ich vertsehe nicht wie ich die Bits setzten muss, um meine
gewünschten 10Hz zu erlangen.
Ich weiß schonmal das TCNT2 für die feste Frequenz zuständig ist und
OCR2 für die Pulsweiter. Nur wie errechne ich das so, dass ich 10Hz
bekomme?
Kommen wir zu Timer 1, bei dem verstehe ich noch viel weniger als bei
Timer 2. Ich steige durch diese ganzen Lows und Highs nicht mehr durch
(OCR1AL/OCR1AH, TCNT1H/TCNT1L usw.).
Aber ich hoffe das ich zumindest die Register TCCR1A und TCCR1B richtig
geschrieben habe, auch hier soll mit einem Interrupt gearbeitet werden,
deswegen bringen mich diese ganzen Compare Matchs total durcheinander.
Aber auch hier möchte ich euch das erarbeitete nicht vorenthalten.
1
// Timer/Counter 1 initialization
2
// Clock source: System Clock
3
// Clock value: 1000,000 kHz
4
// Mode: Fast PWM top=00FFh
5
// OC1A output: Non-Inv.
6
// OC1B output: Non-Inv.
7
// Noise Canceler: Off
8
// Input Capture on Falling Edge
9
// Timer 1 Overflow Interrupt: On
10
// Input Capture Interrupt: Off
11
// Compare A Match Interrupt: Off
12
// Compare B Match Interrupt: Off
13
TCCR1A=0xA1;
14
TCCR1B=0x09;
15
TCNT1H=0x00;
16
TCNT1L=0x3C;
17
ICR1H=0x00;
18
ICR1L=0x00;
19
OCR1AH=0x00;
20
OCR1AL=0xFE;
21
OCR1BH=0x00;
22
OCR1BL=0x7A;
TCCR1A=0b10100001 heißt:
- Bit 7 und 6: OC1A/OC1B bei Compare Match löschen und auf TOP setzten
- Bit 5 und 4: OC1A/OC1B bei Compare Match löschen und auf TOP setzten
- Bit 3 und 2: Kein FOC1A und FOC1B
- Bit 1 und 0: Fast PWM Mode
TCCR1B=0b00001001 heißt:
- Bit 7: Input Capture Noise Canceler aus
- Bit 6: Input Capture Edge aus
- Bit 5: Reserve Bit
- Bit 4 und 3: Fast PWM Mode
- Bit 1 und 0: Prescaler aus
Ich verstehe nun nicht in welchen Verhältnissen die Register
TCNT1H/TCNT1L, ICR1H/ICR1L, OCR1AH/OCR1AL, OCR1BH/OCR1BL zueinander
stehen.
Ich hab zwar verstanden, dass ich mit TCNT1H/TCNT1L glaub ich die
Frequenz vorgebe und OCR1AH/OCR1AL seinen Wert mit dem Wert von
TCNT1H/TCNT1L abgleicht, aber irgendwie komm ich mit den anderen beiden
Registern umsoweniger klar.
Auch bei diesem Timer hätte ich gern eine Taktfrequenz von 10Hz, kann
man sich das irgendwie errechnen oder fehlt mir in dem Punkt das
logische Denken?
Ich würde mich über eure Unterstützung sehr freuen!
Viele liebe Grüße
Tanja M.
Hi Tanja,
bevor dir hier irgenjemand helfen kann solltest du erst mal erklären was
du eigentlich willst.
Benötigdt du ein bestimmtes PWM Signal ?
Was ist mit diesen 10 Hz, wofür?
........
Dann kann man erst mal die prinzipielle Idee für eine Umsetzung
diskutieren und erst dann wird es Zeit für Codeschnipsel.
Ich glaube im Augenblick besteht wirklich ein Logik-Problem
Gruß
Stefan
Du hast anscheinend bei beiden Timern eine 8-Bit-Fast-PWM eingestellt.
Die hat einen festen TOP-Wert (nämlich 0xFF). Da es aber der TOP-Wert
ist, der die Frequenz des Signals bestimmt (in Zusammenarbeit mit dem
Prescaler) wirst Du vermutlich nie auf exakt 10 Hz kommen. Bei einem
8-Bit-Timer sind Frequenzen in dem Bereich sowieso nur bei relativ
geringen Oszillator-Takten möglich (10*256*1024 = ca. 2,6 MHz). Da Du
nicht schreibst, mit welchem Takt Du den Controller betreibst, kann ich
nicht sagen, ob das in Deinem Fall überhaupt funktionieren kann. Um
allerdings eine bestimmte Frequenz möglichst genau einstellen zu können,
brauchst Du einen PWM-Modus, der es gestattet, den TOP-Wert selbst
festzulegen. Diese Möglichkeit gibt es aber nur bei Timer 1.
Außerdem solltest Du Dir vielleicht das AVR-GCC-Tutorial mal näher
ansehen. Da stehen die Grundfunktionen drin. Und der Code Generation
Wizard von CodeVision ist zwar ne tolle Sache, wenn man mal eben schnell
einen Programmrumpf erzeugen will, aber erstens sind die erzeugten
Codeschnipsel sehr schwer zu warten (Wenn was geändert werden soll, muss
auch der Kommentar entsprechend angepasst werden, die Darstellung der
Bitzustände in Hexadezimalzahlen ist ohne Datenblattlektüre selbst für
erfahrenere Programmierer unlesbar) und zweitens sorgt er dafür, dass
keiner sich noch wirklich im Datenblatt (oder im oben erwähnten
Tutorial) die entsprechenden Abschnitte gründlich genug durchliest, um
die Zusammenhänge zu verstehen.
BTW:
> TCCR2=0x6F;> ...> TCCR2=0b01111111 heißt:
Da ist schon der erste Widerspruch: 0x6F entspricht 0b01101111. Bei Dir
ist da eine 1 zuviel und eine 0 zu wenig.
Oh entschuldigung. Natürlich sollte ich vielleicht erstmal den
Sachverhalt erklären.
Es geht um volgendes. Ich möchte mit einem Timer eine 10Hz Frequenz
erzeugen und per PWM die Pulsweite ändern.
Dazu kommt noch ein AD Wandler den ich mit dem PWM Signal zusammenführen
möchte, umd über einen 10-Gang-Poti die Pulsweite zwischen 0 und 200µs
einzustellen.
Das ganze soll dann letztendlich einen MOSFET ansteuern.
Dazu möchte ich per LED die Frequenz zeigen nur die Pulsweite soll da
dann x10 sein.
Ich hoffe ihr versteht in etwa was ich vorhabe.
Das ganze hat kein bestimmtes Ziel, es soll nur einen Lerneffekt geben.
Vielen Dank und liebe Grüße
Tanja
Ich sehe grundsätzlich keine Probleme bei der Realisierung. Allerdings
ändert das nichts an der Tatsache, dass gerade die Grundlagen in oben
erwähntem AVR-GCC-Tutorial (und eben auch im Datenblatt) beschrieben
sind und es wenig Sinn macht, alles noch mal zu schreiben. Außerdem
stehen im AVR-Tutorial, genauer im Abschnitt AVR-Tutorial: Timer
und im Artikel AVR PWM einige für Einsteiger wissenswerte
Informationen (Auch wenn letztere sich nicht auf die Programmierung in C
beziehen). Schau erst mal da nach und versuche, die Basics zu verstehen.
Entweder kommst Du dann selber drauf, wie Du das alles zu einem Projekt
zusammenschustern kannst, oder Du meldest Dich hier noch mal, wenn
offene Fragen bestehen.
Hi Johannes.
Ich hab mich sehr bemüht durch das Datenblatt zum Ziel zu kommen. Ich
hab mir das Tutorial schon durchgelesen nur selbst da verstehe ich das
nicht so wirklich. Deshalb wende ich mich ja nun auch direkt an euch,
damit man mir das vielleicht nochmal spezifisch erklärt.
Mit welchem Takt ich den Controller betreibe stand ja im Code als
Kommentar.
// Clock value: 1000,000 kHz
Deshalb dachte ich das ich das nicht nochmal erwähnen muss, tut mir
leid.
Also ich betreibe den Controller mit seinen 1MH.
Das ist richtig, CideVision hat ein Wizardprogramm der dir die
Grundeinstellungen abnimmt, aber wenn ich es nicht selbst verstehen
wollen würde, was der da jetzt genau macht, würd ich mir ja nicht das
Datenblatt zur Hand nehmen und jedes Bit was gesetzt wurde selber
nochmal durchgehen und mit dem vergleichen was ich gerne möchte.
Hab ja nicht umsonst die Register für mich nochmal in Bits geschrieben.
> TCCR2=0x6F;> ...> TCCR2=0b01111111 heißt:
Ohja das war ein Tippfehler von mir, du hast natürlich vollkommen recht,
0x6F ist nicht 0b01111111 sondern 0b01101111, danke!
Liebe Grüße
Tanja
hallo tanja ...
wie möchtest du den pwm denn einstellen ?
in % (0-100)
von 0-255
oder von 0-65535
ich frage nur, weil du den pwm ja auch in software machen könntest ...
schliesslich ist 10Hz ja nicht gerade schnell ....
nebenbei könntest du dann ja auch mehr als 2 pwm laufen lassen ...
lg
peter
kannst mir gerne mailen: peter (at) cyberbonsai (dot) de
Hallo Peter.
Also am besten wäre natürlich 0-100% und ob nun 0-255 oder 0-65536 ist
nicht so wichtig, mit 0-65536 kann man sicherlich feiner einstellen aber
die 0-255 reichen auch vollkommen aus. Wichtig ist halt auch, dass wenn
das Poti ganz am Anschlag ist, also höchsten Widerstandswert, soll das
Signal durchgehen 0 sein und andersrum dann durchgehend 1 also 5V.
Danke für deine Angebot bezüglich des mailens.
Liebe Grüße
Tanja
Entschuldige Johannes, aber ich erkenne auf deinen Bildern irgendwie
nichts, dass sind so Punkte und Striche, sehr pixelig....
Was wolltest du mir damit denn sagen?
Richtig ich wollte gern TOP selber bestimmen können. Und beim rechnen
habe ich nicht einen Wert von 3,9kH bekommen sondern 3,8Hz. Mein
Rechnung sieht so aus:
0,000001µs (meine 1MH) * 256 (die Schritte die der Timer dann zählt) *
1024 (Prescaler) = 0.262144
1 / 0.262144 = 3,81Hz.
Wenn ich da jetzt nicht falsch gerechnet habe oder einen Denkfehler
habe.
Wie kommst du denn auf 3,9kH?
Liebe Grüße
Tanja
Bei 1MHz Taktfrequenz hat man in 10ms 10.000 Taktzyklen.
Wenn der Timer 1 verwendet wird mit 16bit (65535) kann man das direkt
zählen (ohne Prescale).
Mein Ansatz wäre folgender:
1) TCNT1H/L auf 65.535-10.000 setzen, damit erfolgt ein Überlauf des
Timers nach 10ms
2) OCR1H/L (on compare match) auf einen Wert TCNT1 + X setzen,
mit 0 < X < 10.000 . Damit wird die Pulslänge definiert.
3) Aktiviern des Interrupts für TC1 overflow und einen zugehörigen
Interrupthandler schreiben, der die Aktionen in 1) und 2) durchführt.
Das ganze ist schnell und präzise.
Gruß
Stefan
Danke Stefan das klingt schon sher plausibel, nur jetzt bekomme ich das
Problem was ich wärend des ganze Projektes hatte. DU schreibst TCNT1H/L
auf 65.535-10.000 setzten. Soll das jetzt heißen ich solle TCNT1H=65535
und TCNT1L=10000 setzten? So hab ich das nämlich verstenden.... Kann
sein das ich das falsch verstanden habe.
Punkt 2 klingt sehr logisch für mich und das Verständnis kommt langsam
auf.
Nur OCR1H/L ist ja jeweils ein eigenes Register und muss ich Wetre bei
OCR1A oder OCR1B setzten? Oder ist es egal?
>OCR1H/L (on compare match) auf einen Wert TCNT1 + X setzen,>mit 0 < X < 10.000 . Damit wird die Pulslänge definiert.
Den Teil müsste ich dann im Interrupt definieren richtig? Was müsste ich
OCR1(A? B?)H und OCR1(A? B?)L denn zuweisen? Wenn ich im Interrupt nur
TCNT1 schreibe, weiß der Microkontroller dann was ich meine?
Danke für deine Hilfe.
Liebe Grüße
Tanja
Kleine Ergenzung. Teil 1 hab ich verstanden. 65535-10000 ergibt 55535
und das ist in Hex D8EF, dass D8 muss ich dann bei TCNT1H schreiben und
das EF muss ich bei TCNT1L reinschreiben, richtig so?
Liebe Grüße
Tanja
Tanja M. wrote:
> Danke Stefan das klingt schon sher plausibel, nur jetzt bekomme ich das> Problem was ich wärend des ganze Projektes hatte. DU schreibst TCNT1H/L> auf 65.535-10.000 setzten. Soll das jetzt heißen ich solle TCNT1H=65535> und TCNT1L=10000 setzten? So hab ich das nämlich verstenden.... Kann> sein das ich das falsch verstanden habe.
Ja das hast du falsch verstanden.
Mit TCNT1H/L ist das Registerpärchen TCNT1H und TCNT1L in seiner
Gesamtheit gemeint, den nur beide zusammen bilden den Timer.
> TCNT1H=65535
Wie soll das gehen? TCNT1H ist, wie alle Register, ein 8 Bit Register.
Da gehen maximal Zahlen bis 255 rein.
Aber: Wenn man die beiden physikalischen Register TCNT1H und TCNT1L
zusammen als ein logisches 16 Bit Register auffasst, dann ist das ein
16 Bit Register, in dem man bis 65535 zählen kann. Eine 16 Bit
Zahl besteht aus 2 Bytes, das Highbyte davon ist im Register TCNT1H
abzulegen, das Lowbyte davon im Register TCNT1L
65535 - 10000 = 55535
Als Hexzahl ausgedrückt: D8EF
Also kommt 0xD8 in das Register TCNT1H
und 0xEF in das Register TCNT1L
oder in Code
TCNT1H = ( 65535 - 10000 ) >> 8; // das Highbyte berechnen
TCNT1L = ( 65535 - 10000 ); // das Lowbyte berechnen
manche Compiler erlauben auch, das man das einfacher schreiben
kann:
TCNT1 = 65535 - 10000;
die haben sich dann einen seziellen Namen, TCNT1, für das
Registerpärchen TCNT1H und TCNT1L eingeführt und wissen, dass
sie bei der Zuweisung einer 16 Bit Zahl an dieses fiktive Register
eine Zerlegung in Highbyte und Lowbyte machen müssen und in die
einzelnen 8Bit Register schreiben müssen.
Tanja M. wrote:
> Entschuldige Johannes, aber ich erkenne auf deinen Bildern irgendwie> nichts, dass sind so Punkte und Striche, sehr pixelig....
Ach, Du benutzt vermutlich Internet Explorer. Da soll es Probleme mit
der Darstellung der Formeln geben. Was da stehen sollte (und in anderen
Browsern auch steht) ist
f_PWM = f_CPU / (Prescaler * (TOP + 1)) (obere Formel)
und das Beispiel mit den Zahlenwerten aus Deinem Code:
1 MHz / (1 * (255 + 1)) = 3906,25 Hz
Stefans Vorschlag ist bis auf das Vorladen von TCNT1 für die Erzeugung
einzelner Impulse auch brauchbar. Allerdings ist es bei den AVRs
sinnvoller, dafür den CTC-Modus und den entsprechenden Compare-Interrupt
zu benutzen.
Ja Johannes, bemutze den Internet Explorer, aber danke für das
übersetzten der Zeichen.
Okay soweit so gut, die TCCR1A und TCCR1B Register sind geschrieben,
TCNT1H und TCNT1L sind auch gesetzt.
>Stefans Vorschlag ist bis auf das Vorladen von TCNT1 für die Erzeugung>einzelner Impulse auch brauchbar.
Was spricht dagegen? Ist es über den CTC weniger aufwendig oder besser
zu verstehen? Also wärst du der Meinung ich solle statt Fast PWM Mode
den CTC Mode nehmen oder?
Wenn ich das Datenblatt richtig verstanden habe, dann löscht der CTC das
register auf 0 wenn TCNT1 und OCR1 einen Compare Match haben richtig?
Der Wert in OCR1 ist dann der Wert der TOP vorgibt beim Zähler oder?
Fragen über Fragen...
Danke für eure tolle Unterstüzung!
Liebe Grüße
Tanja
Ähm kleine Ergenzung von mir, bei dem CTC Mode, der soll doch eine
komische Art zu zählen haben oder?
Ahja im übrigen hab ich verstanden, dass der CTC die Werte aus TCNT1 und
OCR1 vergleicht und wenn die übereinstimmen dann setzt der TCNT1 auf 0
und fängt dann wieder von vorne an zu zählen, aber wie gesagt, der soll
doch ne seltsame Art zu zählen haben...
Liebe Grüße
Tanja
Tanja M. wrote:
> Ahja im übrigen hab ich verstanden, dass der CTC die Werte aus TCNT1 und> OCR1 vergleicht und wenn die übereinstimmen dann setzt der TCNT1 auf 0> und fängt dann wieder von vorne an zu zählen, aber wie gesagt, der soll> doch ne seltsame Art zu zählen haben...
Überhaupt nicht. Wie kommst du darauf?
Wenn du deine 10 ms besonders exakt einstellen möchtest, dann
ist das das Mittel zur Wahl. Vor allen Dingen, weil man dann
auch auf das 'seltsame' Vorladen des Timers verzichten kann :-)
Tanja M. wrote:
> Ähm kleine Ergenzung von mir, bei dem CTC Mode, der soll doch eine> komische Art zu zählen haben oder?>> Ahja im übrigen hab ich verstanden, dass der CTC die Werte aus TCNT1 und> OCR1 vergleicht und wenn die übereinstimmen dann setzt der TCNT1 auf 0> und fängt dann wieder von vorne an zu zählen, aber wie gesagt, der soll> doch ne seltsame Art zu zählen haben...
Nö, eigentlich ist daran nichts seltsam. Der CTC-Modus arbeitet nur
"andersrum" als die Variante mit dem Zähler-Vorladen. Im CTC-Modus fängt
der Zähler bei Null an zu zählen und zählt (wie Du ja schon selbst
vermutet hast), bis er den eingestellten TOP-Wert erreicht. Dann wird
der Zähler mit dem jeweils folgenden Timertakt(*) zurückgesetzt und das
Spielchen beginnt von vorn, ohne dass irgendwas nachgeladen werden muss.
Außerdem gibt man im CTC-Modus als TOP-Wert quasi direkt die gewünschte
Anzahl Takte bis zum Ereignis (Interrupt und/oder Umschalten eines
Portpins) vor und nicht "Überlaufwert minus Anzahl der Takte". Ich halte
daher eher die andere Variante für "seltsamer", weil man da den Zähler
auf einen bestimmten Wert setzen muss, damit er nach einer bestimmten
Anzahl Takte überläuft. Das hat besonders bei präzisen Timing-Aufgaben
den großen Nachteil, dass das Nachladen des Timers nie direkt beim
Überlauf passiert, sondern erst dann, wenn das Programm auf den Überlauf
reagiert hat. Und das kann u.U. dauern. Außerdem kann man mit dem
Overflow-Ereignis keinen I/O-Pin hardwaremäßig umschalten. Das geht nur
über die Compare-Einheiten.
(*)Da das Rücksetzen des Timers erst beim jeweils folgenden Timertakt
geschieht, muss der TOP-Wert um eins niedriger angegeben werden als die
gewünschte Anzahl der Timertakte.
Hallo Karl Heinz.
Wie ich darauf komme? Das steht hier in dem Tutorial.
Hmm was meinst du mit seltsames Vorladen des Timers?
Welches Register ist denn für das Vorladen zuständig? Ich dachte das
TCNT1 Register.
Was hab ich bei dem CTC Mode zu beachten und was muss ich letztendlich
an den schon geschriebenen Registern ändern?
Um das ganze in den CTC Mode umzustellen müsste ich im TCCR1B 2 Bits
tauschen, also wäre TCCR1B statt 0b00001001 jetzt 0b00010001 richtig? In
Hex wäre das dann 0x11 hoffe so stimmts.
Auch bei dir möchte ich mich für deine Hilfe bedanken.
Liebe Grüße
Tanja
Tanja M. wrote:
> Hallo Karl Heinz.>> Wie ich darauf komme? Das steht hier in dem Tutorial.
?
Hast du da eine Referenz drauf. Das würde ich mir gerne ansehen.
>> Hmm was meinst du mit seltsames Vorladen des Timers?> Welches Register ist denn für das Vorladen zuständig? Ich dachte das> TCNT1 Register.
Nein.
TCNT1 ist das Timer Zählregister. In diesem Register, ähm,
zählt der Timer so vor sich hin.
> Was hab ich bei dem CTC Mode zu beachten und was muss ich letztendlich> an den schon geschriebenen Registern ändern?
Da gibts nicht viel zu beachten.
Du stellst die Timerzählrate mit dem Vorteiler ein, wie gehabt.
Aktivierst den CTC Modus und stellst die gewünschte Endzahl - 1
im OCR1A Register ein und schon zählt der Timer nur noch von 0
bis zu dieser Zahl. Einfacher gehts nicht.
>> Um das ganze in den CTC Mode umzustellen müsste ich im TCCR1B 2 Bits> tauschen, also wäre TCCR1B statt 0b00001001 jetzt 0b00010001 richtig? In> Hex wäre das dann 0x11 hoffe so stimmts.
Gewöhn dir das Schreiben von Hex-Zahlen an dieser Stelle gleich
wieder ab. Das Bitgepfriemel ist ja nicht auszuhalten.
Um den CTC Modus im Timer1 zu aktivieren, müssen, laut Datenblatt
die Bits WGM12 und CS10 (für einen Vorteiler von 1) gesetzt werden.
Also schreiben wir das auch so:
TCCR1B = ( 1 << WGM12 ) | ( 1 << CS10 );
Welche Bedeutung das setzen der einzelnen Bits hat, muss man immer
noch im Datenblatt nachsehen (so man sie nicht auswendig kann). Aber
zumindest das stumpfsinnige und fehlerträchtige Im-Kopf-die-Hex-Zahl-
in-Bits-aufdröseln-und-abzählen-welche-Bitnummer-das-1-Bit-hat
fällt damit weg.
Johannes deine Erklärung ist wirklich sehr gut und hilfreich! Danke.
Also wenn ich das richtig verstanden habe, muss ich dem Timer nicht mehr
sagen "zähle bis 55535 (65535-10000)" sondern muss dem Timer nur sagen
das er beim 10000 Takt den Interrupt auslösen soll, ist das soweit
richtig?
Da du angemerkt hast, dass er einen Takt weniger brauch für das
Zurücksetzen, müsste ich meinem Timer den Wert 9999 zuweisen oder?
Hmmm der CTC arbeitet also auch nur mit den Werten in den Registern
TCNT1 und OCR1 oder?
Was muss ich dem OCR1 Register eigentlich sagen, was er zu tun hat? Löst
er den Interrupt aus?
Liebe Grüße
Tanja
Kleine Rechnung:
Du willst einen Pin auf High-Pegel setzen und nach jeweils 125
Timertakten umschalten (toggeln).
Variante 1: TIMER-VORLADEN
0.) Timer konfigurieren (Prescaler einstellen, Overflow-Interrupt
aktivieren)
1.) Pin auf High setzen
2.) TCNT1 mit 65536-125 (65411) laden
3.) Auf Interrupt warten
4.) Im Interrupt Handler Pin toggeln
5.) Timerregister TCNT1 wieder mit 65411 laden
Bei 4.) und 5.) kann es zu den oben erwähnten Problemen kommen, weil das
Pin-Toggeln und Timerregister-Neu-Laden nicht sofort nach dem
Overflow-Ereignis geschehen.
Variante 2: CTC-MODUS
0.) Timer konfigurieren (Prescaler und CTC-Modus einstellen, Pin OC1x
toggeln bei Compare-Match, Interrupt wird für die Aufgabe an sich nicht
gebraucht)
1.) Compare-Register mit gewünschtem Wert (Taktzahl - 1) beschreiben
2.) Irgendwas anderes machen. Pin toggeln macht der Controller von jetzt
an selbst, Interrupt gibts auch nicht.
Wenn Toggel-Frequenz geändert werden soll, neuen Wert in
Compare-Register schreiben (OK, das sollte man dann schon im
Interrupt-Handler machen, damit es synchron ist)
Welche Methode einfacher und sinnvoller ist, musst Du jetzt selbst
entscheiden.
EDIT:
Das Einzige, worauf man im CTC-Modus achten muss, ist die Zuweisung
eines neuen TOP-Wertes an OCR1A. Dabei muss man eben aufpassen, dass man
OCR1A keinen Wert zuweist, der kleiner oder gleich dem aktuellen Stand
von TCNT1 ist, da in einem solchen Fall das Compare-Ereignis ausbleibt
und der Timer einmal komplett bis zum Überlauf durchrennt, bevor er
wieder das macht, was er soll. Deshalb kann man für diesen Fall den
Compare-A-Interrupt freigeben und die Aktualisierung von OCR1A im
Interrupt Handler machen. Dann kann man davon ausgehen, dass TCNT1 sich
noch nicht allzu weit von 0 entfernt hat, wenn OCR1A aktualisiert wird.
Tanja M. wrote:
> Johannes deine Erklärung ist wirklich sehr gut und hilfreich! Danke.> Also wenn ich das richtig verstanden habe, muss ich dem Timer nicht mehr> sagen "zähle bis 55535 (65535-10000)"
Das hast du ihm auch bisher nicht gesagt.
Du hast ihm gesagt: Zähle von 55535 bis 65535, denn wenn
du das tust hast du 10000 Zählvorgänge ausgeführt.
Und deswegen empfinden wir (Johannes und ich) das als etwas seltsam.
> sondern muss dem Timer nur sagen> das er beim 10000 Takt den Interrupt auslösen soll, ist das soweit> richtig?
Nicht Takt.
Der Timer soll mit seiner Taktrate von 0 bis 10000 zählen.
Und wenn er dort angelangt ist, soll er wieder bei 0 anfangen.
> Da du angemerkt hast, dass er einen Takt weniger brauch für das> Zurücksetzen, müsste ich meinem Timer den Wert 9999 zuweisen oder?
Yep.
>> Hmmm der CTC arbeitet also auch nur mit den Werten in den Registern> TCNT1 und OCR1 oder?
Yep. CTC ist nur ein Betriebsmodus des Timers.
Der Timer zählt immer im Register TCNT1. In einem gewissen
Sinne könnte man sagen: TCNT1 ist der Timer1.
Alles andere drumherum dreht sich nur um die Auswertung der
Zahl die gerade im TCNT1 ist. Bei bestimmten Zahlen (die
über andere Register einstellbar sind), können Aktionen
ablaufen.
Karl Heinz, wenn ich denn jetzt dem OCR1 Register den TOP Wert zuteile
also die 9999, dann weiß ich jetzt gar nicht mehr was ich dem TCNT1
Register sagen soll. Der TCNT1 muss doch die Frequenz vorgeben oder
nicht? und da ich doch ne Frequenz von 10Hz gern hätte müsste ich dem
doch den Wert D8EF zuteilen oder nicht?
Jetzt komme ich gazn durcheinander...
Hihi, ich rechne die Hex zahlen nicht im Kopf aus sondern hab dafür
einen Taschenrechner.
>TCCR1B = ( 1 << WGM12 ) | ( 1 << CS10 );
Diese Schreibweise erkennt mein CodeVision nicht. Das ist doch Assambler
oder?
Soll ich mir abgewöhnen in Hexzahlen zu schreiben oder in soll ich mir
abgewöhnen in Binärzahlen zu schreiben?
>Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt >sich je
nach eingestelltem Vorteiler ein etwas anderes Zählverhalten.
Das stand im Tutorial, mein fehler, der zählt nur anders und nicht
"seltsam".
Danke und liebe Grüße
Tanja
Tanja M. wrote:
> Karl Heinz, wenn ich denn jetzt dem OCR1 Register den TOP Wert zuteile> also die 9999, dann weiß ich jetzt gar nicht mehr was ich dem TCNT1> Register sagen soll.
Gar nichts.
> Der TCNT1 muss doch die Frequenz vorgeben oder> nicht?
Nein.
In TCNT1 zählt der Timer so vor sich hin.
Lass dieses Register einfach nur in Ruhe zählen.
> da ich doch ne Frequenz von 10Hz gern hätte müsste ich dem> doch den Wert D8EF zuteilen oder nicht?
Du hast es immer noch nicht. Die D8EF waren ja die 10000. Bis
zu dieser Zahl soll der Timer zählen. Das ist also die Obergrenze
und wenn diese Zahl erreicht ist, dann soll der Timer wieder bei
0 anfangen. Die D8EF (oder eben die 10000) müssen daher in das
Register, welches steuert wie weit der Timer zählen soll bevor
er wieder bei 0 anfängt. Das macht das Register OCR1
>> TCCR1B = ( 1 << WGM12 ) | ( 1 << CS10 );>> Diese Schreibweise erkennt mein CodeVision nicht. Das ist doch Assambler> oder?
Nein, das ist C.
Kann aber sein, dass die Bits bei CodeVision anders heissen.
Beim gcc heissen sie so, wie im Datenblatt.
>Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt>sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten.
Ja das stimmt schon.
Da du aber mit einem Vorteiler von 1 arbeiten kannst, braucht dich
das nicht weiter kümmern.
Okay Johannes ich wag es jetzt mal und versuche das was ich meine
verstanden zu haben umzusetzten.
Die Register müssten dann folgend aussehen:
1
TCCR1A=0xA1;
2
TCCR1B=0x11;
3
TCNT1H=0xD8;
4
TCNT1L=0xEF;
5
ICR1H=0x00;
6
ICR1L=0x00;
7
OCR1AH=0x27;
8
OCR1AL=0x0F;
9
OCR1BH=0x00;
10
OCR1BL=0x00;
In OCR1 hab ich jetzt die 9999 reingeschrieben und in TCNT1 hab ich das
so gelassen mit den Wert 55535 für die 10Hz. TCCR1B hab ich
dementsprechend natürlich auch auf den CTC Modus gestellt.
Liebe Grüße
Tanja
Tanja M. wrote:
> Karl Heinz, wenn ich denn jetzt dem OCR1 Register den TOP Wert zuteile> also die 9999, dann weiß ich jetzt gar nicht mehr was ich dem TCNT1> Register sagen soll.
Gar nichts. TCNT1 ist das Zählregister, das von der Hardware mit jedem
Timertakt (Also mit der Frequenz f_CPU/Prescaler) erhöht wird.
> Der TCNT1 muss doch die Frequenz vorgeben oder> nicht?
Nein, siehe oben. Die Frequenz gibt der Prescaler vor. Und mit dieser
Frequenz wird TCNT1 erhöht.
> und da ich doch ne Frequenz von 10Hz gern hätte müsste ich dem> doch den Wert D8EF zuteilen oder nicht?> Jetzt komme ich gazn durcheinander...
Du denkst auch immer noch falschrum. Du zählst nicht mehr von einem
Ausgangswert bis zum Überlauf, sondern (wie man es ja eigentlich auch im
wahren Leben macht) von 0 bis zu einem Vorgegebenen Wert. Oder wie
machst Du das beim Verstecken-Spielen, wenn es heißt: "Auf hundert"?
Fängst Du dann bei 912345 an zu zählen und fängst bei 912444 an zu
suchen?
> Hihi, ich rechne die Hex zahlen nicht im Kopf aus sondern hab dafür> einen Taschenrechner.
Der Compiler versteht auch dezimal...
>>TCCR1B = ( 1 << WGM12 ) | ( 1 << CS10 );>> Diese Schreibweise erkennt mein CodeVision nicht. Das ist doch Assambler> oder?
Nein, das ist C, und zwar ANSI-C. Allerdings sind die Makros für die
Bits im CodeVision anders definiert, weshalb das da u.U. nicht
funktioniert
das setzen von TCNT kannst du weglassen ... das ist einfach nur ein
register für den timer, in dem der timer selbst zählt ... das brauchst
du nicht zu beschreiben.
Hm verdammt, jetzt hab ich die Antwort von Karl Heinz nicht mehr
berücksichtigen können in meinem ersten Anlauf, also hier dann nochmal
unter der Berücksichtigung von Karl Heinz' Annmerkung wegen des Timers.
1
TCCR1A=0xA1;
2
TCCR1B=0x11;
3
TCNT1H=0x00;
4
TCNT1L=0x00;
5
ICR1H=0x00;
6
ICR1L=0x00;
7
OCR1AH=0x27;
8
OCR1AL=0x0F;
9
OCR1BH=0x00;
10
OCR1BL=0x00;
Vielen vielen Dank für eure Geduld.
Liebe Grüße
Tanja
Tanja M. wrote:
> in TCNT1 hab ich das> so gelassen mit den Wert 55535 für die 10Hz.
Nochmal: Lass das TCNT1 Register in Ruhe. Dieses Register
hat dich nicht zu interessieren!
Wenn du mir die Aufgabe gibst bis 100 zu zählen, hat es dich
ja auch nicht zu interessieren ob ich im Kopf zähle, oder
ob ich auf einem Papier Stricherl mache, oder ob ich am Taschenrechner
immer 1 addiere oder ......
Wie und wo ich zähle ist mein Bier. Der Timer zählt halt
im Register TCNT1. Aber das hat dich genausowenig zu interessieren,
wie dich meine Zählweise zu interessieren hat. Für dich ist einzig
und alleine interessant, dass ich mich wieder melde, wenn die
100 erreicht sind!
Okay, okay entschuldigung, meinen ersten Anlauf hab ich geschrieben, als
ihr noch nicht auf meine TCNT1 Antwort geantwortet hattet. Sorry, habs
ja jetzt geändert.
Wegen Dezimalzahlen, da kann ich den Wert direkt schreiben ohne ein 0b
oder 0x vor der Zahl oder?
Entschuldigt bitte, wenn ich eure Geduld etwas überstrapaziert habe.
Ganz liebe Grüße
Tanja
Tanja M. wrote:
> Wegen Dezimalzahlen, da kann ich den Wert direkt schreiben ohne ein 0b> oder 0x vor der Zahl oder?
Ja.
>> Entschuldigt bitte, wenn ich eure Geduld etwas überstrapaziert habe.
Kein Problem.
Niemand wird gezwungen hier zu antworten :-)
Und noch mal: Wenn es um Anzahlen geht (also um Takte o.ä.), dann ist es
besser, erstens die 16-Bit-Register als ganzes anzusprechen (anstelle
von TCNT1H und TCNT1L also einfach TCNT1, für OCR1AL und OCR1AH eben
OCR1A) und dann auch Dezimalzahlen zu verwenden. Der Code Generation
Wizard macht das zwar in Einzelzugriffen, aber wie schon weiter oben
angedeutet, ist das nicht das Gelbe vom Ei, was die Lesbarkeit betrifft.
Außerdem ist bei diesen Registern die Zugriffsreihenfolge extremst
wichtig (beim Lesen erst das Low-Byte, beim Schreiben erst das
High-Byte). Wenn man das 16-Bit-Register als Ganzes anspricht, nimmt
einem der Compiler das ab, so dass schon mal eine mögliche Fehlerquelle
weniger da ist.
Hmmmm jetzt bin ich ein wenig stutzig. Ihr habt euch so bemüht und jetzt
blinkt die LED noch nicht mal....
Hab ich irgendwas falsch gemacht? Ich hab zum testen des Interrupts auch
gesagt, der soll die LED an Port P5 blinken lassen mit einem Delay.
Hmm wo könnte noch der fehler stecken...
Ich hab keinen Compare Match bei CodeVision eingestellt, könnte es daran
liegen? Was anderes fällt mir grad nicht ein....
Kleine Anmerkung, wenn man einen Wert OCR1A zuweist, dann gibt er das
Ergebnis an PORT B2 aus, also da wo eigentlich OC1B steht und wenn ich
den Wert in OCR1B eingebe, dann gibt er das Ergebnis an PORT B1, also da
wo OC1A steht, wieso ist das so?
Liebe Grüße
Tanja
P.S. Danke für dein Verständniss Karl Heinz! =)
Tanja M. wrote:
> Hmmmm jetzt bin ich ein wenig stutzig. Ihr habt euch so bemüht und jetzt> blinkt die LED noch nicht mal....>> Hab ich irgendwas falsch gemacht? Ich hab zum testen des Interrupts auch> gesagt, der soll die LED an Port P5 blinken lassen mit einem Delay.> Hmm wo könnte noch der fehler stecken...> Ich hab keinen Compare Match bei CodeVision eingestellt, könnte es daran> liegen? Was anderes fällt mir grad nicht ein....
Verstehe ich jetzt nicht ganz, was Du da machst. Was für ein Delay?
> Kleine Anmerkung, wenn man einen Wert OCR1A zuweist, dann gibt er das> Ergebnis an PORT B2 aus, also da wo eigentlich OC1B steht und wenn ich> den Wert in OCR1B eingebe, dann gibt er das Ergebnis an PORT B1, also da> wo OC1A steht, wieso ist das so?
Das sind die Ausgangspins der Compare-Units. Die sind intern fest
verdrahtet. Wenn Du einen anderen Pin toggeln willst, musst Du das im
Interrupt-Handler per Software machen.
Ohja, hab die OCR1AH/L auskommentiert und dennoch leuchtet die LED
zumindest, sie blinkt zwar nicht, aber sie tut zumindest dasselbe wie
mit den OCR1AH/L Registern im einzenen betrachtet! Danke für den tollen
Tip! Das erleichtert das ganze natürlich ein bisschen.
Liebe Grüße
Tanja
Also der Delay ist nur im Interrupt um zu testen ob der Interrupt
überhaupt tut. Moment zeige dir den Codeabschnitt eben.
1
// Timer 1 overflow interrupt service routine
2
interrupt[TIM1_OVF]voidtimer1_ovf_isr(void)
3
{
4
PORTB.5=0;
5
delay_ms(50);
6
PORTB.5=1;
7
}
Ahhhhmmm, ich glaub das ist kein wunder, dass der keinen Interrupt
auslöst, schließlich besteht ja kein overflow mehr.
Aber wie löse ich denn jetzt einen Interrupt aus? Per Compare Match?
Dafür müsste ich dem CodeVision aber erstmal sagen, dass er einen
Compare Match auszuführen hat oder?
Hmpf.. Naja wenigstens an ist die LED zumindest schonmal.
Liebe Grüße
Tanja
Tanja M. wrote:
> Ahhhhmmm, ich glaub das ist kein wunder, dass der keinen Interrupt> auslöst, schließlich besteht ja kein overflow mehr.> Aber wie löse ich denn jetzt einen Interrupt aus? Per Compare Match?
Genau!
> Dafür müsste ich dem CodeVision aber erstmal sagen, dass er einen> Compare Match auszuführen hat oder?
Tja, das ist jetzt der oben angedeutete Nachteil von CodeVision bzw. der
Schreibweise. Jetzt heißt es: Datenblatt rauskramen, nachsehen, welche
Bits gesetzt werden müssen und welche nicht, das ganze als Hexadezimal-
oder Binärzahl da eingeben, wenn man weiß, wo es hingehört...
Oh Gott oh Gott.... Na gut! Da muss ich jetzt durch, also ich meld mich
jetzt vorerst ab und versuche dem jetzt erstmal irgendwie beizubringen,
wie man einen Compare Match Interrupt auslöst! =)
Wenn ich irgendeine neue Eingebung hab, melde ich mich bei euch!
Nochmals vielen vielen Dank für eure Hilfe und eurer Geduld, wirklich
sehr lieb, da macht das Programmieren nochmal doppelt so viel spaß, wenn
man versteht was man da macht! =)
Ganz liebe Grüße
Tanja
Ich meld mich schon früher als erwartet.
Mir ist aufgefallen, dass ich mit dem CTC Mode ja recht viel im
Interrupt schreiben muss. Das möchte ich eigentlich nicht, deswegen kam
ich wohl auch nicht auf die Idee den CTC Mode zu benutzt, da ich so
wenig wie möglich per Software machen möchte. Ich möchte mein PWM signal
mit 10Hz über die Hardware laufen lassen.
Entschuldigt, wenn ich mich einfach ungeschickt ausgedrückt habe in dem
was ich eigentlich vor hatte...
Liebe Grüße
Tanja
Tanja M. wrote:
> Ich meld mich schon früher als erwartet.> Mir ist aufgefallen, dass ich mit dem CTC Mode ja recht viel im> Interrupt schreiben muss.
Wenn du die vorgesehenen Output Pins des Prozessors benutzen
kannst, dann brauchst du noch nicht mal eine Interrupt
Funktion.
Ansonsten brauchst du 2 Interrupt Funktionen:
* Eine die den Pin auf 1 setzt, wenn der Timer auf 0 gesetzt wird.
* Eine die den Pin wieder auf 0 setzt, wenn die (Hinweis!)
2. Compare Einheit des Timers1 eine übereinstimmung feststellt.
Das ist doch nicht viel.
Tanja M. wrote:
> Ich meld mich schon früher als erwartet.> Mir ist aufgefallen, dass ich mit dem CTC Mode ja recht viel im> Interrupt schreiben muss. Das möchte ich eigentlich nicht, deswegen kam> ich wohl auch nicht auf die Idee den CTC Mode zu benutzt, da ich so> wenig wie möglich per Software machen möchte. Ich möchte mein PWM signal> mit 10Hz über die Hardware laufen lassen.
Wenn es über die Hardware laufen soll, dann geht es nur mit den
entsprechenden Ausgangspins OC1A bzw. OC1B. Und wenn es tatsächlich ein
echtes PWM-Signal sein soll (also nicht nur einzelne Impulse, sondern
ein kontinuierlich laufendes Rechteck-Signal), dann nimmt man natürlich
auch nicht den CTC-Modus, sondern einen der PWM-Modi. Die machen im
Prinzip fast dasselbe, nur ist bei denen auch die Synchronisation der
Registeraktualisierung automatisiert (zumindest bei den PWM-Modi, die
OCR1A als TOP-Wert benutzen). Nimm am besten eine PWM mit OCR1A als TOP
und dann eben OC1B als Ausgang. Dann kannst Du über OCR1A die
PWM-Frequenz vorgeben und über OCR1B das Tastverhältnis. Wenn Du das so
konfigurierst, dann brauchst Du eigentlich gar keinen Interrupt Handler.
Dann macht der Timer alles selbst, ohne dass das Programm nach der
Initialisierung noch eingreifen muss.
Oben war nur nach den ersten paar Postings nicht ganz klar, ob Du
tatsächlich eine PWM machen, oder ob Du nur einzelne Impulse einer
bestimmten Länge generieren willst.
Wenn Du natürlich den OC1B-Pin aus verdrahtungstechnischen Gründen nicht
verwenden kannst, dann siehe letztes Posting von Karl heinz. Dann
brauchst Du zwei Interrupt Handler, in denen aber jeweils nur eine
einzige Anweisung steht, nämlich in der einen "Portpin auf High-Pegel
setzen" und in der anderen "Portpin auf Low-Pegel setzen".
Ich muss das ja auch noch alles mit dem ADC in einklang bringen...
Manno, hatte so ein aufschwung und nun sowas. Holt einen ganz schön
wieder auf den Boden der Tatsachen... X(
Ich wollt eigentlich einfach nur 10Hz mit den Timer1 über die Hardware
des µC erzeugen. 10Hz sind ja gleich 100ms. In den 100ms muss es eine
Highzeit und eine Lowzeit geben. Aber diese 100ms sollen fest sein und
sich nicht ändern, dann möchte ich nur diese High und Lowzeiten per Poti
verändern können.
Hät echt nicht gedacht, dass das so schwer geht.
Liebe Grüße
Tanja
Danke das ihr mir immernoch beisteht.
Also ich nehme dann den Phase Correct PWM Mode, der hat auch einen OCR1A
als Top.
Wie errechne ich die PWM frequenz? Muss ich da schon irgendwie mein 10Hz
unterbringen? Welches Register ist für die PWM Frequenz zuständig?
Und über OCR1B das Tastverhältnis bestimmen? Also sagen wie lang er High
und wie lang er Low sein soll?
Beide Ausgänge sind frei und können benutzt werden.
Wirklich vielen Dank euch beiden.
Liebe Grüße
Tanja
Rechne mal durch:
10 Hz entsprechen einer Periodendauer von 100 ms. Der Timer muss also so
konfiguriert werden, dass die Compare-Einheit A, die ja die Frequenz
festlegen soll, alle 100 ms ein Compare Match hat. Bei 1 MHz CPU-Takt
sind 100 ms 100000 CPU-Takte. Also: Ein Compare-Match alle 100000 Takte.
Der Prescaler des Timers erlaubt Teilungen durch 1, 8, 64, 256 und 1024.
1 MHz / 8 wären 125 kHz für den Timer. Bei 125 kHz brauchts für 100 ms
noch 12500 Timertakte. Also muss OCR1A so konfiguriert werden, dass nach
12500 Takten ein Compare Match auftritt. Oben hatte ich den Zusammenhang
irgendwo erwähnt, es muss also
1
OCR1A=12499;
da stehen. Damit hätten wir schon mal die Frequenz. Der Rest ist
Spielerei. In OCR1B kannst Du jetzt irgendeinen Wert zwischen 0 und
12499 reinschreiben, und erhältst dementsprechend Tastverhältnisse von
0...100 %.
Nur der Timer und die Compare-Einheit müssen noch entsprechend
konfiguriert werden, dass der Timer auch im richtigen PWM-Modus arbeitet
(z.B. Modus 15). Dazu müssen erstmal die WGM-Bits nach der Tabelle im
Datenblatt entsprechend gesetzt werden. Dann kommen (bei reiner
Hardware-PWM) noch die Bits COM1B0 und COM1B1 dazu, die bestimmen, was
mit dem Ausgangspin bei den jeweiligen Compare-Ereignissen gemacht
werden soll. Die Information steht in der entsprechenden Tabelle im
Datenblatt (Da ein Fast-PWM-Modus eingestellt ist, müssen die Bits nach
der Tabelle für Fast-PWM gesetzt werden. In meiner Version vom
Datenblatt wäre das Tabelle 37 auf Seite 98...)