TIMER2_OVF_vect Interrupt Problem

Gast #1245054
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
Moderator Persönliche Seite #1245059
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.
Moderator Persönliche Seite #1245086
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.
Gast #1245106
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
Moderator Persönliche Seite #1245120
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.
Gast #1245144
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
#1245153
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
Moderator Persönliche Seite #1245171
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);
Gast #1245310
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.
Gast #1245336
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
}
Moderator Persönliche Seite #1245389
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.
Gast #1245485
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?
Moderator Persönliche Seite #1245500
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.
Moderator Persönliche Seite #1245540
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.
Gast #1245541
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
#1246202
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren