Forum: Mikrocontroller und Digitale Elektronik Tastenmatrix 3x4 mittels Interrupt abfragen?


von H. G. (ledi)


Angehängte Dateien:

Lesenswert?

Da ich kein Pollingverfahren verwende, möchte ich über eine 3x4 
Tastenmatrix die einzelnen Tasten über Interrupts abfragen. (Siehe 
Datei)
Die 3 Spalten sind mit den Interruptpins Int0, Int1 und Int2 des 
Controllers verbunden. Die 4 Zeilen mit den Pins PC4...PC7.
Spaltenpins = als Eingänge geschaltet
Zeilenpins = als Ausgänge geschaltet

Die 3 Spalten ergeben sich über die einzelnen Interrupts. Wie mache ich 
das aber am einfachsten mit den Zeilen?

Hier der C-Code:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <avr/wdt.h>
5
#include <avr/sleep.h>
6
7
int main (void)
8
{
9
  DDRC =     (1<<PC4)|(1<<PC5)|(1<<PC6)|(1<<PC7);    // Datenrichtung der Zeilen = Ausgang
10
  DDRD =     (0<<PIND6);                  // Datenrichtung Eingang (für Int 0)
11
  DDRB =     (0<<PINB2)|(0<<PINB5);          // Datenrichtung Eingang (für Int 1, Int 2)
12
  DDRB =     (1<<DDB4)|(1<<DDB3)|(1<<DDB0)|(1<<DDB1);  // LEDs = Ausgang
13
  PORTD |=   (1<<PD6);                  // Pull-up
14
  PORTB |=   (1<<PB2)|(1<<PB5);              // Pull-up
15
16
  EIMSK |=  (1<<INT0)|(1<<INT1)|(1<<INT2);        // Set Int0 - 2 enable
17
  EICRA |=   (1<<ISC01)|(1<<ISC11)|(1<<ISC21);       // Set Int0 - 2 on falling edge
18
19
  sei();                          // enable all Interrupts
20
21
  while (1)             
22
  {
23
    PORTB ^= (1<<PB3);      // Led on/off
24
    _delay_ms(500);        // Warten
25
  }
26
}
27
28
ISR(INT0_vect)              // Aufruf Interrupt Service Routine INT0
29
{
30
  PORTB ^= (1<<PB4);        // Led0 on
31
  _delay_ms(100);
32
}
33
34
ISR(INT1_vect)              // Aufruf Interrupt Service Routine INT1
35
{
36
  PORTB ^= (1<<PB0);        // Led1 on
37
  _delay_ms(100);
38
}
39
40
ISR(INT2_vect)              // Aufruf Interrupt Service Routine INT2
41
{
42
  PORTB ^= (1<<PB1);        // Led2 on
43
  _delay_ms(100);
44
}

von Peter D. (peda)


Lesenswert?

Heimo G. schrieb:
> Da ich kein Pollingverfahren verwende

Warum nicht?
Man hat doch eh meistens nen Timerinterrupt am Laufen.

> Die 3 Spalten ergeben sich über die einzelnen Interrupts. Wie mache ich
> das aber am einfachsten mit den Zeilen?

Indem Du im Timerinterrupt abfragst und entprellst.


Peter

von MaWin (Gast)


Lesenswert?

> Da ich kein Pollingverfahren verwende, möchte ich über eine 3x4
> Tastenmatrix die einzelnen Tasten über Interrupts abfragen

Dann polle die Tastatur in einem Timerinterrupt.

von H. G. (ledi)


Lesenswert?

Peter Dannegger schrieb:
> Heimo G. schrieb:
>> Da ich kein Pollingverfahren verwende
>
> Warum nicht?
> Man hat doch eh meistens nen Timerinterrupt am Laufen.

Da ich die Schaltung über Akkus betreiben möchte, soll der Controller 
"schlafen", wenn kein Tastendruck erfolgt.
>
>> Die 3 Spalten ergeben sich über die einzelnen Interrupts. Wie mache ich
>> das aber am einfachsten mit den Zeilen?
>
> Indem Du im Timerinterrupt abfragst und entprellst.
>

Genau hier habe ich das Problem (Bin noch Anfänger)!
Wie genau mache ich das?
>
> Peter

von MaWin (Gast)


Lesenswert?

> Da ich die Schaltung über Akkus betreiben möchte, soll der Controller
> "schlafen", wenn kein Tastendruck erfolgt.

PC4-PC7 auf Low, PullUps an INT0-INT2 einschalten,
und schlafen gehen, auf Interrupts (durch fallende
Flanke bzw. oft zwingt einen der uC Hersteller zu
low-Pegel) warten.

