Forum: Mikrocontroller und Digitale Elektronik Tasten entprellen ohne _delay_ms()


von Max (Gast)


Lesenswert?

Also, nachdem ja delay funktionen in ISR verpönt sind und cih im Moment
nciht weiter weiß, folgede Frage:

Wie (außer mit 2 delays wie im AVR GCC Tutorial beschrieben), kann ich
einen Pin entprellen?

In meinem jetzigen Programmcode frage ich pins(aktiv low am STK)
einfach so ab:

if (!(PIND & (1<<PIN0)))
{


}

Wie "entprelle" ich eine solche Abfrage?
Habe mir ebreits das beispiel angesehen, wo 8 PINS gleichzeitig
entprellt werden, allerdings war mir das etwas zu hoch...
Vielleciht kann mir jemand ein einfaches Beispiel zeigen?!
Danke!

von müllo (Gast)


Lesenswert?

da gibt es mehrere Möglichkeiten...

(1) Wenn du mit externer Beschaltung arbeiten willst, baust du ein
R-C-Glied als Tiefpass oder mittels Flip-Flop an den Pin.

(2) Softwaremäßig gibt es auch unterschiedliche Lösungen. Bei den
meisten meiner "kleinen Projekte" nutze ich eh einen Timer, meist mit
einem Timertick von 0,1 ... 1 ms. In den entsprechenden ISR lasse ich
dann je nach Anzahl verschiedene Variablen (=> Zeitbasis) zählen. Im
Hauptprogramm frage ich dann meist ganzzahlig Vielfache
(modulo-Division) der Zählvariablen ab. Bei normalen Schaltern hast du
meist eine Prellzeit im Bereich zwischen 5...10 ms. Also könntest Du 10
Timerticks vergehen lassen und schaust dann, ob der Signalpegel noch
anliegt. Wenn nicht, ist der jeweiligen Schalter "nicht sicher"
geschaltet worden.

Viele Grüße
müllo

von Max (Gast)


Lesenswert?

Wie gesagt, das Beispiel von Peter Fleury ist mir zu hoch:

Allein verstehe ich diese Aussage nicht:

/*
   * read current state of keys (active-low),
   * clear corresponding bit in i when key has changed
   */

  i = key_state ^ ~PIND;   // key changed ?

1.)Warum  macht man ein XOR zwishcen keystate und dem INVERTIRTEN Pin?
2.)keystate enthält zu der Zeit des codes noch gar kein Pin Abfrage
3.)warum "clear bit, when key has changed"?
   Soweit ich weiß ist das Ergebnis eine XOR verknüpfung "1", wenn
die zwei verglichenen Bits unterschiedlich waren..?!

von Max (Gast)


Lesenswert?

@müllo:

Danke erstnal für die Antwort, hast du zu deiner Erklärung
möglicherweise irgendein Code Schnipsel?
Ich würde die Entprellung schon gerne softwaremäßig(und
ressourcenschonend) machen, wenn irgendwie möglich..

von müllo (Gast)


Lesenswert?

Ich würde die Sache auf die Schnelle so machen:

if (!(PIND & (1<<PIN0))) key = 1;
  else key = 0;
if ((timerticks % 10 == 1) & (!(PIND & (1<<PIN0))) & (key ==1))
  {
    //mach etwas...
  }
  else
  {
   //mach etwas nicht...
  }

Ohne Gewähr - habe ich gerade zwischen Aufräumen und Heimgehen
gemacht...

mfg
müllo

von peter dannegger (Gast)


Lesenswert?

@Max,

mach mal nen Link auf den ganzen Code, damit man mitreden kann.

So mit 3 Zeilen ausm Zusammenhang raus geht das nicht.


Peter

von Max (Gast)


Lesenswert?

@Peter: Hier ist de komplette Code(zum entprellen von 8 PINS)


/*********************************************************************** 
*****
 Title:    Debouncing 8 Keys
 Author:   Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury,
           based on algorithm of Peter Dannegger <danni@specs.de>
 Date:     December 2003
 Software: AVR-GCC 3.3
 Hardware: AT90S8515 at 4 Mhz, STK200 compatible starter kit

 Description:
 Demonstrates debouncing 8 keys. The state of the eight keys is
sampled
 four times using a timer interrupt. If a key is pressed longer than
 four seconds, the corresponding bit in the key_press global variable
is set.
 The main loop checks if a bit is set in this global variable and
resets
 the bit.

 Pressing the key connected to PIND2 increments a counter, while
pressing
 the key connected to PIND3 decrements the counter.
 The value of this counter is displayed using LEDs on PORTB.

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

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>


#ifndef CTC1
#define CTC1 WGM12              // for compatibility with ATmega
#endif


#define XTAL    4000000L    // Crystal frequency in Hz
#define DEBOUNCE  200L    // debounce clock 200Hz = 5msec


/*
 * Module global variable, informs the main programm when a key is
pressed.
 * This variable must be declared 'volatile, since it is accessed
from
 * both interrupt und main loop.
 */
static volatile uint8_t key_press;


SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  static uint8_t key_state;  // debounced and inverted key state:
  static uint8_t ct0, ct1;      // holds two bit counter for each key
  uint8_t i;


  /*
   * read current state of keys (active-low),
   * clear corresponding bit in i when key has changed
   */
  i = key_state ^ ~PIND;   // key changed ?

  /*
   * ct0 and ct1 form a two bit counter for each key,
   * where ct0 holds LSB and ct1 holds MSB
   * After a key is pressed longer than four times the
   * sampling period, the corresponding bit in key_state is set
   */
  ct0 = ~( ct0 & i );      // reset or count ct0
  ct1 = (ct0 ^ ct1) & i;      // reset or count ct1
  i &= ct0 & ct1;      // count until roll over ?
  key_state ^= i;      // then toggle debounced state

  /*
   * To notify main program of pressed key, the correspondig bit
   * in global variable key_press is set.
   * The main loop needs to clear this bit
   */
  key_press |= key_state & i;  // 0->1: key press detect

}


int debounce( void)
{
  uint8_t count = 0;


  TCCR1B = _BV(CTC1) + _BV(CS10);  // clear timer on compare match, no
prescaler
  OCR1A  =  XTAL/DEBOUNCE;         // timer = 5 msec
  TIMSK  = _BV(OCIE1A);            // enable Output Compare 1 overflow
interrupt

  DDRB  = 0x00;                    // use all pins on PortD for input
  PORTD = 0xff;                    // with pull-up enabled

  DDRB  = 0xff;                    // use all pins on PortB for output

  PORTB = 0xff;                    // turn all LED off

  sei();                           // enable interrupt

  for(;;)
  {
    if (key_press & _BV(2) )
    {
        // key 2 pressed, increment counter
        count++;
        PORTB = ~count;

        key_press = 0;
    }
    else if ( key_press & _BV(3) )
    {
        // key 3 pressed, decrement counter
        count--;
        PORTB = ~count;

        key_press = 0;
    }
  }

}

von Max (Gast)


Lesenswert?

@müllo:


if (!(PIND & (1<<PIN0))) key = 1;
  else key = 0;

Ist mir klar; einfache (invertierte) Abfrage, ob Switch gedrückt

if ((timerticks % 10 == 1) & (!(PIND & (1<<PIN0))) & (key ==1))

Diese Ziele kapier ich net ganz:
-->if ((timerticks % 10 == 1)

Du meinst, wenn der zustand nach 10 timertikt nach wie vor der selbe
geblieben ist?

von Max (Gast)


Lesenswert?

Hmm, irgendwie schnall cih das ganze net so ganz....
Das kann doch nicht so kompliziert sein?!
Im Prinzip suche ich en verenifachte Version von dem oben geposteten
Code von Peter Fleury...

Statt 8 Pins reicht mir einer

von sni (Gast)


Lesenswert?

3-4 mal mit etwas abstand in einer timer isr einlsen und nach einem
wechesl bzw. einer flanke 001 oder so ausschauen

von Hannes L. (hannes)


Lesenswert?


von müllo (Gast)


Lesenswert?

- Ich lassen einen Timer mit T=1ms laufen
- in der Interrupt-Service-Routine (ISR) für den Timerüberlauf lasse
ich die Variable timertick hochzählen (evtl. bei bestimmten Zählerstand
rücksetzen)
=> timerticks wird alle 1ms um 1 erhöht und meinetwegen beim Typ char
bei einem Zählerstand von 219 auf 0 zurückgesetzt

if (!(PIND & (1<<PIN0))) key = 1;
  else key = 0;
//key ist eine Hilfsvariable, mit der ich sehe, ob die Taste am PortD
gedrückt wurde

if ((timerticks % 10 == 1) & (!(PIND & (1<<PIN0))) & (key ==1))
//Wenn der Rest der Division durch 10 = 1 ist (Zählerstand
1,11,21,31...) und die Taste an PortD dauerhaft gedrückt ist, ist die
Tasteneingabe gültig (ansonsten wäre key auf 0 zurückgegangen)

  {
    //mach etwas bei gültiger Tastenabfrage
  }
  else
  {
   //mach etwas bei ungültiger Tastenabfrage
  }

Mfg
müllo

von Baku (Gast)


Lesenswert?

Moinsen!
Manche machen sich das Leben echt schwer...
Wenn ich den Tastenzustand ohnehin in einem Timerinterrupt abfrage,
dann muss ich einfach nur das Abfrageintervall länger als die maximale
Prelldauer machen und fertig ist die Laube.

von peter dannegger (Gast)


Lesenswert?

@Max

"* clear corresponding bit in i when key has changed"

Ich würd mal sagen, da hat sich Peter Fleury verschrieben.
Wie Du ja richtig erkannt hast, wird das Bit gesetzt.


Ansonsten hat Dir ja ...HanneS... Lux den original Link gegeben und da
sind auch ne Menge Erklärungen dabei.
Es dauert nur etwas, diesen langen Thread zu lesen (aber es lohnt
sich).

Diese 8-Tasten-Routine hat nämlich den Vorteil, daß sie kürzer und
schneller als die meisten 1-Tasten Routinen ist.
Sie lohnt sich also schon bei nur einer Taste.

Und oftmals bleibt es ja nicht bei einer Taste. Man hat also das schöne
Gefühl, für die Zukunft vorgesorgt zu haben.


Peter

von peter dannegger (Gast)


Lesenswert?

P.S.:

Sourcen bitte das nächste mal als Dateianhang.


Peter

von müllo (Gast)


Lesenswert?

@Baku:

Klar kannst du es so einfach machen. Wenn man aber mehrere
unterschiedlichen Zeitbasen benötigt und keine freien Timer hat, hilft
die Sache mit den timerticks...ansonsten hast du natürlich Recht.

mfg
müllo

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.