Forum: Mikrocontroller und Digitale Elektronik Sinuserzeugung mit DAC: Probleme Interrupterzeugung für DAC durch TimerB Compareregister


von Vorname N. (brenijunior)


Angehängte Dateien:

Lesenswert?

Hallo zusammen!
Ich hoffe ihr könnt mir vielleicht weiterhelfen :)
Ich versuche im Moment einen Sinussweep von 500 Hz bis 1200 Hz mit einem 
MSP430-F2618 zu erzeugen. Dafür verwende ich den TimerB in Up/Down-Mode. 
TBCCR0 gibt mir die Periode vor, und mittels TBCCR1 möchte ich die 
Interrupts für den DAC erzeugen, um letztendlich aus dem Rechtecksignal 
einen Sinus zu erzeugen. Dafür unterteile ich TBCCR1 in 200 Stufen. 
Immer wenn TimerB den Wert von TBCCR1 erreicht hat, springt dieser in 
die entsprechende ISR und gibt einen Wert an den DAC. Anschließend setze 
ich die TBCCR1 auf einen neuen Wert (i*Periode/200).
Mein Problem ist nun folgendes: Der Sinussweep wird erzeugt, jedoch 
nicht bei der Frequenz welche ich ihm vorgebe. Der 16 MHz Quarz erzeugt 
den SMCLK, mit welchem ich den Takt des TimerB erzeuge. Ich denke, dass 
der MSP eventuell es nicht schafft, das Compareregister schnell genug zu 
rekonfigurieren und somit die Interrupts zwar ausgelöst werden, jedoch 
nicht mehr in der vorgegebenen Zeit. TBCCRO löst immer zur richtigen 
Zeit aus --> Rechtecksweep funktioniert einwandfrei. Ich habe den Code 
als Anhang beigefügt. Ich hoffe es kann nir jemand weiterhelfen :)
Vielen Dank schon einmal im Vorraus!

LG Tobi

von Karl H. (kbuchegg)


Lesenswert?

* Deine Arrayinitialisierung stimmt nicht.
  In C beginnen Array Indizes bei 0 und nicht bei 1.
  Bei der Definition eines Arrays wird die Anzahl der Elemente
  angegeben und nicht der höchste mögliche Index. Der ist immer
  um 1 kleiner als die Anzahl der Elemente

  long int a[200];

  besitzt 200 Elemente. Nummeriert von 0 bis 199.
  Daher ist

  a[200] = 2047;

  falsch und beschreibt Speicher, der nicht zum Array gehört.

  Aber warum machst du das so kompliziert?
1
  long int a[200] = 
2
  { 2047, 2112, 2177, 2241, 2305, 2368, 2432, 2495, 2557, 2619,   //  0 -  9
3
    2681, 2742, 2802, 2861, 2920, 2978, 3035, 3091, 3145, 3199,   // 10 - 19
4
    3253, ....
5
  };
  ist doch viel kürzer, übersichtlicher und als Bonus überwacht dir
  auch noch der Compiler die Anzahl der Elemente.


Für dein eigentliches Problem. Bin noch am Durchsehen das Codes.

von Vorname N. (brenijunior)


Lesenswert?

Hallo und danke für deine Antwort :)
Ja, ich muss zugeben, dass ich noch nicht so lange programiere.
Danke schoneinmal für deinen Tip, werde es gleich umsetzen ;)
LG Tobi

von Vorname N. (brenijunior)


Angehängte Dateien:

Lesenswert?

