Forum: Projekte & Code Entprellen die 2^8te (in C)


von Sebastian Förster (Gast)


Lesenswert?

Hallo,

wollte mal meine Entprellung vorstellen. Warum, gibt doch schon 1000?

1. damit jemand anders vielleicht noch einen Fehler sieht, denn ich 
immer wieder übersehe!

wegen folgenden Vorteile:
- Ein und Ausschalten werden entprellt (sollte ja Standard sein =) )
- der C Code ist einfach gehalten
- der C Code ist ausreichend schnell (kommt natürlich auf das Programm 
an)
- schon verhandener Timer-Interrupt wird "mitbenutzt"
- denoch geschiet die Entprellung im Main Loop
- sollte die Taste nach 10 Jahren abgedroschen und ausgeschlackert sein,
  funktioniert die Entprellung trotzdem. Solange eine Bedingungen 
erfüllt
  ist: Die Abtastzeit hat mindestens die doppelte Frequenz des Prellens

Nachteile1: (klar gibts auch ;) )
- notwenigkeit eines Timer-Interrupts Zeit ca. kleiner als 1ms
  sonst kann es passieren das die Abtastzeit zu klein ist
- wenn der Controller gestartet wird und der ausgeschaltene Taster eine
  Funktion aufrufen soll, muss man beim Start etwas tricksen ;)
- den Entprellcode in einer Funktion auszulagern erscheint mir wenig 
sinnvoll
  ich benutz ihn direkt in der main loop
- man braucht 2 Byte RAM für einen Taster

