Search Microcontrollers

Friday, August 10, 2012

MSP430G2 - Timers and PWM

We previously used a timer (TIMER A) to execute a procedure at regular intervals.
This is definitely a useful way of using the timers, but there is much more we can do with them.

Another interesting application is the generation of PWM (Pulse Width Modulation) signals.
I am not planning to explain here what PWM is, and I will assume you are familiar with the basic concepts (Frequency and Duty Cycle), if not you should be able to find everything here :
http://en.wikipedia.org/wiki/Pulse-width_modulation.

These signals have  a number of different practical applications, they can be used to control precisely the speed of electric motors, to drive power transistors in switching power supplies, they can be used to dim a led light etc.

A PWM signal is characterized by two parameters : Frequency and Duty Cycle.
The MSP430G2 time handles both of them.

The first thing we have to define is the period T (which also defines the frequency F=1/T).

Let's image to run the Timer A  at his full speed (16MHz) having the MCLK sourced from DCO with divider = 1 and SMCLK, also selected as clock source for Timer A, with the minimum division : 1
Let's also assume that Timer A is configured to run in "up mode", basically it will count from 0 to the values set in TACCR0.
Each count will take 1 / 16.000.000 seconds (1 cpu clock), so, if we make the timer to count 8 times, the period will be :

T =   1 / 16.000.000 * 8 = 1 / 2.000.000 s
and obviously the frequency F = 1 / T = 2MHz

TACCR0 has to be set to 7 for this (the count starts from 0)

Defining the Duty Cycle means we are defining the period for which the signal should be on (Ton) in each cycle.

Since the MSP430 has several capture/compare devices per each timer, we can use a second one (CC1) to define Ton.
The duty cycle is defined as Ton/T, but we also know that the periods are directly proportional to the number of timer counts, so, assuming we want to obtain a 25% Duty cycle

 0.25 = Ton / T

We know T = 8 -> Ton = 8 * 0.25 = 2


TACCR0 = 7;  // PWM Period (CC0)
TACCR1 = 1;  // PWM Ton   (CC1)          


In this picture, on the Y axis we have the Timer A counter value, on the X Axis each block is is a counter clock (in our case 1 / 16.000.000 s).
Since the timer is configured in "up mode", it will start from 0 and increment by one until it reaches TACCR0 (7) then it will drop again to 0.
Another action will be triggered when TACCR1 is reached.
How the CC0 and CC1 triggers can be combined is defined by choosing the "output mode" of the time as visible in fig 12-12 (from TI's MSP430 User guide).
The waveform we want to obtain is the red/green one, which matches with Mode 7.



This is chosen setting the right value in the TACCTL1 register

TACCTL1 = OUTMOD_7;

Obviously we could just set up an interrupt, check the TAR counter and drive accordingly an output pin, but that would work only for low frequencies as the cpu cycles needed to compare the values and drive the pin would affect our waveform, plus we would be using the CPU almost exclusively for this purpose.

Luckily the timer is autonomous for this and can automatically drive an output pin, using the criteria defined by the mode selected (reset/set in our case).
This means we do not need an interrupt nor we need to do anything special with the CPU which could be "sleeping" or performing other tasks.

Checking the device specific Datasheet (msp430g2553 in my case) we find that pin P1.2 supports the function TA0.1 (timer A output) with mode 1 (PSEL = 1 , PSEL2 = 0, DIR = output -1-).


The bit related to pin 1.2 is the 3rd in the registers :

7654x3210 
0000x0100
and 0100b = 0x4 in hex  (also 2^pinNbr)


P1DIR |= 0x04;     // P1.2  set as output
P1SEL |= 0x04;     // P1.2  TA0.1 option

So globally the test program is :

#include <msp430g2553.h>

void clockConfig()
{
 // configure the CPU clock (MCLK)
 // to run from DCO @ 16MHz and SMCLK = DCO
 BCSCTL1 = CALBC1_16MHZ; // Set DCO
 DCOCTL = CALDCO_16MHZ;
 BCSCTL2= DIVS_0 + DIVM_0; // divider=1 for SMCLK and 1 for MCLK
}

void main(void)
{
WDTCTL = WDTPW + WDTHOLD;   // Stop WDT
clockConfig();

P1DIR |= 0x04;     // P1.2  output
P1SEL |= 0x04;     // P1.2  set to TA0.1 
TACCR0 = 7;        // PWM Period
TACCTL1 = OUTMOD_7;    // CCR1 reset/set
TACCR1 = 1;  // CCR1 PWM Ton -> duty cycle
TACTL = TASSEL_2 + MC_1;    // SMCLK, up mode
_BIS_SR(CPUOFF);      // Enter LPM0


If we hook an oscilloscope probe to P1.2 we can see this :



In the lower left rectangle you can verify that the signal frequency is correctly 2MHz.
In the next two pictures you can see the effect of changing the Duty Cycle value (via TACCR1) to 50% and to 75%.

Since the maximum clock frequency of the clock is 16MHz and we need at least an "up" value and a "down" value, it means that the maximum pwm frequency we can obtain is 16/2= 8MHz, as visible below. 


The signal appears heavily rounded mainly due to the way I connected the probe (non shielded cable, about 30cm long, definitely not the best setup to monitor a 8MHz signal).
This high frequency is not really usable for PWM purposes, unless you are happy with 3 duty cycle levels (0%,50%,100%).

This is because your Ton time is defined by a finite number of cycles, if you are working with a T defined by a large number in TACCR0 (thus increasing your period T and lowering your PWM frequency) you can probably have  a fine control over the duty clycle.

So it's a bit of trade off between maximum frequency and detailed control of the duty cycle, where to set the bar really depends on your application.
In many cases you will probably be happy with frequencies of few KHz and about 1% of Duty Cycle increments.

Let's make a numeric example :

Required F = 10KHz -> T = 1 / 10.000 s
Duty Cycle increments = 1% -> dT = 1 / 10.000 * 1 / 100 = 1 / 1.000.000 s

The easiest way to achieve this would be to set SMCLK to run at 1MHz, TACCR0 = 99 (100 cycles/period = 1MHz/100 = 10KHz) and TACCR1 between 0 and 99.

I deliberately chose the lowest possible value for SMCLK, any value equal or bigger than that would 4 do, i.e. SMCLK = 4MHz

-> TTACR0 = 4 * 100 -1 = 399;
-> TACCR1 between 0 to 399 with a minimum increment of 1/4% = 0.25%


2 comments:

tsu said...

thank you :)
btw remember there is maximum pwm frequency avalible on msp430 parts, maybe that's because your 2mhz didn't look good!

peace

Francesco Agosti said...

Hello thanks for your comment.
What do you mean by "my 2MHZ did not look good?"

the limitation is indeed there, but should not affect the waveform as far as I know.
The fact that my 8MHz frequency wave was rounded was definitely due to the Oscilloscope setup, I am sure if I did manage to probe the signal directly on the MCU pins it would have been better :)