Personally, I found the TI MSP430 Launchpad demo code a little daunting on first read. It’s easier and more readable than a lot of the demo code I’ve found for the MSP430, but I bet there are a lot of people who still find it difficult. I decided I would devote a page to building up the TI demo part by part in such a way that a beginner could watch the it being created.

This code is for the MSP430g2231 chip with the internal temperature sensor.

This page is a companion page for my MSP430 Launchpad page.

1. Startup mode

When the MSP430 powers up, it blinks the LEDs to let the user know that the device is alive. The key functions are PreApplicationMode() which sets up Timer A1 to generate an interrupt every 1/5 second, and t1a_isr() which handles the interrupt and toggles the LEDs. The chip spends almost all of its time sleeping in low power mode 3 (which is entered right at the end of PreApplicationMode()).

#include  <msp430x20x2.h>

#define     RED_LED                  BIT0
#define     GRN_LED                  BIT6

#define     APP_STANDBY_MODE      0
#define     APP_APPLICATION_MODE  1

unsigned char applicationMode = APP_STANDBY_MODE;

/* the mode the chip initially enters to blink the LEDs */
void PreApplicationMode(void) {
    /* Set both LED pins as output pins and turn red on, green off */
    P1DIR |= RED_LED + GRN_LED;
    P1OUT |= RED_LED;
    P1OUT &= ~GRN_LED;

    /* The basic clock system control register 3 controls the oscillator
     * the auxilliary clock uses.  Change ACLK from external timer crystal
     * oscillator to internal very low-power oscillator (VLO) clock,
     * which runs at appoximately 12kHz.
     */
    BCSCTL3 |= LFXT1S_2;

    /* Basic clock system control register 1 controls the auxilliary clock
     * divider.  Set ACLK to use Divider 1, i.e. divided by 2^1. */
    BCSCTL1 |= DIVA_1;

    /* Set TimerA counter period to 1200 or about 1/5 second */
    TACCR0 = 1200;
    /* Set TimerA to use auxilliary clock TASSEL_1 and count up mode MC_1 */
    TACTL = TASSEL_1 | MC_1;
    /* Set capture/compare mode interrupts enabled, to trigger an interrupt
     * when TACCR1 is reached. OUTMOD_3 not essential here? */
    TACCTL1 = CCIE + OUTMOD_3;
    /* The value at which TimerA1 interrupt triggers. */
    TACCR1 = 600;

    /* Go to sleep in low power mode 3 with interrupts enabled. */
    __bis_SR_register(LPM3_bits + GIE);
}

/* Timer A interrupt service routine 1.  The function prototype
 * tells the compiler that this will service the Timer A1 interrupt,
 * and then the function follows.
 */
void ta1_isr(void) __attribute__((interrupt(TIMERA1_VECTOR)));
void ta1_isr(void)
{
    /* clear the interrupt flag */
    TACCTL1 &= ~CCIFG;

    /* in application mode, we'll turn off the LEDs, otherwise we'll
     * toggle them.
     */
    if (applicationMode == APP_APPLICATION_MODE)
        P1OUT &= ~(RED_LED + GRN_LED);
    else
        P1OUT ^= (RED_LED + GRN_LED);

}

/************** main program begins here ***************/
void main( void ) {
    /* access the watchdog timer control without password and hold
     * the count (so the watchdog timer doesn't reset the chip)
     */
    WDTCTL = WDTPW + WDTHOLD;

    PreApplicationMode();
}

2. Entering Application Mode

This version adds a way to get into the application mode of the program by pressing switch 2. Because of the check in ta1_isr() this will cause the LEDs to go dark. The key new functions are InitializeButton() which configures the button to trigger interrupts, PORT1_ISR() which is triggered by button presses, and WDT_ISR() which is used to handle button bounce in software.

Debouncing is handled in a very clever way. When the button interrupt is triggered, that service handler disables future button interrupts to prevent spurious extra "bounce presses." It then sets the Watchdog timer to wait one quarter second and execute the timer Watchdog interrupt. The Watchdog interrupt service handler then turns the button interrupts back on.

