Forum: Mikrocontroller und Digitale Elektronik Verständnisfragen zur Tasterentprelllung Version von Peter Danneger


von muhh (Gast)


Lesenswert?

Hi Leute,

ich sitze gerade an einem Miniprojekt, in dem ich ein PWM Signal mit 
einem MSP430G2553 erzeuge und die Pulsweite per Tastendruck immer 
erhöhe. Leider funktioniert das ganze ohne Tastenentprellung nicht sehr 
gut.
Da ich gleichzeitig auch noch lernen möchte, wie man die Interrupts im 
MSP430 benutzt, finde ich die Komfortroutine in C von Herrn Dannegger 
(http://www.mikrocontroller.net/articles/Entprellung#Softwareentprellung)
ganz gut, leider komme ich aber bei der "Analyse" des Codes nicht 
weiter.

Ich stecke leider schon ganz am Anfang in der ISR-Routine fest (i = 
key_state ^ ~KEY_PIN).
Folgendes habe ich mal durchgespielt: (erster durchlauf der ISR)

0000 0000    key_state

0000 0010    KEY_PIN
1111 1101   ~KEY_PIN

1111 1101    i = key_state ^ ~KEY_PIN (bis hierhin alles richtig?)

Nächste Zeile: ct0 = ~( ct0 & i )

0000 0000    ct0
1111 1101    i

0000 0000    ct0 & i
1111 1111   ~ct0 & i

Warum werden alle Bits auf 1 gesetzt?

Viele Grüße
Muhh

von Εrnst B. (ernst)


Lesenswert?

Weiter durchspielen, der Interrupt passiert ja nicht nur einmal...

Vielleicht hilfts dir auch weiter, von allen beteiligten Variablen nur 
ein einzelnes Bit (bei dir das zweite) zu betrachten.

Und schau nach, ob dein Code für invertierte Tasteneingänge gedacht war 
(Interner Pullup im µC, Taster nach GND)

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:

> Ich stecke leider schon ganz am Anfang in der ISR-Routine fest (i =
> key_state ^ ~KEY_PIN).
> Folgendes habe ich mal durchgespielt: (erster durchlauf der ISR)
>
> 0000 0000    key_state
>
> 0000 0010    KEY_PIN
> 1111 1101   ~KEY_PIN

anders rum.
KEY_PIN hat an den Stellen ein 1 Bit, an denen die Taste NICHT gedrückt 
ist. Eine gedrückte Taste ist ein 0-Bit.

Das folgt unmittelbar daraus, wie Tasten bei einem AVR angeschlossen 
werden.
1
      +-----
2
            |
3
        PB0 o---------+
4
            |         |
5
        PB1 o          /
6
            |         | 
7
        PB2 o         |
8
            |        GND

Wird der Taster gechlossen, so stellt er eine Verbindung nach GND her. 
Ist der Taster offen, dann hält der im AVR eingebaute Pullup Widerstand 
den Pin auf 1

1
  .....   key_state ^ ~KEY_PIN
das stellt einfach nur alle Bits fest, in denen sich dann KEY_PIN von 
key_state unterscheidet. Das ~ ist notwendig, damit man diese 'gedrückt 
ist gleich 0 Bit'-Logik umdreht. In key_state ist schon alles genau 
anders rum. Ein nicht gedrückter Taster hat ein 0 Bit, ein gedrückter 
hat ein 1 Bit.

von Karl H. (kbuchegg)


Lesenswert?

> Nächste Zeile: ct0 = ~( ct0 & i )

ct0 und ct1 musst du als Einheit sehen. Die beiden implementieren 8 
Stück 2 Bit Zähler.

von muhh (Gast)


Lesenswert?

Meine Konzentration lässt so langsam nach, deswegen frage ich lieber 
nochmal.

Der Code
i &= ct0 & ct1;
entspricht
i = i & ct0 & ct1;
???

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:

> Der Code
> i &= ct0 & ct1;
> entspricht
> i = i & ct0 & ct1;
> ???


Jede
1
  a op= b;
Operation entspricht in C einem
1
  a = a op ( b );
Es ist einfach nur eine Vereinfachung in der Schreibweise, so dass man 
den linken Teil der Zuweisung rechts nicht mehr schreiben braucht.

Beachte, dass der Originalteil 'b' als in Klammern gesetzt zu betrachten 
ist. D.h. bei dir
1
  i &= ct0 & ct1;
spielt das keine große Rolle, weil ausschliesslich & Operationen im 
Spiel sind, und die in beliebiger Reihenfolge abgearbeitet werden 
können, ohne dass sich am Ergebnis was ändert.

Aber ein
1
  a *= b + c;
entspricht einem
1
  a = a * ( b + c );
und nicht etwa einem
1
  a = a * b + c;
was etwas völlig anderes wäre.

von muhh (Gast)


Lesenswert?

Okay, soweit so gut, habe mich selbst nochmal kontrolliert, indem ich 
den Code in eine C-Datei kopiert habe und diese compiliert habe. 
Folgendes ist jetzt beim ersten Aufruf herausgekommen:
1
0000 0000    key_state
2
3
1111 1101    KEY_PIN
4
0000 0010    ~KEY_PIN
5
6
0000 0010    i = key_state ^ ~KEY_PIN
7
8
1111 1111    ct0 = ~(ct0 & i)
9
1111 1111    ct1= ct0 ^ (ct1 & i)
10
11
0000 0010    &i = ct0 & ct1
12
13
0000 0010    key_state ^= i
14
15
0000 0010    key_press |= key_state & i
Die folgenden Zeilen verstehe ich leider gar nicht :S Was hat es damit 
auf sich?
1
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
2
#define REPEAT_START    50                        // after 500ms
3
#define REPEAT_NEXT     20                        // every 200ms
4
5
-----------------------------------------------------------------------
6
7
if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
8
     rpt = REPEAT_START;                          // start delay
9
  if( --rpt == 0 ){
10
    rpt = REPEAT_NEXT;                            // repeat delay
11
    key_rpt |= key_state & REPEAT_MASK;
12
  }

von Karl H. (kbuchegg)


Lesenswert?

1
1111 1111    ct0 = ~(ct0 & i)
2
1111 1111    ct1= ct0 ^ (ct1 & i)


Der Teil stimmt nicht.
Der 'Default' Zustand für ct0 bzw. ct1 ist nicht 0000 0000 sondern 1111 
1111.

Ja ich weis, du findest sie nicht bei den Initialisierungen im C-Code, 
aber darauf läuft es hinaus, es sei denn, beim Einschalten des µC ist 
die Taste bereits gedrückt. Dann wird diese gedrückte Taste sofort 
gemeldet, wie deine Analyse zeigt.


> Die folgenden Zeilen verstehe ich leider gar nicht :S Was hat es damit auf sich?

Das ist die Repeat-Funktionalität.
Ist eine der Tasten aus der 'Menge' der REPEAT_MASK gedrückt (es geht 
immer nur eine, aber das reicht ja normalerweise), und ist sie bereits 
länger als REPEAT_START gedrückt, dann wird das vermerkt, so dass 
Abfragen der Form
1
  if( get_key_press( 1 << TASTE ) || get_key_repeat( 1 << TASTE ) )
diese Taste dann immer wieder als gedrückt identifizieren. Das ist zb 
bei Up-/Down Zahleneingaben recht praktisch:
Der Benutzer drückt auf die Taste und die Zahl wird um 1 größer. Bleibt 
er auf der Taste dann schaltet sich nach kurzer Zeit der Autorepeat zu 
und in angenehmen Tempo wird die Zahl dann laufend 'von alleine' größer. 
So wie wenn du am PC auf die Taste 'a' drückst und nach einiger Zeit 
tauchen dann nacheinander 'von alleine' lauter 'a' auf.

In deinem Code sieht das dann zb so aus
1
...
2
  while( 1 )
3
  {
4
    .....
5
6
    if( get_key_press( 1 << PLUS ) || get_key_repeat( 1 << PLUS ) )
7
    {
8
      if( LCD_Helligkeit < 255 )
9
        LCD_Helligkeit++;
10
    }
11
12
    if( get_key_press( 1 << MINUS ) || get_key_repeat( 1 << MINUS ) )
13
    {
14
      if( LCD_Helligkeit > 0 )
15
        LCD_Helligkeit++;
16
    }
17
18
    ....
19
20
  }

und fertig ist die Helligkeitseinstellung für ein LCD, die sich 
feinfühlig in einzelnen Schritten einstellen lässt, aber auch durch 
Draufbleiben auf der Taste sich komfortabel in großeren Schritten 
verändern lässt, so dass man beim gewollten Verstellen von 0 auf 255 
keine Klopforgie auf der Taste veranstalten muss.

von muhh (Gast)


Lesenswert?

"alles zusammengenommen heißt das, dass ein Tastendruck dann erkannt 
wird, wenn die Taste 4 mal hintereinander in einem anderen Zustand 
vorgefunden wurde als dem zuletzt bekannten entprellten Tastenzustand."

aber ich bin das alles nur einmal durchgegangen undbereits jetzt ist
1
key_press= 0000 0010;

Ich verstehe nicht, welchen Wert das Timer-Register TCNT0 mit der 
Anweisung
1
TCNT0 = (uint8_t)(uint16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
bekommt. Was hat das (uint8_t)(uint16_t) zu bedeuten? Welcher Wert wird 
das?

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:

> aber ich bin das alles nur einmal durchgegangen undbereits jetzt ist
>
1
key_press= 0000 0010;

aber nur weil du bei ct0 und ct1 mit der Bitbelegung von 0000 0000 
angefangen hast. Die haben die aber im Normalfall nicht. Im Normalfall 
haben die eine Ausgangssituation von 1111 1111 und er nach 4 maligem 
Durchlauf der ISR mit gedrückter Taste kommst du in die Situation, dass 
das entsprechende Bit in key_press das erste mal 1 wird.


> Ich verstehe nicht, welchen Wert das Timer-Register TCNT0 mit der
> Anweisung
>
1
TCNT0 = (uint8_t)(uint16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload 
2
> for 10ms
> bekommt.

Setz die Zahlenwerte ein und rechne es aus.

> Was hat das (uint8_t)(uint16_t) zu bedeuten?
Das sind Casts.
Der zuerst angewendete Cast uint16_t wandelt das Floating-Point Ergebnis 
des Ausdrucks in einen 16 Bit unsigned integer. Und der zweite Cast 
wandelt das dann auf 8 Bit runter.

> Welcher Wert wird
> das?

Setz die Zahlenwerte ein, F_CPU ist die Prozessortaktfrequenz, tipp es 
in einen Taschenrechner ein und rechne es aus.


Letzten Endes ist die Idee an dieser Stelle folgende:
Die Interrupt Service Routine soll alle 10ms aufgerufen werden. Der 
Aufruf findet immer dann statt, wenn der Timer überläuft. D.h. die Frage 
lautet: mit welchem Wert muss der Timer anfangen zu zählen, damit er 
nach 10ms seienn Overflowwert von 256 erreicht hat?
Das kann man ausrechnen. Genauso wie man ausrechnen kann, auf welche 
Sekundenmarke man den Sekundenzeiger einer Uhr stellen muss, damit genau 
x Sekunden später der Sekundenzeiger bei 60 ist und die Minuten 
weitergeschaltet werden.


Allerdings: so kritisch ist das hier nicht.
Wenn die ISR so ungefähr alle 5 bis 20ms aufgerufen wird, dann ist das 
gut genug. Kein Grund da auf exakt 10ms hinzuarbeiten. D.h wenn du den 
Code übernimmst, dann stell deinen Timer so ein, dass er seinen Überlauf 
in ungefähr diesem Zeitfenster hat und dann passt das schon. Und wenns 
30ms sind, ist es auch egal.

von W.S. (Gast)


Lesenswert?

muhh schrieb:
> Da ich gleichzeitig auch noch lernen möchte, wie man die Interrupts im
> MSP430 benutzt, finde ich die Komfortroutine in C von Herrn Dannegger..

Anstatt Quellen von anderen Leuten einfach zu übernehmen ohne sie zu 
verstehen, rate ich dir, das anstehende Problem erstmal gründlich zu 
durchdenken. Wenn du das geschafft hast, wirst du in der Lage sein, dir 
deine eigene Tastenentprellung zu schreiben - und auch Peters Quelle 
sinnvoll zu benutzen.

Im Prinzip gibt es 2 Varianten:
a) die einfache Variante: Sobald man irgendeine gedrückte Taste (also 
Pegel entsprechend äußerer Beschaltung) entdeckt, wird diese Taste als 
gedrückt gemeldet und die zugehörige Aktion durchgeführt. Anschließend 
wartet man, bis alle Tasten für einen bestimmten Zeitraum (meist 
50..100 ms) ununterbrochen ungedrückt sind.

b) die Komfort-Variante: Wenn eine Taste für eine gewisse Zeit 
ununterbrochen ungedrückt ist, dann gilt sie als ungedrückt und ihr wird 
eine längere Repetierzeit zugeordnet (meist 700..900ms). Wenn eine Taste 
vom ungedrückten Zustand in den gedrückten Zustand übergeht, dann wird 
die Taste als gedrückt gemeldet und die zugehörige Aktion durchgeführt. 
Unabhängig davon wird die Taste weiterhin überwacht und wenn sie länger 
als die zugeordnete Repetierzeit ununterbrochen gedrückt bleibt, dann 
wird ihr eine kürzere Repetierzeit zugeordnet (meist 150..250ms) und die 
Taste wird erneut so wie oben als gedrückt gemeldet und die zugehörige 
Aktion durchgeführt. Das wiederholt sich so lange, bis die Taste besagte 
gewisse Zeit ungedrückt ist.

Version a kann man ganz simpel mit Trampelschleife erledigen. Sie ist 
für einfache Fälle völlig ausreichend. Für Version b braucht man eine 
Art Systemtick, in dessen Interruptprogramm die Tastaturabfragen 
erledigt werden - und man braucht sowas wie eine Ereignis-Warteschlange, 
wo man die Tastatur-Ereignisse hineinstapelt, denn Einstapeln und 
Entnehmen sind ja völlig voneinander entkoppelt.

W.S.

von muhh (Gast)


Lesenswert?

1
    1111 1111   ct0
2
    1111 1111   ct1
3
    
4
    0000 0000   key_state
5
    1111 1101   KEY_PIN
6
    0000 0010  ~KEY_PIN
7
    
8
    0000 0010   i = key_state ^ ~KEY_PIN
9
    
10
    0000 0010   ct0 & i
11
    1111 1101   ct0 = ~(ct0 & i)
12
    
13
    1111 1111   ct1 = ct0 ^ (ct1 & i)
14
    
15
    0000 0000   i &= ct0 & ct1
16
    
17
    0000 0000   key_state ^= i

Es kann doch nicht sein, dass ich es nicht hinbekomme, wenigstens für 
einen Durchgang mal den key_state zu ändern...
Nehme ich immer die Extremfälle, bei denen das nicht funktioniert?

Ich bin bei dem obenstehenden Code davon ausgegangen, dass ct0 & ct1 auf 
ihrem 'Default' Wert 0xFF sind, der key_state auf 0x00 ist, da lange 
keine Taste gedrückt wurde und das jetzt eine Taste gedrückt wurde und 
KEY_PIN = 0xFD wird.

Warum kommt unten bei i=0 raus? Da müsste i = 0x02 rauskommen, sodass 
dieser wert auch in key_state geschrieben wird....

von Hannes L. (hannes)


Lesenswert?

muhh schrieb:
> wenigstens für
> einen Durchgang mal den key_state zu ändern...

Es dauert 4 Durchgänge...

...

von muhh (Gast)


Lesenswert?

Das ist ja das komische, vielleicht liegt es an dem Code?
1
int main(void)
2
{
3
    uint8_t i, key_state, KEY_PIN, key_press;
4
    static uint8_t ct0, ct1, rpt;
5
6
    key_state = 0x00;
7
    KEY_PIN = (0xFD);
8
    key_press = 0x00;
9
    ct0 = 0xFF;
10
    ct1 = 0xFF;
11
12
    i = key_state ^ ~KEY_PIN;                       // key changed ?
13
    printf("\ni: %X", i);
14
    ct0 = ~( ct0 & i );                             // reset or count ct0
15
    printf("\nct0: %X", ct0);
16
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
17
    printf("\nct1: %X", ct1);
18
    i &= ct0 & ct1;                                 // count until roll over ?
19
    printf("\ni: %X", i);
20
    key_state ^= i;                                 // then toggle debounced state
21
    printf("\nkey_state: %X", key_state);
22
    key_press |= key_state & i;                     // 0->1: key press detect
23
    printf("\nkey_press: %X\n", key_press);
24
25
    KEY_PIN = (0xFF);
26
27
    i = key_state ^ ~KEY_PIN;                       // key changed ?
28
    printf("\ni: %X", i);
29
    ct0 = ~( ct0 & i );                             // reset or count ct0
30
    printf("\nct0: %X", ct0);
31
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
32
    printf("\nct1: %X", ct1);
33
    i &= ct0 & ct1;                                 // count until roll over ?
34
    printf("\ni: %X", i);
35
    key_state ^= i;                                 // then toggle debounced state
36
    printf("\nkey_state: %X", key_state);
37
    key_press |= key_state & i;                     // 0->1: key press detect
38
    printf("\nkey_press: %X\n", key_press);
39
40
    KEY_PIN = (0xFD);
41
42
    i = key_state ^ ~KEY_PIN;                       // key changed ?
43
    printf("\ni: %X", i);
44
    ct0 = ~( ct0 & i );                             // reset or count ct0
45
    printf("\nct0: %X", ct0);
46
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
47
    printf("\nct1: %X", ct1);
48
    i &= ct0 & ct1;                                 // count until roll over ?
49
    printf("\ni: %X", i);
50
    key_state ^= i;                                 // then toggle debounced state
51
    printf("\nkey_state: %X", key_state);
52
    key_press |= key_state & i;                     // 0->1: key press detect
53
    printf("\nkey_press: %X\n", key_press);
54
55
    KEY_PIN = (0xFF);
56
57
    i = key_state ^ ~KEY_PIN;                       // key changed ?
58
    printf("\ni: %X", i);
59
    ct0 = ~( ct0 & i );                             // reset or count ct0
60
    printf("\nct0: %X", ct0);
61
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
62
    printf("\nct1: %X", ct1);
63
    i &= ct0 & ct1;                                 // count until roll over ?
64
    printf("\ni: %X", i);
65
    key_state ^= i;                                 // then toggle debounced state
66
    printf("\nkey_state: %X", key_state);
67
    key_press |= key_state & i;                     // 0->1: key press detect
68
    printf("\nkey_press: %X\n", key_press);
69
70
    KEY_PIN = (0xFD);
71
72
    i = key_state ^ ~KEY_PIN;                       // key changed ?
73
    printf("\ni: %X", i);
74
    ct0 = ~( ct0 & i );                             // reset or count ct0
75
    printf("\nct0: %X", ct0);
76
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
77
    printf("\nct1: %X", ct1);
78
    i &= ct0 & ct1;                                 // count until roll over ?
79
    printf("\ni: %X", i);
80
    key_state ^= i;                                 // then toggle debounced state
81
    printf("\nkey_state: %X", key_state);
82
    key_press |= key_state & i;                     // 0->1: key press detect
83
    printf("\nkey_press: %X\n", key_press);
84
85
}

Habe jedes mal KEY_PIN geändert, dass sollen die verschiedenen 
Durchgänge sein. Kann man das überhaupt so machen?

Die Ausgabe ist:
1
i: 2
2
ct0: FD
3
ct1: FF
4
i: 0
5
key_state: 0
6
key_press: 0
7
8
i: 0
9
ct0: FF
10
ct1: FF
11
i: 0
12
key_state: 0
13
key_press: 0
14
15
i: 2
16
ct0: FD
17
ct1: FF
18
i: 0
19
key_state: 0
20
key_press: 0
21
22
i: 0
23
ct0: FF
24
ct1: FF
25
i: 0
26
key_state: 0
27
key_press: 0
28
29
i: 2
30
ct0: FD
31
ct1: FF
32
i: 0
33
key_state: 0
34
key_press: 0

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:
>
1
>     1111 1111   ct0
2
>     1111 1111   ct1
3
> 
4
>     0000 0000   key_state
5
>     1111 1101   KEY_PIN
6
>     0000 0010  ~KEY_PIN
7
> 
8
>     0000 0010   i = key_state ^ ~KEY_PIN
9
> 
10
>     0000 0010   ct0 & i
11
>     1111 1101   ct0 = ~(ct0 & i)
12
> 
13
>     1111 1111   ct1 = ct0 ^ (ct1 & i)
14
> 
15
>     0000 0000   i &= ct0 & ct1
16
> 
17
>     0000 0000   key_state ^= i
18
>
>
> Es kann doch nicht sein, dass ich es nicht hinbekomme, wenigstens für
> einen Durchgang mal den key_state zu ändern...

Das passt schon so.
Bei deinem ersten Durchgang wird die Taste nicht als gedrückt gewertet.
Aber: ct0 bzw. ct1 haben sich verändert. Der für Bit 1 zuständige 2-Bit 
Zähler ist jetzt 2 und nicht mehr 3.

Der nächste Timer-Interrupt kommt. Die Taste ist immer noch gedrückt. 
Logisch in 10 Millisekunden kannst du eine Taste nicht drücken und 
wieder loslassen :-) Was passiert?
1
    1111 1101   ct0
