Forum: Compiler & IDEs 3x4 Matrix Keypad


von Richard B. (rbrose)


Lesenswert?

Hallo Leute,

ich habe mein Keypad mit Widerständen zusammengefasst und lese das 
Keypad dann mit einem ADC aus. Es klappt auch gut schon.
Hier der Code:
1
uint16_t ReadChannel(uint8_t mux)
2
{
3
  uint8_t i;
4
  uint16_t result;
5
 
6
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);    // Frequenzvorteiler 
7
                               // setzen auf 8 (1) und ADC aktivieren (1)
8
 
9
  ADMUX = mux;                      // Kanal waehlen
10
  ADMUX |= (1<<REFS0); // AVCC als Referenz
11
 
12
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
13
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
14
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
15
  while ( ADCSRA & (1<<ADSC) ) {
16
     ;     // auf Abschluss der Konvertierung warten 
17
  }
18
  result = ADCW;  // ADCW muss einmal gelesen werden,
19
                  // sonst wird Ergebnis der nächsten Wandlung
20
                  // nicht übernommen.
21
 
22
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
23
  result = 0; 
24
  for( i=0; i<4; i++ )
25
  {
26
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
27
    while ( ADCSRA & (1<<ADSC) ) {
28
      ;   // auf Abschluss der Konvertierung warten
29
    }
30
    result += ADCW;        // Wandlungsergebnisse aufaddieren
31
  }
32
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
33
 
34
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
35
 
36
  return result;
37
}
38
39
40
41
void long_delay(uint16_t ms) {
42
    for(; ms>0; ms--) _delay_ms(1);
43
}
44
 
45
int main(void) 
46
{ 
47
   DDRC = 0x00;  
48
   PORTC = 0xFF;
49
     
50
   DDRD = 0x00;
51
   PORTD = 0xFF;
52
53
54
  /*DDRB = 0xFF;
55
   PORTB = 0x00; */
56
57
  uint16_t adcval;
58
 
59
60
  sei();
61
  uart_puts_P("Sender laeuft !\n");
62
63
   while(1) 
64
   {                    
65
    adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -> Channel 0 */
66
    
67
  
68
    if(adcval > 228 && adcval < 232)
69
    {
70
      uart_puts("Taster 1\n");
71
    }
72
73
    if(adcval > 245 && adcval < 250)
74
    {
75
      uart_puts("Taster 4\n");
76
    }
77
    if(adcval > 261 && adcval < 266)
78
    {
79
      uart_puts("Taster 7\n");
80
    }
81
    if(adcval > 134 && adcval < 139)
82
    {
83
      uart_puts("Taster 2\n");
84
    }
85
    if(adcval > 155 && adcval < 160)
86
    {
87
      uart_puts("Taster 5\n");
88
    }
89
    if(adcval > 175 && adcval < 184)
90
    {
91
      uart_puts("Taster 8\n");
92
    }
93
    if(adcval > 195 && adcval < 205)
94
    {
95
      uart_puts("Taster 0\n");
96
    }
97
    if(adcval > 10 && adcval < 20)
98
    {
99
      uart_puts("Taster 3\n");
100
    }
101
    if(adcval > 40 && adcval < 50)
102
    {
103
      uart_puts("Taster 6\n");
104
    }
105
    if(adcval > 60 && adcval < 80)
106
    {
107
      uart_puts("Taster 9\n");
108
    }
109
     
110
     //receive();
111
     long_delay(100);
112
   }
113
114
 return 1;
115
}

Das heißt er wird genau erkannt welche Taster gedrückt wurde.
Das Problem ist aber ... das wenn ich die Taster etwas zu lange halte er 
ja es paar mal ausführt.
Wie mache ich das wenn eine Taster gedrückt wird, sie nur einmal 
gedrückt werden kann .. .bis Sie losgelassen worden ist und nochmal 
gedrückt?

Und zweite Frage: Es soll eine Fernbedingung werden, ist das OK wenn der 
ADC so oft Messen tut? Wird der ADC nicht müde? ;-) Der muss ja ganze 
Zeit Messen weil er sonst den Tasterdruck nicht merken würde ... oder 
macht man das anders?


Vielen Dank schon mal für eure Ideen und Meinungen.

von STK500-Besitzer (Gast)


Lesenswert?

>Wie mache ich das wenn eine Taster gedrückt wird, sie nur einmal
>gedrückt werden kann .. .bis Sie losgelassen worden ist und nochmal
>gedrückt?

