Forum: Mikrocontroller und Digitale Elektronik tastenentprellen von Peter


von udo (Gast)


Lesenswert?

Hallo Experten,

helft mir doch bitte mal.

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

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

der entsprechende Funktionsaufruf?


Hier das Programm von Peter.
1
/************************************************************************/
2
/*                                                                      */
3
/*    Debouncing 8 Keys        */
4
/*        Sampling 4 Times        */
5
/*    With Repeat Function        */
6
/*                                                                      */
7
/*     Author: Peter Dannegger                                 */
8
/*          danni@specs.de                                  */
9
/*                                                                      */
10
/************************************************************************/
11
// Target: ATMega48
12
13
#include <io.h>
14
#include <interrupt.h>
15
16
typedef unsigned char  u8;
17
typedef signed short  s16;
18
19
#define  XTAL    8000000L        // 8MHz
20
21
#define KEY_PIN    PINB
22
#define KEY0    0
23
#define KEY1    1
24
#define KEY2    2
25
#define  KEY3    3
26
27
#define LED_DDR    DDRC
28
#define LED_PORT  PORTC
29
#define LED0    0
30
#define LED1    1
31
#define LED2    2
32
33
#define REPEAT_MASK  (1<<KEY3^1<<KEY2^1<<KEY1^1<<KEY0)
34
#define REPEAT_START  10          // after 100ms
35
#define REPEAT_NEXT  20          // every 200ms
36
37
u8 key_state;                // debounced and inverted key state:
38
                    // bit = 1: key pressed
39
u8 key_press;                // key press detect
40
41
u8 key_rpt;                // key long press and repeat
42
43
44
ISR( TIMER0_COMPA_vect )       // every 10ms
45
{
46
  static u8 ct0, ct1, rpt;
47
  u8 i;
48
49
  i = key_state ^ ~KEY_PIN;        // key changed ?
50
  ct0 = ~( ct0 & i );            // reset or count ct0
51
  ct1 = ct0 ^ (ct1 & i);        // reset or count ct1
52
  i &= ct0 & ct1;            // count until roll over ?
53
  key_state ^= i;            // then toggle debounced state
54
  key_press |= key_state & i;      // 0->1: key press detect
55
56
  if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
57
     rpt = REPEAT_START;        // start delay
58
  if( --rpt == 0 ){
59
    rpt = REPEAT_NEXT;          // repeat delay
60
    key_rpt |= key_state & REPEAT_MASK;
61
   }
62
}
63
64
65
66
u8 get_key_press( u8 key_mask )
67
{
68
  cli();                  // read and clear atomic !
69
  key_mask &= key_press;                            // read key(s)
70
  key_press ^= key_mask;                            // clear key(s)
71
  sei();
72
  return key_mask;
73
}
74
75
u8 get_key_rpt( u8 key_mask )
76
{
77
  cli();                  // read and clear atomic !
78
  key_mask &= key_rpt;                              // read key(s)
79
  key_rpt ^= key_mask;                              // clear key(s)
80
  sei();
81
  return key_mask;
82
}
83
84
u8 get_key_short( u8 key_mask )
85
{
86
  cli();                // read key state and key press atomic !
87
  return get_key_press( ~key_state & key_mask );
88
}
89
90
u8 get_key_long( u8 key_mask )
91
{
92
  return get_key_press( get_key_rpt( key_mask ));
93
}
94
95
int main( void )
96
{
97
  TCCR0A = 1<<WGM01;              // Mode 2: CTC
98
  TCCR0B = 1<<CS02^1<<CS00;            // divide by 1024
99
  OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);    // preload for 10ms
100
  TIMSK0 = 1<<OCIE0A;              // enable timer interrupt
101
102
  LED_PORT = 0xFF;
103
  LED_DDR = 0xFF;
104
  sei();
105
106
  for(;;){                  // main loop
107
108
    switch( get_key_long( 1<<KEY1^1<<KEY0 )){
109
110
      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;
111
112
      case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;
113
114
      case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
115
    }
116
117
    switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){
118
119
      case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;
120
121
      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;
122
123
      case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
124
    }
125
  }
126
}

von *.* (Gast)


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.
1
    switch( get_key_long( 1<<KEY1^1<<KEY0 )){
2
3
      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;
4
5
      case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;
6
7
      case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
8
    }
9
10
    switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){
11
12
      case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;
13
14
      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;
15
16
      case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
17
    }

Speziell für die Funktion get_key_press() sähe das in etwa so aus:
1
    switch( get_key_press( 1<<KEY3^1<<KEY0 )){
2
3
      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;
4
5
      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;
6
7
      case 1<<KEY3^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
8
    }

Da mußt Du nix in der ISR rumfummeln.

von udo (Gast)


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
1
SIGNAL (SIG_OVERFLOW0)
durch
1
ISR(TIMER0_OVF_vect)

