Forum: Mikrocontroller und Digitale Elektronik Verschiedene Tastenzuweisungen


von Lokus P. (derschatten)


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:
1
 while(1)
2
  {
3
    switch (nKeyPress)
4
    {
5
      case 0:
6
        if(!(PINB&0x01))
7
             OUT_PORT = 0x01;
8
        if(!(PINB&0x02))
9
             OUT_PORT = 0x02;
10
      case 1:
11
        if(!(PINB&0x01))
12
             OUT_PORT = 0x04;
13
        if(!(PINB&0x02))
14
             OUT_PORT = 0x01;
15
    }
16
....
Wie würdet ihr das machen?
Dazu kommt noch das auch mehrere Tasten gleichzeitig bedient werden 
können müssen.

von Hannes L. (hannes)


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.

...

von Lokus P. (derschatten)


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?
1
const unsigned char Tabelle[] PROGMEM =

von Karl H. (kbuchegg)


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?
>
>
1
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).
1
#define LED0  ( 1 << PC0 )
2
#define LED1  ( 1 << PC1 )
3
#define LED2  ( 1 << PC2 )
4
#define LED3  ( 1 << PC3 )
5
#define LED4  ( 1 << PC4 )
6
#define LED5  ( 1 << PC5 )
7
8
uint8_t Pattern[ NR_CONFIGS ][ NR_KEYS ] =
9
   { { LED0, LED1, LED2, LED3, LED4, LED5 },    // Konfiguration 0
10
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 1
11
     ....
12
   };
13
14
int main()
15
{
16
.....
17
18
19
  while( 1 ) {
20
    .....
21
22
    if( nKeyPress ) {
23
      // Toggle die LED der betätigten Taste
24
      PORTC = PORTC ^ Pattern[ actConfig ][ actKey ];
25
    }
26
27
  }
28
}

von Lokus P. (derschatten)


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?

von Karl H. (kbuchegg)


Lesenswert?

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

von Lokus P. (derschatten)


Lesenswert?

OK,
Ich poste mal den bisherigen kompletten Code:
1
// ************************************************************************
2
// *                                                                      *
3
// *                             Arcade Stick                             *
4
// *                         Tastenprogrammierung                         *
5
// *                                                                      *
6
// ************************************************************************
7
 
8
#include <avr/io.h>
9
#include <avr/eeprom.h>
10
#include <avr/interrupt.h>
11
#include <avr/pgmspace.h>
12
13
#ifndef F_CPU
14
#define F_CPU      1000000              // Processor Takt-Frequenz definieren
15
#warning kein F_CPU definiert
16
#endif
17
18
// Tasteneingänge definieren
19
// =========================
20
#define KEY_DDR         DDRB
21
#define KEY_PORT        PORTB
22
#define KEY_PIN         PINB
23
#define KEY1            6
24
#define KEY2            0
25
#define KEY3            1
26
#define KEY4            2
27
#define KEY5            3
28
#define KEY6            4
29
#define KEY7            5
30
#define ALL_KEYS        (1<<KEY1 | 1<<KEY2 | 1<<KEY3 | 1<<KEY4 | 1<<KEY5 | 1<<KEY6 | 1<<KEY7)
31
32
// Tastendrückwiederholungsdauer definieren
33
// ========================================
34
#define REPEAT_MASK     (1<<KEY1)
35
#define REPEAT_START    50                           // nach 500ms
36
#define REPEAT_NEXT     20                          // alle 200ms
37
38
// Segment und Tasten-Ausgänge definieren
39
// ======================================
40
#define SEGMENT_DDR    DDRD
41
#define SEGMENT_PORT  PORTD
42
#define OUT_DDR         DDRC
43
#define OUT_PORT        PORTC
44
#define OUT1            0
45
#define OUT2            1
46
#define OUT3            2
47
#define OUT4            3
48
#define OUT5            4
49
#define OUT6            5
50
51
#define LED0  (1 << PC0)
52
#define LED1  (1 << PC1)
53
#define LED2  (1 << PC2)
54
#define LED3  (1 << PC3)
55
#define LED4  (1 << PC4)
56
#define LED5  (1 << PC5)
57
58
uint8_t Pattern[NR_CONFIGS][NR_KEYS] =
59
   { { LED0, LED1, LED2, LED3, LED4, LED5 },    // Konfiguration 0
60
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 1
61
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 2
62
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 3
63
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 4
64
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 5
65
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 6
66
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 7
67
     { LED0, LED2, LED4, LED1, LED3, LED5 },    // Konfiguration 8
68
   };
69
70
volatile uint8_t key_state;                // Entprellt und invertierte Tastenstatus:
71
                            // Bit=1 -> Taste wurde gedrückt
