Forum: Compiler & IDEs Tasteneingänge ohne Funktion?!


von Orikson (Gast)


Lesenswert?

Hi,

ich rätsel seit gestern an einem Problem, welches das funktionieren 
meiner Tasten verhindert. Habe einen Mega8 bei welchem je ein Taster an 
PD5, PD6 und PD7 hängt. An PB0, PB1 und PB2 hängt je eine LED. Die LED's 
leuchten auch schon schön wenn ich sie mit nem Delay an und ausmachen 
lass, aber der Code (siehe unten) mit meinen Tasten funzt nicht so 
richtig, eigentlich macht der gar nix! Auf nen Tastendruck passiert rein 
gar nix. Habe schon die externen Widerstände mal abgelötet und die 
internen aktiviert, ohne Erfolg. Es ist außer den 3 Tasten und 3 LED's 
auch nix weiter dran, also weder LCD noch SPI!

Seltsam ist auch, dass nach aufspielen der Software auf den Mega8 nur 
die LED an PB0 leutet, die anderen sind aus. Verkabelung passt 
eigentlich, die Taster sind Active-Low und auch korrekt verkabelt, 
Durchgang haben die auch, wenn man se drückt...
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <inttypes.h>
4
#include <stdint.h>
5
#ifndef F_CPU
6
#define F_CPU 4000000UL
7
#endif
8
9
/* Einfache Funktion zum Entprellen eines Tasters */
10
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
11
{
12
    if ( ! (*port & (1 << pin)) )
13
    {
14
        /* Pin wurde auf Masse gezogen, 100ms warten   */
15
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz
16
        _delay_ms(50); 
17
        if ( *port & (1 << pin) )
18
        {
19
            /* Anwender Zeit zum Loslassen des Tasters geben */
20
            _delay_ms(50);
21
            _delay_ms(50); 
22
            return 1;
23
        }
24
    }
25
    return 0;
26
}
27
28
int main(void)
29
{
30
  DDRB = (1<<DDB0) | (1<<DDB1) | (1<<DDB2);  //LED Ausgänge
31
  PORTB = (1<<PB0) | (1<<PB1) | (1<<PB2);  //alle an
32
33
  //DDRD &= ~( (1<<DDD5) | (1<<DDD6) | (1<<DDD7) ); //benötigt?
34
  PORTD |= (1<<PD5) | (1<<PD6) | (1<<PD7);  //inerne Pull-Up's an
35
  //Eingänge für Tasten
36
37
  if (debounce(&PIND, PD5)) {         // Falls Taster an PIN PD5 gedrueckt.. 
38
    PORTB = PINB ^ ( 1 << PB0 ); }
39
40
  if (PIND & (1<<PIND6)) {       
41
    PORTB = PINB ^ ( 1 << PB1 ); }
42
43
  if ( (debounce(&PIND, PD7)) || (PIND & (1<<PIND7)) ) {       
44
    PORTB = PINB ^ ( 1 << PB2 ); }
45
46
   while(1)
47
   {
48
   }
49
   return 0;
50
}

von Peter D. (peda)


Lesenswert?

100ms reichen nicht zum Loslassen, ein Mensch drückt etwa 300..1000ms 
lang.

Tastenbetätigungen muß man außerdem in der Mainloop auswerten. Es ist 
ziemlich witzlos, wenn Betätigungen nur beim Power-On erlaubt sind.

Versuch einfach mal, nur eine Taste zu entprellen und eine LED damit zu 
togglen, bis es auch wirklich fehlerlos funktioniert, egal wie lang oder 
kurz jemand drückt.

Dann schmeiß es weg und nimm die Bulletproof Methode aus dem Tutorial.


Peter

von holger (Gast)


Lesenswert?

Peter hat natürlich recht. Ausserdem sollte man folgende Reihenfolge
einhalten:

#include <avr/io.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#include <util/delay.h>
#include <inttypes.h>
#include <stdint.h>

Es ist NICHT egal WO F_CPU definiert wird.
Am besten macht man das im makefile.

Hier sollte man auch noch was ändern:

>  if (debounce(&PIND, PD5)) {         // Falls Taster an PIN PD5 gedrueckt..
>    PORTB = PINB ^ ( 1 << PB0 ); }

   PORTB = PORTB ^ ( 1 << PB0 ); }

Wenn man PINB liest haut man sich evtl. die Einstellung für
bereits aktivierte Pullups auf PORTB weg.

von OliverSo (Gast)


Lesenswert?

>Wenn man PINB liest haut man sich evtl. die Einstellung für
>bereits aktivierte Pullups auf PORTB weg.

Kannst du das mal genauer erklären? Vielleicht mit Verweis auf eine 
entsprechende Stelle im Datenblatt?

Oliver

der bisher der Meinung war, das PINB genau dafür da ist, den Portzustand 
auszulesen.

von Rolf Magnus (Gast)


Lesenswert?

Ich würde es gar schreiben als:
1
PORTB ^= (1 << PB0);

> der bisher der Meinung war, das PINB genau dafür da ist, den
> Portzustand auszulesen.

