Hallo ich habe ein paar Fragen zu Timern und Takt ;-) Im Moment auf dem ATmega8 -> aber nicht speziell. Eine Timebase sollte realisiert werden, die jede Millisekunde durch einen Overflow auf Timer 1 signalisiert. Prinzipiell ist das kein Problem, ich muss den Counter vom Timer 1 (16 BIT) mit entsprechendem Startwert laden und den Rest macht der Controller. Mir ist aber aufgefallen, dass sich das restliche Programm (Serielle Kommunikation etc.) sehr merkwürdig verhält. Kommunikation bricht ab, teilweise zerhackte Datenpakete etc.. Grund hierfür ist anscheinend der "schnell auslösende Timer 1". Schließlich habe ich bei 8MHz nur 8000Takte pro Millisekunde. Der Prescaller Timer1 steht auf 1. Ich habe den Prescaller auf 8 raufgesetzt und schon lief die Kommunikation wie gewohnt. Meines Wissens erledigt der ATmega8 pro Takt Ein Byte Programm (ist das richtig?) - demnach kann ich nicht nachvollziehen, warum der 1ms-Overflow dem restlichen Programm in die quere kommt (in der Interrupt Routine wird nur ein Register incrementiert - nichts weiter). Vlt. kann mich jemand aufklären, wie viel Programm pro Takt erledigt wird - und ob ein Overflow von Einer Millisekunde sinnvoll oder totaler blödsinn ist. Vielen Dank Thomas
> ...erledigt der ATmega8 pro Takt Ein Byte Programm...
Nein, er arbeitet (in den meisten Fällen) einen Befehl ab. Es gibt aber
auch Befehle, die mehrere Taktzyklen benötigen (bis zu 4). Ein Befehl
ist übrigens entweder 16 Bit (zwei Bytes) oder 32 Bit lang.
1 ms (8000 Taktzyklen) sind für den µC ne ziemlich lange Zeit. Da
sollte es eigentlich keine großartigen Kollisionen geben, es sei denn,
in der Interrupt-Routine, die jede ms auftritt, werden irgendwelche
üblen Ausgabefunktionen (immer wieder: printf und Artverwandtes)
ausgeführt.
>>in der
Interrupt Routine wird nur ein Register incrementiert - nichts
weiter
Wird auch das SREG-Register gesichert
und wiederhergestellt ?
MfG Willi
Wie schon gesagt, in der Interrupt Routine wird selber nichts großartiges abgearbeitet. Andere Funktionen rufe ich aus dem Interrupt niemals auf ;-) Was mich halt verwundert, dass die Kommunikation anscheined von dieser Interrupt Routine ausgebremst wird. Kann mir das überhaupt nicht erklären. Sobald ich den Prescaller hoch setze sind die Probleme verschwunden. Kommunkation läuft im übrigen mit 38400baud -> es werden maximal 4Byte am Stück ausgegeben - und das nur jede Millisekunde! Das sollte passen - und selbst wenn nicht, unterbricht der Overflow den USART?
BTW: Eine Zeitbasis machst Du (zumindest wenn sie einigermaßen genau sein soll) am besten nicht über den Timer Overflow Interrupt sondern über einen Compare-Interrupt. Wenn der Timer im CTC-Modus läuft, funktioniert das ganz prima und Du brauchst keinen Wert nachzuladen.
Nein - das SREG wird nicht gesichert -> sehe auch aufgrund der ausgeführen Befehle keinen Grund dazu.
Die eigentliche Datenübertragung über U(S)ART ist eine reine Hardware-Angelegenheit. Die wird nicht durch irgendwelche Interrupts unterbrochen. In dem Moment, in dem Du das UDR schreibst (und damit Deine Daten losschickst) hat das Programm an sich keinen Einfluss mehr auf die Ausgabe (es sei denn, Du fummelst während einer Übertragung an den UART-Steuerregistern rum...). Von Dir fehlen jetzt noch zwei Dinge, damit man Dir hier helfen kann: Die Information, in welcher Programmierumgebung (v.a. Sprache) Du arbeitest und Dein Code (wobei letzterer natürlich i.a. bereits die Information zum ersten Punkt beinhaltet...)
1 | SIGNAL(SIG_OVERFLOW1) |
2 | {
|
3 | //ui_tim1sreg - steuert diesen Interrupt
|
4 | //Bit 0 - Sekunden Impuls!
|
5 | //Bit 1 - Warte-Funktion - zählt jede MilliSekunde
|
6 | //Bit 2 - Warte-Funktion DCF
|
7 | |
8 | //
|
9 | if(ui_tim1sreg &=(1<<1)) |
10 | {
|
11 | ui_tim1cnt++; |
12 | }
|
13 | //
|
14 | if(ui_tim1sreg &= (1<<2)) |
15 | {
|
16 | ui_dcf++; |
17 | }
|
18 | //Korrekturfunktiono für genauen Sekunden Impuls!
|
19 | mycnt++; |
20 | if(mycnt == (TIMXDiv-1)) |
21 | {
|
22 | mycnt = 0; |
23 | ui_tim1sreg |=(1<<0); |
24 | TCNT1H = ((65535-TIMXVal + TIMXRem)>>8); |
25 | TCNT1L = (65535-TIMXVal + TIMXRem); |
26 | }
|
27 | else
|
28 | {
|
29 | TCNT1H = ((65535-TIMXVal)>>8); |
30 | TCNT1L = (65535-TIMXVal); |
31 | }
|
32 | }
|
So - dess ist die Interrupt-Routine für den Overflow. Main läuft in folgender Schleife.
1 | while(1) |
2 | {
|
3 | if(ui_tim1sreg &= (1<<0)) |
4 | {
|
5 | |
6 | usart_send_char('#'); |
7 | |
8 | ui_tim1sreg &=~(1<<0); |
9 | out_dcf(); |
10 | |
11 | }
|
12 | }
|
folgendes könnte noch interessant sein.
1 | #define TIMXDiv 200
|
2 | #define TIMXVal (F_CPU/TIMXDiv)
|
3 | #define TIMXRem (F_CPU%TIMXDiv)
|
4 | volatile uint16_t mycnt; |
5 | |
6 | volatile uint8_t ui_tim1sreg ; |
7 | volatile uint16_t ui_tim1cnt; |
8 | volatile uint16_t ui_dcf; |
Das ganze ist in Win-AVR - c. Im Tutorial habe ich auf die schnelle nichts für CTC-Mode gefunden, hat zufällig jemand einen LInk zu Infos (ausser Datenblatt)?
Wobei TIMXDiv eigentlich 1000 ist (sein sollte - zu Testzwecken auf 200 -> mit 200 läufts im übrigen auch) und nicht wie im Code 200!
CTC-Mode heißt 'Clear Timer on Compare Match', also 'Timer bei Compare-Ereignis löschen'. D.h., sobald ein Compare-Ereignis auftritt, wird das TCNT-Register auf Null zurückgesetzt. Im Compare-Register legt man den Wert für die gewünschte Zeit ab. Da dieser während des Zählens unverändert bleibt, braucht man in der Betriebsart nichts nachzuladen. Da das Nachladen bei obiger Version mit Overflow verzögert geschieht (vom Auftreten des Interrupts bis zur Ausführung der ISR vergehen zunächst mindestens 4 Taktzyklen, dann noch der ganze Code...), bleibt die Genauigkeit auf der Strecke (v.a. da Du den Timer erst am Ende der ISR nachlädst). Zwei Anmerkungen zu Deinem Code: 1. Anscheinend verwendest Du eine veraltete Version von WINAVR. SIGNAL ist nicht mehr aktuell. Am besten bei Gelegenheit eine aktuelle Version besorgen. 2. Du brauchst in C nicht einzeln auf High- und Low-Byte der Timer-Register zuzugreifen. Es sind in der entsprechenden Header-Datei 16-Bit-Register definiert. Wenn Du die benutzt, brauchst Du Dir auch keinen Kopf wegen der Schreibreihenfolge von Low- und High-Byte zu machen.
Zu Punkt 2. Heißt das, dass ich erst Low-Byte und dann High-Byte schreiben muss? UNter dem neueren WinAVR kann ich also "TCNT1 = 0xFFAA;" nutzen? Das ist ned schlecht. Wobei ob ich "SIGNAL" o. "ISR" nutze sollte doch egal sein, oder?
Wenn Du ein neues WINAVR hast, dann solltest Du auch ISR statt SIGNAL verwenden. Zumal Du, um SIGNAL zu benutzen, noch zusätzlich die signal.h includen musst, was bei ISR wegfällt (nur noch interrupt.h). Die Schreib-Reihenfolge, wie Du sie verwendet hast, ist korrekt. Das Low-Byte muss als letztes geschrieben werden. Aber es ist trotzdem besser, die 16-Bit-Register zu nutzen, da man dann nicht jedes Mal wieder überlegen muss. Das mit den 16-Bit-Registern hat WINAVR (bzw. die AVR-libc) schon eine ganze Weile (wenn nicht sogar schon ne Ewigkeit).
Hallo, >Nein - das SREG wird nicht gesichert -> sehe auch aufgrund der >ausgeführen Befehle keinen Grund dazu. das ist die sicherste Methode, um Fehler tagelang zu suchen. ;) Ich habe mit C nichts am Hut, ich weiß also auch nicht, was für einen Code der Compiler baut und ob er SREG selbst sichert. Du hast in main und in der ISR Bedingungs-Abfragen. Was passiert, wenn in main die IF Bedingung soweit bearbeitet wurde, daß jetzt entsprechend der gesetzten Flags gesprungen werden muß? Genau jetzt kommt der IRQ, die Bedingung in der ISR wird bearbeitet und kehrt mit den Flags zurück, die dort gesetzt wurden. Nun springt main mit diesen Flagwerten und Du bist sicher, die passen IMMER zur Bedingung in main? Gruß aus Berlin Michael
@Michael: Ein C-Compiler sichert i.d.R. selber das SREG und andere Register, die er verwendet.
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.