The neat thing is that the program doesn’t have to stop or delay for any of this to work. In effect we just schedule the button to start working again 681 milliseconds in the future and an interrupt will fire automatically when the time comes to make that happen.

#include  <msp430x20x2.h>

#define     RED_LED                  BIT0
#define     GRN_LED                  BIT6

#define     BUTTON                   BIT3

#define     APP_STANDBY_MODE      0
#define     APP_APPLICATION_MODE  1

unsigned char applicationMode = APP_STANDBY_MODE;

/* the mode the chip initially enters to blink the LEDs */
void PreApplicationMode(void) {
    /* Set both LED pins as output pins and turn red on, green off */
    P1DIR |= RED_LED + GRN_LED;
    P1OUT |= RED_LED;
    P1OUT &= ~GRN_LED;

    /* The basic clock system control register 3 controls the oscillator
     * the auxilliary clock uses.  Change ACLK from external timer crystal
     * oscillator to internal very low-power oscillator (VLO) clock,
     * which runs at appoximately 12kHz.
     */
    BCSCTL3 |= LFXT1S_2;

    /* Basic clock system control register 1 controls the auxilliary clock
     * divider.  Set ACLK to use Divider 1, i.e. divided by 2^1. */
    BCSCTL1 |= DIVA_1;

    /* Set TimerA counter period to 1200 or about 1/5 second */
    TACCR0 = 1200;
    /* Set TimerA to use auxilliary clock TASSEL_1 and count up mode MC_1 */
    TACTL = TASSEL_1 | MC_1;
    /* Set capture/compare mode interrupts enabled, to trigger an interrupt
     * when TACCR1 is reached. OUTMOD_3 not essential here? */
    TACCTL1 = CCIE + OUTMOD_3;
    /* The value at which TimerA1 interrupt triggers. */
    TACCR1 = 600;

    /* Go to sleep in low power mode 3 with interrupts enabled. */
    __bis_SR_register(LPM3_bits + GIE);
}

/* Timer A interrupt service routine 1.  The function prototype
 * tells the compiler that this will service the Timer A1 interrupt,
 * and then the function follows.
 */
void ta1_isr(void) __attribute__((interrupt(TIMERA1_VECTOR)));
void ta1_isr(void)
{
    /* clear the interrupt flag */
    TACCTL1 &= ~CCIFG;

    /* in application mode, we'll turn off the LEDs, otherwise we'll
     * toggle them.
     */
    if (applicationMode == APP_APPLICATION_MODE)
        P1OUT &= ~(RED_LED + GRN_LED);
    else
        P1OUT ^= (RED_LED + GRN_LED);

}

/* This function configures the button so it will trigger interrupts
 * when pressed.  Those interrupts will be handled by PORT1_ISR() */
void InitializeButton(void) {
    /* Set button pin as an input pin */
    P1DIR &= ~BUTTON;
    /* set pull up resistor on for button */
    P1OUT |= BUTTON;
    /* enable pull up resistor for button to keep pin high until pressed */
    P1REN |= BUTTON;
    /* Interrupt should trigger from high (unpressed) to low (pressed) */
    P1IES |= BUTTON;
    /* Clear the interrupt flag for the button */
    P1IFG &= ~BUTTON;
    /* Enable interrupts on port 1 for the button */
    P1IE |= BUTTON;
}

/* *************************************************************
 * Port Interrupt for Button Press
 * 1. During standby mode: to exit and enter application mode
 * 2. During application mode: to recalibrate temp sensor
 * *********************************************************** */
void PORT1_ISR(void) __attribute__((interrupt(PORT1_VECTOR)));
void PORT1_ISR(void)
{
    /* clear interrupt flag for port 1 */
    P1IFG = 0;
    /* disable interrupts for the button to handle button bounce */
    P1IE &= ~BUTTON;
    /* set watchdog timer to trigger every 681 milliseconds -- normally
     * this would be 250 ms, but the VLO is slower
     */
    WDTCTL = WDT_ADLY_250;
    /* clear watchdog timer interrupt flag */
    IFG1 &= ~WDTIFG;
    /* enable watchdog timer interrupts; in 681 ms the button
     * will be re-enabled by WDT_ISR() -- program will continue in
     * the meantime.
     */
    IE1 |= WDTIE;

    if (applicationMode == APP_APPLICATION_MODE) {
        // tempCalibrated = tempAverage;
        // calibrateUpdate  = 1;
    } else {
        /* switch to APPLICATION MODE */
        applicationMode = APP_APPLICATION_MODE;
        /* clear the low power mode bit, so when we return from
         * the interrupt call, the program will resume running
         */
        __bic_SR_register_on_exit(LPM3_bits);
    }
}

