3x4 Matrix Keypad

#791382
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.
Gast #791391
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...
#791409
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
#791569
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
#792920
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
#793253
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?
Angehängte Dateien:
#793717
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
#793734
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?
#793748
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?
#793759
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?
#793768
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
#794023
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
#794162
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?
Persönliche Seite #794186
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.
#796393
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren