www.mikrocontroller.net

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


Autor: Sebastian Förster (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#define E_B_Drehrichtung          0
#define E_B_NotAus       1
#define E_B_Start      2
#define E_B_Bremsen      3

//Taster Prellzeit ist abhänig vom Timerinterrupt
#define Prellzeit         20

//"sendet" steigende Flanke von Timer0 an die main...
volatile uint8_t timer_to_main=0;

ISR(TIMER0_OVF_vect) 
{
  //Tasterentprellung an die Main weiterleiten...
  timer_to_main=1;
        //hier restlicher Code
}

int main(void)
{
  //Zähl-Variablen für die Tastenentprellung
  uint8_t i_start=0;
  uint8_t i_bremsen=0;
  uint8_t i_drehrichtung=0;

       //Für Startschalter ein
       uint8_t Start_Schalter=0;
       //Für Bremstaster ein
       uint8_t Brems_Taster=0;
       //Für Drehrichtung R=0/L=1
       uint8_t Drehrichtung_Schalter=0;

  //Eingänge setzen 
  DDRB = 0x00;
  //Interner Pull Up Aktiv
  PORTB = 0xFF;

  //Timer 0 starten mit F_CPU / 64 
        //(ca jede Millisekunde Overflow Ereigniss) F_CPU = 16Mhz
  TCCR0 = 0b00000011;
  //Interrupt für Timer einschalten
  TIMSK |= (1<<TOIE0);

  //Da Drehrichtung nach dem Start eine Funktion ausführen muss,
  //wurd durch das 1 setzen der Drehrichtungsvariable
  //erzwungen, so das die Drehrichtungsfunktion aufgerufen wird.
  if((PINB & (1<<E_B_Drehrichtung))) Drehrichtung_Schalter=1;
        //Interrups starten
        sei();

        while(1) {
    //Timer steigende Flanke an main weitergeleitet
    //Atomarer Zugriff
    cli();
    if(timer_to_main) {
      //bereit machen für nächsten Aufruf
      timer_to_main=0;

      sei();

      //Zuerst mit der Drehrichtung die Schütze einschalten und später
      //die Schalterstellung des Starttasters abfragen
      //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
      if(!(PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung<=Prellzeit) i_drehrichtung++;
      //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
      else if((PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung>=1) i_drehrichtung--;

      //wenn entprellung abgeschlossen und Schalter gedrückt
      if(i_drehrichtung>Prellzeit && !Drehrichtung_Schalter) {
        Drehrichtung_Schalter = 1;
        //Drehrichtung umschalten
        Drehrichtung();
      }
      //wenn entprellung abgeschlossen und Schalter nicht gedrückt
      if(i_drehrichtung<1 && Drehrichtung_Schalter) {
        Drehrichtung_Schalter = 0;
        //Drehrichtung umschalten
        Drehrichtung();
      }

      //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
      if(!(PINB & (1<<E_B_Start)) && i_start<=Prellzeit) i_start++;
      //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
      else if((PINB & (1<<E_B_Start)) && i_start>=1) i_start--;

      //wenn entprellung abgeschlossen und Schalter gedrückt
      if(i_start>Prellzeit && !Start_Schalter)  {
        Start_Schalter = 1;
        Reglerfreigabe = Regler_Start(Start_Schalter, Brems_Taster);
      }
      //wenn entprellung abgeschlossen und Schalter nicht gedrückt
      if(i_start<1 && Start_Schalter)  {
        Start_Schalter = 0;
        Reglerfreigabe = Regler_Stop();
      }

      //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
      if(!(PINB & (1<<E_B_Bremsen)) && i_bremsen<=Prellzeit) i_bremsen++;
      //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
      else if((PINB & (1<<E_B_Bremsen)) && i_bremsen>=1) i_bremsen--;

      //wenn entprellung abgeschlossen und Taster gedrückt
      if(i_bremsen>Prellzeit && !Brems_Taster) {
        Brems_Taster = 1;
      }
      //wenn entprellung abgeschlossen und Taster nicht gedrückt
      if(i_bremsen<1 && Brems_Taster)  {
        Brems_Taster = 0;
      }

                        //Hier weitere Dinge die man jeden Xten Interrupt tun möchte,
                        // ohne Interruptzeiten zu blockieren
        
    }
    //Interrupts ein
    sei();
         }
         return 0;
}

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

Autor: Sebastian Förster (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso... Wenn der Timerinterrupt zu schnell ist, und die Entprellzeit 
255 übersteigen würde. Dann könnte man logischerweise folgendes tun:
ISR(TIMER0_OVF_vect) 
{
  static uint8_t i=255;

  //Tasterentprellung an die Main weiterleiten...
  if(!i) {
     timer_to_main=1;
     i=255;
  }
  i--;

  //hier restlicher Code
}

bzw. das ganze mit 16 Bit...

grüße

Basti

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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/Entprellun...


Peter

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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:

>       //Zuerst mit der Drehrichtung die Schütze einschalten und später
>       //die Schalterstellung des Starttasters abfragen
>       //ist Pin gedrückt und Prellzeit noch nicht auf maximal, dann aufaddieren
>       if(!(PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung<=Prellzeit) i_drehrichtung++;
>       //ist Pin nicht gedrückt und Prellzeit noch nicht auf minimal, dann subtraieren
>       else if((PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung>=1)
> i_drehrichtung--;
> 
>       //wenn entprellung abgeschlossen und Schalter gedrückt
>       if(i_drehrichtung>Prellzeit && !Drehrichtung_Schalter) {
>         Drehrichtung_Schalter = 1;
>         //Drehrichtung umschalten
>         Drehrichtung();
>       }
>       //wenn entprellung abgeschlossen und Schalter nicht gedrückt
>       if(i_drehrichtung<1 && Drehrichtung_Schalter) {
>         Drehrichtung_Schalter = 0;
>         //Drehrichtung umschalten
>         Drehrichtung();
>       }

in diese 2 Zeilen auflösen:

         Drehrichtung_Schalter = !!(key_state & 1<<E_B_Drehrichtung);
         Drehrichtung();

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


Peter

Autor: Sebastian Förster (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
      if(!(PINB & (1<<E_B_Drehrichtung)) && i_drehrichtung<=Prellzeit) {
        i_drehrichtung++;
        //wenn entprellung abgeschlossen und Schalter gedrückt
        if(i_drehrichtung>Prellzeit && !Drehrichtung_Schalter) {
          Drehrichtung_Schalter = 1;
          PORTC |= (1<<A_C_K2);
          PORTC &= ~(1<<A_C_K1);
        }
      }

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


Hab mal nen Sourcecode gebastelt und angehangen...


MfG

Basti

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sebastian Förster (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sebastian Förster (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sebastian Förster (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab ich schon... einfach weiter oben nach ner Preller.c Datei ausschau 
halten!

Autor: Mikes (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wie wärs mit 4 Byte für 8 Taster?

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

sind das nicht 6?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.