/* This function catches watchdog timer interrupts, which are
 * set to happen 681ms after the user presses the button.  The
 * button has had time to "bounce" and we can turn the button
 * interrupts back on.
 */
void WDT_ISR(void) __attribute__((interrupt(WDT_VECTOR)));
void WDT_ISR(void)
{
    /* Disable interrupts on the watchdog timer */
    IE1 &= ~WDTIE;
    /* clear the interrupt flag for watchdog timer */
    IFG1 &= ~WDTIFG;
    /* resume holding the watchdog timer so it doesn't reset the chip */
    WDTCTL = WDTPW + WDTHOLD;
    /* and re-enable interrupts for the button */
    P1IE |= BUTTON;
}


/************** main program begins here ***************/
void main( void ) {
    /* access the watchdog timer control without password and hold
     * the count (so the watchdog timer doesn't reset the chip)
     */
    WDTCTL = WDTPW + WDTHOLD;

    InitializeButton();
    PreApplicationMode();
}

3. Communication With the Computer

The MSP430 doesn’t just blink lights in this demo. It also communicates the internal chip temperature back to the computer. We haven’t yet written any code to measure temperature, but we can frame in the code for communicating with the computer.

After the clock oscillators are calibrated with InitializeClocks(), communication is handled by three functions: ConfigureTimerUart() which setups up TimerA0, Transmit() which queues up a particular transmission, and Timer_A() which actually does the communication.

#include  <msp430x20x2.h>

#define     RED_LED                  BIT0
#define     GRN_LED                  BIT6
#define     TXD                      BIT1
#define     RXD                      BIT2


#define     BUTTON                   BIT3

#define     APP_STANDBY_MODE      0
#define     APP_APPLICATION_MODE  1

#define     TIMER_PWM_MODE        0
#define     TIMER_UART_MODE       1
#define     TIMER_PWM_PERIOD      2000
#define     TIMER_PWM_OFFSET      20

/* Conditions for 2400 Baud SW UART, SMCLK = 1MHz, divider 8
 *  (1 Mhz/8) / 2400 bps = 52 clock cycles per bit
 */
#define     Bitime_5              20    // ~ clock ticks per half bit
#define     Bitime                52    // clock ticks per bit
#define     UART_UPDATE_INTERVAL  1000

unsigned char BitCnt;
unsigned int TXByte;

unsigned char applicationMode = APP_STANDBY_MODE;

unsigned char timerMode = TIMER_PWM_MODE;


/* the mode the chip initially enters to blink the LEDs */
void PreApplicationMode(void) {
    /* Set both LED pins as output pins and turn red on, green off */
    P1DIR |= RED_LED + GRN_LED;
    P1OUT |= RED_LED;
    P1OUT &= ~GRN_LED;

    /* The basic clock system control register 3 controls the oscillator
     * the auxilliary clock uses.  Change ACLK from external timer crystal
     * oscillator to internal very low-power oscillator (VLO) clock,
     * which runs at appoximately 12kHz.
     */
    BCSCTL3 |= LFXT1S_2;

    /* Basic clock system control register 1 controls the auxilliary clock
     * divider.  Set ACLK to use Divider 1, i.e. divided by 2^1. */
    BCSCTL1 |= DIVA_1;

    /* Set TimerA counter period to 1200 or about 1/5 second */
    TACCR0 = 1200;
    /* Set TimerA to use auxilliary clock TASSEL_1 and count up mode MC_1 */
    TACTL = TASSEL_1 | MC_1;
    /* Set capture/compare mode interrupts enabled, to trigger an interrupt
     * when TACCR1 is reached. OUTMOD_3 not essential here? */
    TACCTL1 = CCIE + OUTMOD_3;
    /* The value at which TimerA1 interrupt triggers. */
    TACCR1 = 600;

    /* Go to sleep in low power mode 3 with interrupts enabled. */
    __bis_SR_register(LPM3_bits + GIE);
}

