Forum: Mikrocontroller und Digitale Elektronik Entprellung mit Dannegger-Code auf unterschiedlichen Ports


von M.B. (Gast)


Lesenswert?

Hallo Leute,

ich beutze zur Entprellung meiner taster den genialen Code von Peter 
Dannegger. Ich habe u.a. gestern den ganzen Tag hier im Forum verbracht 
um mich nochmal in den Code einzuarbeiten.

Ich habe nun bei mir Taster an unterschiedlichen Ports (Port C und Port 
B). Jetzt versuche ich die beiden Ports in den Code von PD zu 
verbasteln. Ich bin sicher das ich das hier mal gelesen habe, aber ich 
finde es nicht mehr.
Ich muss irgendwie die unterschiedlichen Ports mit den entsprechenden 
Pins mit "KEY_PIN" verheiraten. Aber ich weiß nicht mehr wie das geht.

Vielleicht hilft mir mal jemand auf die Sprünge oder gibt mir den Link 
zum entsprechendem Thema.
1
/* C-Code für ATmega 88 */
2
3
[...]
4
//#define KEY_PIN    PINB // Version PD
5
#define KEY_PINB    PINB
6
#define KEY_PINC    PINC
7
8
#define _Taster1    0x01    //PB0
9
#define _Taster2    0x02    //PC1
10
11
[...]
12
13
ISR(TIMER2_OVF_vect)
14
{
15
  static uint8_t ct0, ct1;
16
  uint8_t i;
17
18
  TCNT2 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
19
20
//  i = key_state ^ ~KEY_PIN;           // key changed ? PD's Version
21
  i = key_state ^ ~((KEY_PINB & _Taster1) | (KEY_PINC & _Taster2)) // wäre jetzt mein Ansatz
22
// aber stimmt die Richtung, in die ich mich bewege?
23
//Ich könnte mir auch einen Zwischenschritt über eine Variable vorstellen:
24
25
KEY_PIN_neu = ((PORTB |= _Taster1) | (PORTC |= _Taster2));
26
i = key_state ^ ~KEY_PIN_neu;
27
28
[...]

Könnt ihr das nachvollziehen?

von ziegel (Gast)


Lesenswert?

Hallo, hab das mal so gelöst (Codeteile hängen zusammen als Teil der 
Interrupt-Service-Routine, hier nur vereinzelt zum Kommentieren):

Erstmal Portrichtung für die Taster setzen. Bei mir in der 
Interrupt-Service-Routine selber, da die Port-Pins auch als 
LCD-Datenausgang genutzt werden, das delay ist dann nötig für stabile 
Verhältnisse beim Einlesen:
1
  KEY_DIR_RS &= ~ALL_KEYS;                // Portrichtung setzen auf Eingang
2
  KEY_PORT_RS |= ALL_KEYS;                // PullUp's Ein
3
  KEY_PORT_RS &= ~KEY_PIN_RS;             // Tasten an RS abfragen: RS auf low ziehen
4
  _delay_us(2);    //2us

Als nächstes 3 Digitale Inputs einlesen (DIN), liegen auf anderem Port 
als die Tasten, Portrichtung ist anderswo gesetzt:
1
  din_state = (~DIN_PIN_C )& 14;            // DIN Bits 1,2,3 einlesen,  ~ da Optokoppler gegen GND und interner Pull-Up-Widerstand ein, Rest rausmaskieren

Hier noch ne kleine Notaus-Abfrage, die direkt ohne Entprellung aus der 
ISR heraus die Maschine abstellt:
1
  if ( din_state & (D_IN_2) ) {            // wenn D_IN_2 (NOTAUS) gesetzt,
2
    D_OUT_1_off;
3
  }

Jetzt die eingelesenen DIN-Zustände mit den Taster-Zuständen in ein Bit 
verheiraten (hier liegen die einzelnen Bits günstig, falls nicht, dann 
noch zurechtschieben):

1
    i = key_state ^ ( din_state |((~KEY_PIN) & 240));  // key changed ? ~ da Signal gegen GND und interner Pull-Up-Widerstand ein,
2
                              // DIN sind Bits 1,2,3, Tasten sind Bits 4,5,6,7, dort Rest rausmaskieren

dann der "normale" Peter Dannegger Code (4-fach Entprellung, hier mit 
10ms Takt):

1
    ct0 = ~( ct0 & i );              // reset or count ct0, Signale CT0 und CT1 sind invertiert
2
    ct1 = ct0 ^ (ct1 & i);           // reset or count ct1
3
    i &= ct0 & ct1;                  // count until roll over ?
4
    key_state ^= i;                  // then toggle debounced state
5
    key_press |= key_state & i;      // 0->1: key press detect, "speichern"*/

Und zuletzt die Tasten-Ports wieder auf Ausgang schalten:

1
  KEY_DIR_RS |= ALL_KEYS;            // Portrichtung setzen auf Ausgang
2
  KEY_PORT_RS |= ~ALL_KEYS;          // PullUp's aus

Hoffe, das hilft weiter, Gruß!

von Oliver (Gast)


Lesenswert?

1
//  i = key_state ^ ~KEY_PIN;           // key changed ? PD's Version
2
  i = key_state ^ ~((KEY_PINB & _Taster1) | (KEY_PINC & _Taster2)) // wäre jetzt mein Ansatz

Das müsste so funktionieren. An Stelle eines EingangsPorts setzte du dir 
das Byte aus zwei Ports zusammen. Vorassetzung ist natürlich, das deine 
Tasten an "aufeinanderfolgenden" Bist hängen - mit jeweisl einer Taste 
an Bit 0 von PORTB und PORTC ginge das nicht so einfach.
1
KEY_PIN_neu = ((PORTB |= _Taster1) | (PORTC |= _Taster2));
2
i = key_state ^ ~KEY_PIN_neu;

????

Damit komme ich nicht klar. Dazu finde ich auch keine Entsprechung im 
Originalcode, zumindest nicht in dem hier:
http://www.mikrocontroller.net/attachment/highlight/36986

Abgesehen davon hasse ich solche Kontruktionen mit Zuweiseungen 
innerhalb der rechten Seite eines Ausdrucks. Das ist eher was für den 
Obfuscated C Contest.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Oliver schrieb:

> ????
>
> Damit komme ich nicht klar. Dazu finde ich auch keine Entsprechung im
> Originalcode, zumindest nicht in dem hier:
> http://www.mikrocontroller.net/attachment/highlight/36986

Ist doch simpel.
Das Einlesen des kompletten Tastenports geschieht im Originalcode hier
1
  i = key_state ^ ~KEY_PIN;    // key changed ?

Anstatt das Pin Register direkt auszulesen, kannst du das auch so machen
1
  tmp = KEY_PIN;
2
  i = key_state ^ ~tmp;
3
  ...

Noch hat sich nichts geändert.

Jetzt sagt aber kein Mensch, dass du tmp als ganzes von KEY_PIN befüllen 
musst. Du kannst das auch nach Lust und Laune aus mehreren Pin Registern 
zusammensetzen ... komplette Pins nehmen ... einzelne Bits ausmaskieren, 
was immer du willst. Hauptsache jeder Taster hat in tmp sein eigenes Bit
1
  tmp = ( PINB & 0x01 ) | ( PINC & 0x02 ) | ( PINC & 0x01 ) << 2;
2
  i = key_state ^ ~tmp;
3
  ...

Wenn man es hochgestochen ausdrücken will, dann baust du dir damit ein 
'virtuelles Pin Register' zusammen, in dem jedes Bit einem Taster 
entspricht.

>
> Abgesehen davon hasse ich solche Kontruktionen mit Zuweiseungen
> innerhalb der rechten Seite eines Ausdrucks.

Dann machs ganz einfach nicht :-)

