Forum: Mikrocontroller und Digitale Elektronik Interrupt varible in main immer 0


von alias_lars (Gast)


Lesenswert?

Hallo,

ich arbeite gerade mit einem Windsensor und muss die Zeit messen die für 
eine Umdrehung gebraucht wird. Dafür läuft der Timer0 und liefert alle 
1ms einen interrupt. In der ISR zähle ich eine Varible hoch.
Der windsensor löst bei jeder Umdrehung einen Interrupt aus. In dieser 
ISR möchte ich dann die verstrichene Zeit in eine andere Varible retten 
und die zählvariable auf null setzen. Das funktioniert auch alles und 
die Variablen haben auch in den ISRs ihre werte. Aber sobald ich im Main 
die Dauer auswerten möchte ist sie dort immer 0. Trotz volatile bei der 
Variable ;-)
1
/************************************************
2
3
      Wetterstation
4
      ATmega8    XTAKL 4Mhz
5
          22.08.2009
6
7
************************************************/
8
9
10
volatile unsigned int zeit = 0;
11
volatile unsigned int dauer = 0;
12
13
14
#define F_CPU 4000000UL
15
16
#include <avr/io.h>
17
#include <avr/interrupt.h>
18
#include <stdlib.h>
19
#include <util/delay.h>
20
21
#include "uartsubs.h"
22
23
24
25
int main (void)
26
{
27
  char buffer2[16];
28
29
  uartinit(9600);
30
31
  // Port Init
32
  PORTD |= (1<<PD2);  // Pullup für Windsensor
33
34
  
35
  // Timer init
36
  TCCR0 |= (1<<CS01) | (1<<CS00);
37
  TIMSK |= (1<<TOIE0);
38
39
  // Interrupt
40
  GICR |= (1<<INT0);    // INT0 frei
41
  MCUCR |= (1<<ISC01);  // Fallende Flanke
42
  sei();
43
44
45
46
  for(;;)
47
  {
48
49
    putbyte('#');
50
51
    itoa(dauer,buffer2,10);
52
    uart_puts(buffer2);
53
54
    putbyte('\r');
55
    putbyte('\n');
56
57
    for(unsigned char i=0;i<100;i++)
58
      _delay_ms(10);
59
  }
60
}
61
62
ISR(INT0_vect)
63
{
64
  cli();
65
  dauer = zeit;      // Zeit speichern
66
  zeit = 0;
67
68
  for(unsigned char i=0;i<15;i++)  // Prellen abwarten, dauert 150 us Maximal
69
    _delay_us(10);
70
71
  sei();
72
}
73
74
75
ISR(TIMER0_OVF_vect)  // alle 1,008ms
76
{
77
  zeit++;
78
  TCNT0 = 192;
79
}

Ich wäre sehr dankbar wenn mir jemand sagen/ helfen könnte warum die 
variable "dauer" im main immer 0 ist.

Danke

von Carl (Gast)


Lesenswert?

Nicht direkt zu deinem Problem, aber die beiden Befehle "cli ();" und 
"sei ();" in der Interruptroutine sind meines Wissens nach überflüssig. 
Denn alle weiteren Interrupts werden während der Abarbeitung eines 
Interrupts automatisch gesperrt.

von Stefan E. (sternst)


Lesenswert?

> Ich wäre sehr dankbar wenn mir jemand sagen/ helfen könnte warum die
> variable "dauer" im main immer 0 ist.

Weil durch das Prellen die INT0-ISR zwei mal direkt hintereinander 
ausgeführt wird. Und wegen dem sei am Ende wird vom Main-Code nicht mal 
der eine obligatorische Befehle dazwischen ausgeführt, so dass das 
"immer" sogar tatsächlich zu 100% stimmt. Und nein, die Schleife in der 
ISR verhindert das nicht.

von Carl (Gast)


Lesenswert?

Wenn ich mich nicht irre, dann habe ich einen weiteren Fehler entdeckt 
der allerdings nicht direkt mit deinem Problem zusammenhängt.

Wenn ich mich nicht verrechnet habe, so wird deine ISR (TIMER0_OVF_vect) 
nicht jede Millisek. ausgeführt, sondern nur alle 4,1 Millisek.

von alias_lars (Gast)


Lesenswert?

Stimmt, das cli() und sei() in der ISR INT0 ist sinnlos weil int0 sowiso 
höchste priorität hat.

