Forum: Mikrocontroller und Digitale Elektronik entprellen nach Dannegger


von Malte (Gast)


Lesenswert?

Hallo,
die Entprellung mit
1
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
2
{
3
    if ( ! (*port & (1 << pin)) )
4
    {
5
        /* Pin wurde auf Masse gezogen, 100ms warten   */
6
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz
7
        _delay_ms(50); 
8
        if ( *port & (1 << pin) )
9
        {
10
            /* Anwender Zeit zum Loslassen des Tasters geben */
11
            _delay_ms(50);
12
            _delay_ms(50); 
13
            return 1;
14
        }
15
    }
16
    return 0;
17
}

Funktionierte auf Anhieb.

Nun wollte ich mich an die Entprellung nach Dannegger wagen bekomme sie 
aber nicht zum laufen. Umgebung ist ein STK 500 mit Atmega8, Taster sind 
auf PORTB, LEDs auf PORTC, µC auf 4Mhz.
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
 
12
#include <stdint.h>
13
#include <avr/io.h>
14
#include <avr/interrupt.h>
15
#include <util/delay.h>
16
 
17
#ifndef F_CPU
18
#define F_CPU           4000000                   // processor clock frequency
19
#endif
20
 
21
#define KEY_DDR         DDRB
22
#define KEY_PORT        PORTB
23
#define KEY_PIN         PINB
24
#define KEY0            0
25
#define KEY1            1
26
#define KEY2            2
27
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
28
 
29
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
30
#define REPEAT_START    50                        // after 500ms
31
#define REPEAT_NEXT     20                        // every 200ms
32
 
33
#define LED_DDR         DDRC
34
#define LED_PORT        PORTC
35
#define LED0            0
36
#define LED1            1
37
#define LED2            2
38
 
39
volatile uint8_t key_state;                                // debounced and inverted key state:
40
                                                  // bit = 1: key pressed
41
volatile uint8_t key_press;                                // key press detect
42
 
43
volatile uint8_t key_rpt;                                  // key long press and repeat
44
 
45
 
46
ISR( TIMER0_OVF_vect )                            // every 10ms
47
{
48
  static uint8_t ct0, ct1, rpt;
49
  uint8_t i;
50
 
51
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
52
 
53
  i = key_state ^ ~KEY_PIN;                       // key changed ?
54
  ct0 = ~( ct0 & i );                             // reset or count ct0
55
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
56
  i &= ct0 & ct1;                                 // count until roll over ?
57
  key_state ^= i;                                 // then toggle debounced state
58
  key_press |= key_state & i;                     // 0->1: key press detect
59
 
60
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
61
     rpt = REPEAT_START;                          // start delay
62
  if( --rpt == 0 ){
63
    rpt = REPEAT_NEXT;                            // repeat delay
64
    key_rpt |= key_state & REPEAT_MASK;
65
  }
66
}
67
 
68
///////////////////////////////////////////////////////////////////
69
//
70
// check if a key has been pressed. Each pressed key is reported
71
// only once
72
//
73
uint8_t get_key_press( uint8_t key_mask )
74
{
75
  cli();                                          // read and clear atomic !
76
  key_mask &= key_press;                          // read key(s)
77
  key_press ^= key_mask;                          // clear key(s)
78
  sei();
79
  return key_mask;
80
}
81
 
82
///////////////////////////////////////////////////////////////////
83
//
84
// check if a key has been pressed long enough such that the
85
// key repeat functionality kicks in. After a small setup delay
86
// the key is reported beeing pressed in subsequent calls
87
// to this function. This simulates the user repeatedly
88
// pressing and releasing the key.
89
//
90
uint8_t get_key_rpt( uint8_t key_mask )
91
{
92
  cli();                                          // read and clear atomic !
93
  key_mask &= key_rpt;                            // read key(s)
94
  key_rpt ^= key_mask;                            // clear key(s)
95
  sei();
96
  return key_mask;
97
}
98
 
99
///////////////////////////////////////////////////////////////////
100
//
101
uint8_t get_key_short( uint8_t key_mask )
102
{
103
  cli();                                          // read key state and key press atomic !
104
  return get_key_press( ~key_state & key_mask );
105
}
106
 
107
///////////////////////////////////////////////////////////////////
108
//
109
uint8_t get_key_long( uint8_t key_mask )
110
{
111
  return get_key_press( get_key_rpt( key_mask ));
112
}
113
 
