Forum: Mikrocontroller und Digitale Elektronik Programm hängt sich auf (Entprellen?)


von Tio T. (misterten)


Lesenswert?

Einen wunderschönen Guten,

nachdem ich gerne hier auf der Suche nach Hilfe in den bereits 
geschriebenen Beiträgen fündig geworden bin, dachte ich, es wäre mal 
Zeit, dass ich selber auch mal was schreibe. ;)

Ich bin noch ziemlich neu, was das Programmieren von uC angeht. Bisher 
schlage ich mich meiner Meinung nach ganz gut, aber ich muss wohl noch 
eine Menge Trickse und Kniffe lernen. Wie z.B. das Entprellen. Davon 
gibt es ja reichlich Beiträge, aber so richtig verstehen konnte ich noch 
keinen. Weswegen ich mir dann gesagt habe, dass ich dann keine 
Codeschnipsel kopieren werde, sondern selber versuchen werde, was 
Funktionierendes zu kreeiern.

Nun habe ich da was gebastelt, was funktioniert. Aber ich vermute dabei 
auch ein Problem: Nämlich das, dass der uC sich deswegen nach einigen 
Minuten (es sind so knapp 5 davon) aufhängt, und nicht mehr reagieren 
möchte. Es muss meine Entprellroutine sein, weil sonst nichts Anderes im 
Programm passiert.

Ich benutze einen PIC 16F876 mit nem 4MHz Quarz. Da Interrupts und Timer 
besetzt sind, polle ich die Tastenabfrage, was ich folgendermaßen 
realisiert habe: In einer kleinen Schleife wird nachgeschaut, ob eine 
Taste gedrückt wurde. Die Schleife zählt rückwärts. Gegenläufig läuft 
ein weiterer Zähler. Ist während der ganzen Schleifenabfrage eine Taste 
dauerhaft gedrückt worden, so erreicht der zweite Zähler den Anfangswert 
des Schleifenzählers. (Prellt die Taste, wird der zweite Zähler wieder 
auf Null gesetzt.) Somit gilt die Taste als gedrückt, und es darf was 
ausgeführt werden.


1
TRISB = 0b11111101;
2
PORTB = 0b00000000;
3
4
#define TASTER_1      RB2 //input            1
5
#define TASTER_2      RB3 //input            1
6
#define TASTER_3      RB4 //input            1
7
#define TASTER_4      RB5 //input ,no PGM    1
8
#define TASTER_5      RB6 //input            1
9
#define TASTER_6      RB7 //input            1
10
11
unsigned char debounce_state = 0;
12
unsigned char debounce_loop;
13
unsigned char debounced = 150;
14
15
while(1)
16
{ //Entprellen
17
  debounce_state = 0;
18
  debounce_loop = debounced;              //Anfangswert setzen
19
  while (debounce_loop--)
20
  {  //Eine der Tasten gedrückt?
21
     if (!TASTER_1 || !TASTER_2  || !TASTER_3 || !TASTER_4 || !TASTER_5 || !TASTER_6)
22
    {  debounce_state++;
23
    }
24
    else debounce_state = 0;
25
  }
26
27
  //Taste wirklich gedrückt? (Debounced?)
28
  if (debounce_state == debounced)
29
  {
30
            //DANN WIRD HIER WAS AUSGEFÜHRT
31
  .
32
  .
33
  .
34
  }
35
36
37
  //Entprellen Loslassen nach Ausführung
38
  debounce_loop = debounced;
39
  while (debounce_loop--)
40
  {  //Alle Tasten nicht gedrückt?
41
     if (TASTER_1 && TASTER_2  && TASTER_3 && TASTER_4 && TASTER_5 && TASTER_6)
42
    {  debounce_state++;
43
    }
44
    else debounce_state=0;
45
  }
46
47
    if(debounce_state == debounced)
48
    {
49
            //DANN WIRD HIER WAS AUSGEFÜHRT
50
    .
51
    .
52
    .
53
    }
54
}


