Fun with Launchpad: Read Pulse Widths Using Interrupts

Print Friendly

Small code to read pulse widths of square waves using interrupts.Uses hardware UART module for serial communication and interrupt on P2.0. Connect incoming signal on P2.0  Serial functions are borrowed from Nathan Zimmerman hardware UART example.

Hardware :TI MSP430G2 Launchpad

Software :Code Composer Studio Version: 4.2.3.00004

 aewFile1 realterm screen cap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* ----------------------------------------------------------------------
 * This program scan 50 samples on Port2.0 and dumps their pulse widths
 * Coded by Ishan Karve (ishan at the rate gmail dot com)
 * 12 Feb 2012
 * Platform         :   TI MSP-EXP430G2 Launchpad running MSP430G2553
 * Software Suite   :   Code Compose Studio,  Version: 4.2.3.00004
 * ----------------------------------------------------------------------
 */


#include <msp430g2553.h>
#include <stdbool.h>
////////////////Defines////////////////
#define LED1        BIT6
#define LED0        BIT0
#define DAT         BIT0 //P2.0   //input signal port
#define VCC         BIT5 //P1.5
#define GND         BIT4 //P1.4
char charbuffer[8];
int i=0;
int j=0;
unsigned int capture_array[51];             // RAM array for captures
int tick=0;
int cap=0;
int pre_cap=0;
int first_pulse=0;

////////////////Function Protos////////////////
void TX(char *tx_message);
static char *i2a(unsigned i, char *a, unsigned r);
char *itoa(int i, char *a, int r);


static char *i2a(unsigned i, char *a, unsigned r)
{
    if (i/r > 0) a = i2a(i/r,a,r);
    *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
    return a+1;
}

char *itoa(int i, char *a, int r)
{
    if ((r < 2) || (r > 36)) r = 10;
    if (i < 0)
    {
        *a = '-';
        *i2a(-(unsigned)i,a+1,r) = 0;
    }
    else *i2a(i,a,r) = 0;
    return a;
}

void TX(char *tx_message)
{
    unsigned int i=0; //Define end of string loop int
    char *message; // message variable
    unsigned int message_num; // define ascii int version variable
    message = tx_message; // move tx_message into message
    while(1)
    {
        if(message[i]==0) // If end of input string is reached, break loop.
        {break;}
        message_num = (int)message[i]; //Cast string char into a int variable
        UCA0TXBUF = message_num; // write INT to TX buffer
        i++; // increase string index
        __delay_cycles(10000); //transmission delay
        if(i>50) //prevent infinite transmit
        {
            P1OUT |= (LED1+LED0);
            break;  
        }
    } // End TX Main While Loop
} // End TX Function

int main(void)
{ WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    //setup clock to 1MHZ
    BCSCTL1 = CALBC1_1MHZ;            // Set DCO to 1MHz
    DCOCTL = CALDCO_1MHZ;  
    ////////////////USCI setup////////////////
    P1SEL = BIT1 + BIT2;            // Set P1.1 to RXD and P1.2 to TXD
    P1SEL2 = BIT1 + BIT2;            //
    UCA0CTL1 |= UCSSEL_2;            // Have USCI use SMCLK AKA 1MHz main CLK
    UCA0BR0 = 104;                  // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600
    UCA0BR1 = 0;                  // Set upper half of baud select to 0
    UCA0MCTL = UCBRS_1;               // Modulation UCBRSx = 1
    UCA0CTL1 &= ~UCSWRST;             // Start USCI
    ////////////////General GPIO Defines////////////////
    P1DIR |= (LED0 + LED1+GND+VCC); //define output ports  
    P1OUT &= ~(LED0 + LED1+GND); //turn ports low
    P1OUT|=VCC;
    P2IE |= DAT;
    P2IFG &= ~DAT;
    P2SEL = DAT;                             // Set P1.1 to TA0
    /////////////////SETUP TIMER
    TA1CCTL0 = CM_2 + SCS + CCIS_0 + CAP + CCIE; // falling edge + CCI0A (P2.0)// + Capture Mode + Interrupt
    TA1CTL = TASSEL_2 + MC_2;                  // SMCLK + Continuous Mode
    __enable_interrupt();
    for(;;)
    {

    }
}

