Forum: Mikrocontroller und Digitale Elektronik TIMER2_OVF_vect Interrupt Problem


von Tommy (Gast)


Lesenswert?

Hab hier ein Programm, welches einfach mit hile eine Timer2 Overflows 
hochzählt!
Jedoch wird die Interrupt Routine mehrfach hinteteinander ausgeführt.
Kann mir jemand sagen woran das leigt?

Hier der Code
#include<avr/io.h>
#include<avr/interrupt.h>
#include<avr/sleep.h>
#include"timer.h"
//#include"uart.h"

volatile uint8_t time=0;

#include<avr/io.h>
void timer2_init()
{
  ASSR|=(1<<EXCLK)|(1<<AS2);//externer Clock, asynchron Modus
  TCCR2B|=(1<<CS22)|(1<<CS20);//takt durch 128 teilen
  TIMSK2|=(1<<TOIE2);  //timer overflow einschalten
  while(!(TCCR2B&(1<<CS22)));
  while(!(TCCR2B&(1<<CS20)));
}

ISR(TIMER2_OVF_vect)
{
  time++;
}
int main()
{
  DDRA=0xff;
  PORTA=0xff;
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  sleep_enable();

  timer2_init();

  sei();
  while(1)
  {
    if(time==5)
    {
      time=0;
    }
    else
      {
        PORTA=time;
                        //        sleep_cpu();
      }
  }
  return(0);
}
Programmiere einen ATMega644P mit dem STK600 und dem entsprechenden 
Adapter.
Programmieroberfläche ist das Avr Studio

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:

> Jedoch wird die Interrupt Routine mehrfach hinteteinander ausgeführt.

Woran erkennst du das?

> Hier der Code

Bitte benutze die entsprechenden Tags, damit dein Code nicht durch
die Forensoftware verändert wird, siehe Formatierung im Forum.

>   while(!(TCCR2B&(1<<CS22)));
>   while(!(TCCR2B&(1<<CS20)));

Für diese beiden Zeilen sehe ich keinen Sinn.

von Tommy (Gast)


Lesenswert?

Hab vergessen zu schreiben, dass ich jetzt das JTAGICE mk2 angeschlossen 
habe.

von Tommy (Gast)


Lesenswert?

Die Zeilen sind nur dafür da, wenn man den sleep modus verwendet. Sie 
sollen dafür sorgen, das die Timings stimmen, damit er nicht schlafen 
geht bevor er das Register upgedatet hat.

von Tommy (Gast)


Lesenswert?

Ich glaube es gibt gar kein Problem! Wenn ich das Programm laufen 
lassezählt er bis fünf hoch und fängt wieder von vorne an.
Wenn ich aber per Hand debugge, also per Tastendruck durch das Programm 
gehe läuft er öfters durch die Interrupt routine. Vielleicht liegts auch 
nur am Debugger!

von Tommy (Gast)


Lesenswert?

Ne irgendetwas stimmt nicht! Wenn ich etwas über die Uart schicke bleibt 
er ständig in der Interrupt Routine!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:
> Die Zeilen sind nur dafür da, wenn man den sleep modus verwendet. Sie
> sollen dafür sorgen, das die Timings stimmen, damit er nicht schlafen
> geht bevor er das Register upgedatet hat.

Der korrekte Weg dafür steht im Datenblatt:

d. To switch to asynchronous operation: Wait for TCN2UB, OCR2xUB, and 
TCR2xUB.

von Tommy (Gast)


Lesenswert?

Jörg kannst du mir vielleicht mal sagen was das TCnT2 Register überhapt 
macht?
Ich habe das so verstanden, das man Lesen und Schreibernd darauf 
zugreifen kann, aber wofür ist das gut?
Schreibe ich einen Wert dort hinein und trifft der dann mit OCR2x zu, 
tritt dann ein Interrupt auf?
Und schreibe ich keinen Wert dort hinein wird dann das register einfach 
immer erhöht, vom MC?
Und was soll ich in das Register reinschreiben, wenn ich nur ein 
Timeroverflow machen will! Dann brauchen ich dich gar nicht das Register

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:
> Jörg kannst du mir vielleicht mal sagen was das TCnT2 Register überhapt
> macht?

Es ist der aktuelle Zählerwert.  Manchmal will man ja genauere
Zeitinformation haben als nur einen Überlauf oder compare match.
Schreiben wird man das normalerweise nicht, außer vielleicht
ggf. Rücksetzen auf 0, wenn man den Timer neu synchronsieren
möchte.

