Forum: Mikrocontroller und Digitale Elektronik Probleme mit Tastenentprellung get_key_short/get_key_long


von Manfred Daret (Gast)


Lesenswert?

Hallo,

ich verwende folgenden Code auf einem Atmega8:
1
// ----------------------------------------------------------------------------
2
// INCLUDES
3
// ----------------------------------------------------------------------------
4
5
#include <stdint.h>      
6
#include <avr/io.h>        
7
#include <avr/interrupt.h>    
8
#include <inttypes.h>      
9
#include <avr/wdt.h>            
10
#include <util/delay.h>      
11
12
13
// ----------------------------------------------------------------------------
14
// DEFINES
15
// ----------------------------------------------------------------------------
16
#ifndef F_CPU
17
#define F_CPU           8000000UL                   // processor clock frequency 8Mhz
18
#endif
19
20
#define REPEAT_START    41                       
21
#define REPEAT_NEXT     83
22
23
#define LED      PD3    
24
25
// Port Defines
26
#define PORTLED    PORTD
27
28
// --------------------------------------------------------------------------------------------
29
// Funktionsdeklarationen
30
// --------------------------------------------------------------------------------------------
31
void init_ports(void);     // Initialisiert die Ports, Enable global Interrupt
32
void init_timer1(void);   // Initialisiert den Timer1 16-bit
33
34
uint8_t get_key_press_PIND_high( uint8_t key_mask );
35
uint8_t get_key_state_PIND_high( uint8_t key_mask );
36
uint8_t get_key_rpt_PIND_high( uint8_t key_mask );
37
uint8_t get_key_short_PIND_high( uint8_t key_mask );
38
uint8_t get_key_long_PIND_high( uint8_t key_mask );
39
40
41
volatile uint8_t key_state_PIND_high;        // debounced and non-inverted key state: bit = 1: key pressed, liest den Zustand eines Ports ein gemäß der Maske
42
volatile uint8_t key_press_PIND_high;        // key press detect active high
43
volatile uint8_t key_rpt_PIND_high;
44
45
46
//Timer1 16-bit Compare Match B Interrupt Service Routine
47
ISR (TIMER1_COMPB_vect){
48
  static uint8_t u8Entprellcounter;
49
  u8Entprellcounter++;              
50
  if (u8Entprellcounter >= 3){            // alle 12ms Entprellfunktionen aufrufen
51
    // Entprellen des PIND active high
52
    static uint8_t ct0_PIND_high, ct1_PIND_high, rpt_PIND_high;
53
      uint8_t i_PIND_high;
54
       
55
      i_PIND_high = key_state_PIND_high ^ PIND;               // key changed?, not inverted!
56
      ct0_PIND_high = ~( ct0_PIND_high & i_PIND_high );              // reset or count ct0
57
      ct1_PIND_high = ct0_PIND_high ^ (ct1_PIND_high & i_PIND_high); // reset or count ct1
58
      i_PIND_high &= ct0_PIND_high & ct1_PIND_high;                    // count until roll over ?
59
      key_state_PIND_high ^= i_PIND_high;                             // then toggle debounced state
60
      key_press_PIND_high |= key_state_PIND_high & i_PIND_high;        // 0->1: key press detect
61
    
62
      if( (key_state_PIND_high & (1<<PD0)) == 0 )            // check repeat function
63
        rpt_PIND_high = REPEAT_START;                          // start delay
64
      if( --rpt_PIND_high == 0 ){
65
        rpt_PIND_high = REPEAT_NEXT;                            // repeat delay
66
        key_rpt_PIND_high |= key_state_PIND_high & (1<<PD0);
67
      }
68
      
69
    u8Entprellcounter = 0;                // Entprellcounter nullen
70
  }
71
  
72
  
73
  OCR1B = OCR1B+((F_CPU/8000000UL)*31999);                // Compare Match Register wieder vorladen, erzeugt ein Compare Match jede 4ms
74
  
75
}
76
77
78
/*Hauptprogramm Aufgaben:
79
80
Das Hauptprogramm
81
*/
82
int main(void)
83
{
84
  init_ports();                                  
85
  init_timer1();  
86
87
  // LED einschalten, dass man sieht, dass Spannung anliegt und µC läuft
88
  PORTLED &= ~( 1<<LED );  
89
  _delay_ms(300);
90
  PORTLED |= ( 1<<LED );
91
92
  while (1){
93
  
94
    if( get_key_short_PIND_high( 1<<PD0 )){
95
        PORTLED &= ~( 1<<LED );  
96
        _delay_ms(300);
97
        PORTLED |= ( 1<<LED );
98
    }
99
    
100
    if( get_key_long_PIND_high( 1<<PD0 )){
101
        PORTLED &= ~( 1<<LED );  
102
        _delay_ms(1000);
103
        PORTLED |= ( 1<<LED );
104
    }
105
  
106
  
107
  } // Ende main loop
108
} // Ende Hauptprogramm
109
110
111
112
// ----------------------------------------------------------------------------
113
// Funktionen
114
// ----------------------------------------------------------------------------
115
116
/*Funktion zum Initialisierein des µC*/
117
void init_ports(void) {
118
119
  // ===========================================================================================
120
  
121
  DDRB = 0x00;  
122
123
  DDRC = 0x00;              // Datenrichtungsregister zunächst alle Eingang
124
  
125
  DDRD = 0x00;              // Datenrichtungsregister zunächst alle Eingang 
126
  DDRD |= (1 << DDD3);    // LED als Ausgang
127
  PORTD |= (1 << PD1) | (1 << PD2) | (1 << PD3) | (1 << PD4);  // Pullups einschalten, LED ausschalten
128
  
129
130
  sei();                  // setzt globales Interrupt enable
131
  
132
  return;
133
}   
134
135
void init_timer1(void) {
136
  
137
  OCR1B = ((F_CPU/8000000UL)*31999);            // Berechnet den Compare Wert für 4ms bei max. 16Mhz Frequenz
138
  TIMSK |= (1 << OCIE1B);  
139
  TCNT1 = 0;                        
140
  TCCR1B |= (1 << CS10);    // kein Prescaler, Timer1 läuft maximal durch
141
  
142
  return;
143
}
144
145
146
// Funktion für Tastendruck-Erkennung Tasten gegen VCC
147
uint8_t get_key_press_PIND_high( uint8_t key_mask ) {
148
  cli();                                          // read and clear atomic !
149
  key_mask &= key_press_PIND_high;                     // read key(s)
150
  key_press_PIND_high ^= key_mask;                     // clear key(s)
151
  sei();
152
  return key_mask;
153
}
154
155
uint8_t get_key_state_PIND_high( uint8_t key_mask ){
156
157
  key_mask &= key_state_PIND_high;
158
  return key_mask;
159
}
160
161
uint8_t get_key_rpt_PIND_high( uint8_t key_mask )
162
{
163
  cli();                                          // read and clear atomic !
164
  key_mask &= key_rpt_PIND_high;                            // read key(s)
165
  key_rpt_PIND_high ^= key_mask;                            // clear key(s)
166
  sei();
167
  return key_mask;
168
}
169
170
uint8_t get_key_short_PIND_high( uint8_t key_mask )
171
{
172
  cli();                                          // read key state and key press atomic !
173
  return get_key_press_PIND_high( ~key_state_PIND_high & key_mask );
174
}
175
176
uint8_t get_key_long_PIND_high( uint8_t key_mask )
177
{
178
  return get_key_press_PIND_high( get_key_rpt_PIND_high( key_mask ));
179
}