2
    1111 1111   ct1
3
4
    0000 0000   key_state
5
    1111 1101   KEY_PIN
6
    0000 0010  ~KEY_PIN
7
 
8
    0000 0010   i = key_state ^ ~KEY_PIN
9
 
10
    0000 0000   ct0 & i
11
    1111 1111   ct0 = ~(ct0 & i)
12
    1111 1101   ct1 = ct0 ^ (ct1 & i)
13
14
    0000 0000   i &= ct0 & ct1
15
 
16
    0000 0000   key_state ^= i

Immer noch nicht. War ja auch der 2.te Durchgang.
Aber die ct-Werte! Der Zähler ist jetzt schon auf 1

Nächster Timer Interrupt, die Taste ist immer noch gedrückt. (Von Beginn 
der ganzen Aktion aus gemessen sind bis jetzt erst 30 Millisekunden 
vergangen)
1
    1111 1111   ct0
2
    1111 1101   ct1
3
4
    0000 0000   key_state
5
    1111 1101   KEY_PIN
6
    0000 0010  ~KEY_PIN
7
 
8
    0000 0010   i = key_state ^ ~KEY_PIN
9
 
10
    0000 0010   ct0 & i
11
    1111 1101   ct0 = ~(ct0 & i)
12
    1111 1101   ct1 = ct0 ^ (ct1 & i)
