Tastenentprellung seltsames Phänomen

Gast #4420745
Lesenswert?

Hallo, bin noch recht frisch in der Programmer-Scene. Habe ein Projekt 
begonnen, um eine Alarmanlage zu realisieren. Leider scheitert die 
Programmierung schon an der Entprellung der Taster. Habe erfolgreich ein 
I2C-LCD am laufen. Weiter benutze ich ein Arduinoboard Micro 32U4. Wenn 
dem im Anhang besagten Code auf den Controller schmeisse, habe ich ohne 
das ich einen Taster betätige eine Ausgabe eines Tasters am LCD 
(willkürlich).

Das Problem habe ich bis jetzt noch nicht eingrenzen können. Zudem nutze 
ich die Entrellroutine von Peter Dannegger.

Wäre Super wenn mir jemand helfen könnte. Vielleicht bin ich auch nur 
Blind.

Gruß,

East
Angehängte Dateien:
Gast #4420750
Lesenswert?

> Das Problem habe ich bis jetzt noch nicht eingrenzen können.

Reduziere dein Programm auf ein Minimum, also kein LCD ansteuern, 
sondern nur eine LED.

Beispiel: Bei jedem Tastendruck soll die LED toggeln.

Wenn das nicht klappt, beschreibe, was das Programm stattdessen tut und 
poste nochmal den reduzierten Quelltext.
Gast #4420790
Lesenswert?

Lötfehler schliesse ich definitiv aus. Da ich mit Multimeter 5V an allen 
eingänge messe. Und sobald ich diese betätige, werden alle Eingänge auf 
GND gezogen (0,61V).

Habe gerade mal nachgeschaut. Die Hochlaufschritte des Timers 0 sind bei 
meiner rechnung 7,8125 +0.5 für 10ms. Kann aber nicht sein. Wenn ich 
nachrechne um eine Konstante von 10ms (Zeitbasis) brauche, brauche ich 
bei einem 8-Bit Timer mit 8 MHZ bei Prescaler von 1024 eine TCNT von 
78,125.

Kann es sein 10e-3 keine 10ms sondern 1ms sind?
#4420924
Lesenswert?

east schrieb:
> Lötfehler schliesse ich definitiv aus. Da ich mit Multimeter 5V an allen
> eingänge messe. Und sobald ich diese betätige, werden alle Eingänge auf
> GND gezogen (0,61V).

Etwas ausschließen können alle, bei denen etwas nicht funktioniert, sehr 
gut. Das ist insbesondere deswegen interessant, da seltsame Phänomene 
immer vor dem PC oder der Schaltung sitzen.
Aber das ist eigentlich die falsche Aussage, denn das war eher als Wink 
mit dem Zaunpfahl gedacht. Dein Arduino hat überhaupt keine externen 
Pullups. Ich sehe in deiner Initialisierung aber auch nicht, daß die 
internen aktiviert werden. Dabei ist es zwar schleierhaft, wie du 5V 
messen kannst, aber sei's drum. Wo werden die Pullups eingeschaltet?
Moderator (Firma: Titel) Persönliche Seite #4420949
Lesenswert?

East schrieb:
> Wie soll ich sonst die 5V gegen den Eingang messen können?
Wieso misst du irgendwas gegen irgendeinen Eingang? Spannungen werden 
üblicherweise gegen GND gemessen.

East schrieb:
> Übrigens ist es nicht nur
Salamitaktik...

> Es lösen aber alle sporadisch aus.
Zeig mal 1. einen Schaltplan und 2. ein Foto von deinem Aufbau. Sonst 
ist das hier nur Stochern im Nebel...
Gast #4420955
Lesenswert?

Hallo,

Was soll den bei Dir innerhalb der
1
uint8_t get_key_press( uint8_t key_mask, uint8_t PORTx )
2
 KEYPIN = PORTx;
bewirken ?

Ich verwende Peter Danneggers Enprellroutine auch, aber etwas anders.
1
ISR( TIM0_COMPA_vect )                          // every 10ms
2
{
3
  static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;  // bei 1ms ist diese Zahl 16-Bit groß
4
  uint8_t i;
5

6
  i = key_state ^ ~KEY_PIN;                     // key changed ?
7
  ct0 = ~( ct0 & i );                           // reset or count ct0
8
  ct1 = ct0 ^ (ct1 & i);                        // reset or count ct1
9
  i &= ct0 & ct1;                               // count until roll over ?
10
  key_state ^= i;                               // then toggle debounced state
11
  key_press |= key_state & i;                   // 0->1: key press detect
12

13
  if( (key_state & REPEAT_MASK) == 0 )          // check repeat function
14
     rpt = REPEAT_START;                        // start delay
15
  if( --rpt == 0 ){
16
    rpt = REPEAT_NEXT;                          // repeat delay
17
    key_rpt |= key_state & REPEAT_MASK;
18
  }
19
}
1
uint8_t get_key_press( uint8_t key_mask )
2
{
3
  ATOMIC_BLOCK(ATOMIC_FORCEON){
4
    key_mask &= key_press;                      // read key(s)
5
    key_press ^= key_mask;                      // clear key(s)
6
  }
7
  return key_mask;
8
}
9

10

11
uint8_t get_key_rpt( uint8_t key_mask )
12
{
13
  ATOMIC_BLOCK(ATOMIC_FORCEON){
14
    key_mask &= key_rpt;                        // read key(s)
15
    key_rpt ^= key_mask;                        // clear key(s)
16
  }
17
  return key_mask;
18
}
19

20

21
uint8_t get_key_short( uint8_t key_mask )
22
{
23
  uint8_t i;
24

25
  ATOMIC_BLOCK(ATOMIC_FORCEON)
26
    i = get_key_press( ~key_state & key_mask );
27
  return i;
28
}
29

30

31
uint8_t get_key_long( uint8_t key_mask )
32
{
33
  return get_key_press( get_key_rpt( key_mask ));
34
}
35

36

37
uint8_t get_key_long_r( uint8_t key_mask )      // if repeat function needed
38
{
39
  return get_key_press( get_key_rpt( key_press & key_mask ));
40
}
41

42

43
uint8_t get_key_rpt_l( uint8_t key_mask )       // if long function needed
44
{
45
  return get_key_rpt( ~key_press & key_mask );
46
}
Gast #4420958
Lesenswert?

Den LCD_INIT() brauch ich, um das LCD zu clearen. Da das mit 
lcd_command(LCD_CLEAR) nicht hingehauen hatte. Ist eben halt bischen 
rumgebastelt. Daher sage ich auch, das ich noch ziemlich am Anfang 
stehe.

Das key_state und key_press auf 0 gesetzt werden, ist weil der sofort 
nach dem einschalten sonst in einem if (get_keypress()) reingeht. Warum 
habe ich auch noch nicht heraus gefunden.
Gast #4420959
Lesenswert?

Hallo Frank,

Frank schrieb:
> Was soll das sein:TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 +
> 0.5);      // preload for 10ms

Damit berechnet der Preprocessor den Timer0 Preloadwert im TimerO0verlow 
Modus.
Der Prescaler ist 1024, 10ms == 10e-3, +0.5 == aufrunden und dann
(256 - Zählerwert) berechnen.
Denn TimerOverlow zählt der Timer0 von von N bis 256 und bei 256 == 0 
wird ein Overflow Interrupt Request erzeugt.

Alles korrekt !
Gast #4420962
Lesenswert?

OK. Habe den Repeat entfernt, da ich den nicht brauche um meine Tasten 
abzufragen. Habe dies auch schon in funktionierenden Projekten gesehen.

Das mit dem ATOMICBLOCK??? Ist das das selbe wie CLI()? Weil ich ja mit 
cli() alle Interrupts stoppe.
Gast #4420963
Lesenswert?

Karl M. schrieb:
> Karl M. schrieb:
>> ISR( TIM0_COMPA_vect )
>
> East schrieb:
>> Das key_state und key_press auf 0 gesetzt werden, ist weil der sofort
>> nach dem einschalten sonst in einem if (get_keypress()) reingeht. Warum
>> habe ich auch noch nicht heraus gefunden.
>
> Die Antwort steht in der ISR( TIM0_COMPA_vect ) !
> Man muss halt C können.

