Forum: Mikrocontroller und Digitale Elektronik MSP430 Timer Überlauf


von Em G. (exe87)


Lesenswert?

Hallo :)
Ich habe mal eine Frage zu dem Timer bzw dem Überlauf und die Auswertung 
davon. Und zwar habe ich ein Rechtecksignal, in dem ich die Dauer des 
jeweiligen High bzw Low Pegels messen will.
Ich habe bei Flankenwechsel also ein Interrupt ausgelöst, welches die 
Periodendauer bestimmen soll. Wie bekomme ich das aber hin, wenn der 
Timer (cont mode) einmal überläuft. Aus dem Family User Guide werde ich 
nicht schlau und hier im Forum habe ich leider auch nicht das passende 
gefunden.
Ich habe zwar schon ein wenig Probiert, jedoch ist dies nur für einen 
einzigen Überlauf sinnvoll(soweit ich das richtig gemacht habe!?!?).
Ein kleines Problem nebenbei noch: Warum löst er mal nur auf steigenden 
Flanken und mal auf fallenden Flanken aus, ändert sich jedesmal, wenn 
ich das programm auf den uC lade?

Danke schonmal
1
#include <msp430G2153.h>
2
3
#pragma vector=PORT1_VECTOR
4
__interrupt void Port_1(void)
5
{
6
//Periodendauer berechnen
7
  if(stop>start)
8
  {
9
    periode=stop-start;
10
  }
11
  if(stop<start){
12
    periode=(stop+(65535))-start;
13
  }
14
  start=stop;
15
16
  P1OUT ^= BIT0;
17
P1IFG &= ~BIT2;
18
  
19
20
}//interrupt
21
22
int main()
23
{
24
  WDTCTL = WDTPW | WDTHOLD;          //WatchdogTimer ausschalten
25
26
//---------------------------------------------------------------------------
27
28
  //Port Initialisierung
29
  P1DIR |= BIT0;                //Ausgang fuer BIT0
30
  P1SEL = 0x00;                //I/O Funktion
31
  P1IES ^= BIT2;                //Interrupt bei Flankenwechsel an BIT2
32
  P1IFG &= ~BIT2;                //Interruptflag an BIT2 loeschen
33
  P1IE |= BIT2;                //Interrupt an BIT2 erlauben
34
35
//---------------------------------------------------------------------------
36
37
  //Timer_A Initialisierung
38
  BCSCTL1 = CALBC1_16MHZ;
39
  DCOCTL = CALDCO_16MHZ;
40
  TACTL = TASSEL_2;              //SMCLK
41
  TACTL |= TACLR;                //Ruecksetzen des Zaehlers
42
  TACCTL1 = CM_3 + SCS + CAP + CCIE;
43
  TACTL |= MC_2;                //Starte Timer_A in continuous mode
44
  _enable_interrupts ();
45
46
//---------------------------------------------------------------------------
47
48
  //Endlosschleife
49
  while(1)
50
  {}
51
}

von Karl H. (kbuchegg)


Lesenswert?

Em Gr schrieb:

> Ich habe bei Flankenwechsel also ein Interrupt ausgelöst, welches die
> Periodendauer bestimmen soll. Wie bekomme ich das aber hin, wenn der
> Timer (cont mode) einmal überläuft. Aus dem Family User Guide werde ich
> nicht schlau und hier im Forum habe ich leider auch nicht das passende
> gefunden.

Zuallererst mal:

Wenn deine Variablen start und stop unsigned 16-Bit Variablen sind, dann 
kannst du dir das hier

  if(stop>start)
  {
    periode=stop-start;
  }
  if(stop<start){
    periode=(stop+(65535))-start;
  }

sparen.

Durch die unsigned Arithmetik kommt auch dann das richtige Ergebnis 
raus, wenn stop kleiner als start ist.

D.h. wenn zwischen start und stop nur ein einziger Überlauf liegt, UND 
du nicht mehr als 65535 als Differenz rausbekommen kannst, dann reicht 
es, wenn du rechnest

    periode = stop - start;

und in allen Fällen kommt das richtige Ergebnis raus.