// Timer1 interrupt service routine
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1(void)
{
    if (first_pulse==0)
        {  
            pre_cap=TA1CCR0;
            first_pulse=1;
            goto here; //break from interrupt service routine
        }
   
    tick = TA1CCR0;
    cap = tick- pre_cap;
    capture_array[i]=cap;
    i++;
    if (i == 30)
    {
        P1OUT^=LED0;//toggle led
        //turnoff timer 1
        TA1CTL = MC_0;
        //dump samples
        for (j=0;j<=i;j++) //exclude bit0 as it is mots likey erroneous
        {
            itoa(j, charbuffer, 10);
            TX(charbuffer);
            TX("-->");
            itoa(capture_array[j], charbuffer, 10);
            TX(charbuffer);
            TX("\r\n");
        }
        TX("------------------\r\n");
        first_pulse=0;
        i=0;
        //start timer
        TA1CCTL0 |= CM_1;
        TA1CTL = TASSEL_2 + MC_2;                  // SMCLK + Continuous Mode
    }
    pre_cap = tick;                       // store this capture value
    here:
    P1OUT^=LED1;
}
Ishan Karve

About Ishan Karve

Ishan Karve is just an every day normal guy next door who happens to be an Electronics Engineer by profession and dabbles with PHP, Javascript, C++ and python. His interests vary as seasons change.. they change from astronomy to soul searching. This site is just a reflection of what he does to keep his mind engaged when he is not occupied by work and family. He is an extremely objective guy and is always ready for some good arguments.. of course over a glass of 40% proof alcohol.
This entry was posted in DIY, Electronics, MSP-GCC, MSP430, Projects and tagged , , , , , , , , . Bookmark the permalink.

