Forum: Mikrocontroller und Digitale Elektronik Problem mit einfacher 3x3 Schaltermatrix


von Joachim J (Gast)


Lesenswert?

Hallo zusammen,

ich möchte mir ein paar Port PINs sparen und 9 Schalter mit einer 3x3 
Matrix abfragen. Ich habe hier im Forum und im Web auch einige Beispiele 
gefunden und die Matrix entsprechend zusammengelötet. 1N4148 Dioden sind 
in Reihe zu den Schaltern gelötet, keine weiteren Widerstände. Wenn ich 
die Matrix ohne AVR (in diesem Fall ein Mega 8) mit LEDs teste, sieht 
alles richtig aus. Lasse ich den AVR die Matrix auslesen kommt nur 
Blödsinn bei rum.

Wie das Programm arbeitet:
Die 3 Eingänge haben PullUps aktiviert. Die Ausgänge sind anfangs alle 
1. Jetzt wird ein ein Ausgang auf 0 gesetzt. Alle Eingänge, die über 
Schalter mit dem Ausgang verbunden sind, sollen in der Theorie jetzt auf 
0 bzw. GND gezogen werden. In der Praxis bzw. als Ergebnis im Code 
siehts leider anders aus. Wenn ich parallel zu den Ein- und Ausgängen 
LEDs über ULN2803 anschließe, leuchten diese auch wieder richtig. Das 
Ergebnis mit angeschlossenen LEDs sieht auch wieder anders aus als ohne. 
In beiden Fällen aber falsch.

Was kann ich nochmal überprüfen um den Fehler zu finden?

Gruß
Joachim

von Mike (Gast)


Lesenswert?

Joachim J schrieb:
> Alle Eingänge, die über Schalter mit dem Ausgang verbunden sind, sollen
> in der Theorie jetzt auf 0 bzw. GND gezogen werden. In der Praxis bzw.
> als Ergebnis im Code siehts leider anders aus.

Dann ist entweder an der Theorie praktisch etwas falsch oder 
Schaltplan/Programm setzen das gewünschte theoretische Verhalten nicht 
richtig in die Praxis um.

Kurz: Zeige deinen Schaltplan und dein Programm

von Max H. (hartl192)


Lesenswert?

Hat es vllt. etwas mit dem Prellen der Tasten zu tun?

von Joachim J (Gast)


Angehängte Dateien:

Lesenswert?

Deinem Wunsch komme ich sofort nach :-)

Initialisierung der Schalter:
1
//Ausgänge für DIP Schalter Matrix
2
DDRD |=(1<<DDD2)|(1<<DDD3)|(1<<DDD4);        
3
//Eingänge für DIP Schalter Matrix
4
DDRD &=~((1<<DDD5)|(1<<DDD6)|(1<<DDD7));      
5
//Pull-Ups an Port D5 bis D7 aktivieren
6
PORTD |= (1<<PD5)|(1<<PD6)|(1<<PD7);        
7
//Ausgänge auf 1 setzen, Abfrage der Schalter ist invertiert
8
PORTD |= (1<<PD2)|(1<<PD3)|(1<<PD4);


Abfrage der Schalter:
1
PORTD |= ((1<<PD2)|(1<<PD3)|(1<<PD4));//Ausgänge auf High
2
3
//-------- ersten 3 bits --------
4
PORTD &= ~(1<<PD2); //Ausgang PD2, für die ersten 3 bits auf low
5
6
Pin_status = bit_is_set(PIND, 5) ? 0 : 1; 
7
temp_DMXadress|=(Pin_status<<0);
8
9
Pin_status = bit_is_set(PIND, 6) ? 0 : 1; 
10
temp_DMXadress|=(Pin_status<<1);
11
12
Pin_status = bit_is_set(PIND, 7) ? 0 : 1; 
13
temp_DMXadress|=(Pin_status<<2);
14
15
//-------- zweiten 3 bits --------
16
PORTD |= ((1<<PD2)|(1<<PD3)|(1<<PD4));//Ausgänge auf High
17
PORTD&=~(1<<PD3);
18
Pin_status = bit_is_set(PIND, 5) ? 0 : 1;
19
temp_DMXadress|=(Pin_status<<3);
20
21
Pin_status = bit_is_set(PIND, 6) ? 0 : 1;
22
temp_DMXadress|=(Pin_status<<4);
23
  