von Tommy (Gast)


Lesenswert?

Dann habe ich das ja schon mal verstanden, das ist ja schon mal was 
danke.
Ich der Hilfe steht ja zur initialsierung folgendes:

• Warning: When switching between asynchronous and synchronous clocking 
of
Timer/Counter2, the Timer Registers TCNT2, OCR2x, and TCCR2x might be 
corrupted. A safe
procedure for switching clock source is:
a. Disable the Timer/Counter2 interrupts by clearing OCIE2x and TOIE2.
b. Select clock source by setting AS2 as appropriate.
c. Write new values to TCNT2, OCR2x, and TCCR2x.
d. To switch to asynchronous operation: Wait for TCN2UB, OCR2xUB, and 
TCR2xUB.
e. Clear the Timer/Counter2 Interrupt Flags.
f. Enable interrupts, if needed.
• The CPU main clock frequency must be

Also zuerst sollen die Interruptsflags gelöscht werden, was ich schon 
nicht verstehe, da ich sie ja noch gar nicht gesetzt habe.
Punkt b ist klar
Punkt c würde ich dann jetzt so machen: TCNT2 = 0x00, OCR2A =0x00; 
TCCR2B dort setzte ich den Teiler auf 128.
Dann warte ich auf die drei flags :z.B. 
while(!(ASSR&(1\leq\leqTCN2UB)));
Punkt e. was soll ich löschen hab ja noch nichts gesetzt aber okay 
TIMSK2 =0x00;
und f. wäre dann TIMSK2|=(1<<TOIE2);

Wäre das so dann richtig?
Vielen Dank im voraus

von Peter D. (peda)


Lesenswert?

Im asynchron-Modus wird alles erstmal in ein Zwischenlatch geschrieben.
Die CPU schreibt z.B. mit 8MHz was rein und der T2 übernimmt das dann 
mit seinen 32kHz, also irgendwann später.
Das passiert auch mit dem Interruptflag so, blos daß es dafür kein 
Busy-Bit gibt.
Man muß also im Interrupt ein Dummy-Write auf irgendein anderes 
T2-Register machen und dafür das Busy-Bit abtesten.
Ist dann dieses Dummy-Write übernommen worden, ist ja auch das 
Interruptflag gelöscht und man kann wieder einschlafen.
Damit sollte es dann nur einen Interrupt geben.


Peter

von Tommy (Gast)


Lesenswert?

Ah okay so langsam lichtet sich der Schleier:)
Das dann wäre doch diese vorgehensweise, die ich gemacht habe richitg, 
oder?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:

> Ich der Hilfe steht ja zur initialsierung folgendes:

Die ,Hilfe' heißt übrigens offiziell ,Datenblatt'. ;-)

> Also zuerst sollen die Interruptsflags

Nicht die Interruptflags, sondern die Interrupt-Enable-Bits.

> gelöscht werden, was ich schon
> nicht verstehe, da ich sie ja noch gar nicht gesetzt habe.

Wenn du keine Interrupts freigegeben hast, musst du natürlich auch
keine Freigaben zurücknehmen.  Das ist eine Prinzip-Beschreibung,
du bist nicht im Kindergarten, sondern Mitdenken wird vom Programmierer
eines Controllers natürlich schon erwartet.

> Punkt c würde ich dann jetzt so machen: TCNT2 = 0x00, OCR2A =0x00;
> TCCR2B dort setzte ich den Teiler auf 128.

Auch hier wieder: du musst nur die Register anfassen, die dich
interessieren.  Ob du TCNT2 wirklich auf 0 setzen willst/musst,
musst du selbst wissen.  Wenn du den compare match A nicht
benutzt, dann lass das Register OCR2A in Ruhe.

> Dann warte ich auf die drei flags :z.B.
> while(!(ASSR&(1\leq\leqTCN2UB)));

Naja, vor allem willst du:
1
while ((ASSR & (1 << TCR2BUB)) != 0)
2
  /* wait until no longer busy */;

...denn das TCCR2B musst du ja auf jeden Fall anfassen, also musst
du auch warten, bis es fertig ist.

> Punkt e. was soll ich löschen hab ja noch nichts gesetzt