Nur dann, wenn deine Differenz prinzipiell größer als 65535 werden kann, 
dann musst du die Überläufe auch tatsächlich berücksichtigen.
Und dann geht das so:
Wenn stop größer/gleich als start ist, dann werden für alle angefallenen 
Überläufe jeweils 65536 addiert. Andernfalls wird die Anzahl der 
Überläufe um 1 vermindert, weil durch die 16-Bit Subtraktion 1 Overflow 
schon aus der Rechnung rausfällt.

   if( stop < start )
     nrOverflows--;

   periode = (stop - start) + nrOverflows * 65536UL;


(und wenn du in dieses allgemeine Schema probehalber 1 Overflow 
einsetzt, dann kommt dann auch raus, dass man 1 Overflow überhaupt nicht 
berücksichtigen muss, weil sich dieser 1 Overflow durch das if zu 0 
reduziert)

Leider hast du (wie so viele vor dir), wieder mal die 
Variablendefinitionen nicht gezeigt, so dass man nicht sagen kann, ob da 
16 Bit unsigned gerechnet wird (werden kann) oder nicht. Die 
Definitionen bzw. die Datentypen von Variablen sind WICHTIG! Sie 
bestimmen im Detail, wie Berechnungen durchgeführt werden!

von Karl H. (kbuchegg)


Lesenswert?

Em Gr schrieb:


> Ein kleines Problem nebenbei noch: Warum löst er mal nur auf steigenden
> Flanken und mal auf fallenden Flanken aus, ändert sich jedesmal, wenn
> ich das programm auf den uC lade?

(Ohne den MSP im Detail zu kennen)
Ich schätze mal, weil du hier

  P1IES ^= BIT2;                //Interrupt bei Flankenwechsel an BIT2

das Bit toggelst.
Setze es gezielt auf 0 oder auf 1, je nachdem wie du es brauchst. Beim 
'Hochfahren' des Programms willst du gesicherte Verhältnisse und nicht 
davon abhängen, wie das Bit vorher stand und auf welch dubiosen Wegen es 
einen Reset überlebt hat.

Du willst es auf 1 haben? Dann setze es auch auf 1!
   P1IES |= BIT2;

von PIES (Gast)


Lesenswert?

PIES steht für PortInterruptEdgeSelect.


Each PxIES bit selects the interrupt edge for the corresponding I/O pin.
Bit = 0: The PxIFGx flag is set with a low-to-high transition
Bit = 1: The PxIFGx flag is set with a high-to-low transition

Allenfalls musst du die Anfangsbedingung lesen bzw. kennen.

von Em G. (exe87)


Lesenswert?

Vielen Dank für die schnellen Antworten :) TOP!!

Okay, den Sinn habe ich halbwegs verstanden, nur wie füge ich dies in 
den Code ein, ich müsste ja quasi eine Abfrage haben, ob der Timer 
übergelaufen ist und wenn, dann jedesmal nrOverflow++; ausführen, aber 
wenn oder habe ich das jetzt immer noch nicht richtig verstanden?
Wieso bei nicht vorhanden Überlauf nrOverflow--;?