von M.B. (Gast)


Lesenswert?

Zunächst erst mal vielen Dank für Eure Antworten.

Wie ich sehe, kann ich es so ähnlich machen, wie ich es vorgeschlagen 
hatte:
Mein Vorschlag war:
1
KEY_PIN_neu = ((PORTB |= _Taster1) | (PORTC |= _Taster2));
2
i = key_state ^ ~KEY_PIN_neu;


kbuchegg's Vorschlag war:
1
tmp = ( PINB & 0x01 ) | ( PINC & 0x02 ) | ( PINC & 0x01 ) << 2;
2
  i = key_state ^ ~tmp;

Aus
1
PORTB |= 0x01
 muss ich nur
1
PINB & 0x01
 machen?

von Oliver (Gast)


Lesenswert?

Ich hab das schon verstanden,

bis darauf, daß dieser Teil hier alternativ zu den Zeilen davor gedacht 
war:
1
//Ich könnte mir auch einen Zwischenschritt über eine Variable vorstellen:
2
3
KEY_PIN_neu = ((PORTB |= _Taster1) | (PORTC |= _Taster2));
4
i = key_state ^ ~KEY_PIN_neu;

Beides nacheinander, so wie es oben wörtlich steht, macht es eben keinen 
Sinn.

>> Abgesehen davon hasse ich solche Kontruktionen mit Zuweiseungen
>> innerhalb der rechten Seite eines Ausdrucks.

