Forum: Mikrocontroller und Digitale Elektronik Atmega8 und Timer im CTC Mode


von mx12 (Gast)


Lesenswert?

Hallo zusammen,

ich habe eine Frage:
Ich möchte gerne einen Timerinterrupt alle 1ms aufrufen. Deswegen dachte 
ich dass ich den CTC-Mode benutzen muss mit einem "Reload-Wert". 
Allerdings bekomme ich die Implementierung nicht hin, hab verschiedene 
Artikel dazu gelesen, aber irgendwie will das alles nicht!

Kann mir jemand sagen, wie ich das Porgrammieren muss??

Habe im hauptprogramm folgendes benutzt: (Hier aus dem Forum)
1
   //Timer initialisieren
2
   TCCR1A = 0x00;
3
   TCCR1B = 0x0B;      //Prescaler 64, CTC-Mode
4
   OCR1A = 15625;      //Vergleiswert setzten
5
   TIMSK = (1<<OCIE1A);   //OC-Interrupt aktivieren 
6
7
......
8
9
//TimerInterrupt, wenn status_SendData
10
ISR(TIMER0_COMPA_vect) {  
11
12
 char s[7];
13
 uart_puts( itoa( ADC_Read(1), s, 10 ) );
14
 }

Allerdings resetet sich bei mir der komplette µC dann immer. Weiss nicht 
woiran es liegt. Er läuft mit internen 1Mhz. Was ich wenn alles fertig 
ist, natürlich durch einen Quarz verbesser

Kann mir vielleicht auch jemand verraten wie ich den Reload-Wert 
berechnen kann? Also Formelmäßig mit abhängigkeit der Taktfrequenz?

Ich habs versucht alleine mithilfe des Internets zu lösen, aber leider 
war ich dabei nicht erfolgreich!

Danke schonmal

von mx12 (Gast)


Lesenswert?

Keine eine Idee? :(

von Helfer (Gast)


Lesenswert?

Falscher ISR Vektor. Du brauchst den für TIMER1. TIMER0_COMPA_vect 
sollte eigentlich eine Warnmeldung beim Kompilieren produziert haben.

von H.Joachim S. (crazyhorse)


Lesenswert?

Und alle 1ms eine A/D-Wandlung samt Umwandlung in ASCII und Ausgabe per 
UART wird je nach Baudrate sehr schnell eng (4 Zeichen bei 9600 Baud: 
>4ms)

von Helfer (Gast)


Lesenswert?

Eine Beispielrechnung zum Reloadwert ist im Artikel 
AVR-GCC-Tutorial/Die Timer und Zähler des AVR

Wunsch sind 1ms bei 1 MHz und Timer1

1000000 Takte pro Sekunde ohne Prescaler
1000 Takte pro Millisekunde ohne Prescaler
15,625 Takte pro Millisekunde mit Prescaler 64

Dein rechnerisch korrekter Vergleichswert OCR1A wäre damit 15,625-1.

Allerdings kannst du nur ganze Zahlen verwenden, also 14 (15-1) oder 15 
(16-1). Der Fehler ist -4,3% bzw. +2,6%

Ich würde den Prescaler auf 1 lassen, weil damit ein ganzzahliger 
Vergleichswert (999 = 1000-1) möglich ist. 1000 ist bei einem 16-Bit 
Timer erlaubt (max. 65535)

von mx12 (Gast)


Lesenswert?

Okay danke.

Das problem war scheinbar die Kombination aus Vergleichswert und die 
falsche ISR-Routine.

@H.joachim Seifert
Ich möchte später alle 0,1 sekunden einen Wert übertragen, der 
Mikrocontroller läuft mit einem 20Mhz Quarz und die Baudrate soll 9600 
betragen (bzw da bin ich relativ flexibel, weil die Gegenseite ein PC 
ist). Kann es da zu Problemen kommen?

Vielen Dank schonmal für die Hilfe!

von Falk B. (falk)


Lesenswert?

@  mx12 (Gast)

>Ich möchte später alle 0,1 sekunden einen Wert übertragen, der
>Mikrocontroller läuft mit einem 20Mhz Quarz und die Baudrate soll 9600
>betragen

Und dafür braucht man 20 MHz? Hmmm, der AVR wird sich tödlich 
langweilen.

>ist). Kann es da zu Problemen kommen?

Es kann immer zu Problemen kommen. Aber wenn man weiß was man tut ist es 
eine einfache Standardaufgabe.

MFG
Falk

von Helfer (Gast)


Lesenswert?

Alle 0,1s (100ms) hast du genug Zeit, um einen Wert per UART zu 
übertragen. H. Joachim hat da ja schon die Grundrechnung gezeigt: 9600 
Baud => 9600 Bit/1000ms => ~1 ASCII-Zeichen/ms. Ein typ. Messwert 
0..1023 bringt es auf 2-5 ASCII-Zeichen inkl. Trennzeichen, d.h. 2-5ms 
pro Telegramm. Das passt locker in die 100ms Abstände.

Bei der Implementierung solltest du Timer und das Versenden des 
Ergebnisses sauber trennen. In der Regel macht man das über ein 
gemeinsames Flag der ISR und der Arbeitsschleife im Hauptprogramm. Der 
Timer setzt das Flag und das Hauptprogramm reagiert auf das Flag (Messen 
und Senden) und setzt es wieder zurück. Diese Programmiertechnik ist im 
Artikel Interrupt im Detail erklärt.

von H.Joachim S. (crazyhorse)


Lesenswert?

Wenn du weiter nichts zu tun hast, kannst du das so machen.
Der bessere Weg:
-betreibe auch die UART im Interrupt
-setze im Timerinterrupt nur ein flag, dass der timer abgelaufen ist
-in der main prüfst du, ob das flag gesetzt ist
-falls ja: zurücksetzen, A/D-Wandlung ausführen, ASCII-Umwandlung 
durchführen, auf die serielle Schnittstelle schieben

Macht nach aussen hin nichts anderes als dein Ansatz, aber du gewinnst 
jede Menge Prozessorzeit, die du im Moment aber gar nicht brauchst :-) , 
sondern in der mainloop vertrödelst.
Kommt aber mal was hinzu, hast du genug Rechenzeit.
Deine Anwendung ist praktisch Dauer-Nichtstun.

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
Noch kein Account? Hier anmelden.