Hier ist mal der Code, den ich bis jetzt habe, ich dachte vorhin, das es 
reichen würde.
Bitte nicht meckern wie, ich es angesetzt habe^^ bin noch Anfänger.
Es geht darum, ein Signal auszuwerten, welches 4 Byte enthält, und diese 
sollen nun aus dem "eingefangenen" Signal herausgefiltert werden.
kurze Periode(egal ob low oder high) ist ne 0 und lange ist 1.
Dann wird dies in "zahl" geschrieben, und sobald zahl 8 bit voll hat, 
wird zahl in zahl1-4 geschrieben und es fängt wieder von vorne an.
1
#include <msp430G2153.h>
2
3
#define signalmin 50
4
#define signalminmax 100
5
#define signalmax 200
6
7
//Globale Variablen Initialisieren
8
volatile unsigned int stop;
9
volatile unsigned int start;
10
volatile unsigned  periode;
11
12
#pragma vector=PORT1_VECTOR
13
__interrupt void Port_1(void)
14
{
15
//Periodendauer berechnen
16
  if(stop>start)
17
  {
18
    periode=stop-start;
19
  }
20
  if(stop<start){
21
    periode=(stop+(65535))-start;
22
  }
23
  start=stop;
24
25
  P1OUT ^= BIT0;
26
  P1IFG &= ~BIT2;
27
}//interrupt
28
29
int main()
30
{
31
32
  int bit, zahl,zahl1,zahl2,zahl3,zahl4;
33
  int bit_z=0;
34
  int byte_z=0;
35
  int bytemerker=0;
36
37
  WDTCTL = WDTPW | WDTHOLD;          //WatchdogTimer ausschalten
38
39
//---------------------------------------------------------------------------
40
41
  //Port Initialisierung
42
  P1DIR |= BIT0;                //Ausgang fuer BIT0
43
  P1SEL = 0x00;                //I/O Funktion
44
  P1IES ^= BIT2;                //Interrupt bei Flankenwechsel an BIT2
45
  P1IFG &= ~BIT2;                //Interruptflag an BIT2 loeschen
46
  P1IE |= BIT2;                //Interrupt an BIT2 erlauben
47
48
//---------------------------------------------------------------------------
49
50
  //Timer_A Initialisierung
51
  BCSCTL1 = CALBC1_16MHZ;
52
  DCOCTL = CALDCO_16MHZ;
53
  TACTL = TASSEL_2;              //SMCLK
54
  TACTL |= TACLR;                //Ruecksetzen des Zaehlers
55
  TACCTL1 = CM_3 + SCS + CAP + CCIE;
56
  TACTL |= MC_2;                //Starte Timer_A in continuous mode
57
  _enable_interrupts ();
58
59
//---------------------------------------------------------------------------
60
61
  //Endlosschleife
62
  while(1)
63
  {
64
        //Periodenauswertung
65
        if(periode >= signalmin && periode <= signalminmax)
66
          bit=0;
67
        if(periode >= signalminmax && periode <= signalmax)
68
          bit=1;
69
70
        //Bitzaehlung
71
        zahl |= (bit<<bit_z);
72
        bit_z++;
73
74
        //Abfrage ob Byte beschrieben werden darf
75
        if(bit_z==8){
76
          bit_z = 0;
77
          bytemerker = 1;
78
        }
79
        else
80
          bytemerker=0;
81
        if(byte_z==0)
82
          byte_z = 1;
83
        if(bytemerker==1)
84
        {
85
          if(byte_z==1)
86
          {
87
            zahl1 = zahl;
88
            zahl = 0x00;
89
          }
90
          if(byte_z==2)
91
          {
92
            zahl2 = zahl;
93
            zahl = 0x00;
94
          }
95
          if(byte_z==3)
96
          {
97
            zahl3 = zahl;
98
            zahl = 0x00;
99
          }
100
          if(byte_z==4)
101
          {
102
            zahl4 = zahl;
103
            zahl = 0x00;
104
          }
105
          byte_z++;
106
          if(byte_z==5){
107
            byte_z = 1;
108
            //startbitmerker=0;
109
          }
110
        }//if bytemerker
111
112
}

von Em G. (exe87)


Angehängte Dateien:

Lesenswert?

Hier noch einmal als Beispiel das Rechtecksignal..
Vielleicht versteht man dann besser, wie ich es meine.

von Em G. (exe87)


Lesenswert?

Hat vielleicht jemand zu später Stund noch ne Ahnung?
*in dem Interrupt hatte ich noch vergessen:
1
stop=TAR;

.. falls es weiterhilft.

Schönen Abend :)

von TimerA (Gast)


Lesenswert?

Em Gr schrieb:
> Hat vielleicht jemand zu später Stund noch ne Ahnung?

1. Den Timer so konfigurieren oder teilen, dass kein Überlauf erfolgen 
kann.
2. Timer mit Flankenwechsel synchronisieren.
3. Timer Interrupt nutzen und Überlauf erkennen und zählen.
4. In der Port1 ISR die Flankenauswertung umschalten.

von Em G. (exe87)


Lesenswert?

TimerA schrieb:
> 4. In der Port1 ISR die Flankenauswertung umschalten.

Klasse, es funktioniert zumindest schonmal mit dem Flankenwechsel :)
Mit dem TimerInterrupt bekomme ich jetzt auch halbwegs hin.
Danke ;)

