Introduction

Welcome to the MSP430 Timer counter academy. This academy introduces the basics of using the Timer_A module in counter mode to perform basic timer applications and is demonstrated on a MSP-EXP430FR2433 LaunchPad. In the tasks below, you will configure the MCU and setup the timer to use an interrupt service routine (ISR) triggered when the timer overflows as well as learn how to calculate the timer period.

Prerequisites

Hardware

The following hardware is required for this lab

Software

Installing the software

NOTE:

This software examples used for this lab should be imported from within the TI Resource Explorer. This can be accessed from the web or within a locally in CCS by clicking View->Resource Explorer. Prior to Importing a Project, you first need to download MSP430Ware in order to be able to import the examples to the CCS IDE. This may be done when CCS was installed or can be done later by clicking the "Download and Install" icon in the top right. Note that installing MSP430Ware for any project will install ALL labs and content, so this only needs to be done once.

Associated academies

These academies cover using the timer in different modes as well as provide additional information on the timer functionality.

The Precision Labs training videos series provides baseline introduction to the timers and clocks. The documentation can be referenced for the MSP430 MCU and the LaunchPad details.

Overview

In this lab you will be using the MSP430FR2433's Timer_A module to learn how timer modules works in MSP430 MCUs. There are a few versions of timer modules in MSP430 devices that may have varying features (such as Timer_B), however Timer_A is a very good representation of the MSP430 Timer architecture.

Each timer academy will review concepts and walk through example code to show how to initialize necessary I/O pins, clocks, and configure Timer_A for its specified purpose. Then include tasks with instructions for you to modify the provided example as well as quiz questions to check your understanding.

Timer_A Introduction:

Timer_A is a 16-bit timer/counter with up to seven capture/compare registers (CCRx). Timer_A can support multiple captures or compares, PWM outputs, and interval timing. Timer_A also has extensive interrupt capabilities. Interrupts may be generated from the counter on overflow conditions and from each of the capture/compare registers. More details can be found in the Timer_A Chapter of the MSP430FR4xx_2xx MCU UserGuide.

The 16-bit timer/counter has four operating modes: Up Mode, Continuous Mode, Up/Down Mode and Capture Mode.

Continuous Mode

Continuous mode is the most basic timer mode where the 16-bit timer just repeatedly counts up to its max value of 0FFFFh, overflows, and restarts from zero.

The TAIFG interrupt flag is set when the timer counts from 0FFFFh to zero.

Using the CCRx registers, interrupts can generated for independent time intervals and output frequencies. Every time an timer value matches a CCRx value, an interrupt can be generated.

Up Mode

The Up mode is used if the timer period must be different from 0FFFFh counts. The timer repeatedly counts up to the value of compare register TAxCCR0, which defines the timer period.

The TAxCCR0 CCIFG interrupt flag is set when the timer counts to the TAxCCR0 value. The TAIFG interrupt flag is set when the timer counts from TAxCCR0 to zero.

Additional CCRx registers can still be used to generate interrupts during this new time period.

Note:

Changing TAxCCR0 while the timer is running may result in unexpected behaviors. To avoid the uncertainty, TAxCCR0 should only be updated in Stop mode (MC = 0).

Up/Down Mode

The Up/Down mode is used if the timer period must be different from 0FFFFh counts, and if symmetrical pulse generation is needed. The timer repeatedly counts up to the value of compare register TAxCCR0 and back down to zero. The period is twice the value in TAxCCR0.

In Up/Down mode, the TAxCCR0 CCIFG interrupt flag and the TAIFG interrupt flag are set only once during a period, separated by one-half the timer period. The TAxCCR0 CCIFG interrupt flag is set when the timer counts from TAxCCR0 – 1 to TAxCCR0, and TAIFG is set when the timer completes counting down from 0001h to 0000h.

The Up/Down mode supports applications that require dead times between output signals.

Timer Clocking

The 16-bit timer/counter register, TAxR, increments or decrements (depending on mode of operation) with each rising edge of the clock signal. The timer clock can be sourced from the internal ACLK and SMCLK, or externally from TAxCLK or INCLK. The clock source is selected with the TASSEL bits in TAxCTL (Timer_Ax Control) register. The selected clock source may be passed directly to the timer or divided by 2, 4, or 8, using the ID bits of TAxCTL register. The selected clock source can be further divided, if necessary, by 2, 3, 4, 5, 6, 7 or 8 using the TAIDEX bits of the TAxEX0 (Timer_Ax Expansion 0) register.

The default for the ACLK is the internal 32kHz clock. The default for the SMCLK is the internal DCO which is set to 1 MHz on reset. On reset, the default setting for the clock source is TAxCLK. So, it is important to configure the clock source to use the timer.

Note:

The clock divider (ID bits and TAIDEX bits) should not be changed while the timer is running. It could cause unexpected behaviors. Stop the timer first (MC = 0) when changing the ID bits or TAIDEX bits.

Getting Started

Hardware Setup:

  • MSP-EXP430FR2433 LaunchPad

    1. Plug in the MSP430FR2355 Launchpad into your PC using a Micro-USB cable and make sure the P1.0 jumper is populated to connect LED1.

Task 1 – Run counter mode example and modify it

This example code toggles P1.0 using Timer_A and doesn't require modifications to work, but we will walk through the code and make modifications at the end to highlight various Timer_A features.

Import the timer example

Using Resource Explorer, navigate to: Software->MSP430Ware-3.8x.xx.xx->Devices->MSP430FR2433->Register Level->

  1. Import the Timer0_A3 toggle P1.0 example msp430fr243x-ta0-01.c into CCS from TI Resource Explorer.
    • MSP430ware→Devices→MSP430FR2XX_4XX→MSP430FR2433→Peripheral Examples→Register Level→msp430fr243x-ta0-01.c

    • Click import in the upper right hand corner
  2. Go ahead and program the LaunchPad by clicking the "download and debug" button, click play, and you should see LED1 start blinking.

Code overview

Initialization

Before configuring the MSP430's Timer_A module, we need to initialize the the MSP430 by disabling the watchdog timer and initializing the GPIO pin for the LED as an output. The watchdog timer must be stopped to prevent the MCU from reseting while the demo is running. P1.0 is configured for an output and is connected to a header and LED on the LaunchPad.

    #include <msp430.h>

    int main(void)
    {
        WDTCTL = WDTPW | WDTHOLD;                     // Stop WDT

        // Configure GPIO
        P1DIR |= BIT0;                                // P1.0 output  
        P1OUT |= BIT0;                                // P1.0 high

        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings
        PM5CTL0 &= ~LOCKLPM5;

        ...

    }

Timer configuration

Now, we can start initializing the Timer_A module. In this example, we will use Timer A Instance 0 (TA0), Compare/Capture Register 0 (CCR0), and continuous mode to blink an LED1 on the LaunchPad at a specified frequency using an interrupt. In order to enable a Capture/Compare interrupt for TA0 and CCR0, we must enable the CCR0 interrupt by setting the Capture/compare interrupt enable bit in the Capture/Compare Control 0 register (CCTL0).

    TA0CCTL0 |= CCIE;

Now that the Capture/Control Interrupt is enabled for CCR0 in Timer_A0, the MSP430 will interrupt once the timer reaches TA0CCR0. We can set any value we would like for TA0CCR0 as long as it less than 0xFFFF (65,535 decimal). In this case, TA0CCR0 is set to 50000.

    TA0CCR0 = 50000;

Next, we need to set the clock source and timer mode for Timer_A0. In this example, we will source from SMCLK, which runs at 1 MHz by default in the MSP430.

Timer_A has one control register, TAxCTL, for each instance of the timer. The Timer clock source (SMCLK) and Continuous Mode is selected in the following line of code.

    TA0CTL |= TASSEL__SMCLK | MC__CONTINUOUS;

So what are expecting to see? Well, let's do some math on the information we know:

  • SMCLK = 1 MHz, so one timer count is 1/(1 MHz) = 1 us.
  • The timer will interrupt when the Timer_A count TA0R = TA0CCR0 = 50000.
  • Therefore the timer will interrupt in approximately 50000 * 1us = 50000 us = 50 ms.

Timer interrupt

Before going into the ISR, we need to enable global interrupts in the MSP430 so that we can perform an action once TA0R = TA0CCR0.
We also want the CPU to remain in its lower power state while the timer is counting. This is recommended practice to conserve power.

        __bis_SR_register(LPM0_bits | GIE);

The Timer_A module supports Timer_A0 interrupts through the TIMER0_A0_VECTOR. When the counter reaches TA0CCR0, the interrupt below will trigger.

In the interrupt, the code P1OUT ^= BIT0; simply toggles P1.0 (LED1).

        // Timer A0 interrupt service routine
        #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
        #pragma vector = TIMER0_A0_VECTOR
        __interrupt void Timer_A (void)
        #elif defined(__GNUC__)
        void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer_A (void)
        #else
        #error Compiler not supported!
        #endif
        {
            P1OUT ^= BIT0;
            TA0CCR0 += 50000;                             // Add Offset to TACCR0
        }

Updating the CCR0 register

Because the timer is in Continuous mode, it will continue to count to the maximum value 0xFFFF and then rollover to 0x0000, and keep counting. If we don't change anything, then the MCU will interrupt once the timer rolls over and counts back up to 50,000, which would actually be take longer than 50000 counts. To establish another interval of 50000 counts before the interrupt triggers again, TA0CCR0 is increased by 50,000. Even though this number is larger than the 65,535 max count, it's fine because the CCR0 value will also roll over. This is an easy way to trigger an interrupt every 50,000 counts.

What frequency is the LED blinking at currently in the example?

This was slightly a trick question because the timer is turning the LED on for 50 ms and then off for 50 ms. This means the LED will actually be blinking every 100 ms, which is a frequency of 10 Hz!

Blink the LED at 25 Hz.

To check that you understand how the CCR0 is related to period of which the GPIO is being toggled, modify the example to toggle the LED at 25 Hz. 25Hz is rather fast for our eyes, but you should be able to see the LED barely blinking but quite fast. Remember, to know how many counters we want we need to know how fast the timer is counting and how much time we need between interrupts. Once you know the updated count value, update your code and program your LaunchPad.

How fast is the timer counting?

How many counts do we need between interrupts to acheive 25 Hz?

To blink the LED at 25 Hz, you need to increment CCR0 by 20,000 each interrupt like this:

    // Timer A0 interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = TIMER0_A0_VECTOR
    __interrupt void Timer_A (void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer_A (void)
    #else
    #error Compiler not supported!
    #endif
    {
        P1OUT ^= BIT0;
        TA0CCR0 += 20000;                             // Add Offset to TACCR0
    }

Changing timer frequency

Now, suppose you wanted to get a slower interval. Let's try and blink the LED as a slow .5 Hz heart beat, which would be on for exactly one second and then off for one second. How many counts would we need achieve this with the current timer configuration?

All of these values are too large for the 16-bit timer, as the max period is 65,535 (0xFFFF), or ~65.5 ms with the current 1 MHz clock. Anything longer than this is not currently possible. The only option is to slow the timer down, so that the 65,535 counts will represent a long period of time. We must adjust the input clock.

There are two real options: use a slower clock source such as ACLK or use a clock divider on SMCLK to slow down the timer.

You choose:

In this case, we are going to stick with the 1MHz SMCLK as our input source, but we are going to use Timer_A's built in clock dividors introduced in the Timer Clocking section in the Overview. We first need to figure out how much we need to divide the clock. Since Timer_A is only 16 bits, our max count is 65,535. So, to achieve a >1 second period, we need to to be under 65.535 kHz.

We have two 1-8x dividers available on Timer_A: ID (input divide) and IDEX (input divide extended). If we divide the SMCLK/8 = 1MHz/8 = 125kHz. We will still need to divide further. If we take the 125 kHz / 2 = 62.5 kHz. Perfect!

So, how do we configure the dividers? If you search in the datasheet, you will find that the ID bits can be found in the TA0CTL reigster and the TAIDEX bits live in the TA0EX0 register. We need to configure these when we initialize our timer. Here is what that looks like in software:

TA0EX0 = TAIDEX_1;                                    // TAIDEX_1 is dividing the clock by 2
TA0CTL |= TASSEL__SMCLK | ID__8 | MC__CONTINUOUS;     // SMCLK/8,  continuous mode

Note:

Remember to configure IDEX first, since when we configure ID__8 we also start the timer by putting it in continuous mode and we don't want to switch the clock source while the timer is running.

How much do we need to increment CCR0 to achieve a 1 second interval?

Great! Now update this in you CCS project and download it, and you should have a perfect 1 second LED heartbeat.

The frequency of ACLK is 32678 Hz, which means that every count of Timer_A will be 1/(32,678 Hz) = 31 us when ACLK is used. To switch to ACLK, we need to change the bits in TA0CTL like this:

        TA0CTL |= TASSEL__ACLK | MC__CONTINUOUS;

Now, we go back to the ISR and need to determine our period in the blank below. We are running off the 32,768 Hz ACLK now, so how many counts do we need to wait for for 1 second?

        // Timer A0 interrupt service routine
        #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
        #pragma vector = TIMER0_A0_VECTOR
        __interrupt void Timer_A (void)
        #elif defined(__GNUC__)
        void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer_A (void)
        #else
        #error Compiler not supported!
        #endif
        {
            P1OUT ^= BIT0;
            TA0CCR0 += _____;     // Fill in TA0CCR0 value here
        }

How much do we need to increment CCR0 to achieve a 1 second interval?

Great! Now update this in you CCS project and download it, and you should have a perfect 1 second heart beat LED.

Summary

At the end of this Academy you should now be familiar with:

  • Using Timer_A in basic count mode
  • Using CCRx to generate interrupts at fixed time intervals
  • Switching and dividing clock sources to adjust the timer period
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.