Forum: Mikrocontroller und Digitale Elektronik Problem mit PWM (MSP430)


von F. N. (bobba)


Angehängte Dateien:

Lesenswert?

Hallo zusammen.

Ich lerne momentan für meine kommende Embedded Systems Klausur und 
wollte, bevor ich zu den schweren Aufgaben komme, nochmal die leichten 
Aufgaben durchgehen.
Da ich eh noch Probleme mit dem ADC und PWMs habe, hab ich mir bevorzugt 
diese Aufgaben angeguckt.
Leider funktioniert eine Aufgabe nicht mehr richtig und ich habe keine 
Ahnung warum, da eine andere Aufgabe mit einem ähnlichen Aufbau 
einwandfrei funktioniert...

Deswegen hoffe ich das mir hier einer weiterhelfen kann.
Ich benutze ein MSP430G2553 (mit der IAR Workbench).
Die Aufgabe ist relativ simpel...man soll die grüne LED des Pads 
mithilfe einer PWM dimmen.

Hier mal mein Quellcode dafür:
1
#include "msp430.h"
2
3
//PWM
4
5
#define LED_G BIT6
6
7
int main( void )
8
{
9
  // Stop watchdog timer to prevent time out reset
10
  WDTCTL = WDTPW + WDTHOLD;
11
 
12
  //Als Ausgang definieren
13
  P1DIR |= LED_G ;  
14
  P1SEL |= LED_G ;
15
  
16
  
17
 TACTL = TASSEL_2 + MC_3;  //Einstellung Timerregister mit Takt SMCLK im Upmode
18
19
  TACCR0 = 1000;                  
20
  TACCR1 = 9;
21
   
22
  TACCTL1 = OUTMOD_3; // Outmode 3 = Set/Reset
23
24
   while(1){}
25
  
26
}
Probleme:
1. Outmode 3 (Set/Reset) funktioniert nicht richtig.
Die LED is bei allen CCR1 werten gleich hell.

2. Outmode 7 (Reset/Set) funktioniert, allerdings falschrum.
Und zwar ist die LED bei niedrigen CCR1 Werten hell und bei hohen CCR1 
Werten dunkel.
Ein Blick auf das Diagramm und die Tabelle zeigt aber (zumindest nach 
meinem Verständnis)das bei CCR1 der Reset stattfindet und ab CCR0 wieder 
gesetzt wird.
Ergo je größer der CCR1 wert umso kleiner der Resetzyklus umso heller 
müsste die LED leuchten oder sehe ich was falsch?

Zum Vergleich ein Quellcode für eine Aufgabe bei der die LED automatisch 
heller und wieder dunkler wird:
1
#include "msp430.h"
2
3
// Automatisches Dimmen
4
5
#define LED_G BIT6
6
7
void main(void)
8
{
9
  WDTCTL = WDTPW + WDTHOLD;   // Stop WDT
10
  
11
  unsigned int ii, aa;  
12
  
13
  P1DIR |= LED_G;             // Pin von grüner LED als Ausgang
14
  
15
  // Sekundäre Funktion aktivieren, hier Timerausgang => TA0.1
16
  P1SEL |= LED_G;
17
  
18
  TACCR0 = 1023;              // Endwert
19
  
20
  // Out-Modus auf Modus 7 stellen (R/S)
21
  TACCTL1 = OUTMOD0 + OUTMOD1 + OUTMOD2; 
22
  
23
  TACTL = TASSEL1 + MC0;      // Taktquelle, Zählmodus     
24
  
25
  // Eigentliches Programm  
26
  aa = 0;
27
  while(1)                    // Endlosschleife
28
  {
29
    for (ii = 0; ii < 1500; ii++); // Warteschleife    
30
    aa++;                     // Zählervariable erhöhen
31
    
32
    if (aa == 1023)           // Wenn aa == 1023 => OUT-Modus ändern
33
    {
34
      aa= 0;                  // aa zurücksetzen
35
      TACCTL1 ^= OUTMOD2;     // wechsel zwischen Mode 7 und Mode 3     
36
    }
37
    TACCR1= aa;               // TACCR1-Wert auf aa setzen    
38
  }
39
}

Hier startet die PWM im Outmode 7 und die LED wird mit steigendem CCR1 
auch heller (wie auch in Diagramm & Tabelle geschildert).
Und nach dem toggeln in Outmode 3 (Set/Reset) wird die LED mit 
steigendem CCR1 dunkler.

Jetzt ist natürlich meine Frage wieso es alles beim auto dimmen 
funktioniert und in der simpleren Version nur halb und die Hälfte 
nichtmal richtig?

Ich wäre wirklich sehr dankbar wenn mir da jemand Licht ins dunkel 
bringen könnte ich bin da noch nicht so Fit mit dem Thema.

Vielen Dank für die Aufmerksamkeit,

mfg Bobba

: Verschoben durch Moderator
von MSP430 (Gast)


Lesenswert?

Hast du dir das Beispiel schon einmal angeschaut?
Beitrag "Re: MSP430G2553 PWM Duty Cycle mit Poti einstellen"

Um den Ausgang zu invertieren einfach auf OUTMODE 3 abändern.
Aus
1
   TACCTL1 = OUTMOD2 | OUTMOD1 | OUTMOD0;    // TAO.1 im Mode 7
wird
1
   TACCTL1 = OUTMOD1 | OUTMOD0;              // TAO.1 im Mode 3

von F. N. (bobba)


Lesenswert?

Hallo und Danke für die Antwort.

So ähnlich mach ich es ja beim 2. Code und es funktioniert auch alles 
wie es soll.

Allerdings funktioniert der 1. Code nicht wie er soll.
1
TACCTL1 = OUTMOD_7;
entspricht
1
TACCTL1 = OUTMOD0 + OUTMOD1 + OUTMOD2;
Nur funktioniert es im Modus 7 genau andersherum als im 2. Code und im 
Modus 3 überhaupt nicht.
So liefert:
1
TACTL = TASSEL_2 + MC_3;  //Einstellung Timerregister mit Takt SMCLK im Upmode
2
3
  TACCR0 = 1000;                  
4
  TACCR1 = 10;
5
   
6
  TACCTL1 = OUTMOD0 + OUTMOD1 + OUTMOD2;
eine hell leuchtende LED und ein TACCR1 von 900 eine stark gedimmte 
(obwohl es doch umgekehrt sein sollte oder nicht?).

Und im Modus 3 kann ich TACCR1 ändern wie ich will, die LED ist immer 
gleich hell.

Ich weiß leider nicht wieso das im einem Code wie auch dem 
Impulsdiagramm zu entnehmen funktioniert und im anderen nicht :/

Danke vorab für weitere Hilfe.

mfg Bobba

von MSP430 (Gast)


Lesenswert?

Florian N. schrieb:
> so ähnlich mach ich es ja

aber nicht gleich ;-D

Mit welcher Taktquelle verbindest du den Timer?
In welchem Modus läuft der Timer?

Deine Konfiguration:
TACTL = TASSEL_2 + MC_3;

Meine Konfiguration:
TACTL = TASSEL1 | MC0 | TACLR;

Stell doch mein Beispiel einfach einmal auf OUTMODE 3 um.

von Leroy (Gast)


Lesenswert?

Der Fehler im ersten Quellcode liegt am MC_3, der für den Up/Down-Mode 
steht. Ändere dies in MC_1 für den Up-Mode. Folgendes ist dem MSP430 
User Guide, unter dem Kapitel Timer A, entnommen:

MCx Mode Description:
00 Stop The timer is halted.

01 Up The timer repeatedly counts from zero to the value of TACCR0.

10 Continuous The timer repeatedly counts from zero to 0FFFFh.

11 Up/down The timer repeatedly counts from zero up to the value of 
TACCR0  and back down to zero.