>Dann machs ganz einfach nicht :-)

Tue ich auch nicht :-)

Oliver

von ziegel (Gast)


Lesenswert?

Hallo nochmal,

das mit den nicht "aufeinanderfolgenden" Bits lässt sich leicht durch 
Bit-Schieben (>>) lösen.

Bei mir habe ich das Zusammenführen über eine Zwischenvariable din_state 
gelöst, da ich noch eine Notaus-Funktion brauche, die direkt reagiert.

Ihr müsst darauf achten, alle nicht relevanten Bits rauszumaskieren (die 
werden ja sonst wie gedrückte Tasten interpretiert), also definierte 
Zustände zu schaffen. Und genau aufpassen, welche Bits/Bytes wann 
negiert (~) sein wollen.

Das Konstrukt KEY_PIN_neu = ... funktioniert so daher nicht.

hier noch meine Defines zu obigem Code:
1
//D-IN Ports
2
  #define  DIN_PORT_C   PORTC
3
  #define  DIN_DIR_C    DDRC
4
  #define  DIN_PIN_C    PINC
5
  #define D_IN_0      (1 << PC3)
6
  #define D_IN_1      (1 << PC2)
7
  #define D_IN_2      (1 << PC1)  // NOTAUS
8
9
// KEY-Ports
10
  #define KEY_PIN   PIND
11
  #define KEY1      (1 << PD4)
12
  #define KEY2      (1 << PD5)
13
  #define KEY7      (1 << PD6)
14
  #define KEY8      (1 << PD7)
15
16
  #define KEY_PORT_RS   PORTD
17
  #define KEY_DIR_RS    DDRD
18
  #define KEY_PIN_RS    (1 << PD3)  // LCD_RS_PD3
19
20
  #define ALL_KEYS      (KEY1 | KEY2 | KEY7 | KEY8)

Den KEY_..._RS setze ich, da wie geschrieben die Tasten mit der 
LCD-Anzeige gemultiplext sind. Ich habe eigentlich noch eine 
Repeat-Funktion für einzelne Taster eingebaut, die ich hier im Code 
weggelassen habe.

Gruß!

von M.B. (Gast)


Lesenswert?

Hallo zusammen,

ich hab es jetzt gerade mal ausprobiert!

Mit folgender Version von kbuchegg funktioniert es:
1
tmp = ( PINB & 0x01 ) | ( PINC & 0x02 ) | ( PINC & 0x01 ) << 2;
2
  i = key_state ^ ~tmp;

Ich habe aber es folgendermaßen abgewandelt: Ist das Gleiche nur ohne 
Zwischenschritt tmp
1
i = key_state ^~((KEY_PINB & _Taster1) | (KEY_PINC & _Taster2));

>> Oliver wrote:
>> Abgesehen davon hasse ich solche Kontruktionen mit Zuweisungen
>> innerhalb der rechten Seite eines Ausdrucks.

Du musst es ja nicht übernehmen. Es hat halt jeder seinen eigenen 
Programmierstil. ich arbeite gerne mit "sprechenden Variablen" In diesem 
Fall muss man aber wissen was man tut: nicht das die beiden Taster auf 
dem gleichen Bit liegen.

Viele Grüße und Danke nochmal

von M.B. (Gast)


Lesenswert?

Ach noch was:

Falls diese Schreibweise Nebenwirkungen hat, die in meinem Fall nicht 
bzw noch nicht aufgetreten sind, bitte kommentieren und posten!

von Ahnungslos_0815 (Gast)


Lesenswert?

@  M.B. (Gast)

>ich beutze zur Entprellung meiner taster den genialen Code von Peter
>Dannegger.

Sorry mal, bin hier noch relativ neu, wo finde ich den Code?

von Oliver (Gast)


Lesenswert?

>Falls diese Schreibweise Nebenwirkungen hat, die in meinem Fall nicht
>bzw noch nicht aufgetreten sind, bitte kommentieren und posten!

1
KEY_PIN_neu = ((PORTB |= _Taster1) | (PORTC |= _Taster2));

Nun ja, du schaltest mit dieser Zeile auch die Pull-Ups der 
Tastereingänge ein. Das ist vermutlich sinnvoll, machst du das bewusst?

Oliver

von M.B. (Gast)


Lesenswert?

@ Ahnungslos_0815 (Gast)

