mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Verschiedene Tastenzuweisungen


Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe folgendes Vor:

Auf Port B werden 6 Taster angeschlossen die auf Port C durchverbunden 
sein sollen.
Also wird Taster B.0 gedrückt, soll LED C.0 leuchten.
Bei B.1 soll LED C.1 leuchten
usw.

Das ist ja noch relativ einfach.
Jedoch habe ich jetzt mehrere Konfigurationen, die in etwa so aussehen:

Konfig 1:
B.0 -> C.0
B.1 -> C.1
...

Konfig 2:
B.0 -> C.2
B.1 -> C.4
....

Konfig 3 - 6:
....

Und je nachdem welchen Wert die Variable für die 7-Segmentanzeige 
ausgewählt ist, diese Konfiguration soll aktuell sein.

Bis jetzt sieht das in etwa so aus:
 while(1)
  {
    switch (nKeyPress)
    {
      case 0:
        if(!(PINB&0x01))
             OUT_PORT = 0x01;
        if(!(PINB&0x02))
             OUT_PORT = 0x02;
      case 1:
        if(!(PINB&0x01))
             OUT_PORT = 0x04;
        if(!(PINB&0x02))
             OUT_PORT = 0x01;
    }
.... 

Wie würdet ihr das machen?
Dazu kommt noch das auch mehrere Tasten gleichzeitig bedient werden 
können müssen.

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wie würdet ihr das machen?

Ich würde im Flash ein Array mit Bitmustern ablegen.

> Dazu kommt noch das auch mehrere Tasten gleichzeitig bedient werden
> können müssen.

Dann würde ich erstmal die Dannegger-Entprellung so ergänzen, dass 
ich neben dem Drücken der Taste auch das Loslassen gemeldet bekomme 
(also neben "Key_State" und "Key_Press" auch "Key_Lost"). Beim Drücken 
wird das Bitmuster des Flash-Arrays dann zum gegenwärtigen 
Port-Bitmuster dazugeORt, beim Loslassen wird das gegenwärtige 
Port-Bitmuster mit dem invertierten Array-Bitmuster geANDet, um das Bit 
wieder zu löschen. Das Ganze funktioniert auch ganz gut über mehrere 
verschiedene Ports.

In AVR-ASM ist das ein Klacks, wie man es in C formuliert und dabei 
effizienten Code erhält, das weiß ich allerdings nicht.

...

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes Lux schrieb:
> Ich würde im Flash ein Array mit Bitmustern ablegen.

Das ist ein guter Ansatz.
Soll dieses Bitmuster nur die Zustände des Ausgangs beinhalten oder auch 
die des Eingangs?
Kann ich das mit Tabellen machen?
const unsigned char Tabelle[] PROGMEM =

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

Bewertung
0 lesenswert
nicht lesenswert
Manfred W. schrieb:
> Hannes Lux schrieb:
>> Ich würde im Flash ein Array mit Bitmustern ablegen.
>
> Das ist ein guter Ansatz.
> Soll dieses Bitmuster nur die Zustände des Ausgangs beinhalten oder auch
> die des Eingangs?
> Kann ich das mit Tabellen machen?
>
>
const unsigned char Tabelle[] PROGMEM =

Das PROGMEM würde ich fürs erste weglassen.
Eine Tabelle ist nichts anderes als ein Array.

Und ich würde ein 2D Array dafür vorsehen.
Die eine Achse ist die Konfiguration, die andere Achse ist die 
Tastennummer. Am Schnittpunkt der beiden findet sich dann das Bitmuster, 
welches am Port C auszugeben ist (entweder setzen oder löschen, je nach 
Tastenzustand).
#define LED0  ( 1 << PC0 )
#define LED1  ( 1 << PC1 )
#define LED2  ( 1 << PC2 )
#define LED3  ( 1 << PC3 )
#define LED4  ( 1 << PC4 )
#define LED5  ( 1 << PC5 )

uint8_t Pattern[ NR_CONFIGS ][ NR_KEYS ] =
   { { LED0, LED1, LED2, LED3, LED4, LED5 },    // Konfiguration 0
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 1
     ....
   };

int main()
{
.....


  while( 1 ) {
    .....

    if( nKeyPress ) {
      // Toggle die LED der betätigten Taste
      PORTC = PORTC ^ Pattern[ actConfig ][ actKey ];
    }

  }
}

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen dank erstmal!

Bei mir entspricht die Variable nKeyPress die actConfig.
Ich müßte demnach für jede Taste abfragen ob sie gedrückt wurde, oder 
soll der ganze Port überwacht werden?
Wie bekomme ich denn den Wert für actKey?

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

Bewertung
0 lesenswert
nicht lesenswert
Warum beschreibst du nicht gleich ganz einfach deine Aufgabenstellung 
und wir programmieren dir das dann hier aus?

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK,
Ich poste mal den bisherigen kompletten Code:
// ************************************************************************
// *                                                                      *
// *                             Arcade Stick                             *
// *                         Tastenprogrammierung                         *
// *                                                                      *
// ************************************************************************
 
#include <avr/io.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#ifndef F_CPU
#define F_CPU      1000000              // Processor Takt-Frequenz definieren
#warning kein F_CPU definiert
#endif

// Tasteneingänge definieren
// =========================
#define KEY_DDR         DDRB
#define KEY_PORT        PORTB
#define KEY_PIN         PINB
#define KEY1            6
#define KEY2            0
#define KEY3            1
#define KEY4            2
#define KEY5            3
#define KEY6            4
#define KEY7            5
#define ALL_KEYS        (1<<KEY1 | 1<<KEY2 | 1<<KEY3 | 1<<KEY4 | 1<<KEY5 | 1<<KEY6 | 1<<KEY7)

// Tastendrückwiederholungsdauer definieren
// ========================================
#define REPEAT_MASK     (1<<KEY1)
#define REPEAT_START    50                           // nach 500ms
#define REPEAT_NEXT     20                          // alle 200ms

// Segment und Tasten-Ausgänge definieren
// ======================================
#define SEGMENT_DDR    DDRD
#define SEGMENT_PORT  PORTD
#define OUT_DDR         DDRC
#define OUT_PORT        PORTC
#define OUT1            0
#define OUT2            1
#define OUT3            2
#define OUT4            3
#define OUT5            4
#define OUT6            5

#define LED0  (1 << PC0)
#define LED1  (1 << PC1)
#define LED2  (1 << PC2)
#define LED3  (1 << PC3)
#define LED4  (1 << PC4)
#define LED5  (1 << PC5)

uint8_t Pattern[NR_CONFIGS][NR_KEYS] =
   { { LED0, LED1, LED2, LED3, LED4, LED5 },    // Konfiguration 0
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 1
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 2
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 3
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 4
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 5
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 6
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 7
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 8
   };

volatile uint8_t key_state;                // Entprellt und invertierte Tastenstatus:
                            // Bit=1 -> Taste wurde gedrückt
volatile uint8_t key_press;                // Tastendruck registriert
volatile uint8_t key_rpt;                // Tastendruckdauer
volatile uint8_t nKeyPress;                // Tastendruckanzahl
volatile uint8_t Marker;                // Speichermarkierung im 7-Segment

uint8_t eeFooByte;                    // EEPROM Variable

// Zeichentabelle für 7-Segment
// ============================
const unsigned char Tabelle[] PROGMEM = {249, 164, 176, 153, 146, 130, 248, 128, 144};

// Timer Interrupt von 10ms definieren
// ===================================
ISR(TIMER0_OVF_vect)
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;

  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);

  i = key_state ^ ~KEY_PIN;              // Taste geändert?
  ct0 = ~(ct0 & i);                  // resete oder zähle CT0
  ct1 = ct0 ^ (ct1 & i);                // resete oder zähle CT1
  i &= ct0 & ct1;                    // Überlauf gezählt?
  key_state ^= i;                    // dann Entprellstatus ändern
  key_press |= key_state & i;              // 0->1: Tastendruck erkannt

  if((key_state & REPEAT_MASK) == 0)          // Überprüfe Tastenwiederholfunktion
    rpt = REPEAT_START;                // Starte Verzögerung
  if(--rpt == 0)
  {
    rpt = REPEAT_NEXT;                // Wiederhole Verzögerung
    key_rpt |= key_state & REPEAT_MASK;
  }
}