13
14
    0000 0000   i &= ct0 & ct1
15
 
16
    0000 0000   key_state ^= i

Noch nicht. (ist erst das dritte mal, dass die Taste gedrückt 
vorgefunden wurde)
1
    1111 1101   ct0
2
    1111 1101   ct1
3
4
    0000 0000   key_state
5
    1111 1101   KEY_PIN
6
    0000 0010  ~KEY_PIN
7
 
8
    0000 0010   i = key_state ^ ~KEY_PIN
9
 
10
    0000 0000   ct0 & i
11
    1111 1111   ct0 = ~(ct0 & i)
12
    1111 1111   ct1 = ct0 ^ (ct1 & i)
13
14
    0000 0010   i &= ct0 & ct1
15
 
16
    0000 0010   key_state ^= i

Bingo.
Die Taste wurde 4 mal hintereinander gedrückt vorgefunden. Und erst 
jetzt gilt sie als gedrückt.

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:
> Das ist ja das komische, vielleicht liegt es an dem Code?

tut es.
Dein Code simuliert einen prellenden Taster.
Du musst KEY_PIN schon ueber einen hinreichend langen Zeitraum konstant 
auf 0xFD lassen!



> Habe jedes mal KEY_PIN geändert, dass sollen die verschiedenen
> Durchgänge sein. Kann man das überhaupt so machen?