Schau mal hier:
[[Beitrag "Universelle Tastenabfrage"]]

@ Oliver

Sollte natürlich PINB und PINC heißen und nicht PORT

Sorry, hatte mich vertippselt

von Peter D. (peda)


Lesenswert?

M.B. schrieb:
>
1
/* C-Code für ATmega 88 */
2
> //#define KEY_PIN    PINB // Version PD
3
> #define KEY_PINB    PINB
4
> #define KEY_PINC    PINC
5
> 
6
> #define _Taster1    0x01    //PB0
7
> #define _Taster2    0x02    //PC1
8
>   i = key_state ^ ~((KEY_PINB & _Taster1) | (KEY_PINC & _Taster2)) //
9
>

Das ist vollkommen korrekt so.


>
1
> KEY_PIN_neu = ((PORTB |= _Taster1) | (PORTC |= _Taster2));
2
>

Das ist Bullshit (auch mit PINB,PINC) und funktioniert daher nicht.


Peter

von Peter D. (peda)


Lesenswert?

ziegel schrieb:
> Hier noch ne kleine Notaus-Abfrage, die direkt ohne Entprellung aus der
> ISR heraus die Maschine abstellt:


Du bist sicher, daß der Notaus auch darauf reagieren muß, wenn jemand 
elektrostatisch aufgeladen dem Taster näher kommt oder bei sonstigen 
Störungen?

Du bist sicher, daß ein Notaus 30ms später wirklich eine Rolle spielt?


Peter

von ziegel (Gast)


Lesenswert?

Hallo,

@ Peter, das kommt natürlich auf die Anwendung an. Es handelt sich hier 
um einen schnell laufenden Antrieb, der µC realisiert über eine Rampe 
und Frequenzumrichter das langsame anlaufen und abbremsen. Notaus ist 
ein Öffner-Stromkreis gegen GND, der durch verschiedene Bedingungen 
unterbrochen wird. Prellen und statische Ladung sind hier also 
uninteressant. Es ist außerdem völlig egal, ob der Notaus zu oft 
Auftritt, wichtig ist, daß der Antrieb schellstmöglich gebremst wird, um 
Schäden zu vermeiden. Außerdem, um die Entprell-Diskussion anzuheizen, 
ich habe immerhin die Info, daß der Zustand aufgetreten ist, und das zu 
einem diskreten Zeitpunkt.
Mir ging es hier aber primär ums Prinzip, vor der "Entprellung" noch 
eine zusätzliche Abfrage zu realisieren. Sonst brauchts auch keine 
Zwischenvariable.

Und Vielen Dank für die hervorragende Entprell-Routine! Gruß!

von East (Gast)


Lesenswert?

Hallo,

ist zwar ein  bischen her, aber habe ein Problem das diese Routine auf 
Unterschiedlichen Ports nicht funzt. Habe soweit auch verstanden, wie 
das mit dem Maskieren der unterschiedlichen Ports funzt. Problem ist 
noch das in getkeypress() nur die variable des PINs (1 oder 2) an 
Keypress übergeben wird. Wenn jetzt aber gleiche >PINs aus 
unterschiedlichen Ports abgefragt werden sollen ( PINC1 oder PINB1 ), 
wie wird das dann in der getkeypress gemacht. Muss man dann nicht zwei 
variblen übergeben?

von Peter D. (peda)


Lesenswert?

East schrieb:
> Wenn jetzt aber gleiche >PINs aus
> unterschiedlichen Ports abgefragt werden sollen

Dann schiebe den einen Wert auf freie Bits oder nimm 16Bit Variablen:
1
uint16_t i = PINB | (PINC<<8);

von East (Gast)


Angehängte Dateien:

Lesenswert?

Könntest Du vielleicht mal einen Blick drauf werfen. Habe es zwar zum 
laufen gebracht. Aber das Programm macht faxen. Heisst wenn ich mit der 
Hand ohne Berührung über den MC fahre, löst das immerwieder einen 
Eingang aus.

von East (Gast)


Lesenswert?

Habe es etwas anders gelöst. Habe zwei Variblen für die Routine.

Vielen Dank schonmal im Voraus.

Dennis

von East (Gast)


Lesenswert?

Nach erfolgreicher Fehlersuche, habe ich vielleicht noch eine Frage.

Wie sieht nach der beschaltung der unterschiedlichen Ports, die Übergabe 
in getkeypress(x) aus?

Wird einfach nur wie zuvor, der Wert des Pins übergeben? Oder reicht 
einfach nur Name der Deklaration (_taster1) aus.?

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.