MSP430 Example: Sleeping, Timers, and the Low-Frequency Clock

A key to getting good power performance out of an MSP430 application is good use of timers and hardware interrupts. Because a sleeping processor with stopped clocks uses several orders of magnitude less energy than a running processor (0.7µA in LPM3 sleep using the internal low-speed oscillator versus 300µA for 1MHz active computation on a MSP430G2xxx value line processor), sleeping for long periods of time can extend battery-powered applications' effective battery life by months or even years. Simply suspending the processing core (without stopping any clocks) reduces current consumption by almost an order of magnitude!

With this in mind, we will explore a typical blinkenlights example which does nothing but flash an LED at approximately 0.5Hz (one second on, one second off), using the internal very low power low-frequency oscillator (VLO) or external low-frequency watch crystal for timings. An external watch crystal will slightly increase current consumption but greatly improve timing accuracy, at the expense of one external component and the loss of two I/O pins.

The device-specific sections of this page pertain to the MSP430G2xxx value line parts, but are also applicable to many other MSP430 devices. The example code has been successfully run on a Launchpad development board and an eZ430-F2013.

Example code

Example source code to go with this tutorial can be found in the Mercurial repository msp430-examples/slow_timer on this server. The salient C file, slow_timer.c is available for individual download while reading this article. Details not relevant to the timer interrupt operation are left out of this article, so the source (or other examples on this site, as they are filled in) should be consulted to clear these up.

VLO versus LFXT1

The data sheet for your part will describe the tradeoffs between using the internal VLO and an external crystal for low speed timings. For the parts we are looking at, the immediate observations are that:

  • VLO draws less power. VLO consumes a typical 0.2µA less current (and potentially as much as 0.8µA) than an external crystal oscillator.

  • LFXT1 is more accurate. An external crystal oscillator provides a reference clock of 32.768kHz, with an error of 1PPM or less being reasonably achievable, with temperature effects accounting for no more than a few PPM over "room" temperature ranges.

  • VLO is environmentally sensitive. The internal VLO has a nominal frequency of 12kHz, but may vary from as low as 4kHz to as high as 20kHz, with a drift of 0.5% per degree C and 4% per volt Vcc at 25°C.

  • LFXT1 uses two GPIO pins. On these small parts, the external crystal occupies two pins which are multiplexed with GPIO (ports 2.6 and 2.7 on the MSP430G2x[0123]1).

Which option you will choose for your application depends on your requirements and cost sensitivity. If price, power consumption, or I/O flexibility are paramount and absolute timings are of little importance, VLO may be a good choice. If timing is critical, an external crystal oscillator may be a good choice. Note that the crystal oscillator may be turned off for deep sleep when the processor is not waiting on a timer, which may mitigate the power cost of more precise timings in some circumstances, but that starting and locking the crystal may take some time.

Selecting the low-frequency clock source

While the MSP430 clock tree is not as complicated as some processors, its power-sipping capabilities require some understanding of clocking and clock selection. Not only can the various clocks in the processor use configurable frequency sources, but many devices can select between multiple clocks at runtime. The auxiliary clock (ACLK) is a dedicated low-frequency clock source which is well-suited to our needs, as it can be left running while the processor core and its oscillator are shut down.

LFXT1 will be used for the ACLK low-frequency clock source by default. This means that, for operation of the low-frequency clock, the programmer must either configure the crystal correctly or enable VLO. If VLO operation is desired, the VLO must be enabled by setting bits 4 and 5 (LFXT1S) to 2 in the Basic Clock System Control Register 3 (BCSCTL3), as follows:

BCSCTL3 |= LFXT1S_2;

If, instead, a watch crystal is to be used, the LFXT1S bits are left unchanged and the crystal load capacitors are set appropriately by configuring the XCAP bits (2 and 3) of BCSCTL3. See the reference sheet for appropriate values; for a 12.5pF crystal, for example, one would use an XCAP value of 3:

BCSCTL3 |= XCAP_3;

