Hallo!
Mir ist bekannt, wie ich in C die Flanke für einen Taster auswerten kann
und wie ich diesen Entprellen kann. Allerdings bezogen sich diese
Ausführungen immer nur auf eine oder zwei Tasten, sodass der Aufwand
sich in Grenzen hielt. Nun aber möchte ich 12 Tasten unabhängig
voneinander auswerten. Natürlich kann ich für jeden Taster eine separate
Funktion schreiben, was aber viel zu aufwändig wäre. Ich finde nur
irgendwie gerade keinen richtigen Ansatz, wie ich das bewerkstelligen
könnte. Meine Idee war, ein Struct zu erzeugen. z.B.:
1
2
3
structTaste
4
{
5
uint8_tflanke_pos,flanke_neg,gedrueckt,inaktiv;
6
}taste1,taste2,taste3.......;
wie kann ich denn nun die entsprechenden Parameter an eine Funktion
übergeben, sodass innerhalb der Funktion die entsprechende
Strukturvariable benutzt wird. Ich steh grade voll auf dem Schlauch. Ich
möchte im besten Fall nur noch die Funktion aufrufen und als Parameter
den Namen der Taste angeben.
Flanke schrieb:> Nun aber möchte ich 12 Tasten unabhängig> voneinander auswerten.
Und? Sowas ist doch kein wirkliches logisches Problem - oder?
Deine Idee, pro Taste ein struct zu haben, kann man so machen - aber
nicht so wie du es angedacht hast. Du hast nämlich nicht wirklich drüber
nachgedacht, sondern drauflos gepoltert.
Tasten sind normalerweise recht langsame Inputs, weswegen man bei Tasten
im Gegensatz zu Drehencodern mit einem Polling alle 10 ms auskommt.
Dabei sieht das ganze Verfahren etwa so aus so aus:
1
macheGeneraltestobIRGENDEINETastegedrücktist
2
wennnicht,dannraushier,
3
sonst:füralleTastendo
4
begin
5
wennTastejetztgedrücktist,danndo
6
begin
7
wennEntprellcounter0ist,// Taste war zuvor ungedrückt
dekrementiereEntprellcounter// wenn 0 dann Taste ungedrückt
30
end
31
end
32
end
So, das ist der Algorithmus in salopper Form geplaudert. Du brauchst pro
Taste nur 2 Zählzellen, jeweils 1 Byte reicht aus. Als lange
Repetierzeit ist zumeist ca. 0.8 Sekunden (bei 10ms Polling = 80
Pollings) ok, als kurze Repetierzeit kommt eher 0.2 Sekunden (20
Pollings) in Frage. Wenn deine Tastatur nicht elendig lange prellt, dann
sind 40 ms Entprellzeit (4 Pollings) ausreichend.
Für deine 12 Tasten brauchst du also 24 Byte RAM zum individuellen
Entprellen.
W.S.
Ja, die Funktionsweise einer Entprellroutine ist mir nicht unbekannt. Wo
ich allerdings einen Knoten im Hirn habe, ist die Art und Weise, wie ich
es schaffe, eine Funktion zu schreiben, welcher ich einen Portpin (also
den Taster, dessen Zustand ich auswerten möchte) übergebe und die
zugehörige Variable im entsprechenden struct mit dem korrekten Wert
beschreibe, so dass ich später an einer x-beliebigen Stelle im Programm
den Zustand des Tasters zur Verfügung habe. Bei einem einzelnen Taster
ist das völlig unproblematisch, da ich den portpin direkt in die
Funktion schreiben kann und den Zustand dort direkt auswerten kann. In
der Funktion, die ich brauche, muss aber an eben dieser Stelle ein
"Platzhalter" stehen, dessen Wert ich der Funktion übergeben muss. Und
genau da hapert es.
Flanke schrieb:> eine Funktion zu schreiben, welcher ich einen Portpin (also> den Taster, dessen Zustand ich auswerten möchte) übergebe
Man kann sich natürlich wahnsinnig komplexe Strukturen ausdenken und
viel RAM damit belegen.
Man kann es sich aber auch einfach machen und alle Tasten an den selben
Port anschließen und den ganzen Port entprellen. Die ARM haben ja
mindestens 16Bit breite Ports und beim AVR nimmt man 2 Ports, die man in
ein uint16_t kombiniert.
Oder man nimmt einen ADC-Eingang und ein paar Widerstände:
Beitrag "Tastenmatrix auslesen über nur 2 Leitungen"
Es hat geklappt! Der Knoten ist weg! Keine Ahnung, warum ich nicht schon
eher drauf gekommen bin. Ich habe das oben genannte Struct. Dann
definiere ich eine Funktion, der ich als Parameter die Hardwareadresse
des Tasters übergebe, sowie einen Pointer auf ein Struct (z.B. struct
Taste * taste). Innerhalb der Funktion kann ich z.B. mittels
taste1->flanke_pos den entsprechenden Wert ändern. Dann kann ich beim
Funktionsaufruf als Parameter die Portadresse und das Struct für die
entsprechende Taste (z.B. &taste1) übergeben. Schon läuft es.
Flanke schrieb:> Wie bekomme ich jetzt vier mögliche Zustände pro Taste> unabhängig voneinander gespeichert, sodass ich jederzeit darauf> zugreifen kann?
Im einfachsten Fall mit paralleler Entprellung und Auswahl der Taste per
Bitmaske, siehe mein Beispiel. Eine Bitmaske ist erheblich
codesparender, als das umständliche Hantieren mit Arrayindexen.
Nimmt man für die Variablen uint64_t, lassen sich nach dieser Methode
bis zu 64 Tasten entprellen.
Die loslassende Flanke brauche ich zwar nicht, aber die läßt sich
einfach dazu basteln, siehe:
Beitrag "Re: Universelle Tastenabfrage"
Hier noch Beispiele mit Sonderfunktionen (lang/kurz, Repeat, 2 Tasten
gleichzeitig).
Beitrag "Universelle Tastenabfrage"
Peter D. schrieb:> Man kann es sich aber auch einfach machen und alle Tasten an den selben> Port anschließen und den ganzen Port entprellen.
Du kannst getrost davon ausgehen daß es in der Mehrheit der Fälle
schlichtweg unmöglich ist alle Leitungen an genau die Pins zu führen die
man sich gerne wünscht weil einfach kein Platz auf der Platine ist für
die daraus resultierenden Knäuel aus Leiterbahnen und Vias. Es ist
oftmals ein stundenlanges oder manchmal auch tagelanges Getüftel bis man
eine Anordnung gefunden hat bei der man endlich alle benötigten
Spezialfunktionen an einen seiner jeweilig zur Auswahl stehenden
Alternativpins angeschlossen hat und dann am Schluß die normalen GPIOs
noch gerade so mit Hängen und Würgen irgendwie dazwischen bekommt ohne
die Zahl der Lagen zu erhöhen oder gezwungen zu sein irgendwelchen
Hardwarefunktionen nun doch in Software erledigen zu müssen weil man
irgend eine Leitung beim besten Willen nicht an diesen einen Pin auf der
anderen Seite hingeroutet bekommt.
Die Forderung "einfach [...] alle Tasten an den selben Port" ist also in
der Praxis meist vollkommen unrealistisch, es sei denn die Kosten und
die Größe des Produktes spielen keine Rolle und die Platine darf doppelt
so groß sein als sie eigentlich müsste.
W.S. schrieb:> das ist der Algorithmus in salopper Form
Und falsch. Wenn eine Entprell-Routine gedrückt und nicht gedrückt
unterschiedlich behandelt, ist prinzipiell etwas faul in ihr.
Peter D. schrieb:> Man kann es sich aber auch einfach machen und alle Tasten an den selben> Port anschließen und den ganzen Port entprellen.
Richtig.
1
inttasten,gedrueckt;
2
3
while(1)// die Programm-Hauptschleife
4
{
5
tasten=INPUT_PORT;// 12 Taster auf ein mal, liefern 1 wenn gedrückt (sonst ~PIND)
6
gedrueckt=tasten&~gedrueckt;
7
if(gedrueckt&1)
8
{
9
// Taster 1 wurde gerade runtergedrückt, mach was
10
}
11
if(gedrueckt&2)
12
{
13
// Taster 2 wurde gerade runtergedrückt, mach was
14
}
15
:
16
// mach was sonst in der Programm-Hauptschleife passieren muß
17
gedrueckt=tasten;
18
_delay_ms(10);// damit sie bestimmt länger dauert als eventuelles Prellen
MaWin schrieb:> int tasten,gedrueckt;
warum int?
ich hätte uint16_t gewählt und sonst wie in PeDas Routine die Auswertung
in uint16_t statt uint8_t gemacht.
voila 12 Tasten in einer VAR.
Bernd K. schrieb:> Die Forderung "einfach [...] alle Tasten an den selben Port" ist also in> der Praxis meist vollkommen unrealistisch
Na übertreib mal nicht so maßlos.
Ich konnte bisher die Tasten immer so auf die Ports verteilen, daß das
Lesen aller Tasten in eine Variable ohne großen Aufwand erfolgen konnte.
Aber auch eine völlig wirre Anordnung ist überhaupt kein Problem, wie
mein Posting genau über Deinem zeigt.
Es ist der Entprellroutine übrigens völlig wurscht, wie man die
Inputvariable einliest. Wie es mit dem ADC geht, habe ich ja schon
gezeigt. Man kann sie aber auch über SPI- oder I2C-Expander einlesen.
Die Routine ist darin völlig flexibel.
Ich mach das gerne so, dass ich für jede Taste eine uint8_t nehme und
dann die Abfrage per Interrupt alle x ms aufrufe.
Bei jedem Aufruf werden die Bits um 1 nach links geschoben und dann das
letzte Bit entsprechend dem Zustand der Taste gedrückt.
Eine Flanke ist dann, wenn
(taste_xy == 0b01111111)
Somit ist die Taste gut entprellt.
Man kann auch 16 oder 32 Bit Variablen nehmen.
Wenn du uint8_t nimmst, brauchst 12 Byte RAM.
Taster aktiv wäre in diesem Fall:
(taster_xy)
Taster inaktiv:
(!taster_xy)
Negative Flanke:
(taster_xy == 0b10000000)
MaWin schrieb:> Und falsch. Wenn eine Entprell-Routine gedrückt und nicht gedrückt> unterschiedlich behandelt, ist prinzipiell etwas faul in ihr.
Du schreibst mal wieder Unsinn: prinzipiell faulen Unsinn.
MaWin schrieb:> while(1)// die Programm-Hauptschleife> {
Ja, Polling in der main-loop. Klasse!
Was du als falsch bezeichnest, ist genau das RICHTIGE. Schließlich ist
ein µC nicht dazu gedacht, sich ausschließlich um das Tastenpolling zu
kümmern.
Also laß derartige Einwürfe bleiben und lerne lieber etwas durch das
Studium meiner Zeilen.
Flanke schrieb:> Wo> ich allerdings einen Knoten im Hirn habe, ist die Art und Weise, wie ich> es schaffe, eine Funktion zu schreiben, welcher ich einen Portpin (also> den Taster, dessen Zustand ich auswerten möchte) übergebe und die> zugehörige Variable im entsprechenden struct mit dem korrekten Wert> beschreibe, so dass ich später an einer x-beliebigen Stelle im Programm> den Zustand des Tasters zur Verfügung habe.
Der Knoten besteht darin, daß du quasi diktatorisch und nicht
ereignisorientiert denkst. Ich übersetze hier mal das Problem durch
deine Denkweise in beschreibende Worte:
Du willst ein Programm schreiben, das an irgendeiner "x-beliebigen"
Stelle eine Funktion aufrufen und ihr die Frage stellen will "Haben wir
einen Tastendruck bei Taste 17? Ja oder nein - gib Antwort JETZT!"
So herum wird das auf ewig NICHTS.
Ein Tastendruck, so wie er von jedem normalen Menschen heutzutage
gemeint ist, besteht NICHT darin, daß die Taste gedrückt IST, sondern
darin, daß die Taste gedrückt WURDE, also daß es einen Übergang vom
ungedrückten Zustand zum gedrückten Zustand gegeben hat (das ist das
gesuchte Ereignis), UND daß es zwecks Entprellen eines geordneten
Überganges vom gedrückten zurück zum ungedrückten Zustand bedarf.
Du brauchst also KEINE Funktion, der du ein Portpin übergibst, sondern
ein Event-System, das dir Ereignisse der Art "Taste 17 wurde grad
gedrückt" liefern kann, wobei der Lowlevel-Treiber, der die Tastatur
überwacht und Drück-Ereignisse ermittelt und zwecks späterer Behandlung
in einer Ereignis-Warteschlange speichert, auch das Entprellen vornimmt,
ohne daß dabei die übrigen Schichten deiner Firmware sich drum kümmern
müßten.
Und in die main-Loop (a la MaWin) gehört so etwas überhaupt nicht.
Nochwas:
Peter hat hier ja gemeint, daß man die Portbelegung den Tasten anpassen
müßte, um sich dadurch das Einzel-Entprellen leichter zu machen.
Schön wär's, ist aber unrealistisch. Bernd hat's bereits ausreichend
dargelegt.
Was man also machen kann, ist in der SysTick-Routine, die zumeist zum
Tasten-Abfragen herangezogen wird, zunächst alle Tasten-Zustände in ein
Wort zusammenzufassen (wie auch immer), um dann alle Bits in diesem Wort
der Reihe nach auszuwerten. Das ist zunächst etwas wilde Bit-Schieberei
oder noch etwas Komplizierteres, je nachdem, wo welche Taste denn
angeschlossen ist, aber da führt kein Weg dran vorbei.
W.S.
W.S. schrieb:> Peter hat hier ja gemeint, daß man die Portbelegung den Tasten anpassen> müßte, um sich dadurch das Einzel-Entprellen leichter zu machen.
Dreh einem doch nicht die Worte im Munde rum, nirgends habe ich gesagt,
man muß.
Ich habe nur gesagt, daß es den Code einfacher macht.
Eine Erweiterung für beliebige Zuordnung habe ich bereits gepostet:
Beitrag "Re: Flankenauswertung bei vielen Tasten"
W.S. schrieb:> Schön wär's, ist aber unrealistisch. Bernd hat's bereits ausreichend> dargelegt.
Er hat nichts dargelegt, sondern nur behauptet. Ich benutze allerdings
keine 1-lagen Platine, da die in der Fertigung nicht günstiger sind als
2-lagige.
Und ab 2 Lagen muß man nicht tagelang tüfteln, um ein paar Tasten
anzuschließen. Oftmals sind die MCs so klein, daß man eh jeden IO
erstmal auf ein Via legen muß. Große DIP, PLCC sind am Aussterben.
Man muß ja nicht absichtlich die Tasten wild anschließen, sondern kann
sie auf wenige Ports konzentrieren. Dann reichen wenige and, or,
schieben aus, um sie in eine Variable zu packen. Unrealistisch ist das
jedenfalls nicht, ich mache es ja in meinen Projekten.
Peter D. schrieb:> Dreh einem doch nicht die Worte im Munde rum, nirgends habe ich gesagt,> man muß.
Ich habe dir gar kein Wort irgendwo umgedreht, lies deine Bemerkung doch
bitte nochmal:
Peter D. schrieb:> Man kann es sich aber auch einfach machen und alle Tasten an den selben> Port anschließen und den ganzen Port entprellen. Die ARM haben ja> mindestens 16Bit breite Ports und beim AVR nimmt man 2 Ports, die man in> ein uint16_t kombiniert.
Und Bernd hat darauf hingewiesen, daß das unrealistisch ist. Und genau
das ist es: unrealistisch. Wohl weniger wegen des Pinouts, sondern eher
wegen der Verteilung der diversen Peripheriecores auf die Pins.
Heutzutage lebt man damit, daß bis zu 7 verschiedene Funktionalitäten
auf ein Pin gelegt sind und daß man diese nicht einfach auf andere Pins
verlegen kann. Da bleiben für etwaige Tasten eben nur noch die
Lückenbüßer übrig.
Deine Idee "alle Tasten an den selben Port" ist tatsächlich nicht
wirklich realistisch.
W.S.
Hi
>Deine Idee "alle Tasten an den selben Port" ist tatsächlich nicht>wirklich realistisch.
Na und. Mit etwas Hirnschmalz hat man das ruck-zuck in die passenden
Variablen verfrachtet. Habe ich schon öfters gemacht.
MfG Spess
W.S. schrieb:> Deine Idee "alle Tasten an den selben Port" ist tatsächlich nicht> wirklich realistisch.
So generell gesagt ist das schlichtweg falsch.
In den meisten Fällen kann man 2..6 Tasten sehr wohl auf einen Port
legen.
Aber auch 12 Tasten an einem ARM (z.B. LPC1768) mit 32Bit-Ports lassen
noch viel Freiheit bei der Zuordnung.
Ich würde daher sagen:
Es kann manchmal Fälle geben, wo man Tasten auf mehrere Ports verteilen
muß.