Forum: Mikrocontroller und Digitale Elektronik Gelöst: Komfortroutine C - Taster entprellen


von Stefan S. (sschultewolter)


Lesenswert?

Hallo,

ich habe das Problem, dass sich der Status über die Leds nicht ändert. 
Sobald das Programm startet, werden die Ausgänge der Leds HIGH gesetzt 
und danach bleiben diese unverändert. Sie reagieren nicht auf die 
Tastendrücke.

Die Taster sind als Low-Active angeschlossen
1
                            T
2
                ___        ---
3
 VCC ----------|___|--o----o o---------- GND
4
                10k   |
5
                      '----------- INPUT MCU
6
7
                ___         LED
8
 OUTPUT MCU ---|___|-------->|- -------- GND
9
                680
10
(created by AACircuit v1.28.6 beta 04/19/05 www.tech-chat.de)
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
#define F_CPU 16000000UL
13
14
#include <stdint.h>
15
#include <avr/io.h>
16
#include <avr/interrupt.h>
17
18
#define KEY_DDR         DDRB
19
#define KEY_PORT        PORTB
20
#define KEY_PIN         PINB
21
#define KEY0            0    // Arduino Pro Mini Pin 8 | PB0
22
#define KEY1            1    // Arduino Pro Mini Pin 9 | PB1
23
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1)
24
25
#define REPEAT_MASK     (1<<KEY1) 
26
#define REPEAT_START    50
27
#define REPEAT_NEXT     20
28
29
#define LED_DDR         DDRD
30
#define LED_PORT        PORTD
31
#define LED0            2    // Arduino Pro Mini Pin 2 | PD2
32
#define LED1            3    // Arduino Pro Mini Pin 3 | PD3
33
34
volatile uint8_t key_state;
35
volatile uint8_t key_press;
36
volatile uint8_t key_rpt;
37
38
39
ISR( TIMER0_OVF_vect )
40
{
41
  static uint8_t ct0, ct1, rpt;
42
  uint8_t i;
43
  
44
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
45
  
46
  i = key_state ^ ~KEY_PIN;    // LOW ACTIVE
47
  //i = key_state ^ KEY_PIN;    // HIGH ACTIVE    
48
  ct0 = ~( ct0 & i );
49
  ct1 = ct0 ^ (ct1 & i);
50
  i &= ct0 & ct1;
51
  key_state ^= i;
52
  key_press |= key_state & i;
53
  
54
  if( (key_state & REPEAT_MASK) == 0 )
55
  rpt = REPEAT_START;
56
  if( --rpt == 0 )
57
  {
58
    rpt = REPEAT_NEXT;
59
    key_rpt |= key_state & REPEAT_MASK;
60
  }
61
}
62
63
uint8_t get_key_press( uint8_t key_mask )
64
{
65
  cli();
66
  key_mask &= key_press;
67
  key_press ^= key_mask;
68
  sei();
69
  return key_mask;
70
}
71
72
uint8_t get_key_rpt( uint8_t key_mask )
73
{
74
  cli();
75
  key_mask &= key_rpt; 
76
  key_rpt ^= key_mask;
77
  sei();
78
  return key_mask;
79
}
80
81
uint8_t get_key_state( uint8_t key_mask )
82
{
83
  key_mask &= key_state;
84
  return key_mask;
85
}
86
87
uint8_t get_key_short( uint8_t key_mask )
88
{
89
  cli();
90
  return get_key_press( ~key_state & key_mask );
91
}
92
93
uint8_t get_key_long( uint8_t key_mask )
94
{
95
  return get_key_press( get_key_rpt( key_mask ));
96
}
97
98
int main( void )
99
{
100
  LED_PORT = 0xFF;
101
  LED_DDR = 0xFF;
102
  
103
  KEY_DDR &= ~ALL_KEYS;
104
  KEY_PORT |= ALL_KEYS; 
105
  
106
  TCCR0A = (1<<CS02)|(1<<CS00);
107
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
108
  TIMSK0 |= 1<<TOIE0;
109
  
110
  sei();
111
  
112
  while(1)
113
  {
114
    if( get_key_short( 1<<KEY0 ))
115
    LED_PORT ^= 1<<LED0;
116
    
117
    if( get_key_long( 1<<KEY1 ))
118
    LED_PORT ^= 1<<LED1;
119
  }
120
}

