Forum: Mikrocontroller und Digitale Elektronik Tastendrücke und Portbits


von Jakob M. (Firma: Student) (jakkob)


Lesenswert?

Hallo,

ich weiss selber nicht so ganz genau, was ich von dieser Frage halten 
soll. Auf jeden Fall aber hat Sie mich gestern mind. 4 Std. Fehlersuche 
gekostet unbd ich steh halt immer noch genauso dumm da wie vorher??
Kann mir also bitte Jemand das Mysteruim dieser Funktion erklären, die 
ein zwölftastites Tastenfeld (Matrix) ausließt, indem man immer eine 
Reihe auf Null zieht und dann schaut ob in irgendeiner  Spalte 
irgendetwas auf Null gezogen ist. Aus dem "Schnittpunkt" ergibt sich 
halt die Taste, wenn denn eine gedrückt wurde. Das funzt soweit auch 
vorzüglich, nur wenn ich jetzt die Bits in  PORTB einzeln setze, wie in 
den Kommentaren angemerkt, dann gehts gar nicht mehr. Was dumm ist, denn 
das Tastenfeld braucht nur sieben Pins und ich würde halt gerne den 
achten (PB7) auch noch verwenden, was halt nicht geht, wenn ich den PORT 
gesamt setze.
Ich weiss nicht ob man für diese Frage noch mehr Infos braucht, aber 
Fakt ist halt dass, es in der "PORT="-Version geht und in der 
"PORTB|="-Version nicht...(im Prgramm benutze ich selbstverständlich 
immer nur eine Version des ganzen).
Ach ja, und entprellt ist das ganze auch, aber als komplette Funktion im 
Stile von: Funktion()...warte soundsolange... Funktion()

vielen Dank für eure Hilfe
ich bin mit meinem Latein am Ende
Jakob


[c]
//init von PORTB:
//PORTB=0b11111111;
//DDRB= 0b11111000;

char scan_taste() {

  uint8_t C =0;

  /* PORTB setzen einzeln */
  PORTB |= (1<<PB6) | (1<<PB5) | (1<<PB4) | (1<<PB2) | (1<<PB1) | 
(1<<PB0);
  PORTB &= ~(1<<PB3) &~(1<<PB7);

  /* PORTB setzen gesamt */
  PORTB=0b01110111;
              //PIND0-2= Pullup_aktiv
              //PIND3= log.0
              //PIND4-6= log.1
  C = PINB | 0b11111000;  //um die drei letzten bits zu vergl.

  switch(C) {
    case 0b11111111 : break;   //nix in R0 gedrückt, nix Passiert
    case 0b11111011 : return 10;
    case 0b11111101 : return 11;
    case 0b11111110 : return 12;
  }

  /* PORTB setzen einzeln */

  PORTB |= (1<<PB6) | (1<<PB3) | (1<<PB5) | (1<<PB2) | (1<<PB1) | 
(1<<PB0);
  PORTB &= ~(1<<PB4) &~(1<<PB7);

  /* PORTB setzen gesamt */
  PORTB=0b01101111;     //PIND4= log.1 --->R1=5V, andere Rs=0V
              //PIND5-6 und PIND3= log.0


  C= PINB | 0b11111000;

  switch(C) {
    case 0b11111111 : break;     //nix gedrückt in R1, nix Passiert
    case 0b11111011 : return 1;
    case 0b11111101 : return 2;
    case 0b11111110 : return 3;
  }

  /* PORTB setzen einzeln */

  PORTB |= (1<<PB6) | (1<<PB3) | (1<<PB4) | (1<<PB2) | (1<<PB1) | 
(1<<PB0);
  PORTB &= ~(1<<PB5) &~(1<<PB7);

  /* PORTB setzen gesamt */
  PORTB=0b01011111;

                  //PIND5= log.1 --->R2=5V, andere Rs=0V
                  //PIND3-4 und PIND6= log.0
  C= PINB | 0b11111000;

  switch(C) {
    case 0b11111111 : break;   //nix gedrückt in R2, nix Passiert
    case 0b11111011 : return 4;
    case 0b11111101 : return 5;
    case 0b11111110 : return 6;
  }

  /* PORTB setzen einzeln */
  PORTB |= (1<<PB3) | (1<<PB5) | (1<<PB4) | (1<<PB2) | (1<<PB1) | 
(1<<PB0);
  PORTB &= ~(1<<PB6) &~(1<<PB7);


  /* PORTB setzen gesamt */
  PORTB=0b00111111;

                  //PIND6= log.1 --->R3=5V, andere Rs=0V
                  //PIND3-5= log.0
  C= PINB | 0b11111000;

  switch(C) {
    case 0b11111111 : break ; //nix gedrückt in R3, nix Passiert
    case 0b11111011 : return 7;
    case 0b11111101 : return 8;
    case 0b11111110 : return 9;
  }
  return 0;
}
[c]