Beim aufwachen nach einem Interrupt mit PC4-PC7
herausfinden, welches Taste es nun war.

von H. G. (ledi)


Lesenswert?

MaWin schrieb:
>> Da ich die Schaltung über Akkus betreiben möchte, soll der Controller
>> "schlafen", wenn kein Tastendruck erfolgt.
>
> PC4-PC7 auf Low, PullUps an INT0-INT2 einschalten,
> und schlafen gehen, auf Interrupts (durch fallende
> Flanke bzw. oft zwingt einen der uC Hersteller zu
> low-Pegel) warten.
>

OK! Das ist mir klar!

> Beim aufwachen nach einem Interrupt mit PC4-PC7
> herausfinden, welches Taste es nun war.

Und hier habe ich das Problem!
Wie finde ich heraus, welche Taste nun gedrückt wurde?

von Peter D. (peda)


Lesenswert?

Heimo G. schrieb:
> Wie finde ich heraus, welche Taste nun gedrückt wurde?

Z.B.:
1
#include <avr\io.h>
2
3
#define NOP();  asm volatile("nop"::);
4
5
6
#define KEY_PIN         PINB
7
#define KEY_PORT        PORTB
8
#define KEY_DDR         DDRB
9
10
// Pin 0..3 = column 1..4
11
// Pin 4..6 = row 1..3
12
13
14
uint8_t keyscan( void )
15
{
16
  uint8_t col = 0, row = 0;
17
18
  KEY_PORT |= 0x7F;                     // all pullups on
19
  KEY_DDR = (KEY_DDR & 0x80) | 0x70;    // pin 6..4 = output
20
  KEY_PORT &= 0x8F;                     // pin 6..4 = output low
21
  NOP();                                // wait until inputs sampled
22
  if( ~KEY_PIN & 1<<0 )                 // if pin 0 = low
23
    col = 4;
24
  if( ~KEY_PIN & 1<<1 )
25
    col = 3;
26
  if( ~KEY_PIN & 1<<2 )
27
    col = 2;
28
  if( ~KEY_PIN & 1<<3 )
29
    col = 1;
30
  row = col;
31
  if( col ){                            // if column found, check row
32
    KEY_PORT |= 0x7F;                   // all pullups on
33
    KEY_DDR = (KEY_DDR & 0x80) | 0x0F;  // pin 3..0 = output
34
    KEY_PORT &= 0xF0;                   // pin 3..0 = output low
35
    NOP();                              // wait until inputs sampled
36
    if( ~KEY_PIN & 1<<4 )
37
      row = 5;
38
    if( ~KEY_PIN & 1<<5 )
39
      row = 9;
40
    if( ~KEY_PIN & 1<<6 )
41
      row = 13;
42
  }                                     // 0 = no key
43
  return row - col;                     // 1..12 = key
44
}


Wenn Dein (unbekannter) AVR zum Aufwachen nen Pin-Change-Interrupt hat, 
kannst Du alle 7 Pins auf einen Port legen.


Peter

von H. G. (ledi)


Lesenswert?

Ach ja....!

Ich verwende einen ATMEL 90PWM316

von H. G. (ledi)


Angehängte Dateien:

Lesenswert?

Hallo Peter!
Erstmal vielen Dank für Deine Hilfe!

Ich habe nun den Code für meine Anwendung eingebaut.
Soweit funktioniert das auch, wobei der in der Funktion zurückgegebene 
Wert nicht mit meiner Tastatur übereinstimmt. (Siehe Bild: Die roten 
Ziffern werden berechnet).
Wie baue ich das am am einfachsten um?