Das Ganze funktioniert prima, die Tasten werden damit prima entprellt, 
Tastendruck und Loslassen der Taste werden erkannt, die Ausführungen 
werden dann gemacht. Nur eben nach schätzungweise 5 Minuten (ich drücke 
keine Taste und es trifft auch sonst kein Ereignis ein) bleibt alles 
stehen. Hat jemand eine Idee?

Danke!

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Wie stellst du fest, dass das Programm sich aufgehängt hat?

Was passiert denn, wenn du die Tasten nicht entprellst?
Abgesehen natürlich davon, dass dann die Tasten prellen ;-)
Aber du drückst dann ja auch keine Taste...


Du kommst übrigens in das zweite
>    if(debounce_state == debounced)
auch rein, wenn nicht alle Tasten losgelassen sind. Denn debounce_state 
wird vor der dem "Entprellen Loslassen" nicht mehr auf 0 gesetzt.

Aber insgesamt ist das schon recht kreativ programmiert: ein Zähler, der 
runterzählt, damit ein anderer Zähler hochgezählt werden kann...

von Tio T. (misterten)


Lesenswert?

Hi Lothar. Vielen Dank für die Lobpreisung meiner Kreativität. Ich tue 
mein momentan Bestes. :) Aber es funktioniert zufriedenstellend.

Nun, in der weiteren Ausführung zeige ich Ziffern in einer LCD an. Bei 
Tastendruck ändert sich die (angezeigte) Zahl, und nach knapp 5 Minuten 
halt nicht mehr. Auch eine LED, die den Tastendruck anzeigt leuchtet 
nicht mehr auf.

Ja, dass das zweite
1
>    if(debounce_state == debounced)
immer arbeitet, habe ich auch grad bemerkt... Ich habe mal das 
Entprellen nach Loslassen komplett in den ersten Ausführungezweig 
hineingepacktaber das klappt nicht. Ich füge noch eine Variable namens 
keypressed hinzu, die beim ersten debounce auf 1 gesetzt wird. Das 
zweite Entprellen wird dann nur ausgeführt wenn keypressed, und darin 
wird keypresssed wieder auf Null gesetzt.

--- Nö, das half auch nicht. Im Gegenteil: Jetzt muss ich keine 5 
Minuten warten... (!?)

von Tio T. (misterten)


Lesenswert?

Oh, es scheint doch daran gelegen zu haben. Wenn ich keine Taste drücke, 
verschwindet debounce_state wohl irgendwann ins Nirwana, und keine 
meiner Abfragen trifft dann für weitere Ausführungen zu. Oh je, das Auge 
dafür muss ich noch entwickeln. Ich sitze schon seit einer Woche daran, 
und erkenne das Problem erst jetzt, wo ich es poste. Sorry, das Board 
mehr oder weniger zugemüllt zu haben. :)

Für die, die es interessiert mit Problembehebung:
1
TRISB = 0b11111101;
2
PORTB = 0b00000000;
3
4
#define TASTER_1      RB2 //input            1
5
#define TASTER_2      RB3 //input            1
6
#define TASTER_3      RB4 //input            1
7
#define TASTER_4      RB5 //input ,no PGM    1
8
#define TASTER_5      RB6 //input            1
9
#define TASTER_6      RB7 //input            1
10
11
unsigned char keypressed = 0;
12
unsigned char debounce_state = 0;
13
unsigned char debounce_loop;
14
unsigned char debounced = 150;
15
16
while(1)
17
{ //Entprellen
18
  debounce_state = 0;
19
  debounce_loop = debounced;              //Anfangswert setzen
20
  while (debounce_loop--)
21
  {  //Eine der Tasten gedrückt?
22
     if (!TASTER_1 || !TASTER_2  || !TASTER_3 || !TASTER_4 || !TASTER_5 || !TASTER_6)
23
    {  debounce_state++;
24
    }
25
    else debounce_state = 0;
26
  }
27
28
  //Taste wirklich gedrückt? (Debounced?)
29
  if (debounce_state == debounced)
30
  {
31
      keypressed = 1;                     //Taste als gedrückt erkannt
32
33
            //DANN WIRD HIER WAS AUSGEFÜHRT
34
  .
35
  .
36
  .
37
  }
38
39
40
  //Entprellen Loslassen nach Ausführung
41
42
  if (keypressed)
43
  {   debounce_loop = debounced;
44
     while (debounce_loop--)
45
     {  //Alle Tasten nicht gedrückt?
46
        if (TASTER_1 && TASTER_2  && TASTER_3 && TASTER_4 && TASTER_5 && TASTER_6)
47
       {  debounce_state++;
48
       }
49
       else debounce_state=0;
50
     }
51
52
    if(debounce_state == debounced)
53
    {
54
       keypressed = 0;                 //Taste losgelassen
55
56
            //DANN WIRD HIER WAS AUSGEFÜHRT
57
    .
58
    .
59
    .
60
    }
61
  }
62
}