Nein.
Denn genau das machst du ja in der Realität auch nicht!

Die 'Durchgänge' finden in ca. 10 MILLI-Sekunden Abständen statt. Du 
kannst als Mensch in diesen Zeiträumen eine Taste nicht drücken und 
wieder loslassen. Wenn du als Mensch eine Taste ganz normal drückst und 
wieder loslässt, dann bedeutet das für den µC, dass er sie ein paar 10 
mal hintereinander als gedrückt sehen wird. Bis eben ein paar mal am 
Anfang der 'Drückzeit' in der der Pin möglicherweise 2 bis 3 mal 
zwischen gedrückt und nicht gedrückt wechselt, weil die Taste prellt. 
Und genau dieses gilt es auszufiltern - das ist der Zweck der ganzen 
Übung.
Damit eine Taste tatsächlich als gedrückt gilt, muss sie mindestens 4 
mal hintereinander als gedrückt vorgefunden worden sein. Erst dann gilt 
sie als gedrückt.

von muhh (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Damit eine Taste tatsächlich als gedrückt gilt, muss sie mindestens 4
> mal hintereinander als gedrückt vorgefunden worden sein. Erst dann gilt
> sie als gedrückt.

Oh man... Das habe ich einfach nicht verstanden.... aber jetzt ist es 
klar, ich dachte auch, das der Zustand jedes mal anders sein muss, aber 
das muss er gar nicht, da der key_state erst beim 4. mal verändert 
wird....

Danke für die Hilfe bis jetzt, dass war eine ganzschön schwere Geburt, 
habe einen ganzen Tag nur für diesen Code gebraucht...

Morgen geht's dann mit dem Rest weiter, da komme ich dann hoffentlich 
schneller durch :)

Danke nochmal :)

von Karl H. (kbuchegg)


Lesenswert?

Ersetze in deinem Testprogramm den Teil hier
1
    ct0 = ~( ct0 & i );                             // reset or count ct0
2
    printf("\nct0: %X", ct0);
3
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
4
    printf("\nct1: %X", ct1);
durch
1
    ct0 = ~( ct0 & i );                             // reset or count ct0
2
    printf("\nct0: %X", ct0);
3
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
4
    printf("\nct1: %X", ct1);
5
    printf("\nCount für Bit 1: %d", ((ct1 & 0x02)*2 + (ct0 & 0x02))/2);

dann siehst du den Zählerstand des 2-Bit Zählers für deine 'Testtaste' 
besser.