: Bearbeitet durch User
von Stefan S. (sschultewolter)


Lesenswert?

Also an den Leds liegt es nicht, habe die nun anders getestet. Die 
Taster machen jedoch immer noch nicht, was ich von Ihnen möchte.

Habe diese ohne Entprellen getestet und es ging.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Ich persönlich hab noch nie ganz verstanden warum so oft so großer 
proceduraler Aufwand betrieben wird um softwaremässig zu entprellen. Ein 
Kondensator und ein Wiederstand mehr reichen und man muss nichts in der 
SW entprellen. Eine ISR dafür zu nehmen ist eh übertrieben und schade um 
den INtrrupt.

von michi (Gast)


Lesenswert?

Warum probierst du nicht einfach _delay_ms(50);

if(taste high)
{_delay_ms(x);
led ein;
}

X durch probiern auswaehlen..

Mach ich bei Matrixtastatur etc. immer so und hab noch keinen Nachteil 
festgestellt.

Undja das ist nicht ideal aber fuer ne led find ich eine ISR 
uebertrieben ;)

von Stefan S. (sschultewolter)


Lesenswert?

@Thomas Holmes:
Sicherlich ist es meist auch ausreichend mit einem Kondensator sowie 
Widerstand an dem Taster. Habe das auch bereits ein paar mal zu 
testzwecken gemacht. Geht an sich auch immer ganz gut. Es sind aber 
somit auch 2 Bauteile mehr. Im Tutorial wird darauf hingewiesen, dass es 
kaum noch einen Sinn macht, da die Software das auch auswerten kann. Das 
soll keine Diskussion über den Sinn nun geben ;)

@michi:
Solche Sachen mit den delays versuche ich wenn möglich immer zu 
vermeiden, auch wenn das hier natürlich kaum ins Gewicht fällt.

Ich habe einen ähnlichen Code vor ein paar Wochen geschrieben. (Der 
Atmega328P 16MHz diente lediglich zum kurzen Testen). Das ganze wurde 
mit einem Attiny85 betrieben. Der Code gibt nicht alles wieder. Die 
ganzen anderen Bibiotheken werden über die main.h einbezogen.

Ich habe vom Prellen nichts mitbekommen bei dieser Methode. Ich habe 
hier bereits einen Timer im Einsatz für die Zeit-funktion. In der habe 
ich ich Millisekunden, Sekunden, Minuten und Stunden, die über einem 
Interupt-Overflow gezählt werden. Basierend auf dem millis Zähler werden 
auch diverse Frequenzen zur Verfügung gestellt. U.a. 50Hz. Ich habe hier 
keinerlei Probleme mit dem Entprellen feststellen können. Nichts wurde 
doppelt gezählt.