void InitializeClocks(void) {
    /* Use calibration values to set digitally controlled oscillator
     * to 1 mhz, and set the submain clock to the same (i.e. we turn
     * off the /8 divider bit).
     */
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
    BCSCTL2 &= ~(DIVS_3);
}

void ConfigureTimerPwm(void) {
    timerMode = TIMER_PWM_MODE;

    TACCR0 = TIMER_PWM_PERIOD;                              //
    TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
    TACCTL0 = CCIE;
    TACCTL1 = CCIE + OUTMOD_3;                // TACCTL1 Capture Compare
    TACCR1 = 1;
}

void ConfigureTimerUart(void) {
    /* This flag tells us that we're using the timer for communcation
     * rather than pulse width modulation.
     */
    timerMode = TIMER_UART_MODE;

    /* Set transmission pin high for now (until we later set it low
     * to start communication). */
    CCTL0 = OUT;

    /* TimerA is using submain clock (at 1mhz), in continuous mode,
     * with a clock divider of 2^3=8
     */
    TACTL = TASSEL_2 + MC_2 + ID_3;

    /* Set transmit and receive pins latched to the timer, and the
     * transmit pin is set as an output pin.
     */
    P1SEL |= TXD + RXD;
    P1DIR |= TXD;
}

void Transmit() {
    /* Function Transmits Character from TXByte.  We'll have 10 bits total
     * to communicate, 8 bits of data plus a start bit (zero) and a
     * stop bit (one).
     */
    BitCnt = 10;

    /* Make sure we don't catch the TAR register while it is changing.
     * As long as the difference is only in the lower bits, we'll call
     * it close enough.
     */
    do {
        CCR0 = TAR;
    } while( (CCR0^TAR) > 3 );

    /* Set time when the transmission will actually begin */
    CCR0 += Bitime;
    /* Add a one bit to act as stop bit (least significant bits in the TXByte get sent first) */
    TXByte |= 0x100;
    /* Shift left to make an initial zero bit to act as start bit */
    TXByte = TXByte << 1;

    /* Timer A0 to start triggering interrupts to do the actual sending */
    CCTL0 =  CCIS0 + OUTMOD0 + CCIE;

    /* We'll loop and wait for the transmission to finish */
    while ( CCTL0 & CCIE ) ;
}

// Timer A0 interrupt service routine
void Timer_A(void) __attribute__((interrupt(TIMERA0_VECTOR)));
void Timer_A(void) {
    if (timerMode == TIMER_UART_MODE) {
        /* schedule when the next bit is communicated */
        CCR0 += Bitime;

        /* If the transmission bit is set */
        if (CCTL0 & CCIS0) {
            /* Are there bits left to be transmitted? */
            if ( BitCnt == 0) {
                /* No, disable the interrupt.  We're done. */
                CCTL0 &= ~ CCIE;
            } else {
                /* If the next bit is a one, set pin high/mark */
                if (TXByte & 0x01) {
                    CCTL0 &= ~ OUTMOD2;
                } else {/* Otherwise set pin low/space */
                    CCTL0 |=  OUTMOD2;
                }

                /* Shift right to drop the bit we just sent, and update the count. */
                TXByte = TXByte >> 1;
                BitCnt --;
            }
        }
    } else {
//      if (tempPolarity == TEMP_HOT)
//          LED_OUT |= LED1;
//      if (tempPolarity == TEMP_COLD)
//          LED_OUT |= LED0;

    }
    /* Clear the interrupt flag */
    TACCTL0 &= ~CCIFG;
}


/* Timer A interrupt service routine 1.  The function prototype
 * tells the compiler that this will service the Timer A1 interrupt,
 * and then the function follows.
 */