Ich weiss auch das der Timmer 0 nach dem sei() in der main sofort 
startet. Aber warum löst getkeypress(), ohne ein zutun aus????
Gast #4420968
Lesenswert?

East schrieb:
> Karl M. schrieb:
>> Karl M. schrieb:
>>> ISR( TIM0_COMPA_vect )
>>
>> East schrieb:
>>> Das key_state und key_press auf 0 gesetzt werden, ist weil der sofort
>>> nach dem einschalten sonst in einem if (get_keypress()) reingeht. Warum
>>> habe ich auch noch nicht heraus gefunden.
>>
>> Die Antwort steht in der ISR( TIM0_COMPA_vect ) !
>> Man muss halt C können.
>
> Ich weiss auch das der Timmer 0 nach dem sei() in der main sofort
> startet. Aber warum löst getkeypress(), ohne ein zutun aus????

Es ist ja so, dass wenn der MC nach dem Einschalten startet, sofort im 
LCD eine Ausgabe des jeweiligen textes der Taster ausgibt. Ohne das 
dieser Betätigt wurde.
Gast #4420984
Lesenswert?

Ok East,

dann mache es richtig und verwende nur einen Port, dort kann man 8 Bits 
Abfragen und als Eingänge nutzen.

So wie es abgeändert wurde, wird es nicht funktionieren.

Es gibt einen Trick z.B zwei Ports zu verwenden.
Aber das passier alles nur in der Timer Interruptroutine und nur in 
der Zeile:
1
i = key_state ^ ~KEY_PIN;                     // key changed ?

East schrieb:
> Das habe ich hinzugefügt, da ich unterschiedliche POrts benutze.
Gast #4420990
Lesenswert?

Karl M. schrieb:
> Damit berechnet der Preprocessor den Timer0 Preloadwert im TimerO0verlow
> Modus.
> Der Prescaler ist 1024, 10ms == 10e-3, +0.5 == aufrunden und dann (256 -
> Zählerwert) berechnen.
> Denn TimerOverlow zählt der Timer0 von von N bis 256 und bei 256 == 0
> wird ein Overflow Interrupt Request erzeugt.
>
> Alles korrekt !

Mir geht es eher darum das vom Datentyp int16_t ein Wert abgezogen wird. 
Wusste nicht dass das geht.

East schrieb:
> Den LCD_INIT() brauch ich, um das LCD zu clearen.

Jou, du vertrödelst halt bei jedem Tastendruck ca. 20ms.
Gast #4420993
Lesenswert?

Karl M. schrieb:
> Ok East,
>
> dann mache es richtig und verwende nur einen Port, dort kann man 8 Bits
> Abfragen und als Eingänge nutzen.
>
> So wie es abgeändert wurde, wird es nicht funktionieren.
>
> Es gibt einen Trick z.B zwei Ports zu verwenden.
> Aber das passier alles nur in der Timer Interruptroutine und nur in
> der Zeile:i = key_state ^ ~KEY_PIN;                     // key changed ?
> East schrieb:
>> Das habe ich hinzugefügt, da ich unterschiedliche POrts benutze.

Das heisst also, da ich das schon extern vorbereite bekommt der Timer 
ein problem bei den setzen der unterschiedlichen PORTS???

Also so ist besser???