und
1
#define KEY0    4    // TASTER AN PD4? Bin mir nicht sicher,das ist bestimmt falsch oder?
2
#define KEY1    6
3
#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:
1
/************************************************************************/
2
/*                      Debouncing 8 Keys        */
3
/*      Sampling 4 Times        */
4
/*      With Repeat Function        */
5
/*                                                                      */
6
/*              Author: Peter Dannegger                                 */
7
/************************************************************************/
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
12
typedef unsigned char  u8;
13
typedef signed short  s16;
14
15
#define  XTAL    7372800    
16
17
#define KEY_PIN    PIND
18
#define KEY0    4
19
#define KEY1    6
20
#define KEY2    7
21
22
#define LED_DDR    DDRA
23
#define LED_PORT  PORTA
24
#define LED0    4
25
#define LED1    6
26
#define LED2    7
27
28
#define REPEAT_MASK  (1<<KEY1^1<<KEY2)      // repeat: key1, key2
29
#define REPEAT_START  50              // after 500ms
30
#define REPEAT_NEXT  20                // every 200ms
31
32
u8 key_state;                      // debounced and inverted key state:
33
                              // bit = 1: key pressed
34
u8 key_press;                      // key press detect
35
36
u8 key_rpt;        // key long press and repeat
37
38
ISR(TIMER0_OVF_vect) 
39
{
40
  //{
41
    static u8 ct0, ct1, rpt;
42
    u8 i;
43
44
    TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);  // preload for 10ms
45
46
    i = key_state ^ ~KEY_PIN;    // key changed ?
47
    ct0 = ~( ct0 & i );      // reset or count ct0
48
    ct1 = ct0 ^ (ct1 & i);    // reset or count ct1
49
    i &= ct0 & ct1;      // count until roll over ?
50
    key_state ^= i;      // then toggle debounced state
51
    key_press |= key_state & i;    // 0->1: key press detect
52
53
    if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
54
       rpt = REPEAT_START;    // start delay
55
    if( --rpt == 0 )
56
    {
57
      rpt = REPEAT_NEXT;      // repeat delay
58
      key_rpt |= key_state & REPEAT_MASK;
59
      }
60
}
61
62
63
u8 get_key_press( u8 key_mask )
64
{
65
  cli();          // read and clear atomic !
66
  key_mask &= key_press;                        // read key(s)
67
  key_press ^= key_mask;                        // clear key(s)
68
  sei();
69
  return key_mask;
70
}
71
72
u8 get_key_rpt( u8 key_mask )
73
{
74
  cli();          // read and clear atomic !
75
  key_mask &= key_rpt;                          // read key(s)
76
  key_rpt ^= key_mask;                          // clear key(s)
77
  sei();
78
  return key_mask;
79
}
80
81
u8 get_key_short( u8 key_mask )
82
{
83
  cli();      // read key state and key press atomic !
84
  return get_key_press( ~key_state & key_mask );
85
}
86
87
u8 get_key_long( u8 key_mask )
88
{
89
  return get_key_press( get_key_rpt( key_mask ));
90
}
91
92
int main( void )
93
{
94
      TCCR0 = 1<<CS00^1<<CS021;      // divide by 1024
95
      TIMSK = 1<<TOIE0;        // enable timer interrupt
96
97
      LED_PORT = 0xff;
98
      LED_DDR = 0xFF;
99
100
    PORTD=0xff;
101
      sei();
102
103
      for(;;)
104
    {          // main loop
105
               // single press
106
107
            if( get_key_press( 1<<KEY0 ))
108
              LED_PORT ^= 1<<LED0;
109
110
              // release after short press: task 1
111
              // long press: task 2
112
113
            if( get_key_short( 1<<KEY1 ))
114
              LED_PORT ^= 1<<LED1;
115
116
            if( get_key_long( 1<<KEY1 ))
117
              LED_PORT ^= 1<<LED2;
118
119
              // single press and repeat
120
121
            if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))
122
          {
123
              u8 i = LED_PORT;
124
125
              i = (i & 0x07) | ((i << 1) & 0xF0);
126
              if( i < 0xF0 )
127
              i |= 0x08;
128
              LED_PORT = i;      
129
            }
130
      }
131
}

von *.* (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

Da schlägt warscheinlich die Inlining-Falle des GCC zu.

Änder mal:
1
volatile u8 key_press;                      // key press detect
2
3
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

von udo (Gast)


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:
1
DDRD=0x00;
2
PORTD=0xff;

Schmeiße ich jetzt was durcheinander?

Udo

von Sven S. (schwerminator)


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:
>
1
> DDRD=0x00;
2
> PORTD=0xff;
3
>
>
> Schmeiße ich jetzt was durcheinander?

Nein, du hast recht.

von *.* (Gast)


Lesenswert?

Kollege sieht seinen Fehler auch gerade.

Danke für's geraderücken!!

von Peter D. (peda)


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

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.