Der von dir gewählte (TASSEL_2) entspricht dem Takt des interen 
Taktgebers SMCLK von 1,1MHz. Entspricht einer Zeit von ca. 1µs pro 
Zähltakt. Das ergibt hinsichtlich deines Codes:

TACCR0 = 1000; // ca. 1ms
TACCR1 = 9; // ca. 9µs AUS 991 µs AN

Die LED sollte bei einem Wert von TACCR1 = 9950 stark gedimmt sein.

Wenn ich raten müsste, würde ich sagen, das riecht nach einer Klausur 
von Herr Prof. K. vom Campus GM der FH K..

von Atomix, Entry,Entry2,Entry99... (Gast)


Lesenswert?

Tipp an alle MSP Anfänger,

"Trollmodus an"

Kuckt euch erst die Errata Files an!

"Trollmodus aus"

Zitat:"Das ist wirklich so, ling ling und bauer".

Im Ernst, vieles wird im Datenblatt beschrieben und in Wirklichkeit geht 
es nur mit "Workaround". -> Z.B. Interuptmode bei I2C fail -> 430F2618T.

Gruß.

"Jetzt aber Trollmodus wirklich aus"

von F. N. (bobba)


Lesenswert?

Danke für die Antworten :)

Leroy schrieb:
> Der Fehler im ersten Quellcode liegt am MC_3, der für den Up/Down-Mode
> steht.

Es sind immer diese Kleinigkeiten (und ich sitz da so lange dran und 
weiß nit woran es liegt)....
Aber ok vielen Dank^^. (auch an MSP430)

Leroy schrieb:
> Wenn ich raten müsste, würde ich sagen, das riecht nach einer Klausur
> von Herr Prof. K. vom Campus GM der FH K..

Keine Klausur sondern normale Aufgaben :P (Klausur dürfte um einiges 
schwerer werden)
Aber ja, macht man sowas an anderen Schulen nicht?(Oder woher kommt die 
direkte Vermutung).

Hätte mittlerweile aber auch noch eine Frage:
Wenn ich eine PWM auslesen und auf meinem Launchpad wiedergeben möchte, 
muss ich da den Timer_B mitbenutzen?
Fürs auslesen der PWM brauch ich ja schon 1 Timerregister im Capture 
Mode.
Dann bräuchte ich ja noch einen für meine eigene PWM und der 
TACCTL0-Register fällt ja weg da der CCR0-Wert als Vergleichswert für 
die PWM genommen wird (zumindest im Up-mode).

Laut Userguide hätte mein Launchpad auchnoch einen 3. Timerregister 
(Register 2).
Allerdings finde ich in den Pin-Belegungen keinen mit Sonderfunktion 
(Timer-Ausgang) für diesen Register...

Dann war meine nächste Idee die PWM mithilfe des Continious-Mode zu 
machen.
Das also ein Ereignis bei einem Zählerstand (z.B. CCR1) und 0xFFFFh 
ausgelöst wird.
Aber leider würde, laut Impulsdiagram, mit CCR0 & CCR1 gearbeitet werden 
und bei 0xFFFFh nichts passieren.

Jetzt ist die Frage ob ich den Timer_B genau wie den Timer_A benutzen 
kann?
Und/Oder ob der CCR1 Zählregister, wenn ich den CCTL1-Register im 
Capture-Mode betreibe, für die Compare-Funktionen ausgeblendet wird (er 
im Up-mode also kein Ereignis auslösen würde)?

(eine Aufgabe aus dem Tutorium falls die Frage aufkommt)

Wäre auch hier nocheinmal dankbar für Hilfe.

mfg Bobba

von Leroy (Gast)


Lesenswert?

F. N. schrieb:
> Aber ja, macht man sowas an anderen Schulen nicht?(Oder woher kommt die
> direkte Vermutung).

Vermutlich wird dies auch an anderen Hochschulen zum Einsatz kommen, nur 
die Aufgabenstellung, sowie die Lösung des 2. Quellcode kam mir sehr 
vertraut vor, obwohl es schon paar Jährchen zurück liegt. ;)