Gäbe es ansonsten Anregungen, wie ich ggf. falsche Rückgabewerte 
vermeiden kann in diesem Beispiel? Für die fertige Version plane ich mit
http://www.mikrocontroller.net/wikifiles/f/f8/RC_debouncer.png Die 
Schmitt-Trigger ICs stehen jedoch derzeit erst einmal nur auf der 
Möchte-Haben-Liste und sind noch nicht bestellt.
1
/*
2
ATtiny85_Equinox.c
3
Created: 31.07.2014 16:16:07
4
Author: sschultewolter
5
6
(PCINT5/!RESET/ADC0/dW)      PB5  1  | AT |  8  VCC
7
(PCINT3/XTAL1/CLKI/!OC1B/ADC3)  PB3  2  | TI |  7  PB2  (SCK/USCK/SCL/ADC/T0/INT0/PCINT2)
8
(PCINT4/XTAL2/CLKO/OC1B/ADC2)  PB4  3  | NY |  6  PB1  (MISO/DO/AIN1/OC0B/OC1A/PCINT1)
9
________________________________GND  4  | 85 |  5  PBO (MOSI/DI/SDA/AIN0/OC0A/!OC1A/AREF/PCINT0)
10
11
PB1 - WS2812B
12
PB3 - Button1
13
PB4 - Button2
14
*/
15
16
#include "main.h"
17
18
int main(void)
19
{
20
  time_init();
21
  //time_set(0, 15, 30);
22
  
23
  DDRB &= ~((1 << DDB3) | (1 << DDB4));  // PB3 + PB4 als Eingang
24
  PORTB |= ((1 << PB3) | (1 << PB4));    // PB3 + PB4 PullUp aktivieren
25
  
26
  while(1)
27
  {
28
    //>-- Zu Testzwecken mit != 0 initialisiert
29
    static int16_t mode1=10;
30
    static int16_t mode2=5;
31
    //<--
32
    
33
    {
34
      static int8_t ct_button1;
35
      static int8_t ct_button2;
36
      
37
      int8_t state_button1;
38
      static int8_t lastState_button1;
39
      
40
      int8_t state_button2;
41
      static int8_t lastState_button2;
42
      
43
      static int16_t lastChange;
44
      if(frequency_50 != lastChange)    // alle 20ms
45
      {
46
        lastChange = frequency_50;
47
        
48
        state_button1 = !(PINB & (1 << PINB3));
49
        state_button2 = !(PINB & (1 << PINB4));
50
        
51
        if(state_button1) ct_button1++;
52
        else if(!state_button1 && lastState_button1)
53
        {
54
          if(ct_button1 > 50) mode1++;
55
          else mode2++;
56
          ct_button1 = 0;
57
        }
58
        lastState_button1 = state_button1;
59
        
60
        if(state_button2) ct_button2++;
61
        else if(!state_button2 && lastState_button2)
62
        {
63
          if(ct_button2 > 50) mode1--;
64
          else mode2--;
65
          ct_button2 = 0;
66
        }
67
        lastState_button2 = state_button2;
68
        
69
        //--> Visuelles Debugging
70
        leds_clear();
71
        leds_rgb(leds_index(mode1), 255,0,0);
72
        leds_rgb(leds_index(mode2), 0,0,255);
73
        leds_on();
74
        //<--
75
      }
76
    }
77
  }
78
  
79
  return 0;
80
}

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Stefan S. schrieb:
> icherlich ist es meist auch ausreichend mit einem Kondensator sowie
> Widerstand an dem Taster. Habe das auch bereits ein paar mal zu
> testzwecken gemacht. Geht an sich auch immer ganz gut. Es sind aber
> somit auch 2 Bauteile mehr. Im Tutorial wird darauf hingewiesen, dass es
> kaum noch einen Sinn macht, da die Software das auch auswerten kann. Das
> soll keine Diskussion über den Sinn nun geben ;)

Hallo, nein diskutieren will ich auch nicht. Schließlich ist weder das 
was im Tutorial steht, noch meine persönliche Meinung ein Gesetz. Jeder 
hat so seine Strategie. Meine ist es immer den geringsten 
dimensionierten Controller einzusetzen (größte Kosten) und der 
Performance des Codes die höchste Priorität einzuräumen. Timer und ISR's 
halte ich für Aufgaben frei die anders nicht zu lösen sind. Auch wenn 
der Algorithmus  von Herrn Dannegger programmiertechnisch exzellent ist, 
so geht  SW Entprellen,  auch wesentlich weniger codeintensiv und auch 
ohne Timer.

Was aber gar nicht in meinen Kopf hinein will, ist dass Du auf der einen 
Seite ein Kondensator und einen Wiederstand als zuviel zusätzliche 
Hardware ansiehst aber dann planst einen völlig Überflüssigen 
Schmitt-Trigger (AVR µC haben einen Schmitt-Trigger intern an den 
Eingängen)  nebst drei weiteren passiven Bauelementen zu Planen.

Du führst ja deine eigenen Argumente ad absurdum.

von Peter D. (peda)


Lesenswert?

Stefan S. schrieb:
> if( get_key_short( 1<<KEY0 ))
>     LED_PORT ^= 1<<LED0;
>
>     if( get_key_long( 1<<KEY1 ))

get_key_short() und get_key_long() müssen immer je Taste aufgerufen 
werden.
Wenn man die Drückdauer unterscheidet, macht man das ja, um auch beide 
Ereignisse zu benutzen.
Die HW-Entprell-Päbste können das natürlich nicht erkennen und müssen 
ihren falschen Senf dazugeben.

