8-channel PWM with the MSP430G2553

Last week, I spent a bit of time working out how to generate PWM for servo control using the MSP430G2553. It took me a couple of hours to work it out, but worked well once I got it going. Today, I’ve been experimenting with a small robot manipulator that we’re exhibiting in our open day tomorrow in DIT Kevin St. This manipulator has a total of seven servo motors, but only 6 degrees of freedom because two of the servos are doubled up on the shoulder joint and are controlled by the same PWM signal. I wanted to control this manipulator with an MSP430G2553, but it doesn’t provide that many dedicated PWM outputs, so I needed to investigate an alternative way of doing it. The code below is my ad hoc solution.

//
// 8-channel PWM example for MSP430G2553
//
// Written by Ted Burke - last updated 4-4-2014
//

#include <msp430.h>

// PWM channels duty cycle array
int pw[] = {1000,1000,1000,1000,1000,1000,1000,1000};

int main( void )
{
    WDTCTL = WDTPW + WDTHOLD; // Disable watchdog timer

    P1DIR = 0b00111111; // Make P1.0-5 outputs

    // Configure Timer A0 Compare interrupts
    TA0CTL = TASSEL_2 + MC_1 + ID_0; // "up" mode, input divider = 1
    TA0CCR0 = 2500;                  // set timer period to 2.5ms
    TA0CCTL0 = CCIE;                 // enable CC0 interrupt
    TA0CCR1 = 1500;                  // set pulse width to 1.5ms
    TA0CCTL1 = CCIE;                 // enable CC1 interrupt
    _BIS_SR(GIE);                    // global interrupt enable

    // From this point on, we only need to write values
    // into the pw[] array to set the duty cycle on all
    // eight PWM channels (P1.0-7), or to be precise,
    // whichever channels are actually enabled as digital
    // outputs (six in this case).

    //
    // A quick example to test: Do a different number of
    // angle steps on each of the six PWM outputs. 1 step
    // on channel 0, 2 steps on channel 1, 3 steps on
    // channel 2, and so on.
    //
    int channel, counter = 0;
    while(1)
    {
        counter++;
        for (channel=0 ; channel<6 ; ++channel)
        {
            pw[channel] = 1000 + (counter%(channel+1))*100;
        }
        __delay_cycles(500000);
    }

    return 0;
}

//
// Timer A0 CC0 interrupt service routine.
// This ISR is triggered when Timer A0 reaches
// its maximum value TA0CCR0 and resets to zero.
// This ISR starts a pulse on one PWM channel
// every 2.5ms. It cycles through all eight channels
// in turn. After starting the pulse, it sets
// the TA0CCR1 register to the pulse width of
// the current pin so that the pulse will be
// ended by the partner ISR after the appropriate
// time delay (for this channel's duty cycle).
//
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0_CC0(void)
{
    static n=0;         // PWM channel index

    P1OUT = 1 << n;     // Set P1.n high
    TA0CCR1 = pw[n];    // Set timer for current pin's pulse width

    n = (n+1)%8;        // Move on to next PWM channel
    TA0CCTL0 &= ~CCIFG; // Reset CC interrupt flag
}

//
// Timer A0 CC1 interrupt service routine.
// This ISR is responsible for ending each PWM
// pulse on all channels. It is triggered when
// Timer A0 matches TA0CCR1, which is at a
// different time for each PWM channel.
//
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A1_CC1(void)
{
    P1OUT = 0;
    TA0CCTL1 &= ~CCIFG;
}

The following video shows what the above program does. In the video you can see me switching the signal controlling the servo motor from one PWM output to the next in the following sequence: P1.0, P1.1, P1.2, P1.3, P1.4, P1.5.

This method involves cooperation between two Timer A0 interrupt service routines (ISRs), one which is responsible for starting pulses on every channel (TA0 CC0) and one which is responsible for ending pulses on every channels (TA1 CC1). The pulses for the six channels are interleaved, in the sense that the channels take it in turns to output one pulse. There is never more than one PWM channel outputting a pulse at any time. Even at its longest pulse width, the servo control PWM has a relatively low duty cycle (approximately 10%), so there is no difficulty interleaving 8 channels in this fashion, as illustrated below:

8-channel_pwm

Download Editable SVG version of above image

This entry was posted in Uncategorized. Bookmark the permalink.

4 Responses to 8-channel PWM with the MSP430G2553

  1. aniutchi emeka says:

    can you write program on dsp sinewave inverter

    • batchloaf says:

      Hi Aniutchi,

      Can you provide a little more detail please, such as:

      • What frequency sinewave?
      • What frequency PWM signal – i.e. how many pulses per second?
      • What range of pulse widths? What is the desired maximum and minimum duty cycle?
      • Do you just want a single channel PWM signal with pulse width that varies sinusoidally?
      • Do you need more than one PWM output, e.g. for complimentary transistors?

      If you can provide these details, I’ll try to sketch out a simple example.

      Ted

      • DebD says:

        Hi… writing this comment with the hope that this thread is still active. So lemme get to the point straight. I need to run a 3phase BLDC motor from a three phase bridge inverter. The pwm signals for the switches of the inverter need to be generated using a microcontroller. I’ve been given the msp exp430g2 launchpad (which comes with the g2553 20 pin variant on board) for this purpose. I need 6 PWM outputs i.e. complementary swtiches as you pointed out in response to the first comment. Is this possible with the launchpad that I have? Also it’d be helpful if you could write a code or something for the same. Pardon me if I’m being a bit impolite here but I’m in urgent need. I’m dropping my email id as well: [EMAIL REDACTED]

  2. Nick says:

    Hi Ted,
    I have been doing project control self-balancing two-wheeled robot with msp430g2553
    I’m used 16Mhz clock so if wanna set timer period of Timer A0 is 0.1:
    I write this code

    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;
    TA0CCTL0 = CCIE;
    TA0CTL = TASSEL_2 + MC_1 + ID_3; // SMCLK/8, up mode
    TA0CCR0 = 200000 // (16Mhz/8)*0.1

    Am I right? Thanks!

    Nick

Leave a comment