Selber bin ich nicht mehr ganz fit, was den MSP430 angeht aber zu deiner 
Problematik des PWM auslesen würde ich folgende Betrachtung machen.

PWM = zyklisches EIN/AUS-Schalten,somit würde ich versuchen mittels 
Interrupt über die Flankenerkennung an einem der Pins die EIN- und 
AUS-Zeit zu ermitteln, um diese dann mit dem Timer A nachzubilden, indem 
du die Werte in den TACCR0 und TACCR1 schreibst.

Beispiel Pin-Config:

Steigende Flanke
P1IES &= ~BIT 3;

Fallende Flanke
P1IES |= BIT3;

Ob du das auslesen der anliegenden PWM kontinuierlich betreibst, nach 
einer Zeit t neu einliest oder das neu Einlesen per Taster auslöst, 
überlasse ich deiner Fantasie.

von F. N. (bobba)


Angehängte Dateien:

Lesenswert?

Danke nochmals für die Antwort :)

Leroy schrieb:
> die Lösung des 2. Quellcode kam mir sehr
> vertraut vor
Obwohl das in Eigenarbeit mit ein bissle Tutoriumshilfe entstanden ist 
:O

Hab jetzt selber noch ein wenig rumprobiert.
Wie ich die PWM auslese ist mir relativ klar.
Ich nehm nen Capture auf ne steigende Flanke, die folgende fallende und 
wieder die steigende und kann das ganze dann ins Verhältnis setzen.
Das ganze braucht ja an sich auch nur 1 Timerregister.
Wenn ich aber jetzt gleichzeitig auch eine PWM rausgeben will bräuchte 
ich allerdings ebenfalls 2 Timerregister (zumindest im Up-Mode).
Durch probieren hab ich jetzt rausgefunden das es anscheinend mit 1 
Register im Contionous-Mode auch funktioniert.
1
#define LED_G BIT6
2
int main( void )
3
{
4
  // Stop watchdog timer to prevent time out reset
5
  WDTCTL = WDTPW + WDTHOLD;
6
 //Als Ausgang definieren
7
  P1DIR |= LED_G ;
8
  P1SEL |= LED_G ; //Sonderfunktion am Pin (Timerausgang für Register TACCTL1(?))
9
  
10
TACTL = TASSEL_2 + MC_2;      //SMCLK + Continious-Mode
11
12
TACCTL1 |= OUTMOD_3; //PWM zwischen CCR1 & 0xFFFFh (?)(zumindest blinkt die LED)
13
TACCR1 = 40000;
14
 while(1){}
15
}

Jetzt ist meine Frage ob es immernoch geht wenn ich den TACCTL0 Register 
jetzt für die Capture-Funktion nehme?
Laut Diagramm2 würde er die Set/Reset Operationen ja normalerweise 
zwischen CCR0 & CCR1 machen.
Ignoriert er CCR0 dann wenn ich den Register im Capture-Mode habe und 
arbeitet wie oben zwischen CCR1 & 0xFFFFh oder benutzt der dann CCR0 & 
CCR1 für den Set/Reset?
Bzw.
1
int main( void )
2
{
3
  // Stop watchdog timer to prevent time out reset
4
  WDTCTL = WDTPW + WDTHOLD;
5
 //Als Ausgang definieren
6
  P1DIR |= LED_G ;
7
/ P1SEL |= LED_G ; //Sonderfunktion am Pin (Timerausgang für Register TACCTL1(?))
8
  
9
 P1DIR &= ~BIT1 ; //Pin 1.1 als Eingang (Tabelle s.43 Datenblatt)
10
 P1SEL |= BIT1 ;  // Sonderfunktion Timer Capture für den Register TACCTL0 (?)
11
 //Für Register TACCTL1 Capture-Mode müsste ich Pin 1.2 nehmen?
12
   
13
TACTL = TASSEL_2 + MC_2;      //SMCLK + Continious-Mode
14
15
TACCTL0 = CAP + CM_1 + CCIE ; //Register 0 im Capture Mode
16
 
17
 __bis_SR_register(GIE);  
18
 TACCTL1 |= OUTMOD_3; //PWM zwischen CCR1 & 0xFFFFh (?)(zumindest blinkt die LED)
19
 TACCR1 = 40000;
20
 
21
  while(1){}
22
}
Würde dieser Grundgedanke funktionieren?
Also der 0-Register die Captures ausführen in seiner ISR und der 
1-Register die PWM?

