mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik tastenentprellen von Peter


Autor: udo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Experten,

helft mir doch bitte mal.

Ich verstehe nicht, wie die Funktionen wie z.Bsp.
u8 get_key_press( u8 key_mask )
aufgerufen werden.

Ist diese Zeile in der ISR
key_press |= key_state & i; 

der entsprechende Funktionsaufruf?


Hier das Programm von Peter.
/************************************************************************/
/*                                                                      */
/*    Debouncing 8 Keys        */
/*        Sampling 4 Times        */
/*    With Repeat Function        */
/*                                                                      */
/*     Author: Peter Dannegger                                 */
/*          danni@specs.de                                  */
/*                                                                      */
/************************************************************************/
// Target: ATMega48

#include <io.h>
#include <interrupt.h>

typedef unsigned char  u8;
typedef signed short  s16;

#define  XTAL    8000000L        // 8MHz

#define KEY_PIN    PINB
#define KEY0    0
#define KEY1    1
#define KEY2    2
#define  KEY3    3

#define LED_DDR    DDRC
#define LED_PORT  PORTC
#define LED0    0
#define LED1    1
#define LED2    2

#define REPEAT_MASK  (1<<KEY3^1<<KEY2^1<<KEY1^1<<KEY0)
#define REPEAT_START  10          // after 100ms
#define REPEAT_NEXT  20          // every 200ms

u8 key_state;                // debounced and inverted key state:
                    // bit = 1: key pressed
u8 key_press;                // key press detect

u8 key_rpt;                // key long press and repeat


ISR( TIMER0_COMPA_vect )       // every 10ms
{
  static u8 ct0, ct1, rpt;
  u8 i;

  i = key_state ^ ~KEY_PIN;        // key changed ?
  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
  key_press |= key_state & i;      // 0->1: key press detect

  if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
     rpt = REPEAT_START;        // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;          // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
   }
}



u8 get_key_press( u8 key_mask )
{
  cli();                  // read and clear atomic !
  key_mask &= key_press;                            // read key(s)
  key_press ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}

u8 get_key_rpt( u8 key_mask )
{
  cli();                  // read and clear atomic !
  key_mask &= key_rpt;                              // read key(s)
  key_rpt ^= key_mask;                              // clear key(s)
  sei();
  return key_mask;
}

u8 get_key_short( u8 key_mask )
{
  cli();                // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}

u8 get_key_long( u8 key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}

int main( void )
{
  TCCR0A = 1<<WGM01;              // Mode 2: CTC
  TCCR0B = 1<<CS02^1<<CS00;            // divide by 1024
  OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);    // preload for 10ms
  TIMSK0 = 1<<OCIE0A;              // enable timer interrupt

  LED_PORT = 0xFF;
  LED_DDR = 0xFF;
  sei();

  for(;;){                  // main loop

    switch( get_key_long( 1<<KEY1^1<<KEY0 )){

      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
    }

    switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){

      case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
    }
  }
}

Autor: *.* (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

>> Ich verstehe nicht, wie die Funktionen wie z.Bsp.
>> u8 get_key_press( u8 key_mask )
>> aufgerufen werden.


So, wie in Deinem Codebeispiel.
    switch( get_key_long( 1<<KEY1^1<<KEY0 )){

      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
    }

    switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){

      case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
    }

Speziell für die Funktion get_key_press() sähe das in etwa so aus:
    switch( get_key_press( 1<<KEY3^1<<KEY0 )){

      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY3^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
    }

Da mußt Du nix in der ISR rumfummeln.

Autor: udo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe eine andere Variante von Peters Programm gefunden. Die möchte 
ich in meinen Programmen benutzen. Ich habe an

PD7, PD6 und PD4 Taster, die gegen Ground geschaltet werden. Habe das 
Originalprogramm etwas umgebaut.Also
SIGNAL (SIG_OVERFLOW0)
durch
ISR(TIMER0_OVF_vect)

und

#define KEY0    4    // TASTER AN PD4? Bin mir nicht sicher,das ist bestimmt falsch oder?
#define KEY1    6
#define KEY2    7

ersetzt.

Es funktioniert nicht. Muss ich noch irgendwo dran drehen? Ist das ein 
Programm, dass unter einer älteren Version des avr-gcc Compilers nur 
läuft? Meine Hardware ist o.k., weil die Entprellung mit dem 
Assemblerprogramm von Peter funktioniert.

Hier der Code:

/************************************************************************/
/*                      Debouncing 8 Keys        */
/*      Sampling 4 Times        */
/*      With Repeat Function        */
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/************************************************************************/

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

typedef unsigned char  u8;
typedef signed short  s16;

#define  XTAL    7372800    

#define KEY_PIN    PIND
#define KEY0    4
#define KEY1    6
#define KEY2    7

