Hallo! Ich versuche die ganze Zeit mit dem ATMEAG8535 eine Uhr zu realisieren. Der Controller wird mit einer Taktfrequenz von 8Mhz versorgt. Ich habe den Teimer folgendermaßen so eingestellt: TCCR1A=0x00; TCCR1B=0x03; //Prescaler CLK/64 TCNT1H=0x00; TCNT1L=0x00; OCR1AH=0x04; //Wert errechnet für 10ms: 1/(8Mhz/64)=8us; 10ms/8us=1250(dez) --> 4E2 (hex) OCR1AL=0xE2; OCR1BH=0x00; OCR1BL=0x00; interrupt [TIM1_OVF] void timer1_ovf_isr(void) { PORTB = ~PORTB; if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; } } } } } meine Uhr läuft viel zu langsam. Was habe ich da falsch gemacht?
Du hast falsche interrupt gewehlt. Richtig ist : // Timer 1 output compare A interrupt service routine interrupt [TIM1_COMPA] void timer1_compa_isr(void) Programierst du mit CodeVisionAVR?
Wenn ich in diesen Registern Änderungen vornehme, dann tut sich gar nichts. Der Timer1 läuft immer gleich.
Hallo! Ich habe es mal getestet. Jetzt zählt er gar nicht mehr hoch. Ich möchte nur den Timer1 Überlauf nutzen. (overflow irq)
Das Register TIMSK habe ich folgendermaßen so eingestellt: // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x90; Jetzt läuft der Timer. Aber irgendwie noch nicht so wie ich will. Grrr....
Hallo Joe! Timer1 ist 16bit breit, dass heißt Überlauf Interrupt wird jede (65536x8µS=0,524288) Sekunde ausgelöst und Output Compare Register hat mit dem nichts zu tun. Vergesse nicht OutputCompare1A Interrupt freizugeben und Timer1 in interrupt routine auf Null zu setzen! // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x10;
Vielen Dank Bronko! Phuuu...jetzt bin ich total durcheinander. Kannst du mir nicht ein kleines Beispiel ins Forum posten? Ich möchte eigentlich nur, dass der Timer1 jede 10ms ausgelöst wird.
Hallo Joe! // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 125,000 kHz // Mode: CTC top=OCR1A (Clear Timer on compare match) // OC1A output: Discon. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge TCCR1A=0x00; TCCR1B=0x0B; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x04; OCR1AL=0xE2; OCR1BH=0x00; OCR1BL=0x00; // Timer 1 output compare A interrupt service routine interrupt [TIM1_COMPA] void timer1_compa_isr(void) { PORTB = ~PORTB; if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; } } } } }
Hmmm...jetzt läuft die Uhr zu schnell. Das Register TIMSK habe ich auf 0x10hex eingestellt. Was kann das jetzt noch sein.
Was mir dabei komisch vorkommt ist, dass ich den Wert für 10ms (siehe oben) berechnet habe. Der Timer läuft viel zu schnell. An was kann dies noch liegen?
Hi... Ohne speziellen Uhrenquarz wird das nie eine Uhr. Dazu ist der interne RC-Oszillator zu ungenau. Such mal hier im Forum, da gibt es die Variante mit 32kHz-Uhrenquarz und Timer2. Wenn es dir aber nur um den Lerneffekt geht, dann musst du dich entscheiden, welchen Timer-Int du verwenden willst. - Overflow: Dann muss der Timer in jedem Int auf den Startwert gesetzt werden, von dem die Zeit bis zum Überlauf (65536) deinem Zeittakt entspricht. - Compare-Match: Du setzt die OCR-Register auf den Timer-Endwert und stellst den Timer so ein, dass er bei Erreichen des OCR-Wertes auf 0 gesetzt wird. ...HanneS...
Hallo Hannes! Ich habe hier den Compare-Match Mode gewählt. Leider läuft der Timer1 noch zu schnell. Was könnte ich da falsch gemacht haben? Könntest du mir nicht ein Beispiel geben für z.b: Overflow bzw. Compare Mode? TCCR1A=0x00; TCCR1B=0x0B; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x04; OCR1AL=0xE2; OCR1BH=0x00; OCR1BL=0x00; TIMSK=0x10; // Timer 1 output compare A interrupt service routine interrupt [TIM1_COMPA] void timer1_compa_isr(void) { PORTB = ~PORTB; if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; } } } } }
Hallo Joe! Du kannst den Inhalt von OC1AH und OC1AL erhöhen um Timer1 langsamer zu machen. Viel Glück.
Hallo Branko! Muss ich in der Interrupt Routine noch irgend ein Register verändern bzw. löschen? Was mir nur noch auffällt ist, dass ich den Wert ja errechnet habe und trotzdem läuft der Timer zu schnell. Vielleicht habe ich ja im Pony Prog bei den Security Bits was falsch gemacht! Stimmt dieser Code dann so: TCCR1A=0x00; TCCR1B=0x0B; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x04; OCR1AL=0xE2; OCR1BH=0x00; OCR1BL=0x00; TIMSK=0x10; // Timer 1 output compare A interrupt service routine interrupt [TIM1_COMPA] void timer1_compa_isr(void) { PORTB = ~PORTB; if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; } } } } }
Hey Joe... Hast du mal drüber nachgedacht, dass das in Arbeit ausartet, schon alleine deine Timer-Register-Zuweisungen zu überprüfen? Denn sie sind ohne Suchrei im Datenblatt nicht nachvollziehbar, da du keine symbolischen Namen benutzt, sondern kryptische Hex-Zahlen. Es ist besser nachvollziehen, wenn man die symbolischen Namen der Bits verwendet, wie sie im Datenblatt und in der Include-Datei angegeben bzw. definiert werden. Wie man das macht, wird hier im Forum an vielen Stellen gezeigt. Schau dir mal im Datenblatt die Architektur des Timers an. Schau genau hin, welche Register welche Aufgabe haben. Nur wenn du verstehst, was da abgeht, kannst du den Timer ordentlich programmieren ohne herumzueiern. Du hast das Pech, dass du dir einen AVR ausgesucht hast, der recht komplexe Timer hat. Dadurch erscheint das Datenblatt etwas aufgebläht, man hat (ohne Vorwissen) Mühe, die relevanten Stellen im Datenblatt zu finden. Da du aber nur einfache Standardfunktionen nutzen möchtest (also kein PWM und was auch immer), solltest du dir vielleicht mal das Datenblatt eines alten Classic-AVRs ansehen um die Standardfunktionen der Timer zu verstehen. Dort ist das kurz, knapp und verständlich erklärt. Mit diesem Vorwissen (sofern du es verstanden hast) wirst du auch das Datenblatt modernerer AVRs verstehen und dich über die zusätzlichen Features freuen (die dich jetzt nur verwirren). Ein Code-Beispiel kann ich dir nicht geben, denn ich kann kein C. Und ein ASM-Beispiel habe ich jetzt auch nicht herumliegen, findest du aber sicherlich in irgendeinem Tutorial oder in der Codesammlung. Wenn ich dir jetzt eins zusammenbasteln würde, dann wäre das ungeprüft und könnte fehlerhaft sein. Das will ich dir und mir nicht antun. Setze das, was ich dir weiter oben in Worten beschrieben habe, in die Programmiersprache deiner Wahl um, dann müsste das klappen... ...HanneS...
Ok hier habe ich genau beschriebn was ich da eingestellt habe: TCCR1A=0x00; TCCR1B=0x0B; //Prescaler CLK/64(CS12 und CS10); CTC1 TCNT1H=0x00; TCNT1L=0x00; OCR1AH=0x04; //Wert errechnet für 10ms: 1/(8Mhz/64)=8us; 10ms/8us=1250(dez) --> 4E2 (hex) OCR1AL=0xE2; OCR1BH=0x00; OCR1BL=0x00; TIMSK=0x10; // OCIE1A (output compareA Match Interrupt Enable) // Timer 1 output compare A interrupt service routine interrupt [TIM1_COMPA] void timer1_compa_isr(void) { PORTB = ~PORTB; if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; } } } } }
So ich habe jetzt mal das Signal an einer LED am PORTB mit dem Oszi nachgemessen. TCCR1A=0x00; TCCR1B=0x0B; //Prescaler CLK/64(CS12 und CS10); CTC1 TCNT1H=0x00; TCNT1L=0x00; OCR1AH=0x00; OCR1BH=0x00; OCR1BL=0x00; TISMK=0x10; Mit dieser Einstellung messe ich 50kHz. Aber wenn ich den Wert ausrechne komme ich auf 8Mhz / 64 = 125kHz. Wie kann das sein?
So ich habe jetzt mal das Signal an einer LED am PORTB mit dem Oszi nachgemessen. TCCR1A=0x00; TCCR1B=0x0B; //Prescaler CLK/64(CS12 und CS10); CTC1 TCNT1H=0x00; TCNT1L=0x00; OCR1AH=0x00; OCR1BH=0x00; OCR1BL=0x00; TISMK=0x10; Mit dieser Einstellung messe ich 50kHz. Aber wenn ich den Wert ausrechne komme ich auf 8Mhz / 64 = 125kHz. Wie kann das sein?
(8MHz/64) ist der Zähltakt für deinen Timer, nicht der Takt des Überlaufs oder Compare-Match oder was auch immer...
Ich habe somtlich Datenblätter durchgeschaut. Ich weiss jetzt nun nicht was ich tun soll. Der Zähltakt müsste sich doch ändern, wenn ich einen andere Prescaler angebe, oder? Der Zähltakt bleibt immer gleich (50kHz).
Den Zähltakt kannst du nicht messen, der ist am Zählereingang des Timers... Und der ist nicht auf einen Pin herausgeführt. Oder misst du den direkt auf dem Chip?? Vorschlag: Vergiss Timer1 erstmal und eigne dir die Grundlagen mittels Timer0 an. Dort gibt es nicht so viele Möglichkeiten sich zu irren.
Was ich brauche ist nur mal ein Beispiel wie ich jetzt 10ms mit dem 16Bit Timer1 realisieren kann. Und nicht mehr. Warum wird heir so ein großes Geheimnis daraus gemacht. Grrr...
Doch, du kannst dir helfen... Geh systematisch ran und eiere nicht so herum. - Du weißt nicht recht, welchen Int du nehmen willst. - Du präsentierst uns den (leeren) Screenshot von den Fuses, ohne die Fuses vorher ausgelesen zu haben. - Du misst ein Signal, das nur im Inneren des AVR zu finden ist - Du misst immer 50kHz, sagst uns aber nicht wo oder was du misst. - Du behauptest, du hättest das Datenblatt gelesen. Hast du es auch verstanden??? (Nicht das ganze Datenblatt, nur die Funktion des Timers1). - Du sagst, es will/kann dir keiner helfen. Hast du denn auf nur einen Tip reagiert? Und es waren einige sinnvolle Hinweise in diesem Thread. - Du fängst einfach einen neuen Thread an weil dir die Antworten hier nicht gefallen. Was erwartest du eigentlich? Willst du eine fertige Lösung ohne selbst etwas dafür zu tun? Deine Fragen beweisen doch, dass du dich nicht ernsthaft mit dem Thema beschäftigt hast. - Lies gefälligst im Datenblatt nach, wie der Timer funktioniert, lies aber so oft, bis du es verstanden hast. - Fang mit Timer0 an, der ist überschaubar und hat nicht so viele Features. - Versuche unbedingt, die Hardware zu verstehen. Denn du programmierst nicht für ein Betriebssystem mit Treibern (PC), sondern direkt die Hardware. Das geht nur, wenn du verstehst, was du tust. - Lies andere Threads hier im Forum, lies die Tutorials, lies die Beiträge im WIKI und besonders in der Codesammlung. Und lies nicht nur, sondern versuche auch zu verstehen. Du lernst das nicht für deinen Lehrer sondern ganz allein für dich. - Wenn dir das zu mühsam ist, dann versuche es doch mal mit einem anderen Hobby, z.B. "Malen nach Zahlen". Viel Erfolg... ...HanneS...
Hallo! Ich verwende den "output compare A" vom Timer1. Ich habe schon verstanden wie der funktioniert. Ich greife vom PortB Pin2 das Signal mit einem Oszi ab. Gemessener Wert 50Khz. Ok das scheint in Ordnung zu sein. So wie es aussieht muss ich die Register OCR1AH + OCR1AL so einstellen: (8Mhz / 64) = 8us 10ms / 8us = 1250(dez) 65536 - 1250 = 64286(dez) --> FB1E(hex) --> OCR1AH = 0xFB; --> OCR1AL = 0x1E; So müsste es funktionieren.
So wie oben beschrieben mußt du den Overrun Interrupt nehmen.Der Output-Compare lebt davon, daß der Timer bei Null anfängt zu zählen bis zu dem Zählerstand, der im Output-Compare-Register steht. Dann generiert die Maschine einen Interrupt. Aber nicht vergessen, den Timer wieder zurücksetzen. Wo evtl noch eine Fehlerquelle sein kann, schau dir mal an in welcher Reihenfolge "quasi" 16-Bit Register beschrieben werden. Ich programmiere in C unter IAR. Da muß ich mich um soetwas nicht kümmern. Michael
Hey Joe... Wenn du das Signal am TOC-Pin messen kannst, dann hast du eine der PWM-Betriebsarten eingestellt. Wolltest du das? Oder wolltest du eine Folge von Interrupts erzeugen, deren Abstand 10ms beträgt. Interrupts sind innerhalb des AVR, PWM schaltet das TOC-Pin. Und die 50kHz passen auch nicht so recht. Hast du dich da nicht verrechnet? (Messen kannst du ja mit dem (einfachen) Oszilloskop nur µs, keine kHz.) Lass uns mal überschlagen: Dein Takt beträgt 8MHz... (wirklich?? externer Quarz? oder interner RC-Oszillator? kalibriert?) Dein Vorteiler steht auf 64... Dann wird dein Timer1 (der ja ein 16-Bit-Zähler ist) mit 125 kHz angesteuert. Nun kann der Zähler diese Frequenz teilen. Und zwar durch ganze Zahlen im Bereich von 1 bis 65536. Durch 1 geht da nicht, da das ja der Eingangstakt wäre. Durch 2 ergäbe 62,5kHz, durch 3 etwa 42kHz, durch 4 etwa 31kHz, durch 5 etwa 25kHz... Woher nimmst du die 50kHz, die du da misst??? Wenn die stimmen, dann läuft dein AVR nicht mit 8MHz oder dein Vorteiler nicht mit 64... Falls dein AVR mit internem RC-Oszillator mit 8MHz läuft, dann erreichst du diese 8MHz nur, wenn du den AVR von Hand (also per deinem Programm) kalibrierst. Dazu musst du mittels eines ISP-Programms (am PC) das "Calibrationsbyte" aus dem Signature-Bereich des AVR auslesen (H-Byte an Adresse $03 ist das für 8MHz) und mittels deinem AVR-Programm in das I/O-Register OSCCAL schreiben. Wie das genau geht, findest du im Datenblatt, in einer AppNote zum Thema RC-Calibration und in diversen Threads hier im Forum. Also liegt die Vermutung nahe, dass dein AVR mit etwa 6,4MHz tuckert und am Timer etwa 100kHz gezählt werden. Durch falsche Konfiguration teilt der durch 2 und lässt das PWM-Bit (TOC) mit 50 kHz klappern. Das wäre eine Erklärung für deine 50kHz... Wobei ich nicht nachvollziehen kann, wie du mit einer PWM die Uhr realisieren willst, denn die PWM ist außen, am TOC-Pin, die Uhr braucht aber eine ISR, und zwar drinnen (im AVR)... Einfachster Weg: - Oszillator auf 1MHz intern einstellen, denn dann wird er bei jedem Start automatisch kalibriert (steht da so im Datenblatt, aber das hast du ja alles gelesen). - Vorteiler so wählen, dass du mit dem Einstellbereich des Timers (1:2...1:65536) deine gewünschte Interrupt-Frequenz erreichen kannst. Erster Versuch: Vorteiler 64 - Bei 1MHz, VT64 hast du 15,625kHz am (internen) Takteingang von Timer1. - Du möchtest 100Hz als Interrupt-Frequenz - Dann müsstest du durch 156,25 teilen... - Der Timer kann aber keine Dezimalbrüche, also teilen wir durch 156. (das ist kleiner als ein Byte fasst, das würde Timer0 schaffen) - Versuchen wir den nächsten Vorteiler-Wert, also 8 - 1MHz/8=125kHz, also 125kHz am Takteingang von Timer1... - Du willst immernoch 100Hz Interrupt-Frequenz... - 125000/100=1250 => Du musst durch 1250 teilen - Wir nutzen zunächst den Timer1-Überlauf - Du musst beim Start und in jeder ISR den Timer (Zählregister) auf einen Startwert einstellen, der 1250 vor dem Überlauf ist, also 65536-1250. - Du musst den Timer (Control-Register usw.) so einstellen, dass er mit Vorteiler8 am Prozessortakt hängt, munter drauflos zählt, und bei Überlauf einen Int auslöst. - In dieser ISR musst du den Timer (tcnt1h und tcnt1l) wieder auf den Startwert (65536-1250) setzen und kannst dann deine Zahlen bearbeiten und zur Display-Ausgabe vorbereiten. Das ist die Version, die eigentlich immer funktioniert... (aber eben mangels Uhrenquarz sehr ungenau) Die Version mit Output-Compare-Interrupt erfordert genauestes Studium des Datenblattes, denn da muss man ergründen, ob der Timer beim CompareMatch automatisch gelöscht werden kann (CTC). Das geht glaube nicht mit jedem Timer Output-Compare-Register, ist zumindest nicht bei allen AVRs gleich. Das Datenblatt ist da auch sehr umfangreich, da die Timer der Megas eine Menge Features haben, die ja auch erklärt werden müssen. Man findet das Gesuchte nicht so schnell und muss selbst bei einfachen Dingen immer alle Features im Auge haben, also nicht nur die beiden Timer1-Controlregister... Also nutzen wir die einfache Version, die komfortable kannst du später immer noch nutzen... Viel Erfolg... ...HanneS...
Hallo! So hier verwende ich den Overflow Interrupt. In dieser Routine tue ich die LEDS an Port B blinken lassen. Tut auch! z.B. an PORTB pin1 habe ich das Signal am Oszi gemessen. Und da messich die 50Khz. Der Mikrocontroller wird schon mit 8Mhz versorgt. Habe ich nachgemessen. Und zwar verwende ich da einen Chip-keramikresonator von 8Mhz. Den internen Takt möchte ich nicht verwenden. interrupt [TIM1_OVF] void timer1_ovf_isr(void) { PORTB = ~PORTB; if (zehntelsek < 9) ++zehntelsek; else { zehntelsek = 0; if (sekunden < 59) ++sekunden; else { sekunden = 0; if (minuten < 59) ++minuten; else { minuten = 0; if (stunden < 23) ++stunden; else { stunden = 0; } } } } }
Hallo Hannes! Ich habe irgendwie bedenken mit meinen eingestellten Fusebits. Da ich einen 8Mhz Chip-Keramikresonator verwende, wie muss ich die FuseBits einstellen. Ich habe keine Haken gesetzt. 8Mhz leigen an. Ist das so richtig?
Tja, das Ding ist leer... Entweder hast du die Fuses nicht ausgelesen, bevor du den Bildschirm kopiert hast oder du hast wirklich alle Fuses gelöscht. Vor dem Verändern und Schreiben von Fuses sollte man immer den aktuellen Stand aus dem AVR auslesen. Ob Pony das automatisch macht, weiß ich nicht, ich benutze Pony nicht. Ich kenne mich (noch) nicht bei allen Fuses aus, daher vergreife ich mich nur an den Fuses, die ich verändern möchte. Von den anderen lasse ich meine Finger, die lasse ich also so, wie sie vom Hersteller eingestellt wurden. Du müsstest dir also eine Liste machen, in der du für jedes Fusebit aufschreibst, warum du es so und nicht anders gesetzt hast. Das Datenblatt (ab Seite 234) wird dir dabei helfen. Kannst du mit einer Fuse nix anfangen (Verständnisproblem), dann setze sie auf Default (auch im Datenblatt ab Seite 234 zu finden). Es bringt sicherlich nix, alle Fuses zu löschen (1), nur weil du die 4 CKSEL-Fuses löschen willst (1111). Du erwartest jetzt sicher, dass ich dir die korrekten Fuseeinstellungen zusammensuche und hier poste. Das werde ich aber nicht tun, und das hat folgende Gründe: - Ich habe keinen Mega8535 herumliegen - Ich habe keinen Keramik-Schwinger (den Typ, den du nutzt) herumliegen - Ich kann daher die Einstellungen nicht testen - Ich muss/will keine Digitaluhr bauen - Ich habe eigentlich garkeine Zeit dazu - Ich habe inzwischen auch keine Lust mehr, da du alle Hinweise ignorierst Viel Erfolg... ...HanneS...
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.