This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

PWM randomly flickering

Other Parts Discussed in Thread: MSP430F5510

Hi guys

I am now having some problems with a simple PWM signal out of my MSP430F5510. I noticed that in my previous code the PWM seemed to be extending its duty cycle for just one period once in a while. I thought it might have been problem of the whole code interfering with the PWM generation so I reduced the code to just outputting the PWM, and the problem is still there. I have the program loop the PWM duty cycle from 0-100% and I use an LED to visualize it. Watching the LED it goes from non to full brightness, but ocasionally it flickers with full brightness, sometimes when it is at approx. 50%, sometimes when it is at approx. 20%, and so on. I couldn't find a pattern in the flickering. My PWM implementation (Sorry I didn't get the hang of the Code-inserting tool) :



#include <msp430.h> 

int main(void) {
	  WDTCTL = WDTPW + WDTHOLD;                			// Stop WDT
	  P1DIR |= BIT2 + BIT3; 		         		 	// P1.2, P1.3
	  P1SEL |= BIT2 + BIT3;                    		 	// P1.2, P1.3 options select
	  TA0CCR0 = 5275;                        			// PWM Period ~@200Hz
	  TA0CCTL2 = OUTMOD_7;								// CCR2 reset/set
	  TA0CTL = TASSEL_2 + MC_1 + TACLR;       			// SMCLK, up mode, clear TAR
	  TA0CCR2 = 0;  	                        		// CCR2 initial PWM duty cycle

	  volatile unsigned int i,guf=1;



	  for(;;){

		  if(TA0CCR2<TA0CCR0 && guf){					// Duty cycle lower than 100% and currently going up?
			  TA0CCR2++;								// Increase duty cycle
			  for(i=50;i>0;i--);
			  if(TA0CCR2==TA0CCR0) guf=0;				// if duty cycle of 100% reached, clear going-up-flag
		  }

		  if(TA0CCR2>0 && !guf){						// Duty cycle greater than 0 and currently going down?
			  TA0CCR2--;								// Decrease duty cycle
			  for(i=50;i>0;i--);
			  if(TA0CCR2==0) guf=1;						// If duty cycle of 0% reached, set going-up-flag
		  }

	  }
}





Using the oscilloscope to analyse the signal it looks something like this: 

Sorry for the horrible MSPaint drawing but I don't have the oscilloscope right now, I hope you get the idea. And like I said, the flicker occurs sometimes when the duty cycle is at 50%, sometimes at 10%, at 0%, sometimes it doesn't even appear. I noticed that the higer the TA0CCR0 value, the more frequent the flickering occurs. From approximately 1500 and below the flickering disappears. The problem is that I need a 200Hz frequency, which is that TA0CCR0 value.

Am I missing something pretty obvious there? I've tried the same code with two different MSP430F5510 chips, and it behaves the same way in both of them, so I'm guessing it is a code issue.

Thanks in advance for your help!