void ta1_isr(void) __attribute__((interrupt(TIMERA1_VECTOR)));
void ta1_isr(void)
{
    /* clear the interrupt flag */
    TACCTL1 &= ~CCIFG;

    /* in application mode, we'll turn off the LEDs, otherwise we'll
     * toggle them.
     */
    if (applicationMode == APP_APPLICATION_MODE) {
        P1OUT &= ~(RED_LED + GRN_LED);
    } else {
        P1OUT ^= (RED_LED + GRN_LED);
    }
}

/* This function configures the button so it will trigger interrupts
 * when pressed.  Those interrupts will be handled by PORT1_ISR() */
void InitializeButton(void) {
    /* Set button pin as an input pin */
    P1DIR &= ~BUTTON;
    /* set pull up resistor on for button */
    P1OUT |= BUTTON;
    /* enable pull up resistor for button to keep pin high until pressed */
    P1REN |= BUTTON;
    /* Interrupt should trigger from high (unpressed) to low (pressed) */
    P1IES |= BUTTON;
    /* Clear the interrupt flag for the button */
    P1IFG &= ~BUTTON;
    /* Enable interrupts on port 1 for the button */
    P1IE |= BUTTON;
}

/* *************************************************************
 * Port Interrupt for Button Press
 * 1. During standby mode: to exit and enter application mode
 * 2. During application mode: to recalibrate temp sensor
 * *********************************************************** */
void PORT1_ISR(void) __attribute__((interrupt(PORT1_VECTOR)));
void PORT1_ISR(void)
{
    /* clear interrupt flag for port 1 */
    P1IFG = 0;
    /* disable interrupts for the button to handle button bounce */
    P1IE &= ~BUTTON;
    /* set watchdog timer to trigger every 681 milliseconds  -- normally
     * this would be 250 ms, but the VLO is slower
     */
    WDTCTL = WDT_ADLY_250;
    /* clear watchdog timer interrupt flag */
    IFG1 &= ~WDTIFG;
    /* enable watchdog timer interrupts; in 681 ms the button
     * will be re-enabled by WDT_ISR() -- program will continue in
     * the meantime.
     */
    IE1 |= WDTIE;

    if (applicationMode == APP_APPLICATION_MODE) {
        // tempCalibrated = tempAverage;
        // calibrateUpdate  = 1;
    } else {
        /* switch to APPLICATION MODE */
        applicationMode = APP_APPLICATION_MODE;
        /* clear the low power mode bit, so when we return from
         * the interrupt call, the program will resume running
         */
        __bic_SR_register_on_exit(LPM3_bits);
    }
}

/* This function catches watchdog timer interrupts, which are
 * set to happen 681ms after the user presses the button.  The
 * button has had time to "bounce" and we can turn the button
 * interrupts back on.
 */
void WDT_ISR(void) __attribute__((interrupt(WDT_VECTOR)));
void WDT_ISR(void)
{
    /* Disable interrupts on the watchdog timer */
    IE1 &= ~WDTIE;
    /* clear the interrupt flag for watchdog timer */
    IFG1 &= ~WDTIFG;
    /* resume holding the watchdog timer so it doesn't reset the chip */
    WDTCTL = WDTPW + WDTHOLD;
    /* and re-enable interrupts for the button */
    P1IE |= BUTTON;
}


/************** main program begins here ***************/
void main( void ) {
    /* access the watchdog timer control without password and hold
     * the count (so the watchdog timer doesn't reset the chip)
     */
    WDTCTL = WDTPW + WDTHOLD;

    InitializeClocks();
    InitializeButton();
    PreApplicationMode();

    /* application mode begins */
    applicationMode = APP_APPLICATION_MODE;

    ConfigureTimerPwm();

    for( ; ; ) {
        ConfigureTimerUart();
        TXByte = '.';           // for testing
        Transmit();
    }
}

To use this program, I typed:

$ mspdebug rf2500 exit ; minicom -b 2400 -o -D /dev/ttyACM0

Pressing Switch 2 shows me a series of dots, i.e. period characters. The reset button on the Launchpad stops it.

4. Internal temperature sensor

This code contains two new functions, ConfigureAdcTempSensor() and ADC10_ISR(). The first function configures the analogue to digital converter to use the internal temperature sensor and takes a reading. It’s important to know that ADC conversions are not instantaneous. Effectively, you have to schedule a temperature reading and then wait for it to occur. That’s what the ADC10_ISR() function is for. It responds to the interrupt that occurs when a new value is ready and wakes up the main program.