Bei den Umprogrammiervorgängen kann u. U. ein Interrupt triggern,
der gar nicht gewollt ist (an dieser Stelle).  Daher löscht man
danach alle anhängigen Interrupts erstmal (so man hernach mit
Interrupts arbeiten will, aber das will man mit dem asynchronen
Timer eigentlich am Ende immer).

> aber okay
> TIMSK2 =0x00;

Nein.  Das ist die Maske, nicht die Interruptflags.
1
  TIFR2 = 0xff;

Oder wenn du es ,,politisch korrekt'' machen willst:
1
  TIFR2 = (1 << OCF2B) | (1 << OCF2A) | (1 << TOV2);

von Tommy (Gast)


Lesenswert?

Die while Schleife scheint schon mal falsch zu sein, da der Debugger da 
gar nicht mehr raus kommt.

von Tommy (Gast)


Lesenswert?

Jörg vielen Dank!
Werds gleich ausprobieren

von Tommy (Gast)


Lesenswert?

Hat geplappt, der Timer läuft jetzt auch im Sleepmodus ohne Probleme!
Versuche jetzt die USART zum laufen zu kriegen!
Im Moment kommt nämlich kein Zeiche an.

von Tommy (Gast)


Lesenswert?

Bin jetzt soweit:

#include<avr/io.h>
#include<avr/interrupt.h>
#include<avr/sleep.h>
#include<util/delay.h>
#include"timer.h"
#include"uart.h"

#define F_CPU 4000000UL

// 9600 baud
#define UART_BAUD_RATE 9600

volatile uint8_t time=0;

ISR(TIMER2_OVF_vect)
{
  time++;
}
int main()
{  uint8_t test=0;
  DDRA=0xff;
  PORTA=0xff;

  while(test!=100)
  {  _delay_ms(10);
    test++;
  }test=0;
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  sleep_enable();
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );

  timer2_init();

  sei();

  while(1)
  {
    if(time==5)
    {
      TCCR2B|=(1[math]\leq\leq[math]CS22)|(1[math]\leq\leq[math]CS20);
      while ((ASSR & (1[math]\leq\leq[math]TCR2BUB)) != 0);
      uart_puts("test");
      time=0;
    }
    else
      {
        PORTA=time;
        TCCR2B|=(1[math]\leq\leq[math]CS22)|(1[math]\leq\leq[math]CS20);
        while ((ASSR & (1[math]\leq\leq[math]TCR2BUB)) != 0);
        sleep_cpu();
      }
  }
  return(0);
}
Aber über die USART kommt nichts! Wenn ich jedoch den Sleepmode 
auskommentiere klappt alles. Ergo es muss was mit der Aufwachzeit zu 
tuen haben. Im Datenblatt steht, das er eine gewisse Zeit braucht um 
wach zu werden aber packe ich z.B. die Funktion _delay_ms(50) vor die 
uart_puts Funktion schreibt der zwar etwas über die Schnittstelle aber 
bei "hello world" sehe ich nur world.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitte nimm die Code-Markierungen!  Das kann doch so keine Sau lesen.

von Tommy (Gast)


Lesenswert?

