Forum: Mikrocontroller und Digitale Elektronik Atmega, Timer und Takte


von Thomas P. (Gast)


Lesenswert?

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

von johnny.m (Gast)


Lesenswert?

> ...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.

von Willi (Gast)


Lesenswert?

>>in der
Interrupt Routine wird nur ein Register incrementiert - nichts
weiter

Wird auch das SREG-Register gesichert
und wiederhergestellt ?

MfG Willi

von Thomas P. (Gast)


Lesenswert?

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?

von johnny.m (Gast)


Lesenswert?

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.

von Thomas P. (Gast)


Lesenswert?

Nein - das SREG wird nicht gesichert -> sehe auch aufgrund der
ausgeführen Befehle keinen Grund dazu.

von johnny.m (Gast)


Lesenswert?

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...)

von Thomas P. (Gast)


Lesenswert?

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)?

von Thomas P. (Gast)


Lesenswert?

Wobei TIMXDiv eigentlich 1000 ist (sein sollte - zu Testzwecken auf 200
-> mit 200 läufts im übrigen auch) und nicht wie im Code 200!

von johnny.m (Gast)


Lesenswert?

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.

von Thomas P. (Gast)


Lesenswert?

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?

von johnny.m (Gast)


Lesenswert?

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).

von Michael U. (Gast)


Lesenswert?

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

von johnny.m (Gast)


Lesenswert?

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