Forum: Mikrocontroller und Digitale Elektronik Entprellungsproblem MSP430


von A. H. (dernetteeddie1978)


Lesenswert?

Hi,

ich versuche schon die ganze Zeit mein Entprellungsproblem zu lösen und 
schnalls einfach net.

Eigentlich will ich nur die rote LED blinken lassen und mit Druck auf 
den Taster die güne ein- und wieder ausschalten.

Leider schaltet die güne LED kurz nach Tastendruck ab und zu ein zweites 
Mal (je nachdem entweder ein oder aus) aber auch nicht immer. Auch 
schaltet sie bei Tastendruck manchmal verzögert.

Eigentlich dachte ich mir der Tastendruck löst einen Interrupt aus, in 
dem  die Interrupts des P1 disabled und die Flags gelöscht werden und 
die grüne LED geschaltet wird. Innerhalb dieses Interrupts schalte ich 
den Watchdog auf Intervall und mit erreichen desselben wird ein zweiter 
Interrupt ausgelöst in dem der Interrupt für P1 wieder aktiviert wird. 
Dann solls von vorne losgehen.

Was ich rausfinden konnte ist dass die Interruptroutine von P1 teilweise 
zweimal ausgelöst wird. Ich hatte in beide ISR`s je eine Zählvariable 
eingefügt. Die Frage ist wieso? In der ISR von P1 werden die Interrupts 
von P1 ja disabled und erst nach 250ms wiedre aktiviert. Von daher 
solltre ein prellen ausgeschlossen sein.
1
/*
2
 * main.c
3
 */
4
5
6
#include "msp430g2553.h"
7
8
void InitializeClocks(void);
9
10
void main(void) {
11
  
12
  InitializeClocks();
13
14
  WDTCTL = WDTPW + WDTHOLD;
15
  IFG1 &= ~OFIFG;
16
  P1DIR = BIT6+BIT0;
17
  P1REN = BIT3;
18
  P1OUT = BIT3;
19
  P1IES = BIT3;
20
  P1IFG = 0;
21
  IFG1 &= ~WDTIFG;                     /* clear interrupt flag */
22
  P1IE = BIT3;
23
  _BIS_SR(GIE);
24
25
  while(1)
26
  {
27
    __delay_cycles(500000);
28
      P1OUT ^= BIT0;
29
  }
30
}
31
32
void InitializeClocks(void)
33
{
34
  BCSCTL1 = CALBC1_1MHZ;                    // Set range
35
  DCOCTL = CALDCO_1MHZ;
36
  BCSCTL2 &= ~(DIVS_3);                     // SMCLK = DCO = 1MHz
37
  BCSCTL1 |= DIVA_1;                        // ACLK/2
38
  BCSCTL3 |= LFXT1S_2;                      // ACLK = VLO
39
}
40
41
#pragma vector=PORT1_VECTOR
42
__interrupt void PORT1_ISR (void)
43
{
44
  P1IE &= ~BIT3;
45
  P1IFG = 0;
46
  IE1 |= WDTIE;
47
  P1OUT^=BIT6;
48
  WDTCTL = WDT_ADLY_250;
49
50
51
}
52
53
// WDT Interrupt Service Routine used to de-bounce button press
54
#pragma vector=WDT_VECTOR
55
__interrupt void WDT_ISR(void)
56
{
57
  IE1 &= ~WDTIE;                   /* disable interrupt */
58
    IFG1 &= ~WDTIFG;                 /* clear interrupt flag */
59
    WDTCTL = WDTPW + WDTHOLD;        /* put WDT back in hold state */
60
    P1IE |= BIT3;                  /* Debouncing complete */
61
}

von Karl H. (kbuchegg)


Lesenswert?

A. H. schrieb:


> eingefügt. Die Frage ist wieso? In der ISR von P1 werden die Interrupts
> von P1 ja disabled und erst nach 250ms wiedre aktiviert.

Tja. Aber nur weil du Interrupts disabled hast, werden sie ja deswegen 
nicht verworfen.
Selbstverständlich wird ein Interrupt Ereignis auch dann registriert, 
wenn die Interrupts disabled sind. Einzig das Auslösen des Aufrufs der 
ISR unterbleit, aber der µC hat sich in einem Bit in einem Register 
gemerkt, dass eine entsprechende 'Anforderung' eingetroffen ist. Und 
sobald die Interrupts wieder freigegeben werden, wird dann dieser 
Interrupt 'nachgeholt'.

Also entweder du suchst dir in der Doku, welches Bit in welchem Register 
diese 'Speicherung' macht und löscht es, bevor du die Interrupts wieder 
freigibst oder du akzeptierst, dass man zum Abfragen von Tasten keine 
externen Interrupts braucht, sondern das genau das im Grunde genommen 
mehr Aufwand ist, als wie wenn du das genau so machst wie es der Rest 
der (vernünftigen) Programmiererwelt auch macht: mit einem Timer, der 
alle paar Millisekunden einen Interrupt auslöst, in dem ganz banal 
nachgesehen wird, ob die Taste gedrückt ist und wenn in der Interrupt 
Routine das zb 4 mal hintereinander festgestellt wurde, dann gilt die 
Taste als gedrückt. So schnell kannst du als Mensch eine Taste gar nicht 
drücken und wieder loslassen, dass der µC mit seiner 'Nachschau' alle 
paar Millisekunden das nicht mitbekommen würde.

von A. H. (dernetteeddie1978)


Lesenswert?

Okay,

danke für die schnelle Hilfe. Ich dachte man sollte nen Taster per 
Interrupt abfragen eben damit nicht ständig nachgeschaut werden muss. So 
hette ich es auch mal in nem Lehrbuch gelesen. Ist auch mein erstes µC 
Programm...

Den Code hatte ich aus dem, von Werk beim Launchpad mitgelieferten, 
Demoprogramm Temperaturesense genommen und adaptiert.

So oder so ist dein Vorschlag natürlich auch ne gute Idee an sowas 
heranzugehen. Vor allem weil es kompliziertere Entprellmechanismen 
umgeht.

Was unun den Fehler anbelangt, so versteh ich das noch nicht ganz. Ich 
lösche ja in der ISR alle Flags, und schalte dann nur den Enable für den 
Port des Tasters wieder ein. Der Rest ist aus.

LG

von A. H. (dernetteeddie1978)


Lesenswert?

Okay, habs grad gerafft. Ich muss die Flags natürlich nach der 
Warteschleife löschen.

von A. H. (dernetteeddie1978)


Lesenswert?

1
#pragma vector=PORT1_VECTOR
2
__interrupt void PORT1_ISR (void)
3
{
4
  P1IE &= ~BIT3;
5
  IE1 |= WDTIE;
6
  P1OUT^=BIT6;
7
  WDTCTL = WDT_ADLY_250;
8
  P1IFG = 0;

von A. H. (dernetteeddie1978)


Lesenswert?

Okay, das wars auch noch net. So schalte ich die Interrupts ja natürlich 
auch wieder vor Löschung der Flags ein. Also ab mit dem Befehl in die 
WDT ISR:
1
#pragma vector=PORT1_VECTOR
2
__interrupt void PORT1_ISR (void)
3
{
4
  P1IE &= ~BIT3;
5
  IE1 |= WDTIE;
6
  P1OUT^=BIT6;
7
  WDTCTL = WDT_ADLY_250;
8
}
9
10
// WDT Interrupt Service Routine used to de-bounce button press
11
#pragma vector=WDT_VECTOR
12
__interrupt void WDT_ISR(void)
13
{
14
  IE1 &= ~WDTIE;                   /* disable interrupt */
15
    IFG1 &= ~WDTIFG;                 /* clear interrupt flag */
16
    WDTCTL = WDTPW + WDTHOLD;     /* put WDT back in hold state */
17
    P1IFG = 0;
18
    P1IE |= BIT3;                  /* Debouncing complete */
19
}


So, nun scheint nix mehr zu prellen.

Aber das Programm ist sehr langsam. Die Tastendrücke werden nur mit fast 
1 Sek. Wartezeit dazwischen registriert und auch wenn der Druck 
registriert ist so geht die grüne LED nicht immer unmittelbar an. 
Eigentlich sollte das WDT Intervall ja nur 250ms dauern.
Evtl. ist die Taktung falsch eingestellt. Wenn das Programm aber an der 
Stelle hängt, dann dürfte die rote LED in der zeit ja eigentlich nicht 
blinken. Die main ist ja eigentlich unterbrochen. Naj, mal schauen ob 
ich das auch noch finde.

LG

von Konrad S. (maybee)


Lesenswert?

Wenn du mal von der Rumbastelei die Nase voll hast und du dich etwas 
entspannen willst, dann ist das hier ein gute Lektüre:
http://www.mikrocontroller.net/articles/Entprellung

von A. H. (dernetteeddie1978)


Lesenswert?

Danke,

den Artikel hatte ich schon gefunden. Ich wollte nur verstehen warum 
meine nicht funktioniert. Das Prinzip ist ja letztenendes gleich. Es 
wird eine gewisse Zeit gewartet und dann  der Taster wieder "scharf" 
geschaltet.

Die Lösung für die lange Wartezeit zwischen den Drücken hab ich 
mittlerweile auch gefunden. Die Taktung scheint falsch gewesen zu sein. 
Ich hab mal zwei Zeilen auskommentiert:
1
void InitializeClocks(void)
2
{
3
  BCSCTL1 = CALBC1_1MHZ;                    // Set range
4
  DCOCTL = CALDCO_1MHZ;
5
  BCSCTL2 &= ~(DIVS_3);                     // SMCLK = DCO = 1MHz
6
  //BCSCTL1 |= DIVA_1;                        // ACLK/2
7
  //BCSCTL3 |= LFXT1S_2;                      // ACLK = VLO
8
}

Nun ists schneller.

Nachdem ich mich mit SingleStep mal zwischen Breakpoints durchgeklickt 
hab, hab ich auch gesehen dass der Code nach  Start des WDT Intervalls 
nicht direkt in die WDT ISR springt sondern erstmal wieder in die main 
und kurz drauf in die WDT ISR.

Offenbar läuft der WDT Timer quasi parallel zum Rest ab. Ich hatte erst 
gedacht wenn er gestartet ist wird gewartet bis er abgelaufen ist und 
dann gehts weiter im Code.

Jetzt muss ich noch versuchen die Taktung so zu ändern das die 250ms 
kürzer werden. So ists eindeutig zu lang, da werden schnelle 
Tastendrücke verschluckt.

LG

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.