So vielleicht besser:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<avr/sleep.h>
4
#include<util/delay.h>
5
#include"timer.h"
6
#include"uart.h"
7
8
#define F_CPU 4000000UL
9
10
// 9600 baud
11
#define UART_BAUD_RATE 9600
12
13
volatile uint8_t time=0;
14
15
ISR(TIMER2_OVF_vect)
16
{
17
  time++;
18
}
19
int main()
20
{  uint8_t test=0;
21
  DDRA=0xff;
22
  PORTA=0xff;
23
24
  while(test!=100)
25
  {  _delay_ms(10);
26
    test++;
27
  }test=0;
28
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
29
  sleep_enable();
30
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
31
32
  timer2_init();
33
34
  sei();
35
36
  while(1)
37
  {
38
    if(time==5)
39
    {
40
      TCCR2B|=(1<<CS22)|(1<<CS20);
41
      while ((ASSR & (1[<<TCR2BUB)) != 0);
42
      uart_puts("test");
43
      time=0;
44
    }
45
    else
46
      {
47
        PORTA=time;
48
        TCCR2B|=(1<<CS22)|(1<<CS20);
49
        while ((ASSR & (1<<TCR2BUB)) != 0);
50
        sleep_cpu();
51
      }
52
  }
53
  return(0);
54
}

von Tommy (Gast)


Lesenswert?

Wenn ich hinter der uart_puts Routine ein delay von 5ms einfüge erhalte 
ich den String test.
Schreibe ich aber statt test z.B. hello world bekomme ich nur cryptische 
Zeichen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:

> Wenn ich hinter der uart_puts Routine ein delay von 5ms einfüge erhalte
> ich den String test.

Wartet dein uart_puts() denn, bis die Zeichen raus sind, oder schreibst
du das interruptgesteuert raus?  Dann brauchst du dich sonst nicht
wundern, dass die UART aufhört zu senden, wenn du ihr den Takt
klaust...

Das ständige erneute Setzen von TCCR2B verstehe ich überhaupt nicht.

von Tommy (Gast)


Lesenswert?

Ich mach das Interruptgesteuert!
Habe vor der USART Funtkoin jetzt sleep_disable() geschrieben und der 
String kamm vollständig an. Kann aber sein, dass er jetzt nicht mehr 
schlafen geht, das muss ich jetzt noch konrollieren. Hast recht, das 
neusetzen war überflüssig.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du solltest jetzt den bestehenden Code wegwerfen und dir erstmal
Gedanken über eine Struktur machen...

von Tommy (Gast)


Lesenswert?

Ich muss doch der Uart den Takt zurückgeben können und danach wieder 
zurück zum timer2! wenn ich sleep_disable() eingebe klappt das ganze 
einen Durchlauf lang danach geht er nciht mehr schlafen. Ist ja auch 
verständlich, da ich ja den Flschmodus zurücksetze. Wenn ich ihn 
allerdinsg wieder aktiviere klappt es wieder nicht!

von Tommy (Gast)


Lesenswert?

Ist die Struktur denn so schlecht?
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<avr/sleep.h>
4
#include<util/delay.h>
5
#include"timer.h"
6
#include"uart.h"
7
8
#define F_CPU 4000000UL
9
10
// 9600 baud
11
#define UART_BAUD_RATE 9600
12
13
volatile uint8_t time=0;
14
15
ISR(TIMER2_OVF_vect)
16
{
17
  time++;
18
}
19
int main()
20
{  uint8_t test=0;
21
   DDRA=0xff;
22
  PORTA=0xff;
23
24
  while(test!=100)
25
  {  _delay_ms(10);
26
    test++;
27
  }test=0;
28
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
29
  sleep_enable();
30
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
31
  
32
  timer2_init();
33
34
  sei();
35
36
  while(1)
37
  {
38
    if(time==5)
39
    {  
40
      sleep_disable();
41
      uart_puts("Bin wieder aufgewacht");
42
      time=0;
43
    }
44
    else
45
      {
46
        PORTA=time;
47
        sleep_cpu(); 
48
      }
49
  }
50
  return(0);
51
}

Was meinst du denn?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:
> Ich muss doch der Uart den Takt zurückgeben können und danach wieder
> zurück zum timer2! wenn ich sleep_disable() eingebe klappt das ganze
> einen Durchlauf lang danach geht er nciht mehr schlafen. Ist ja auch
> verständlich, da ich ja den Flschmodus zurücksetze. Wenn ich ihn
> allerdinsg wieder aktiviere klappt es wieder nicht!

Eben, derartige Gedanken meine ich mit "Struktur".  Vor dem Coden
sollte erst einmal eine Phase stehen, in der man sich Gedanken um
den Programmfluss macht.  Da gehören solche Betrachtungen mit hinein,
dass man den IOclk eben nicht abschalten kann, solange die UART noch
etwas sendet.  Erst, wenn du diese Struktur hast, kannst du sinnvoll
anfangen (von kleinen Experimenten natürlich abgesehen), sie in Code
umzusetzen.

Ob du für die Struktur nun ein Struktogramm, einen Programmablaufplan,
ein bisschen Gekritzel auf einem Zettel oder einer Wandtafel oder was
auch immer brauchst, ist rein deine Sache.  Sie nur im Kopf zu
behalten, funktioniert offensichtlich im Moment erst einmal (noch)
nicht, sonst wäre dir das mit dem Schlafen gehen, während die UART
noch sendet, nicht passiert.

von Tommy (Gast)


Lesenswert?

Verstehe was du meinst!
Hab mir mal ein Ablaufplan auf einen Zettel gekritzelt. Nur so wie ich 
das gelesen habe gibt es kein Bit welches angibt, das der komplette 
String übertragen ist, sonst könnte ich einfach solange in einer 
Schleife bleiben, bis dieses Bit gesetzt ist und danach lasse ich ihn 
wieder schlafen!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tommy wrote:
>
> Hab mir mal ein Ablaufplan auf einen Zettel gekritzelt. Nur so wie ich
> das gelesen habe gibt es kein Bit welches angibt, das der komplette
> String übertragen ist, ...

Ich würde das ungefähr so machen:

. uart_send() trägt das Zeichen in den Puffer ein und aktiviert den
  UDRE-Interrupt

. dieser Interrupt triggert sofort, die ISR schiebt das erste Zeichen
  aus dem Puffer nach UDR; wenn sie feststellt, dass dieses Zeichen
  das letzte im Puffer ist, dann aktiviert sie den TXE-Interrupt

. wenn weitere Zeichen in den Puffer kommen, wird der TXE-Interrupt
  wieder abgeklemmt; der UDRE-Interrupt kümmert sich dann um das
  Nachfüttern des UDR

. wenn der TXE-Interrupt dann zuschlägt, sind keine Zeichen mehr im
  Software-Puffer, und die CPU kann sich (wenn sonst auch nichts mehr
  zu tun ist) schlafen legen.

von Kachel - Heinz (Gast)


Lesenswert?

> Nur so wie ich
> das gelesen habe gibt es kein Bit welches angibt, das der komplette
> String übertragen ist,

Dafür hast Du sogar einen Interrupt... (TX-Complete)

Im UDRE-Int schaufelst Du das nächste Zeichen des Strings, bei Erreichen 
der Ende-Kennung hörst Du damit auf (es gibt ja kein nächstes Zeichen). 
UART schiebt nun das letzte Zeichen raus, wenn das draußen ist, schlägt 
der TXC-Interrupt zu und Du kannst UART deaktivieren (oder auch nicht) 
und den Sleep-Mode (der jetzt Idle war) auf Tiefschlaf umschalten.

KH

von Tommy (Gast)


Lesenswert?

Vielen Dank, muss jetzt allerdings schluß machen, werde mich morgen 
wieder dran setzen. Vielleicht kann dann der Lehrgang ja wieder 
weitergehen ;)