Weiters möchte ich die Funktion in eine Interruptroutine einfügen. D.h, 
egal, welche Taste ich drücke soll der Int. ausgelöst werden. Ich möchte 
hier aber nur einen einzigen Int. verwenden. Geht das?
1
#include <avr\io.h>
2
3
#define NOP();  asm volatile("nop"::);
4
5
6
#define KEY_PIN         PINB
7
#define KEY_PORT        PORTB
8
#define KEY_DDR         DDRB
9
10
// Pin 0..3 = column 1..4
11
// Pin 4..6 = row 1..3
12
13
uint8_t keyscan( void )
14
{
15
  uint8_t col = 0, row = 0;
16
17
  KEY_PORT |= 0x7F;                     // all pullups on
18
  KEY_DDR = (KEY_DDR & 0x80) | 0x70;    // pin 6..4 = output
19
  KEY_PORT &= 0x8F;                     // pin 6..4 = output low
20
  NOP();                                // wait until inputs sampled
21
  if( ~KEY_PIN & 1<<0 )                 // if pin 0 = low
22
  {
23
    col = 4;
24
  PORTD |= (1<<PD4);      //Taste 1
25
  }
26
  if( ~KEY_PIN & 1<<1 )
27
  {
28
    col = 3;
29
  PORTD |= (1<<PD5);      //Taste 4
30
  }
31
  if( ~KEY_PIN & 1<<2 )
32
  {
33
    col = 2;
34
  PORTD |= (1<<PD6);      //Taste 7
35
  }
36
  if( ~KEY_PIN & 1<<3 )
37
  {
38
    col = 1;
39
  PORTD |= (1<<PD7);      //Taste *
40
  }
41
  row = col;
42
  if( col ){                            // if column found, check row
43
    KEY_PORT |= 0x7F;                   // all pullups on
44
    KEY_DDR = (KEY_DDR & 0x80) | 0x0F;  // pin 3..0 = output
45
    KEY_PORT &= 0xF0;                   // pin 3..0 = output low
46
    NOP();                              // wait until inputs sampled
47
    if( ~KEY_PIN & 1<<4 )
48
  {
49
      row = 5;
50
    PORTC |= (1<<PC0);    //Spalte1
51
    }
52
    if( ~KEY_PIN & 1<<5 )
53
  {
54
      row = 9;
55
    PORTC |= (1<<PC1);    //Spalte2
56
    }
57
    if( ~KEY_PIN & 1<<6 )
58
  {
59
      row = 13;
60
    PORTC |= (1<<PC2);    //Spalte3
61
    }
62
  }                                     // 0 = no key
63
  return row - col;                     // 1..12 = key
64
}
65
66
int main (void)
67
{
68
  DDRD = 0xFF;  
69
  DDRC = 0xFF;          
70
  while(1)
71
  {
72
    PORTD = keyscan();
73
  }
74
}

von Karl H. (kbuchegg)


Lesenswert?

Heimo G. schrieb:

> Soweit funktioniert das auch, wobei der in der Funktion zurückgegebene
> Wert nicht mit meiner Tastatur übereinstimmt. (Siehe Bild: Die roten
> Ziffern werden berechnet).
> Wie baue ich das am am einfachsten um?

Da sind offenbar Zeilen mit Spalten vertauscht. Das könnte man jetzt im 
Code entsprechend umbauen.
Oder aber men benutzt ganz einfach einen Mapping-Array um das Mapping 
richtig zu stellen.
1
uint8_t KeyMap[] =
2
  { 0,
3
    '1', '4', '7', '*',
4
    '2', '5', '8', '0',
5
    '3', '6', '9', '#'
6
  };
7
8
9
uint8_t keyscan( void )
10
{
11
  ....
12
13
  return KeyMap[ row - col ];
14
}

Ob du da jetzt im Mapping Array die Tasten als Tastennummer oder aber 
(so wie ich jetzt) als Zeichen einträgst, musst du entscheiden. Im 
Grunde ist es einfach nur ein Array, in das du mit deiner ermittelten 
Nummer reingehst und welches dir sagt, welche Taste dann vorliegt. Wie 
die Einträge aussehen müssen hab ich mir aus deinem Bild abgeleitet. Die 
in deinem Bild rote Zahl ist die bisherige Nummer, so wie sie von 
keyscan bis jetzt ermittelt wurde. Da ich die als Index in das Array 
nehme, sagt mir das Bild, was an dieser Position im Array abgelegt sein 
muss.

von Detlev T. (detlevt)


Lesenswert?

Als Anmerkung: Die AVRs haben (meist?) auch einen Pin-Change-Interrupt. 
Vorteil: Man "verschwendet" nicht INT0-2 und es wären auch mehr als drei 
Spalten möglich.

von H. G. (ledi)


Lesenswert?

> Da sind offenbar Zeilen mit Spalten vertauscht. Das könnte man jetzt im
> Code entsprechend umbauen.
> Oder aber men benutzt ganz einfach einen Mapping-Array um das Mapping
> richtig zu stellen.
>
>
1
> uint8_t KeyMap[] =
2
>   { 0,
3
>     '1', '4', '7', '*',
4
>     '2', '5', '8', '0',
5
>     '3', '6', '9', '#'
6
>   };
7
> 
8
> 
9
> uint8_t keyscan( void )
10
> {
11
>   ....
12
> 
13
>   return KeyMap[ row - col ];
14
> }
15
>


SUPER!!!
Funktioniert einwandfrei!
Hast du auch eine Idee, wie ich das ganze nun in einer 
Interruptserviceroutine so verpacke, dass ich mit einem einzigen 
Interrupt auskomme?
Ich verwende den ATMEL 90PWM316 und der hat keinen Pin-Change-Interrupt.

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.