PINB ist dazu da, die Eingänge zu lesen. Du hast hier aber Ausgänge. 
Solange du alle Leitungen von PORTB als Ausgang benutzt, müßte auch mit 
PINB funktionieren. Da liest der Port dann eben seinen eignen Wert 
wieder zurück. Bei Eingängen können sich die Werte in PORTB und in PINB 
auch unterscheiden. Wenn PB4 z.B. als Eingang mit Pull-Up definiert ist 
(PB4 in PORTB = 1) und da gerade low-Pegel anliegt, liest du von PINB4 
eine 0 zurück. So wird durch deine Zeile PB4 gelöscht und der Pull-Up 
ausgeschaltet. Wenn du stattdessen PORTB liest, wird wirklich nur PB0 
beeinflußt.

von Johannes M. (johnny-m)


Lesenswert?

OliverSo wrote:
> der bisher der Meinung war, das PINB genau dafür da ist, den Portzustand
> auszulesen.
Genau. PINx liefert den aktuellen Zustand am Pin. Der kann aber auch von 
außen beeinflusst werden. Was Du aber willst, ist, die 
Port-Ausgangsstufe umschalten. Und der aktuelle Zustand der 
Ausgangsstufe steht nunmal in PORTx und nicht in PINx. Wenn der Pin ein 
Ausgang ist, sollte zwar normalerweise der Zustand der Ausgangsstufe 
auch dem Zustand am Pin entsprechen, es sei denn, es liegt ein 
Kurzschluss vor oder es hängt eine Last mit nennenswert verzögerter 
Sprungantwort direkt am Pin (stark kapazitive Last). Beide Fälle sollte 
man allerdings tunlichst vermeiden, so dass es bei einem als Ausgang 
konfigurierten Pin i.d.R. keinen Unterschied machen sollte, ob man PORTx 
oder PINx einliest. Allerdings sollte man sich das nicht angewöhnen, 
denn wenn ein Pin als Eingang konfiguriert ist, dann macht es i.d.R. 
schon einen Unterschied...

Merke: PINx ist dann (und nur dann) zu lesen, wenn tatsächlich der 
aktuelle Zustand am Pin (der auch von außen beeinflussbar ist) 
eingelesen werden soll. Ist ein Pin als Ausgang konfiguriert, macht es 
i.d.R. keinen Sinn, PINx zu lesen.

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus wrote:
> Ich würde es gar schreiben als:
>
>
1
> PORTB ^= (1 << PB0);
2
>
>
>> der bisher der Meinung war, das PINB genau dafür da ist, den
>> Portzustand auszulesen.
>
> PINB ist dazu da, die Eingänge zu lesen. Du hast hier aber Ausgänge.
> Solange du alle Leitungen von PORTB als Ausgang benutzt, müßte auch mit
> PINB funktionieren. Da liest der Port dann eben seinen eignen Wert
> wieder zurück. Bei Eingängen können sich die Werte in PORTB und in PINB
> auch unterscheiden.

@Orikson

Auch bei Ausgängen kann das sein, wenn am Ausgang ein Kurzschluss
vorliegt.

Über PORTx wird der Ausgang auf 1 geschaltet.
Liest man den Wert vom PORTx Register zurück erhält man auch
diese 1 wieder.
Liest man den Wert aber über das PINx Register zurück, erhält
man eine 0

Also:
  Wenn der Portpin auf Ausgang geschaltet ist, kriegt man
  beim Lesen von
    PORTx   den Wert den man zuletzt geschrieben hat
    PINx    den Wert den der Ausgangspin tatsächlich hat.
            Und das auch nur mit einer kleinen Verzögerung
            nach dem Setzten des Pins durch Schreiben auf das
            PORTx Register.

Das gilt aber nur, wenn der Pin auf Ausgang geschaltet ist!

von OliverSo (Gast)


Lesenswert?

Mein Frage bezog sich eigentlich nur auf den Satz:

>Wenn man PINB liest haut man sich evtl. die Einstellung für
>bereits aktivierte Pullups auf PORTB weg.

Das ist trotzdem Quark. PINx kann man lesen, so oft man will, an den 
Pullups ändert sich dabei gar nichts.

Das in der Zeile
1
PORTB = PINB ^ ( 1 << PB0 );

PORTB gelesen werden sollte, ist natürlich richtig, das hatte ich dabei 
aber völlig übersehen.

Oliver

von Rolf Magnus (Gast)


Lesenswert?

> Das ist trotzdem Quark. PINx kann man lesen, so oft man will, an den
> Pullups ändert sich dabei gar nichts.

Durch das Lesen selbst nicht, aber durch das Schreiben des dort 
gelesenen Werts nach PORTB.

von Orikson (Gast)


Lesenswert?

Danke Leute, so funktionierts:
1
PORTB ^= (1 << PB0);

Aber was meint ihr mit der Bulletproof Methode zum Entrellen? Den Code 
jetz hab ich schon aus dem Tut:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#.28Tasten-.29Entprellung

Oder meint ihr die Variante mit Interrupts? Die hatte ich eh vor zu 
verwenden: 
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

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.