Eventually, the program will keep a history of temperature values so it can compute a moving average, but for now we just read in a single value and use our Transmit() code to send it back as a byte to the PC. If your room is a reasonable temperature (at least 65F) you should see upper case letters.

#include  <msp430x20x2.h>

#define     RED_LED                  BIT0
#define     GRN_LED                  BIT6
#define     TXD                      BIT1
#define     RXD                      BIT2


#define     BUTTON                   BIT3

#define     APP_STANDBY_MODE      0
#define     APP_APPLICATION_MODE  1

#define     TIMER_PWM_MODE        0
#define     TIMER_UART_MODE       1
#define     TIMER_PWM_PERIOD      2000
#define     TIMER_PWM_OFFSET      20

/* Conditions for 2400 Baud SW UART, SMCLK = 1MHz, divider 8
 *  (1 Mhz/8) / 2400 bps = 52 clock cycles per bit
 */
#define     Bitime_5              20    // ~ clock ticks per half bit
#define     Bitime                52    // clock ticks per bit
#define     UART_UPDATE_INTERVAL  1000

unsigned char BitCnt;
unsigned int TXByte;

/* We'll keep a history of 8 temperature measurements from the ADC
 * and calculate a moving average.
 */
long tempMeasured[8];
unsigned char tempMeasuredPosition=0;
long tempAverage;

long tempCalibrated, tempDifference;

unsigned char applicationMode = APP_STANDBY_MODE;

unsigned char timerMode = TIMER_PWM_MODE;


/* the mode the chip initially enters to blink the LEDs */
void PreApplicationMode(void) {
    /* Set both LED pins as output pins and turn red on, green off */
    P1DIR |= RED_LED + GRN_LED;
    P1OUT |= RED_LED;
    P1OUT &= ~GRN_LED;

    /* The basic clock system control register 3 controls the oscillator
     * the auxilliary clock uses.  Change ACLK from external timer crystal
     * oscillator to internal very low-power oscillator (VLO) clock,
     * which runs at appoximately 12kHz.
     */
    BCSCTL3 |= LFXT1S_2;

    /* Basic clock system control register 1 controls the auxilliary clock
     * divider.  Set ACLK to use Divider 1, i.e. divided by 2^1. */
    BCSCTL1 |= DIVA_1;

    /* Set TimerA counter period to 1200 or about 1/5 second */
    TACCR0 = 1200;
    /* Set TimerA to use auxilliary clock TASSEL_1 and count up mode MC_1 */
    TACTL = TASSEL_1 | MC_1;
    /* Set capture/compare mode interrupts enabled, to trigger an interrupt
     * when TACCR1 is reached. OUTMOD_3 not essential here? */
    TACCTL1 = CCIE + OUTMOD_3;
    /* The value at which TimerA1 interrupt triggers. */
    TACCR1 = 600;

    /* Go to sleep in low power mode 3 with interrupts enabled. */
    __bis_SR_register(LPM3_bits + GIE);
}

void ConfigureAdcTempSensor(void) {
    volatile unsigned int i;
    /* Configure ADC input channel 10 (i.e. the temperature sensor)
     * with clock divider /4
     */
    ADC10CTL1 = INCH_10 + ADC10DIV_3;

    /* not sure what these mean */
    ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;

    /* A delay loop for ADC reference to settle */
    for( i = 0; i < 500; i++ ) ;

    /* Enable ADC conversion and set it to start conversion */
    ADC10CTL0 |= ENC + ADC10SC;
    /* Sleep in LPM0 until the conversion is ready */
    __bis_SR_register(LPM0_bits + GIE);
    /* Read off the temperature raw value */
    tempCalibrated = ADC10MEM;

    /* We'll keep a history of temperatures to compute a moving average,
     * but for now they're all initialized to this starting temp.  So
     * is the average temperature.
     */
    for (i=0; i < 8; i++)
        tempMeasured[i] = tempCalibrated;
    tempAverage = tempCalibrated;
}