The ACLK can optionally be divided off its frequency source by a factor of 2, 4, or 8 by setting bits 4 and 5 (DIVA) of the Basic Clock System Control Register 1 (BCSCTL1) to a value other than 0, the reset value. Since we do not need particularly long timings for our exercise, we will leave this divider alone.

Timing off the low-frequency clock

The timer modules of the MSP430 can be configured to run off of several potential clock sources, including ACLK and the sub-main clock (SMCLK), both of which can be driven from several possible frequency sources. Depending on your particular part, the particular timer in question, and the external hardware support available, some combinations of frequency sources, timers, and clocks may not be available.

For this exercise, we will be using the low-frequency oscillator we previously configured as a frequency source for ACLK, driving TimerA in count-up mode, to achieve an approximate one interrupt per second timing. The Watchdog timer (WDT) or TimerB would also be reasonable interrupt sources, and any of these timers can be sourced by either ACLK or SMCLK. Consult the data sheets for the implications of changing up devices and clock sources.

Count-up mode causes TimerA to repeatedly count from zero to a configurable value between 1 and 32,767, optionally triggering an interrupt when the configured value is reached and the timer resets. In order to set the interval to approximately one second, we will stuff the counter value at which we wish to reset into Timer_A Capture/Compare Register 0 (TACCR0). To achieve a roughly one interrupt per second interval, we need to use a trigger value of 32767 if LFXT1 is present, and about 12000 for VLO. (Recall that this value will change considerably for VLO with varying Vcc and ambient temperature, so it's only a ballpark!)

#ifdef USEXTAL
#define TRIGGER 32767
#else
#define TRIGGER 12000
#endif

TACCR0 = TRIGGER;

We want to be notified when the timer resets, so we enable the register 0 interrupt by setting bit 4 (CCIE) of the Timer_A Capture/Compare Control Register 0 (TACCTL0). This will cause a wakeup event each time the count-up timer reaches the value stored in TACCR0, as well as the execution of the Timer_A interrupt handler if it is defined.

TACCTL0 |= CCIE;

Once the timer trigger value is set and its interrupt enabled, we must configure it for count-up mode and start the timer. These operations are both performed in the Timer_A Control Register, TACTL. To select ACLK as our clock source, we set bits 8 and 9 (TASSEL) to 01, and to enable the timer in count-up mode, we set bits 4 and 5 (MC) to 01 as well:

TACTL = TASSEL_1 | MC_1;

Handling the Timer_A interrupt

The details of interrupt handling could easily fill a tutorial (or three!) of their own. For the purpose of this exercise, only very simple interrupt handling is required. Each time the Timer_A interrupt is triggered, we will toggle P1.0, which is attached to an LED on both the Launchpad and eZ430-F2013 development boards. To accomplish this, we declare a function of type interrupt on TIMERA0_VECTOR, as follows:

interrupt (TIMERA0_VECTOR) ta_handler()
{
    P1OUT ^= LED;
}

Each time this function is invoked, it will invert the state of P1.0, causing the LED to flash with the interrupt rate. Because we have not specified any other options for interrupt handling, when the processor finishes this operation it will return to whatever it was doing before — including sleeping, if it was in a low-power mode when the interrupt fired.

The application body

Since our toy application doesn't do anything but blink the LED, our application body consists of a single instruction! Once Timer_A is configured to interrupt on an appropriate interval, an interrupt handler installed, and the timer started, the processor can sleep forever. The interrupt handler will take care of our dirty work for us, waking the processor from sleep when required. Therefore, our application body is simply a drop into Low-Power Mode 3 (LPM3).

LPM3;

There are other details to this application (such as disabling the watchdog, configuring the GPIO port for output, etc.) which can be found in the example source code. You are encouraged to examine this code at your leisure, and experiment with tasks such as blinking the LED off the WDT or Timer_B, using alternate modes with Timer_A, and performing background computations rather than eternally sleeping in the mainloop.

[an error occurred while processing this directive] [an error occurred while processing this directive] [an error occurred while processing this directive]