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
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)
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.
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
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
00000000key_state
2
3
11111101KEY_PIN
4
00000010~KEY_PIN
5
6
00000010i=key_state^~KEY_PIN
7
8
11111111ct0=~(ct0&i)
9
11111111ct1=ct0^(ct1&i)
10
11
00000010&i=ct0&ct1
12
13
00000010key_state^=i
14
15
00000010key_press|=key_state&i
Die folgenden Zeilen verstehe ich leider gar nicht :S Was hat es damit
auf sich?
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
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
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.
"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=00000010;
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?
muhh schrieb:> aber ich bin das alles nur einmal durchgegangen undbereits jetzt ist>
1
key_press=00000010;
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>
> 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.
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.
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....
>> 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
11111101ct0
2
11111111ct1
3
4
00000000key_state
5
11111101KEY_PIN
6
00000010~KEY_PIN
7
8
00000010i=key_state^~KEY_PIN
9
10
00000000ct0&i
11
11111111ct0=~(ct0&i)
12
11111101ct1=ct0^(ct1&i)
13
14
00000000i&=ct0&ct1
15
16
00000000key_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
11111111ct0
2
11111101ct1
3
4
00000000key_state
5
11111101KEY_PIN
6
00000010~KEY_PIN
7
8
00000010i=key_state^~KEY_PIN
9
10
00000010ct0&i
11
11111101ct0=~(ct0&i)
12
11111101ct1=ct0^(ct1&i)
13
14
00000000i&=ct0&ct1
15
16
00000000key_state^=i
Noch nicht. (ist erst das dritte mal, dass die Taste gedrückt
vorgefunden wurde)
1
11111101ct0
2
11111101ct1
3
4
00000000key_state
5
11111101KEY_PIN
6
00000010~KEY_PIN
7
8
00000010i=key_state^~KEY_PIN
9
10
00000000ct0&i
11
11111111ct0=~(ct0&i)
12
11111111ct1=ct0^(ct1&i)
13
14
00000010i&=ct0&ct1
15
16
00000010key_state^=i
Bingo.
Die Taste wurde 4 mal hintereinander gedrückt vorgefunden. Und erst
jetzt gilt sie als gedrückt.
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.
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 :)
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_ti,key_state,KEY_PIN,key_press;
2
staticuint8_tct0,ct1,rpt;
3
4
voidDebounce(uint8_tKEY_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
intmain(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.
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.
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(00000010)
2
{
3
cli();
4
returnget_key_press(~(00000010)&00000010);
5
~key_state&key_mask
6
}
Gibts es da einen Trick bei? Was soll die Invertierung von key_state
bewirken?
muhh schrieb:> Der Code würde dann ja so aussehen:>>
1
>get_key_short(00000010)
2
>{
3
>cli();
4
>returnget_key_press(~(00000010)&00000010);
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.
@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:
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.
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
__interruptvoidTimerA(void)....
2
3
__interruptvoidblabla(void)....
möglich?
Woher weiß den der Compiler welche Routine ausgelöst wird?
Bestimmt das nicht der Vector?
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
>__interruptvoidTimerA(void)....
2
>
3
>__interruptvoidblabla(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.
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
__interruptvoidTimer_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
__interruptvoidTimer0_A0_ISR(void)
3
{
4
staticuint8_tct0,ct1,rpt;
5
....
So gesehen ....
> weil die Namen für die Interrupt_Vektoren anders sind.
... nö, sind sie nicht. Die Pragma-Namen sind identisch.
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....
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?
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.
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
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.
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
volatileuint8_tkey_state;// debounced and inverted key state: bit = 1: key pressed
2
volatileuint8_tkey_press;// key press detect
3
volatileuint8_tkey_rpt;// key long press and repeat // key long press and repeat
4
volatileuint8_trpt_count;
Kommen die mit in die Bibliothek oder müssen die in der "main.c"
deklariert werden?
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?
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
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
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?
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.
...
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 :)
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.
...