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.

MSP430g2553 change pwm duty cycle every x seconds

Other Parts Discussed in Thread: MSP430G2553

Hi,

I am interested in generating a PWM at p1.6 such that the duty cycle changes every say 5 seconds to a new value but the frequency stays the same. For example, a pwm that run at 10Khz frequency and the duty cycles changes from 5% to 50% in steps of 5%.  

Please help!

Sahar 

  • Hi Sahar!

    That is easy! If you want to use P1.6 for the PWM, then use Timer0 and it's TA0CCR0 as the frequency, whereas TA0CCR1 is used for the duty cycle. Run the timer in up mode and select OUTMOD_7 as operating mode for TA0CCTL1. In addition you enable TAIE and create an interrupt vector for TAIFG. This interrupt occurs every time your value in TA0CCR0 rolls over. In this interrupt you can count up a variable. Because of the known freuqency of your PWM you know how many additions you have to make to get a fixed elapsed time like your 5s. When this is reached, write a new value to TA0CCR1. That's it - very little code.

    Dennis
  • Sahar,

    have you been successful? Or need further help with that?

    Dennis
  • Hi Dennis,

    Thank you very much for your help. I wrote the code below but have not had a chance to compile it yet since I left my microcontroller in my lab today. For now, can you please tell me if it looks reasonable.

    I used a for loop to increment and update the values into TA0CCR1, which runs from 10% to 100% duty cycle. I also used an if loop to count to 5 seconds before TA0CCR1 is updated again.

    Sorry for any obvious mistakes, I am very new to this field. 

    #include "msp430g2553.h"
    #include <stdint.h>
    unsigned int counter = 0;
    unsigned int n[10];
    unsigned int i;
    void main( void )
    {
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer

    BCSCTL1 = CALBC1_1MHZ; // Set range to 1MHz
    DCOCTL = CALDCO_1MHZ; // Set DCO step and modulation to 1MHz
    BCSCTL2 = DIVS_3; // SMCLK divider 1

    P1SEL = 0x40; // Set special function of P1.6 (LED2) to timer module
    P1SEL2 = 0x00; // Clear P1SEL2
    P1DIR = 0x40; // Set P1.6 (LED2) to output direction

    TA0CCR0 = 100; // Frequency 10Khz;
    TA0CCR1 = 0;
    TA0CCTL1 = OUTMOD_7; // Reset/set
    TA0CTL = TASSEL_2 | ID_0 | MC_1 | TACLR | TAIE; // SMCLK, divider 1, up-mode, clear

    _BIS_SR(LPM0_bits); // Enter Low power mode 0
    }

    #pragma vector=TIMER0_A0_VECTOR

    __interrupt void Timer0_A0 (void) {
    for (i=1; i<10; i++){
    if (counter ==50000) {
    n[i]=i*10;
    TA0CCR1 = n[i]; // duty-cycle
    counter = 0;
    }
    else
    counter++;
    }
    }

  • Correction:
    BCSCTL2 = DIVS_0; // SMCLK divider 1
  • Hi Sahar,

    From the first look, you are definitely on the right way! I did not test your program, but I can see three things immediately:

    • You chose the wrong interrupt vector - Timer0_A0 is not the right vector for TAIE
    • You forgot to enable the global interrupts, so no interrupt will occur
    • Inside the ISR, you do not read the IV, so your IFG will never get cleared. If Timer0_A0 would be the correct vector, it would get cleared automatically, but you have to clear it yourself in this case (or read TAIV)

    But first test the program yourself.

    Dennis

  • And variables, declared outside an ISR, but used inside, should be declared as volatile.
  • Sahar,

    this was my approach to your problem - I wrote it yesterday when I had a few minutes of free time.

    Since you tried to solve it yourself already, I post it just for comparison.

    #FreeSampleCode

    #include "msp430G2553.h"
    #include <stdint.h>
    
    
    
    void main( void )
    {
      WDTCTL   = (WDTPW | WDTHOLD);                       // Stop watchdog timer
    
      BCSCTL1  = CALBC1_8MHZ;                             // Set range to 8MHz
      DCOCTL   = CALDCO_8MHZ;                             // Set DCO step and modulation to 8MHz
    
      P1SEL    = 0x40;                                    // Set special function of P1.6 (LED2) to timer module
      P1DIR    = 0x40;                                    // Set P1.6 (LED2) to output direction
    
      TA0CCR0  = (800 - 1);                               // Frequency 10kHz
      TA0CCR1  = 40;                                      // Duty cycle 5%
      TA0CCTL1 = OUTMOD_7;                                // Reset / set
      TA0CTL   = (TASSEL_2 | ID_0 | MC_1 | TACLR | TAIE); // SMCLK, divider 1, up-mode, clear, interrupt enabled
    
      _BIS_SR( GIE );                                     // Enable global interrupts
    
      while( 1 );                                         // Endless loop
    }
    
    
    
    // Timer0 A1 interrupt service routine
    #pragma vector = TIMER0_A1_VECTOR
    __interrupt void Timer0_A1_ISR( void )
    {
      static uint16_t counter   = 0;                      // Cycle counter
      static uint8_t  direction = 1;                      // 1: up, 0: down
    
      TA0CTL &= ~TAIFG;                                   // Clear interrupt flag
    
      if( ++counter >= 50000 )                            // 5s
      {
        if( direction )                                   // Direction is up
        {
          TA0CCR1 += 40;                                  // Add 5% to existing value
    
          if( TA0CCR1 == 400 )                            // Reached 50% duty cycle
          {
            direction = 0;                                // Next time down
          }
        }
        else                                              // Direction is down
        {
          TA0CCR1 -= 40;                                  // Subtract 5% from existing value
    
          if( TA0CCR1 == 40 )                             // Reached 5% duty cycle
          {
            direction = 1;                                // Next time up
          }
        }
    
        counter = 0;                                      // Reset counter
      }
    }

    This program increments your PWM in 5% steps, starting from 5% up to 50%, then decrements in 5% steps down to 5% again. Increment and decrement happens every 5 seconds. This cycle is looped over and over again.

    Dennis

  • Hi Dennis,
    Thank you for your suggestions and free sample code. As always, very helpful.
    Would you please let me know why you used an 8MHz clock?
    In terms of power consumption, is it more efficient to run the clock at lower clk cycle?
    Thank you,
    Sahar
  • Sahar Elyahoodayan said:
    Would you please let me know why you used an 8MHz clock?
    In terms of power consumption, is it more efficient to run the clock at lower clk cycle?

    Yes, absolutely right. Lower clock speed means lower power. But as you can see I also did not use any low power mode. This was just an example, not trimmed to save energy.

    Why I used 8MHz instead of 1MHz? Well, it is only because you get larger values for the timer's CCR registers. When using 1MHz, you have

    TA0CCR0  = (100 - 1);                               // Frequency 10kHz
    TA0CCR1  = 5;                                       // Duty cycle 5%
    

    And any increment is += 5 as well, instead of 40. In this case this does not make a difference. You can use 1MHz and 5 as increment.

    But I prefer higher values. Imagine you want to increment 0.5% while running the timer @ 1MHz - you would now need to add 0.5 which is not possible. When having 8MHz as clock source, you increment by 4 and get your desired percentage. So larger numbers for the timer are simply more flexible. 16MHz would also be possible, of course.

    But it depends on the application - if you want 5% and low power then you're absolutely right, use 1MHz instead of 8.

    Dennis

  • Makes sense! Thank you for the explanation.
  • Hi Dennis,
    I am confused about why you used Timer0 A1 as the timer interrupt. In general, how do I decide on the timer interrupt for any task?
    I tried adding to this code by toggling P1.0 and P1.1 by using a second interrupt loop but it does't seem to ever enter that loop. Would you please give an explanation of how to vary PWM in one loop while at the same time toggle two other pins.
    Thank you,
    Sahar
  • Hi Sahar!

    I used Timer0 A1 because I used TAIFG to trigger the interrupt, look:

    TA0CTL = (TASSEL_2 | ID_0 | MC_1 | TACLR | TAIE); // SMCLK, divider 1, up-mode, clear, interrupt enabled

    Since the timer is running in up-mode, this IFG is set each time when one frequency cycle is completed - this was set here:

    TA0CCR0 = (800 - 1); // Frequency 10kHz

    And the TAIFG is handled by

    pragma vector = TIMER0_A1_VECTOR

    CCR0 always has it's own interrupt vector, this would be

    pragma vector = TIMER0_A0_VECTOR

    But CCR1, CCR2, CCR3 (if available), ... and TAIFG share the A1 vector. And since I only used TAIFG, I do not have to determine which source caused the interrupt.

    But I do not understand your problem. Please explain it once again and / or upload your code.

    Dennis

  • Dennis,

    Thank you for the explanation. It helped me figure out that my problem wasn't with the timer.
    Inside the interrupt loop I was trying to toggle P1.0 and P1.1 and the reason why I wasn't seeing an output was because I was using
    P1OUT |= 0X01; //set P1.0 on, P1.1 low
    When I should have used
    P1OUT = 0X01; //set P1.0 on, P1.1 low

    Thanks a lot.

    Sahar

**Attention** This is a public forum