mikrocontroller.net

Forum: Compiler & IDEs 3x4 Matrix Keypad


Autor: Richard B. (rbrose)
Datum:

Bewertung
0 lesenswert
nicht 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:
uint16_t ReadChannel(uint8_t mux)
{
  uint8_t i;
  uint16_t result;
 
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);    // Frequenzvorteiler 
                               // setzen auf 8 (1) und ADC aktivieren (1)
 
  ADMUX = mux;                      // Kanal waehlen
  ADMUX |= (1<<REFS0); // AVCC als Referenz
 
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
  while ( ADCSRA & (1<<ADSC) ) {
     ;     // auf Abschluss der Konvertierung warten 
  }
  result = ADCW;  // ADCW muss einmal gelesen werden,
                  // sonst wird Ergebnis der nächsten Wandlung
                  // nicht übernommen.
 
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
  result = 0; 
  for( i=0; i<4; i++ )
  {
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while ( ADCSRA & (1<<ADSC) ) {
      ;   // auf Abschluss der Konvertierung warten
    }
    result += ADCW;        // Wandlungsergebnisse aufaddieren
  }
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
 
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
 
  return result;
}



void long_delay(uint16_t ms) {
    for(; ms>0; ms--) _delay_ms(1);
}
 
int main(void) 
{ 
   DDRC = 0x00;  
   PORTC = 0xFF;
     
   DDRD = 0x00;
   PORTD = 0xFF;


  /*DDRB = 0xFF;
   PORTB = 0x00; */

  uint16_t adcval;
 

  sei();
  uart_puts_P("Sender laeuft !\n");

   while(1) 
   {                    
    adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -> Channel 0 */
    
  
    if(adcval > 228 && adcval < 232)
    {
      uart_puts("Taster 1\n");
    }

    if(adcval > 245 && adcval < 250)
    {
      uart_puts("Taster 4\n");
    }
    if(adcval > 261 && adcval < 266)
    {
      uart_puts("Taster 7\n");
    }
    if(adcval > 134 && adcval < 139)
    {
      uart_puts("Taster 2\n");
    }
    if(adcval > 155 && adcval < 160)
    {
      uart_puts("Taster 5\n");
    }
    if(adcval > 175 && adcval < 184)
    {
      uart_puts("Taster 8\n");
    }
    if(adcval > 195 && adcval < 205)
    {
      uart_puts("Taster 0\n");
    }
    if(adcval > 10 && adcval < 20)
    {
      uart_puts("Taster 3\n");
    }
    if(adcval > 40 && adcval < 50)
    {
      uart_puts("Taster 6\n");
    }
    if(adcval > 60 && adcval < 80)
    {
      uart_puts("Taster 9\n");
    }
     
     //receive();
     long_delay(100);
   }

 return 1;
}

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.

Autor: STK500-Besitzer (Gast)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (Gast)
Datum:
Angehängte Dateien:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (Gast)
Datum:

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

Autor: Richard B. (Gast)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (rbrose)
Datum:
Angehängte Dateien:

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

Autor: Richard B. (rbrose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
XMM1 ist die Leitung zum ADC und XMM2 ist die Leitung zum Pin.

Autor: Richard B. (rbrose)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (rbrose)
Datum:

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

Autor: Richard B. (rbrose)
Datum:
Angehängte Dateien:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (rbrose)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (rbrose)
Datum:

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

Autor: Richard B. (rbrose)
Datum:

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

Autor: Falk Brunner (falk)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

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

Autor: Richard B. (rbrose)
Datum:

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

Autor: Random ... (thorstendb) Benutzerseite
Datum:

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

Autor: Richard B. (rbrose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

Benutze jetzt einen AtMega168 und der Pin Change Interrupt funktioniert 
super.
HIer der COde:
ISR( SIG_PIN_CHANGE1 )
{
  PORTB ^= ( 1 << PB0 );
  DDRD |= (1<<PD2);
  PORTD &= ~(1<<PD2);
  adcval = ReadChannel(0);

  char buffer[7];
  itoa(adcval, buffer,10);
  uart_puts(buffer);

  DDRD &= ~(1<<PD2);
  PORTD &= ~(1<<PD2);
}


int main(void) 
{
  
...     
  PCICR |= (1<<PCIE1);  
    PCMSK1 |= (1<<PCINT8);
...
}
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

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Richard,

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



Gruß Stefan

Autor: Falk Brunner (falk)
Datum:

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

Autor: Richard B. (rbrose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso ich kann direkt auf PINx Zugreifen. Das ist super.Danke

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.