Was halten denn Profis sonst noch von meiner Entprellroutine? Was ist 
gut und was nicht so gut daran? Wo kann es bei meiner Lösung zu 
Problemen kommen?

Grüße

von Peter D. (peda)


Lesenswert?

Tihomir Tomsic wrote:
> Was halten denn Profis sonst noch von meiner Entprellroutine?

Garnichts, da ich nirgends eine Entprellroutine sehe.

Deine Schleifen dürften wenige µs dauern, ein Taster prellt aber im 
ms-Bereich.

Warscheinlich erfolgt das Entprellen nur durch die Ausführungszeit der 
nicht sichtbaren "//DANN WIRD HIER WAS AUSGEFÜHRT"-Teile.


> Ich benutze einen PIC 16F876 mit nem 4MHz Quarz. Da Interrupts und Timer
> besetzt sind,...

Wenn man es nicht zu ungeschickt anstellt, kann ein Timerinterrupt viele 
zeitgesteuerte Funktionen ausführen.


Peter

von DanVet (Gast)


Lesenswert?

Ja ich seh das auch wie Peter.
Du solltest einen Timer aufsetzten. Z.B. für 10ms und dort die Taster 
entprellen. Dann hast du eine definierte Entprellzeit.
Weißt du denn, wann deine While-Loop aufgerufen wird und wie lange die 
dauert?

von Tio T. (misterten)


Lesenswert?

Hi Ihr zwei,

danke für Eure Beiträge. Das mit dem Timer führt zu den 
Entprellroutinen, die ich bisher nicht ganz nachvollziehen konnte.

Meine Theorie basiert darauf:
Ich möchte erkennen, ob eine Taste gewollt gedrückt wurde. Ich frage in 
der  "while (debounce_loop--)" 150 Mal ab, ob eine der Tasten betätigt 
wurde. Für jedes TRUE inkrementiere ich "debounce_state". Lag kein 
Prellen vor, so werde ich 150 Mal ein TRUE erhalten haben, ich betrachte 
die betätigte Taste als gedrückt, und gebe die dazugehörige Ausführung 
frei.

Liegt ein Prellen vor, werde ich dabei sicherlich mindestens ein FALSE 
erhalten, "debounce_state" kann folglich den Wert 150 nicht erreichen 
("debounce_state" setze ich für den Fall zusätzlich zurück). Erst bei 
(evtl. mehreren) erneuten Durchläufen des Hauptprogramms (und somit der 
"while (debounce_loop--)"-Schleife) wird dann die Taste als gedrückt 
erkannt, weil "debounce_state" irgendwann nur durch gewolltes Betätigen 
den Wert 150 erreichen kann.

Das ist dann quasi meine Entprellung: Erst wenn die 150 komplett voll 
gemacht wurden, gibt's grünes Licht, notfalls oder auch gezwungenermaßen 
nach mehrmaligem Durchlaufen des Hauptprogramms. Mein Hauptprogramm ist 
sozusagen ein virtueller Zähler, der sich selbstständig zurücksetzt. s