von Bastler (Gast)


Lesenswert?

@michi: wo wird da entprellt?

> Warum probierst du nicht einfach _delay_ms(50);
>
> if(taste high)
>{_delay_ms(x);
>  led_ein;
>}

von Stefan S. (sschultewolter)


Lesenswert?

Bastler schrieb:
> @michi: wo wird da entprellt?
>
>> Warum probierst du nicht einfach _delay_ms(50);
>>
>> if(taste high)
>>{_delay_ms(x);
>>  led_ein;
>>}
Es funktioniert, weil das Prellen innerhalb von 50ms meistens 
abgeklungen ist. Das Programm macht in dem Zeitraum nichts und bekommt 
das Prellen nicht mit.

Peter Dannegger schrieb:
> Stefan S. schrieb:
>> if( get_key_short( 1<<KEY0 ))
>>     LED_PORT ^= 1<<LED0;
>>
>>     if( get_key_long( 1<<KEY1 ))
>
> get_key_short() und get_key_long() müssen immer je Taste aufgerufen
> werden.
> Wenn man die Drückdauer unterscheidet, macht man das ja, um auch beide
> Ereignisse zu benutzen.
> Die HW-Entprell-Päbste können das natürlich nicht erkennen und müssen
> ihren falschen Senf dazugeben.
Richtig, aber dann müsste auch folgendes funktionieren
1
  while(1)
2
  {
3
    if( get_key_short( 1<<KEY0 ))
4
    LED_PORT ^= 1<<LED0;
5
  }

Ich werde aber gleich mal versuchen es mit UART zu debuggen. Ob es 
verschwendung ist, einen Timer zu nehmen, oder nicht, hängt immer von 
der verwendeten Hardware ab. Ich brauche für das Projekt eine gewissen 
Menge an Flash des Attinys. 4KB wird hier für den Flash sehr sehr knapp. 
Daraufhin habe ich mich für einen Attiny85 entschieden (eigentlich 
sollte es ein AT841 werden, jedoch macht mir Farnell einen Strich durch 
die Rechnung).
Ich brauche keine große Anzahl an Timern. Lediglich diesen zum 
Entprellen, sowie den für die Zeit selber. Diesen könnte man sogar 
notfalls kombinieren da die doch ähnlich aufgebaut sind.

von Bastler (Gast)


Lesenswert?

>@michi: wo wird da entprellt?

>> Warum probierst du nicht einfach _delay_ms(50);
>>
>> if(taste high)
>>{_delay_ms(x);
>>  led_ein;
>>}
> Es funktioniert ....

Entprellen bedeutet aber nicht beim ersten Muckser am Pin 50ms zu 
warten, sondern sicherzustellen, daß dies der Beginn eines "Gedrückt" 
war. Es fehlt da die erneute/wiederholt Abfrage des Eingangs. Einfach 
mal Dr. Google nach "Prellen Schalter" fragen, um zu erfahren was das 
identisch ist oder einfach hier den passenden Grundlagenartikel lesen.

von Teo D. (teoderix)


Lesenswert?

Bastler schrieb:
> Entprellen bedeutet aber nicht beim ersten Muckser am Pin 50ms zu
> warten, sondern sicherzustellen, daß dies der Beginn eines "Gedrückt"
> war.

Kann man machen, muss man aber nicht.
Wenn man's nicht braucht und keine größeren Störungen hat o. befürchten 
muss, kann man das getrost auch weglassen.

von Bastler (Gast)


Lesenswert?

Dann braucht man auch nicht warten, oder??

von Stefan S. (sschultewolter)


Lesenswert?

Teo Derix schrieb:
> Bastler schrieb:
>> Entprellen bedeutet aber nicht beim ersten Muckser am Pin 50ms zu
>> warten, sondern sicherzustellen, daß dies der Beginn eines "Gedrückt"
>> war.
>
> Kann man machen, muss man aber nicht.
> Wenn man's nicht braucht und keine größeren Störungen hat o. befürchten
> muss, kann man das getrost auch weglassen.

Dann wurde aber im restlichen Code meist schon unnötig viele delays 
genutzt.

Nicht prellende Taster im Low-Budget Bereich sind mir nicht bekannt.

