Hallo zusammen,
ich schreibe derzeit ein Programm für einen Mid-Range
PIC-Mikrocontroller (PIC16F873) in C und benutze dafür MPLABX und den
kostenfreien Compiler XC8.
Der Mikrocontroller wird extern mit 4MHz getaktet und läuft zuverlässig
in der Schaltung. Am I2C-Bus hängen ein paar IO-Expander mit denen ich
verschiedene Stati visualisiere (die Expander steuern LEDs). Am PortB
sind acht Taster angeschlossen. Alle Taster sind Hardware-Entprellt
(RC-Glieder und Schmitt-Trigger). Wenn ich die verschiedenen Eingänge
mit dem Oszilloskop betrachte und einen Taster betätige, sehe ich
saubere Rechteckimpulse (ca.50ms breit, wenn sehr kurz betätigt). Auch
im Programmcode lassen sich beliebige Aufgaben mithilfe der externen
Taster lösen. Soweit funktioniert alles einwandfrei.
Nun habe ich eine kleine State-Maschine programmiert und wundere mich
über das verhalten des Microcontrollers. In der main-Funktion des
Programms wird die state-maschine aufgerufen. Die state-maschine liest
mithilfe der Funktion key_Handler() zuerst alle Eingänge und ein
generiert aus dem aktuellen Zustand einen Status und arbeitet darauf hin
die notwendigen (vom Status abhängigen) Aktionen ab.
Nun zu meiner Frage: Warum macht es einen Unterschied, wo ich
(PORTBbits.RB7==1) Abfrage? Wird der Zustand als erstes Abgefragt,
registriert das Programm jeden Tastendruck. Wird er als letztes
abgefragt, wird nur unregelmäßig die zugehörige Aktion ausgeführt. Kann
mir das jemand beantworten?
Vielen Dank und freundlichen Gruß
musicmiles
Anhang:
Funktion key_handler() und Funktion key_statemaschine()
@ musicmiles (Gast)
>Nun habe ich eine kleine State-Maschine programmiert
Kleiner Tipp: Nutze symbolische Namen (#define oder enum) für die
States, damit wird das Ganze deutlich besser lesbar, siehe
Statemachine.
>über das verhalten des Microcontrollers. In der main-Funktion des>Programms wird die state-maschine aufgerufen. Die state-maschine liest>mithilfe der Funktion key_Handler() zuerst alle Eingänge und ein>generiert aus dem aktuellen Zustand einen Status und arbeitet darauf hin>die notwendigen (vom Status abhängigen) Aktionen ab.
Deine Struktur ist ungünstig. Die Aktionen gehören in die State machine.
Ausserdem macht man eine Tasterentprtellung bzw. Flankenerkennung
anders. Der Klassiker: Die Entprellroutin von Peter Danneger. ISt zwar
für AVR, kann man aber leicht auf PIC anpassen.
Siehe Entprellung.
Vielen Dank für den Tipp mit den Symbolischen Namen - das werde ich noch
in Angriff nehmen.
Falk Brunner schrieb:> Die Aktionen gehören in die State machine.
Was gehört genau in die State maschine? Das Abfragen der Eingänge?
Falk Brunner schrieb:> Ausserdem macht man eine Tasterentprtellung bzw. Flankenerkennung> anders. Der Klassiker: Die Entprellroutin von Peter Danneger. ISt zwar> für AVR, kann man aber leicht auf PIC anpassen.
Das es andere Methoden gibt, dass steht ausser Frage. Allerdings
interessiert mich, warum es so wie oben beschrieben steht, nicht
funktioniert. Natürlich kann ich gleich das Programm umbauen und mir
vorgefertigten Code zunutze machen. Aber ich würde gerne nachvollziehen
können, warum der PIC die Eingaben nicht mitbekommt.
Gruß
musicmiles schrieb:
BIst du sicher, dass deine Tasten im Ruhezustand eine 0 liefern und nur
wenn gedrückt eine 1?
Wie sind deine Taster verschaltet? Schalten die nach GND oder schalten
die nach Vcc? Gibt es Pullup bzw. Pulldown Widerstände?
(Ich geh mal davon aus, dass eine gedrückte Taste bei dir eine 1
liefert)
Dein Code ist insofern ungünstig, weil du grundsätzlich immer nur die
Taste mit der höchsten Bitnummer akzeptierst. Durch Drücken von RB7 kann
ich damit alle anderen Tasten lahm legen. Und wenn deine
Hardware-Entprellung zb wegen offener Eingänge doch nicht so gut
funktioniert, wie du denkst, dann kann dir ein floatender Eingang mit
einer Nummer höher als RB1 den RB1 lahmlegen. Vor allen Dingen dann,
wenn der toggelt wie verrückt. Denn dein Code WILL ja den Eingang 11 mal
hintereinander (mit jeweils 10ms Wartezeit dazwischen) auf Low sehen,
ehe der Tastendruck akzeptiert wird. Jeder Toggler an einem Eingang
startet diese 110ms wieder erneut.
> if(PORTBbits.RB1==1)> {> state=1;> for(db_cnt = 0; db_cnt <= 10; db_cnt++)> {> __delay_ms(1);> if(PORTBbits.RB1==1)> {> db_cnt = 0;> }> }> }
Das ist ziemlich kompliziert geschrieben. Wenn deine Hardware
Entprellung funktioniert, dann ist das alles recht unnötig. Vor allen
Dingen die Wartezeiten. Wenn die Hardware Entprellung in Ordnung ist,
dann ist es so
viel simpler. Alleine dadurch, dass du da scheinbar einen delay
brauchst, lässt mich daran glauben, dass deine Entprellung eben doch
nicht so gut funktioniert, wie du denkst.
Karl Heinz schrieb:> wenn der toggelt wie verrückt. Denn dein Code WILL ja den Eingang 11 mal> hintereinander (mit jeweils 10ms Wartezeit dazwischen)
Entschuldigung. Es sind nur 1 ms und nicht 10. Demenstprechend dann auch
11ms komplett und nicht 110.
Am grundsätzlichen Grundproblem ändert das allerdings immer noch nichts.
Die Tastenabfrage ist so inakzeptabel. Durch drücken einer Taste und
gedrückt halten der Taste kann ich den kompletten µC mit Ausnahme von
Interrupt Routinen lahm legen.
Vielen Dank für die schnellen Antworten.
Der Mikrocontroller ist in diesem Fall ausschließlich dafür zuständig,
die Eingaben des Benutzers auszuführen und zu visualisieren. Wenn er
eine Taste lange drücken möchte, kann er das tun - und es passiert
nichts.
Aber davon abgesehen verstehe ich nicht, warum es einen Unterschied
macht, an welcher Stelle im key-handler() die einzelnen Bits der
Eingänge abgefragt werden. Ich drücke die Taste am RB7. Steht die
if-Anweisung if(PORTBbits.RB7==1) ganz oben, funktioniert alles (wie von
mir zu diesem Zeitpunkt gewünscht) wunderbar. Setze ich die if-Anweisung
ganz nach unten, bekommt der Mikrocontroller den Tastendruck nicht immer
mit. Und alle anderen Eingänge liegen definitiv auf low (werden durch
den Schmitt-Trigger in Verbindung mit einem Pull_Down runtergezogen).
Vielen Dank und Gruß
musicmiles schrieb:> mit. Und alle anderen Eingänge liegen definitiv auf low (werden durch> den Schmitt-Trigger in Verbindung mit einem Pull_Down runtergezogen).
Dann musst du dir ansehen, wo der µC die Zeit vertödelt.
Ich denke immer noch, er hängt in irgendeiner anderen Tastenabfrage,
weil die Eingänge nicht so ruhig sind, wie du glaubst das sie sind.
Mach dir dort halt mal ein paar Ausgaben zb mittels LED einschalten
rein. Dann kommst du dem auf die Schliche
Karl Heinz schrieb:> Dann musst du dir ansehen, wo der µC die Zeit vertödelt.
Scheinbar hing der µC in einer while-schleife fest, die auf das ACK von
einem I2C-Slave wartete. Jetzt funktioniert es wie gewünscht. Danke.
Nun noch eine Frage: Ich möchte jetzt noch verschiedene
Tastendruckszenarien mithilfe des integrierten Timers und einer
zugehörigen Interrupt-Routine programmieren, wie z.B. Taste ist länger
als drei Sekunden gedrückt, dann schalte zwei LEDs ein, anstatt nur
eine.
Ich setze bei Tastendruck den TIMER0 und das zugehörige Interrupt-Flag
auf 0. Zähle dann die Überläufe und nach einer gewissen Zeit (abhängig
vom Takt und den Prescaler-Einstellungen) passiert dann etwas in der
Interrupt-Routine. Ich möchte aber alle Zustände in der Statemaschine
verwalten, d.h. wenn eine Taste kurz gedrückt wird, entspricht dies bsp.
dem Zustand 11, wenn Sie aber länger als drei Sekunden gedrückt wird,
entspricht dies dem Zustand 12.
Rufe ich aus der Interrupt-Routine die Statemaschine auf oder wie löst
man so etwas sauber? So wie ich es verstanden habe, liest man alle
Zustände (Taster gedrückt/nicht gedrückt/lange gedrückt) ein und dann
entscheidet die statemaschine was passiert.
Gruß