void InitializeClocks(void) {
    /* Use calibration values to set digitally controlled oscillator
     * to 1 mhz, and set the submain clock to the same (i.e. we turn
     * off the /8 divider bit).
     */
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
    BCSCTL2 &= ~(DIVS_3);
}

void ConfigureTimerPwm(void) {
    timerMode = TIMER_PWM_MODE;

    TACCR0 = TIMER_PWM_PERIOD;                              //
    TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
    TACCTL0 = CCIE;
    TACCTL1 = CCIE + OUTMOD_3;                // TACCTL1 Capture Compare
    TACCR1 = 1;
}

void ConfigureTimerUart(void) {
    /* This flag tells us that we're using the timer for communcation
     * rather than pulse width modulation.
     */
    timerMode = TIMER_UART_MODE;

    /* Set transmission pin high for now (until we later set it low
     * to start communication). */
    CCTL0 = OUT;

    /* TimerA is using submain clock (at 1mhz), in continuous mode,
     * with a clock divider of 2^3=8
     */
    TACTL = TASSEL_2 + MC_2 + ID_3;

    /* Set transmit and receive pins latched to the timer, and the
     * transmit pin is set as an output pin.
     */
    P1SEL |= TXD + RXD;
    P1DIR |= TXD;
}

void Transmit() {
    /* Function Transmits Character from TXByte.  We'll have 10 bits total
     * to communicate, 8 bits of data plus a start bit (zero) and a
     * stop bit (one).
     */
    BitCnt = 10;

    /* Make sure we don't catch the TAR register while it is changing.
     * As long as the difference is only in the lower bits, we'll call
     * it close enough.
     */
    do {
        CCR0 = TAR;
    } while( (CCR0^TAR) > 3 );

    /* Set time when the transmission will actually begin */
    CCR0 += Bitime;
    /* Add a one bit to act as stop bit (least significant bits in the TXByte get sent first) */
    TXByte |= 0x100;
    /* Shift left to make an initial zero bit to act as start bit */
    TXByte = TXByte << 1;

    /* Timer A0 to start triggering interrupts to do the actual sending */
    CCTL0 =  CCIS0 + OUTMOD0 + CCIE;

    /* We'll loop and wait for the transmission to finish */
    while ( CCTL0 & CCIE ) ;
}

// Timer A0 interrupt service routine
void Timer_A(void) __attribute__((interrupt(TIMERA0_VECTOR)));
void Timer_A(void) {
    if (timerMode == TIMER_UART_MODE) {
        /* schedule when the next bit is communicated */
        CCR0 += Bitime;

        /* If the transmission bit is set */
        if (CCTL0 & CCIS0) {
            /* Are there bits left to be transmitted? */
            if ( BitCnt == 0) {
                /* No, disable the interrupt.  We're done. */
                CCTL0 &= ~ CCIE;
            } else {
                /* If the next bit is a one, set pin high/mark */
                if (TXByte & 0x01) {
                    CCTL0 &= ~ OUTMOD2;
                } else {/* Otherwise set pin low/space */
                    CCTL0 |=  OUTMOD2;
                }

                /* Shift right to drop the bit we just sent, and update the count. */
                TXByte = TXByte >> 1;
                BitCnt --;
            }
        }
    } else {
//      if (tempPolarity == TEMP_HOT)
//          LED_OUT |= LED1;
//      if (tempPolarity == TEMP_COLD)
//          LED_OUT |= LED0;

    }
    /* Clear the interrupt flag */
    TACCTL0 &= ~CCIFG;
}


/* Timer A interrupt service routine 1.  The function prototype
 * tells the compiler that this will service the Timer A1 interrupt,
 * and then the function follows.
 */
void ta1_isr(void) __attribute__((interrupt(TIMERA1_VECTOR)));
void ta1_isr(void)
{
    /* clear the interrupt flag */
    TACCTL1 &= ~CCIFG;

    /* in application mode, we'll turn off the LEDs, otherwise we'll
     * toggle them.
     */
    if (applicationMode == APP_APPLICATION_MODE) {
        P1OUT &= ~(RED_LED + GRN_LED);
    } else {
        P1OUT ^= (RED_LED + GRN_LED);
    }
}