114
115
int main( void )
116
{
117
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
118
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
119
 
120
  TCCR0 = (1<<CS02)|(1<<CS00);       // divide by 1024
121
  TIMSK = 1<<TOIE0;               // enable timer interrupt
122
 
123
  LED_PORT = 0xFF;
124
  LED_DDR =  0xFF;                     // long press: task 2
125
 
126
127
  sei();
128
129
  PORTC &= ~(1<<PC5);                  // LED5 zum Testen einschalten 
130
131
 while(1)
132
    {
133
134
     if( get_key_short( 1<<KEY1 )) PORTC &= ~(1<<PC1); //mit entprellen 
135
136
    
137
138
     if ( !(PINB & (1<<PINB4)) ) PORTC &= ~(1<<PC4); // Ohne entprellen LED4 ein 
139
    
140
    }
141
           
142
      
143
}

Die Test LEDs 5 und 4 gehen jeweils an nur die LED 1 mit dem entprellen 
nicht, ich sehe das Problem irgend wie nicht :(.

Danke für die Tips

lg Malte

von Malte (Gast)


Lesenswert?

hat sich erledigt, die hardware war es ;)

von Malte (Gast)


Lesenswert?

eine Sache ist da noch, wie kann ich die routine auslagern so das ich 
sie in einem extra debounce.c File habe ?

wenn ich eine debounce.h erzeuge
1
#ifndef F_CPU
2
#define F_CPU           4000000                   // processor clock frequency
3
#warning kein F_CPU definiert
4
#endif
5
 
6
#define KEY_DDR         DDRB
7
#define KEY_PORT        PORTB
8
#define KEY_PIN         PINB
9
#define KEY0            0
10
#define KEY1            1
11
#define KEY2            2
12
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
13
 
14
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
15
#define REPEAT_START    50                        // after 500ms
16
#define REPEAT_NEXT     20                        // every 200ms
17
 
18
#define LED_DDR         DDRC
19
#define LED_PORT        PORTC
20
#define LED0            0
21
#define LED1            1
22
#define LED2            2

meine debounce.c
1
#include "debounce.h"
2
#include <stdint.h>
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
 
6
7
 
8
volatile uint8_t key_state;                                // debounced and inverted key state:
9
                                                  // bit = 1: key pressed
10
volatile uint8_t key_press;                                // key press detect
11
 
12
volatile uint8_t key_rpt;                                  // key long press and repeat
13
 
14
15
 
16
ISR( TIMER0_OVF_vect )                            // every 10ms
17
{
18
  static uint8_t ct0, ct1, rpt;
19
  uint8_t i;
20
 
21
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
22
 
23
  i = key_state ^ ~KEY_PIN;                       // key changed ?
24
  ct0 = ~( ct0 & i );                             // reset or count ct0
25
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
26
  i &= ct0 & ct1;                                 // count until roll over ?
27
  key_state ^= i;                                 // then toggle debounced state
28
  key_press |= key_state & i;                     // 0->1: key press detect
29
 
30
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
31
     rpt = REPEAT_START;                          // start delay
32
  if( --rpt == 0 ){
33
    rpt = REPEAT_NEXT;                            // repeat delay
34
    key_rpt |= key_state & REPEAT_MASK;
35
  }
36
}
37
 
38
///////////////////////////////////////////////////////////////////
39
//
40
// check if a key has been pressed. Each pressed key is reported
41
// only once
42
//
43
uint8_t get_key_press( uint8_t key_mask )
44
{
45
  cli();                                          // read and clear atomic !
46
  key_mask &= key_press;                          // read key(s)
47
  key_press ^= key_mask;                          // clear key(s)
48
  sei();
49
  return key_mask;
50
}
51
 
52
///////////////////////////////////////////////////////////////////
53
//
54
// check if a key has been pressed long enough such that the
55
// key repeat functionality kicks in. After a small setup delay
56
// the key is reported beeing pressed in subsequent calls
57
// to this function. This simulates the user repeatedly
58
// pressing and releasing the key.
59
//
60
uint8_t get_key_rpt( uint8_t key_mask )
61
{
62
  cli();                                          // read and clear atomic !
63
  key_mask &= key_rpt;                            // read key(s)
64
  key_rpt ^= key_mask;                            // clear key(s)
65
  sei();
66
  return key_mask;
67
}
68
 
69
///////////////////////////////////////////////////////////////////
70
//
71
uint8_t get_key_short( uint8_t key_mask )
72
{
73
  cli();                                          // read key state and key press atomic !
74
  return get_key_press( ~key_state & key_mask );
75
}
76
 
77
///////////////////////////////////////////////////////////////////
78
//
79
uint8_t get_key_long( uint8_t key_mask )
80
{
81
  return get_key_press( get_key_rpt( key_mask ));
82
}

