Fizzbuzz

msp430 - basics

It’s interesting that msp430 has low power mode (LPM) which enables the MCU to get into sleep mode and stay operating. There are 6 profiles for this: 5 low-power modes, and one active mode.

These modes related to which components get disabled, thus lowering power consumption. The components are: CPU, MCLK (Master Clock), SMCLK (Sub-System Master Clock), ACLK (Auxiliary Clock), DCO (Digitally Controlled Oscillator), and internal crystal oscillator. By default, after power-up, DCO is used as the source oscillator for MCLK and SMCLK, while ACLK is sourced from a low-frequency (~32kHz) additional watch-crystal (LFXT1), All the clocks is software selectable, and can use either DCO, additional crystal (LFXT1), or internal low-power oscillator (VLO), but ACLK is the only clock that can not be configured to use DCO as its source oscillator.

Now back to the operating modes, here is the list of profile for msp430

  • Active mode is the normal operation profile, all components is enabled and running.
  • LPM 0 has CPU and MCLK disabled
  • LPM 1 has CPU, MCLK, and DCO disabled. DCO only disabled if it’s not used in active mode.
  • LPM 2 has CPU, MCLK, and SMCLK disabled.
  • LPM 3 has CPU, MCLK, SMCLK, and DCO disabled.
  • LPM 4 has CPU, MCLK, SMCLK, ACLK, DCO, and internal crytal oscillator disabled.

So yes, LPM 4 is the deep sleep mode.

The low-power mode can be set to be interruptible. That is, by setting the interrupt-enable flag together with the LPM profile. We’ll see how to do that in a moment.

The usual first program for microcontroller is the grand LED-blinking code. And to do that naively is to XOR-ing the bit to the pin where the led is mounted, and add some delay/sleep for some cycles so it’s alternating between high and low currents.

#include <msp430.h> 

int main(void) {
    WDTCTL = WDTPW | WDTHOLD;   // Stop watchdog timer
    /* Set P1.0 direction to output */
    P1DIR |= BIT0;

    /* Set P1.0 output high */
    P1OUT |= BIT0;

    while (1) {
        /* Wait for 200000 cycles */
        __delay_cycles(200000);
        /* Toggle P1.0 output */
        P1OUT ^= BIT0;
    }

    return 0;
}

/* Adapted from http://simplyembedded.wordpress.com/tutorials/lesson3/
 */

Now you see the code above would require the MCU to run in active mode all the time, and for MSP430 to living up to its title, we can optimize the code so it can run with less power consumption (shame on me because I haven’t measure the actual power consumption yet). First let’s take a look at the first line inside our main block above. The default watchdog timer was set to do a h/w reset in every 32ms (or the default timer set, since it’s probably different), so we have to hold it, or more like killing it, since we wont have anything to do with the timer.

But hey, it’s a timer. Could we possibly use that as the timer for our blinking led, sure we could. The watchdog can be configured to interrupt the normal execution and move the PC (Program Counter) to the ISR (Interrupt Service Routine), and execute the code over there. This combined with the low-power mode can be used to eliminate the infinite loop above, just like this:

#include <msp430.h> 

int i;

int main(void) {
    /* Set Watchdog timer to interrupt in interval 32ms,
     * clocked from SMCLK. */
    WDTCTL = WDT_MDLY_32;
    IE1 |= WDTIE;

    /* Set P1.0 direction to output */
    P1DIR |= BIT0;

    /* Set P1.0 output high */
    P1OUT |= BIT0;

    i = 0;

    __bis_SR_register(LPM1_bits | GIE);
}


/* Watchdog Timer interrupt service routine */
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(WDT_VECTOR))) watchdog_timer (void)
#else
#error Compiler not supported!
#endif
{
    i++;
    if (i >= 25) {
        P1OUT ^= BIT0;                            /* Toggle P1.0 using exclusive-OR */
        i = 0;
    }
}

For now I wont mind with the extra flag checking over there, but in case you curious, it’s checking for the compiler version and will use the pragma and __interrupt void watchdog_timer(void) for TI or IAR C compiler, and will use a bit longer line void __attribute__ ((interrupt(WDT_VECTOR))) watchdog_timer (void) for gcc. Other than those compiler, throw error.

Now back to our topic. I said I will show you how to set an interruptible low-power mode, and that it is. With some addition, actually, we should add the Watchdog interrupt-enable flag to the register, then proceed to the LPM1 mode while standby for any interrupts registered.

We defined the ISR as watchdog_timer and since 32ms is a bit too fast, we multiply the counter by 25 so it will blinking with interval at roughly 800ms.

Read more