von Tommy (Gast)


Lesenswert?

Es gibt nur noch eine Sache die ich noch nicht verstehe!
Wieso hat das nicht mit dem delay geklappt?
Wenn ich nach der uart_puts() Funktion mehrere Millisekunden warte oder 
sogar eine Sekunde, dann hatte der Mc doch genug Zeit den String 
rauszuschicken wieso hat das denn nicht geklappt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dein delay hat doch geklappt, oder?  Bei 9600 Bd braucht ein Zeichen
halt rund eine Millisekunde.  Daher kannst du mit 5 ms delay halt
zwar "test" raussenden, nicht aber "hello, world!".

von Tommy (Gast)


Lesenswert?

Das stimmt aber ich hatte das delay nachher erhoeht aber dann kam gar 
nichts mehr ueber die Uart

von Tommy (Gast)


Lesenswert?

Wenn ich mit sleep_cpu()
den MC schlafen lasse, gibt es eigentlich ein Flag, welches erkennt, 
dass der MC wieder wach geworden und wieder bereit ist Daten über die 
UART zu schicken?

von Tommy (Gast)


Lesenswert?

Hallo Jörg du hattest recht mit dem delay!
Es lag noch an etwas anderem das er nichts rübergeschickt hat. Erhöht 
man das delay kommen mehr zeichen über die UART. Versuche jetzt eine 
bessere Programmieriung mit HIlfe vom TXC Flag

von Andreas V. (tico)


Lesenswert?

Jörg Wunsch wrote:
> . wenn weitere Zeichen in den Puffer kommen, wird der TXE-Interrupt
>   wieder abgeklemmt; der UDRE-Interrupt kümmert sich dann um das
>   Nachfüttern des UDR

Diesen Schritt kann man sich nach meiner Erfahrung sparen, da der 
UDRE-Interrupt immer vor dem TXE kommt. Es wird also rechtzeitig 
"nachgelegt", so dass der TXE erst auftritt, wenn der UDRE-Interrupt 
keine weiteren Zeichen mehr abschicken konnte, weil keine mehr da sind.

Gruss
Andreas

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.