Habe den Code gleich mal überarbeitet.
Jetzt stimmt leider nur noch die Frequenz nicht :(
Kann es sein, dass der MSP430 das Compareregister nicht so schnell neu 
setzen kann?
LG Tobi

von Karl H. (kbuchegg)


Lesenswert?

Tobias Brengartner schrieb:
> Habe den Code gleich mal überarbeitet.
> Jetzt stimmt leider nur noch die Frequenz nicht :(
> Kann es sein, dass der MSP430 das Compareregister nicht so schnell neu
> setzen kann?

Glaub ich nicht.

Alelerdings ist deine if-Kette in der ISR Unfug. In jedem if setzt du 
den DAC12_0DAT Ausgang und ganz zum Schluss im else praktisch auch immer 
noch mal.


Mir ist auch nicht wirklich klar, was du da mit dem  TBCCR1 Register 
aufführst. So ganz macht das nicht wirklich Sinn, was da in der ISR 
passiert.

Der Timer_B1_INTERRUPT soll doch regelmässig aufgerufen werden um die 
Schwingung rauszutakten. Warum setzt du dann das Compare Register in 
dieser ISR manchmal neu? Und dann noch dazu abhängig von i.

IMHO müsste das Compare-Register in Timer_B0_INTERRUPT neu belegt 
werden, wenn ein neuer Wert für w_PeriodCOUNT festgelegt wird.

von Karl H. (kbuchegg)


Lesenswert?

IMHO müsste das so aussehen
1
#pragma vector=TIMERB0_VECTOR
2
__interrupt void Timer_B0_INTERRUPT (void)
3
{
4
  j++;
5
  
6
  if (j % 2 == 0)
7
  {
8
    P3OUT ^= 0x01;
9
    w_PeriodCOUNT = w_PeriodCOUNT - 2;
10
    w_SetCCR = 1;
11
  }
12
13
  if( w_PeriodCOUNT <= w_StopCOUNT )
14
    w_PeriodCOUNT = w_StartCOUNT;
15
}
16
17
#pragma vector=TIMERB1_VECTOR
18
__interrupt void Timer_B1_INTERRUPT (void)
19
{
20
  i2++;
21
  
22
  if( TBIV == 0x02 )
23
  { 
24
    DAC12_0DAT = a[i];
25
  
26
    if( i == 0 )
27
      P1OUT ^= 0x02;
28
29
    else if( i == 50 )
30
      P1OUT ^= 0x01;
31
32
    else if( i == 100 )
33
      P1OUT ^= 0x02;
34
  
35
    else if( i == 150 )
36
      P1OUT ^= 0x01;
37
38
    i++;
39
    if( i == 200 )
40
      i = 0;
41
  }
42
}

w_SetCCR müsste eine volatile Variable sein.
Wenn Timer_B0_INTERRUPT einen neuen Werte für w_PeriodCOUNT hat, wird 
über den Umweg von w_SetCCR dieser in der Hauptschleife gesetzt und an 
TBCCR1 zugewiesen.

Das XOR bei P1OUT macht mich auch noch stutzig.

von Vorname N. (brenijunior)


Angehängte Dateien:

Lesenswert?

Hallo und danke nochmal für deine Bemühungen :)
So, nochmal überarbeitet. Hattest recht, die if-Anweisungen waren 
falsch. Trotzdem ist die resultierende Frequenz immer noch nicht richtig 
:(
Das XOR verwende ich um einen Port zu toggeln. Damit erzeuge ich 
zusätzlich zu dem Sinussignal zwei Rechtecksignale. Es ist so: TBCCR0 
gibt mir die Periodendauer des Sweeps vor. Immer wenn TBCCRO zwei mal 
durchlaufen wurde, setze ich die Periode neu indem ich TBCCR0 um 2 
Zähler verringere. TBCCR1 unterteile ich in TBCCR0/100. Somit erhalte 
ich 100 Interrupts bis der Timer TBCCR0 erreicht hat. Diese verwende 
ich, um Werte an den DAC zu schieben. Somit erzeuge ich, wenn der Timmer 
zweimal TBCCR0 durchlaufen hat eine Sinusperiode. Aber leider werden die 
Interrupts einfach zu langsam ausgeführt :(
Der Sweep läuft anstatt von 500 Hz - 1200 Hz nur von ca 10 Hz - 25 Hz 
--> DAC gibt nur alle 880us einen neuen Wert aus.

von Karl H. (kbuchegg)


Lesenswert?

Hmm

__interrupt void Timer_B1_INTERRUPT (void)
{


if(TBIV == 0x02 | 0x0E)
{
  t++;

  DAC12_0DAT = a[t-1];
  TBCCR1 = i*w_PeriodCOUNT/w;

TBCCR1 in diesem Interrupt zu verändern ist äusserst kontraproduktiv. 
Das hat da mit Sicherheit nichts drinnen verloren.

Hast du denn schon mal mit einem Frequenzmesser rausgemessen, mit 
welcher Freuquenz die ISR aufgerufen wird?

Timer_B1_INTERRUPT muss 100 mal so oft aufgerufen werden wie 
Timer_B0_INTERRUPT. Ich kenn mich leider mit dem speziellen µC überhaupt 
nicht aus, daher kann ich auch nicht sagen, ob da die Einstellungen der 
Timer richtig sind.

von Vorname N. (brenijunior)


Angehängte Dateien:

Lesenswert?

Ok, hab ich umgeändert. Hat sich aber nichts an der Situation geändert. 
Was interresant ist: Teile ich die Frequenz des Timers (SMCLK), von ID_0 
auf ID_2 herunter, so wird der Sweep schneller. Fängt aber auch an etwas 
herumzuwackeln. Woran kann das liegen???

von Vorname N. (brenijunior)


Lesenswert?

Karl heinz Buchegger :
> Timer_B1_INTERRUPT muss 100 mal so oft aufgerufen werden wie
> Timer_B0_INTERRUPT. Ich kenn mich leider mit dem speziellen µC überhaupt
> nicht aus, daher kann ich auch nicht sagen, ob da die Einstellungen der
> Timer richtig sind.

Ja, hab ich. Dort liegt das Problem. Timer_B1_INTERRUPT wird 
ausschließlich doppelt so viel mal aufgerufen wie Timer_B0_INTERRUPT.
Da liegt der Grund für die zu niedrige Frequenz!

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.