Forum: Mikrocontroller und Digitale Elektronik Atmega8 Taster an Interrupt löst zweimal aus


von BeYourself (Gast)


Lesenswert?

Abend,
ich bin gerade dabei ein kleines Programm für den Atmega zu schreiben, 
aber verzweifle an den ganzen Enprellgeschichten :(

Hier mal mein Code, das Problem ist, dass er nach 2,5 Sekunden wieder 
ausgelöst wird
1
ISR(INT0_vect) { //Interrupt an Pin INT0
2
  uart_puts("Taste gedrueckt\n");
3
  if(TIMSK & OCIE1A) {
4
    TIMSK |= (0<<OCIE1A);
5
    }
6
  else {
7
    TIMSK |= (1<<OCIE1A);
8
    }
9
  _delay_ms(2500);
10
}

von der mechatroniker (Gast)


Lesenswert?

>  _delay_ms(2500);

in der ISR. Sag mal, bist du des Wahnsinns?

von Markus F. (5volt) Benutzerseite


Lesenswert?

Während eine ISR ausgeführt wird, speichert sich der Controller alle 
weiteren Interrupts und führt sie nachher aus.
Durch das Prellen des Tasters wird INT0 mehrmals ausgelöst, kann aber 
nur einmal alle 2,5s ausgeführt werden. Daher wird der Interrupt zweimal 
nacheinander ausgelöst.
Abhilfe würde vermutlich bringen, in der ISR INT0 abzuschalten und am 
Ende wieder zu aktivieren.
Und 2,5s Wartezeit in einer ISR macht man normalerweise auch nicht...

von der mechatroniker (Gast)


Lesenswert?

Ok, ein Versuch einer Erklärung, was passiert.

1. Der Interrupt wird ausgelöst. Das interne Flag für die 
Interruptquelle wird zurückgesetzt.

2. Das Interruptflag im SREG wird gelöscht (dafür sorgt der Compiler).

3. Das bisschen Code vor dem delay wird abgearbeitet, dann beginnt das 
Delay

4. Der Taster prellt, das Flag für den INT0 wird gesetzt, wegen 
gesperrten Interrupts wird die ISR nicht nochmal aufgerufen.

5. Das Delay ist zuende nach 2,5 sec

6. Die Interrupts werden global wieder aktiviert (dafür sorgt der 
Compiler) und die ISR verlassen

7. INT0 steht an und Interrupts sind aktiv -> ISR wird angesprungen

Klar warum das alles eine schlechte Idee ist, was du da vorhast?

von spess53 (Gast)


Lesenswert?

Hi

Wow. Ich kann zwar kein C, aber in dem Stück Code dürfte alles, was man 
in einer Interrupt-Routine falsch machen kann drin sein.

MfG Spess

von Hc Z. (mizch)


Lesenswert?

Mal angenommen, der TE ist nicht des Wahnsinns, sondern es ist nur zur 
Übung und nicht zur Verwendung in einem realen Programm.

Dann muss nach dem delay_ms() das Flag für INT0 rückgesetzt werden, denn 
das wurde zwar beim Aufruf der Interrupt-Routine automatisch 
rückgesetzt, danach aber gleich wieder durch Prellen gesetzt.  Deshalb 
kommt der Interrupt gleich wieder.

von Peter D. (peda)


Lesenswert?

BeYourself schrieb:
> aber verzweifle an den ganzen Enprellgeschichten :(

Wobei denn konkret?

Hier z.B. mit einem Demoprogramm, welches die Entprellschritte im 
1s-Takt anzeigt:

http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=1801


> Hier mal mein Code, das Problem ist, dass er nach 2,5 Sekunden wieder
> ausgelöst wird

Wirklich ein sehr schönes Beispiel, warum man doch besser eine 
Entprellroutine nehmen sollte.


Peter

von BeYourself (Gast)


Lesenswert?

Abend nochmal,
hab mir mal den Link angeschaut und versucht, das ganze auf einem 
Atmega8 zum laufen zu bringen. Anstatt dem Timer1 habe ich es so 
versucht, dass der zweite Timer verwendet wird (Den 16bit-Timer verwende 
ich schon in meinem Programm). Leider passiert nichts, wenn ich PB0 oder 
PB1 auf GND oder VCC ziehe :(

Und mein Code oben wollte nicht in einem "richtigen" Programm verwenden, 
sondern habe die Zeit erstmal einmal erhöht, um auszuschließen, dass die 
Zeit zwischen zwei Interrupts nicht zu schnell vorbeigeht.

Hier mal mein Versuch, der auch noch eine Warnung ausspuckt:
../tasten_entrpellen.c:112: warning: overflow in implicit constant 
conversion

Den Compiler habe ich mit "--std=c99" gestartet.
Werde mich dann morgen/heute nochmal dransetzen

MFG BeYourself
1
/************************************************************************/
2
/*                                                                      */
3
/*                      Debouncing 8 Keys        */
4
/*      Sampling 4 Times        */
5
/*                                                                      */
6
/*              Author: Peter Dannegger                                 */
7
/*                                                                      */
8
/************************************************************************/
9
10
#include <util\atomic.h>    // need "--std=c99"
11
12
13
typedef unsigned char  u8;
14
typedef signed short  s16;
15
16
#define  XTAL    8e6    // 8MHz
17
18
#define KEY_PIN    PINB
19
#define KEY_PORT  PORTB
20
#define KEY_DDR    DDRB
21
#define KEY0    0
22
#define  KEY1    1
23
24
#define LED_DDR    DDRD
25
#define LED_PORT  PORTD
26
#define LED0    0
27
#define  LED1    1
28
#define LED3    3
29
#define LED4    4
30
#define LED5    5
31
#define LED6    6
32
#define LED7    7
33
34
35
u8 key_state;        // debounced and inverted key state:
36
          // bit = 1: key pressed
37
u8 key_press;        // key press detect
38
39
u8 ct0 = 0xFF, ct1 = 0xFF;    // internal debouncing states
40
41
42
ISR( TIMER2_COMP_vect )
43
{
44
  u8 i;
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
54
55
u8 get_key_press( u8 key_mask )
56
{
57
  ATOMIC_BLOCK(ATOMIC_FORCEON){
58
    key_mask &= key_press;    // read key(s)
59
    key_press ^= key_mask;    // clear key(s)
60
  }
61
  return key_mask;
62
}
63
64
65
void display_debounce_key0( void )  // demonstrate debounce working
66
{
67
  u8 i;
68
69
  ATOMIC_BLOCK(ATOMIC_FORCEON){
70
    i = ct0 & 1<<KEY0;      // collect the two bits for KEY0
71
    if( ct1 & 1<<KEY0 )      // from vertical counter
72
      i |= 2;
73
  }
74
  switch( i ){        // LED7..4 = debounce counter for KEY0
75
    case 0:
76
        LED_PORT &= ~(1<<LED4);
77
        LED_PORT |= 1<<LED5;
78
        LED_PORT |= 1<<LED6;
79
        LED_PORT |= 1<<LED7;
80
        break;
81
    case 1:
82
        LED_PORT |= 1<<LED4;
83
        LED_PORT &= ~(1<<LED5);
84
        LED_PORT |= 1<<LED6;
85
        LED_PORT |= 1<<LED7;
86
        break;
87
    case 2:
88
        LED_PORT |= 1<<LED4;
89
        LED_PORT |= 1<<LED5;
90
        LED_PORT &= ~(1<<LED6);
91
        LED_PORT |= 1<<LED7;
92
        break;
93
    case 3:
94
        LED_PORT |= 1<<LED4;
95
        LED_PORT |= 1<<LED5;
96
        LED_PORT |= 1<<LED6;
97
        LED_PORT &= ~(1<<LED7);
98
        break;
99
  }
100
  if( key_state & 1<<KEY0 )    // LED3 = debounced state
101
    LED_PORT &= ~(1<<LED3);
102
  else
103
    LED_PORT |= 1<<LED3;
104
}
105
106
107
int main( void )
108
{
109
  TCCR2 = 0;
110
  TCCR2 = 1<<WGM21      // Mode 4: CTC
111
   ^ 1<<CS22^1<<CS20;    // divide by 1024
112
  OCR2 = XTAL / 1024.0 * 1 - 1;  // 1s
113
  TCCR2 = 0;
114
  TIMSK = 1<<OCIE2;      // enable T1 interrupt
115
116
  KEY_DDR = 0;        // input
117
  KEY_PORT = 0xFF;      // pullups on
118
  LED_PORT = 0xFF;      // LEDs off (low active)
119
  LED_DDR = 0xFF;      // LED output
120
  key_state = ~KEY_PIN;      // no action on keypress during reset
121
  sei();
122
123
  for(;;){          // main loop
124
    display_debounce_key0();
125
126
    if( get_key_press( 1<<KEY0 ))  // LED0 = toggle on keypress
127
      LED_PORT ^= 1<<LED0;
128
129
    if( get_key_press( 1<<KEY1 ))  // LED1 = toggle on keypress
130
      LED_PORT ^= 1<<LED1;
131
  }
132
}

von Stefan E. (sternst)


Lesenswert?

1
  TCCR2 = 0;
2
  TCCR2 = 1<<WGM21      // Mode 4: CTC
3
   ^ 1<<CS22^1<<CS20;    // divide by 1024
4
  OCR2 = XTAL / 1024.0 * 1 - 1;  // 1s
5
  TCCR2 = 0;
Wenn du den Timer gleich nach der Konfiguration wieder abschaltest, ist 
es auch kein Wunder, wenn nichts passiert. ;-)

von Vlad T. (vlad_tepesch)


Lesenswert?

alles wird einfacher, wenn man taster nicht an einen interupt 
anschließt, sondern pollt

von Peter D. (peda)


Lesenswert?

BeYourself schrieb:
> Hier mal mein Versuch, der auch noch eine Warnung ausspuckt:
> ../tasten_entrpellen.c:112: warning: overflow in implicit constant
> conversion

Ja, da hat er auch recht.

Es ist ja explizit als Demo gedacht, damit man an den LEDs die einzelnen 
Phasen der Entprellung mitverfolgen kann.

In der Praxis is 1s Entprelltakt völliger Unsinn, da nimmt man etwa 
2ms..50ms. Und dann paßt der Reloadwert auch in 8Bit rein.


Peter

von I. E. (anfaenger69)


Lesenswert?

Ahem, Chef... das hier funktioniert nicht:

    TIMSK |= (0<<OCIE1A);

Es müsste lauten:

    TIMSK &= ~(1<<OCIE1A);

Vielleicht ist das der ganze Fehler weil Du den Timer nicht 
ausgeschaltet hast.

von BeYourself (Gast)


Lesenswert?

Soo, ich habe mich nochmal dran gesetzt und möchte euch meine Lösung 
natürlich nicht vorenthalten ;-)

Erstmal hier meine Anpassung für  Peter Danneggers Entprellmethode für 
einen Atmega8 und den 2. Timer:
1
/************************************************************************/
2
/*                                                                      */
3
/*                      Debouncing 8 Keys        */
4
/*      Sampling 4 Times        */
5
/*                                                                      */
6
/*              Author: Peter Dannegger                                 */
7
/*                                                                      */
8
/************************************************************************/
9
10
#include <util\atomic.h>    // need "--std=c99"
11
12
13
typedef unsigned char  u8;
14
typedef signed short  s16;
15
16
#define  XTAL    8e6    // 8MHz
17
18
#define KEY_PIN    PINB
19
#define KEY_PORT  PORTB
20
#define KEY_DDR    DDRB
21
#define KEY0    0
22
#define  KEY1    1
23
24
#define LED_DDR    DDRD
25
#define LED_PORT  PORTD
26
#define LED0    0
27
#define  LED1    1
28
#define LED3    3
29
#define LED4    4
30
#define LED5    5
31
#define LED6    6
32
#define LED7    7
33
34
35
u8 key_state;        // debounced and inverted key state:
36
          // bit = 1: key pressed
37
u8 key_press;        // key press detect
38
39
u8 ct0 = 0xFF, ct1 = 0xFF;    // internal debouncing states
40
41
42
ISR( TIMER2_COMP_vect )
43
{
44
  u8 i;
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
54
55
u8 get_key_press( u8 key_mask )
56
{
57
  ATOMIC_BLOCK(ATOMIC_FORCEON){
58
    key_mask &= key_press;    // read key(s)
59
    key_press ^= key_mask;    // clear key(s)
60
  }
61
  return key_mask;
62
}
63
64
int main( void )
65
{
66
  TCCR2 = 0;
67
  TCCR2 |= 1<<WGM21      // Mode 4: CTC
68
   ^ 1<<CS22^1<<CS20;    // divide by 1024
69
  OCR2 = XTAL / 1024.0 * 1 - 1;  // 1s
70
  TIMSK |= 1<<OCIE2;      // enable T1 interrupt
71
72
  KEY_DDR = 0;        // input
73
  KEY_PORT = 0xFF;      // pullups on
74
  LED_PORT = 0x00;      // LEDs off (low active)
75
  LED_DDR = 0xFF;      // LED output
76
  key_state = ~KEY_PIN;      // no action on keypress during reset
77
  sei();
78
79
  for(;;){          // main loop
80
    //display_debounce_key0();
81
82
    if( get_key_press( 1<<KEY0 )) {// LED on
83
      LED_PORT = 0xFF;
84
    }
85
86
    if( get_key_press( 1<<KEY1 )) {  // LED1 = toggle on keypress
87
      LED_PORT ^= 1<<LED0;
88
    LED_PORT ^= 1<<LED1;
89
    LED_PORT ^= 1<<LED3;
90
    LED_PORT ^= 1<<LED4;
91
    }
92
  }
93
}

@Igor:
Da hat du auch wieder recht, dass hat zwar glaubig mal funktioniert, ist 
aber auch nicht gerade schön, habe ich jetzt mal geändert.

Um noch einmal auf die Warnung zu sprechen zu kommen: Ich habe mich dann 
mal für die 2ms entschieden und es entsprechen angepasst:
1
TCCR2 |= 1<<CS22;    // divide by 64
2
    OCR2 = 6;

Bis jetzt funktioniert wieder alles und ich kann mal ein wenig 
weiterschreiben. Als nächstes muss ich gucken, wie ich verschiedene 
Variablen und Befehle vom PC oder einem anderen Controller (über RS/TWI) 
in den Controller bekomme.

Ich sage noch einmal danke für eure Mithilfe
BeYourself

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.