von Falk B. (falk)


Lesenswert?

@ Jakob M. (Firma Student) (jakkob)

>vorzüglich, nur wenn ich jetzt die Bits in  PORTB einzeln setze, wie in
>den Kommentaren angemerkt, dann gehts gar nicht mehr. Was dumm ist, denn

Hehe, böser Fehler. ;-) Dass es überhaupt geht ist schon fast ein 
Wunder, liegt möglicherweise am mehrfachaufruf der Funktion. Denn!!! 
zwischen der Ausgabe und dem Einlesen MUSS mindestens ein Takt Pause 
sein. Der AVR ist ein RSIC Prozessor mit einstufiger Pipeline. Das ist 
aber schon ein Hardcore Insiedertrick.
1
  /* PORTB setzen einzeln */
2
  PORTB |= (1<<PB6) | (1<<PB5) | (1<<PB4) | (1<<PB2) | (1<<PB1) |
3
  (1<<PB0);
4
  PORTB &= ~(1<<PB3) &~(1<<PB7);
5
  
6
  // hier MUSS mindestens ein ASM Befehl rein, ala _NOP();
7
8
  C = PINB | 0b11111000;  //um die drei letzten bits zu vergl.

Ausserem sollte man das Ganze mit ne Schleife programmieren, ist 
eleganter und kürzer.

MFG
Falk

von Stefan B. (stefan) Benutzerseite


Lesenswert?

@ Jakob

Aus dem

  // Problemcode #1
  PORTB |= (1<<PB6) | (1<<PB5) | (1<<PB4) | (1<<PB2) | (1<<PB1) | 
(1<<PB0);
  PORTB &= ~(1<<PB3) &~(1<<PB7);

  // Funktionsfähigem Code
  PORTB=0b01011111;

und der Frage

> denn das Tastenfeld braucht nur sieben Pins und ich würde halt
> gerne den achten (PB7) auch noch verwenden, was halt nicht geht,
> wenn ich den PORT gesamt setze.

Würde ich den Code #1 so abändern (und an den anderes zwei Stellen 
entsprechend):

  PORTB = 0b01011111 | (PORTB & (1<<PB7));

Dadurch lässt du PB7 von PORTB in dieser Routine scan_taste() 
wunschgemäß unverändert.

Zur Sicherheit muss man hier den erzeugten Assemblercode und das 
Restprogramm ansehen:

Kann es passieren, dass sich PORTB.7 zwischen Auslesen (PORTB), 
Maskieren mit (& (1<<PB7)) und Setzen (=) ändert?

Eine solche Änderung könnte z.B. durch eine aktive Interruptroutine 
erfolgen. D.h. ggf. sind für diese Zeile eventuell freigegebene 
Interrupts zu sperren.

@ Falk

Danke für diesen Hinweis! Das wäre einen Eintrag in der Checkliste oder 
in den IO-Abschnitten der Tutorials wert (wenn es nicht schon drin 
steht, habe noch nicht nachgesehen).

von Jakob M. (Firma: Student) (jakkob)


Lesenswert?

Hallo,

@ Falk

hat sich ja verdammt gut angehört deine Antwort, war leider aber noch 
nicht die (ganze) Lösung. Ich habe jetzt mal
1
Taste_Zeit=Zeit; while (Taste_Zeit+1>Zeit) {wdt_reset();}
2
F=Zeit/1000;
an besagte Stelle eingefügt (Zeit wird per ISR im msec-Takt hochgezählt, 
sowohl Zeit als auch F sind volatile uint_32's; meine Funktion ist nicht 
zeitkritisch).
Das sollte Taktmäßig ja absolut ausreichen. Leider funktioniert es 
allerdings immer noch nicht, wenn ich die Bits in PORTB einzeln 
setze..???  :-(
Weißt Du vielleicht noch mehr Rat?

 @Stefan

...auch dieser Code funzt aus mir unbekannten Gründen nicht. Und ich 
habe nur eine ISR, die Zeit++; macht und sonst nüscht...:-(


gruß und dank
jakob

von Falk B. (falk)


Lesenswert?

@ Jakob M. (Firma Student) (jakkob)

>Taste_Zeit=Zeit; while (Taste_Zeit+1>Zeit) {wdt_reset();}
>F=Zeit/1000;

>an besagte Stelle eingefügt (Zeit wird per ISR im msec-Takt hochgezählt,
>sowohl Zeit als auch F sind volatile uint_32's; meine Funktion ist nicht
>zeitkritisch).

Volatile reicht nicht bei uint32_t! Die kann der AVR nicht atomar 
verarbeiten! Da musst du mit

cli()
sei()

klammern!

Lass den Watchdog erstmal aussen vor.

>...auch dieser Code funzt aus mir unbekannten Gründen nicht. Und ich
>habe nur eine ISR, die Zeit++; macht und sonst nüscht...:-(

Poste mal VOLLSTÄNDIGEN Code, als Anhang.

MfG
Falk

von Jakob M. (Firma: Student) (jakkob)


Angehängte Dateien:

Lesenswert?

Hi,

hmm...volatile hat eigentlich immer gefunzt, auch bei Zeit, die ja 
uint_32t ist...

.../* musss an dieser Stelle mal 1000 Küsse vergeben an die Leute, die 
hier so fleißig Probleme lösen helfen */

danke

von Jakob M. (Firma: Student) (jakkob)


Angehängte Dateien:

Lesenswert?

und der asm

von Falk B. (falk)


Lesenswert?

@  Jakob M. (Firma Student) (jakkob)

>hmm...volatile hat eigentlich immer gefunzt, auch bei Zeit, die ja
>uint_32t ist...

Das kann aber schief gehen. In 1 von 1 Million Fällen. Es ist 
nachweislich NICHT sicher! Und dann viel Spass bei Debuggen.

http://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff

Nun Zum Programm.

[c]
#ifndef avr_io_h
#define avr_io_h
#include <avr/io.h>
#endif

#ifndef _interrupt_h
#define _interrupt_h
#include <avr/interrupt.h>
#endif

#ifndef Funktionen_h
#define Funktionen_h
#include "Funktionen.h"
#endif

#ifndef avr_wdt_h
#define avr_wdt_h
#include <avr/wdt.h>
#endif

#ifndef Kontrollvabsis_h
#define Kontrollvabsis_h
#include "Kontrollvabsis.h"
#endif
[c]

Das ist Käse. Diese Dinger gehören nur IN die jeweiligen Headerfiles 
rein.

Deine verkorksten Warteschleifen in scan_taste() sind auch Unsinn. So 
eine Tastenabfrage läuft einmal komplett OHNE Warteschleifen durch und 
wird zyklisch alle 2..20 ms im Timerinterrupt aufgerufen. NCIHT ander 
herum! Duch die "langsame" Abtastung erreicht man schon eine gute 
Entprellung. Zusätzlich kann man prüfen obe 2..X mal der gleiche 
Tastencode geliefert wird, dann ist es absolut wasserdicht.

MFG
Falk

von Jakob M. (Firma: Student) (jakkob)


Lesenswert?

@ Falk

ja, die Wartesacxhleifen in scan_taste() sind Unsinn. Es gibt sie auch 
erst seit ein paar Studen, da Du ja meintest es sollten ein paar Befehle 
zwischen setzen und lesen der Ports. Und das mit den headerfiles, nun 
ja, da hab oich mich wohl als totaler newbi geoutet :-) (habe das jetzt 
aber auch behoben! Danke hierfür), aber das ändert doch nix an meinem 
Problem...das ja irgendwie mit der Portzuweisung zu tun hat (scheinbar 
zumindest). Und das scan_taste() nicht inner ISR aufgerufen wird, liegt 
daran, dass sie nur in ganz bestimmten Zuständen gebraucht wird...
1
char scan_taste() {      
2
    //PORTB=0b11111111;
3
  //DDRB= 0b11111000;  
4
  volatile uint8_t C =0;  
5
            // C wird an switch übergeben
6
            //am Anfang ist R0 auf 5V    
7
  cli();
8
  PORTB= (0b01110111 | (PORTB & (1<<PB7)));
9
  //geht leider immer noch nicht
10
  //gehen würde verrückterweise aber
11
  //PORTB= 0b01110111
12
  
13
  
14
  C=234;        //um irgendwas zu machen zwischen
15
            //schreiben und lesen
16
  C|=0xDD;
17
  C = PINB | 0b11111000;  //um die drei letzten bits zu vergl.
18
  sei();            // wenn Pin0=Col.3 gedrückt--> PORTB: 0b01111110, wenn in R0
19
  switch(C) {
20
    case 0b11111111 : break;   //nix in R0 gedrückt, nix Passiert
21
    case 0b11111011 : return 10;
22
    case 0b11111101 : return 11;
23
    case 0b11111110 : return 12;
24
  }
25
   cli();
26
   PORTB=(0b01101111| (PORTB & (1<<PB7)));  
27
              //PIND4= log.1 --->R1=5V, andere Rs=0V
28
              //PIND5-6 und PIND3= log.0
29
  C=234;
30
  C|=0xDD;
31
  C= PINB | 0b11111000;
32
    sei(); //if (B==1){PORTB |= (1<<PB7);}
33
    switch(C) {
34
    case 0b11111111 : break;     //nix gedrückt in R1, nix Passiert
35
    case 0b11111011 : return 1;
36
    case 0b11111101 : return 2;
37
    case 0b11111110 : return 3;
38
  }
39
    
40
  cli();
41
  PORTB=(0b01011111| (PORTB & (1<<PB7)));;
42
                    //PIND5= log.1 --->R2=5V, andere Rs=0V
43
                  //PIND3-4 und PIND6= log.0
44
    C=234;
45
  C|=0xDD;
46
  C= PINB | 0b11111000;
47
  sei();
48
  switch(C) {
49
    case 0b11111111 : break;   //nix gedrückt in R2, nix Passiert
50
    case 0b11111011 : return 4;
51
    case 0b11111101 : return 5;
52
    case 0b11111110 : return 6;
53
  }
54
    cli();
55
  PORTB=(0b00111111 | (PORTB & (1<<PB7)));
56
                    //PIND6= log.1 --->R3=5V, andere Rs=0V
57
                  //PIND3-5= log.0
58
  C=234;
59
  C|=0xDD;
60
  C= PINB | 0b11111000;
61
  sei();
62
    switch(C) {
63
    case 0b11111111 : break ; //nix gedrückt in R3, nix Passiert
64
    case 0b11111011 : return 7;
65
    case 0b11111101 : return 8;
66
    case 0b11111110 : return 9;
67
    }
68
  return 0;
69
}

von Falk B. (falk)


Lesenswert?

@ Jakob M. (Firma Student) (jakkob)

>erst seit ein paar Studen, da Du ja meintest es sollten ein paar Befehle
>zwischen setzen und lesen der Ports.

Naja, aber leider ist der Compiler "schlauer" als du, und optimiert ggf. 
den Zugriff anders.
Das muss man ihm expizit verbieten, z.B. indem men per Inline Assembler 
ein NOP einfügt. Hab aber im Moment nicht die Syntax im Kopf. Jörg?
Für

>Problem...das ja irgendwie mit der Portzuweisung zu tun hat (scheinbar
>zumindest). Und das scan_taste() nicht inner ISR aufgerufen wird, liegt
>daran, dass sie nur in ganz bestimmten Zuständen gebraucht wird...

Wie testest du denn überhaupt? teste mal nur die funktion, ohne 
irgendwelches anderes Zeug.
1
char scan_taste() {      
2
    //PORTB=0b11111111;
3
  //DDRB= 0b11111000;  
4
  volatile uint8_t C =0;
Sowas ist insinnig. Volatile ist nur für GLOLE Variablen nötig, die 
SOWOHL im Interrupt als auch im normalen Prpgramm verwendet werden.

Das cli() und sei() ist jetzt natürlich auch sinnlos, weil du ja nicht 
mehr auf solche variablen zugreifst.
1
  PORTB= (0b01110111 | (PORTB & (1<<PB7)));
2
  //geht leider immer noch nicht
3
  //gehen würde verrückterweise aber
4
  //PORTB= 0b01110111

Verdrahtungsfehler?

Versuchs mal so
1
char scan_taste() {      
2
  uint8_t C =0;  
3
            // C wird an switch übergeben
4
            //am Anfang ist R0 auf 5V    
5
6
  DDRB = 0xFF;
7
  PORTB= 0xFF;  // alles Ausgang, HIGH
8
  DDRB = 0b11111000;
9
  PORTB= (0b01110111 | (PORTB & (1<<PB7)));
10
  
11
  C = PINB;    // NICHT optimierbar, da PINC volatile ist
12
  C = PINB & 0b111;  //um die drei letzten bits zu vergl.
13
14
  // mit UND-Verknüpfung wird das einfacher und übersichtlicher!
15
16
  switch(C) {
17
    case 0b111 : break;   //nix in R0 gedrückt, nix Passiert
18
    case 0b011 : return 10;
19
    case 0b101 : return 11;
20
    case 0b110 : return 12;
21
  }
22
23
  DDRB = 0xFF;
24
  PORTB= 0xFF;  // alles Ausgang, HIGH
25
  DDRB = 0b11111000;
26
27
  PORTB=(0b01101111| (PORTB & (1<<PB7)));  
28
              //PIND4= log.1 --->R1=5V, andere Rs=0V
29
              //PIND5-6 und PIND3= log.0
30
  C= PinB;
31
  C= PINB & 0b111;
32
33
    switch(C) {
34
    case 0b111 : break;     //nix gedrückt in R1, nix Passiert
35
    case 0b011 : return 1;
36
    case 0b101 : return 2;
37
    case 0b110 : return 3;
38
  }
39
    
40
  DDRB = 0xFF;
41
  PORTB= 0xFF;  // alles Ausgang, HIGH
42
  DDRB = 0b11111000;
43
44
  PORTB=(0b01011111| (PORTB & (1<<PB7)));;
45
                    //PIND5= log.1 --->R2=5V, andere Rs=0V
46
                  //PIND3-4 und PIND6= log.0
47
  C= PINB;
48
  C= PINB | 0b11111000;
49
50
  switch(C) {
51
    case 0b111 : break;   //nix gedrückt in R2, nix Passiert
52
    case 0b011 : return 4;
53
    case 0b101 : return 5;
54
    case 0b110 : return 6;
55
  }
56
57
  DDRB = 0xFF;
58
  PORTB= 0xFF;  // alles Ausgang, HIGH
59
  DDRB = 0b11111000;
60
61
  PORTB=(0b00111111 | (PORTB & (1<<PB7)));
62
                    //PIND6= log.1 --->R3=5V, andere Rs=0V
63
                  //PIND3-5= log.0
64
  C= PINB;
65
  C= PINB | 0b11111000;
66
67
    switch(C) {
68
    case 0b111 : break ; //nix gedrückt in R3, nix Passiert
69
    case 0b011 : return 7;
70
    case 0b101 : return 8;
71
    case 0b110 : return 9;
72
    }
73
  return 0;
74
}

MFG
Falk

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.