Hallo zusammen Zwar habe ich ein Problem mit meinem 16-bit timer vom Atmega128 ( Quarz 7372800 Hz) . Kann mir jemand helfen wieso er nicht läuft, was mache ich an der Initalisierung falsch? Will eine Frequenz von ca. 122.88kHz am Port OC1B. //Initalisierung TCCR1A = 0x40; // ( COM1A1 = 0 ¦ COM1A0 = 1 ¦ WGM11 = 0 ¦ WGM10 = 0) TCCR1B = 0x01; // ( CS10 = 1) OCR1A = 0x001E; //(122.88kHz ->Berechnung 7372800Hz/((2*1228800)-1) Besten dank für eure Hilfe.
Lädst du TCNT1 auch bei jedem interrupt wieder neu? Interrupt überhaupt aktiviert?
Ja setzte die Interrupt, habe die vergessen hier zu erwähnen. Setzte diese so: TIMSK = (1<<TOIE1); TIMSK = (1<<TICIE1); Nein das TCNT1 setzt ich nicht neu, da ich ja über Compare Outpurt A arbeite. Ist das falsch?
> TIMSK = (1<<TOIE1); > TIMSK = (1<<TICIE1); ...und mit der zweiten Anweisung löschst Du das Overflow-Interrupt-Enable wieder. Das kann so nicht funktionieren. Übrigens hat das TICIE1 nichts mit dem zu tun, was Du oben beschrieben hast.
Tanja Hofmann wrote: > Nein das TCNT1 setzt ich nicht neu, da ich ja über Compare Outpurt A > arbeite. Ist das falsch? Dann musst Du den Timer aber im CTC-Modus betreiben und die WGM-Bits entsprechend setzen! Und dann den Compare-Interrupt benutzen und nicht den Overflow. So wie Du es oben eingestellt hast, läuft der Timer immer bis zum Overflow weiter und die Interrupt-Frequenz wäre F_CPU/65536, weil nach 65536 Zyklen der Overflow auftritt. AVR-Tutorial, AVR-GCC-Tutorial
Mhh irgendwie scheint mir das noch nicht so klar zu sein. Gibts evt. ein beispiel wie im meinen 16bit timer für 122 kHz einstellen muss damit ich das signal am OC1B nutzen kann?
> TIMSK = (1<<TOIE1); > TIMSK = (1<<TICIE1); Hier kannst Du Dir die erste Zeile sparen, weil die zweite Zeile die TIMSK-Einstellung der ersten ja einfach überschreibt. Ich bevorzuge diese Notation:
1 | TIMSK = 0<<OCIE2 | 0<<TOIE2 | 1<<TICIE1 | 0<<OCIE1A | 0<<OCIE1B | 1<<TOIE1 | 0 | 0<<TOIE0 |
Das "Gerüst" ist dabei immer fest (alle Gerüste für alle I/O-Register stehen bei mir in einer Extra-Datei, wo ich sie bei Bedarf rauskopiere). Als Modifikationen an dieser Zeile sind nur Änderungen von 0en in 1en oder umgekehrt erlaubt. So kann man bequem I/O-Bits setzen und löschen und dabei stets mühelos sehen, welche es sind. >Nein das TCNT1 setzt ich nicht neu, da ich ja über Compare Outpurt A >arbeite. Ist das falsch? Ja. Dann läuft der Timer ja immer bis 65536, was Du nicht willst. Du musst den CTC-Mode verwenden.
Hast du den ein einfaches Beispiel für ein 16-Bit timer der für den OC1B eingestellt ist ?
Tanja Hofmann wrote: > Hast du den ein einfaches Beispiel für ein 16-Bit timer der für den OC1B > eingestellt ist ? Schau Dir im Datenblatt die Tabelle mit den Timer-Betriebsarten an. Da steht drin, welche Bits in TCCR1A und TCCR1B gesetzt werden müssen. In Deinem Fall kämen Modus 4 oder 12 in Frage.
Ja habe es nach dem Datenlatt versucht, läuft aber leider nicht ...... Niemand ein brauchbares beispiel ?
>Ja habe es nach dem Datenlatt versucht, läuft aber leider nicht ......
Macht er gar nichts, oder einfach nur zu schnell/langsam? Geht er
überhaupt in die ISR? Hast mal debuggt?
Wie sieht deine ISR denn aus? Ich hätte jetzt einfach das TCCR mit 0x1E oder was auch immer geladen und dann immer beim TIMER1_OVF_vect das TCCR wieder neu geladen, anstatt es mit dem Compare zu machen. Müsste eigentlich so gehen:
1 | int main(){ |
2 | TCCR1A = 0; |
3 | TCCR1B = 0x01; // ( CS10 = 1) |
4 | |
5 | TCNT1 = 0xFFFF-0x001E; |
6 | TIMSK |= (1<<TOIE1); |
7 | sei(); |
8 | while(1); |
9 | |
10 | }
|
11 | |
12 | |
13 | ISR(TIMER1_OVF_vect){ |
14 | TCNT1 = 0xFFFF-0x001E; |
15 | }
|
Konnte jetzt nur Simulieren, aber zumindest gibt mir das alle 30 Takte oder so ein Interrupt. Was bei 7372800 Hz eben 122880/2 Hz entspricht. Man muss aber auch bedenken, dass das hin und herspringen auch einige Takte kostet.
Timmo H. wrote: > Wie sieht deine ISR denn aus? > Ich hätte jetzt einfach das TCCR mit 0x1E oder was auch immer geladen > und dann immer beim TIMER1_OVF_vect das TCCR wieder neu geladen, anstatt > es mit dem Compare zu machen. Und warum? Wozu hat der AVR denn wohl den CTC-Modus? Exakt: Damit man keinen Timer-Reload machen muss! Außerdem ist das "TCCR" ein Steuerregister und da wird nix "geladen".
Naja, wenns funktioniert. Kann man doch erstmal so machen. Ob der jetzt dafür 2 Takte länger braucht als mit Autoreload. Der Thread-Ersteller postet ja auch nur Bruchteile des Codes. Ohne was zusammenhängendes ist halt doof.
Ich verstehe nicht, was an dem, was ich oben schon gesagt habe, so schwer ist. OK, der CTC-Modus wird im Tutorial nicht beschrieben (wenn ich Zeit habe, ändere ich das vielleicht mal), aber ich habe doch deutlich genug auf das Datenblatt hingewiesen, und da steht doch wohl eindeutig, was man tun muss. Wenn man z.B. CTC-Modus 4 in der Tabelle "Waveform Generation Mode Bit Description" (im neuesten Datenblatt ist das Tabelle Nr. 61 auf S. 135) anschaut, dann steht da ganz exakt, welche der WGM-Bits gesetzt werden müssen, um diesen Modus einzustellen. In der Register-Beschreibung steht auch, welche der Bits in TCCR1A und welche in TCCR1B stehen. Wenn das geschehen ist, dann kannst Du in OCR1A einen Wert schreiben. Wenn der Timer beim Hochzählen diesen Wert erreicht, wird er automatisch zurückgesetzt. Wenn Du den zu OCR1A gehörenden Portpin (OC1A) noch entsprechend konfigurierst (über die COM-Bits), dann wird der Pin bei jedem Erreichen von OCR1A umgeschaltet. Alternativ (oder auch zusätzlich) kannst Du selbstverständlich auch den entsprechenden Compare-Interrupt aktivieren und im Interrupt-Handler irgendwas machen (z.B. einen anderen Pin umschalten). Das ist allerdings mit viel Overhead verbunden, weshalb da die erreichbaren Frequenzen nicht sehr hoch sind. Das Umschalten von OCR1A geht aber verzögerungsfrei. Und dran denken: Wenn Du 122 kHz haben willst, musst Du mit der doppelten Frequenz den Portpin umschalten (also mit 244 kHz)!
Timmo H. wrote: > Naja, wenns funktioniert. Kann man doch erstmal so machen. Ob der jetzt > dafür 2 Takte länger braucht als mit Autoreload. Der Thread-Ersteller > postet ja auch nur Bruchteile des Codes. Ohne was zusammenhängendes ist > halt doof. Timer Reload macht man beim AVR nicht, genau dafür gibt's den CTC-Schiss. Außerdem bringt allein der Interrupt Handler 20-30 Takte Overhead mit, weshalb das bei den geforderten Frequenzen mit großer Wahrscheinlichkeit in die Hose geht (schließlich soll hier schon nach 30 Takten umgeschaltet werden, und da machen nebenbei auch 2 Takte Unterschied schon ganz schön was aus(*))! Bei 8051ern, die eine Auto-Reload-Funktion haben, kannste mit sowas kommen, aber bitte erzähl keinem AVR-Anfänger so was! Bei dem Post-Verhalten von "Tanja" gebe ich Dir allerdings Recht... (*) Bedenke, dass beim Aufruf eines Interrupt Handlers erstmal der aktuell laufende Befehl zu Ende abgearbeitet wird, und wenn der Interrupt mal bei einem 1-Zyklen-Befehl und mal in einem 2-Zyklen-Befehl reinhaut, dann hat man schon Abweichungen im Prozentbereich.
So ich war mal so frei. Hab jetzt einfach das gemacht wie es im Datenblatt steht und es scheint zu gehen:
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | #include <util/delay.h> |
4 | |
5 | int main(){ |
6 | TCCR1A = (1<<COM1A1); |
7 | TCCR1B = (1<<CS10) | (1<<WGM12); |
8 | |
9 | TCNT1 = 0; |
10 | OCR1A = 0x1E; |
11 | TIMSK |= (1<<OCIE1A); |
12 | sei(); |
13 | while(1); |
14 | |
15 | }
|
16 | |
17 | |
18 | ISR(TIMER1_COMPA_vect){ |
19 | ....
|
20 | }
|
Der Fehler ist wohl das WGM12 gewesen (siehe Bild)
Danke für deine Hilfe Timmo. Jedoch habe ich ein weiteres problem mit deine Lösung, meine Frequenz springt zwischen 80Khz und ca 118khz herum. An was liegt das??
Timmo H. wrote:
So weit so gut, aber warum schreibst Du da...
> OCR1A = 0x1E;
ein 0x1E hin? Eine 30 hätte es auch getan, und jeder Anfänger merkt
erstens, dass der Compiler in der Lage ist, dezimal zu verstehen (*) und
man sieht außerdem sofort, was da gemacht wird. Für Bitmasken nehme ich
auch direkt Hexadezimalwerte, weil man sich aus denen direkt die
Bitmuster ableiten kann, aber für Sachen wie Zykluszahlen sollte man
auch dezimal bleiben.
(*) Wie war das noch? Es gibt auf der Welt genau 10 Sorten Menschen:
Diejenigen, die binär verstehen und diejenigen, die es nicht können...
Tanja Hofmann wrote: > Danke für deine Hilfe Timmo. Jedoch habe ich ein weiteres problem mit > deine Lösung, meine Frequenz springt zwischen 80Khz und ca 118khz herum. Dann solltest Du vielleicht endlich mal Deinen Code posten!
>So weit so gut, aber warum schreibst Du da... > OCR1A = 0x1E; >ein 0x1E hin? Eine 30 hätte es auch getan, und jeder Anfänger merkt >erstens, dass der Compiler in der Lage ist, dezimal zu verstehen (*) und Ist mir auch klar, dass der Compiler das versteht. Nur so bringt man Tanja nicht so durcheinander, weil sie ja mit Hex angefangen hat.
Timmo H. wrote: > Ist mir auch klar, dass der Compiler das versteht. Nur so bringt man > Tanja nicht so durcheinander, weil sie ja mit Hex angefangen hat. Ich glaube nicht, dass sie in der Schule mit Hex angefangen hat, es sei denn, sie ist auf eine Hexenschule gegangen... SCNR
>Ich glaube nicht, dass sie in der Schule mit Hex angefangen hat, es sei >denn, sie ist auf eine Hexenschule gegangen... Ne, aber in ihrem ersten Posting. Hex-Hex...
Mhhh lassmer das mal mit dem dem Hexen-Zeugs ..... Habe das halt nie so gelehrnt das man register nicht mit hex beschreiben sollte. Nun mein programm erzeugt immer noch nicht die gewünnschten 122 kHz. Es macht mit diesen Einstellungen nur 24kHz. TCCR1B = 0x00; //stop TCNT1H = 0xFF; //load counter HIGH value TCNT1L = 0xE1; //load counter LOW value TCCR1A = 0x00; TCCR1B = 0x01; //start Timer TIMSK = 0x04; //timer interrupt sources ISR(TIMER1_OVF_vect){ TCNT1H = 0xFF; //load counter HIGH value TCNT1L = 0xE1; //load counter LOW value } Was mache ich falsch? Habe mit diesem Problem seit tagen schon zu kämpfen und bin sehr sehr froh für weiter hilfe. grüssel
Ich habe mich doch oben ganz klar und unmissverständlich zu dem Thema ausgelassen: Auf diese Weise bekommst Du keine 122 kHz! Erstens ist der Timer Reload Murks (wie schon mehrfach angedeutet) und zweitens hat der Interrupt Handler zu viel Overhead. Und warum beschreibst Du jetzt plötzlich die Timer-Zähl-Register in zwei Schritten? Die Reihenfolge stimmt zwar (zufälligerweise?), aber in C kann man TCNT1 als ganzes beschreiben und muss (bzw. sollte) nicht das Low- und High-Byte getrennt schreiben! Oben wurden Beispiele geliefert, wie das ganze mit der Compare-Einheit zu realisieren ist. Bist Du dermaßen lernresistent oder tust Du nur so? Langsam glaube ich das alles nicht mehr!
Ja sicher habe ich das, jedoch sprint mir dann die frequenz 80Khz -118khz herum. Meine langsam bin ich ja auch am verzweifeln denn einen Timer zu realisieren sollte nicht so schwierig sein.
Also mein code sieht momentan folgendermassen aus: TCNT1 = 0; //load timer TCCR1A = 0x00; TCCR1B = 0x01; //start Timer TIMSK |= (1<<TOIE1); TIMSK |= (1<<OCIE1A); OCR1A = 30; In die Compare - Interrupt routine kommt er aber macht mir nicht die gewünschte 122.8kHz !
>Was mache ich falsch? Du probierst auf gut Glück herum ohne über ein gründliches theoretisches Verständnis der µC-Komponente Timer zu verfügen. >In die Compare - Interrupt routine kommt er aber macht mir nicht die >gewünschte 122.8kHz ! Dann versuch doch mal herauszufinden, was genau der Timer bei dieser Konfiguration tut.
>In die Compare - Interrupt routine kommt er aber macht mir nicht die >gewünschte 122.8kHz ! Was ist denn mit WGM12? Ich habs drin und bei mir gehts (zumindest beim Simulieren). Ohne WGM12 kommt imho kein Auto-Clear
1 | TCCR1B = (1<<CS10) | (1<<WGM12); |
So komme glaub dem fehler auf die Spur .... Also der Interrupt wird ausgeführt, jedoch habe ich kein Signal am OC1A obwohl ich im Programm mit DDRB = (1<<PB5) den pin als Output deklariere. Zudem habe ich noch TCCR1A = (1<<COM1A0) geändert um am OC1A zu togglen. Was habe ich vergessen oder mache ich da falsch?
Wie änderst du denn das Signal am pin? Du hättest dir und uns ne Menge arbeit erspart, wenn du einmal deinen ganzen Quellcode gepostest hättest!!! Und was ist mit WGM12?
Das ist der Code für die Timer initaliserung. Das Signal ändere ich ja indem ich COM1A0 auf 1 legge, sprich dann wird getogglet. Müsste doch so laufen oder? Respektiv der Interrupt wird schon aufgerufen, jedoch habe ich nichts am Pin 0C1A. // Timer Init DDRB = (1<<PB5) TCCR1A = (1<<COM1A0); TCCR1B = (1<<CS10) | (1<<WGM12); TCNT1 = 0; OCR1A = 0x1E; TIMSK = (1<<OCIE1A);
Wow, so kurz ist dein GANZES Programm? So ohne Main Funktion und so? Was ist daran so schwer ALLES zu Posten. Wenigstens Main und deine ISR? So kann man doch das alles nicht vernünftig überblicken!!! Das haben doch jetzt schon mehrere gesagt. Zudem ist COM1A0 auch nicht PB5 sondern PB6. Kann ja nicht funzen wenn du das DDR falsch setzt. Und eigentlich sollte man auch
1 | DDRB |= (1<<DDB0); |
Schreiben anstatt PB0. Ist zwar das selbe, kann aber ggf. Verwirrung stiften.
Habe gemeint das COM1A0 und COM1A1 für den Compare Output Mode Channel A sind. Das heisst OC1A enspricht PB5, laut datenblatt. Oder sehe ich das falsch?
Jo, PB5 stimmt schon. Aber es ist wirklich an der Zeit, mal endlich einen kompletten, compilierbaren Code zu schicken!
?? Was bitte hat die Bitstelle im control-Register mit dem Hardware-Pin zu tun? Richtig, nichts.
Stimmt PB5 ist richtig, bin jetzt selbst durcheinandergekommen, so ganz ohne Code :-). Kommt der eigentlich nocht? Wahrscheinlich nicht. Ich steige mal aus...
Ja der Code sieht folgendermassen aus: #include <avr\io.h> #include <avr\interrupt.h> #include <avr\iom128.h> void port_init(void) { DDRB = (1<<PB5); } void timer1_init(void) { TCCR1A = (1<<COM1A0); TCCR1B = (1<<CS10) | (1<<WGM12); TCNT1 = 0; OCR1A = 0x1E; TIMSK = (1<<OCIE1A); } void init_devices(void) { port_init(); timer1_init(); sei(); } ISR(TIMER1_COMP_vect){ } int main(void) { init_devices(); while(1); }
Tanja Hofmann wrote: > #include <avr\iom128.h> Die Device-Header werden nie direkt eingebunden! Müsste aber eigentlich auch ne Warnmeldung geben. Der Controllertyp wird im Makefile bzw. im AVRStudio unter Configuration Options angegeben und nur die avr/io.h wird eingebunden. > ISR(TIMER1_COMP_vect){ Und genau da liegt vermutlich der Hund begraben (oder zumindest einer von mehreren)! Der Interrupt-Vektor heißt mindestens TIMER1_COMPA_vect (hab jetzt nicht in die Doku geschaut). Da fehlt ein "A"! Das gibt jedes Mal einen Reset, wenn der Interrupt angesprungen wird, weil Du einen Interrupt freigegeben hast, zu dem es keinen Handler gibt! Deaktiviere mal den Interrupt, den brauchst Du nämlich für die Signalerzeugung überhaupt nicht. Das macht der Timer alles allein. Also ISR raus, sei() und TIMSK = 1<<OCIE1A ebenfalls weg! Verstehst Du jetzt vielleicht endlich, warum es wichtig ist, den kompletten Code zu schicken und nicht nur ein paar zusammenhanglose Zeilen?
Kann demfall die "#include <avr\iom128.h>" weglassen! Jedoch funktioniert das Togglen am Pin PB5 nicht, obwohl die ISR methode aufgerufen wird. Habe ich vergessen irgendwo was zu setzten?
Ja leuchet mir ein, dachte halt das mein Feher in der Initalisierung war. Werde dies gegen den Abend mal versuchen ohne der ISR ,sei() und TIMSK = 1<<OCIE1A ! Hoffe klappt dann bald einmal.
Siehste, hättest du viel früher deinen Code gepostet hätte es und dir und uns ne Menge Zeit gespart. Wars denn so schwer?
Trau mich fast nicht mehr reinzuschreiben .... aber habe das Problem gefunden! Es hatte den Pin PB5 bei der Produktion nicht richtig verlötet mit dem Print. Also besten dank für eure grossen Bemühungen. Schätze das sehr und weiss das es nicht selbstverständlich ist.
Habe zum schluss doch noch eine Frage. Sollte schon gehen mit einem Timer den CompareA, CompareB gleichzeitig mit unterschiedlichen Frequenzen zu betreiben oder was mache ich für ein Überlegungsfehler? Hier mein Code: #include <avr\io.h> #include <avr\interrupt.h> void port_init(void) { DDRB = (1<<PB5); DDRB = (1<<PB6); } void timer1_init(void) { TCCR1A = (1<<COM1A0)| (1<<COM1B0); TCCR1B = (1<<CS10) | (1<<WGM12); TCNT1 = 0; OCR1A = 0x1E; OCR1B = 0x0F10; TIMSK = (1<<OCIE1A); TIMSK = (1<<OCIE1B); } void init_devices(void) { port_init(); timer1_init(); sei(); } ISR(TIMER1_COMPA_vect){ } ISR(TIMER1_COMPB_vect){ } int main(void) { init_devices(); while(1); }
Nö, mit unterschiedlichen Frequenzen geht natürlich nicht. Wie soll denn ein Timer mit zwei unterschiedlichen Frequenzen laufen können? Und noch mal: Wenn Du mit Hardware die Pins toggelst und beim Compare Match sonst nix zu tun ist, dann lass die Interrupt-Sachen weg! Leere Interrupt Handler erzeugen nur unnötigen Overhead.
Wieso, der Timer kann doch gleich bleiben. Sind doch nur unterschiedliche Compares.
man kann aber unterschiedliche compares nicht verschiedene Frequenzen erzeugen. Die Frequenz wird ausschliesslich vom Takt (ggf. mit Teiler) und dem Rücksetzwert bestimmt (kann ein Überlauf mit anschliessendem reload oder ein compare-Ereignis sein).
Will ja nicht 2 verschiedene Frequenzen am timer einstellen, sondern am PB5 und PB6 mit den CompareA und CompareB zwei verschiedene Frequenzen erzeugen. OCR1A = 0x1E; OCR1B = 0x0F10; Soltte ja schon gehen sonst machen die Compares ja gar keinen sinn oder?
Tanja Hofmann wrote: > Will ja nicht 2 verschiedene Frequenzen am timer einstellen, sondern am > PB5 und PB6 mit den CompareA und CompareB zwei verschiedene Frequenzen > erzeugen. > > OCR1A = 0x1E; > OCR1B = 0x0F10; > > Soltte ja schon gehen sonst machen die Compares ja gar keinen sinn oder? NEIN !!! mit einem Timer (und die Compare-Einheiten gehören beide zum selben Timer!) kann man keine zwei verschiedenen Frequenzen gleichzeitig fahren! Die Compare-Einheiten haben nichts mit unterschiedlichen Frequenzen zu tun. Man kann nur einen TOP-Wert für den Timer angeben, und der bestimmt die Frequenz!
Falls das nicht klar sein sollte (was ich vermute): Die beiden Compare-Einheiten dienen u.a. der Erzeugung zweier synchroner PWM-Signale mit unterschiedlichen Tastverhältnissen und gleicher Frequenz.
>mit einem Timer (und die Compare-Einheiten gehören beide >zum selben Timer!) kann man keine zwei verschiedenen Frequenzen >gleichzeitig fahren! Man kann mit T/C1 über die beiden Compare-Einheiten auch zwei verschiedene Frequenzen erzeugen - innerhalb gewisser Grenzen und unter Verwendung der Compare-Match-Interrupts. Dazu lässt man T/C1 im Mode 0 laufen (kein CTC!) Sowohl bei Compare Match A als auch bei Compare Match B muss der entsprechende Interrupt ausgelöst werden. Jetzt der "Trick": Im COMPA-Interrupthandler erhöht man OCR1A um einen bestimmten, festen Wert (z. B. 600), im COMPB-Interrupthandler dasselbe mit OCR1B (z. B. 800). Zur Frequenzerzeugung werden COM1A0 und COM1B0 im TCCR1A gesetzt (Einstellung "Pin toggeln bei Compare Match"). Solange die Inkrementierwerte in den Handlern nicht zu klein und nicht zu groß sind (kein Compare-Match-Interrupt darf verloren gehen), werden an den Pins OC1A und OC1B zwei voneinander völlig unabhängige, durch die Inkrementierwerte festgelegte taktgenaue Frequenzen anliegen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.