mikrocontroller.net

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


Autor: alias_lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)
/************************************************

      Wetterstation
      ATmega8    XTAKL 4Mhz
          22.08.2009

************************************************/


volatile unsigned int zeit = 0;
volatile unsigned int dauer = 0;


#define F_CPU 4000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>

#include "uartsubs.h"



int main (void)
{
  char buffer2[16];

  uartinit(9600);

  // Port Init
  PORTD |= (1<<PD2);  // Pullup für Windsensor

  
  // Timer init
  TCCR0 |= (1<<CS01) | (1<<CS00);
  TIMSK |= (1<<TOIE0);

  // Interrupt
  GICR |= (1<<INT0);    // INT0 frei
  MCUCR |= (1<<ISC01);  // Fallende Flanke
  sei();



  for(;;)
  {

    putbyte('#');

    itoa(dauer,buffer2,10);
    uart_puts(buffer2);

    putbyte('\r');
    putbyte('\n');

    for(unsigned char i=0;i<100;i++)
      _delay_ms(10);
  }
}

ISR(INT0_vect)
{
  cli();
  dauer = zeit;      // Zeit speichern
  zeit = 0;

  for(unsigned char i=0;i<15;i++)  // Prellen abwarten, dauert 150 us Maximal
    _delay_us(10);

  sei();
}


ISR(TIMER0_OVF_vect)  // alle 1,008ms
{
  zeit++;
  TCNT0 = 192;
}

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

Danke

Autor: Carl (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Carl (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: alias_lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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:
  dauer = zeit;      // Zeit speichern
  zeit = 0;
...
  dauer = zeit;      // Zeit speichern
  zeit = 0;
Ergo: "dauer" in main immer 0.

Autor: alias_lars (Gast)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, am Ende der ISR.

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

Autor: alias_lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: alias_lars (Gast)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: alias_lars (Gast)
Datum:

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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.