Forum: Mikrocontroller und Digitale Elektronik Erzeugung eines zum Eingang phasengleichen Signals mit dem MSP430


von Simon H. (simon12)


Angehängte Dateien:

Lesenswert?

Hallo,
ich arbeite mich gerade in den MSP430 ein (aktuell noch mit dem 
Launchpad, MSP430G2553) und habe nun das erste programmiertechnische 
Problem;-) Ich möchte über den Capture Mode von Timer1_A1 die Frequenz 
am Pin P2.1 messen. Dazu wird der Timer beim ersten Aufruf der ISR 
gestartet und nach 5 Perioden der Zählerstand ausgelesen. Anschließend 
soll Timer0_A1 im Up-Mode die LED an Pin 1.0 mit der gemessenen Frequenz 
phasengleich zum Eingang toggeln (das mit der LED ist nur testweise). 
Das Problem ist, dass es zu einer kontinuierlichen Phasenverschiebung 
zwischen dem gemessenen Signal und dem am µC erzeugten Signal kommt (s. 
Oszi-Bilder: In türkis das Signal der LED an P1.6, dass bei jedem ISR 
Aufruf getoggelt wird, in gelb das Signal der LED an P1.0 die mit der 
berechneten Frequenz getoggelt wird). Jemand eine Idee woher die 
Phasenverschiebung kommt und was ich da am Code ändern muss? Hat das was 
mit der verwendeten Clock zu tun? Würde gerne die 32kHz bei behalten 
weil ich das ganze dann im LPM3 laufen lassen kann. Gemessene Frequenzen 
sind im Bereich 50 – 100 Hz.

Vielen Dank und viele Grüße
Simon

Hier noch der Code:
1
#include <msp430.h> 
2
3
volatile int period, startTime, stopTime;             // timer-Variablen
4
volatile int counter = 0;                             // counter-Variable
5
6
int main(void) {
7
  WDTCTL = WDTPW | WDTHOLD;  // Stop watchdog timer
8
    P1DIR |= 0x40 + 0x01;                             // P1.1 + P1.6 als Ausgang
9
    P2SEL |= 0x02 + 0x10;                             // P2.1 + P2.4 als Eingang für Timer_A
10
    TA0CTL = TASSEL_1 + MC_1 + TAIE;                  // ACLK + Up Mode + Enable Interrupt
11
    TA1CTL = TASSEL_1 + MC_2;                         // ACLK + Continuous Mode
12
    TA1CCTL2 = CM_1 + CCIS_0 + SCS + CAP + CCIE;      // Capture on rising edge + CCI2A + Synchronous capture + Capture mode
13
    TA1CCTL1 = CM_1 + CCIS_0 + SCS + CAP + CCIE;      // Capture on rising edge + CCI1A + Synchronous capture + Capture mode
14
    __enable_interrupt();                             // gloabale Interrupts aktivieren
15
    while(1) {
16
    }
17
    return 0;
18
}
19
20
#pragma vector = TIMER1_A1_VECTOR
21
__interrupt void Timer1_A1_ISR(void) {
22
  switch(TA1IV) {
23
    case  2:  counter++;                             // counter-Variable inkrementieren
24
        if(counter == 1) {                           // Beim ersten Interrupt-Aufruf
25
          startTime = TA1CCR1;                       // aktuellen Timer-Wert auslesen
26
          }
27
        if(counter == 6) {                           // Beim zweiten Interrupt-Aufruf
28
          period = (TA1CCR1 - startTime)/5;          // Periode bestimmen
29
          TA0CCR0 = period;
30
          counter = 0;                               // counter-Variable zurücksetzen
31
        }
32
        P1OUT ^= 0x40;                               // LED an P1.6 toggeln
33
        break;
34
      case  4:  break;
35
      case 10:  break;
36
    }
37
}
38
39
#pragma vector = TIMER0_A1_VECTOR
40
__interrupt void Timer0_A1_ISR(void) {
41
  switch(TA0IV)
42
  {
43
    case  2:  break;                                 // CCR1 not used
44
    case  4:  break;                                 // CCR2 not used
45
    case 10:  P1OUT ^= 0x01;                         // LED an P1.1 toggeln
46
              break;
47
  }
48
}

von asynchron (Gast)


Lesenswert?

Wenn du die zwei Timer (externer vom Eingangssignal, interner A0) frei 
laufen lässt, wird es immer eine Phasenverschiebung geben. Du musst mit 
dem Periodenbeginn vom Eingangssignal synchronisieren.

von Simon H. (simon12)


Lesenswert?

Ich dachte ich wäre relativ synchron da ich den Timer für die 
Frequenzerzeugung beim Aufruf des Interrupts durch die externe 
Signalquelle starte. Und wenn es eine konstante Phasenverschiebung wäre, 
wäre das auch nicht schlimm das könnte man ja noch irgendwie 
kompensieren. Mein größtes Problem ist, dass die Phase sich ständig 
ändert. Kann das überhaupt mit einem einzigen Timer funktionieren oder 
sollte ich die Frequenzerzeugung mit Timer_B realisieren?

von Sascha W. (sascha-w)


Lesenswert?

Hallo,

