MSP430 Interval Timer

// Written by Todd Doucet.  doucet@lambentresearch.com

#include <io.h>
#include <signal.h>
#include <iomacros.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "fll.h"



void sensor_setup()
{
    P1DIR = ~BIT1;  // Configure P1.1 (pin 13) as an input, rest as outputs.
    P1SEL = BIT1;   // That pin selected selected for timer_A.

    P2DIR = ~BIT2;  // Configure P2.2 (pin 22) as an input, rest as outputs.
    P2SEL = BIT2;   // That pin selected selected for timer_A.

    TACTL = TASSEL_2 + MC_2;   // timer_A: SMCLK + Continuous Mode

/*
    P2SEL = BIT0;   // So I can measure the watch xtal freq conveniently.
    P2DIR = BIT0;   
    BCSCTL1 &= ~(BIT4 | BIT5);  // Don't divide xtal for output at P2.0.
*/

}

/* We use the built-in uart for sending and receiving characters.
 * On the MS430F149, serial out is pin 32, and serial in is pin 33.
 */

void usart0_setup()
{
    UCTL0 = SWRST;  // reset the USART
    U0ME |= UTXE0 + URXE0;  // enable USART0 module

      // For usart0, we use P3.4 (pin 32) for transmit, and
      // P3.5 (pin 33) for receive.
    P3SEL |= BV(4) + BV(5); // set Port 3 pins for USART0

    UCTL0 |= CHAR;      // 8 bits, no parity, 1 stop
      // select the baudrate generator clock
    UTCTL0 |= SSEL1 + SSEL0; 
    UMCTL0 = 0;         // no modulation
    UBR10 = 0;
    UBR00 = 0xa0;       // 9600: divide by 160 for our freq
    URCTL0 = 0;         // init receiver contol register
    UCTL0 &= ~SWRST;    // enable usart
    U0IE |= URXIE0;     // enable receiver interrupts
}


    // Allow time for the watch crystal to become stable at startup.
void xtal_warmup() {
   volatile int i;  // Don't optimize away.
   for (i = 0; i < 30000; i++){
      nop();
   }
}


void hardware_setup()
{
    WDTCTL = WDTPW | WDTHOLD;   // Disable watchdog timer.

    xtal_warmup();


        // Set the frequency-locked loop to  the desired frequency,
        // using the 32k768 crystal as reference.
    fllInit();

    usart0_setup();
    sensor_setup();
    eint();         //enable interrupts
}

void serial_putc(char c)
{
    while ( !(IFG1 & UTXIFG0) )  // wait for TX buffer to empty
        continue;
    TXBUF0 = c;
}


#define putchar            serial_putc
#define printf(args...) uprintf(putchar, args)



   // Ring buffer for serial input.
#define CHARBUFSIZE 32
volatile char charbuf[CHARBUFSIZE];
volatile int firstch, nextch;


    // Called by isr to enqueue a character.
void recv_char(char rxdata)
{
   charbuf[nextch] = rxdata;
   nextch = (nextch + 1) % CHARBUFSIZE;
}


interrupt (UART0RX_VECTOR) wakeup usart0_recv_vec(void)
{
  volatile char dummy;

  if (URCTL0 & RXERR) {  // check status register for receive errors
      dummy = RXBUF0;    // clear error flags by forcing a dummy read
      return;
    }
    recv_char(RXBUF0);
}


char getchar()
{
    char r;

    while(1){
        if (nextch != firstch) {
            r = charbuf[firstch];
            firstch = (firstch + 1) % CHARBUFSIZE;
            return r;
        }

        LPM0;   // Sleep.  Awakened by irq.
    }
}

int is_eol(int c)
{
    return (c == '\r') || (c == '\n') ;
}



    // Get a line of input, with simple line editing.
void gets(char *buf, int bufsize)
{
    int pos = 0;
    char c;

    while ( !is_eol(c = getchar())  ){
        if ( (c == '\b') || (c == 127) ){
            if (pos > 0){
                pos--;
                putchar('\b');  // go back
                putchar(' ');   // erase on screen
                putchar('\b');  // go back              
            }
        } else if (pos + 1 < bufsize){
            putchar(c);       // echo it
            buf[pos++] = c;
        }
    }
    buf[pos] = 0;
    putchar('\r');  // echo crlf
    putchar('\n');  
    return;
}



    // Convert time ticks to microseconds, avoiding overflow.
