Im new to MCU and MSP430 Programming and was confused about PWM Channels. I have a MSP430g2231 Chip thats included in the launchpad and was wondering How many PWM channels it has and how to determine how much PWM other microcontrollers have is it based on Timers compare channels? in this chip there seem to only be two channels CCR0 and CCR1 so can it only drive two in continuous mode?
tsukukinsei,
If you are looking for information on PWM, look at the device family user guide in the section about the timers…
http://www.ti.com/litv/pdf/slas694b
You can find a link for this on any TI device page under documents. Also there is useful chip specific information that you can find in each devices data sheet.
If you are completely new to microcomputers, I would recommend not starting with PWM it is not the easiest thing to dive into if you have no experience. Just my opinion though :-P. If you want to, read up on how the timers work in the device. I haven’t looked, but I’m sure theres an article or post somewhere online that does a good job explaining. Eventually I might discuss this on my blog, but not for a while.
Best of luck.
-NJC
There is one timer and two capture compare registers. This means only one Timer based PWM. You can do it in software, but…
How about something like this. Use the PWM period at the total timer period. Use the ISR for each CCRx and get two PWM’s?
Change TACCRx to get your PWM… Change clock to get PWM period you need? Not great but…
TACTL = TASSEL_2 + ID_0 + MC_2 + TAIE; // SMCLK, no div, cont mode
TACCR0 = 0x03FF; // Capture compare 0
TACCR1 = 0x01FF; // Capture compare 1
TACCTL0 = CCIE; // Enable interrupt
TACCTL1 = CCIE; // Enable interrupt
_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt
}
// Timer A0 interrupt service routine for TACCR0
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void)
{
P1OUT &= ~BIT0; // Blue LED OFF
}
// Timer_A2 Interrupt Vector (TAIV) handler for everything except TACCR0
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A1(void)
{
switch( TAIV )
{
case 2: P1OUT &= ~BIT4; // Green LED OFF
break;
case 10: P1OUT |= BIT0; // Blue LED ON
P1OUT |= BIT4; // Green LED ON
break;
}
}
OK. If you keep it simple this works. Crank the speed to 8MHz. Setup an interrupt and do the pwm in the ISR. Ugly… Yes, but I can do PWM at 100Hz on 4 outputs and still have time to do some other stuff. Gas it up to 16MHz if you must.
I put sparkfun’s trackball on the breadboard to turn the led’s on and off and to switch between the colors to change teh intensity just to see it work. I ran out of pins on the G2211 so don’t have all the colors on the trackball.
volatile unsigned int uiLEDBlueInt = 20;
volatile unsigned int uiLEDRedInt = 20;
volatile unsigned int uiLEDGreenInt = 20;
volatile unsigned int uiLEDWhiteInt = 20;
volatile unsigned int uiLampON = 0;
const unsigned int cLEDIncr = 100;
const unsigned int cLEDStep = 2;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Hold watchdog timer.
DCOCTL = DCO0 + DCO1; // This one setup 8.2MHz
BCSCTL1 = RSEL0 + RSEL2 + RSEL3; // but may slow it down later
// BCSCTL2 |= 0; // MSP430 clocks.
// Port setups.
// Port 1 P1.3 is already wired to a SMD switch SW2.
P1DIR = BIT5 + BIT6 + BIT7; // Pins to be outputs
P1SEL = 0; // Function select. No special func
P1OUT = 0; // Output value or pull up resistor
P1REN = 0; // Which inputs have the resistor enabled
P1IE = BIT0 + BIT1 + BIT2 + BIT3 + BIT4; // Interrupt enable
P1IES = 0; // Interrupt enable edge select
P1IFG = 0; // Clear interrupt requests
// Defines for Port 1
#define ipButton BIT0; // Trackball button pressed
#define ipRight BIT1; // Trackball rolled right
#define ipLeft BIT2; // Trackball rolled left
#define ipUp BIT3; // Trackball rolled up
#define ipDown BIT4; // Trackball rolled down
#define opLEDButtonWhite BIT5; // Trackball button LED
#define opLEDButtonBlue BIT6; // Daylight white LED
#define opLEDGreen BIT7; // Green LED
//Port 2 Don’t use P2.7 as an input or excess current will flow
P2DIR = 0xFF; // Pins to be in and out
P2SEL = 0; // XIN and XOUT I/O
P2OUT = 0; // Output value or pull up resistor
P2REN = 0; // Which inputs have the resistor enabled
P2IE = 0; // Interrupt enable
P2IES = 0; // Interrupt enable edge select
P2IFG = 0; // Clear interrupt requests
// Defines for Port 2
#define opLEDBlue BIT6;
#define opLEDRed BIT7;
// Timer A setup.
// Several clocks can be used for the timer. Various dividing ratios and
// 4 modes available. TACCR0 useless in up mode. TAIE enables the interrupt
// at each rollover. Using TACCR0 at 8.2Mhz as 100Hz interrupt timing.
TACTL = TASSEL_2 + ID_0 + MC_1; // SMCLK, no div, up mode,
TACCR0 = 0x01FF; // Capture compare 0 not used
TACCR1 = 0x0000; // Capture compare 1 not used
TACCTL0 = CCIE; // Enable interrupt
//TACCTL1 = CCIE; // Enable interrupt
// GIE must be enabled to allow any ISR to work.
_BIS_SR(GIE); // Enter LPM0 w/ interrupt
for(;
{
}
}
// Interrupt service routine for Port 1
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)
{
static unsigned int uiSelBlue = 1;
static unsigned int uiSelRed = 1;
static unsigned int uiSelGreen = 1;
//static unsigned int uiSelWhite = 1;
static unsigned int uiLRMove = 0;
static unsigned int uiLimitR = 1;
static unsigned int uiLimitL = 0;
// It would be good to disable the GIE here
_BIC_SR(GIE); // Dis-allow interrupts
if (P1IFG & BIT0)
{
P1IFG &= ~BIT0; // Reset interrupt flag
if (uiLampON == 0) // If OFF turn ON
{
uiLampON = 1; // If ON turn OFF
}
else
{
uiLampON = 0;
}
uiLRMove = 0; // Reset left or right move
}
if (P1IFG & BIT1) // Entry of BIT1 isr
{
P1IFG &= ~BIT1; // Reset interrupt flag
uiLRMove++;
if (uiLRMove > 2 && uiLimitR == 0) // If really mean to change color
{
if (uiSelRed == 1 && uiLRMove > 0)
{
uiSelRed = 0;
uiSelGreen = 1;
uiSelBlue = 0;
uiLimitR = 0;
uiLRMove = 0;
uiLimitL = 0;
P1OUT &= ~opLEDButtonBlue;
P1OUT &= ~opLEDButtonWhite;
}
if (uiSelGreen == 1 && uiLRMove > 0)
{
uiSelRed = 0;
uiSelGreen = 0;
uiSelBlue = 1;
uiLimitR = 0;
uiLRMove = 0;
uiLimitL = 0;
P1OUT &= ~opLEDButtonWhite;
P1OUT |= opLEDButtonBlue;
}
if (uiSelBlue == 1 && uiLRMove > 0)
{
uiSelRed = 1;
uiSelGreen = 1;
uiSelBlue = 1;
uiLimitR = 1;
uiLRMove = 0;
uiLimitL = 0;
P1OUT |= opLEDButtonWhite;
P1OUT &= ~opLEDButtonBlue;
}
}
}
if (P1IFG & BIT2)
{
P1IFG &= ~BIT2; // Reset interrupt flag
uiLRMove++;
if (uiLRMove > 2 && uiLimitL == 0) // If really mean to change color
{
if (uiSelRed == 1 && uiSelGreen == 1 && uiSelBlue == 1 && uiLRMove > 0)
{
uiSelRed = 0;
uiSelGreen = 0;
uiSelBlue = 1;
uiLimitL = 0;
uiLRMove = 0;
uiLimitR = 0;
P1OUT &= ~opLEDButtonWhite;
P1OUT |= opLEDButtonBlue;
}
if (uiSelBlue == 1 && uiLRMove > 0)
{
uiSelRed = 0;
uiSelGreen = 1;
uiSelBlue = 0;
uiLimitL = 0;
uiLRMove = 0;
uiLimitR = 0;
P1OUT &= ~opLEDButtonBlue;
P1OUT &= ~opLEDButtonWhite;
}
if (uiSelGreen == 1 && uiLRMove > 0)
{
uiSelRed = 1;
uiSelGreen = 0;
uiSelBlue = 0;
uiLimitL = 1;
uiLRMove = 0;
uiLimitR = 0;
P1OUT &= ~opLEDButtonBlue;
P1OUT &= ~opLEDButtonWhite;
}
}
}
if (P1IFG & BIT3)
{
P1IFG &= ~BIT3; // Reset interrupt flag
uiLEDBlueInt = uiLEDBlueInt + (cLEDStep * uiSelBlue);
uiLEDGreenInt = uiLEDGreenInt + (cLEDStep * uiSelGreen);
uiLEDRedInt = uiLEDRedInt + (cLEDStep * uiSelRed);
if (uiLEDBlueInt > cLEDIncr) uiLEDBlueInt = cLEDIncr;
if (uiLEDGreenInt > cLEDIncr) uiLEDGreenInt = cLEDIncr;
if (uiLEDRedInt > cLEDIncr) uiLEDRedInt = cLEDIncr;
uiLRMove = 0; // Reset left or right move
}
if (P1IFG & BIT4)
{
P1IFG &= ~BIT4; // Reset interrupt flag
if (uiLEDBlueInt > 0)
{
uiLEDBlueInt = uiLEDBlueInt - (cLEDStep * uiSelBlue);
}
if (uiLEDGreenInt > 0)
{
uiLEDGreenInt = uiLEDGreenInt - (cLEDStep * uiSelGreen);
}
if (uiLEDRedInt > 0)
{
uiLEDRedInt = uiLEDRedInt - (cLEDStep * uiSelRed);
}
uiLRMove = 0; // Reset left or right move
}
if (P1IFG & BIT5)
{
P1IFG &= ~BIT5; // Reset interrupt flag
}
if (P1IFG & BIT6)
{
P1IFG &= ~BIT6; // Reset interrupt flag
}
if (P1IFG & BIT7)
{
P1IFG &= ~BIT7; // Reset interrupt flag
}
_BIS_SR(GIE); // Allow interrupts
}
// Interrupt service routine for Port 2
#pragma vector = PORT2_VECTOR
__interrupt void Port_2(void)
{
P2IFG = 0;
}
// Interrupt service routine for Timer A0 TACCR0
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A0(void)
{
static unsigned int uicounts; // Number of times in isr
uicounts++; // Increment PWM scan number
if (uicounts > cLEDIncr) uicounts = 0; // Check for overflow
if (uicounts < uiLEDBlueInt && uiLampON) //
{
P2OUT |= opLEDBlue;
}
else
{
P2OUT &= ~opLEDBlue;
}
if (uicounts < uiLEDGreenInt && uiLampON)
{
P1OUT |= opLEDGreen;
}
else
{
P1OUT &= ~opLEDGreen;
}
if (uicounts < uiLEDRedInt && uiLampON)
{
P2OUT |= opLEDRed;
}
else
{
P2OUT &= ~opLEDRed;
}
/*if (uicounts < uiLEDWhiteInt && uiLampON)
{
P1OUT |= opLEDWhite;
}
else
{
P1OUT &= ~opLEDWhite;
}*/
}
The meter clock on instructables has 2PWMs and uses the “less gifted” microcontroller that comes with the Launchpad (MSP430G2211).