i=kay_state ^~((KEY_PINB & (1 << PB1)|(KEY_PIND &(1 <<PD2))
Gast #4420994
Lesenswert?

Frank schrieb:
> Karl M. schrieb:
>> Damit berechnet der Preprocessor den Timer0 Preloadwert im TimerO0verlow
>> Modus.
>> Der Prescaler ist 1024, 10ms == 10e-3, +0.5 == aufrunden und dann (256 -
>> Zählerwert) berechnen.
>> Denn TimerOverlow zählt der Timer0 von von N bis 256 und bei 256 == 0
>> wird ein Overflow Interrupt Request erzeugt.
>>
>> Alles korrekt !
>
> Mir geht es eher darum das vom Datentyp int16_t ein Wert abgezogen wird.
> Wusste nicht dass das geht.
>
> East schrieb:
>> Den LCD_INIT() brauch ich, um das LCD zu clearen.
>
> Jou, du vertrödelst halt bei jedem Tastendruck ca. 20ms.

Also doch die scheiss _delay_ms(15) in der LCD Routine..

Misst.
Gast #4421006
Lesenswert?

East schrieb:
> Frank schrieb:
>> Karl M. schrieb:
>>> Damit berechnet der Preprocessor den Timer0 Preloadwert im TimerO0verlow
>>> Modus.
>>> Der Prescaler ist 1024, 10ms == 10e-3, +0.5 == aufrunden und dann (256 -
>>> Zählerwert) berechnen.
>>> Denn TimerOverlow zählt der Timer0 von von N bis 256 und bei 256 == 0
>>> wird ein Overflow Interrupt Request erzeugt.
>>>
>>> Alles korrekt !
>>
>> Mir geht es eher darum das vom Datentyp int16_t ein Wert abgezogen wird.
>> Wusste nicht dass das geht.
>>

OK. Habe auch zuerst gedacht, aber dann wieder verworfen. Also uint8t == 
256.

256 - (F_CPU /1024 *10e-3 +0.5)
Gast #4421008
Lesenswert?

Hall East,

ich hatte Dir den original Beitrag/ Thread von Peter Dannegger (peda) 
zitiert. Den solltest Du lesen und verstehen.

Die Routinen von Peter sind nur für das Entprellen eines 8bit Ports 
ausgelegt !

Also nutze erst mal nur einen Port mit seinen max. 8 Bits als Einfänge.
Wenn Du dass am laufen bekommst, versuche den Code in der Timer 
Interrupt Service Routine komplett zu verstehen.
Dann sollte Dir ein Licht aufgehen.

East schrieb:
> Also so ist besser???
>
> i=kay_state ^~((KEY_PINB & (1 << PB1)|(KEY_PIND &(1 <<PD2))
Gast #4421013
Lesenswert?

East schrieb:
> Frank schrieb:
>> Karl M. schrieb:
>>> Damit berechnet der Preprocessor den Timer0 Preloadwert im TimerO0verlow
>>> Modus.
>>> Der Prescaler ist 1024, 10ms == 10e-3, +0.5 == aufrunden und dann (256 -
>>> Zählerwert) berechnen.
>>> Denn TimerOverlow zählt der Timer0 von von N bis 256 und bei 256 == 0
>>> wird ein Overflow Interrupt Request erzeugt.
>>>
>>> Alles korrekt !
>>
>> Mir geht es eher darum das vom Datentyp int16_t ein Wert abgezogen wird.
>> Wusste nicht dass das geht.
>>
>> East schrieb:
>>> Den LCD_INIT() brauch ich, um das LCD zu clearen.
>>
>> Jou, du vertrödelst halt bei jedem Tastendruck ca. 20ms.
>
> Also doch die scheiss _delay_ms(15) in der LCD Routine..
>
> Misst.

Könnte ich das eventuell umgehen? zb:

if(Getkeypress()){ z=1<;}

if (z !=0 ){ lcd_printlc(1,7("PD4"));}
Gast #4421021
Lesenswert?

Karl M. schrieb:
> Hall East,
>
> ich hatte Dir den original Beitrag/ Thread von Peter Dannegger (peda)
> zitiert. Den solltest Du lesen und verstehen.
>
> Die Routinen von Peter sind nur für das Entprellen eines 8bit Ports
> ausgelegt !
>
> Also nutze erst mal nur einen Port mit seinen max. 8 Bits als Einfänge.
> Wenn Du dass am laufen bekommst, versuche den Code in der Timer
> Interrupt Service Routine komplett zu verstehen.
> Dann sollte Dir ein Licht aufgehen.
>


OK. Das wede ich wohl erstmal machen müssen. Vielen Dank für deine 
Mühen.
Gast #4421288
Lesenswert?

Stefan,

ich sehe, dass Du das Problem noch nicht erkannt hast, mit Delay kann 
man keine Tasten entprellen und auch noch Ereignisse generieren !

Der Code von Peter liefert dem Anwender ja verschiedene Ereignisse, das 
Entprellen von Drücken und Loslassen ist dann eher nur ein Nebeneffekt.

Stefan U. schrieb:
> Mal was anderes: Wenn du nach jedem Tastendruck etwas auf dem Display
> ausgibst, und das mehr als 10ms dauert, dann brauchst du gar keinen Code
> zur Entprellung.
Moderator (Firma: Titel) Persönliche Seite #4421293
Lesenswert?

Stefan U. schrieb:
> Mal was anderes: Wenn du nach jedem Tastendruck etwas auf dem Display
> ausgibst, und das mehr als 10ms dauert, dann brauchst du gar keinen Code
> zur Entprellung.
Dafür reagierst du dann aber auch auf kurze EMV-Spikes und Störungen, 
wenn sie zur rechten Zeit kommen  :-o

Oder andersrum: ein _delay_ms() deutet meist auf einen strukturellen 
Fehler im Programm hin. Denn es ist aktive Rechenzeitverschwendung. Und 
auch die Ausgabe auf ein Display muss nicht warten. Sie muss nur 
kontrollieren, ob die vorige Aktion schon fertig ist...
Gast #4421294
Lesenswert?

Ich bin vom ursprünglich geposteten Code main() ausgegangen. Da wird 
nach jedem Tastendruck irgendwas auf dem LCD ausgegeben. Wenn das 10ms 
dauert und ich danach auf das Loslassen der Taste warte (das hatte ich 
vergessen, zu schreiben), brauche ich keine Entsprellung mehr. Die 
ergibt sich aus der zeit, die die Displayausgabe dauert.
Gast #4421304
Lesenswert?

Zur Korrektur, nicht nach Tastendrücken sondern auf einen 
Tasten-Ereignis wird reagiert.
D.h. mit dem original Code ist der Zustandwechsel schon über 4x 10ms 
konstant, danach wird das Ereignis festgestellt.
Da die gesamte Tastenverarbeitung über den Timer0 Interrupt läuft, ist 
es egal, ob man im Hauptprogramm ein LCD bedient auch wartet.
Gast #4421534
Lesenswert?

Lothar M. schrieb:
> Stefan U. schrieb:
>> Mal was anderes: Wenn du nach jedem Tastendruck etwas auf dem Display
>> ausgibst, und das mehr als 10ms dauert, dann brauchst du gar keinen Code
>> zur Entprellung.
> Dafür reagierst du dann aber auch auf kurze EMV-Spikes und Störungen,
> wenn sie zur rechten Zeit kommen  :-o
>
> Oder andersrum: ein _delay_ms() deutet meist auf einen strukturellen
> Fehler im Programm hin. Denn es ist aktive Rechenzeitverschwendung. Und
> auch die Ausgabe auf ein Display muss nicht warten. Sie muss nur
> kontrollieren, ob die vorige Aktion schon fertig ist...

Mag schon sein, das ein _delay_xx darauf hindeutet, dass zeit 
verschwendet wird. Trotz alle dem gibt es gerade bei LCD Routinen im ein 
Delay, um zb die Ausgabe an ein HD44780 anzupassen. Bis Dato habe ich 
noch keine Routine für LCD gesehen die kein _delay_xx benutzt.
Gast #4421542
Lesenswert?

Karl M. schrieb:
> Hall East,
>
> ich hatte Dir den original Beitrag/ Thread von Peter Dannegger (peda)
> zitiert. Den solltest Du lesen und verstehen.
>
> Die Routinen von Peter sind nur für das Entprellen eines 8bit Ports
> ausgelegt !
>
> Also nutze erst mal nur einen Port mit seinen max. 8 Bits als Einfänge.
> Wenn Du dass am laufen bekommst, versuche den Code in der Timer
> Interrupt Service Routine komplett zu verstehen.
> Dann sollte Dir ein Licht aufgehen.

Glaube habe es verstanden. Und auch meinen Fehler gefunden. Da die 
Routine einen Port mit allen 8 Bits kontrolliert, wurde so wie ich 
vermute durch meine Änderung, etwas Überschnitten. Wenn der Timer bei 
einem überlauf ein Taster (Eingang) über Getkeypress registriert hat und 
auswertet, habe ich durch meine Änderung einen beim nächsten überlauf 
neuen Port übertragen.

Da liegt glaube ich der Fehler. Dort wurden dann von zwei 
Unterschiedlichen Ports, Pins vermischt und führten so zur gegnseitigen 
Prellung.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren