Getting Started with MSP430 Timers – Part 3

A Timer Example – Blinking a LED
A blinking LED is the “hello world” of embedded systems, and is also a great example of how to use the Timer.
The Inefficient Way
The “hello world” programs are usually the first ones that we write, so of course need to be simple. Usually the LED is blinked using a delay loop, as it’s the quickest easiest way to get that first project flashing at you. They’re also very inefficient, as the CPU is using all its energy counting out the delay. The below is a typical example:
/*
* DEFINE LED PORT & PIN
*/
#define LED_PDIR P1DIR
#define LED_POUT P1OUT
#define LED_PIN BIT0
/*****************************************************************
*
* FUNCTION: blinkDelay
*
* PURPOSE: Blink an LED using a Delay loop
*
*****************************************************************/
void blinkDelay()
{
//Configure the LED
LED_PDIR |= LED_PIN; //Set P1.0 as an Output pin
LED_POUT &= ~LED_PIN; //Set P1.0 LOW (turn LED off)
//Infinite loop to blink the LED
while (1)
{
LED_POUT ^= LED_PIN; //Toggle the LED, using XOR operator
//Delay for 100000 cycles – the CPU is occupied during this
__delay_cycles(100000);
}
}
Using the Timer
Let’s give the CPU a rest (and save a whole load of power) by using the timer. In this example, we’ll use the Up Mode of the timer as it’s best suited to what we need to do. Take a look at the example below (we’ll go through it in more detail afterwards):
/*
* DEFINE LED PORT & PIN
*/
#define LED_PDIR P1DIR
#define LED_POUT P1OUT
#define LED_PIN BIT0
/*****************************************************************
*
* FUNCTION: blinkInterrupt
*
* PURPOSE: Blink an LED using TimerA and interrupts
*
* PARAMETERS: none
*
*****************************************************************/
void blinkInterrupt()
{
//Configure the LED
LED_PDIR |= LED_PIN; //Set P1.0 as an Output pin
LED_POUT &= ~LED_PIN; //Set P1.0 LOW (turn LED off)
ConfigTimerA(1000); //Configure the timer
while (1)
{
_bis_SR_register(LPM3_bits + GIE); //Enter Low Power Mode 3 with interrupts
}
}
/*****************************************************************
*
* FUNCTION: configTimerA
*
* PURPOSE: Configure the TimerA
*
* PARAMETERS: delayCycles: number of clock cycles to delay
*
*****************************************************************/
void ConfigTimerA(unsigned int delayCycles)
{
TACCTL0 |= CCIE; //Enable Interrupts on Timer
TACCR0 = delayCycles; //Number of cycles in the timer
TACTL |= TASSEL_1; //Use ACLK as source for timer
TACTL |= MC_1; //Use UP mode timer
}
/*****************************************************************
*
* FUNCTION: Timer_A0
*
* PURPOSE: Interrupt Handler to service the TimerA0 interrupt
*
* PARAMETERS: none
*
*****************************************************************/
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0(void)
{
LED_POUT ^= LED_PIN; //Toggle the LED
//When we exit the interrupt routine we return to Low Power Mode
}
Let’s look at the 3 functions above in a little more detail
blinkInterrupt
This is simply the “parent” routine. It configures the LED’s, calls the routine to configure the TimerA, and then goes to sleep in Low Power Mode. Now that the processor is asleep, the interrupt will handle all the action.
ConfigTimerA
Now you need to start looking at the data sheet and user guide in more detail. Find the section in the user guide that refers to the TimerA registers TACCTL0, TACCR0 and TACTL. These are all that we need to get the timer ticking:
- TACCTL0: This is the Capture/Compare Control Register. There are 3 of these, one for each of the TimerA instances, numbered TACCTL0, TACCTL1 and TACCTL2. For our example we’re leaving the register at its default power-on values (i.e. in simple compare mode), except for the CCIE bit – by setting this to 1, we are enabling the interrupt. In the next few lines of code we configure when the interrupt will fire.
- TACCR0: This is the Capture/Compare Register, and contains (for our purposes) the counter value at which the interrupt is triggered. We have set a value of 1000, which means that the interrupt will trigger whenever the timer counter reaches 1000. Again, there are 3 of these TACCR0, TACCR1 and TACCR2. We’re only using TACCR0 for this example. But there’s something missing – what about the various modes – Continuous, Up and Up/Down? We set the mode next.
- TACTL: This is the TimerA Control Register – and there is only one of these for TimerA. We use this timer to specify which clock source we want as well as the timer mode. In this example we’re using the ACLK (Auxiliary Clock) as it is slowest, uses the least power, and operates even when the CPU has gone to sleep. This is set with the TASSELx bits. Finally the mode: we’ve set the timer to run in Up mode using the MCx bits.
With the above configuration, we’ve set the timer to count up to 1000 using the ACLK and trigger an interrupt. As it’s in Up mode, the timer counter then overflows back to zero and starts the process again.
The Interrupt Service Routine (ISR)
This routine toggles the LED. The way the routine is defined is a little different from “normal” routines. The portion that reads
is a standard way to define the TimerA0 ISR; the
can be named whatever you prefer.
When the interrupt has been processed, the MSP430 re-enters the low power state it was in before the interrupt was triggered. You can prevent this if you want (say, to do other processing in the main() routine) by adding the following line before exiting the ISR:
It’s Really That Simple
After a fair bit of theory, you’ll see that the actual coding of a timer is pretty straightforward. We haven’t nearly covered all the features of the Timer, but this should give you a good foundation to continue exploring.
-
Some of the topics you could look at include:
- Making Timers output directly to a pin – without taking any of the CPU resources
- Using Timers for PWM
- Using the Capture functionality of the CCR : capture incoming interrupts
As always, please drop me a line with any questions or comments!
For the MSP430 coding environment do you recommend code composer studio?
Peter B.
To be honest I haven’t used IAR Workbench (www.iar.com) the other big player. IAR is code-size limited to 8kb unless you are prepared to buy it; whereas Code Composer Studio is limited to 16KB (or is totally free if you use the GCC toolchain). I’m very happy with Code Composer Studio – in my mind I’d only really switch to IAR if I wanted to develop for multiple MCU’s in a single development environment (IAR supports many manufacturers).
Thank you for this simplification of subject. I have a homework that i should build a frequency meter with MSP430 using its timers and they are not very clearly explained anywhere exept here. Your work has been a lifesaver for me. Thank you again.
Thanks for the feedback, really pleased it helped you!
how does one figure out, in c, how to name the interrupt service routine?
#pragma vector=TIMER0_A0_VECTOR __interrupt
How did you know that TIMER0_A0_VECTOR is what you needed to properly name the ISR
Good question…
Page 11 of the datasheet for the MSP430G2553 gives a table of the interrupt vector addresses. We are trying to trap the interrupt for Timer0 (in column: Interrupt Source), for TA0CCR0 (in column: Interrupt Flag). The address for this interrupt is 0FFF2h (or written like we do in C, it is 0xFFF2).
Back to Code Composer Studio, and find the “msp430g2553.h” file (look under the Includes branch on the project explorer). A seach for the interrupt address 0xFFF2 shows:
#define TIMER0_A0_VECTOR ".int09" /* 0xFFF2 Timer0_A CC0 */
There’s your interrupt vector name!
There may be a shorter way, but by referencing the datasheet and the header file for your processor you’ll always be 100% right.
Hope that gets you up and running
Cheers
Andrew
Thanks for the reply
I also found that name in the Header File for the processor
Hi,
Regarding the following:
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
// Do stuff
}
Can you explain why you defined the timer ISR the way you did? I understand why you set vector equal to ” TIMER_A0_VECTOR” thanks to your explanation above. However I still have a few questions. Can you explain your use of “#pragma”? Also, how did you know to use “__interrupt”? After a little searching, I found other people using “__interrupt”, even on other non-TI hardware. I read somewhere “__interrupt” in not strictly part of the C language but it is an extension of it. What does this mean?
Thanks for the help,
David
Hi David
The #pragma and the __interrupt are the syntax that the MSP430 expects its interrupt handlers to be in. To be honest I don’t recall where I first read that this was required. Other microcontrollers (and I guess possibly other compilers) use different ways to define their interrupts. So you’re right – they aren’t part of C, but are just how the interrupt handlers work for the MSP430.
Hope that helps…
Cheers
Andrew
Hi all,
I’m struggle with implementing a timer A, is any one willing help me?
Thank you
Sure, drop me a mail: info (at) crash-bang.com
Give as much detail as possible..
Cheers
Andrew
Thank you for taking the time to explain. I read every where that the delay loop was an inefficient way to perform delays but no one took the time to provide a solution. Great tutorial and easy to follow. Keep up the great work. I look forward to reading more of your posts.
Pleased that it helped! It took me a while originally to get to grips, so happy that my learning was useful.
Cheers
Andrew
I’m working on a frequency meter and I need to mesure up to 500KHz. I tried to use the UP mode of Timer A but I find that the timer can’t work under 2µs period.
With TA0CCR = 15 or 31, I have the same period (~2µs).
Then, if I add my function the execution of all the task take ~3µs, so I can’t mesure a frequency higher than 333KHz.
Is there any other solution ?
Hi. I can’t immediately think of a solution without spending time in the datasheets and testing. Perhaps try exploring one of the TI ARM boards which run at higher frequency, or their E2E forum?
Sorry I couldn’t help, Andrew
Hi, thank you for replying so quickly.
Actually i’m working with a photodiod circuit that give me a square signal which is function of the luminosity. The sensor will work on battery, that’s why i chosed the MSP430.
I didn’t know this forum thank you. If I find a solution I’ll post it here !
Is it possible to measure frequency of a square wave signal , without capturing TAR values ? because i am using both the timers for other purpose.. to generate particular controlling signal.