Forum: Compiler & IDEs Delay in Interrupt - routine


von Fabian (Gast)


Lesenswert?

HI

Mein Interrupt 0 ist durch ein Taster gesteuert.

Ich möchte die variable "press" inkrementieren, wenn der Taster nur
kurz gedrückt ist.

Wenn der Taster jedoch 2sec  lang gedrückt wurde, soll auf dem Port C
(LED's) 0xAA ausgegeben werden!

Nun mein Problem:
Ich simuliere das Programm im AVR Studio und die while Schlaufe
funktioniert nur, wenn ich darin kein Delay aufrufe!
Wieso ist das so?

THANKS




SIGNAL(SIG_INTERRUPT0)
{
  unsigned char s;

  _delay_loop_2(60000);                   //entprellen
  _delay_loop_2(60000);
  _delay_loop_2(20000);

  TIMSK = (0<<TOIE0);                     //Timer0 abschalten
  do
  {
    _delay_loop_2(10000);           // 10ms Delay
    int_flag = bit_is_set(PIND,3);
    PORTC = int_flag;
    s++;
  }
  while ( (int_flag >= 1) && (s <= 200) );

  if (s >= 200)
  {
    PORTC = 0xAA;
  }
  else
  {
    press++;
    address = press * 1000;
    if (press >= 15) press = 0;
    PORTC = ~press;
    TIMSK = (0<<TOIE0);
  }
  GIFR = (1<<INTF0);
}

von Jörg Wunsch (Gast)


Lesenswert?

Interruptroutinen macht man so kurz wie möglich.  Delays verbieten
sich damit automatisch.

Benutze einen Timer dafür, den Du aus dem Interrupt nur anschiebst
bzw. Software-Timer.

von Fabian (Gast)


Lesenswert?

Also die ersten Delay's (140ms) sind Warteroutinen, damit der Taster
entprellt ist...

Danach wird im "normal" - Betrieb des Tasters nur noch ein 10ms Delay
dazu addiert! also 150ms! (ansonsten prellt mein Taster)

Wenn dann aber der Taster 2sec lange gedrückt wird, soll mein uC in den
Sleep Modus (OFF) versetzt werden!

Sollte man das wirklich nicht mit Delays machen?


(Ich glaube es liegt daran, dass ich eine Variable (s) in der ISR
definiert habe!

von Jörg Wunsch (Gast)


Lesenswert?

Ja, man sollte das wirklich nicht mit delays machen.

Laß einen Timer z. B. alle 10 ms ticken, dort kannst Du das
dranhängen.  Damit lassen sich dann beliebige Vielfache von 10 ms als
Verzögerungen realisieren, auch die Entprellung selbst.

Ich habe sowas auch schon gemacht, allerdings ist das Projekt nie
wirklich fertig geworden (daher mag ich es nicht veröffentlichen).
Aber genau diese Aufgabe, unterschiedliche Funktionalität je nachdem,
ob Taster kurz oder lange gedrückt, habe ich damit realisiert.

von Fabian (Gast)


Lesenswert?

Also sollte ich in der INT0 Routine nur ein Flag setzen und alles andere
im Hauptprogramm machen???

Hmm... werde es mal versuchen!

Vielen Dank noch...

von Michael (Gast)


Lesenswert?

Genau so.
Also Timer-IRQ kurz genug. Dann Taste bei jedem IRQ prüfen, bei
gedrückter Taste Flag setzen. Bei jedem folgenden IRQ Zähler
hochzählen.
Falls Taste nicht mehr gedrückt, wenn der Zähler kleiner als die
Prellzeit ist, Flag löschen.
Ansonsten weiterzählen und weiterprüfen. Wenn dann die Taste wieder
losgelassen wird, Zähler überprüfen und wenn kleiner 2s dann Flag für
einfachen Tastendruck setzen oder aber für 2s-Tastendruck.

von Jörg Wunsch (Gast)


Lesenswert?

Eins nur noch: es empfiehlt sich, einen externen Interrupt innerhalb
der Interruptroutine als allererstes mal abzuschalten, ansonsten
bekommst Du aufgrund des Tastenprellens X Stück davon ausgeliefert.
Erst nach Ablauf der Entprellzeit schaltest Du ihn dann wieder zu.

Ggf. kann man ihn dann auch ,negiert' zuschalten und das Ganze
herumdrehen, dann bekommst Du ein Ereignis geliefert, wenn der Nutzer
die Taste wieder losläßt (und mußt danach natürlich die Negation
wieder negieren ;-).

Aber das Prinzip ist richtig, in der Interruptroutine macht man nur
das, was wirklich zeitkritisch ist, für den Rest setzt man ein Flag
und macht es später in der Hauptschleife (`volatile' nicht
vergessen!).

von Fabian (Gast)


Lesenswert?

Erstmal vielen vielen Dank für die vielen Ideen.

So wie ich das jetzt verstehe, muss ich in der Interruptroutine
folgendes machen:


1. Interrupt ausschalten
2. Taster_flag = 1
3. entprellzeit abwarten (delay) -> wie lange ist das üblicherweise?
4. Interrupt wieder einschalten


In der Timer routine mache ich folgendes:


1. zähler inkrementieren
2. Taster - Pin einlesen
3. Timer zurückstellen


Im Hauptprogramm

Endlosschlaufe, die verlassen wird, wenn der Taster losgelassen wird,
oder der Zähler auf dem Endwert ist...


Funktioniert das so? -> Vorallem das Entprellen würde mich wunder
nehmen, denn jetzt habe ich ja wieder ein Delay in der ISR.

von Michael (Gast)


Lesenswert?

Ich würde den externen IRQ gar nicht nutzen. Port einfach per IO
abfragen. In der Timer-IRQ, und dann jeden Durchlauf abfragen.
Und in der Hauptschleife den MC in den idle-Mode schicken, wird durch
die timer-IRQs jeweils aufgeweckt und falls dann ein Flag gesetzt
wurde, dann wird die entsprechende Routine aufgerufen.

von Jörg Wunsch (Gast)


Lesenswert?

Externen Interrupt kann man schon nehmen, aber eben nicht dort warten.

Ungefähr so:

Timer-Interrupt tickt alle 10 ms und sieht nach, was alles an
Aufträgen da ist.  Diese werden abgearbeitet.  (Bei mir in einer
verketteten Liste, aber das ist ziemlich aufwendig und braucht
außerdem malloc().)

Externer Interrupt läuft ein, klemmt sich erstmal ab, und gibt einen
Auftrag für den Timerinterrupt nach 20 ms (was effektiv heißt,
irgendwas zwischen 10 und 20 ms) auf.

Der Timer-Auftrag prüft, ob die Taste wirklich noch gedrückt ist.
Wenn nicht -> alles Spaß, zurück über "Los!".  Wenn ja: registrieren
des negierten Interrupts (Loslassen der Taste), neuer Zeitgeberauftrag
um festzustellen, ob die Taste lange gedrückt worden ist.

Je nachdem, ob nun zuerst das Ereignis "Taste losgelassen" oder
"langer Zeitgeber" eintrifft, werden die weiteren Aktionen ausgelöst.

von F___ (Gast)


Lesenswert?

also das mit dem negierten Interrupt pack ich nicht ganz?

von Jörg Wunsch (Gast)


Lesenswert?

> also das mit dem negierten Interrupt pack ich nicht ganz?

Wie ich oben schon mal schrob: Du registrierst den Interrupt auf der
jeweils entgegengesetzten Flanke.  Wenn also anfangs die H->L Flanke
den Interrupt ausgelöst hat (Taster gegen Masse), dann regisrierst Du
danach (nach Ablauf der Entprellzeit) einen, der auf die L->H Flanke
reagiert, damit bekommst Du ein Ereignis gemeldet, wenn der Taster
wieder losgelassen wird.

von Tipper (Gast)


Lesenswert?

@Jörg Wunsch:

Ich verfolge diesen Thread sehr aufmerksam, da es für mich einiges zu
lernen gibt. In einen obigen Beitrag hast Du geschrieben: 'volatile
nicht vergessen'. Kannst Du das bitte genauer erklären?

Besten Dank  Tipper

von Jörg Wunsch (Gast)


Lesenswert?

FAQ #1 lesen.

Kurz:

volatile struct {
  intbit_0: 1;
  intbit_1: 1;
  /* ... */
} intbits;

SIGNAL(SIG_FOO)
{
  /* ... */
  intbits.intbit_0 = 1;
}

...
int
main(void)
{
  ...
  for (;;) {
    if (intbits.intbit_0) {
      intbits.intbit_0 = 0;
      /* handle interrupt here */
    }
  }
}

von Peter D. (peda)


Lesenswert?

Ich staune immer wieder, wie aufwendig und fehleranfällig Leute eine
Tastenentprellung und Abfrage hinkriegen, nur weil sie unbedingt den
völlig ungeeigneten externen Interrupt verwenden wollen.

Am sichersten und einfachsten ist und bleibt doch der Timerinterrupt:

http://www.mikrocontroller.net/forum/read-4-20549.html


Peter

von Stefan Kleinwort (Gast)


Lesenswert?

Sehe ich genauso wie Peter.

Wer seine Tasten im Timer-IR abfragt (alle 10 - 20ms), wird die
Probleme  mit Tastenprellen bald nicht mehr verstehen können.

Tasten an IR-Eingängen machen für mich dann Sinn, wenn mit diesen
Tasten die CPU aus dem Sleep geweckt werden soll.

Stefan

von Michael (Gast)


Lesenswert?

Sag ich doch ;)

von Stefan Kleinwort (Gast)


Lesenswert?

Sorry, wollte natürlcih schreiben:

"Sehe ich genauso wie Peter und Michael" :-)))

Stefan

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.