und main.c
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
#include "debounce.h"
6
7
8
9
int main( void )
10
{
11
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
12
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
13
 
14
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
15
  TIMSK = 1<<TOIE0;        // enable timer interrupt
16
 
17
  LED_PORT = 0xFF;
18
  LED_DDR = 0xFF;                     
19
 
20
  sei();
21
 
22
  while(1){
23
    if( get_key_short( 1<<KEY1 ))
24
      LED_PORT ^= 1<<LED1;
25
 
26
    if( get_key_long( 1<<KEY1 ))
27
      LED_PORT ^= 1<<LED2;
28
 
29
                                                  // single press and repeat
30
 
31
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
32
      uint8_t i = LED_PORT;
33
 
34
      i = (i & 0x07) | ((i << 1) & 0xF0);
35
      if( i < 0xF0 )
36
        i |= 0x08;
37
      LED_PORT = i;      
38
    }
39
  }
40
}

Funktioniert es nicht mehr... vielleicht übersehe ich da auch wieder was 
:)

von Alex (Gast)


Lesenswert?

Hi!

Probier mal #include "debounce.c"

von Heiko (Gast)


Lesenswert?

Moin,

woher soll der Compiler, wenn er die main.c kompiliert, denn wissen, 
dass es eine Funktion get_key_short gibt?

Das kann man ihm auf zwei verschiedene Weisen bekannt machen:

1) Wie Alex vorschlägt, die .c includen. Das würde ich 
Holzhammer-Methode nennen :)
2) Wie allgemein üblich: Funktionsprototypen in die .h schreiben. Das 
sieht so aus:
1
uint8_t get_key_rpt( uint8_t key_mask );

Damit ist klar getrennt: In der .h steht alles, was man nach außen hin 
wissen muss (wie man eine Funktion aufruft, sínnvollerweise auch, was 
sie tut (Dokumentation) und ggf. einige defines zur Anpassung...) In der 
.c steht nur noch "interner" Code, den du auch beliebig ersetzen 
könntest, solange er noch das gleiche tut. Wie er genau aussieht, 
interessiert deine main ja nicht, sondern nur, was er tut.

Wichtig: Im zweiten Fall muss man noch irgendwie dafür sorgen, dass die 
.c trotzdem kompiliert und dazugelinkt wird. Zum Beispiel, indem man im 
Beispielmakefile zu SRC noch debounce.c hinzufügt.

MfG, Heiko

von Malte (Gast)


Lesenswert?

1
unsigned char get_key_press( unsigned char key_mask );
2
unsigned char get_key_rpt( unsigned char key_mask );
3
unsigned char get_key_short( unsigned char key_mask );
4
unsigned char get_key_long( unsigned char key_mask );

das habe ich noch in die header datei eingefügt und zack es ging, so 
langsam blicke ich es glaube ich...

von Bernd O. (bitshifter)


Lesenswert?

Alex schrieb:
> Hi!
>
> Probier mal #include "debounce.c"
Das ist ganz sicher der falsche Weg.

Ziel der Trennung von Code-Teilen ist es, separate Objektfiles zu 
erzeugen, die der Linker dann verbindet.
Die Header-Datei soll das enthalten, was der Verwender des Objectfiles 
benötigt - aber eben nur das. Sämtliche Implementierungsdetails sollten 
ausschliesslich in der jeweiligen C-Datei oder einem separaten Header, 
der vom Verwender nicht eingebunden wird gekapselt sein.

So ist es möglich, Module unabhängig voneinander zu ändern und - viel 
wichtiger - man erhält ein sauber getrenntes System, bei dem nicht 
kreuz- und quer über viele Dateien hinweg Variablen geschrieben werden.

@Malte:
Die Initialisierung der Tasten solltest Du auch noch in eine Funktion 
kapseln. Es ist einfach besser gekapselt, wenn sich debounce.* komplett 
um die verwendeten Pins kümmert:

void debounce_init(void)
{
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
}


Gruß,
Bernd

von Malte (Gast)


Lesenswert?

Hallo,
ich bin gerade bei meinem ersten größeren Projekt und beschäftige mich 
eigentlich nur damit Struktur rein zu bekommen. Es geht um eine einfache 
Temperatur Steuerung mit Display, nichts wildes aber wenn man es sauber 
aufbauen will muss man sich ganz schön in die Grundlagen fuchsen.

Da kommt gerade einiges zusammen, mit entprellen, LCD Display, 
Festkommaarithmetik, ADC, PWM, aufs EEPROM schreiben... ich versuche das 
jetzt gut auf zu teilen das alles übersichtlich bleibt...

deinen tipp habe ich schon beherzigt ;), danke :D

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.