Forum: Mikrocontroller und Digitale Elektronik externer Interrupt 0 --> Entprellung funktioniert nicht !


von Stefan Z. (derdespot)


Lesenswert?

Hallo,

ich möchte mit einem Taster 3 LED's ein- und ausschalten. Ich nutze den 
Interrupt 0 an PORTD.2. Ich habe versucht den Taster zu entprellen! 
Leider funktioniert es nicht richtig. Die LED's gehen oft beim loslassen 
des Taster aus oder andersrum oder reagieren gar nicht.

Könnt ihr mir helfen?

Vielen Dank schonmal...

Stefan

//----------------------------------------------------------------------
// Titel     : externer Interrupt 0 & Entprellung (Ein- und Ausschalter)
//----------------------------------------------------------------------
// Funktion  : 3 LED's werden über Taster ein- und ausgeschalten
// Schaltung : Taster 1 an PORTD.2, LED's an PORTB.0-2
//----------------------------------------------------------------------
// Prozessor : ATmega8, 3.6864 MHz
// Sprache   : C
// Datum     : 07.06.2008
// Version   : 1.0
// Autor     : Stefan
//----------------------------------------------------------------------
#define F_CPU 3686400
#include <avr\io.h>
#include <inttypes.h>
#include <avr\interrupt.h>
#include <util/delay.h>
//----------------------------------------------------------------------
volatile unsigned char buffer; // globale Variable
volatile unsigned char toggle; // globale Variable
//----------------------------------------------------------------------
SIGNAL(SIG_INTERRUPT0){

  if(toggle == 0b00000001){

    _delay_ms(50); // entprellen
    _delay_ms(50);

              buffer = 0b00000111;
              toggle = 0b00000000;
        }

        else if(toggle == 0b00000000){

              _delay_ms(50); // entprellen
          _delay_ms(50);

            buffer = 0b00000000;
    toggle = 0b00000001;
  }
}
//======================================================================
main(){

    DDRB  = 0xff;               // PortB = Output
    PORTB = 0xff;    // alle LEDs ON
    DDRD  = 0x00;               // PortD = Input
    PORTD = 0b00000100;         // pull-up
    GICR  = 0x40;               // enable external int0 Bit 6
    MCUCR = 0x02;               // falling egde: int0 Bit 1

    sei();                      // enable interrupts

    toggle = 0b00000001;

    do {

    PORTB = buffer;

    } while (true);        // Mainloop
}
//======================================================================

von Karl H. (kbuchegg)


Lesenswert?

Auch wenn du in deiner ISR Wartezeiten drinn hast, ändert
das nichts an der Tatsache, dass der µC mit jedem Prellen
eine erneute Interruptanforderung registriert. Wenn das
Programm zu diesem Zeitpunkt in der ISR steckt, dann wird diese
Anforderung nicht einfach ignoriert sondern registriert und
abgearbeitet, wenn der Prozessor aus der ISR wieder herauskommt.

Warum verwendest du nicht einfach diese Tasten-Einlese-und-Entprell-
ISR zusammen mit einerm Systemtimer?
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

Die ist 100% zuverlässig.

von Stefan Z. (derdespot)


Lesenswert?

Hmmm... Ich versteh das Programm von deinem Link nicht so ganz. Ich bin 
noch ein absoluter Anfänger. Kann man mein Programm nicht einfach ein 
bissel abändern, so das es funktioniert??

von Peter D. (peda)


Lesenswert?

Stefan Z. wrote:
> SIGNAL(SIG_INTERRUPT0){
...
>     _delay_ms(50); // entprellen

Grrr, brauchbare Interrupthandler sehen anders aus.

Da rotiert ja Konrad Zuse mit 10.000U/min im Grabe.
Solch monströse Rechenzeitvergeudung hätte er nicht mal auf seinem 
Relais-Rechner zugelassen.


Peter

von Stefan Z. (derdespot)


Lesenswert?

bzw. kann ich nicht irgendwie einstellen, das Interrupts während der 
Abarbeitung eines interrupts unterbunden werden?

von Peter D. (peda)


Lesenswert?

Stefan Z. wrote:
> Hmmm... Ich versteh das Programm von deinem Link nicht so ganz. Ich bin
> noch ein absoluter Anfänger. Kann man mein Programm nicht einfach ein
> bissel abändern, so das es funktioniert??

Nein, ein bissel abändern hilft nicht, man muß es richtig machen.
Ein bissel falsch ist immer noch falsch.

Ich sehe ein, daß man als Anfänger Probleme hat, die Funktionsweise 
eines Interrupts zu verstehen.

Lies Dir im Datenblatt durch, welche Bedingungen erfüllt sein müssen, 
damit ein Interrupthandler ausgeführt wird.
Diese Bedingungen müssen nicht gleichzeitig erfüllt werden.
Insbesondere das Interrupt Pending Flag kann schon viel früher gesetzt 
werden (z.B. während gerade ein Interrupthandler in Arbeit ist).



Peter

von 3358 (Gast)


Lesenswert?

Tasten nimmt man auf den Timer. Alle 10ms werden die Tasten gelsesen und 
erst wenn zwei mal hintereinander derselbe Wert da ist wird er 
akzeptiert.

von Daniel (Gast)


Lesenswert?

Hallo,

Nie lange im Interrupt verbleiben! Ein Interrupt hat so kurz wie 
moeglich und so kurz wie noetig zu sein!

Wenn du es aber wirklich so machen moechtestm, musst du am Ende der 
Routine das Flag fuer die ISR wieder loeschen.
Das Flag fuer die ISR(Interrupt 0) findest du im Interrupt Flag 
register. Am Ende des Datenblatts ist eine grosse Tabelle.

Da hat Peter vollkommen Recht, so eine Routine im Interrupt ist absolut 
nosens.
Da ein Interrupt hoehere Prioritaet als der Code der Hauptschleife des 
Programmes wird das Programm unterbrochen und der Interrupt 
abgearbeitet. Wenn du da jetzt nur mit einem Delay eine Zeit abwartest 
(was nichts weiter macht als zaehlen bis ein Wert welcher der Delayzeit 
entspricht, erreicht ist) wird dein Hauptprogramm nicht fortgesetzt. Was 
das sich dann spaeter in deinem Programm bemerkbar macht, das reaktionen 
ausgeloest durch mehrere Taster, sehr spaet erfolgen. Bei einem wirst du 
das noch nicht so mitbekommen, aber bald :), wenn du dann die Schaltung 
erweiterst.