ich weiß zwar nicht was das bringen soll ein zum Eingang identisches 
Signal zu erzeugen aber egal.
Dein Problem ist, das du mit dem Timer die Eingangsfrequenz nicht exakt 
nachbilden kannst da abhängig von Taktfrequenz und Vorteiler des Timers 
weder eine 100% exakte Messung der Periodendauer möglich ist, noch deren 
Ausgabe. Die einzige Möglichkeit ist, das du den Ausgabetimer mit jeder 
steigenden/fallenden Flanke des Einganssigals syncronisierst, so das die 
Abweichung sich nicht aufsummieren kann.

Sascha

von asynchron (Gast)


Lesenswert?

In der ISR vom Timer1 passt du zwar die Periodenlänge an, aber der 
Timer0 läuft weiter. Er wird nicht gestoppt, gelöscht und wieder neu 
gestartet, also nicht synchronisiert.

Und nebenbei,Schau dir einmal die Outmode des Timers im Family User 
Guide an. Das sollte mit nur einem Timer und reiner HW ohne ISR gehen.

von Simon H. (simon12)


Lesenswert?

@Sascha: Ja, das Unterfangen klingt erstmal ziemlich sinnfrei! Wenn es 
dich wirklich interessiert kann ich dir das gern auf noch auf ner halben 
Seite ausführen ;-)

Es ist eben die Frage ob die Phasenverschiebung nur durch den Offset 
kommt oder sich die Interrupts gegenseitig behindern (gibt ja 
verschiedene Prioritäten wenn ich das richtig gelesen habe). Aber 
vermutlich muss man das Eingangssignal ständig überwachen. Sich die 
Frequenz zu "merken" hätte den Vorteil gehabt Strom zu sparen. Leider 
geht es bei meiner Anwendung um jedes µA!

von Simon H. (simon12)


Lesenswert?

@ asynchronus: Meinst du den Timer stoppen mit
1
 TA0CTL = TASSEL_1 + MC_1
 oder
1
 TACTL |= TACLR
 ? Eig. sollte der Timer doch resetet werden wenn er überläuft, im 
Up-Mode wenn der Wert in TA0CCTR0 erreicht ist! Was meinst du mit, das 
könnte ohne ISR funktionieren?

von asynchron (Gast)


Lesenswert?

Ja, der Timer fängt nach Überlauf wieder vom Anfang an. Aber der 
Überlauf erfolgt nicht durch die Zuweisung xxCCR0!

Bsp. Der Timer läuft, noch kein Überlauf. Jetzt erfolgt intr vom 
Eingang. Du weist die neue Periode zu, die Länger ist. Der Timer läuft 
nun noch bis zum Ende der neuen Periode! Die alte Periode hast du jetzt 
verlängert! Wenn das ein paar mal passiert, bekommst du das Bild von 
oben.

von Simon H. (simon12)


Lesenswert?

Klingt einleuchtend! Eigentlich sollte es so aussehen, das aus 5 
Perioden die Frequenz ermittelt wird und die Interrupts deaktiviert 
werden. Dann sollte geschilderte Fall ja nicht mehr auftreten?! Ich 
versuch das mal zu programmieren und meld mich wieder! Danke an alle bis 
hierher!

von asynchron (Gast)


Lesenswert?

Das war nur ein Beispiel. Beim Messen der Periode bekommst du immer 
einer Fehler, denn der Timerhat ja keine unendliche Auflösung. Der 
Fehler summiert sich auch auf. Du musst also unbedingt synchronisieren, 
wenn es Phasen gleich laufen soll.

Wirf einen Blick in den family user guide. Die Outmode der Timer sind 
dort gut erklärt. Deine Frequenz Ausgabe sollte nur in HW laufen, also 
ohne ISR.

von Simon H. (simon12)


Lesenswert?

@asynchron: Meinst du mit "synchron", dass das Eingangssignal die ganze 
Zeit anliegen muss? Genau das wollte ich ja eig. vermeiden! Ich wollte 
über 5 Perioden die Frequenz messen und dann für Sagen wir max. 3 
Sekunden die Frequenz im µC halten. Ich hab das jetzt grad mal 
durchgerechnet mit dem Offset. Wenn ich mir im Debug-Modus die 
Periodendauer anschaue, liegt die ziemlich konstant bei einem Zählerwert 
von 327 selten 328. Ausgehend davon, dass der Quarz wirklich mit 32768Hz 
schwingt beträgt der Fehler nach einer Sekunde etwa 2ms. Aber nach 3 
Sekunden hab ich dann halt ne Phasenverschiebung von über 90°. Das heißt 
ja eigentlich die einzige Schraube an der ich drehen könnte wäre eine 
höhere Taktfrequenz oder öfters Synchronisieren. Was meinst du mit in HW 
laufen lassen? Wenn ich die Ereignisse der verschiedenen Output-Modes 
detektieren will läuft das doch auch über die ISR oder? Bin wie gesagt 
noch sehr neu in der MSP430 Thematik!

von asynchron (Gast)


Lesenswert?

Du hast die Phasenverschiebung zum Eingang bemängelt. Die bekommst du 
nur weg, wenn du Eingangssignal und Ausgangssignal synchronisierte. Du 
kannst bei jeder Periode oder auch alle 5, 10, ..., 1234 Perioden 
synchronisieren. Du musst entscheiden, wieviel Abweichung du toleriert.

Die reine Frequenz Erzeugung kann per HW ohne ISR erfolgen. Für die 
Frequenzänderung benötigt du ein Ereignis. Das macht aber der andere 
Timer.

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.