Zum Timing:
Meine   "while (debounce_loop--)"-Schleife beobachtet min. 75 us lang 
((150 + x) * 1/(2MHz)), ob ein gedrückter Zustand erkannt wird. Wobei x 
noch die mir unbekannte Anzahl der Maschinenzyklen ist, die durch die 
Tastenabfrage und das Inkrementieren von "dbounce_state" produziert 
wird. Ich schätze mal, dass dadurch 600 us vorliegen, wenn ich je Taste 
und für die Inkrementierung einen Maschinenzyklus berechne. In dieser 
Zeit sollte jedes Entprellen vorüber sein (bei meinen Tastern sind es im 
allerhöchsten Höchstfall 200 us, üblicherweise 30-40).

Mein Hauptprogramm ist recht klein, so dass ich keine Angst haben muss, 
dass möglicherweise ein Tastendruck nicht erkannt wird, weil diese 
außerhalb der "while (debounce_loop--)"-Schleife betätigt wurde. Im 
Gegenteil, ich muss noch ein Ausgeführt-Flag setzen, damit beim erneuten 
Durchlaufen des Hauptprogramms die Ausführung nicht wiederholt 
aufgerufen wird.

Rein theoretisch gibt es da auch kein Problem: Mein Hauptprogramm mit 
allen Unterprogrammausführungen müsste das einmalige Ausführen des 
Hauptprogramms auf mindestens 20-50 ms ausdehnen, weil sogar der 
schnellste Finger die Taste nicht kürzer drücken kann. Und das tut es 
nicht. Lediglich hier muss der Anwender (ich in diesem Falle) dafür 
Sorge tragen, dass er keine Warteschleifen von mehreren Millisekunden im 
Programm hat.

Hmm, ein wenig umständlich, nech? ;) Ich werde mal bei Zeiten mir das 
mitm Timer nochmal genauer angucken.

von Matthias L. (Gast)


Lesenswert?

>Ich möchte erkennen, ob eine Taste gewollt gedrückt wurde. Ich frage in
>der  "while (debounce_loop--)" 150 Mal ab, ob eine der Tasten betätigt
>...
>gewolltes Betätigen
>den Wert 150 erreichen kann. Das ist dann quasi meine

Genau so ist der Ablauf einer Entprellung. NUR dass das Timing der 
Timer macht. zB alle 10ms wird jede Taste geprüft, dann wird entweder 
der Tasten-spezifische Zähler auf Null oder eins hoch gesetzt.
Bei zb 15 (=150ms) wird das als "gedrückt" erkannt...

von Tio T. (misterten)


Lesenswert?

Naja, mein Programm stützt sich auf die Tastenabfrage. Es soll nur bei 
Tastendruck etwas passieren. Somit polle ich sie bei jedem Durchlauf, da 
das Programm im Falle des Nicht-Tastendrucks nichts anderes zu tun hat. 
Da brauche ich dann nicht noch Timer abzufragen. Das Programm gibt mir 
sozusagen das Timing vor: Unkritisch bei mir, da in meinem Falle im 
Mikrosekundenbereich, im Höchstfall (bei Ausführungen nach 
Tastendrucken) im sehr kleinen Millisekundenbereich ist.

Sicher könnte ich da trotzdem mit Timing arbeiten, um den uC auch 
schlafen zu schicken, wg. Stromverbrauch. Aber das kommt später mal. :)

von Matthias L. (Gast)


Lesenswert?

>Das Programm gibt mir sozusagen das Timing vor:

Und das ist Murks. Genau das gibt dir Probleme, wo du ewig suchst und 
nie genacu nachvollziehen kannst, warum was mal sporadisch auftritt.

Machs einfach gleich richtig und lerne die Algorithmen (hier mit Timer)

von Tio T. (misterten)


Lesenswert?

Okay, danke!!

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.