Zur Pin-Belegung:
Sind meine Annahmen da richtig?
http://www.ti.com/lit/ds/symlink/msp430g2553.pdf (ab s.43)

Sachen die mir beim rumprobieren aufgefallen sind:
-Wenn ich den Timer im Continious-Mode betreibe, funktioniert der 
Interrupt (wenn ich ihn aktiviere) nur mit dem TACCTL0 Register.
Mit dem 1er Register kann ich keinen Interrupt auslösen.
Sprich:
1
int main( void )
2
{
3
  // Stop watchdog timer to prevent time out reset
4
  WDTCTL = WDTPW + WDTHOLD;
5
 //Als Ausgang definieren
6
  P1DIR |= LED_G ;
7
8
 TACTL = TASSEL_2 + MC_2 + ID_3;      //SMCLK + Continious-Mode 
9
 TACCTL0 = CCIE;                     //TACCTL1 = CCIE; funktioniert nicht (ISR wurde beim Versuch auf A1 umgestellt)
10
11
  __bis_SR_register(GIE); 
12
  while(1){}
13
}
14
#pragma vector=TIMER0_A0_VECTOR  //ISR  
15
  __interrupt void Timer_A (void)
16
  {
17
   P1OUT ^= LED_G;
18
      }
Hab ich was falsch gemacht oder kann ich das so hinnehmen?^^

edit:
Ebenfalls im Up-Mode mit vorgegebenen Wert funktioniert es nur im 
0-Register.
1
TACCTL1 = CCIE;
2
TACCR1 = 5000;
funktioniert nicht.
Ich kann Timerinterrupts im Compare-Modus also nur über den CCR0 regeln?

Danke schonmal vorab für die Mühe.

mfg Bobba

: Bearbeitet durch User
von F. N. (bobba)


Lesenswert?

Hallo ich bins nochmal.
Der obige Post kann größtenteils ignoriert werden, hab das ganze mal 
getestet mit:
1
#define LED_G BIT6
2
#define TASTER BIT3
3
int main( void )
4
{
5
  // Stop watchdog timer to prevent time out reset
6
  WDTCTL = WDTPW + WDTHOLD;
7
 //Als Ausgang definieren
8
  P1DIR |= LED_G ;
9
  P1SEL |= LED_G ; //Sonderfunktion am Pin (Timerausgang für Register TACCTL1(?))
10
  
11
 P1DIR &= ~BIT1 ; //Pin 1.1 als Eingang (Tabelle s.43 Datenblatt)
12
 P1SEL |= BIT1 ;  // Sonderfunktion Timer Capture für den Register TACCTL0 (?)
13
 //Für Register TACCTL1 müsste ich Pin 1.2 nehmen?
14
 
15
 P1DIR &= ~TASTER; //BIT3 wird gelöscht (hier nicht nötig da 3 bereits gelöscht)
16
  P1REN |= TASTER; // Pull-up aktivieren
17
  P1OUT |= TASTER; // Pull-Up selektiert
18
  
19
  //Interrupt konfigurieren
20
  P1IE |= TASTER;               //Interrupts erlauben
21
  P1IES |= TASTER;              // Auf fallende Flanke (bei 1 geht er von hoch-> tief da der pull up auf masse zieht)
22
   
23
24
 TACCTL0 = CAP + CM_1 + CCIE ; 
25
 
26
 __bis_SR_register(GIE);  
27
  TACTL = TASSEL_2 + MC_2;      //SMCLK + Continious-Mode
28
 TACCTL1 |= OUTMOD_3; //PWM zwischen CCR1 & 0xFFFFh (?)(zumindest blinkt die LED)
29
 TACCR1 = 40000;
30
31
  while(1){}
32
}
33
#pragma vector=TIMER0_A0_VECTOR  //ISR  //A1 da ich im 1er Register bin
34
  __interrupt void Timer_A (void)