Best Regards

  • Cesar,

    If you look at the 5xx UG Timer_A chapter, you will see that it is generally suggested that you stop the timer before modifying the contents of its registers.

    What I think is happening is a timing specific error where you are changing the CCRx registers on the fly and the module is not catching the proper change in the output before the CCRx register is modified.

  • Does that mean that such a looping-PWM can't be flawlessly programmed? I've tried stopping the timer (writing 0 to TA0CCR0), writing the new value to TA0CCR2 (increment it or decrement it), and then rewriting TA0CCR0s old value, which didn't work. The flickering is gone, but it doesn't continuously loop, it stays at 100% value for a few seconds, then loops til it reaches a 100% again, and so on. 

  • Cesar,

    I wouldn't go so far as to say that a looping PWM can't be flawlessly programmed.

    The way that your code is written could also be causing some problems. You are using a SW loop to hold the timer configuration for only 50 MCLK cycles. You may want to consider changing your code to a more interrupt driven process.

    Below is a quick example I wrote up with a G2553 LP. I think you should have more success if you structure your program this way.

    //******************************************************************************
    //  MSP430G2xx3 Demo - Timer_A, 200Hz PWM, Blink LED with PWM
    //
    //           MSP430G2xx3
    //         ---------------
    //     /|\|            XIN|-
    //      | |               |
    //      --|RST        XOUT|-
    //        |               |
    //        |           P1.2|-->TA0.1-->LED
    //
    //******************************************************************************
    
    #include <msp430.h>
    
    unsigned char direction = 1;                // 1 - increasing, 0 - decreasing
    
    int main(void) {
      WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT
    
      // MCLK = SMCLK = 1MHz
      if (CALBC1_1MHZ==0xFF)					// If calibration constant erased
      {
        while(1);                               // do not load, trap CPU!!
      }
      DCOCTL = 0;                               // Select lowest DCOx and MODx settings
      BCSCTL1 = CALBC1_1MHZ;                    // Set range
      DCOCTL = CALDCO_1MHZ;                     // Set DCO step + modulation
    
      // Set P1.2 for Timer Output
      P1DIR |= BIT2;                            // P1.2 Output
      P1SEL |= BIT2;                            // TA0.1 Function
      P1SEL2 &= ~BIT2;
    
      // Set up Timer for 200Hz period
      TACCTL0 = CCIE;                           // CCR0 interrupt
      TACCR0 = 5000;                            // 200Hz Period
      TACCTL1 = OUTMOD_7;                       // Reset/Set
      TACCR1 = 0;                               // 0% duty to start
      TACTL = TASSEL_2 | MC_1;                  // SMCLK, Up Mode
    
      __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupt
    }
    
    // Timer_A3 Interrupt Vector
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void Timer_A_CCR0(void) {
      TACTL &= ~MC_3;                           // Stop Timer
      if(direction) {
    	TACCR1++;                               // Increase PWM duty cycle
    	if(TACCR1 >= TACCR0) direction = 0;     // At limit, turn around
      }
      else {
    	TACCR1--;                               // Decrease PWM duty cycle
    	if(TACCR1 == 0) direction = 1;          // At limit, turn around
      }
      TACTL |= MC_1;                            // Start Timer
    }

  • Cesar Triana said:
    Does that mean that such a looping-PWM can't be flawlessly programmed?

    Oh, it can. But you'll need to synchronize the CCR change with the timer count.

    Imagine what happens when you have CCRx set to 50. Timer is at 40. Now you change CCRx to 30 to change the DC. The timer will count to 50 and nothing happens (because CCRx is no longer 50). and continues to count until it reaches CCR0. Then it begins with 0 again and when it comes to 30, the output changes. But between the old 50 and the new 30 cycle, you did get a full 100% cycle.
    If you ensure that the timer count is either past the old CCRx value or below the new one, when you do the change, then all is well.
    TimerB implements a latch mechanism that delays any changes to CCRx to the next timer overflow, so the problem doesn't happen if you use this feature.

  • I've implemented such a code (probably in a very naive way) and it did get better, but the flickering is sometimes still there. This time it doesn't seem like its a 100% DC when it flickers but rather some value between 50-70% DC.

    #include <msp430.h>
    
    volatile unsigned int i,guf=1;
    /*
     * main.c
     */
    int main(void) {
    
        WDTCTL = WDTPW | WDTHOLD;					// Stop watchdog timer
        P1DIR |= BIT2 + BIT3		;          		// P1.2, P1.3 output
        P1SEL |= BIT2 + BIT3;                      	// P1.2, P1.3 options select
        TA0CCR0 = 5275;                        	  	// PWM Period ~@200Hz
        TA0CCTL2 = OUTMOD_7;                     	// CCR1 reset/set
        TA0CTL = TASSEL_2 + MC_1 + TACLR;       	// SMCLK, up mode, clear TAR
        TA0CCR2 = 2650;                            	// CCR2 initial PWM duty cycle
        guf = 1;									// Starts going up
    
    
        for(;;){
    
        	if(guf && (TA0R>TA0CCR2 || TA0R<(TA0CCR2+1))){
        		TA0CCR2++;
        		for(i=10;i>0;i--);
        		if(TA0CCR2==TA0CCR0) guf=0;
        	}
        	else if (TA0R>TA0CCR2 || TA0R<(TA0CCR2+1)){
        		TA0CCR2--;
        		for(i=10;i>0;i--);
        		if(TA0CCR2==0) guf=1;
        	}
    
        }
    }
    

  • One count isn’t enough for safety. Between your comparison and the CCR update, several MCLK cycles pass. And SMCLK=SMCLK. You should have at least 10-20 here.

    However, when increasing CCR2, you don’t need to check. You have either already passed the trigger point or not. So this current cycle will be the new or the old DC. Only when decrementing CCR2, you might have a cycle that is neither one.

    Besides this, your PWM cycle is 5276 timer ticks, but your for loop executes about every 100 MCLK cycles or less, so multiple times per PWM cycle.

    Also, don’t use an empty FOR loop for a delay. It might be optimized away, as it doesn’t change the system state (time wasting is not considered a state change by the compiler).
    Use a timer (e.g. enter LPM at the end of the for loop, and wake up on the CCR2 interrupt, but this would conflict with the mechanism you just use to prevent glitches) or use _delay_cycles for a purposeful time wasting
    J

**Attention** This is a public forum