#define LED_DDR    DDRA
#define LED_PORT  PORTA
#define LED0    4
#define LED1    6
#define LED2    7

#define REPEAT_MASK  (1<<KEY1^1<<KEY2)      // repeat: key1, key2
#define REPEAT_START  50              // after 500ms
#define REPEAT_NEXT  20                // every 200ms

u8 key_state;                      // debounced and inverted key state:
                              // bit = 1: key pressed
u8 key_press;                      // key press detect

u8 key_rpt;        // key long press and repeat

ISR(TIMER0_OVF_vect) 
{
  //{
    static u8 ct0, ct1, rpt;
    u8 i;

    TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);  // preload for 10ms

    i = key_state ^ ~KEY_PIN;    // key changed ?
    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
    key_press |= key_state & i;    // 0->1: key press detect

    if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
       rpt = REPEAT_START;    // start delay
    if( --rpt == 0 )
    {
      rpt = REPEAT_NEXT;      // repeat delay
      key_rpt |= key_state & REPEAT_MASK;
      }
}


u8 get_key_press( u8 key_mask )
{
  cli();          // read and clear atomic !
  key_mask &= key_press;                        // read key(s)
  key_press ^= key_mask;                        // clear key(s)
  sei();
  return key_mask;
}

u8 get_key_rpt( u8 key_mask )
{
  cli();          // read and clear atomic !
  key_mask &= key_rpt;                          // read key(s)
  key_rpt ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}

u8 get_key_short( u8 key_mask )
{
  cli();      // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}

u8 get_key_long( u8 key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}

int main( void )
{
      TCCR0 = 1<<CS00^1<<CS021;      // divide by 1024
      TIMSK = 1<<TOIE0;        // enable timer interrupt

      LED_PORT = 0xff;
      LED_DDR = 0xFF;

    PORTD=0xff;
      sei();

      for(;;)
    {          // main loop
               // single press

            if( get_key_press( 1<<KEY0 ))
              LED_PORT ^= 1<<LED0;

              // release after short press: task 1
              // long press: task 2

            if( get_key_short( 1<<KEY1 ))
              LED_PORT ^= 1<<LED1;

            if( get_key_long( 1<<KEY1 ))
              LED_PORT ^= 1<<LED2;

              // single press and repeat

            if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))
          {
              u8 i = LED_PORT;

              i = (i & 0x07) | ((i << 1) & 0xF0);
              if( i < 0xF0 )
              i |= 0x08;
              LED_PORT = i;      
            }
      }
}


Autor: *.* (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Na, dann schalte doch mal PD4, 6 und 7 als Eingang!
    PORTD=0xff;
So wird das nix.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da schlägt warscheinlich die Inlining-Falle des GCC zu.

Änder mal:

volatile u8 key_press;                      // key press detect

volatile u8 key_rpt;        // key long press and repeat


Oder besser den Compilerschalter "-fno-inline-small-functions" setzen.
Das dreht dann mächtig den Flash-Verbrauch runter.


Peter

Autor: udo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter!Danke ....!

volatile war der Klemmer.

Habe noch 2 Fragen:

1

Habe deine älteren Tasten-Entprell-Versionen probiert. Die 
funktionierten nicht. Muss ich ältere Programme generell umbauen, die 
vor der aktuellen Compilerversion programmiert wurden, wie in diesem 
Fall?


2

Der Kollege in der Antwort schrieb, dass ich PORTD auf 0x00 (Eingang) 
legen soll. Ich denke mit 0xff aktiviere ich die Pullup-Widerstände.

Also Eingang ist doch:
DDRD=0x00;
PORTD=0xff;

Schmeiße ich jetzt was durcheinander?

Udo

Autor: Sven S. (schwerminator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
udo wrote:
> 2
>
> Der Kollege in der Antwort schrieb, dass ich PORTD auf 0x00 (Eingang)
> legen soll. Ich denke mit 0xff aktiviere ich die Pullup-Widerstände.
>
> Also Eingang ist doch:
>
> DDRD=0x00;
> PORTD=0xff;
> 
>
> Schmeiße ich jetzt was durcheinander?

Nein, du hast recht.

Autor: *.* (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kollege sieht seinen Fehler auch gerade.

Danke für's geraderücken!!

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
udo wrote:
> volatile war der Klemmer.

Jain.
Der eigentliche Klemmer ist die Inlining-Wut des GCC 4.3. und die sollte 
man besser abstellen (siehe oben).

Informiere Dich mal in den Tutorials, was volatile bedeutet.

Warum es nur beim GCC 4.3. zuschlägt, liegt daran, daß das Inlining aus 
einem einmaligen Zugriff einen wiederholten Zugriff macht.


Peter

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.