35
  {
36
   __no_operation();
37
      }
38
#pragma vector=PORT1_VECTOR             //Interrupt Vektortabelle für Port 1
39
  __interrupt void PORT_1(void){        //ISR Taster
40
 TACCTL0|= CCIS_2;  //Eingangssignal simuliert steigende Flanke
41
  TACCTL0&= ~CCIS_2;
42
  TACCTL0|= CCIS_3;
43
  TACCTL0&= ~CCIS_3;
44
  P1IFG &= ~TASTER;                     // Flag wird zurückgesetzt
45
  }
Wie befürchtet nimmt er, sobald er einen CCR0-Wert bekommt durch nen 
Capture, selbigen und führt die PWM-Befehle (Set/Reset) zwischen CCR0 & 
CCR1 aus anstatt zwischen CCR1 & 0xFFFFh.

Jetzt ist natürlich meine Frage wie ich das sonst noch realisieren kann 
das an einem Pin eine PWM eingelesen wird und ich an einem anderen eine 
Ausgeben kann mit variablen duty Cycle.
(Hatte vor das Tastverhältnis zwischen CCR1 & dem Continious-Endwert 
0xFFFFh zu regeln).

Hinzu kommt noch das kleine Problem das ich den Timer ja nicht nach 
belieben zurücksetzen kann, wenn gleichzeitig noch ne PWM ausgegeben 
werden soll.
Ich muss ja beim auslesen der eingehenden PWM irgendwas machen damit der 
sich nicht mit den Timerwerten bei der Rechnung verrennt.(Er könnte ja 
eine steigende Flanke bei 0xFF10h kriegen und die nächste bei 0x00ABh 
und würde dann wahrscheinlich Murks für die Periodendauer rechnen).

Meine einzige Vermutung ist jetzt nen 2. Timer zuzuschalten der sich um 
die PWM kümmert.
Nur leider hab ich noch nie mit dem Timer_B aus dem Userguide gearbeitet 
:/
(http://www.ti.com/lit/ug/slau144j/slau144j.pdf ab Seite 374 kommt 
Timer_B, Die Timer_A Register starten auf Seite 369).
Weiterhin finde ich auch keinen Pin der eine Zusatzfunktion für diesen 
Timer erfüllt. (Immer nur TA0.1/TA0.0/TA1.0/TA1.1)

edit:
Fast vergessen...
Meine andere Idee wäre es, den Timer_A Register TACCTL2 für den Capture 
zu nehmen.
(Sofern mir dieser zu Verfügung steht im Userguide ist er mit der 
Bemerkung "Not present on MSP430 devices with Timer_A2 like MSP430F20xx 
and other devices." gelistet.
Ich weiß jetzt nicht ob meiner zu "other devices" zählt...irgendwas mit 
nem Timer_A2 kann ich aber auch nicht finden :/ )
Allerdings blicke ich, wie oben bereits erwähnt, mit den Pinbelegungen 
nicht wirklich durch.
So wie ich das System bis jetzt verstanden habe gibt es keinen Pin der 
Sonderfunktionen für den Timerregister TACCTL2 erfüllt.
Was bei mir die Frage aufkommen lässt wofür genau dieser Register gut 
ist wenn man ihn weder für nen Capture noch für nen Compare nutzen kann?

Wäre wirklich dankbar wenn mir da noch jemand bei helfen könnte.

mfg Bobba

: Bearbeitet durch User
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.