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
> 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.
Ich tippe auf einen Lötfehler am externen Pullup des besagten Tasters.
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?
Bei einer Ansteuerung von einer LED funzt das. Problem, da sehe ich aber nicht von welchem Eingang das kommt.
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?
:
Bearbeitet durch User
Die externen Pullups sind auf der Platine. Alle gegen VCC und den Eingängen.
Achso, die internen habe ich nicht eingeschaltet, da externe existieren. Wie soll ich sonst die 5V gegen den Eingang messen können?
Übrigens ist es nicht nur ein Taster sondern 8. Und wäre ein oder mehrere Pullups nicht richtig gelötet, würde immer nur diese auslösen. Es lösen aber alle sporadisch aus.
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...
Habe immer gegen Masse gemessen. Daher auch die gemessenen Spannungen.
Was soll das lcd_init() bei jedem Tastendruck? Was soll das sein:
1 | TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms |
Mich wundert dass das überhaupt übersetzt.
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 | }
|
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.
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 !
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.
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.
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????
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.
Karl M. schrieb: > Siehe: > > http://www.nongnu.org/avr-libc/user-manual/group__... Also doch das gleiche wie cli().
Ich schreibe es ferne nochmal: Die Antwort steht in der ISR( TIM0_COMPA_vect ) ! Beitrag "Re: Universelle Tastenabfrage"
Karl M. schrieb: > Hallo, > > Was soll den bei Dir innerhalb deruint8_t get_key_press( uint8_t > key_mask, uint8_t PORTx ) > KEYPIN = PORTx;bewirken ? > Das habe ich hinzugefügt, da ich unterschiedliche POrts benutze.
East schrieb: > Den LCD_INIT() brauch ich, um das LCD zu clearen. Schaltest du auch jedesmal das Auto aus, wenn du den Gang wechseln willst? > Da das mit lcd_command(LCD_CLEAR) nicht hingehauen hatte. Dann such den Fehler. In der Praxis geht das nämlich...
Karl M. schrieb: > Ich schreibe es ferne nochmal: > > Die Antwort steht in der ISR( TIM0_COMPA_vect ) ! > > Beitrag "Re: Universelle Tastenabfrage" WAS??????????????? Du benutzt den COMPA_Vector?????? Aber in der Routine von PD wird doch der Overflow benutzt!?!
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.
Genau, ich bin Programmierer und nutze meine Timer unterschiedlich, mal Timer0 oder auch TimerN im Modus CTC. East schrieb: > WAS??????????????? Du benutzt den COMPA_Vector?????? > > Aber in der Routine von PD wird doch der Overflow benutzt!?!
East schrieb: > Aber warum löst getkeypress(), ohne ein zutun aus???? Karl hat ja schon darauf hingewiesen: Variablen haben es meist gerne wenn sie mit einem bestimmten Wert initialisiert werden.
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.
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))
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.
Heisst aber, das ich innerhalb einer Tasterabfrage nichts auf meinem LCD anzeigen darf. Ist ziemlich doof.
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)
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))
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"));}
Also eher so:
1 | TCNT0 = (uint8_t)((int16_t)(0) - (int16_t)(F_CPU / 1024 * 10e-3 + 0.5) ); |
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.
Karl M. schrieb: > Also eher so: > TCNT0 = (uint8_t)((int16_t)(0) - (int16_t)(F_CPU / 1024 * 10e-3 + 0.5) > ); Wie wird den (uint8_t) von (uint16_t) ohne einen operator unterschieden? Glaube verstehe das noch nicht.
Sube bitte mal nach C CAST für (uint8_t) und (int16_t). Und schaue dir bitte die unterschiedlichen Zahlentypen auf einen Atmel AVR µC an.
Vielen Dank. Jetzt klappt es. Muss noch ein bischen die Routine von PD verstehen.
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.
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.
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...
:
Bearbeitet durch Moderator
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.
> Dafür reagierst du dann aber auch auf kurze EMV-Spikes und Störungen
Das tut der ursprünglich gepostete Code auch.
Stefan U. schrieb: > Das tut der ursprünglich gepostete Code auch. Also gleich weit und gleich schlecht...
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.
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.
Was natürlich fraglich ist, warum LCD_CLEAR nicht funktioniert. Da ich den Code hier aus dem Forum habe, habe ich angenommen dass der auch lauffähig ist.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.