1
uint8_t i, key_state, KEY_PIN, key_press;
2
static uint8_t ct0, ct1, rpt;
3
4
void Debounce( uint8_t KEY_PIN )
5
{
6
    i = key_state ^ ~KEY_PIN;                       // key changed ?
7
    printf("\ni: %X", i);
8
    ct0 = ~( ct0 & i );                             // reset or count ct0
9
    printf("\nct0: %X", ct0);
10
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
11
    printf("\nct1: %X", ct1);
12
    printf("\nCount für Bit 1: %d", ((ct1 & 0x02)*2 + (ct0 & 0x02))/2);
13
    i &= ct0 & ct1;                                 // count until roll over ?
14
    printf("\ni: %X", i);
15
    key_state ^= i;                                 // then toggle debounced state
16
    printf("\nkey_state: %X", key_state);
17
    key_press |= key_state & i;                     // 0->1: key press detect
18
    printf("\nkey_press: %X\n", key_press);
19
}
20
21
int main(void)
22
{
23
    key_state = 0x00;
24
    key_press = 0x00;
25
    ct0 = 0xFF;
26
    ct1 = 0xFF;
27
28
29
    Debounce( 0xff );     // nichts gedrückt
30
    Debounde( 0xfd );     // gedrückt, 1. Durchgang
31
    Debounce( 0xff );     // Hoppla, ein Preller
32
    Debounce( 0xfd );     // aber jetzt, 1
33
    Debounce( 0xfd );     // immer noch, 2
34
    Debounce( 0xfd );     // noch immer, 3
35
    Debounce( 0xfd );     // und noch immer, 4
36
                          // ab jetzt gilt die Taste als gedrückt
37
    Debounce( 0xfd );     // der Benutzer ist auf der Taste eingeschlafen
38
    Debounce( 0xff );     // jetzt hat er losgelassen, aber hat er das wirklich, 1
39
    Debounce( 0xff );     // immer noch losgelassen, 2
40
    Debounce( 0xff );     // noch immer, 3
41
    Debounce( 0xff );     // und zum 4 mal losgelassen. Jetzt gilt die
42
                          // Taste tatsächlich als losgelassen
43
}


Ja, der Code ist trickreich.
Vor allem das Zusammenspiel von ct0 und ct1, die gemeinsam 8 Stück 2-Bit 
Zähler implementieren, ist nicht einfach zu durchschauen.

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:

> Oh man... Das habe ich einfach nicht verstanden.... aber jetzt ist es
> klar

Sehr gut.
Damit bist du einer der wenigen, die die Internals verstanden haben. :-)

Im Ernst:
Den Code muss man nicht im Detail verstehen, dazu funktioniert er 
einfach zu gut. Ich hab auch eine Weile gebraucht, bis ich diese 4 
Zeilen
1
    ct0 = ~( ct0 & i );
2
    ct1 = ct0 ^ (ct1 & i);
3
    i &= ct0 & ct1;
4
    key_state ^= i;
in ihrer vollen Tragweite verstanden habe. In diesen 4 Zeilen passiert 
eine Menge. Da werden 8 Zähler parallel und unabhängig voneinander von 3 
auf 0 heruntergezählt und wenn der jeweilige Zähler dann von 0 auf 3 
unterläuft, wird in key_state das zugehörige Bit getoggelt. Zusätzlich 
werden die Zähler dann auch noch auf 3 gesetzt wenn in i das 
entsprechende 1 Bit fehlt.

von muhh (Gast)


Lesenswert?

So, aber 2 Dinge stören mich noch :P

Ich verstehe die Funktionen
1
get_key_short()
2
get_key_long()

nicht. Die sollen vermutlich gucken, wie die Taster gedrückt werden und 
zusammen mit dem Code
1
if( get_key_short( 1<<KEY1 ))
2
      LED_PORT ^= 1<<LED1;
3
 
4
if( get_key_long( 1<<KEY1 ))
5
      LED_PORT ^= 1<<LED2;

bei kurz gedrücktem Taster die LED1 einschalten und bei einspringender 
Repeat-Funktion, also wenn die Taster länger gedrückt werden die LED2 
auch einschalten.

Ich bin in der Funktion genauso vorgegangen wie in der ISR, habe immer 
niäre Werte eingesetzt, allerdings kommt bei mir 0x00 raus (für 
get_key_short), wenn ich annehme, dass

key_state = 0x02
key_mask = 0x02

sind.

Der Code würde dann ja so aussehen:
1
get_key_short( 0000 0010 )
2
{
3
    cli();
4
    return get_key_press( ~(0000 0010) & 0000 0010 );
5
                          ~key_state   &   key_mask
6
}

Gibts es da einen Trick bei? Was soll die Invertierung von key_state 
bewirken?

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:

> Der Code würde dann ja so aussehen:
>
>
1
> get_key_short( 0000 0010 )
2
> {
3
>     cli();
4
>     return get_key_press( ~(0000 0010) & 0000 0010 );
5
>                           ~key_state   &   key_mask
6
> }
7
>
>
> Gibts es da einen Trick bei? Was soll die Invertierung von key_state
> bewirken?

Um Kurz von Lang unterscheiden zu können, muss die Taste bereits 
losgelassen worden sein.

Denn erst mal ist jede Taste kurz gedrückt. Das interessiert dich aber 
nicht. Dich interessiert, ob die Taste insgesammt kurz oder lang 
gedrückt war. Und das lässt sich erst feststellen, wenn die Taste 
definitiv wieder losgelassen wurde. D.h. Lang lässt sich auch schon vor 
dem Loslassen feststellen. Aber kurz nicht.

Bei GUIs gibt es ein ähnliches Problem. Wann hat der Benutzer eine 
Maustaste nur einfach und wann hat er sie doppelt geklickt?
Denn jeder Doppelklick beginnt erst mal mit einem Einfachklick der sich 
möglicherweise irgendwann zu einem Doppelklick mausert.

von Edgar F. (edgarfalke)


Lesenswert?

Hallo Mäh,
Es gibt von Motorola einen C-Mos Logigbaustein 4490 zum Entprellen von
Kontakten. Vielleicht kann Du damit Dein Problem beheben.
MfG
Edgar

von muhh (Gast)


Lesenswert?

@Edgar Falke:

Hehe, danke für den Tipp, aber ich bevorzuge im Moment die 
Softwarelösung, da ich so den MSP430 besser kennenlerne.
------------------------------------------------------------------------ 
--

Mein nächstes Problem ist, dass ich nicht weiß wie ich den Interrupt in 
Gang bekomme....

Kennt sich jemand mit denen aus? Ich benutzte einen MSP430G2553.

Hier mein Code:
1
#include  <msp430g2553.h>
2
#include <stdint.h>
3
4
#define KEY_PIN      0x01
5
6
#define REPEAT_MASK     (1<<BIT0)             // repeat: Pin 0
7
#define REPEAT_START    50                         // after 500ms
8
#define REPEAT_NEXT     20                          // every 200ms
9
10
#define TRUE  1
11
#define FALSE  0
12
13
/**************************************************/
14
15
volatile uint8_t key_state;                                // debounced and inverted key state: bit = 1: key pressed
16
volatile uint8_t key_press;                                // key press detect
17
volatile uint8_t key_rpt;                                  // key long press and repeat
18
19
/**************************************************/
20
21
void ConfigWDT( void );
22
void ConfigPeripheral( void );
23
void ConfigTimerA( void );
24
25
/**************************************************/
26
27
void ConfigWDT( void )
28
{
29
  WDTCTL = WDTPW + WDTHOLD; // Stop WDT
30
}
31
32
/**************************************************/
33
34
void ConfigPeripheral( void )
35
{
36
  P1SEL |= (0x02 | 0x04 | 0x10);  // Enable secondary peripheral
37
  P1DIR = (0x02 | 0x04 | 0x10);    // Set In/Out Directions
38
}
39
40
/**************************************************/
41
42
void ConfigTimerA( void )
43
{
44
  TACTL =  TASSEL_2 | TACLR | ID_0;
45
  TACTL |= MC_1;
46
  
47
  //TACCTL0 |= CCIE ;                // enable interrupt
48
  TACCTL1 = OUTMOD_3;
49
  TACCR0 = 0x000F;
50
  TACCR1 = 0x0000;
51
52
  
53
  TACCTL2 = OUTMOD_0 | CCIE;  // OUTPUT_MODE 0, Capture/Compare Interrupt Enable
54
  TACCR2  = 0x03E8;        // 1000 - 10ms
55
}
56
57
/**************************************************/
58
59
#pragma vector=TIMER0_A0_VECTOR
60
__interrupt void Timer0_A0_ISR( void )
61
{
62
  static uint8_t ct0, ct1, rpt;
63
  uint8_t i;
64
 
65
  TACCR2 = 0x03E8;  // preload for 10ms
66
 
67
  i = key_state ^ ~KEY_PIN;                       // key changed ?
68
  ct0 = ~( ct0 & i );                             // reset or count ct0
69
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
70
  i &= ct0 & ct1;                                 // count until roll over ?
71
  key_state ^= i;                                 // then toggle debounced state
72
  key_press |= key_state & i;                     // 0->1: key press detect
73
 
74
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
75
     rpt = REPEAT_START;                          // start delay
76
  if( --rpt == 0 ){
77
    rpt = REPEAT_NEXT;                            // repeat delay
78
    key_rpt |= key_state & REPEAT_MASK;
79
  }
80
}
81
82
///////////////////////////////////////////////////////////////////
83
//
84
// check if a key has been pressed. Each pressed key is reported
85
// only once
86
//
87
uint8_t get_key_press( uint8_t key_mask )
88
{
89
  _DINT();                                          // read and clear atomic !
90
  key_mask &= key_press;                          // read key(s)
91
  key_press ^= key_mask;                          // clear key(s)
92
  _EINT();
93
  return key_mask;
94
}
95
 
96
///////////////////////////////////////////////////////////////////
97
//
98
// check if a key has been pressed long enough such that the
99
// key repeat functionality kicks in. After a small setup delay
100
// the key is reported being pressed in subsequent calls
101
// to this function. This simulates the user repeatedly
102
// pressing and releasing the key.
103
//
104
uint8_t get_key_rpt( uint8_t key_mask )
105
{
106
  _DINT();                                          // read and clear atomic !
107
  key_mask &= key_rpt;                            // read key(s)
108
  key_rpt ^= key_mask;                            // clear key(s)
109
  _EINT();
110
  return key_mask;
111
}
112
 
113
///////////////////////////////////////////////////////////////////
114
//
115
// check if a key is pressed right now
116
//
117
uint8_t get_key_state( uint8_t key_mask )
118
 
119
{
120
  key_mask &= key_state;
121
  return key_mask;
122
}
123
 
124
///////////////////////////////////////////////////////////////////
125
//
126
uint8_t get_key_short( uint8_t key_mask )
127
{
128
  _DINT();                                          // read key state and key press atomic !
129
  return get_key_press( ~key_state & key_mask );
130
}
131
 
132
///////////////////////////////////////////////////////////////////
133
//
134
uint8_t get_key_long( uint8_t key_mask )
135
{
136
  return get_key_press( get_key_rpt( key_mask ));
137
}
138
139
140
void main(void)
141
{
142
  
143
  ConfigWDT();
144
  ConfigPeripheral();
145
  ConfigTimerA();
146
  
147
   while(TRUE)
148
   {
149
       if(get_key_press(1<<0))
150
       {
151
         if(TACCR1 == TACCR0)
152
         {
153
           TACTL &= ~MC_1;
154
           TACCR1 = 0x0000;
155
           TACTL |= MC_1;
156
         }
157
         else
158
         {
159
           TACTL &= ~MC_1;
160
           TACCR1 = TACCR1+1;
161
           TACTL |= MC_1;
162
         }
163
       }
164
   }
165
}

von Karl H. (kbuchegg)


Lesenswert?

Ehe ich mich an ein schon recht komplexes Beispiel wage, würde ich an 
deiner Stelle erst mal mich nur mit dem Timer beschäftigen. Als 
drumherum reicht es fürs erst aus, wenn man eine LED an einem Portpin 
Timer- und damit Interruptgesteuert zum Blinken bringt. Das hat den 
Vorteil, das man unmittelbar sieht, ob alles passt oder nicht und das 
man auch sehen kann, wie sich Veränderungen in den Werten auswirken.

http://www.embeddedrelated.com/showarticle/182.php

Und erst dann baue ich in eine funktionierende Timer-Interrupt Lösung 
die Tastenentprellung ein.

von muhh (Gast)


Lesenswert?

Danke für den Link, aber leider hilft der mir nicht weiter, weil die 
Namen für die Interrupt_Vektoren anders sind.

Kann man den Namen der Routine willkürlich setzen?

Also ist z.B. anstelle von
1
__interrupt void TimerA ( void ) ....
2
3
__interrupt void blabla ( void ) ....

möglich?

Woher weiß den der Compiler welche Routine ausgelöst wird?
Bestimmt das nicht der Vector?

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:
> Danke für den Link, aber leider hilft der mir nicht weiter, weil die
> Namen für die Interrupt_Vektoren anders sind.
>
> Kann man den Namen der Routine willkürlich setzen?

Das weiß ich nicht. Du bist der, der MSP430 programmiert.

>
> Also ist z.B. anstelle von
>
>
1
> __interrupt void TimerA ( void ) ....
2
> 
3
> __interrupt void blabla ( void ) ....
4
>
>
> möglich?

In irgendeiner Form muss da drinnen logischerweise vorkommen, welchen 
Interrupt die ISR behandelt. Ich sehe jetzt nicht, wie das hier 
bewerkstelligt wäre.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> In irgendeiner Form muss da drinnen logischerweise vorkommen, welchen
> Interrupt die ISR behandelt. Ich sehe jetzt nicht, wie das hier
> bewerkstelligt wäre.


Ich schätze mal, dass
1
#pragma vector=TIMERA0_VECTOR
2
__interrupt void Timer_A (void)
3
{

das vorhergehende #pragma diesen Punkt regelt.

#pragma sind immer irgendwelche compilerabhängige Dinge. Und 
Interruptfunktionen sind in ihrer Syntax hochgradig compilerabhängig, 
weil es die im offiziellen C-Standard gar nicht gibt.


Hast du ja auch gemacht :-)
1
#pragma vector=TIMER0_A0_VECTOR
2
__interrupt void Timer0_A0_ISR( void )
3
{
4
  static uint8_t ct0, ct1, rpt;
5
  ....

So gesehen ....
> weil die Namen für die Interrupt_Vektoren anders sind.
... nö, sind sie nicht. Die Pragma-Namen sind identisch.

von muhh (Gast)


Lesenswert?

Ich habs geschafft :)
1
#pragma vector=TIMER0_A1_VECTOR;
2
__interrupt void TimerA( void )
3
{
4
  static uint8_t ct0, ct1, rpt;
5
  ....

Alter Schwede, dass finde ich relativ schlecht gemacht, steht zwar im 
Datenblatt des jeweiligen Mikrocontrollers drin, aber das hilft einem 
nicht weiter, dann muss man erst wieder in die Header-Datei des 
Mikrocontrollers gucken und schauen, für welchen Interrupt-Vektor (in 
diesem Fall 0xFFF2) das ganze passt. Aber zur Verwirrung, waren Lücken 
in den Kommentaren, aus denen nicht ersichtlich war, das dieser Vektor 
auch für mein CCTL2 Register und desssen InterruptFlag zuständig ist....

von muhh (Gast)


Lesenswert?

Entschuldigt bitte, dass ich mein Thema jetzt nochmal "ausgrabe", aber 
darf ich den Code einfach so verwenden? Also vielleicht auch für größere 
Projekte, auch in der Uni?

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:
> Entschuldigt bitte, dass ich mein Thema jetzt nochmal "ausgrabe", aber
> darf ich den Code einfach so verwenden? Also vielleicht auch für größere
> Projekte, auch in der Uni?

Aus unserer Sicht: ja, natürlich. Deswegen wurde er ja hier im Forum 
veröffentlicht.

Aus Sicht deiner Uni: kommt wohl drauf an, was du gerade machst. Wenn es 
eine Diplomarbeit ist, würde ich einen Verweise auf das Original im 
Anhang aufführen, um nicht in den Verdacht von 'gutenbergen' zu kommen.

von muhh (Gast)


Lesenswert?

Hehe, das gute alte 'gutenbergen' :D

Nein, ich würde natürlich auf das Original verweisen, aber ich komme 
erst ins 3. Semester :) Ein bisschen Zeit habe ich noch ;)

Aber das ist super!!! Ich bin zwar normalerweise nicht der Fan davon, 
Code von anderen zu "klauen", aber das funktioniert so gut und ist, wie 
ich finde, so stark optiemiert (Behandlung von bis zu 8 Entprellungen 
parallel!!!)
das es schwer fallen dürfte da noch was rauszukitzeln.
In diesem Fall muss ich das Rad nicht neu erfinden :P

von Karl H. (kbuchegg)


Lesenswert?

muhh schrieb:

> Aber das ist super!!! Ich bin zwar normalerweise nicht der Fan davon,
> Code von anderen zu "klauen", aber das funktioniert so gut und ist, wie
> ich finde, so stark optiemiert (Behandlung von bis zu 8 Entprellungen
> parallel!!!)

Das Gesicht deines Betreuers würde ich gerne sehen, wenn er den Code zu 
Gesicht bekommt :-)
Allerdings bin ich guter Dinge. Ich denke du hast den Code tatsächlich 
verstanden, so dass du ihm ihn erklären kannst.

von muhh (Gast)


Lesenswert?

Jop, das könnte ich, ich hab hier mehrere Seiten Papier, auf denen ich 
mit immer ausprobiert habe, wie der Counter hochzählt und wann ein 
gedrückter Taster erkannt wird, so wie ich das hier auch gepostet habe 
;)

Habe aber noch eine Frage. Ich habe jetzt auch das UART von meinem 
MSP430 zum Laufen bekommen und schicke mir jetzt immer fleißig selbst 
Nachrichten ;)
Ich möchte den UART aber mit einer PC-gesteuerten PWM-Reglung 
kombinieren.

Damit der Code nicht zu unübersichtilich wird, möchte ich die 
Tastenentprellung in eine "debouncing.h" auslagern. Was ich mich aber 
Frage ist, was passiert mit diesen Variablen?
1
volatile uint8_t key_state;                                // debounced and inverted key state: bit = 1: key pressed
2
volatile uint8_t key_press;                                // key press detect
3
volatile uint8_t key_rpt;                                 // key long press and repeat                                 // key long press and repeat
4
volatile uint8_t rpt_count;

Kommen die mit in die Bibliothek oder müssen die in der "main.c" 
deklariert werden?

von W. M. (muhh)


Lesenswert?

Oh... was ich noch übersehen habe und nochmal nachfragen möchte:
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);

Wir hatten weiter oben bereits geklärt, das der Wert zunächst nach 
16-bit, dann nach 8-bit gecastet wird.

TCNT0 ist vermutlich das Counter Register?

1024 steht für den prescaler.

10e-3 steht für die Einheit ms?

0.5 rundet auf?

Achso, das negative Vorzeichen dient dazu, den Timer auf einen Wert zu 
setzen, von dem aus er 10ms bis 0x00 braucht? Um sozusagen die Zeit, die 
die Interrupt-Routine braucht abzufangen?

Wie kommt man auf die Berechnung?

von Uwe S. (de0508)


Lesenswert?

Hallo Willy,

ja so ein 8 Bit Timer0 beim Atmel AVR µC zählt im Mode 0 von TCNT0 bis 
256 mod 256 = 0 und löst dann einen Interrupt aus.

Dieses Timer0 Overflow Interrupt Signal führt zum Aufruf der zugehörigen 
Interruptfunktion.

Die Berechnung ist vielleicht etwas einfacher, wenn Dir folgendes 
bekannt ist:

* F_CPU ist ein DEFINE mit der CPU Frequenz in Hz.
* "1024" ist der (Frequenz-)Vorteiler von Timer0
* "10e-3" entspricht 0,001 Sekunden das sind 1ms oder 1000Hz
* "+0.5" ist ein Rundungswert, da das Macro von Präprozessor in 
Gleitkommazahl‎ (floating point) berechnet wird.

Da wir also
1
#define T0_CNT (uint16_t)(F_CPU / 1024 * 10e-3 + 0.5)
Schritte bis zum Überlauf benötigen,
rechnen wir weiter
1
TCNT0 = (uint8_t)(256 - T0_CNT);
und erhalten den Startwert für den Timer0.

Vielleicht ist diese Darstellung einfacher zu verstehen.

von W. M. (muhh)


Lesenswert?

Hier mal die Berechnung mit F_CPU = 10e6:

oder


