mikrocontroller.net

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


Autor: H. G. (ledi)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/sleep.h>

int main (void)
{
  DDRC =     (1<<PC4)|(1<<PC5)|(1<<PC6)|(1<<PC7);    // Datenrichtung der Zeilen = Ausgang
  DDRD =     (0<<PIND6);                  // Datenrichtung Eingang (für Int 0)
  DDRB =     (0<<PINB2)|(0<<PINB5);          // Datenrichtung Eingang (für Int 1, Int 2)
  DDRB =     (1<<DDB4)|(1<<DDB3)|(1<<DDB0)|(1<<DDB1);  // LEDs = Ausgang
  PORTD |=   (1<<PD6);                  // Pull-up
  PORTB |=   (1<<PB2)|(1<<PB5);              // Pull-up

  EIMSK |=  (1<<INT0)|(1<<INT1)|(1<<INT2);        // Set Int0 - 2 enable
  EICRA |=   (1<<ISC01)|(1<<ISC11)|(1<<ISC21);       // Set Int0 - 2 on falling edge

  sei();                          // enable all Interrupts

  while (1)             
  {
    PORTB ^= (1<<PB3);      // Led on/off
    _delay_ms(500);        // Warten
  }
}

ISR(INT0_vect)              // Aufruf Interrupt Service Routine INT0
{
  PORTB ^= (1<<PB4);        // Led0 on
  _delay_ms(100);
}

ISR(INT1_vect)              // Aufruf Interrupt Service Routine INT1
{
  PORTB ^= (1<<PB0);        // Led1 on
  _delay_ms(100);
}

ISR(INT2_vect)              // Aufruf Interrupt Service Routine INT2
{
  PORTB ^= (1<<PB1);        // Led2 on
  _delay_ms(100);
}


Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: MaWin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: H. G. (ledi)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: MaWin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: H. G. (ledi)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Peter Dannegger (peda)
Datum:

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

Z.B.:
#include <avr\io.h>

#define NOP();  asm volatile("nop"::);


#define KEY_PIN         PINB
#define KEY_PORT        PORTB
#define KEY_DDR         DDRB

// Pin 0..3 = column 1..4
// Pin 4..6 = row 1..3


uint8_t keyscan( void )
{
  uint8_t col = 0, row = 0;

  KEY_PORT |= 0x7F;                     // all pullups on
  KEY_DDR = (KEY_DDR & 0x80) | 0x70;    // pin 6..4 = output
  KEY_PORT &= 0x8F;                     // pin 6..4 = output low
  NOP();                                // wait until inputs sampled
  if( ~KEY_PIN & 1<<0 )                 // if pin 0 = low
    col = 4;
  if( ~KEY_PIN & 1<<1 )
    col = 3;
  if( ~KEY_PIN & 1<<2 )
    col = 2;
  if( ~KEY_PIN & 1<<3 )
    col = 1;
  row = col;
  if( col ){                            // if column found, check row
    KEY_PORT |= 0x7F;                   // all pullups on
    KEY_DDR = (KEY_DDR & 0x80) | 0x0F;  // pin 3..0 = output
    KEY_PORT &= 0xF0;                   // pin 3..0 = output low
    NOP();                              // wait until inputs sampled
    if( ~KEY_PIN & 1<<4 )
      row = 5;
    if( ~KEY_PIN & 1<<5 )
      row = 9;
    if( ~KEY_PIN & 1<<6 )
      row = 13;
  }                                     // 0 = no key
  return row - col;                     // 1..12 = key
}


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


Peter

Autor: H. G. (ledi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach ja....!

Ich verwende einen ATMEL 90PWM316

Autor: H. G. (ledi)
Datum:
Angehängte Dateien:
  • preview image for TM.png
    TM.png
    193 KB, 423 Downloads

Bewertung
0 lesenswert
nicht 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?
#include <avr\io.h>

#define NOP();  asm volatile("nop"::);


#define KEY_PIN         PINB
#define KEY_PORT        PORTB
#define KEY_DDR         DDRB

// Pin 0..3 = column 1..4
// Pin 4..6 = row 1..3

uint8_t keyscan( void )
{
  uint8_t col = 0, row = 0;

  KEY_PORT |= 0x7F;                     // all pullups on
  KEY_DDR = (KEY_DDR & 0x80) | 0x70;    // pin 6..4 = output
  KEY_PORT &= 0x8F;                     // pin 6..4 = output low
  NOP();                                // wait until inputs sampled
  if( ~KEY_PIN & 1<<0 )                 // if pin 0 = low
  {
    col = 4;
  PORTD |= (1<<PD4);      //Taste 1
  }
  if( ~KEY_PIN & 1<<1 )
  {
    col = 3;
  PORTD |= (1<<PD5);      //Taste 4
  }
  if( ~KEY_PIN & 1<<2 )
  {
    col = 2;
  PORTD |= (1<<PD6);      //Taste 7
  }
  if( ~KEY_PIN & 1<<3 )
  {
    col = 1;
  PORTD |= (1<<PD7);      //Taste *
  }
  row = col;
  if( col ){                            // if column found, check row
    KEY_PORT |= 0x7F;                   // all pullups on
    KEY_DDR = (KEY_DDR & 0x80) | 0x0F;  // pin 3..0 = output
    KEY_PORT &= 0xF0;                   // pin 3..0 = output low
    NOP();                              // wait until inputs sampled
    if( ~KEY_PIN & 1<<4 )
  {
      row = 5;
    PORTC |= (1<<PC0);    //Spalte1
    }
    if( ~KEY_PIN & 1<<5 )
  {
      row = 9;
    PORTC |= (1<<PC1);    //Spalte2
    }
    if( ~KEY_PIN & 1<<6 )
  {
      row = 13;
    PORTC |= (1<<PC2);    //Spalte3
    }
  }                                     // 0 = no key
  return row - col;                     // 1..12 = key
}

int main (void)
{
  DDRD = 0xFF;  
  DDRC = 0xFF;          
  while(1)
  {
    PORTD = keyscan();
  }
}


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
uint8_t KeyMap[] =
  { 0,
    '1', '4', '7', '*',
    '2', '5', '8', '0',
    '3', '6', '9', '#'
  };


uint8_t keyscan( void )
{
  ....

  return KeyMap[ row - col ];
}

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.

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: H. G. (ledi)
Datum:

Bewertung
0 lesenswert
nicht 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.
>
>
> uint8_t KeyMap[] =
>   { 0,
>     '1', '4', '7', '*',
>     '2', '5', '8', '0',
>     '3', '6', '9', '#'
>   };
> 
> 
> uint8_t keyscan( void )
> {
>   ....
> 
>   return KeyMap[ row - col ];
> }
> 


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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.