72
volatile uint8_t key_press;                // Tastendruck registriert
73
volatile uint8_t key_rpt;                // Tastendruckdauer
74
volatile uint8_t nKeyPress;                // Tastendruckanzahl
75
volatile uint8_t Marker;                // Speichermarkierung im 7-Segment
76
77
uint8_t eeFooByte;                    // EEPROM Variable
78
79
// Zeichentabelle für 7-Segment
80
// ============================
81
const unsigned char Tabelle[] PROGMEM = {249, 164, 176, 153, 146, 130, 248, 128, 144};
82
83
// Timer Interrupt von 10ms definieren
84
// ===================================
85
ISR(TIMER0_OVF_vect)
86
{
87
  static uint8_t ct0, ct1, rpt;
88
  uint8_t i;
89
90
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
91
92
  i = key_state ^ ~KEY_PIN;              // Taste geändert?
93
  ct0 = ~(ct0 & i);                  // resete oder zähle CT0
94
  ct1 = ct0 ^ (ct1 & i);                // resete oder zähle CT1
95
  i &= ct0 & ct1;                    // Überlauf gezählt?
96
  key_state ^= i;                    // dann Entprellstatus ändern
97
  key_press |= key_state & i;              // 0->1: Tastendruck erkannt
98
99
  if((key_state & REPEAT_MASK) == 0)          // Überprüfe Tastenwiederholfunktion
100
    rpt = REPEAT_START;                // Starte Verzögerung
101
  if(--rpt == 0)
102
  {
103
    rpt = REPEAT_NEXT;                // Wiederhole Verzögerung
104
    key_rpt |= key_state & REPEAT_MASK;
105
  }
106
}
107
108
// Überprüfen, ob eine Taste gedrückt wurde.
109
// Jede gedrückte Taste wird nur einmal gemeldet
110
// =============================================
111
uint8_t get_key_press(uint8_t key_mask)
112
{
113
  cli();                        // Interrupts deaktivieren
114
  key_mask &= key_press;                // Tasten auslesen
115
  key_press ^= key_mask;                // Tasten löschen
116
  sei();                        // Interrupts aktivieren
117
  return key_mask;
118
}
119
120
// Erfolgt ein längerer Tastendruck, so wird nach einiger Zeit
121
// der Tastendruck automatisch wieder verworfen.
122
// ===========================================================
123
uint8_t get_key_rpt(uint8_t key_mask)
124
{
125
  cli();                        // Interrupts deaktivieren
126
  key_mask &= key_rpt;                // Tasten auslesen
127
  key_rpt ^= key_mask;                // Tasten löschen
128
  sei();                        // Interrupts aktivieren
129
  return key_mask;
130
}
131
132
// Kurzer Tastendruck
133
// ==================
134
uint8_t get_key_short(uint8_t key_mask)
135
{
136
  cli();                        // Interrupts deaktivieren
137
  return get_key_press(~key_state & key_mask);
138
}
139
140
// Langer Tastendruck
141
// ==================
142
uint8_t get_key_long(uint8_t key_mask)
143
{
144
  return get_key_press(get_key_rpt(key_mask));
145
}
146
147
int main(void)
148
{
149
  KEY_DDR &= ~ALL_KEYS;                // Tasten als Eingang definieren
150
  KEY_PORT |= ALL_KEYS;                // PullUp aktivieren
151
152
  TCCR0 = (1<<CS02)|(1<<CS00);            // Teilen bei 1024
153
  TIMSK = 1<<TOIE0;                  // TimerInterrupt aktivieren
154
155
  SEGMENT_DDR = 0xFF;                  // 7-Segment als Ausgang definieren
156
  OUT_DDR = 0xFF;                    // Tasten als Ausgang definieren
157
  OUT_PORT = 0x00;                  // Ausgänge auf LOW setzen
158
159
  nKeyPress = eeprom_read_byte(&eeFooByte);      // Aktuellen Wert aus EEPROM auslesen
160
  SEGMENT_PORT = pgm_read_byte(&Tabelle[nKeyPress]);  // und an 7-Segment übergeben
161
  Marker = nKeyPress;                  // EEPROM-Wert für Marker speichern
162
  sei();                        // Interrupts aktivieren
163
164
  while(1)
165
  {
166
    if (nKeyPress == Marker)
167
    {
168
      SEGMENT_PORT = pgm_read_byte(&Tabelle[nKeyPress]) & ~(1 << 7);
169
    }
170
    if(get_key_short(1<<KEY1))            // kurzer Tastendruck
171
    {
172
      if(nKeyPress < 8)              // Wurde Taste weniger als 8 mal gedrückt,
173
      {
174
        nKeyPress++;              // Wert erhöhen
175
      }
176
      else
177
      {
178
        nKeyPress = 0;              // ansonsten Wert zurücksetzen
179
      }
180
      SEGMENT_PORT = pgm_read_byte(&Tabelle[nKeyPress]);  // Wert an 7-Segment übergeben
181
    }
182
    if(get_key_long(1<<KEY1))            // langer Tastendruck
183
    {
184
      eeprom_write_byte(&eeFooByte, nKeyPress);  // Wert in EEPROM speichern
185
      Marker = nKeyPress;              // EEPROM-Wert für Marker speichern
186
    }
187
  }
188
}

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.

von Lokus P. (derschatten)


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?

von Karl H. (kbuchegg)


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.

von Lokus P. (derschatten)


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.

von Karl H. (kbuchegg)


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;

von Lokus P. (derschatten)


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?

von Karl H. (kbuchegg)


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.

von Lokus P. (derschatten)


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.

von Karl H. (kbuchegg)


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!

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.