mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Tastendrücke und Portbits


Autor: Jakob M. (Firma: Student) (jakkob)
Datum:

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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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.
  /* PORTB setzen einzeln */
  PORTB |= (1<<PB6) | (1<<PB5) | (1<<PB4) | (1<<PB2) | (1<<PB1) |
  (1<<PB0);
  PORTB &= ~(1<<PB3) &~(1<<PB7);
  
  // hier MUSS mindestens ein ASM Befehl rein, ala _NOP();

  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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

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

Autor: Jakob M. (Firma: Student) (jakkob)
Datum:

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

Autor: Falk Brunner (falk)
Datum:

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

Autor: Jakob M. (Firma: Student) (jakkob)
Datum:
Angehängte Dateien:

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

Autor: Jakob M. (Firma: Student) (jakkob)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
und der asm

Autor: Falk Brunner (falk)
Datum:

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

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

Autor: Jakob M. (Firma: Student) (jakkob)
Datum:

Bewertung
0 lesenswert
nicht 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...
char scan_taste() {      
    //PORTB=0b11111111;
  //DDRB= 0b11111000;  
  volatile uint8_t C =0;  
            // C wird an switch übergeben
            //am Anfang ist R0 auf 5V    
  cli();
  PORTB= (0b01110111 | (PORTB & (1<<PB7)));
  //geht leider immer noch nicht
  //gehen würde verrückterweise aber
  //PORTB= 0b01110111
  
  
  C=234;        //um irgendwas zu machen zwischen
            //schreiben und lesen
  C|=0xDD;
  C = PINB | 0b11111000;  //um die drei letzten bits zu vergl.
  sei();            // wenn Pin0=Col.3 gedrückt--> PORTB: 0b01111110, wenn in R0
  switch(C) {
    case 0b11111111 : break;   //nix in R0 gedrückt, nix Passiert
    case 0b11111011 : return 10;
    case 0b11111101 : return 11;
    case 0b11111110 : return 12;
  }
   cli();
   PORTB=(0b01101111| (PORTB & (1<<PB7)));  
              //PIND4= log.1 --->R1=5V, andere Rs=0V
              //PIND5-6 und PIND3= log.0
  C=234;
  C|=0xDD;
  C= PINB | 0b11111000;
    sei(); //if (B==1){PORTB |= (1<<PB7);}
    switch(C) {
    case 0b11111111 : break;     //nix gedrückt in R1, nix Passiert
    case 0b11111011 : return 1;
    case 0b11111101 : return 2;
    case 0b11111110 : return 3;
  }
    
  cli();
  PORTB=(0b01011111| (PORTB & (1<<PB7)));;
                    //PIND5= log.1 --->R2=5V, andere Rs=0V
                  //PIND3-4 und PIND6= log.0
    C=234;
  C|=0xDD;
  C= PINB | 0b11111000;
  sei();
  switch(C) {
    case 0b11111111 : break;   //nix gedrückt in R2, nix Passiert
    case 0b11111011 : return 4;
    case 0b11111101 : return 5;
    case 0b11111110 : return 6;
  }
    cli();
  PORTB=(0b00111111 | (PORTB & (1<<PB7)));
                    //PIND6= log.1 --->R3=5V, andere Rs=0V
                  //PIND3-5= log.0
  C=234;
  C|=0xDD;
  C= PINB | 0b11111000;
  sei();
    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;
}

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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.
char scan_taste() {      
    //PORTB=0b11111111;
  //DDRB= 0b11111000;  
  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.
  PORTB= (0b01110111 | (PORTB & (1<<PB7)));
  //geht leider immer noch nicht
  //gehen würde verrückterweise aber
  //PORTB= 0b01110111

Verdrahtungsfehler?

Versuchs mal so
char scan_taste() {      
  uint8_t C =0;  
            // C wird an switch übergeben
            //am Anfang ist R0 auf 5V    

  DDRB = 0xFF;
  PORTB= 0xFF;  // alles Ausgang, HIGH
  DDRB = 0b11111000;
  PORTB= (0b01110111 | (PORTB & (1<<PB7)));
  
  C = PINB;    // NICHT optimierbar, da PINC volatile ist
  C = PINB & 0b111;  //um die drei letzten bits zu vergl.

  // mit UND-Verknüpfung wird das einfacher und übersichtlicher!

  switch(C) {
    case 0b111 : break;   //nix in R0 gedrückt, nix Passiert
    case 0b011 : return 10;
    case 0b101 : return 11;
    case 0b110 : return 12;
  }

  DDRB = 0xFF;
  PORTB= 0xFF;  // alles Ausgang, HIGH
  DDRB = 0b11111000;

  PORTB=(0b01101111| (PORTB & (1<<PB7)));  
              //PIND4= log.1 --->R1=5V, andere Rs=0V
              //PIND5-6 und PIND3= log.0
  C= PinB;
  C= PINB & 0b111;

    switch(C) {
    case 0b111 : break;     //nix gedrückt in R1, nix Passiert
    case 0b011 : return 1;
    case 0b101 : return 2;
    case 0b110 : return 3;
  }
    
  DDRB = 0xFF;
  PORTB= 0xFF;  // alles Ausgang, HIGH
  DDRB = 0b11111000;

  PORTB=(0b01011111| (PORTB & (1<<PB7)));;
                    //PIND5= log.1 --->R2=5V, andere Rs=0V
                  //PIND3-4 und PIND6= log.0
  C= PINB;
  C= PINB | 0b11111000;

  switch(C) {
    case 0b111 : break;   //nix gedrückt in R2, nix Passiert
    case 0b011 : return 4;
    case 0b101 : return 5;
    case 0b110 : return 6;
  }

  DDRB = 0xFF;
  PORTB= 0xFF;  // alles Ausgang, HIGH
  DDRB = 0b11111000;

  PORTB=(0b00111111 | (PORTB & (1<<PB7)));
                    //PIND6= log.1 --->R3=5V, andere Rs=0V
                  //PIND3-5= log.0
  C= PINB;
  C= PINB | 0b11111000;

    switch(C) {
    case 0b111 : break ; //nix gedrückt in R3, nix Passiert
    case 0b011 : return 7;
    case 0b101 : return 8;
    case 0b110 : return 9;
    }
  return 0;
}

MFG
Falk

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.