The usual way to generate PWM with the MSP430 is to set the timer to up mode, CCR0 to the PWM period, and then use CCR1 and up in reset/set mode to set the PWM pulse width. This provides a precise hardware driven PWM with minimal CPU overhead. The only ISR required is the CCR0 interrupt so new PWM values can be updated without creating glitches.
It is possible to use CCR0 to do PWM in addition to CCR1 and up by using continuous mode and output compare toggle mode. This requires an ISR for each CCR used. It is glitch free and allows different PWM periods for each output if desired.
The basic ISR is simple:
Complete code for 3 PWM outputs using CCR0, CCR1, and CCR2 of Timer 1
It is possible to use CCR0 to do PWM in addition to CCR1 and up by using continuous mode and output compare toggle mode. This requires an ISR for each CCR used. It is glitch free and allows different PWM periods for each output if desired.
The basic ISR is simple:
__interrupt void isr_ccr0(void) // - ISR for CCR0
{ //
static unsigned s = 0; // State
static unsigned d; // On duration
//
TA1CCR0 += (s++ & 1) ? (pwm_period[0] - d) : (d = pwm_on[0]);
} //
Each time the ISR occurs, the output time is advanced in to the future. The lsb of a state variable toggles between adding the on duration or off duration to the previous time. When the lsb is 0, then the on duration is used, and also saved for later. When the lsb is 1, then the off duration is calculated from the saved on duration and the PWM period. Toggling of the output pin is automatic because the CCR output mode is set to toggle.Complete code for 3 PWM outputs using CCR0, CCR1, and CCR2 of Timer 1
#include <msp430.h>
static unsigned pwm_on[3]; // PWM on duration
static unsigned pwm_period[3]; // Total PWM period - on duration + off duration
//
#pragma vector = TIMER1_A0_VECTOR // - ISR for CCR0
__interrupt void isr_ccr0(void) //
{ //
static unsigned s = 0; // State
static unsigned d; // On duration
//
TA1CCR0 += (s++ & 1) ? (pwm_period[0] - d) : (d = pwm_on[0]);
} //
//
#pragma vector = TIMER1_A1_VECTOR // - ISR for CCR1, CCR2
__interrupt void isr_ccr12(void) //
{ //
static unsigned s1 = 0; // State
static unsigned d1; // On duration
static unsigned s2 = 0; //
static unsigned d2; //
//
switch(TA1IV) { //
case 0x02: // - CCR1
TA1CCR1 += (s1++ & 1) ? (pwm_period[1] - d1) : (d1 = pwm_on[1]);
break; //
case 0x04: // - CCR2
TA1CCR2 += (s2++ & 1) ? (pwm_period[2] - d2) : (d2 = pwm_on[2]);
break; //
} //
} //
//
static void pwm_init(void) //
{ //
P2DIR |= (BIT0 | BIT2 | BIT4); // PWM outputs on P2.0, P2.2, P2.4
P2SEL |= (BIT0 | BIT2 | BIT4); // Enable timer compare outputs
P2SEL2 &= ~(BIT0 | BIT2 | BIT4); //
//
TA1CTL = TASSEL_2 | ID_3 | MC_2; // Setup timer 1 for SMCLK / 8, continuous mode
//
TA1CCTL0 = TA1CCTL1 = TA1CCTL2 = OUTMOD_4 | CCIE; // Set timer output to toggle mode, enable interrupt
TA1CCR0 = TA1CCR1 = TA1CCR2 = TA1R + 1000; // Set initial interrupt time
} //
//
int main(void) //
{ //
WDTCTL = WDTPW | WDTHOLD; //
//
DCOCTL = 0; // Run DCO at 8 MHz
BCSCTL1 = CALBC1_8MHZ; //
DCOCTL = CALDCO_8MHZ; //
//
pwm_init(); // Initialize PWM
//
// Setup PWM period
pwm_period[0] = pwm_period[1] = pwm_period[2] = 20000;
pwm_on[0] = 1000; // Setup servo times
pwm_on[1] = 1500; //
pwm_on[2] = 2000; //
//
_enable_interrupts(); // Enable all interrupts
//
for(;;) { //
} //
//
return 0; //
} //