// Überprüfen, ob eine Taste gedrückt wurde.
// Jede gedrückte Taste wird nur einmal gemeldet
// =============================================
uint8_t get_key_press(uint8_t key_mask)
{
  cli();                        // Interrupts deaktivieren
  key_mask &= key_press;                // Tasten auslesen
  key_press ^= key_mask;                // Tasten löschen
  sei();                        // Interrupts aktivieren
  return key_mask;
}

// Erfolgt ein längerer Tastendruck, so wird nach einiger Zeit
// der Tastendruck automatisch wieder verworfen.
// ===========================================================
uint8_t get_key_rpt(uint8_t key_mask)
{
  cli();                        // Interrupts deaktivieren
  key_mask &= key_rpt;                // Tasten auslesen
  key_rpt ^= key_mask;                // Tasten löschen
  sei();                        // Interrupts aktivieren
  return key_mask;
}

// Kurzer Tastendruck
// ==================
uint8_t get_key_short(uint8_t key_mask)
{
  cli();                        // Interrupts deaktivieren
  return get_key_press(~key_state & key_mask);
}

// Langer Tastendruck
// ==================
uint8_t get_key_long(uint8_t key_mask)
{
  return get_key_press(get_key_rpt(key_mask));
}

int main(void)
{
  KEY_DDR &= ~ALL_KEYS;                // Tasten als Eingang definieren
  KEY_PORT |= ALL_KEYS;                // PullUp aktivieren

  TCCR0 = (1<<CS02)|(1<<CS00);            // Teilen bei 1024
  TIMSK = 1<<TOIE0;                  // TimerInterrupt aktivieren

  SEGMENT_DDR = 0xFF;                  // 7-Segment als Ausgang definieren
  OUT_DDR = 0xFF;                    // Tasten als Ausgang definieren
  OUT_PORT = 0x00;                  // Ausgänge auf LOW setzen

  nKeyPress = eeprom_read_byte(&eeFooByte);      // Aktuellen Wert aus EEPROM auslesen
  SEGMENT_PORT = pgm_read_byte(&Tabelle[nKeyPress]);  // und an 7-Segment übergeben
  Marker = nKeyPress;                  // EEPROM-Wert für Marker speichern
  sei();                        // Interrupts aktivieren

  while(1)
  {
    if (nKeyPress == Marker)
    {
      SEGMENT_PORT = pgm_read_byte(&Tabelle[nKeyPress]) & ~(1 << 7);
    }
    if(get_key_short(1<<KEY1))            // kurzer Tastendruck
    {
      if(nKeyPress < 8)              // Wurde Taste weniger als 8 mal gedrückt,
      {
        nKeyPress++;              // Wert erhöhen
      }
      else
      {
        nKeyPress = 0;              // ansonsten Wert zurücksetzen
      }
      SEGMENT_PORT = pgm_read_byte(&Tabelle[nKeyPress]);  // Wert an 7-Segment übergeben
    }
    if(get_key_long(1<<KEY1))            // langer Tastendruck
    {
      eeprom_write_byte(&eeFooByte, nKeyPress);  // Wert in EEPROM speichern
      Marker = nKeyPress;              // EEPROM-Wert für Marker speichern
    }
  }
}

