08 June 2010

Crystal Timers

The MSP430 has great internal clocks, but for time-critical applications we need something more stable. Crystals are one of the best oscillators available, and are quite stable if they are handled correctly. While it would make more sense to look at timers starting with the internal clocks, I have an immediate need to work toward USB communication, which requires the use of crystals for timing.

MSP430's can have up to two crystals to set clocks externally. XT1 is typically a low frequency (< 1 MHz), which is why it is usually referred to as LFXT1 (low frequency crystal 1). All of TI's MCU's have pins that can be used with one of these low frequency crystals.  The chips can also be configured to use a high frequency crystal if needed.  Many MCU's also have an XT2 built specifically for HF oscillators.  The exact frequency chosen depends on the application, but a commonly used frequency is 32,768 Hz, often referred to as 32 kHz even though it is not a decimal kHz. It is, however, a binary kHz, which is why it is a convenient frequency for timing applications. In exactly 1 second, the crystal will go through 2**15 oscillations. When 15 bits roll over, the timer is reset to zero, and every reset happens at exactly 1 s intervals.

Connecting a crystal to an MSP430 is a little tricky. Fortunately, TI has provided some excellent documentation to help. Essentially, the crystal should be close to the chip and isolated from other signals in the circuit. Crystals have particular capacitances that need to be matched correctly in order to be accurate. The newer MSP430's (anything but the x1xx series, really) are able to set capacitors to a few values internally. The target board I received included a 32 kHz crystal with 12.5 pF capacitance. 12.5 pF is one of the values that can be set internally, so the crystal can be put on the board without needing external capacitors. It does require, however, setting the MCU software initially to have the right capacitance internally. This is done by setting the appropriate value to the XCAPx bits in BCSCTL3 (Basic Clock System Control 3). The header files include the values XCAP_x to help set the proper bits correctly. For my 12.5 pF crystal, XCAP_3 is the correct selection. Note in the code below rather than setting BCSCTL3 = XCAP_3, |= is used to only change the XCAPx bits in the register.

In the errata for the MSP430F2132, it mentions that the MCU sometimes has issues with crystals with an equivalent series resistance (ESR) less than 40 kΩ. The crystals I purchased have 30 kΩ ESR, but seem to work fine. I don't know if my fumbling soldering technique increased the resistance sufficiently or if it just appears to work and isn't perfectly accurate, but it seems close enough for now. Future workarounds are to pick a crystal with a higher ESR or to add a resistor into the circuit as explained in TI's application note about this particular erratum.

Once the crystal is in place, it should be checked to be certain it is oscillating. This post's program won't be able to prove accuracy of the crystal, but it at least shows that the crystal oscillates and can be used. The crystal's frequency is accessed with the auxiliary clock ACLK.

/* 32kcrystalF2132: quick program to test the functionality of a watch crystal
* soldered to the target board. Flashes an LED on P1.0 at 2 Hz.

#include <msp430f2132.h>

#define LED BIT0

void main(void) {
    int i;                      // internal counter
    WDTCTL = WDTPW + WDTHOLD; // turn WDT off
    P1OUT = 0x00;
    P1DIR = LED;
    BCSCTL3 |= XCAP_3; // xtal has 12.5 pF caps
    TACCR0 = 16383; // f_xtal / 2 - 1 gives 2 Hz
    TACTL = TASSEL_1 + MC_1 + TACLR; // ACLK + Up Mode + Clear timer

    for (;;) {
        while ((TACTL & BIT0) == 0) { // wait for timer to reach TACCR0
    TACTL &= ~BIT0;       // reset TAIFG
    P1OUT |= LED;                 // LED on
    for (i=0; i<0x314; i++) { // short delay
    P1OUT &= ~LED; // LED off

} // main

The comments in the code should help explain how the program works, but it's worth taking some time to explain some of the features of Timer A.
  • Any of the timers in an MSP430 can be configured to run off of any of the clocks (MCLK, SMCLK, ACLK) by setting the TASSELx bits in TACTL. TASSEL_1 in the header file configures the timer to run off the auxiliary clock connected to the crystal.
  • The MCx bits set the mode of the timer. These are initialized to 0 when the MCU is powered, keeping the timers turned off and conserving power. MC_1 in the header file configures the timer in "Up Mode", which counts upward until it reaches a value set in TACCR0. MC_2 sets the timer in "Continuous" mode, which counts upward until the counter rolls over (from 0x0000 to 0xFFFF). This code could use continuous mode, but would then only be able to flash the LED at exactly 1 Hz. Using up mode allows you to set the flash to exact values at frequencies higher than 1 Hz and up to 32 kHz.
  • The TACLR bit clears the timer so that it starts at 0 from wherever the bit is set. This is useful because the timer continues to run even when the code is not using it or looking at it. If an exact time is needed, the timer should be cleared to be certain it counts the right number of times before reaching TACCR0.
  • The TAIFG bit (this is bit 0 in TACTL) is set when the timer rolls over, either at the top in continuous mode or at TACCR0 in up mode. The code watches this bit to know when to flash the LED. Note that the flag must be reset manually. The timer continues to run while the code is turning on the LED, waiting briefly (0x314 is just a random value I picked for a short flash), and turning off the LED, so when the loop starts again, the timer has already been running. This feature is very useful for exact timing applications, because we don't need to be concerned about accounting for the processing time between loops. As long as the program takes less time to run than intervals between TAIFG being set, the program will execute at exactly the time we require.
One final note about the value set to TACCR0--Since the timer starts at 0, and 0xFFFF coresponds to 65,535, the timer can only trigger at integer values between these two.  For this crystal, 32,768 counts would correspond to 1 s, so by setting TACCR0 to 32767 we would trigger every second.  The correct way to get the exact timing is to count to f/n - 1 (using whole fractions of the oscillation frequency, i.e. 1/2 s., 1/3 s., 1/4 s., etc as the period between flashes), so 1/3 s would be TACCR0 = (1/3) / 32,768 - 1 = 10,922. As long as this value is less than 65,536, nothing other than setting this register needs to be done.


Anonymous said...

I don't understand your final note, since 0xFFFF is 65535, not 32767?

David said...

Yep, looks like I got that wrong. In my defense, the posts before the ones titled "Tutorial..." are very early on in my learning, and so I reserve the right to be more wrong in those than I do in the tutorials. =)

Thanks for pointing that out... It's been fixed.

HendersonCheeng said...

Hey, Dave. I found this example to be extremely informative. It's the first time I've tried to do anything with the clock system of any uC, and I wasn't precisely sure what I was doing with the BCS registers or the TAIFG. It's just a lot of information to take in, but this example helped to clear my mind!

I do have a small question though. When setting up the TACTL register, there is an option for turning on the interrupt flag (TAIE). Any idea what is meant by that since your code works without turning that on?

Great work on these blogs. Love them!

David said...

HC: Thanks for the comments; the interrupt feature isn't used at all in this code. This simple setup simply polls the flag showing a rollover (TAR goes to zero). If TAIE is enabled, that will also flag an interrupt, requiring a service routine to deal with it. In this example, we simply poll that flag, and so no interrupts are needed. It does mean the MCU is otherwise occupied; an interrupt would let it work on other things while it waits for the timer. See the later tutorials for more info!

Anonymous said...

You might want to correct this final statement.
"TACCR0 = (1/3) / 32,768 - 1 = 10,922"
i think it should be
"TACCR0 = (1/3)* 32,768 - 1 = 10,922"