Also das zweite Ergebniss würde ja irgendwie mehr Sinn machen, 
allerdings weiß ich nicht, wass passiert, wenn man das in 8-bit 
umwandelt...

von W. M. (muhh)


Lesenswert?

976563 entspricht 0x0EE6B3

(uint16_t) (0x0EE6B3) = 0xE6B3
(uint8_t) (0xE6B3) = 0xB3 = 179

Und wenn der Timer die MasterClock mit einem prescaler von 1024 benutzt, 
dann dauert ein Erhöhung von TCNT0 um 1

Was habe ich falsch gemacht?

von Uwe S. (de0508)


Lesenswert?

Sorry,

ich habe eine Zahl falsch gelesen und 0,001s = 1000Hz angenommen.

Peter (peda) rechnete aber mit
1
F_CPU = 1MHZ

Also erhalten wir:
1
#define T0_CNT (uint16_t)(1.0 * F_CPU /1024 /100 + 0.5) // 100Hz

und das berechnet ergibt: T0_CNT = (uint16_t)(1.000.000 /1024 /100 +0.5) 
= 10
1
TCNT0 = (uint8_t)(256 - T0_CNT);

Und somit ist TCNT0 = (uint8_t)(256 - 10) = 246

von W. M. (muhh)


Lesenswert?

oh okay :) das hatte ich vorhin auch raus...
Je länger man dran sitzt, desto komischer rechnet man :S

Bitte entschuldigt, wenn ich mich irre, aber kann es sein, dass die 
Berechnung in dem Beispiel von Herrn Dannegger nicht ganz korrekt ist?

Sie müsste anstelle von
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);

eine 10er-Potenz weniger haben, also:
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-2 + 0.5);

oder hat die unterschiedliche Potenz was mit der Schreibweise zu tun?

von Uwe S. (de0508)


Lesenswert?

Hallo,

so ist es richtig:
1
T0_Tick = 10e-3 = 10* 10^-3 = 10/1000 = 1/100

von Hannes L. (hannes)


Lesenswert?

Willy M. schrieb:
> aber kann es sein,

Was hältst Du Dich eigentlich mit dieser auf AVR-GCC optimierten 
Timer-Berechnung auf? Bring den Timer Deines MSP dazu, Interruprs im 
Abstand von 5 bis 40 ms zu generieren, in denen dann die Entprellung 
läuft. Da die meisten Programme sowiso einen Timer-Interrupt als 
Zeitbasis brauchen, ist es sinnvoll, diesen so zu dimensionieren, dass 
er die Entprellung nebenher mit erledigen kann. Wenn ich einen 
Sekundentakt brauche und Tasten zu entprellen habe, dann erzeuge ich 
mittels Timer-Interrupt ein Intervall von 10 ms oder 20 ms, aus dem ich 
mittels Nachteiler die Sekunden ableite. Ist noch ein Drehgeber 
abzufragen und/oder ein Text-LCD (mit Bildschirmspeicher im 
Controller-RAM) anzusteuern, dann bevorzuge ich 1 ms als Basistakt für 
Drehgeberabfrage und LCD-Update und leite die anderen Takte 
(Entprellung, Uhr) durch weitere Teiler davon ab. Allerdings nicht auf 
MSP und nicht in C, sondern auf AVR in ASM.

muhh schrieb:
> ich würde natürlich auf das Original verweisen,

Das mache ich auch. In meinen Quelltexten steht bei der 
Tastenentprellung grundsätzlich ein Hinweis, dass der Algorithmus bei 
Peter Dannegger geklaut wurde... ;-) Ich verwende ihn aber in 
verschiedenen Varianten, mal in der ISR, mal als Job der Mainloop, vom 
Timer-Int (oder auch ADC-Int) synchronisiert, mal mit Autorepeat, mal 
ohne, mal mit Kurz/Lang-Unterscheidung, mal mit Shift-Funktion einer 
Taste, also immer so, wie ich es gerade brauche.

...

von W. M. (muhh)


Lesenswert?

Hey Hannes,

danke für die Hinweise ;) Das mit dem Drehgeber und 1ms ist ein guter 
Tipp. Das werde ich so machen, sobald ich einen habe.

hannes schrieb:
>Was hältst Du Dich eigentlich mit dieser auf AVR-GCC optimierten Timer-Berechnung 
auf?

Naja, das schöne bei dem Code ist doch, das er für fast alle Frequenzen 
(alle für MC gebräuchlichen)einen Counterwert ausgibt, der ungefähr 
~10ms des Timers beansprucht, bis dieser wieder ein Interrupt ausliest. 
Dadurch muss ich nicht jedes mal den Wert von Hand neu berechnen.
Dazu kommt noch, dass ich immer ein ziemlich schlechtes Gewissen habe, 
wenn ich Code von anderen benutze und noch schlimmer wird es, wenn ich 
nichtmal verstehe, warum das so funktioniert wie es funktioniert... :S

Was die Varianten für die Interrupts betrifft, ich kann leider erst die 
Timer benutzten, das UART und da hörts auch schon auf :S Ich bin leider 
noch nicht so weit, habe aber auch erst mit dem MSP430 angefangen, als 
ich diesen Thread erstellt habe ;)

Was ich jetzt vorhabe ist, über das UART die Pulsweite meines 
PWM-Signals zu steuern :) Mal schauen, ob das so funktioniert wie ich es 
mir erhoffe.
Wenn ich diese Baustelle fertig habe, dann werde ich mich mal an dem ADC 
auslassen und gucken, was man damit alles für "unfug" treiben kann :)

von Hannes L. (hannes)


Lesenswert?

Willy M. schrieb:
> habe aber auch erst mit dem MSP430 angefangen,

Da kann ich nicht mitreden, mit dieser Controllerfamilie werde ich mich 
nicht befassen. Mich stört daran der geringe Spielraum betreffs 
Versorgungsspannung.

Ich steuere oft FETs über PWM an, auch mal Relais, da komme ich mit 3,3V 
nicht allzuweit. Ich betreibe die AVRs auch gern mal aus der ersten 
Zelle eines LiIon-Akkupacks, also mit 3V (leer) bis 4,2V (voll). Dabei 
habe ich den weiten Versorgungsspannungsbereich der AVRs schätzen 
gelernt.

Willy M. schrieb:
> Naja, das schöne bei dem Code ist doch, das er für fast alle Frequenzen
> (alle für MC gebräuchlichen)einen Counterwert ausgibt, der ungefähr
> ~10ms des Timers beansprucht, bis dieser wieder ein Interrupt ausliest.
> Dadurch muss ich nicht jedes mal den Wert von Hand neu berechnen.

Das sollte man aber trotzdem tun. Es ist gut für das Verständnis der 
Funktion und schützt auch vor Fehlern durch suboptimale Wahl des 
Vorteilers. Da kann es durchaus passieren, dass das Ergebnis nicht in 
die Variable passt und dadurch Müll eingestellt wird.

...

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.