int ticks_to_microsecs(int ticks)
{
    long int r = ticks;
    r *= 125;
    r /= 192;
    return r;
}


volatile int tstamp;  // Set by timerA_vec;

interrupt (TIMERA0_VECTOR) wakeup timerA_vec(void)
{
    tstamp = TACCR0;  // The timer_A clock value at the time of the capture.
}

/*
 * Wait for a positive edge transition on the specified input pin, then
 * return the timer counter that was present when that event occurred.
 */

int edge_trigger(int source)
{
            // rising edge, capture mode, interrupt
    TACCTL0 = CM_1 + SCS + source + CAP + CCIE; 
    LPM0;               // Sleep.  Awakened by edge transition interrupt.
    TACCTL0 &= ~CCIE;   // Disable further interrupts.

    return tstamp;
}

int edgeA_timestamp()
{
    return edge_trigger(CCIS_0);        // CCI0A (P1.1, pin 13)
}

int edgeB_timestamp()
{
    return edge_trigger(CCIS_1);        // CCI0B (P2.2, pin 22)
}

/*
 * Measure the time elapsed during an arbitrary interval.  The
 * functions func1() and func2() return the timestamp of the desired
 * event, and are supposed to use timer capture, like the above.  This
 * allows accurate timings and clear, nonduplicated code.
 */

int interval_time( int (*func1)(), int (*func2)() )
{
    int bgn, end;

    U0IE &= ~URXIE0;    // Disable uart interrupts during measurment.

    TAR = 0;  // So the timestamps do not overflow.
    bgn = (*func1)();
    end = (*func2)();

    U0IE |= URXIE0;     // Re-enable uart interrupts.

    return end - bgn;
}


// The implementations of the various user commands.

void cmd_time()
{
    int period = interval_time(&edgeA_timestamp, &edgeA_timestamp);
    printf("  period of A: %d ticks, %d microsecs\r\n", period, ticks_to_microsecs(period));
}


void cmd_timeAB()
{
    int elapsed = interval_time(&edgeA_timestamp, &edgeB_timestamp);
    printf("  A/B interval: %d ticks, %d microsecs\r\n", elapsed, ticks_to_microsecs(elapsed));
}


void cmd_freq()
{
    int ticks = interval_time(&edgeA_timestamp, &edgeA_timestamp);    
    int period = ticks_to_microsecs(ticks);
    long f = 1000000L / period;
    printf ("  frequency of A: %ld Hz\r\n", f);
}

void cmd_bullet()
{
    int elapsed = interval_time(&edgeA_timestamp, &edgeB_timestamp);
    int period = ticks_to_microsecs(elapsed);
    unsigned long mph = 2237414 / period;  // conversion factor computed elsewhere.
    printf ("  bullet speed: %ld mph\r\n", mph);    
}

void banner()
{
    printf("\r\nAE3K interval timer for MSP430\r\n");
    printf("    time -- measures and reports time elapsed between leading edges\r\n");
    printf("    freq -- frequency, determined from measuring one cycle\r\n");
    printf("    timeAB -- A/B interval timer, two different inputs\r\n");
    printf("    bullet -- A/B interval, interpreted as velocity across 1 meter\r\n");
}

int main(void)
{
    char buf[100];

    hardware_setup();

    banner();
    while(1){
        printf("\r\nCommand> ");
        gets(buf, sizeof(buf));
        if (!strcmp(buf, "time")){

            cmd_time();

        } else if (!strcmp(buf, "freq")){

            cmd_freq();

        } else  if (!strcmp(buf, "timeAB")){

            cmd_timeAB();

        } else  if (!strcmp(buf, "bullet")){

            cmd_bullet();

        } else if (!buf[0]){

            banner();

        } else {

            printf("unknown command: %s\r\n", buf);
            banner();

        }
    }
    printf("\r\n");
}