So hier nun Ausschnitte aus dem Code:
1
#define E_B_Drehrichtung          0
2
#define E_B_NotAus       1
3
#define E_B_Start      2
4
#define E_B_Bremsen      3
5
6
//Taster Prellzeit ist abhänig vom Timerinterrupt
7
#define Prellzeit         20
8
9
//"sendet" steigende Flanke von Timer0 an die main...
10
volatile uint8_t timer_to_main=0;
11
12
ISR(TIMER0_OVF_vect) 
13
{
14
  //Tasterentprellung an die Main weiterleiten...
15
  timer_to_main=1;
16
        //hier restlicher Code
17
}
18
19
int main(void)
20
{
21
  //Zähl-Variablen für die Tastenentprellung
22
  uint8_t i_start=0;
23
  uint8_t i_bremsen=0;
24
  uint8_t i_drehrichtung=0;
25
26
       //Für Startschalter ein
27
       uint8_t Start_Schalter=0;
28
       //Für Bremstaster ein
29
       uint8_t Brems_Taster=0;
30
       //Für Drehrichtung R=0/L=1
31
       uint8_t Drehrichtung_Schalter=0;
32
33
  //Eingänge setzen 
34
  DDRB = 0x00;
35
  //Interner Pull Up Aktiv
36
  PORTB = 0xFF;
37
38
  //Timer 0 starten mit F_CPU / 64 
39
        //(ca jede Millisekunde Overflow Ereigniss) F_CPU = 16Mhz
40
  TCCR0 = 0b00000011;
41
  //Interrupt für Timer einschalten
42
  TIMSK |= (1<<TOIE0);
43
44
  //Da Drehrichtung nach dem Start eine Funktion ausführen muss,
45
  //wurd durch das 1 setzen der Drehrichtungsvariable
46
  //erzwungen, so das die Drehrichtungsfunktion aufgerufen wird.
47
  if((PINB & (1<<E_B_Drehrichtung))) Drehrichtung_Schalter=1;
48
        //Interrups starten
49
        sei();
50
51
        while(1) {
52
    //Timer steigende Flanke an main weitergeleitet
53
    //Atomarer Zugriff
54
    cli();
55
    if(timer_to_main) {
56
      //bereit machen für nächsten Aufruf
57
      timer_to_main=0;
58
59
      sei();
60
61
      //Zuerst mit der Drehrichtung die Schütze einschalten und später
62
      //die Schalterstellung des Starttasters abfragen
63
      //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
64
      if(!(PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung<=Prellzeit) i_drehrichtung++;
65
      //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
66
      else if((PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung>=1) i_drehrichtung--;
67
68
      //wenn entprellung abgeschlossen und Schalter gedrückt
69
      if(i_drehrichtung>Prellzeit && !Drehrichtung_Schalter) {
70
        Drehrichtung_Schalter = 1;
71
        //Drehrichtung umschalten
72
        Drehrichtung();
73
      }
74
      //wenn entprellung abgeschlossen und Schalter nicht gedrückt
75
      if(i_drehrichtung<1 && Drehrichtung_Schalter) {
76
        Drehrichtung_Schalter = 0;
77
        //Drehrichtung umschalten
78
        Drehrichtung();
79
      }
80
81
      //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
82
      if(!(PINB & (1<<E_B_Start)) && i_start<=Prellzeit) i_start++;
83
      //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
84
      else if((PINB & (1<<E_B_Start)) && i_start>=1) i_start--;
85
86
      //wenn entprellung abgeschlossen und Schalter gedrückt
87
      if(i_start>Prellzeit && !Start_Schalter)  {
88
        Start_Schalter = 1;
89
        Reglerfreigabe = Regler_Start(Start_Schalter, Brems_Taster);
90
      }
91
      //wenn entprellung abgeschlossen und Schalter nicht gedrückt
92
      if(i_start<1 && Start_Schalter)  {
93
        Start_Schalter = 0;
94
        Reglerfreigabe = Regler_Stop();
95
      }
96
97
      //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
98
      if(!(PINB & (1<<E_B_Bremsen)) && i_bremsen<=Prellzeit) i_bremsen++;
99
      //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
100
      else if((PINB & (1<<E_B_Bremsen)) && i_bremsen>=1) i_bremsen--;
101
102
      //wenn entprellung abgeschlossen und Taster gedrückt
103
      if(i_bremsen>Prellzeit && !Brems_Taster) {
104
        Brems_Taster = 1;
105
      }
106
      //wenn entprellung abgeschlossen und Taster nicht gedrückt
107
      if(i_bremsen<1 && Brems_Taster)  {
108
        Brems_Taster = 0;
109
      }
110
111
                        //Hier weitere Dinge die man jeden Xten Interrupt tun möchte,
112
                        // ohne Interruptzeiten zu blockieren
113
        
114
    }
115
    //Interrupts ein
116
    sei();
117
         }
118
         return 0;
119
}

Laut AVR Studio braucht die reine Tasterabfrage für 3 Taster ca 65 
Cycles und dies auch nur, wenn ein Timerinterrupt ausgelöst wurde.

Das ausschalten der Interrupts kann man eventuell weglassen, da es nur 
eine 8 Bit Variable ist.

Der Code lief bei mir auf einem ATMEGA32.

Ich hoffe ihr müsst nur noch die Headers einbinden und dann läuft er... 
hab ihn jetzt nur rauskopiert und zusammengestückelt. So noch nicht 
getestet...

Anregungen und Kritik ausdrücklich erwünscht...

Grüße

Sebastian Förster

von Sebastian Förster (Gast)


Lesenswert?

Achso... Wenn der Timerinterrupt zu schnell ist, und die Entprellzeit 
255 übersteigen würde. Dann könnte man logischerweise folgendes tun:
1
ISR(TIMER0_OVF_vect) 
2
{
3
  static uint8_t i=255;
4
5
  //Tasterentprellung an die Main weiterleiten...
6
  if(!i) {
7
     timer_to_main=1;
8
     i=255;
9
  }
10
  i--;
11
12
  //hier restlicher Code
13
}

bzw. das ganze mit 16 Bit...

grüße

Basti

von Peter D. (peda)


Lesenswert?

Sebastian Förster schrieb:

> - man braucht 2 Byte RAM für einen Taster

Das ist viel.
Wie wärs mit 4 Byte für 8 Taster?


> Laut AVR Studio braucht die reine Tasterabfrage für 3 Taster ca 65
> Cycles und dies auch nur, wenn ein Timerinterrupt ausgelöst wurde.

Das ist viel.
Wie wärs mit 20 Zyklen für 8 Taster?


http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29


Peter

von Peter D. (peda)


Lesenswert?

Sebastian Förster schrieb:
> - den Entprellcode in einer Funktion auszulagern erscheint mir wenig
> sinnvoll
>   ich benutz ihn direkt in der main loop

Ich finde, es macht viel Sinn, das Main möglichst übersichtlich zu 
halten, d.h. allen Ballast, der nicht zum Main gehört, in Funktionen 
auszulagern.

Z.B. würde sich dieser Wust:
1
>       //Zuerst mit der Drehrichtung die Schütze einschalten und später
2
>       //die Schalterstellung des Starttasters abfragen
3
>       //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
4
>       if(!(PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung<=Prellzeit) i_drehrichtung++;
5
>       //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
6
>       else if((PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung>=1)
7
> i_drehrichtung--;
8
> 
9
>       //wenn entprellung abgeschlossen und Schalter gedrückt
10
>       if(i_drehrichtung>Prellzeit && !Drehrichtung_Schalter) {
11
>         Drehrichtung_Schalter = 1;
12
>         //Drehrichtung umschalten
13
>         Drehrichtung();
14
>       }
15
>       //wenn entprellung abgeschlossen und Schalter nicht gedrückt
16
>       if(i_drehrichtung<1 && Drehrichtung_Schalter) {
17
>         Drehrichtung_Schalter = 0;
18
>         //Drehrichtung umschalten
19
>         Drehrichtung();
20
>       }

in diese 2 Zeilen auflösen:
1
         Drehrichtung_Schalter = !!(key_state & 1<<E_B_Drehrichtung);
2
         Drehrichtung();

Zur Erklärung:
Meine Entprellfunktion stellt alle 8 Tasten entprellt in der Variable 
"key_state" zur Verfügung.


Peter

von Sebastian Förster (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Peter,

Peter Dannegger schrieb:

> Ich finde, es macht viel Sinn, das Main möglichst übersichtlich zu
> halten, d.h. allen Ballast, der nicht zum Main gehört, in Funktionen
> auszulagern.

Ja, darum stand der Text unter Nachteile und ich halte auch viel davon 
die main sauber zu halten ;)

Man kann das ganze schon als Funktion zusammen fassen... aber das 
überlass ich jedem selbst. Der Funktion müssten 3 Parameter übergeben 
werden und einen davon als Pointer und man bekommt eine returned... Ob, 
dass jetzt so viel besser ist, soll jeder selbst entscheiden.


Ich hab mir übrigens deine Entprellroutine vorher auch angeguckt. Und es 
ist wirklich nen sehr ausgefeilter Code und wenn ich nen Tastenfeld 
hätte und sehr viel Entprellen müsste, dann würd ich den sicherlich auch 
verwenden.
Ich für meinen Teil, hab aber ne einfache Lösung gesucht !ohne! 
Bitoperationen auszuführen. Natürlich könnte ich auch nur ein Byte pro 
Variable benutzen!!! Die ersten 5 Bit zähle ich bis 20 hoch und das MSB 
sagt mir dann ob der Taster gesetzt ist...
Aber möcht ich das? Jedenfalls nicht für 3 Taster...

Außerdem kann man so, mehr als 2 Taster gleichzeitig entprellen (obs 
gebrauch wird, ist ne andere Frage) und ich kann Taster entprellen die 
über 40ms Prellen...
@Peter Ich hoffe ich hab deinen Code jetzt richtig interpretiert


Mir ist noch aufgefallen das man die If Abfragen besser schachteln kann 
und so nochmal bis zur hälfte der Zeit sparen kann:
1
      if(!(PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung<=Prellzeit) {
2
        i_drehrichtung++;
3
        //wenn entprellung abgeschlossen und Schalter gedrückt
4
        if(i_drehrichtung>Prellzeit && !Drehrichtung_Schalter) {
5
          Drehrichtung_Schalter = 1;
6
          PORTC |= (1<<A_C_K2);
7
          PORTC &= ~(1<<A_C_K1);
8
        }
9
      }

jetzt sollte man aber die Variablen zum Zählen wie folgt aufrufen:
1
  //Zähl-Variablen für die Tastenentprellung
2
  uint8_t i_start=1;
3
  uint8_t i_bremsen=1;
4
  uint8_t i_drehrichtung=1;


Hab mal nen Sourcecode gebastelt und angehangen...


MfG

Basti

von Peter D. (peda)


Lesenswert?

Sebastian Förster schrieb:

> Ich hab mir übrigens deine Entprellroutine vorher auch angeguckt. Und es
> ist wirklich nen sehr ausgefeilter Code und wenn ich nen Tastenfeld
> hätte und sehr viel Entprellen müsste, dann würd ich den sicherlich auch
> verwenden.

Nö, der lohnt sich schon ab einer Taste. Daß nebenbei die 7 anderen Pins 
mit entprellt werden, kostet nix extra.
Schau mal ins Assemblerlisting, wie wenig Code erzeugt wird.


> Ich für meinen Teil, hab aber ne einfache Lösung gesucht !ohne!
> Bitoperationen auszuführen.

Nö, Bitoperation sind aus CPU-Sicht das einfachste, was es gibt.
Deshalb sind sie ja auch so schnell und effizient.


> Natürlich könnte ich auch nur ein Byte pro
> Variable benutzen!!! Die ersten 5 Bit zähle ich bis 20 hoch und das MSB
> sagt mir dann ob der Taster gesetzt ist...

Viel zu kompliziert.


> Außerdem kann man so, mehr als 2 Taster gleichzeitig entprellen (obs
> gebrauch wird, ist ne andere Frage) und ich kann Taster entprellen die
> über 40ms Prellen...

Kein Problem, die 1..8 Taster werden gleichzeitig und unabhängig 
entprellt.


> @Peter Ich hoffe ich hab deinen Code jetzt richtig interpretiert

Ich glaube nicht.

Der besondere Vorteil ist, daß Tastendrücke gemerkt werden.
Mich regt das immer furchtbar auf, wenn man bei kommerziellen Geräten 
immer erst warten muß, bis das Programm soweit ist, ehe man die nächste 
Taste drücken darf.


Peter

von Sebastian Förster (Gast)


Lesenswert?

Ich glaub wir reden aneinander vorbei...

Dein Code ist mit sicherheit sehr schnell... das er 8 Tasten schneller 
entprellt als ich eine, stell ich auch nicht infrage.
Das der Prozessor lieber Bitzustände behandelt als Bytezustände 
auszuwerten ist mir auch bewusst ;)

Ich wollte einen Entprellcode schreiben, die einfach nachvollzieh und 
lesbar ist. Dabei ist es mir egal ob ich 1% der CPU Zeit beanspruche 
oder 0,1%... =)

> Der besondere Vorteil ist, daß Tastendrücke gemerkt werden.
> Mich regt das immer furchtbar auf, wenn man bei kommerziellen Geräten
> immer erst warten muß, bis das Programm soweit ist, ehe man die nächste
> Taste drücken darf.

Versteh ich jetzt nicht, erklär mal bitte genauer...

MfG

Basti

von Peter D. (peda)


Lesenswert?

Sebastian Förster schrieb:
>> Der besondere Vorteil ist, daß Tastendrücke gemerkt werden.
>> Mich regt das immer furchtbar auf, wenn man bei kommerziellen Geräten
>> immer erst warten muß, bis das Programm soweit ist, ehe man die nächste
>> Taste drücken darf.
>
> Versteh ich jetzt nicht, erklär mal bitte genauer...

Im Timerinterrupt wird das Tastenbit gesetzt beim Übergang von 
Losgelassen nach Gedrückt (Flanke). Und das Main kann es später 
auswerten, auch wenn Du die Taste schon wieder losgelassen hast.

Bei der Flankenerkennung im Main geht dagegen das Drücken verloren, wenn 
das Main erst noch ne andere Task beenden muß.
Du bist also darauf angewiesen, daß die Mainloop immer schnell genug 
durchlaufen wird. Oder der Benutzer drückt, nichts passiert und er 
ärgert sich.


Peter

von Peter D. (peda)


Lesenswert?

Sebastian Förster schrieb:
> Ich wollte einen Entprellcode schreiben, die einfach nachvollzieh und
> lesbar ist.

Ob ein Code nachvollziehbar ist, liegt ganz im Auge des Betrachters.

Ich gebe zu, daß man für meinen Code etwas Verständnis von 
Logikoperatoren (AND, OR, XOR, NOT) haben sollte. Ideal ist, wenn man 
mal was mit TTL-ICs oder PALs selber entwickelt hat.

Der Trick ist, das Logikoperatoren immer bitweise arbeiten. D.h. man muß 
sich in jedem Byte nur ein Bit (z.B. Bit 0) ansehen, um den Code zu 
verstehen. Dann kann man es im AVRStudio schnell mal simulieren.

Wenn man dagegen die kompletten Bytes betrachtet, versteht man natürlich 
garnichts. Sie sind ja die Summe aus allen 8 Tastenbetätigungen.


Peter

von Sebastian Förster (Gast)


Lesenswert?

Ja stimmt, wenn natürlich die main nicht jede ms durchläuft, dann wird 
ein Tasterdruck nicht so schnell erkannt.
Das ist dann wieder Geschmackssache. Ob man den Code, lieber gleich im 
Timerinterrupt abarbeitet oder ob man sich sagt, dass Tasteneingabe eher 
eine sekundäre Aufgabe ist, liegt dann am Programmierer.


Ja genau Peter... es ist immer ne Frage wo man her kommt... Ich hab 
früher viel Anwendungssoftware in C für Windows geschrieben. Wenn ich 
heute noch kleine Programm in Visual Basic schreibe, dann denk ich mir 
auch: Wo ist den hier der Pointer? Wie unwirtschaftlich ist das denn?
Und wenn du aus der ASM Ecke kommst, dann siehst du das auch mit ganz 
andere Augen!
Aber das soll ja jetzt keine Grundsatzdiskusion über Programmiersprachen 
werden =)

Grüße

Basti

von Peter D. (peda)


Lesenswert?

Um mal auf Deinen Code zurück zu kommen, vielleicht könntest Du mal 
einen einfachen compilierbaren Code as Anhang posten, z.B. 2 Tasten und 
2 LEDs, jede Taste toggled ihre LED. Dann könnte man ihn mal 
ausprobieren.

Das ist ja mit dem bisherigen Code nicht möglich. Daher kann man nicht 
sagen, ob er überhaupt funktioniert.


Peter

von Sebastian Förster (Gast)


Lesenswert?

Hab ich schon... einfach weiter oben nach ner Preller.c Datei ausschau 
halten!

von Mikes (Gast)


Lesenswert?

>Wie wärs mit 4 Byte für 8 Taster?

key_state;key_press;key_rpt;ct0;ct1;rpt;

sind das nicht 6?

von Peter D. (peda)


Lesenswert?

Mikes schrieb:
> key_state;key_press;key_rpt;ct0;ct1;rpt;
>
> sind das nicht 6?

8 Tasten entprellen: 3 Byte SRAM
+ Flanke Gedrückt: 4 Byte
+ Repeat: 6 Byte
+ Flanke Losgelassen: 7 Byte


Peter

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.