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.

Can't get the pwm code working

Other Parts Discussed in Thread: MSP430G2231

Hi there,

this is giving me headaches quite a while, I'm trying to get the breathing LED (http://osx-launchpad.blogspot.com/2010/11/breathing-led-effect-with-launchpad.html) running with two leds. I'm aiming to get a simple left to right (red to green led) fading effect.

I tried a couple of things now (e.g. Timer A + WDT) but nothing seems to work, I found the post:

http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/t/60128.aspx#215108

which looks promising to me, I changed the code to my needs but still can't get it working :-(

the red led is flashing continuously (probably at 20Hz) and the green led is fading out then stays full on for 10 seconds or so...

do I have a timing/performance problem or what's going wrong here?

my code:

#include <msp430g2231.h>
#include <signal.h> //add for interrupt

const unsigned char curve[] = {
  1,     1,     1,     1,     1,     1,     1,     1,
  1,     1,     1,     1,     1,     1,     1,     1,
  1,     1,     1,     2,     2,     2,     2,     2,
  2,     2,     3,     3,     3,     3,     3,     3,
  4,     4,     4,     4,     4,     5,     5,     5,
  5,     6,     6,     6,     6,     7,     7,     7,
  8,     8,     8,     8,     9,     9,     9,    10,
  10,    10,    11,    11,    11,    12,    12,    13,
  13,    13,    14,    14,    15,    15,    15,    16,
  16,    17,    17,    18,    18,    18,    19,    19,
  20,    20,    21,    21,    22,    22,    23,    23,
  24,    24,    25,    25,    26,    26,    27,    27,
  28,    29,    29,    30,    30,    31,    31,    32,
  33,    33,    34,    34,    35,    36,    36,    37,
  38,    38,    39,    39,    40,    41,    41,    42,
  43,    43,    44,    45,    46,    46,    47,    48,
  48,    49,    50,    50,    51,    52,    53,    53,
  54,    55,    56,    56,    57,    58,    59,    59,
  60,    61,    62,    62,    63,    64,    65,    66,
  66,    67,    68,    69,    70,    70,    71,    72,
  73,    74,    75,    75,    76,    77,    78,    79,
  80,    80,    81,    82,    83,    84,    85,    86,
  87,    87,    88,    89,    90,    91,    92,    93,
  94,    95,    95,    96,    97,    98,    99,   100,
  101,   102,   103,   104,   105,   106,   106,   107,
  108,   109,   110,   111,   112,   113,   114,   115,
  116,   117,   118,   119,   120,   121,   122,   122,
  123,   124,   125,   126,   127,   128,   129,   130,
  131,   132,   133,   134,   135,   136,   137,   138,
  139,   140,   141,   142,   143,   144,   145,   146,
  147,   148,   149,   150,   151,   152,   153,   154,
  155,   156
};
unsigned int period = 625;
unsigned char redflg = 0; //flag to toggle LED
unsigned char greenflg=0;
unsigned int red_on = 1;  //duty cycle time (counts)
unsigned int green_on = 156;  //duty cycle time (counts)
unsigned int red_index =1;
unsigned int green_index =500;

void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                        // Stop watchdog timer
 
  //setup MCLK
  DCOCTL= 0;
  BCSCTL1= CALBC1_1MHZ;
  DCOCTL= CALDCO_1MHZ; //set Clock to 1 MHz
  BCSCTL2 &= ~(DIVS_3);  //unset Clock divider
  //BCSCTL2 |= DIVS_3; //set Clock Divider MCLK/8 = 125kHz
 
  TACTL = TASSEL_2 | MC_2 | ID_3;                         // TA clock = SMCLK, mode control = continuous and set Timer divider /8 --> MCLK/8 = 125kHz
  TACCTL0 = OUTMOD_4 + CCIE;                       // output mode = toggle, interrupt enabled The output is toggled when the timer counts to the TACCRx value. The output period is double the timer period.
  TACCTL1 = OUTMOD_1 + CCIE;                       // output mode = set, interrupt enabled The output is set when the timer counts to the TACCRx value. It remains set until a reset of the timer, or until another output mode is selected and affects the output.
  P1SEL =  BIT0 + BIT6;                            //  P1.0 to TA0.0, P1.6 to TA0.1
  P1DIR =  BIT0 + BIT6;                            // P1.0 and P1.6 to output direction
 
  //setup Timer A
  TACCR0 = 1;
  TACCR1 = 1;
 
  __bis_SR_register(CPUOFF | GIE); //go to sleep with interrupts enabled
}//end main


// TimerA CCR0 interrupt service routine higher priority than CCR1
#pragma vector=TIMERA0_VECTOR
__interrupt void timer_A0(void)
{
  if (redflg == 0)
  {
    TACCR0 = TAR + red_on;  //actual Timer Counter (TAR) + red_on time the LED output stays high
    redflg = 1;
  }
  else
  {
    TACCR0 = TAR + (period - red_on); // Period 625 - duty cyle if TAR + (period - red_on) is higher than TACCR0 mx (OxFFFFh) TACCR0 rolls over automatically.
    
    if (red_index < 500) {
      red_on = curve[red_index++ >> 1];
    } else if (red_index < 1000) {
      red_on = curve[(999 - red_index++) >> 1];
    } else {
      red_index = 0;
      red_on=1;
    }
    redflg = 0;
  }
}
// TimerA CCR1 interrupt service routine
#pragma vector=TIMERA1_VECTOR
__interrupt void timer_A1(void)
{
  if (greenflg == 0)
  {
    TACCTL1 = OUTMOD_5 + CCIE;                     // output mode = reset, interrupt enabled The output is reset when the timer counts to the TACCRx value. It remains reset until another output mode is selected and affects the output.
    TACCR1 = TAR + green_on;
    greenflg = 1;
  }
  else
  {
    TACCTL1 = OUTMOD_1 + CCIE;                     // output mode = set, interrupt enabled
    TACCR1 = TAR + (period - green_on);
    
    if (green_index < 500) {
      green_on = curve[green_index++ >> 1];
    } else if (green_index < 1000) {
      green_on = curve[(999 - green_index++) >> 1];
    } else {
      green_index = 0;
      green_on=1;
    }
    greenflg = 0;
  }
}


  • I haven't analyzed the code, but usually, this kind of erratic bhavior is caused by limited resources.
    It is likely that past a certain point, your MSP is too slow to react 'in time' and update the timer registers, so you observe latchup effects.

    Using LPM (which adds some wakeup time to the plain ISR latency) isn't a help here.

    Replace the last line of main with a 'while(1);' and see if something changes.

    Check, how much time your settings give until next interrupt, and how much time is required to execute the ISR. If I see it right, a timer tick is only 8 MCLK cycles. So entering any one of the ISRs and exiting it without doing anything, will require more time than one timer tick. I see a lot of '1', '2' and such values in your table, which you add to TAR as new CCRx value. This will cause the interrupt to have already happened when you are still inside the ISR.

  • Marco Hippler said:
      P1SEL =  BIT0 + BIT6;                            //  P1.0 to TA0.0, P1.6 to TA0.1
      P1DIR =  BIT0 + BIT6;                            // P1.0 and P1.6 to output direction

      Hi Marco, I read your code, I hope you wish run two independent breathing led task, take care of pin behavior, see on datasheet http://www.ti.com/lit/gpn/msp430g2231  so led on launchpad are connected to BIT0 (red) and BIT6 (green), BIT6 alternate is counter timer 1 output but BIT0 refer to tacclk not output, TA0 output pin is P1.1

     so change BIT0 with BIT1, remove jumper from TXD and insert to P1.0 P1.1 to enable led drive from BIT1.

     Again software error exist in timer service. TAR is running @sclk so when you add TAR to value one clock cycle elapse to do calculation, then you add TAR with value less than 2 TAR is current to CCR and need overflow before set again interrupt. A workaround can be add 1 more to CCR.

     I also removed all useless braces and last sentence, last read from table still is 1. If you wish use BIT0, set and reset led bit as you can see in modified comment code.

     If this solve your trouble please check answer.

     Regards

    // TimerA CCR0 interrupt service routine higher priority than CCR1
    #pragma vector=TIMERA0_VECTOR
    __interrupt void timer_A0(void)
    {
      if (redflg == 0)
      {
        TACCR0 = TAR + red_on+1;  //actual Timer Counter (TAR) + red_on time the LED output stays high
        redflg = 1;
        P1OUT|=BIT0;   // Set Led ON
      }
      else
      {
        TACCR0 = TAR + (period - red_on); // Period 625 - duty cyle if TAR + (period - red_on) is higher than TACCR0 mx (OxFFFFh) TACCR0 rolls over automatically.
        P1OUT&=~BIT0; // Led Off
        
        redflg = 0;
        if (red_index < 500)
          red_on = curve[red_index++ >> 1];
        else
          if (red_index < 1000)
            red_on = curve[(999 - red_index++) >> 1];
          else
            red_index = 0;
      }
    }
    // TimerA CCR1 interrupt service routine
    #pragma vector=TIMERA1_VECTOR
    __interrupt void timer_A1(void)
    {
      if (greenflg == 0)
      {
        TACCTL1 = OUTMOD_5 + CCIE;                     // output mode = reset, interrupt enabled The output is reset when the timer counts to the TACCRx value. It remains reset until another output mode is selected and affects the output.
        TACCR1 = TAR + green_on +1;    // Add 1 more to TAR due to add cycle
        greenflg = 1;
      }
      else
      {
        TACCTL1 = OUTMOD_1 + CCIE;                     // output mode = set, interrupt enabled
        TACCR1 = TAR + (period - green_on);
        
        greenflg = 0;
        if (green_index < 500)
          green_on = curve[green_index++ >> 1];
        else
          if (green_index < 1000)
            green_on = curve[(999 - green_index++) >> 1];
          else
            green_index = 0;
      }
    }


  • Sometimes it's so simple...

    Thanks a lot your code works perfect for me!

    There are two things:

    -Where in the manual does it say that P1.0 refers to TACLK and P1.1 to TA0? I skimmed over the manual a couple of times but couldn't find anything :-(

    -I just noticed that with my previous declaration 

    P1SEL = BIT0+BIT6

    the red LED gets brighter the closer my finger comes to the Microcontroller, so it's reacting to capacity changes on a certain PIN. Can someone explain this behaviour?

    Thanks

  • Marco Hippler said:
    Where in the manual does it say that P1.0 refers to TACLK and P1.1 to TA0?

    Nowhere. The manual is for the whole family. The document for this kind of information is the device datasheet. it contains (amongst many other things) the pinout and the pin assignment for a specific MSP.

    P1SEL.0 turns P1.0 into ACLK output if in output mode, or TA0.TACLK input if in input mode.
    P1SEL.6 turns P1.6 into TA0.1capture input.

    When not connecting these pins, the inputs act as antenna, catching radio waves. This shouldn't have any effect unless the timer runs on external clock or you turned TA0CCR1 into capture mode. Both does not apply. Are you sure this effect is related to this code line?

    I just checked the G2231 datasheet, and the port pin schematics for P1.6 does not show TA0.1 as output signal. Only as input for capturing. The compare output signal is available on P1.2 and P2.6. At least that's what the port pin schematics says. But I think, the schematics is wrong here, as the pin function table says differently, and it obviously works as output. I added this to my long list of datasheet errors.

**Attention** This is a public forum