warum soll die Schleife da nicht helfen?
Beim schalten habe ich eine perfekte fallende Flanke, dann 10us pause 
und dann für etwa 110us Prellen. Und in diesen 10us macht mein 
controller immerhin 40 zyklen. da sollte der Interrupt bereits gesperrt 
sein bevor das Prellen eigentlich beginnt.

Timer overflow stimmt schon mit 1ms
4Mhz -> 250ns
250ns*64(vorteiler) -> 16us
16us * (256-192) -> 1,024ms

Aber bleiben wir bei der Frage warum die variable "dauer" im main immer 
0 ist. In der isr ist sie es nämlich nicht!

von Stefan E. (sternst)


Lesenswert?

alias_lars schrieb:

> warum soll die Schleife da nicht helfen?
> Beim schalten habe ich eine perfekte fallende Flanke, dann 10us pause
> und dann für etwa 110us Prellen. Und in diesen 10us macht mein
> controller immerhin 40 zyklen. da sollte der Interrupt bereits gesperrt
> sein bevor das Prellen eigentlich beginnt.

Weil in der Zeit, in der du wartest, das Interrupt-Flag durch das 
Prellen schon längst wieder gesetzt ist. Also feuert der Interrupt 
sofort ein weiteres mal.

> Aber bleiben wir bei der Frage warum die variable "dauer" im main immer
> 0 ist. In der isr ist sie es nämlich nicht!

Weil eben die ISR zweimal hintereinander ausgeführt wird, und damit 
ergibt sich quasi dieser Code:
1
  dauer = zeit;      // Zeit speichern
2
  zeit = 0;
3
...
4
  dauer = zeit;      // Zeit speichern
5
  zeit = 0;
Ergo: "dauer" in main immer 0.

von alias_lars (Gast)


Lesenswert?

Ach ich verstehe! Du meinst ich sollte das Interrupt-Flag von INT0 
löschen bevor ich das cli() ausführe.

von Stefan E. (sternst)


Lesenswert?

Nein, am Ende der ISR.

Übrigens, das cli ist eh völlig ohne Funktion.

von alias_lars (Gast)


Lesenswert?

Wunderbar so hats geklappt. Einfach das INT0 Flag im Register GIFR 
zurücksetzten.

Achtung: dazu muss eine 1 an die betreffende Stelle geschrieben werden, 
keine 0! Und GIFR ist nicht bitadressierbar. Also zuerst GIFR in eine 
Variable kopieren. So umschreiben wie man es haben will und dann wieder 
auf GIFR ablegen.

Ich hoffe dieser Beitrag kann anderen auch helfen.

von alias_lars (Gast)


Lesenswert?

Ja, natürlich am Ende, sorry, habe das sei() gemeint,habe mich 
verschrieben.

von Stefan E. (sternst)


Lesenswert?

Bei dir ist aber auch noch etwas Nachhilfe in Sachen Interrupt nötig. 
;-)

Denn das hier ist Quatsch:
> Stimmt, das cli() und sei() in der ISR INT0 ist sinnlos weil int0 sowiso
> höchste priorität hat.

von alias_lars (Gast)


Lesenswert?

Damit meinte ich, dass INT0 nicht vom Timer interrupt unterbrochen wird.

von Stefan E. (sternst)


Lesenswert?

Beim AVR können sich Interrupts grundsätzlich nicht gegenseitig 
unterbrechen, es sei denn man erlaubt es explizit mit einem sei() in der 
ISR. Deshalb ist dein cli/sei nicht nur überflüssig, sondern sogar 
kontraproduktiv. Mit Prioritäten hat das beim AVR nichts zu tun. Die 
Priorität entscheidet nur, welcher Interrupt zuerst kommt, wenn mehrere 
Flags gesetzt sind.

von Hc Z. (mizch)


Lesenswert?

> Und GIFR ist nicht bitadressierbar. Also zuerst GIFR in eine
> Variable kopieren. So umschreiben wie man es haben will und dann wieder
> auf GIFR ablegen.

Nein.  Damit löschst Du alle Interrupt-Flags, die evtl. gerade gesetzt 
sind.  Das mach vielleicht im Moment nix, kann aber später mal peinlich 
werden, wenn das Programm größer wird.

Einfach eine 1 in das gewünschte Bit reinschreiben.  Nichts auslesen, 
die andern Bits null lassen.  Denn alle Bits, die mit 0 beschrieben 
werden, bleiben unverändert (es wird KEINE Null eingeschrieben).

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.