von Teo D. (teoderix)


Lesenswert?

Bastler schrieb:
> Dann braucht man auch nicht warten, oder??

Hmm... Das war eindeutig tu dünn!

Etwas mehr angaschment bitte.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Peter Dannegger schrieb:
> Die HW-Entprell-Päbste können das natürlich nicht erkennen und müssen
> ihren falschen Senf dazugeben.

Selbstverständlich kann man auch noch von HW entprellten 
Eingangssignalen die Signalzeit bestimmen...Es gibt keinen falschen 
Senf, nur anderen...

von Stefan S. (sschultewolter)


Lesenswert?

Wäre schön, wenn wir zum eigentlichen Thema noch einmal zurückkommen 
könnten.

Habe mir in der Zwischenzeit den Schaltung auf eine kleine 
Streifenrasterplatine aufgebaut, da die Taster nicht immer optimal im 
Steckbrett halten.
1
                                            5V
2
           .-----------------------o---------o
3
           |                       |
4
          .-.                     .-.
5
       4k7| |   PINB0    PINB1    | |4k7
6
          | |      o      o       | |
7
          '-'      |      |       '-'
8
  PIND2    |       |      |        |     PIND3
9
  o--------o       |      |        o---------o
10
           |       |      |        |
11
           |       |      |        |
12
        T  |       |      |        |  T
13
       --- |       |      |        | ---
14
     .-o o-'       |      |        '-o o--.
15
     |             |      |               |
16
     |  ___     LED|      | LED     ___   |
17
     o-|___|----|<-'      '->|-----|___|--o
18
     |  680                         680   |
19
     |                                    |GND
20
     '------------------------------------o--o
21
(created by AACircuit v1.28.6 beta 04/19/05 www.tech-chat.de)

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
#define F_CPU 16000000UL
13
14
#include <stdint.h>
15
#include <avr/io.h>
16
#include <avr/interrupt.h>
17
#include <util/delay.h>
18
19
#define KEY_DDR         DDRB
20
#define KEY_PORT        PORTB
21
#define KEY_PIN         PINB
22
#define KEY0            0
23
#define KEY1            1
24
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1)
25
26
#define REPEAT_MASK     (1<<KEY1)       // repeat: key1, key2
27
#define REPEAT_START    50                        // after 500ms
28
#define REPEAT_NEXT     20                        // every 200ms
29
30
#define LED_DDR         DDRD
31
#define LED_PORT        PORTD
32
#define LED0            PIND2    // Arduino Pro Mini Pin 2
33
#define LED1            PIND3    // Arduino Pro Mini Pin 3
34
35
volatile uint8_t key_state;
36
volatile uint8_t key_press;
37
volatile uint8_t key_rpt;
38
39
40
ISR(TIMER0_OVF_vect)
41
{
42
  static uint8_t ct0, ct1, rpt;
43
  uint8_t i;
44
  
45
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
46
  
47
  i = key_state ^ ~KEY_PIN;    // LOW ACTIVE
48
  //i = key_state ^ KEY_PIN;    // HIGH ACTIVE
49
  ct0 = ~( ct0 & i );
50
  ct1 = ct0 ^ (ct1 & i);
51
  i &= ct0 & ct1;
52
  key_state ^= i;
53
  key_press |= key_state & i;
54
  
55
  if( (key_state & REPEAT_MASK) == 0 )
56
  rpt = REPEAT_START;
57
  if( --rpt == 0 )
58
  {
59
    rpt = REPEAT_NEXT;
60
    key_rpt |= key_state & REPEAT_MASK;
61
  }
62
}
63
64
uint8_t get_key_press( uint8_t key_mask )
65
{
66
  cli();
67
  key_mask &= key_press;
68
  key_press ^= key_mask;
69
  sei();
70
  return key_mask;
71
}
72
73
uint8_t get_key_rpt( uint8_t key_mask )
74
{
75
  cli();
76
  key_mask &= key_rpt;
77
  key_rpt ^= key_mask;
78
  sei();
79
  return key_mask;
80
}
81
82
uint8_t get_key_state( uint8_t key_mask )
83
{
84
  key_mask &= key_state;
85
  return key_mask;
86
}
87
88
uint8_t get_key_short( uint8_t key_mask )
89
{
90
  cli();
91
  return get_key_press( ~key_state & key_mask );
92
}
93
94
uint8_t get_key_long( uint8_t key_mask )
95
{
96
  return get_key_press( get_key_rpt( key_mask ));
97
}
98
99
int main( void )
100
{
101
  LED_DDR |= (1 << LED0) | (1 << LED1);
102
  
103
  KEY_DDR &= ~ALL_KEYS;
104
  KEY_PORT |= ALL_KEYS;
105
  
106
  TCCR0A = (1<<CS02)|(1<<CS00);
107
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
108
  TIMSK0 |= 1<<TOIE0;
109
  
110
  sei();
111
112
  while(1)
113
  {
114
    if( get_key_short(1<<KEY0 ))
115
    LED_PORT ^= 1<<LED0;
116
    
117
    if( get_key_long( 1<<KEY0 ))
118
    LED_PORT ^= 1<<LED1;
119
  }
120
}