24
Pin_status = bit_is_set(PIND, 7) ? 0 : 1;
25
temp_DMXadress|=(Pin_status<<5);
26
27
28
//-------- dritten 3 bits, zu Testzwecken 9. weggelassen --------
29
PORTD |= ((1<<PD2)|(1<<PD3)|(1<<PD4));//Ausgänge auf High
30
PORTD&=~(1<<PD4);
31
32
Pin_status = bit_is_set(PIND, 5) ? 0 : 1;
33
temp_DMXadress|=(Pin_status<<6);
34
  
35
Pin_status = bit_is_set(PIND, 6) ? 0 : 1;
36
temp_DMXadress|=(Pin_status<<7);

Gruß
Joachim

von Joachim J (Gast)


Lesenswert?

Zum Thema prellen: Die Schalter "lege ich einmal um" und dann wars das. 
Prellen kann man hier vernachlässigen.

von Tlol (Gast)


Lesenswert?

Joachim J schrieb:
>
1
> //Ausgänge auf 1 setzen, Abfrage der Schalter ist invertiert
2
> PORTD |= (1<<PD2)|(1<<PD3)|(1<<PD4);
3
>

Kommentar erzeugt Endlosschleife bei mir.

Code ist nicht komplett.

von Joachim J (Gast)


Lesenswert?

Hallo,

was genau meinst du mit "Kommentar erzeugt Endlosschleife bei mir." ?

Der Code der Abfrage liegt in read_dips(), der Rest in der Main. Ich 
habe mir die Ergebnisse dann per RS232 ausgegeben.

Gruß
Joachim

von Peter D. (peda)


Lesenswert?

Joachim J schrieb:
> Pin_status = bit_is_set(PIND, 5) ? 0 : 1;
> temp_DMXadress|=(Pin_status<<0);
>
> Pin_status = bit_is_set(PIND, 6) ? 0 : 1;
> temp_DMXadress|=(Pin_status<<1);
>
> Pin_status = bit_is_set(PIND, 7) ? 0 : 1;
> temp_DMXadress|=(Pin_status<<2);

Das ist ja von hinten durch die Brust ins Auge.
Wie wärs einfach mit:
1
  temp_DMXadress |= 0x07 & (~PIND >> 5);

Und davor natürlich noch mindestens ein NOP, da die Eingänge im 
vorherigen Zyklus gelatcht werden.

: Bearbeitet durch User
von Joachim J (Gast)


Lesenswert?

Hallo Peter,

mit einer großzügingen Menge an _delay_ms gehts. :-) Danke!
Die Abfrage geschieht nur einmal und ist alles andere als zeitkritisch.

Kannst du mir vielleicht auch noch etwas näher erklären warum es da 
"klemmt" oder mir sagen wo ich das nachlesen kann? So tief bin ich noch 
nicht in AVR und C und das "latchen" ist mir als Fehler noch nicht 
untergekommen.

Gruß
Joachim

von Uwe (de0508)


Lesenswert?

Hallo Joachim,

das "latchen" steht im Datenblatt deines µC, nicht direkt zu finden, 
aber als Anmerkung ist es vorhanden.

Und bedeutet, etwas salopp geschrieben: eine Portänderung, z.B. PullUP 
"on", dauert länger, als bis zum Ende des Befehls.

: Bearbeitet durch User
von H.Joachim S. (crazyhorse)


Lesenswert?

9 Taster bekommt man auch an einem einzigen Analogeingang sauber 
ausgewertet.
Nachteil: man braucht auch 9 Widerstände.

von Elekto Nick (Gast)


Lesenswert?

Könntest Du erklären, wie das geht?
Danke

von oldmax (Gast)


Lesenswert?

Hi
Nun, es ist nicht selten der Fall, das die negative Logik durch die 
0-Abfrage bedingt durch den Pullup etwas Verwirrung bringt.Aus diesem 
Grund lege ich Eingänge immer in logischer Lage zum Schalter in 
Variablen ab. treu nach dem EVA-Prinzip. Einlesen - Verarbeiten - 
Ausgeben. Da ich C nicht beherrsche, versuche ich es einfach mal zu 
erläutern. Du selektierst Reihe 1 durch Ausgabe einer 0. Anschließend 
liest du deie Eingänge ein, drehst die Bits, maskierst die relevanten 
Bits aus und legst das Ergebnis in Variable Input_1 ab. In Asembler in 
etwas so:
1
Read_IO:
2
  In     r16, PortD