Taster hängt an PD0 mit 10k Pulldown gegen GND und 10k in Reihe zum 
Portpin, um den Strom zu begrenzen.
LED hängt an PD3

Der Taster ist also active high, weswegen ich auch diese Zeile die 
Negierung gelöscht habe:
1
i_PIND_high = key_state_PIND_high ^ PIND

Das Problem ist, dass ich den Taster betätige, gedrückt lasse und nach 
kurzer Zeit <1s die LED für 300ms leuchtet, ohne dass ich die Taste 
loslasse.
Die Funktionen sollen doch aber erst beim Loslassen entscheiden, ob eine 
Taste gedrückt ist oder nicht. So kann nämlich nicht zwischen lang und 
kurz unterschieden werden...

Kann jemand helfen?

von Karl H. (kbuchegg)


Lesenswert?

> Der Taster ist also active high
aus welchem Grund?

Wenn dein taster aktiv high ist, dann ist das Einschalten der Pullup 
Widerstände kontraproduktiv. Vor allen Dingen dann, wenn du selber dann 
auch noch externe Pulldown Widerstände angebracht hast. Auf die Art 
kriegst du niemals einen sauberen low Pegel.
Auf einem AVR schliesst man die Taster active low an. Das ist das 
einfachste und macht am wenigsten Probleme. Gewöhn dich daran - das ist 
softwaremässig überhaupt kein Problem, bei den Abfragen dieses active 
low zu berücksichtigen und die Abfrage umzudrehen.

Empfehlung: schmeiss die Pulldown raus und hänge die Taster genau so an, 
wie 50 Millionen andere Programmierer weltweit auch. Und dann nimm den 
PeDa Code so wie er ist.

von Manfred Daret (Gast)


Lesenswert?

Der Pullup an PD0 ist nicht aktiv, d.h. hier ist nur der externe 
Pulldown vorhanden und sollte ein sauberer low-Pegel sein.

Die Taste ist leider extern, d.h. nicht auf meiner Schaltung (zumindest 
in der späteren Anwendung) und schaltet 12V durch. Daher muss ich es so 
tun und auch so simulieren.

von Manfred Daret (Gast)


Lesenswert?

Es scheint doch zu funktionieren, wie ich jetzt nochmal durch ändern der 
delay Zeiten herausgefunden habe.

Es ist aber nicht so, dass get_key_long erst beim Loslassen der Taste 
erkannt wird, sondern sobald die repeat Funktion einen repeat erkannt 
hat, auch wenn man noch auf der Taste steht.

von Peter D. (peda)


Lesenswert?

Manfred Daret schrieb:
> Die Funktionen sollen doch aber erst beim Loslassen entscheiden, ob eine
> Taste gedrückt ist oder nicht.

Nö, sie entscheiden es zum frühest möglichen Zeitpunkt.

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.