von Karl H. (kbuchegg)


Lesenswert?

Em Gr schrieb:

> Klasse, es funktioniert zumindest schonmal mit dem Flankenwechsel :)
> Mit dem TimerInterrupt bekomme ich jetzt auch halbwegs hin.
> Danke ;)


Da dich ja sowieso nur interessiert, ob die Länge in bestimmten 
Bereichen ist
1
        //Periodenauswertung
2
        if(periode >= signalmin && periode <= signalminmax)
3
          bit=0;
4
        if(periode >= signalminmax && periode <= signalmax)
5
          bit=1;

und es dir auf die ganz genauen Werte eh nicht ankommt, würde ich an 
deiner Stelle den Vorteiler des Timers soweit erhöhen, bis die erwartete 
Pulslänge in einem Bereich ist, so dass nicht mehr als 65535 Timerticks 
anfallen und ich somit Überläufe überhaupt nicht berücksichtigen muss. 
Ich muss mir ja nicht selbst das Leben schwer machen.

von Em G. (exe87)


Lesenswert?

Mit dem Timer Overflow funktioniert es leider noch nicht so ganz.
Ich glaube, dass ich das falsch verstanden/programmiert habe. Er zählt 
zwar hoch(wenn der Zähler wieder auf 0 gesetzt wird) und wird auch bei 
erkanntem Signal(Flankenwechsel) ausgewertet und wieder auf 0 gesetzt, 
jedoch sind die Werte meiner Meinung nach zu hoch(mal 20-30 und mal ca. 
1000-2000)..
Dieser dürfte doch bei der kurzen Dauer der Flankenwechsel höchstens ein 
paar Werte ca.1-5 hoch gezählt werden und nur bei der Ruhephase in die 
Höhe gehen!?

Karl Heinz Buchegger schrieb:
> würde ich an
>
> deiner Stelle den Vorteiler des Timers soweit erhöhen


Also mal meine Gedankengänge:
Ich hab ein Rechtecksignal, mit unterschiedlich langen high und low 
Flankenwechsel,
Kurzer Wechsel = 0, (ca. 40µs)
Langer/längerer Wechsel = 1, (ca 70µs)
Nun kommt eine Ruhephase, die ca. 20-30ms dauert.
Dann kommt wieder das Signal.

Soweit ich errechnet habe, beträgt einmal bis 65535 zählen ca. 4ms.
Deswegen kann ich doch denke ich mal gar keine Vorteiler setzen, da 
sonst der schnelle Wechsel der Flanken zu schnell für ein ordentliches 
Ergebnis sind oder sehe ich das falsch?

Wegen dme Overflowzähler:
Zwischen dem include und dem Port1 Interrupt habe ich folgendes 
eingefügt:
1
//Timer Ueberlaufe auswerten
2
#pragma vector=TIMER0_A1_VECTOR
3
__interrupt void Timer_A(void){
4
  taov++;      //Overflows zaehlen
5
  TACTL &= ~TAIE;     //Interrupt deaktivieren
6
}

In den Port1 Interrupt:
1
taov=0;     //Overflows auf 0 setzen

Und in die Main:
1
TACTL |= TAIE;     //wieder aktivieren

von Em G. (exe87)


Angehängte Dateien:

Lesenswert?

Hier nochmal ein Bild vom Osz.
Gelb: ankommende Signal
Blau: Port1 Interrupt
Violett: Timer1 Interrupt

Vielleicht hilft es ja :)

von Timer_A (Gast)


Lesenswert?

Em Gr schrieb:
> Vielleicht hilft es ja

Wem?

Du hast die notwendigen Tipps erhalten, jetzt bist dur dran.

Em Gr schrieb:
> Also mal meine Gedankengänge:

Vergleiche den Timer mit einer Uhr: Er zählt die Sekunden. Bei einer 
Minute erfolgt der Überlauf. Die Sekunden beginnen wieder bei Null und 
die Minuten werden mit einem anderen Zeiger gezählt.
Der Sekundentakt reicht, um Minuten, Stunden, Tage, Wochen, ... 
abzuleiten.

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.