von chris (Gast)


Lesenswert?

in deinem Schaltplan sind die LEDs und Taster vertauscht gegenüber den 
Angaben im Code.

von Stefan S. (sschultewolter)


Lesenswert?

Den Schaltplan habe ich nach den Tests erstellt. Habe da die Pins 
vertauscht. Programm und Platine ist richtig.

von Teo D. (teoderix)


Lesenswert?

TIP:
Wen Du schon solche Schwierigkeiten mit fremd Code hast, dann 
programmiere es halt selbst.
Der Lerneffekt ist unübertrefflich, wenn man was selber macht :)

von chris (Gast)


Lesenswert?

du hast nirgends geschrieben, welchen Controller du benutzt (aber 
irgendwas von Arduino, also vermute ich Atmega328).

Ich habe das gerade kurz getestet auf einem Mega88 - die Timer-ISR wird 
garnicht aufgerufen. (einfach festzustellen mittels LED in der ISR 
anschalten).
Der Blick ins Datenblatt verrät auch sofort warum:
Der Prescaler wird beim Mega88 / 328 nicht in TCCR0A eingestellt, 
sondern in TCCR0B.
Dein Timer läuft also nicht.

nächstes Problem: du fragst "get_key_long(1<<KEY0)" ab. Um lange 
Tastendrücke feststellen zu können, muss KEY0 in der REPEAT_MASK 
eingetragen werden.