Merk dir in einer Variable, ob ein Taster schon gedrückt wurde und 
vergleich das auch noch.

Beispielsweise so:
uint8 Taste = 0;
   while(1)
   {
    adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -> Channel 0 */


    if(adcval > 228 && adcval < 232)
    {
      if (Taste != 1)
      {
       uart_puts("Taster 1\n");
       Taste = 1;
      }
    }

    if(adcval > 245 && adcval < 250)
    {
      if (Taste != 4)
      {
       uart_puts("Taster 4\n");
       Taste = 4;
      }
    }
//usw...
}

Die Version sorgt dafür, dass man erst eine andere Taste drücken muß, 
bevor man diese erneut drücken darf.
Man muß also noch erkennen, ob keine Taste gedrückt wird. Dann muß Taste 
= 0 gesetzt werden.

>Und zweite Frage: Es soll eine Fernbedingung werden, ist das OK wenn der
>ADC so oft Messen tut? Wird der ADC nicht müde? ;-) Der muss ja ganze
>Zeit Messen weil er sonst den Tasterdruck nicht merken würde ... oder
>macht man das anders?

Die Arbeitszeit von ADC regelt die ADC-Gewerkschaft...
Wenn dein ADC nicht in der Gewerkschaft ist (ein Trittbrettfahrer 
also...), dann muß er so lange arbeiten, wie der Arbeitsgeber es 
wünscht.
Chinesische ADC darf man bei Arbeitsverweigerung auch erschiessen...

von Peter D. (peda)


Lesenswert?

Richard B. wrote:
> Wie mache ich das wenn eine Taster gedrückt wird, sie nur einmal
> gedrückt werden kann .. .bis Sie losgelassen worden ist und nochmal
> gedrückt?

Beitrag "Tastenmatrix auslesen über nur 2 Leitungen"


> Und zweite Frage: Es soll eine Fernbedingung werden, ist das OK wenn der
> ADC so oft Messen tut?

Nen hochohmigen Widerstand an die Tastatur legen und dann mit dem 
Pinchangeinterrupt den MC aufwecken, sonst ist schnell die Batterie 
leer.


Peter

von Richard B. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Peter,

das ist eine gute Idee. Aber wie meinst du es genau? Ich benutze die 
Schaltung hier von dir(Im Anhang).

Wo soll ich den die Leitung Abzapfen für den Int Eingang?

von Peter D. (peda)


Lesenswert?

Also im worst case sind es 11k, wenn eine Taste gedrückt wird. Nun 
schaltest Du 100k gegen VCC und bei ner gedrückten Taste erkennt der PCI 
low. dann aktivierst Du den 10k und mißt genau nach, welche Taste es 
ist.
Du brauchst also noch nen extra Pin für denn 10k, der im Ruhezustand 
Tristate ist.


Peter

von Richard B. (Gast)


Lesenswert?

Mhh kannst mir nicht richtig vorstellen. kannst du die Schaltung kurz 
erweitern und hochladen? Wäre sehr Dankbar.

Sind 1 Mohm auch in Orndung.

von Richard B. (Gast)


Lesenswert?

Wie soll ich den 10K Widerstand aktivieren? Soll ich den per Relaise 
schalten?
Irgendwie verstehe ich es nicht ganz wie Peter es meint.

von Peter D. (peda)


Lesenswert?

Richard B. wrote:
> Wie soll ich den 10K Widerstand aktivieren? Soll ich den per Relaise
> schalten?

Nein, der kommt an einen Pin.
Zum Abfragen wird er Ausgang high gesetzt, sonst Eingang low (also 
tristate).


Peter

von Richard B. (rbrose)


Angehängte Dateien:

Lesenswert?

Hallo,

etwa so?
Das heißt, der Pin ist dann immer High geschaltet im AVR und wenn er 
lowgeschaltet wird durch Drücken einer Taste ... dann Schalte ich in Low 
und Messe mit dem ADC den Wert. Richtig?

von Richard B. (rbrose)


Lesenswert?

XMM1 ist die Leitung zum ADC und XMM2 ist die Leitung zum Pin.

von Richard B. (rbrose)


Lesenswert?