15 Responses to Fun with Launchpad: Read Pulse Widths Using Interrupts

  1. vankhoa says:

    i fix this project a little to measure the distance by SRF05, it works but i still don’t understand you code in the interupt timer, i confuse when does the timer start to count and when does it stop, and what you store in the “tick” and pre_cap, please expain them to me, thank you.

  2. Ishan Karve Ishan Karve says:

    Ok.. I will shed a bit of light

    The interrupt has been initially configured to trigger on a falling edge (Line 95)
    The timer starts when you enable the timer (line no 96 above) .. consider this event as start of a stop watch
    The timer doesn’t stop when it is interrupted by the first change in edge of incoming signal (H->L). we just record the time (Line 110)
    It is important to remember that the code above buffers about 30 samples until it dumps the output to the TX buffer.
    To measure the time (pulse width) we need to know 2 values.. start time and end time.
    In the program loop end time of the last sample is the start time of the current sample .. so on
    After this the end time becomes the start time for the next pulse, this is done at line 141.
    so the variable ticks stores the end time (current time) Line 115
    we turn off the timer at Line 123 so as to prevent the interrupt from triggering during serial transmission.

    At line 138 we toggle the edge to read a transition from L->H

    Hope this clears your doubt.

  3. vankhoa says:

    ok i get it now, i didn’t notice that you changed mode of your timer capture from down to up, so i wonder how could you get the start point of the pulse.Thank you!!!

  4. Don Varela says:

    I am using your program to measure an incoming pulse. In your for;; loop I put
    P2OUT ^= PULSE; // Toggle P1.0 using exclusive-OR

    i = 620; // Delay
    do (i–);
    while (i != 0);
    which creates an approximate 15ms pulse. I output this signal on P2.4 If I REN the DAT signal and change the second MC_1 to MC_2 the program works fine. The design of my circuit board would better work if I used P2.5 as the input. This is where my programming world starts to fall apart. I changed the DAT bit to bit5. Changed TA1CCTL0 to TA1CCTL2, changed CCIS_0 to CCIS_1, changed #pragma vector=TIMER1_A0_VECTOR to #pragma vector=TIMER1_A1_VECTOR and changed TA1CCR0 to TA1CCR2. The program starts but never stops when I remove the interrupt. The measurement of the pulse is zero (0). I don’t understand what TA1.2 is and CCI2B which is defined in the msp430g2553.pdf. It appears that I,m not using the correct clock to compare against or I don’t know how to set the Ta1.2 and CCI2B. Could you shed some light on how to get pin P2.5 to work as an input? I have been working on this problem for days and can’t figure it out.

  5. Don Varela says:

    Correction to my previous post. The program for testing the pulse measurement in the for loop is:
    volatile unsigned int i;

    P2OUT ^= PULSE; // Toggle P2.4 using exclusive-OR

    i = 620; // Delay
    do (i–);
    while (i != 0);

  6. Don Varela says:

    Another correction. I changed CM_1 to CM_2 not MC.

  7. Don Varela says:

    This is me again. I think I found the problem. For those having similar problems I found out that vector=TIMER1_A0_VECTOR __interrupt void TIMER1(void) using TA1CCTL0 and CCR0 only has one interrupt. When you use vector=TIMER1_A1_VECTOR __interrupt void TIMER1(void) you have to use TA1CCTL1 or TA1CCTL2 and CCR1 or CCR2. These create multiple interrupts, so in the case of your program using vector=TIMER1_A1_VECTOR it continually does the routine without actually going out and acquiring another interrupt. I found to fix this problem you have to determine which interrupt is being performed. To do this under the vector=TIMER1_A1_VECTOR you put the following steering program.

    switch (__even_in_range(TA1IV, 10)) // Efficient switch-implementation
    {
    case 2: break; // TACCR1 not used
    case 4: break; // TACCR2 not used
    case 10: P1OUT ^= 0×01; // overflow
    break;

    If you replace the break with something like TX(“2″) for case 2, TX(“4″) for case 4 you will find that the interrupt happens on case 4. If you make the changes I mentioned in the first posts and place the program under case4 it will work. Make sure there is a break at the end of the program.

    If there is an easier method I would sure like to hear about it.

  8. Mallek says:

    Hey everybody, i’m a biomedical engineer student and i work with a friend to calculate the impulsions of an ECG signal and diplay it in an LCD screen. We make the circuit and it work very well but the problem is in the MSP430 part. We use an MSP430g2553 and we use P2.5 as an imput for our signal. I use the this code cause i think it’s really interesting for us but the changes that Donvarela makes don’t work for me ;(((((((((((((
    I used the first program that ishan published, I can see the Led works but the LCD don’t show the results of the signal
    I think the problem was in for(;;)
    {
    P2OUT ^= PULSE; // Toggle P2.4 using exclusive-OR (i don’t get this ??) cause i don’t have PULSE variable so i use DAT on the place of Pulse

    i = 620; // Delay
    do (i=i-1);
    while (i!=0);
    }
    Please i need an answer and more details in the program
    Have a nice weekend and thanks for the program

    • Ishan Karve Ishan Karve says:

      Hi Mallek.. I have not quite understood your problem statement.

      What do you want to display on your lcd . I would recommend that you test the code on a serial terminal.

  9. Mallek says:

    Hi Ishan,

    I’m giving an imput square signal (which frequency is 2Hz) and i want to display the frequency in the screen.
    Using your program, the leds flash twice a second, that’s normal, but i cannot have the display in the lcd.
    Do you have any ideas to help me please?

    Thanks a lot.

  10. Mallek says:

    Hi,

    I just used the code that you posted; i use an emulation MSP-EXP430G2. I found that the green led flash when i change the frequency of my signal.
    here the code that i use, the lcd screen display the two first messages i have programmed, but not the frequency.

    #include “msp430.h”
    #include
    ////////////////Defines////////////////
    #define LED1 BIT6
    #define LED0 BIT0
    #define DAT BIT0 //P2.0 //input signal port
    #define VCC BIT5 //P1.5
    #define GND BIT4 //P1.4
    char charbuffer[8];
    volatile unsigned int i=0;
    int j=0;
    unsigned int capture_array[51]; // RAM array for captures
    int tick=0;
    int cap=0;
    int pre_cap=0;
    int first_pulse=0;
    int toto=4;

    /*UART
    * P1.1 : rxd
    * P1.2 : txd
    * Attention: sur le launchpad c’est inverse
    */

    void initUART()
    {
    /* Set UCSWRST (bis.b #UCSWRST, &UCAxCTL1)
    * Initialize all USCI registers with UCSWRST
    * = 1 (including UCAxCTL1)
    * Configure ports
    * Clear UCSWRST via software (BIC.B #UCSWRST, &UCAxCTL1)
    * Enable interrupts (optional via UCAxRXIE and/or UCAxTXIE)
    */
    /* configure pin */
    P1DIR&=~BIT1; /* rxd */
    P1DIR|=BIT2; /* txd */
    UCA0CTL1 = UCSWRST;
    UCA0CTL0 = 0;
    UCA0CTL1 |= UCSSEL_2;
    /*
    UCA0CTL0 = 0<<7 | // parity disable
    0<<6 | //Odd or event not used if parity disabled
    0<<5 | // MSB first
    0<<4 |// 8 bits data
    0<<3 |// One stop bit
    0<<1 |// OO uart mode
    0<<0 ;// asynchronious mode
    UCA0CTL1 = UCSSEL_2| // clock selection TBD
    0<<5|// erroneous char rejected
    0<<4|// break char not generate IT
    0<<3|// all char not generate IT
    0<<2|// next data is data
    0<<1|// TBD
    1<<0; // TBD
    */
    /* 1MHz : for 19200 slau144h.pdf p.429
    * UCBRx = 52, UCABR0 = 52, UCABR0=0
    * UCA0MCTL :
    * UCBRS : 0, UCBRF:0, UCOS16=0
    */
    UCA0BR0 = 52; // 1MHz 19200
    UCA0BR1 = 0×00;
    UCA0MCTL = 0< 0) a = i2a(i/r,a,r);
    *a = “0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ”[i%r];
    return a+1;
    }

    char *itoa(int i, char *a, int r)
    {
    if ((r 36)) r = 10;
    if (i 50) //prevent infinite transmit
    {
    P1OUT |= (LED1+LED0);
    break;
    }
    } // End TX Main While Loop
    } // End TX Function

    void send_char(char carac){ //envoi des bits
    UCA0TXBUF = carac; /* envoie du char */
    while ((UCA0STAT & 0×01)!=0×00); /* test si la transmission est finie */
    __delay_cycles(100000); /* pause necessaire de 100ms pour le lcd */
    }

    void send_string(char tablo[], int taille){ //envoi des octet
    send_char(0×0);
    send_char(0xA3); //effacer l’ecran en attendant l’instruction send_char(0×1) pour nous mettre en position initiale
    send_char(0×1); // position initiale
    send_char(0xA2); //On envoie des char jusqu’a send_char(0×0)
    if(taille <= 16)
    {
    for (i=0; i<taille;i++)
    {
    send_char(tablo[i]);
    }
    send_char(0×0);
    }
    else
    {
    for (i=0; i<16;i++)
    {
    send_char(tablo[i]);
    }

    send_char(0×0);//pour arrÍter de mettre des caractËres
    send_char(0xA1); // on change de position, la nouvelle position sera dÈfinie par les 2 prochains send_char()
    send_char(0×0); // position colonne 0
    send_char(0×1); // position ligne 1
    send_char(0xA2); // on renvoie des char
    for (i=16; i<taille;i++)
    {
    send_char(tablo[i]);
    }
    }
    // UCA0TXBUF = carac; /* envoie du char */
    // while ((UCA0STAT & 0×01)!=0×00); /* test si la transmission est finie */
    // __delay_cycles(100000); /* pause necessaire de 100ms pour le lcd */
    }

    void imprimer(char valeur)
    {
    send_char(0xA2);
    if (valeur >4);
    int H2 = ((nombre & 0xF00)>>8);
    send_char(0xA3);
    send_char(0×1);
    send_char(0xA2);
    send_char(‘F’);
    send_char(‘r’);
    send_char(‘q’);
    send_char(‘=’);
    send_char(”);

    send_char(0xA1);
    send_char(toto);
    send_char(0×0);

    imprimer (H0);
    imprimer (H1);
    imprimer (H2);
    }

    void main(void)
    {

    int taille=32;
    char message[]= ” Electro Cardiogramme”;
    char message2[]= ” Placez vos doigts”;
    WDTCTL = WDTPW + WDTHOLD;
    BCSCTL1 = CALBC1_1MHZ; //set range
    DCOCTL = CALDCO_1MHZ; // Set DCO to 1MHz
    BCSCTL2 = (SELM_0 | DIVM_0); //MCKL= DCOCLK/1=1MHz
    P1DIR |= BIT0+BIT6; /* leds */
    initUART();
    send_string(message,taille);
    // __delay_cycles(2000000);
    send_string(message2,taille);
    segmenter(DAT);

    ////////////////USCI setup////////////////
    P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD
    P1SEL2 = BIT1 + BIT2; //
    UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK
    UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600
    UCA0BR1 = 0; // Set upper half of baud select to 0
    UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1
    UCA0CTL1 &= ~UCSWRST; // Start USCI
    ////////////////General GPIO Defines////////////////
    P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
    P1OUT &= ~(LED0 + LED1+GND); //turn ports low
    P1OUT|=VCC;
    P2IE |= DAT;
    P2IFG &= ~DAT;
    P2SEL = DAT; // Set P1.1 to TA0

    /////////////////SETUP TIMER
    TA1CCTL0 = CM_2 + SCS + CCIS_0 + CAP + CCIE; // falling edge + CCI0A (P2.0)// + Capture Mode + Interrupt
    TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode
    __enable_interrupt();

    for(;;)
    {

    }

    }

    // Timer1 interrupt service routine
    #pragma vector=TIMER1_A0_VECTOR
    __interrupt void TIMER1(void)
    {
    if (first_pulse==0)
    {
    pre_cap=TA1CCR0;
    first_pulse=1;
    goto here; //break from interrupt service routine
    }

    tick = TA1CCR0;
    cap = tick- pre_cap;
    capture_array[i]=cap;
    i++;
    if (i == 30)
    {
    P1OUT^=LED0;//toggle led
    //turnoff timer 1
    TA1CTL = MC_0;
    //dump samples
    for (j=0;j”);
    itoa(capture_array[j], charbuffer, 10);
    TX(charbuffer);
    TX(“\r\n”);
    }
    TX(“——————\r\n”);
    first_pulse=0;
    i=0;
    //start timer
    TA1CCTL0 |= CM_1;
    TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode
    }
    pre_cap = tick; // store this capture value
    here:
    P1OUT^=LED1;
    }

    thanks a lot for your answers.

    • Ishan Karve Ishan Karve says:

      Mallek.. are you using a serial LCD?
      If yes.. then just try pumping some characters to the lcd to check if it works.
      Nways I will check the code once i return back home

  11. Mallek says:

    Hi,

    Yes, i’m using a serial LCD which works well.

    Tanks for your time.

    Mallek

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>