/* This function configures the button so it will trigger interrupts
 * when pressed.  Those interrupts will be handled by PORT1_ISR() */
void InitializeButton(void) {
    /* Set button pin as an input pin */
    P1DIR &= ~BUTTON;
    /* set pull up resistor on for button */
    P1OUT |= BUTTON;
    /* enable pull up resistor for button to keep pin high until pressed */
    P1REN |= BUTTON;
    /* Interrupt should trigger from high (unpressed) to low (pressed) */
    P1IES |= BUTTON;
    /* Clear the interrupt flag for the button */
    P1IFG &= ~BUTTON;
    /* Enable interrupts on port 1 for the button */
    P1IE |= BUTTON;
}

/* *************************************************************
 * Port Interrupt for Button Press
 * 1. During standby mode: to exit and enter application mode
 * 2. During application mode: to recalibrate temp sensor
 * *********************************************************** */
void PORT1_ISR(void) __attribute__((interrupt(PORT1_VECTOR)));
void PORT1_ISR(void)
{
    /* clear interrupt flag for port 1 */
    P1IFG = 0;
    /* disable interrupts for the button to handle button bounce */
    P1IE &= ~BUTTON;
    /* set watchdog timer to trigger every 681 milliseconds  -- normally
     * this would be 250 ms, but the VLO is slower
     */
    WDTCTL = WDT_ADLY_250;
    /* clear watchdog timer interrupt flag */
    IFG1 &= ~WDTIFG;
    /* enable watchdog timer interrupts; in 681 ms the button
     * will be re-enabled by WDT_ISR() -- program will continue in
     * the meantime.
     */
    IE1 |= WDTIE;

    if (applicationMode == APP_APPLICATION_MODE) {
        // tempCalibrated = tempAverage;
        // calibrateUpdate  = 1;
    } else {
        /* switch to APPLICATION MODE */
        applicationMode = APP_APPLICATION_MODE;
        /* clear the low power mode bit, so when we return from
         * the interrupt call, the program will resume running
         */
        __bic_SR_register_on_exit(LPM3_bits);
    }
}

/* This function catches watchdog timer interrupts, which are
 * set to happen 681ms after the user presses the button.  The
 * button has had time to "bounce" and we can turn the button
 * interrupts back on.
 */
void WDT_ISR(void) __attribute__((interrupt(WDT_VECTOR)));
void WDT_ISR(void)
{
    /* Disable interrupts on the watchdog timer */
    IE1 &= ~WDTIE;
    /* clear the interrupt flag for watchdog timer */
    IFG1 &= ~WDTIFG;
    /* resume holding the watchdog timer so it doesn't reset the chip */
    WDTCTL = WDTPW + WDTHOLD;
    /* and re-enable interrupts for the button */
    P1IE |= BUTTON;
}

/* This function catches interrupts from the analogue to digital
 * controller (ADC), which fire after a temperature has been sampled.
 * Each time we sample, we sleep until ready, so all this routine
 * has to do is wake the program back up.
 */
void ADC10_ISR(void) __attribute__((interrupt(ADC10_VECTOR)));
void ADC10_ISR(void)
{
    __bic_SR_register_on_exit(LPM0_bits);
}

/************** main program begins here ***************/
void main( void ) {
    /* access the watchdog timer control without password and hold
     * the count (so the watchdog timer doesn't reset the chip)
     */
    WDTCTL = WDTPW + WDTHOLD;

    InitializeClocks();
    InitializeButton();
    PreApplicationMode();

    /* application mode begins */
    applicationMode = APP_APPLICATION_MODE;
    ConfigureAdcTempSensor();

    ConfigureTimerPwm();

    for( ; ; ) {
        ConfigureTimerUart();

        /* This conversion formula is magic! */
        TXByte = (unsigned char)( ((tempAverage - 630) * 761) / 1024 );

        Transmit();
    }
}

To use this program, I typed:

$ mspdebug rf2500 exit ; minicom -b 2400 -o -D /dev/ttyACM0

Pressing Switch 2 shows me a series of C characters because my room is 67F. The reset button on the Launchpad stops it.

Return to MSP430 Launchpad page.