Zur Funktion:

Der AVR soll als Kontroller für einen Arcade-Stick mit frei 
programmierbaren Tasterzuordnungen genutzt werden.

Dazu gibt es eine Taste mit der die Konfiguration ausgewählt werden 
kann( #define KEY1) und eine 7-Segment-Anzeige die die ausgewählte 
Konfiguration anzeigt.

Wobei hier gilt:
Kurzer tastendruck -> nächste Konfiguration
Langer tastendruck -> aktuellen Konfigurationswert ins EEPROM speichern.

Es fehlt nun nur noch die Funktion der 6 zusätzlichen tasten. Diese 
sollten ja nach eingestellter Konfiguration am Ausgang (PortC) 
durchgeschliffen werden.
Dabei sit allerdings zu beachten das jede dieser 6 tasten auch 
gleichzeitigt gedrückt werden können.

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hoffe das mir da jemand einen Tip geben kann.
Eine weitere Frage dazu vielleicht noch.

Ist der ATMEGA eigentlich schnell genug um für Spiele die tasten 1:1 
zeitgleich durchzuschalten? Oder kann es passieren das ich dann eine 
Verzögerung habe?

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

Bewertung
0 lesenswert
nicht lesenswert
Manfred W. schrieb:

> Der AVR soll als Kontroller für einen Arcade-Stick mit frei
> programmierbaren Tasterzuordnungen genutzt werden.
>
> Dazu gibt es eine Taste mit der die Konfiguration ausgewählt werden
> kann( #define KEY1) und eine 7-Segment-Anzeige die die ausgewählte
> Konfiguration anzeigt.
>
> Wobei hier gilt:
> Kurzer tastendruck -> nächste Konfiguration
> Langer tastendruck -> aktuellen Konfigurationswert ins EEPROM speichern.

Sinnlos.
Speichere die Konfigurationsnummer bei jedem Wechsel ab. So kann der 
Benutzer nicht darauf vergessen.
Wegen Haltbarkeit des EEPROM.
Die ist nur dann ein Problem, wenn dein Benutzer alle 10 Sekunden, rund 
um die Uhr, 7 Tage die Woche und ich glaube es waren so 2.5 Jahre die 
Konfiguration wechselt (habs jetzt nicht nachgerechnet).

Wenn dein Benutzer aber hauptsächlich spielt und nicht Konfiguration 
wechselt, dann hält das EEPROM länger als er in seinem ganzen Leben 
spielen wird.

> Es fehlt nun nur noch die Funktion der 6 zusätzlichen tasten. Diese
> sollten ja nach eingestellter Konfiguration am Ausgang (PortC)
> durchgeschliffen werden.
> Dabei sit allerdings zu beachten das jede dieser 6 tasten auch
> gleichzeitigt gedrückt werden können.

Die Frage ist: müssen die überhaupt entprellt werden?
Reich doch einfach die Signale von einem PIN-Eingang umkodiert auf einen 
anderen PORT Ausgang weiter.

> Ist der ATMEGA eigentlich schnell genug um für Spiele die tasten 1:1
> zeitgleich durchzuschalten? Oder kann es passieren das ich dann eine
> Verzögerung habe?

Verzögerung wirst du haben.
Die Frage ist allerdings, ob dein Benutzer eine Verzögerung von ein paar 
Millisekunden (wenns hoch kommt) überhaupt ohne Messgerät feststellen 
kann.

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Sinnlos.
> Speichere die Konfigurationsnummer bei jedem Wechsel ab. So kann der
> Benutzer nicht darauf vergessen.
> Wegen Haltbarkeit des EEPROM.
> Die ist nur dann ein Problem, wenn dein Benutzer alle 10 Sekunden, rund
> um die Uhr, 7 Tage die Woche und ich glaube es waren so 2.5 Jahre die
> Konfiguration wechselt (habs jetzt nicht nachgerechnet).

Worauf vergessen? Die richtige Konfiguration einzustellen?
Was ist falsch daran das ich die nur wen gewünscht abspeichere?

> Die Frage ist: müssen die überhaupt entprellt werden?
> Reich doch einfach die Signale von einem PIN-Eingang umkodiert auf einen
> anderen PORT Ausgang weiter.

Ich denke, das hier die Entprellung wegfallen kann.
Oder wäre es auch mit machbar?
Wichtig dabei ist jedoch das beim Prellen die anderen tasten nicht 
mitgerissen werden.
Dieses "weiterreichen" ist ja nicht nur ein durchschleifen.
Ich muß ja mit IF-Abfragen jede einzelne Tasten auf betätigung prüfen 
und dann auf den richtigen Port weiterleiten. Geht das nicht auf die 
Performance?
Oder wie mach ich das jetzt Richtig mit dem von dir vorgeschlagenen 
Array mit Bitmustern?

> Verzögerung wirst du haben.
> Die Frage ist allerdings, ob dein Benutzer eine Verzögerung von ein paar
> Millisekunden (wenns hoch kommt) überhaupt ohne Messgerät feststellen
> kann.

Sollange es den Spielfluss nicht beeinträchtigt ist es ziemlich egal.

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

Bewertung
0 lesenswert
nicht lesenswert
Manfred W. schrieb:
> Karl heinz Buchegger schrieb:
>
>> Sinnlos.
>> Speichere die Konfigurationsnummer bei jedem Wechsel ab. So kann der
>> Benutzer nicht darauf vergessen.
>
> Worauf vergessen? Die richtige Konfiguration einzustellen?
> Was ist falsch daran das ich die nur wen gewünscht abspeichere?

Praxistauglichkeit.
Dein Benutzer weiß nie, ob er schon gespeichert hat oder nicht.
Wenn er sich nicht darum kümmern muss, sondern beim Wiedereinschalten 
immer die zuletzt benutzte Konfiguration bekommt, ist das für den 
Benutzer am einfachsten. Das Gerät verhält sich dann so, als ob es nie 
ausgeschaltet worden wäre.

> Ich denke, das hier die Entprellung wegfallen kann.
> Oder wäre es auch mit machbar?

Das Gerät, welches dann hinter deinen Umkodierer angeschlossen wird, 
macht das ja sowieso. Ich würde genz einfach die Signale (entsprechend) 
umgestellt so wie sie vom Input-Port kommen, auf den Output-Port 
durchschleifen.

> Oder wie mach ich das jetzt Richtig mit dem von dir vorgeschlagenen
> Array mit Bitmustern?

Einfach umkodieren. Input einmal einlesen und für jedes interessante Bit 
im Input in der Muster Tabelle nachsehen, wo im Output ein 1 Bit hin 
muss.

   Summe = 0;
   Input = PINB;

   if( Input & 0x01 )
     Summe |= Pattern[actConfig][0];
   if( Input & 0x02 )
     Summe |= Pattern[actConfig][1];
   if( Input & 0x04 )
     Summe |= Pattern[actConfig][2];
   ...

   PORTC = Summe;

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Praxistauglichkeit.
> Dein Benutzer weiß nie, ob er schon gespeichert hat oder nicht.

Doch. Dafür ist der Marker gedacht.
Der gespeicherte Wert wird am 7-Segment mit dem Punkt angezeigt.

Wie müssen die Variablen:
NR_CONFIGS
NR_KEYS
Summe

deklariert werden?

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

Bewertung
0 lesenswert
nicht lesenswert
Manfred W. schrieb:
> Karl heinz Buchegger schrieb:
>
>> Praxistauglichkeit.
>> Dein Benutzer weiß nie, ob er schon gespeichert hat oder nicht.
>
> Doch. Dafür ist der Marker gedacht.
> Der gespeicherte Wert wird am 7-Segment mit dem Punkt angezeigt.
>
> Wie müssen die Variablen:
> NR_CONFIGS
> NR_KEYS
> Summe
>

#define NR_KONFIGS   5   // es werden maximal 5 Konfigurationen 
unterstützt
#define NR_KEYS      6   // du hast 6 Tastem, die du durchrouten willst

uint8_t Summe;

SChau dir doch einfach an, wie die Dinge verwendet werden, dann weißt du 
(meistens) auch, wie sie definiert werden müssen.

Autor: Lokus Pokus (derschatten)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Funktioniert fast perfekt!
Vielen dank.

Jetzt ist es nur so das die LED's mit LO geschalten werden und immer 
HIGH sind.
Wie dreh ich das um?
Also das ein Tastendruck die LEDs erst zu leuchten bringt und sie 
standardmäßig dunkel sind.

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

Bewertung
0 lesenswert
nicht lesenswert
Manfred W. schrieb:
> Funktioniert fast perfekt!
> Vielen dank.
>
> Jetzt ist es nur so das die LED's mit LO geschalten werden und immer
> HIGH sind.
> Wie dreh ich das um?


   PORTC = ~Summe;

Und kauf dir ein C-Buch!

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.