Mit freundlichen Gruessen
Daniel

von Stefan Z. (derdespot)


Lesenswert?

Hi, danke für die die Beiträge. Was ich eigentlich vorhabe: Ich habe 2 
Taster zur Verfügung. Der Eine soll mein Programm 
Starten(weitermachen)/Stoppen(anhalten) der zweite Taster soll das 
Programm reseten. Zum Reseten nutz ich einfach den Reset Taster meines 
Eval Boards.

Dieses Programm funkioniert sehr gut-->

/*********************************************************************** 
*/
/* 
*/
/*                      Debouncing 8 Keys 
*/
/*                      Sampling 4 Times 
*/
/*                      With Repeat Function 
*/
/* 
*/
/*              Author: Peter Dannegger 
*/
/*                      danni@specs.de 
*/
/* 
*/
/*********************************************************************** 
*/

http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

Aber ich verstehe nicht so ganz wie die Repeat Funktion funktioniert 
bzw. wie ich sie aus dem Code entferne. (bzw. ich bin grade dabei) Kann 
ich mit dem Code während des gesammten Programms einen Interrupt 
auslösen? Hab ich das richtig verstanden, dass der Timer läuft und ein 
Port PIN während des gesammten Hauptprogramms abgefragt wird, und dann 
die Funktion (im Beispiel LED on/off) auslöst?

von Hannes L. (hannes)


Lesenswert?

Ich kenne zwar nur Peters ASM-Version der Entprellung, die C-Version 
dürfte aber (fast) ganauso arbeiten...

Die Entprellung funktioniert etwa so:

Im Timer-Interrupt (alle etwa 10ms) wird

- der Port (das Register PINx) eingelesen,

- invertiert, da die Taster L-aktiv sind,

- Änderung erkannt und Prellzähler bedient,

- wenn Prellzähler überläuft (4 Runden lang neuer Zustand gegenüber
  vorher), dann wird

  * der neue Zustand übernommen,

  * ein Bit in einem Merker gesetzt (Taste wurde betätigt),

  * der Repeat-Zähler heruntergezählt...

Bei Ablauf des Repeat-Zählers wird dieser auf den Startwert (der kann 
unterschiedlich für erste Wiederholung und folgende Wiederholungen sein) 
gesetzt und eine weitere Aktion ausgelöst, also entweder durch Setzen 
des Bits im Tastendruckmerker ein erneuter Tastendruck simuliert oder 
ein anderer Merker für andere Zwecke gesetzt.

Die Mainloop fragt nun die Tastendruck-Merker ab und verzweigt bei 
gesetztem Merker zu der Routine, die die gewünschte Aktion ausführt. 
Diese Routine löscht den Merker (das Bit) und führt die Aktion aus.

In ASM heißen Peters Tastendruck-Merker "key_press" (eine Taste wurde 
gedrückt, jedes Bit repräsentiert eine andere Taste) und "key_state" 
(entprellter Zustand zum Vergleich mit den Neuwerten). In C dürften die 
Variablen ähnlich heißen.

...

von 3358 (Gast)


Lesenswert?

Ich mach das jeweils sogar noch einfacher.

Im timerinterrupt eine Boolean setzen : Timercame=1

Im schnellen Hauptprogramm abfragen ob der timer gekommen ist :

if (timercam==1) {
  .. entprell ..
  ..
  timercame=0; }

von Hannes L. (hannes)


Lesenswert?

3358 wrote:
> Ich mach das jeweils sogar noch einfacher.
>
> Im timerinterrupt eine Boolean setzen : Timercame=1
>
> Im schnellen Hauptprogramm abfragen ob der timer gekommen ist :
>
> if (timercam==1) {
>   .. entprell ..
>   ..
>   timercame=0; }

Das wird nie funktionieren... (da Timercame und Timercam zwei völlig 
unterschiedliche Variablen sind...)

Aber Spaß beiseite, wenn ich die Entprellvariablen im RAM füren muss und 
noch weitere Interrupts aktiv sind, dann läuft die Entprellung auch bei 
mir in der Mainloop, synchronisiert über einen Merker von einem 
Timer-Interrupt.

...

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.