Nach diesen beiden Änderungen funktioniert der Code einwandfrei:

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
#define F_CPU 8000000UL
13
14
#include <stdint.h>
15
#include <avr/io.h>
16
#include <avr/interrupt.h>
17
#include <util/delay.h>
18
19
20
#define KEY_DDR         DDRB
21
#define KEY_PORT        PORTB
22
#define KEY_PIN         PINB
23
#define KEY0            0
24
#define KEY1            1
25
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1)
26
27
// für langen Tastendruck muss die Taste hier eingetragen werden!
28
#define REPEAT_MASK     (1<<KEY0|1<<KEY1)       // repeat: key1, key2
29
30
#define REPEAT_START    50                        // after 500ms
31
#define REPEAT_NEXT     20                        // every 200ms
32
33
#define LED_DDR         DDRD
34
#define LED_PORT        PORTD
35
#define LED0            PIND2    // Arduino Pro Mini Pin 2
36
#define LED1            PIND3    // Arduino Pro Mini Pin 3
37
38
volatile uint8_t key_state;
39
volatile uint8_t key_press;
40
volatile uint8_t key_rpt;
41
42
ISR(TIMER0_OVF_vect)
43
{
44
  static uint8_t ct0, ct1, rpt;
45
  uint8_t i;
46
  
47
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
48
  
49
  i = key_state ^ ~KEY_PIN;    // LOW ACTIVE
50
  //i = key_state ^ KEY_PIN;    // HIGH ACTIVE
51
  ct0 = ~( ct0 & i );
52
  ct1 = ct0 ^ (ct1 & i);
53
  i &= ct0 & ct1;
54
  key_state ^= i;
55
  key_press |= key_state & i;
56
  
57
  if( (key_state & REPEAT_MASK) == 0 )
58
  rpt = REPEAT_START;
59
  if( --rpt == 0 )
60
  {
61
    rpt = REPEAT_NEXT;
62
    key_rpt |= key_state & REPEAT_MASK;
63
  }
64
}
65
66
uint8_t get_key_press( uint8_t key_mask )
67
{
68
  cli();
69
  key_mask &= key_press;
70
  key_press ^= key_mask;
71
  sei();
72
  return key_mask;
73
}
74
75
uint8_t get_key_rpt( uint8_t key_mask )
76
{
77
  cli();
78
  key_mask &= key_rpt;
79
  key_rpt ^= key_mask;
80
  sei();
81
  return key_mask;
82
}
83
84
uint8_t get_key_state( uint8_t key_mask )
85
{
86
  key_mask &= key_state;
87
  return key_mask;
88
}
89
90
uint8_t get_key_short( uint8_t key_mask )
91
{
92
  cli();
93
  return get_key_press( ~key_state & key_mask );
94
}
95
96
uint8_t get_key_long( uint8_t key_mask )
97
{
98
  return get_key_press( get_key_rpt( key_mask ));
99
}
100
101
int main( void )
102
{
103
  LED_DDR |= (1 << LED0) | (1 << LED1);
104
  
105
  KEY_DDR &= ~ALL_KEYS;
106
  KEY_PORT |= ALL_KEYS;
107
  
108
  // Prescaler ist nicht in TCCR0A, sondern in TCCR0B!
109
  TCCR0B = (1<<CS02)|(1<<CS00);
110
  
111
  
112
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
113
  TIMSK0 |= 1<<TOIE0;
114
  
115
  sei();
116
117
  while(1)
118
  {
119
    if( get_key_short(1<<KEY0 ))
120
    LED_PORT ^= 1<<LED0;
121
    
122
    if( get_key_long( 1<<KEY0 ))
123
    LED_PORT ^= 1<<LED1;
124
  }
125
}


lg
Chris

von Stefan F. (Gast)


Lesenswert?

Ja, das sehe ich auch so. Für Anfänger ist es einfacher und lehrreicher, 
keinen fremden Code einzusetzen.

Fremden Code zu analysieren (wenn er mal nicht wie erwartet 
funktioniert) ist erheblich schwieriger und eher etwas für 
Fortgeschrittene.

von chris (Gast)


Lesenswert?

Nachtrag:
auf neueren AVRs (wie z.B. Mega88 / 328) kann man direkt Ausgangspins 
togglen, indem man ins PINx-Register schreibt. Das hat den Vorteil, dass 
es zum einen schneller geht und weniger Code erzeugt, zum anderen ist es 
aber auch atomar! D.h. es gibt keine Probleme, auch wenn du in 
Interrupts auf den gleichen Port schreibst.
Das sieht dann so aus:
1
#define LED_PIN    PIND
2
3
#define LED_DDR         DDRD
4
#define LED_PORT        PORTD
5
#define LED0            PIND2    // Arduino Pro Mini Pin 2
6
#define LED1            PIND3    // Arduino Pro Mini Pin 3
7
8
if( get_key_short(1<<KEY0 ))
9
LED_PIN = (1<<LED0);
10
    
11
if( get_key_long( 1<<KEY0 ))
12
LED_PIN = (1<<LED1);

von Stefan S. (sschultewolter)


Lesenswert?

Einen eigenen Code habe ich bereits geschrieben, irgendwo oben, der auch 
soweit funktionierte.

Das es sich um einen Atmega328P handelt, bzw. Pro Mini, habe ich oben 
bereits geschrieben bzw. es im Code hinterlegt.

Nun geht es soweit, der einzige Fehler, den ich mehr oder weniger 
gemacht habe ist, dass ich hätte das B-Register wählen müssen anstatt 
des A-Registers.

Werde es nun wieder auf den Attiny85 portieren.

von Bastler (Gast)


Lesenswert?

> Fremden Code zu analysieren (wenn er mal nicht wie erwartet
> funktioniert) ist erheblich schwieriger und eher etwas für
> Fortgeschrittene.

Aber man kann eben auch was dabei lernen ;-)
Fehler gezielt zu suchen (und dann auch zu beheben), ist das 
Eigentliche, was man können muß. Und da hilft nur Training!

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.