Hallo,
in den letzten Tagen habe ich mir das Tutorial AVR-GCC etwas
vorgenommen, um mit meinem STK500+Atmega8L ein weinig rumzuspielen.
Ich nutze AVR Studio 4.16 und als compiler die neuste WinAVR Version.
Ich habe mir in diesem Zusammenhang eine Entprellfunktion gebastelt. Die
"millis" werden zuverlässig durch einen Timer erzeugt und sind notwendig
für meine Entprellfunktion.
Der Funktionsaufruf erfolgt zum Beispiel mittels:
Debounce(PINC,PINC0)
Rückgabewert ist dann die Variable "Zustand", welche mir dann zum
Beispiel über den Rückgabewert 1 oder 3 ein "entprellte" steigende oder
sinkende Flanke ausgibt.
Die Funktion funktioniert zuverlässig, solange sie nur einmal in der
Dauerschleife vorkommt. Nutze ich sie hingegen zum Beispiel für
verschiedene Taster, dann beeinflussen die Variablen der beiden Aufrufe
sich gegenseitig. Das sind meines Erachtens nach die Variablen:
static int Zustand;
static int Vorher;
static int UebergabeZustand;
static und das definieren von lokalen Variablen war nur ein Versuch, das
Problem zu lösen und nicht zweckerfüllend.
Das Problem macht sich insoweit bemerkbar, dass mehrmalige Tastendrücke
erkannt werden, obwohl diese gar nicht stattgefunden haben.
Hat jemand eine klügere Idee, die Funktion aufzurufen, Variablen zu
definieren (ja ich weiß, alles mit int ist nichts ressourcensparend,
sollte aber overflows vorerst vermeiden) oder erkennt, woran es hakt?
Ich freue mich auf eure Antworten.
1
/*
2
FUSES:
3
Int. RC Osc. 1MHz => UNGENAUER WERT FÜR millis
4
*/
5
6
#include<avr/io.h>
7
#include<stdint.h>
8
#include<avr/interrupt.h>
9
10
//#include <stdbool.h>
11
volatileuint32_tmillis=0;
12
13
intModus=0;
14
15
//DEBOUNCE
16
uint32_tDebStamp=0;
17
intDebTime=50;
18
19
20
/* DEBOUNCE FUNKTION Pinabfrage Vorher Verwendung
21
Zustand = 1 -> Taster gedrückt, war nicht gedrückt 1 0 Tastendruck
22
Zustand = 2 -> Taster gedrückt, war gedrückt 1 1 Während Taste gehalten wird
23
Zustand = 3 -> Taster nicht gedrückt, war gedrückt 0 1 Tastenloslassen
24
Zustand = 4 -> Taster nicht gedrückt, war nicht gedrückt 0 0 Nachdem Taste losgelassen wurde
Solange die Variablen in der Funktion als static deklariert sind,
können sie gar nicht anders als sich gegenseitig zu beeinflussen.
Damit die Variablen sich nicht gegenseitig beeinflussen, müssen sie
lokal (d.h. auf dem Stack) angelegt sein, und sie dürfen auf keinen Fall
static sein.
Sollen die Variablen irgendwelche Werte über mehrere nacheinander
ausgeführte Funktionsaufrufe behalten, dann musst Du für jede "Instanz"
der Funktion (d.h. unterschiedliche Taster) eigene Variablen anlegen.
Das könntest Du tun, indem Du die Variablen in einer Struktur
zusammenfasst und diese jeweils an der Stelle zur Verfügung stellst, wo
Du die Funktion aufrufst; der Funktion übergibst Du dann einen Pointer
auf die jeweilige zum Taster passende Struktur.
Stichwort: Objektorientiert programmieren. Geht grundsätzlich auch in C
indem man der objekt-spezifischen Funktion einen Zeiger auf die
Datenstruktur des Objektes übergibt. C++ und Java machen das unter der
Haube ebenso.
Rufus Τ. F. schrieb:> Solange die Variablen in der Funktion als static deklariert sind,> können sie gar nicht anders als sich gegenseitig zu beeinflussen.>> Damit die Variablen sich nicht gegenseitig beeinflussen, müssen sie> lokal (d.h. auf dem Stack) angelegt sein, und sie dürfen auf keinen Fall> static sein.
->Macht Sinn. Denn die Werte werden ja dann für Taster 1
zwischengespeichert, bis zum Aufruf der Funktion für Taster 2, die die
alten Werte weiter bentzen will. Mit static läufts also nicht.
> Sollen die Variablen irgendwelche Werte über mehrere nacheinander> ausgeführte Funktionsaufrufe behalten, dann musst Du für jede "Instanz"> der Funktion (d.h. unterschiedliche Taster) eigene Variablen anlegen.>> Das könntest Du tun, indem Du die Variablen in einer Struktur> zusammenfasst und diese jeweils an der Stelle zur Verfügung stellst, wo> Du die Funktion aufrufst; der Funktion übergibst Du dann einen Pointer> auf die jeweilige zum Taster passende Struktur.
Genau da liegt quasi meine Frage. Also müsste ich nach folgendem Schema
arbeiten:
Erster Schleifendurchlauf:
->Funktionsaufruf Debounce(PINC,PINC0)
->Lege Variablen an:
static int VorherPINC0;
static int UebergabeZustandPINC0;
->Benutze diese Variablen, mit genau diesem Namen
->Funktionsaufruf Debounce(PINC,PINC1)
->Lege Variablen an:
static int VorherPINC1;
static int UebergabeZustandPINC1;
->Benutze diese Variablen, mit genau diesem Namen
Zweiter Schleifendurchlauf:
->Funktionsaufruf Debounce(PINC,PINC0)
->Lege KEINE neuen Variablen an und arbeite mit den alten Werten für:
VorherPINC1
UebergabeZustandPINC1
->Funktionsaufruf Debounce(PINC,PINC1)
->Lege KEINE neuen Variablen an und arbeite mit den alten Werten für:
VorherPINC1
UebergabeZustandPINC1
Frage wäre jetzt:
-Wie erzeuge ich die Variablen mit Name in Abhängigkeit von
DebPort/DebButton
-Wie verhindere ich ein neu anlegen der Variablen (sollte über static
gegeben sein, richtig?)
Du musst die Variablen des Zustandes außerdem initialisieren. In deinem
Fall sind diese 0, weil sie static Variablen sind (C Standard). Sobald
diese auf dem Stack liegen ist das nicht mehr der Fall.
Ich würde außerdem für die Zustände ein enum verwenden. Im Prinzip ist
es das gleiche, wie deine Zahlen, nur dass die Zahlen dann sprechende
Namen haben können und der Code besser verständlich ist.
Besonders speichersparend ist das vertical Counter Prinzip, d.h. jede
Taste entspricht einem Bit in der Variable (uint8_t für bis zu 8 Tasten,
uint16_t bis 16 Tasten usw.).
Die geringsten Seiteneffekte hat man, wenn man das Entprellen und Flanke
erkennen in den Timerinterrupt auslagert und das Main muß sich nur noch
die Tastenereignisse abholen. Dann kann das Main ruhig mal etwas länger
busy sein, ohne das Tastendrücke verloren gehen.
M.K. B. schrieb:> struct DebounceZustand> {> int Zustand;> int Vorher;> int UebergabeZustand;> };
Ist nicht so der Bringer, wenn man z.B. auf nem ATtiny13 mit 64 Byte
SRAM je Taste schon 6 Byte verbraucht.
Es ist viel einfacher, jeder einzelne Taster hat einen globalen Zustand,
da ist nicht mit "uebersprechen". Ferner verwendet man keine Debounce
procedure, sondern laesst den KeyProcess bei jedem Timer durch, und
setzt dort ein flag wenn sich etwas veraendert hat. Im main schaut man
obs Veraenderungen gab.
Stefan U. schrieb:> Die richtigen Stichwörter habe ich Dir gegeben. Aber anscheinend hast du> nicht nach Anleitung gesucht, sondern lieber hier nochmal (dumm)> gefragt.
Hi Stefan,
die Antwort habe ich angefangen zu verfassen, bevor du geantwortet hast.
Tatsächlich habe ich wohl 25min an meiner Antwort geschrieben, da ich
während des antwortens die Aussagen von rufus versucht habe
nachzuvollziehen. Als ich auf die Vorschau gegangen bin, ist mir deine
Antwort ins Auge gestochen, jedoch bin ich nicht drauf eingegangen, da
bei eisiger Kälte ein Kumpel vor der Türe auf mich gewartet hat :D
Normalerweise ist es nicht meine Art, eine Antwort einfach so zu
überlesen.
Ich werde jetzt die Antworten mal durchgehen, entsprechend
nachvollziehen und schauen, wie ich vorgehe.
Peter D. schrieb:> Ist nicht so der Bringer, wenn man z.B. auf nem ATtiny13 mit 64 Byte> SRAM je Taste schon 6 Byte verbraucht.
Das war doch nur ein Beispiel. Im Eingangspost wurde doch von mir extra
erwähnt, dass das so erstmal nur zum testen ist.
Ziel ist es auf jeden Fall, eine auf andere Programme gut adaptierbare
Lösung zu erstellen. Die Lösung mit den millis finde ich recht
ansprechend, da sie vielseitig, auch für andere Teile des Programmes,
verwendet werden kann.
Beste Grüße und danke für alle, die sich die Mühe machen, hier zu
antworten.
Sim J. schrieb:> Frage wäre jetzt:> -Wie erzeuge ich die Variablen mit Name in Abhängigkeit von> DebPort/DebButton
Das ist alles andere als trivial.
M.K. B. schrieb:> int Debounce (int DebPort, int DebButton, DebounceZustand* pZustand);
Wäre eine Möglichkeit, bloß der Aufrufoverhead steigt damit noch weiter
an (3 Argumente: Copy&Paste Fehlergefahr).
Ein andere wäre, den ganzen Port zu entprellen und die gewünschte Taste
per Bitmaske zu übergeben.
Der Hauptnachteil bleibt allerdings bestehen, daß Dir Tastendrücke
verloren gehen, sobald das Main beschäftigt ist. Daher trennt man
üblicher Weise Entprellen (Timerinterrupt) und Aktion (Main)
voneinander.
Peter D. schrieb:> Besonders speichersparend ist das vertical Counter Prinzip, d.h.> jede> Taste entspricht einem Bit in der Variable (uint8_t für bis zu 8 Tasten,> uint16_t bis 16 Tasten usw.).> Die geringsten Seiteneffekte hat man, wenn man das Entprellen und Flanke> erkennen in den Timerinterrupt auslagert und das Main muß sich nur noch> die Tastenereignisse abholen. Dann kann das Main ruhig mal etwas länger> busy sein, ohne das Tastendrücke verloren gehen.
Das mit dem Vertical Counter Prinzip klingt auf jeden Fall interessant.
Das werde ich mir bei Gelegenheit mal näher ansehen.
Sabberalot W. schrieb:> Es ist viel einfacher, jeder einzelne Taster hat einen globalen> Zustand,> da ist nicht mit "uebersprechen". Ferner verwendet man keine Debounce> procedure, sondern laesst den KeyProcess bei jedem Timer durch, und> setzt dort ein flag wenn sich etwas veraendert hat. Im main schaut man> obs Veraenderungen gab.
Quasi Taste beispielsweise alle 50ms abfragen, Zustand aller Taster in
Variable auslagern (zB nach Vertical Counter Prinzip in einem Byte) und
dann den Input in der Main entsprechend verarbeiten?
Peter D. schrieb:> M.K. B. schrieb:>> int Debounce (int DebPort, int DebButton, DebounceZustand* pZustand);>> Wäre eine Möglichkeit, bloß der Aufrufoverhead steigt damit noch weiter> an (3 Argumente: Copy&Paste Fehlergefahr).> Ein andere wäre, den ganzen Port zu entprellen und die gewünschte Taste> per Bitmaske zu übergeben.>> Der Hauptnachteil bleibt allerdings bestehen, daß Dir Tastendrücke> verloren gehen, sobald das Main beschäftigt ist. Daher trennt man> üblicher Weise Entprellen (Timerinterrupt) und Aktion (Main)> voneinander.
Copy&Paste Fehlergefahr...den muss ich mir merken :D
Hab mir jetzt ein bisschen Input verschafft und denke, dass ich die
Sache jetzt verstanden habe. Mir recht das erstmal so. Funktioniert
nämlich super und war ja das, was ich wollte. Obs im konkreten Fall des
Entprellens so sinnvoll ist, ist die andere Frage. Wenn der Speicher mal
knapp wird, kann ich immernoch "tunen".
Hier nochmal der aktuelle Code:
1
/* DEBOUNCE FUNKTION Pinabfrage Vorher Verwendung
2
Zustand = 1 -> Taster gedrückt, war nicht gedrückt 1 0 Tastendruck
3
Zustand = 2 -> Taster gedrückt, war gedrückt 1 1 Während Taste gehalten wird
4
Zustand = 3 -> Taster nicht gedrückt, war gedrückt 0 1 Tastenloslassen
5
Zustand = 4 -> Taster nicht gedrückt, war nicht gedrückt 0 0 Nachdem Taste losgelassen wurde
6
*/
7
8
//DEBOUNCE
9
uint32_tDebStamp=0;
10
intDebTime=50;
11
12
structDebounceStruct{//Erstelle eigenen Datentyp, welcher Zustand, Vorher und UebergabeZustand beinhaltet
Sim J. schrieb:
[...]
> So kann ich den Zustand in der Main bei Bedarf bequem abrufen über:> xxx = Debounce(PINC,PINC0, &Taster0);
OMG
Wenn das kein lupenreines Argument wider die C-Seuche ist, was denn
dann?
Das geht in Assembler nicht nur mit weniger Rechenzeit (das wäre ja
sowieso erwartbar), nein es geht auch noch mit deutlich weniger Zeilen
Code. Wo genau ist also hier der Vorteil des Einsatzes einer
(sogenannten) Hochsprache?
> Wenn das kein lupenreines Argument wider die C-Seuche ist,> was denn dann?
Es ist niemals eine gute Idee, eine schlechte Anwendung von einer Sache
als Gegenargument für die Sache zu verwenden. Falls du da anderer
Meinung bis, solltest du alle deine Schraubendreher wegwerfen, weil man
damit ziemlich schlechte Sachen anstellen kann.
> Wo genau ist also hier der Vorteil des Einsatzes einer> (sogenannten) Hochsprache?
Das weißt du längst, stell dich nicht so blöd, nur um hier zu
provozieren!