3
  ORI    r16, 0b00011100   ; Alle Reihen auf "1"
4
  ANDI   r16, 0b11111011   ; selektiere Reihe 1
5
  OUT    PortD, r16
6
  In     r16, PortD     ; Eingänge lesen
7
  COM    r16
8
  ANDI   r16, 0b11100000   ; Eingänge ausmaskieren
9
  ROR    r16
10
  SWAP   r16               ; Eingänge auf bits 0 bis 2 schieben
11
  STS    Input_1, r16      ; und in Variable Input_1 ablegen
12
  In     r16, PortD
13
  ORI    r16, 0b00011100   ; Alle Reihen auf "1"
14
  ANDI   r16, 0b11110111   ; selektiere Reihe 2
15
  OUT    PortD, r16
16
  In     r16, PortD     ; Eingänge lesen
17
  COM    r16
18
  ANDI   r16, 0b11100000   ; Eingänge ausmaskieren
19
  ROR    r16
20
  SWAP   r16               ; Eingänge auf bits 0 bis 2 schieben
21
  STS    Input_2, r16      ; und in Variable Input_2 ablegen
22
  In     r16, PortD
23
  ORI    r16, 0b00011100   ; Alle Reihen auf "1"
24
  ANDI   r16, 0b11101111  ; selektiere Reihe 3
25
  OUT    PortD, r16
26
  In     r16, PortD     ; Eingänge lesen
27
  COM    r16
28
  ANDI   r16, 0b11100000   ; Eingänge ausmaskieren
29
  ROR    r16
30
  SWAP   r16               ; Eingänge auf bits 0 bis 2 schieben
31
  STS    Input_3, r16      ; und in Variable Input_3 ablegen
32
RET

Sieht zwar gewaltig aus, ist es aber nicht. Ich habe nun das tganze 
Programm, um den Inhalt von Input 1-3 auszuwerten und genau nach der 
Logik: Ist ein Taster gedrückt, dann ist das Bit "1". Bei einer 
Taktfrequenz von 1 MHz braucht diese Routine rd. 30 µs. Noch eine 
Bemerkung zur Entprellung. Da in den Variablen Input_1 bis Input_3 die 
aktuellen Eingänge liegen, kannst du mit einem EOR (Exclusiv-Oder) und 
einem alten Wert feststellen, ob eine Änderung eingetreten ist, oder ob 
die Eingänge eine Zeitlang stabil waren
1
Debounce_IO:
2
  LDS   r16, Input_1    ; Akt. Eingänge
3
  LDS   r17, Old_In_1   ; Stand beim letzten Durchlauf
4
  EOR   r17, r16        ; Ergebnis in r17
5
  BREQ  Chk_Time        ; ist gleich,  Entprellzeit prüfen, evtl.reduzieren
6
  STS   Old_In_1, r16   ; aktuelle Eingänge ablegen
7
  LDS   r17, 50         ; Entprellzeit neu setzen
8
  STS   Deb_Time_1, r17 ; und merken
9
  RJMP  Row_2
10
  LDS   r17, Deb_Time_1 ; Restzeit laden
11
  CPI   r17, 0          ; schon abgelaufen?
12
  BREQ  Row_2
13
  DEC   r17             ; Zeit reduzieren
14
  STS   Deb_Time_1, r17 ; und merken
15
  BRNE  BREQ            ; Zeit nicht abgelaufen, dann weiter
16
  STS   Akt_In_1, r16   ; r16 hat noch den gültigen Wert der Eingänge
17
Row_2:                  ; nächste Reihe prüfen und entprellen
18
  ....
19
Row_3:
20
  ....
21
RET
Auch hierist genau berechenbar, wie lange die Entprellroutine bei einem 
Durchlauf braucht. Etwa 60 µs.
Die Bits in akt_In_x sind nun entprellte gültige Signale. Sicherlich 
wird es dir keine große Mühen machen, diese Programmteile in C 
nachzubauen.
Gruß oldmax

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.