Es funktioniert nicht so. Kann es mir jemand genauer erklären? 
Vielleicht mit einer Zeichnung? Wäre sehr dankbar. Komme leider nicht 
weiter ... hab schon ganzen Mittag & Nachmittag probiert :-(

von Peter D. (peda)


Lesenswert?

Richard B. wrote:
> Hallo,
>
> etwa so?
> Das heißt, der Pin ist dann immer High geschaltet im AVR und wenn er
> lowgeschaltet wird durch Drücken einer Taste ... dann Schalte ich in Low
> und Messe mit dem ADC den Wert. Richtig?

Nein, der R6 kommt gegen nen IO-Pin, der im Sleep Eingang ist und von 
dort ein 100k Pullup. Zum Messen setzt man den Pin auf High.
Der ADC-Eingang ist im Sleep auf Pin-change gesetzt zum Aufwachen.


Peter

von Richard B. (rbrose)


Lesenswert?

Hallo Peter,

R6 in deiner Zeichnung (PDF) oder in der kyxpad.jpg?
"Sleep Eingang" heist low?

> Der ADC-Eingang ist im Sleep auf Pin-change gesetzt zum Aufwachen.
Was meinst du mit "Sleep"? Das war mit bei Pins noch nicht wirklich 
bekannt.

von Richard B. (rbrose)


Angehängte Dateien:

Lesenswert?

So, irgendwie komme ich hier nicht wirklich weiter. Wieso tun wir uns so 
schwer es bisschen einfacher zu erklären damit es auch ein Anfänger 
verstehen kann?


Ich habe hier mal eine Zeichnung gemacht mit der ich hoffe wir jetzt 
schneller zum Zeil kommen.

Pin PD0 ist an 10K angeschlossen. PD0 ist low. Wenn einer Taste gedrückt 
wird ... wird PD0 High und ich weiss das eine Taste gerückt ist ... dann 
Schalte ich PD0 High und Messe mit PB4 per ADC den Wert.
Ist es so richtig?


Mhh irgendwie scheint es nicht richtig zu sein. ist PD0 so nicht immer 
HIGH?
Oder muss PB4 auch low sein ... damit PD0 High wird?

von Peter D. (peda)


Lesenswert?

Richard B. wrote:
> Pin PD0 ist an 10K angeschlossen. PD0 ist low.

PD0 ist niemals low, es ist entweder hochohmig (Eingang) oder high 
(Ausgang)


> Wenn einer Taste gedrückt
> wird ... wird PD0 High und ich weiss das eine Taste gerückt ist

Nö, dann ist PB4 low.

> ... dann
> Schalte ich PD0 High und Messe mit PB4 per ADC den Wert.
> Ist es so richtig?

Ja.


Peter

von Richard B. (rbrose)


Lesenswert?

Guten Nabend Peter,

also ist meine Schaltung so richtig? Du hast es nicht beantwortet :-(

Wie lese ich beim Eingang(PD0) den Zustand?
Das heißt wenn der Eingang(PD0)(High geschaltet ist 5V) und eine Taste 
gedrückt wird ... dann ist PB4 low und beim Eingang(PD0) kann ich ein 
welchsel von High auf Low messen, dann weiss ich das eine Taste gedrückt 
wird. Richtig?

von Peter D. (peda)


Lesenswert?

Du willst doch Strom sparen (Batteriebetrieb)?

Dann setzt Du die CPU in sleep, wenn ne Weile lang keine Taste gedrückt 
erkannt wurde.
Vorher  aber den ADC abschalten, Pinchange Interrupt auf PB4 einschalten 
und PD0 auf Eingang.

Und sobald ne Taste gedrückt wird, wacht die CPU auf (Interrupthandler 
nicht vergessen!).


Peter

P.S.:
PD0 läßt vermuten, Du nimmst keinen ATtiny13 sondern nen dicken AVR. 
Warum dann das Auslesen der Matrix per ADC?

von Richard B. (rbrose)


Lesenswert?

Ich verwende einen ATMega8. Hab eine 3x4 Tastatur dran, einen RFM12 und 
3x7 Segment Anzeige. Und 2 einfache Taster.
Langsam bin ich auch der Meinung die Tastatur komplett an AVR 
anschliessen.

Wenn ich die Tastatur komplett anschliesse kann ich einfach im Timer 
jede 10ms testen ob Taste gedrückt oder mache ich das auch per 
Pinchange?

von Richard B. (rbrose)


Lesenswert?

MMhh jetzt habe ich das kapiert!! danke.
Das Problem ist, das bei Tastendruck 4,7,8,*,0 wird nicht erkannt das es 
gedrückt wurde. Warscheinlicht ist die Spannung zu hoch um es noch als 
low zu registrieren.
Also geht die Methode nicht wirklich ... oder man hofft auch glück das 
die anderen Tasten zuerst gedrück werden :-)

AChja R5 und R6 sind bei mir 4.7K

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>AChja R5 und R6 sind bei mir 4.7K

Ist nicht wirklich clever. Die ganze Sache ist schon etwas empfindlich 
was die Widerstände angeht. Das sollte man 1% Metallschicht nehmen, mit 
den angegebenen Werten.

MfG
Falk

von Peter D. (peda)


Lesenswert?

Richard B. wrote:

> Also geht die Methode nicht wirklich ... oder man hofft auch glück das
> die anderen Tasten zuerst gedrück werden :-)

Hat überhaupt nichts mit Glück zu tun.
110k zu 12,4k reicht dicke aus, um sicher low zu erkennen.
Warscheinlich hast Du die internen Pullups mit angeschaltet.


> AChja R5 und R6 sind bei mir 4.7K

Kein Problem, wenn Du wie ich die Schwellen vom Präprozessor automatisch 
berechnen läßt.
In Deinem Codeschnipsel hast Du allerdings feste Werte eingetragen. Ob 
die stimmmen, weiß ich nicht.


Peter

von Richard B. (rbrose)


Lesenswert?

@Peter
Habe eben nach gelesen das der AtMega8 keine Pin Change 
Interrupts(PCINT) hat.
Somit kann ich das mit dem PCINT vergessen :-( Da ich dafür nur INT0 und 
INT1 benutzen kann, die aber nicht ADC können.

Hab aber einen AtMega168-20DIP hier noch liegen. Der soll auch weniger 
Strom verbrauchen im Gegensatz zum AtMega8.
Ich werde den mal ausprobieren.

Kann ich das Programm von Atmega8 1zu1 übernehmen ... oder gibt es da 
gravierende unterschiede zwischen atmega8 und atmega168?

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Zum Keyboard Scannen:

Hab mal für eine Tastatur, die zur Abfrage gescannt wurde, alle zeilen 
per DiodenODER auf High gelegt (hatte noch einen Ausgangspin am MUX 
frei).

In Verbindung mit dem Auslesen der Datenleitung kann man so einen 
kbhit() realisieren, und startet erst bei positiver Rückmeldung die 
KeyScan funktion. Spart Rechenleistung, wenn man eine einfache Lösung 
ohne Interrupt sucht.

Wobei ... etwas weiter gedacht könnte man die Datenleitungen ver-odern 
und mit dem Signal nen INT eingang speisen :-)

VG,
/r.

von Richard B. (rbrose)


Lesenswert?

Hallo zusammen,

Benutze jetzt einen AtMega168 und der Pin Change Interrupt funktioniert 
super.
HIer der COde:
1
ISR( SIG_PIN_CHANGE1 )
2
{
3
  PORTB ^= ( 1 << PB0 );
4
  DDRD |= (1<<PD2);
5
  PORTD &= ~(1<<PD2);
6
  adcval = ReadChannel(0);
7
8
  char buffer[7];
9
  itoa(adcval, buffer,10);
10
  uart_puts(buffer);
11
12
  DDRD &= ~(1<<PD2);
13
  PORTD &= ~(1<<PD2);
14
}
15
16
17
int main(void) 
18
{
19
  
20
...     
21
  PCICR |= (1<<PCIE1);  
22
    PCMSK1 |= (1<<PCINT8);
23
...
24
}
Nur wird er bei steigernder und fallender Flanke ausgeführt.
Wie kann ich in nur bei steigernder Flanke ausführen?
Ich weiss es geht nicht Hardware mässig, also muss ich in dem Interrupt 
eine if abfrage einbauen ... aber auf was kann ich zugreifen um zu 
überprüfen ob der Auslöser jetzt eine steigernde oder fallende Flanke 
war?

Danke

von Stefan (Gast)


Lesenswert?

Hallo Richard,

Einfach Pin Prüfen, ist er High wars ne steigende Flanke, und ist er 
low, wars ne fallende...



Gruß Stefan

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>eine if abfrage einbauen ... aber auf was kann ich zugreifen um zu
>überprüfen ob der Auslöser jetzt eine steigernde oder fallende Flanke
>war?

Die Bits in PINx. Sind sie 1 wars ne steigende Flanke . . .

MFG
Falk

von Richard B. (rbrose)


Lesenswert?

Achso ich kann direkt auf PINx Zugreifen. Das ist super.Danke

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.