Fun with Launchpad: Read Pulse Widths Using Interrupts

Print Friendly, PDF & Email

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;
}
Posted in DIY, Electronics, MSP-GCC, MSP430, Projects | Tagged , , , , , , , , | 15 Comments

Fun with Interrupts : Decoding Quadrature Encoders on TI Launchpad MSP-EXP430G2

Print Friendly, PDF & Email

This is one of those programming assignments I had undertaken to learn more about interrupts and timers. I have used a detented quadrature encoder before and used it in a small project . However the routine for these earlier trysts with encoder were not 100% original and somehow I had a feeling that I had not grasped the routine.
Although there are number of routines available online to use these encoders, nothing gives you a kick like the one you get re-inventing the wheel.
The encoder which I am using is actually a knob having detents (clicks) at every turn increment. The knob also has a keyswitch  akin to a centre-click on mice.

Sureelectronics  Rotary Encoder Switch With Keyswitch

Sureelectronics Rotary Encoder Switch With Keyswitch

This is those kind of knobs which you see on Hi-Fi audio systems/radio for volume or frequency controls.   This is the link to the product page. The output of these encoders is 2 bit gray code. One of the benefit using these encoders is that they can replace push buttons and if coded well can be used for menu operations.

The encoder outputs the data on two channel (CH A and CH B ); the output signal sequence depends on two factors viz bias (active high or active low) and direction of rotation.  In my test routine I have catered for an active high configuration .i.e the common pin is connected to GND and CH A, CH B and Button pins are connected directly to MCU pins which have been pulled up. So, if you take a multi-meter  and measure voltage on CH A , CH B or Button pin , you will read VCC with respect to Ground. The subsequent explanation is based on the abovementioned configuration.

As I mentioned above, at steady state the CH A , CH B or Button pins will read VCC. However when the knob is turned or pressed the pins will be pulled low momentarily and again pulled. Illustrated below is the signal sequence for rotation in both direction

So, as is evident from the above two diagrams, to determine direction of rotation we just have to interpret the change in bit values.  A transition of bit value from 1 to 0 would indicate a CCW rotation or a transition from 3 to 2 would indicate a clockwise rotation.

The code is sufficiently commented for ease of understanding. Please free to post a comment for your suggestions and queries

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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
 /* ----------------------------------------------------------------------
 * Demo program routine to read/interpret a detented rotary encoder
 * Coded by Ishan Karve (ishan at the rate gmail dot com)
 * 14 Feb 2012
 * Platform : TI MSP-EXP430G2 Launchpad running MSP430G2553
 * Encoder  :  Rotary Encoder Switch With Keyswitch
 *             http://www.sureelectronics.net/goods.php?id=221
 * Software Suite : Code Compose Studio, Version: 4.2.3.00004
 * ----------------------------------------------------------------------
 *
 * Routine Caters for
 *      Clockwise Rotation.
 *      Counter Clockwise Rotation
 *      Single Button Click
 *      Double Button Click
 * To Do       
 *      Click and Turn (CW or CCW)
 */


#include <msp430g2553.h>
#include <stdbool.h>
//Defines for Launchpad LEDs
#define LED1    BIT6
#define LED0    BIT0
//define for quarature encoder
#define CH_B    BIT0    //P2.0  Encoder Pin
#define CH_A    BIT1    //P2.1  Encoder Pin
#define BUTTON  BIT2    //P2.2  Encoder Pin
#define COMM    BIT5    //P1.5  Encoder Pin
//function declaration
void pattern_match();                   //determine direction of rotation
void TX(char *tx_message);              //Serial UART TX
static char *i2a(unsigned i, char *a, unsigned r);  //convert int to char
char *itoa(int i, char *a, int r);          //convert int to char
void ADC_init(void);

//variable declaration
char buffer[5];
int a=0;        //variable stores interrupt edge
int b=0;        //variable stores interrupt edge
int cval=3;     //store current value to determine rotation
int pval=3;     //stores previous value to determine rotation direction
int tr_state=0;     //variable to store transition state  
int num=0;      //arbitary number. will increment or decrement iff tr_satate=4
int tr;         //holds trigget state for variable tr_state
int pnum=0;    
int inc_fact=1;     //this variable holds the increment factor
int click=0;        //holds button click count
int sclick=false;   //flag holds single click, has to be reset in program loop
int dblclk=false;   //flag holds double click, has to be reset in program loop
void main(){
    //stop watchdog
    WDTCTL = WDTPW + WDTHOLD;
 
    //setup internal DCO
    BCSCTL1 = CALBC1_1MHZ;            // Set DCO to 1MHz
    DCOCTL = CALDCO_1MHZ;  
   
    //Setup output ports
    P1DIR |= (LED0+LED1+COMM);
    P1OUT &=~(LED0+LED1+COMM);
   
    //setup interrupts for quadrature encoder
    P2DIR&= ~(CH_A + CH_B + BUTTON);            //set ports as inputs
    P2IE |=  (CH_A + CH_B + BUTTON);        //enable interrupts for Quad Encoder
   
    //active-high configuration
    P2REN |=(CH_A + CH_B + BUTTON);         //enable pullups on respective ports
    P2OUT |=(CH_A + CH_B + BUTTON);
   
    P2IFG&=~(CH_A + CH_B + BUTTON);         //clear interrupt flag
    P2IES&=~(CH_A + CH_B + BUTTON);         //Interrupt on Hi->Lo transition
   
    //Setup USCI for UART communication at 9800-N-1
    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
    TX("Rotary Encoder Demo\r\n");
   
    /* setup timer to watch for double click
     * the double clik period is 0.5 second
     * mcu is running at 1 MHZ, hence we will divide (prescale)
     * the clock by 8 to get a frequency of 125 KHZ. Clock source
     * is SMCLK
     *
     * interrupt_period = (ticks/timer_clock_freq)
     * hence for a period of 0.5 second no of ticks = 62500
     */

     
     TACTL= TASSEL_2 + ID_3 + TACLR;    //SMCLJK + divide by 8 + clear counter
     TACCTL0=CCIE;              //enable timer
     TACCR0=62500;              //set timer interrupt count
     //note timer is not enabled. timer will be enabled in button interrupt routine

    _enable_interrupt();
    for (;;)
    {
        if (num!=pnum){
        itoa(num, buffer, 10);
        TX(buffer);
        TX("\r\n");
        pnum=num;
        }
    }
}

void pattern_match(){
 /* Transition sequence is as follows (Active High Config)
  *         --Clockwise--  
  * CH_A    ->  H L L H H
  * CH_B    ->  H H L L H
  *     ---Counter-Clockwise---
  * CH_A    ->  H H L L H
  * CH_B    ->  H L L H H
  *
  * If the above pattern is stored in a 2 bit array with CH_A as Bit 0 and CH_B as Bit 1
  * The value shall be as follows (integer)
  * CW      -> 3-2-0-1-3
  * CCW     -> 3-1-0-2-3
  */

    int cw[]={3,2,0,1,3};
    int ccw[]={3,1,0,2,3};
    int i;
    for (i=1;i<5;i++){
        if (pval==cw[i-1] && cval==cw[i])
        {
            if (tr){tr_state++;} else (tr=1);
            if (tr_state>=5)
            {
                num=num+inc_fact;
                tr_state=0;                                            
            }
           
        }
    }
       
    for (i=1;i<5;i++){
        if (pval==ccw[i-1] && cval==ccw[i])
        {
            if (!tr){tr_state++;} else (tr=0);
            if (tr_state>=5)
            {
                num=num-inc_fact;              
                tr_state=0;
            }
           
        }
    }
}
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;
            break;  
        }
    } // End TX Main While Loop
} // End TX Function

// Port 2 interrupt service routine
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
    pval=cval;      //store current_value
//disable all interrupts
    P2IE&=~(CH_A + CH_B + BUTTON);
//check interrupt flags to find which interrupt was triggred
   
    //Check for CH_A interrupt
    if ((P2IFG & CH_A)==CH_A)
    {
        if ((P2IN & CH_A)==CH_A) {a=1;}else {a=0;}  //read pin state
        if ((P2IN & CH_B)==CH_B) {b=1;}else {b=0;}      //read pin state
        cval=(a << 0 ) | (b <<1);              
        P2IES^=CH_A;                    //toggle interrupt edge
           
    }
   
    //Check for CH_B interrupt
    if ((P2IFG & CH_B)==CH_B)
    {
        if ((P2IN & CH_A)==CH_A) {a=1;}else {a=0;}  //read pin state
        if ((P2IN & CH_B)==CH_B) {b=1;}else {b=0;}      //read pin state
        cval=(a << 0 ) | (b <<1);
        P2IES^=CH_B;    //toggle interrupt edge
       
    }
   
   
   
   
    //Check for Buttton interrupt also caters for double click
    if ((P2IFG & BUTTON)==BUTTON)
    {
        click++;   
        if (click>=2)
        {
            click=0;
            dblclk=true;    //set flag true, has to be set false once action is taken on flag
            P1OUT ^= (LED0+LED1);   //toggle both leds
        }      
        TACTL |= MC_1;          //enable timer
       
    }
    pattern_match();
    //clear all interrupts
    P2IFG&=~(CH_A + CH_B + BUTTON);
    //enable all interrupts
    P2IE|=(CH_A + CH_B + BUTTON);

 }
 

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer0(void)
{
    if (click==1){sclick=true;P1OUT ^= LED0;}
    TACTL |= MC_0;              //disable timer
    click=0;
   
   
}

You can download the project files here or source c file

Posted in Default | 7 Comments

Fun with Timers : Decoding an Sony SIRC protocol on TI Launchpad MSP-EXP430G2

Print Friendly, PDF & Email

This post is one of those which have resulted out of my self learning . Over the time I have become fairly conversant with Arduino (ATMega type)  and PIC  and hence  I decided to take the plunge into the world of TI Launchpad to understand more about TI’s value line  processors. The last post on reading LM35 temperature sensor emanated from a similar urge.


Understanding concept of timers and their effective use  has always eluded me. Though superficially it looks simple, however once you get to the implementation part, things start looking really uphill.
Getting to work around MSP timers was especially challenging, since I am fairly new to these devices and their configurations.
One of my greatest obstacle in these so called self learning initiatives has been the lack of test equipment. The only one I have is a super el-cheapo multi-meter. That’s it. I know that’s blasphemous, however considering what I do and money I get, I excuse myself.
This week I decided to decode a Sony Remote Control Protocol (SIRC) as I had a spare TSOP IR sensor lying on my table, begging to be used.
Since I had used this protocol earlier, I was conversant with its basic structure.
Simply put, the data is sent in a stream of bits, LSB first. The bit stream consists of 13 bits including the start bit. The ‘mark’ period of the start bit is 2.4mS and the ‘space’ period is 600uS, which is common. Binary 1 is ‘mark’ of 1.2mS and ‘space’ of 600uS, Binary 0 is’mark’ and ‘space’ of 600uS each.
So all the three pulses, Start, One and Zero have different duty cycles and time periods.
The 13 symbol bit stream can be broken down into three parts Start Bit (1 bit), Command (7 bit) and Address (5 bit)
Since I did not have a signal generator handy, I decided to use a Arduino to do its job. I programmed to send a stream of pulses to emulate a key press of the remote. I did this so that I can tweak with the bit timings and be sure that that end program is working correctly. I have not used any PWM for generating pulse only bit banging.
Arduino Sketch:

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
void setup()
{
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);//LED output
}

void one(){
digitalWrite(3, HIGH);digitalWrite(4, HIGH);
delayMicroseconds(1200);
digitalWrite(3,LOW);digitalWrite(4, LOW);
delayMicroseconds(600);
}

void zero(){
digitalWrite(3, HIGH);digitalWrite(4, HIGH);
delayMicroseconds(600);
digitalWrite(3,LOW);digitalWrite(4, LOW);
delayMicroseconds(600);

}

void start()
{
digitalWrite(3, HIGH);digitalWrite(4, HIGH);
delayMicroseconds(2400);
digitalWrite(3,LOW);digitalWrite(4, LOW);
delayMicroseconds(600);

}

void loop()
{
start();
one();
one();
zero();
zero();
one();
zero();
zero();
one();
zero();
zero();
zero();
zero();
delay(5000);
}

Launchpad Code (Code Composer Studio)

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
/* ----------------------------------------------------------------------
* 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
#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];
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 &gt; 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}

char *itoa(int i, char *a, int r)
{
if ((r &lt; 2) || (r &gt; 36)) r = 10;
if (i &lt; 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&gt;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 &amp;= ~UCSWRST; // Start USCI
////////////////General GPIO Defines////////////////
P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
P1OUT &amp;= ~(LED0 + LED1+GND); //turn ports low
P1OUT|=VCC;
P2IE |= DAT;
P2IFG &amp;= ~DAT;
P2SEL = DAT; // Set P1.1 to TA0
/////////////////SETUP TIMER
TA1CCTL0 = CM_1 + SCS + CCIS_0 + CAP + CCIE; // Rising 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 == 12)
{
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
TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode
}
pre_cap = tick; // store this capture value
here:
P1OUT^=LED1;
}

Pulse Timings

Once I was at this step I was pretty sure that I was on right track and 80% job was done.  The timing as seen above are pulse period (Mark+Space duration). This is because I am capturing the pulse on the rising edge.
The next step was just to connect an actual sensor and test the code. Here is the output. Channel 1 button is pressed.

Pulse Timings

As you may see, there are some variations, this is because the key presses being repeated and delay caused by serial transmission.

Modified Code

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
/* ----------------------------------------------------------------------
* Decode Sony IR Remote (RM-GA020) using MSP430 Microcontroller
* 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
////////////////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;
int start_bit=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 &gt; 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}

char *itoa(int i, char *a, int r)
{
if ((r &lt; 2) || (r &gt; 36)) r = 10;
if (i &lt; 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&gt;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 &amp;= ~UCSWRST; // Start USCI
////////////////General GPIO Defines////////////////
P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
P1OUT &amp;= ~(LED0 + LED1+GND); //turn ports low
P1OUT|=VCC;
P2IE |= DAT;
P2IFG &amp;= ~DAT;
P2SEL = DAT; // Set P1.1 to TA0
/////////////////SETUP TIMER
TA1CCTL0 = CM_1 + SCS + CCIS_0 + CAP + CCIE; // Rising 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;
//wait till start bit is intercepted
if ((cap &gt;=29000) &amp;&amp; (cap =1190) &amp;&amp; (cap =1790) &amp;&amp; (cap }
if (i == 11)
{
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
TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode
}
pre_cap = tick; // store this capture value
here:
P1OUT^=LED1;
}

 

Timings Interpreted

Now that I have reached here, the last thing to do is to group the bits into bytes, change their order and output it on the screen. So below is the final code.

Final Code

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
/* ----------------------------------------------------------------------
* Decode Sony IR Remote (RM-GA020) using MSP430 Microcontroller
* 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
////////////////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;
int start_bit=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 &gt; 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}

char *itoa(int i, char *a, int r)
{
if ((r &lt; 2) || (r &gt; 36)) r = 10;
if (i &lt; 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&gt;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 &amp;= ~UCSWRST; // Start USCI
////////////////General GPIO Defines////////////////
P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
P1OUT &amp;= ~(LED0 + LED1+GND); //turn ports low
P1OUT|=VCC;
P2IE |= DAT;
P2IFG &amp;= ~DAT;
P2SEL = DAT; // Set P1.1 to TA0
/////////////////SETUP TIMER
TA1CCTL0 = CM_1 + SCS + CCIS_0 + CAP + CCIE; // Rising 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)
{
int mask=0;
int addr=0;
int cmd=0;
if (first_pulse==0)
{
pre_cap=TA1CCR0;
first_pulse=1;
goto here; //break from interrupt service routine
}

tick = TA1CCR0;
cap = tick- pre_cap;
//wait till start bit is intercepted
if ((cap &gt;=29000) &amp;&amp; (cap =1190) &amp;&amp; (cap =1790) &amp;&amp; (cap }
if (i == 11)
{
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");
//extract command and address
mask = 0x01;
for (j=0; j if (capture_array[j])
cmd = cmd | mask;
mask &lt; }

mask = 0x01;
for (j=7; j if (capture_array[j])
addr = addr | mask;
mask &lt; }
TX("CMD:");
itoa(cmd, charbuffer, 10);
TX(charbuffer);
TX(" ADDR:");
itoa(addr, charbuffer, 10);
TX(charbuffer);
TX("\r\n");
first_pulse=0;
i=0;
//start timer
TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode
}
pre_cap = tick; // store this capture value
here:
P1OUT^=LED1;
}

Interpreting Bit Stream

Download Project Files

Following Links have provided me valuable insight to help me understand the topic

  1. http://processors.wiki.ti.com/index.php/MSP430_LaunchPad_LED_Timer
  2. http://www.msp430launchpad.com/2010/07/timers-and-clocks-and-pwm-oh-my.html
  3. http://www.ti.com/lit/ml/slap113/slap113.pdf
  4. http://www.ccs.neu.edu/home/noubir/Courses/CSU610/S07/MSP430-Clock-Timers.pdf
  5. http://picprojects.org.uk/projects/sirc/sonysirc.pdf
  6. http://www.43oh.com/forum/viewtopic.php?f=9&t=358
  7. http://www.43oh.com/wp-content/uploads/2010/10/Lanchpad_Maxbotix_Ultrasonic_Rangefinder_MSP430G2211_main.c
  8. http://forum.sparkfun.com/viewtopic.php?t=8443
Posted in Arduino, Default, DIY, MSP-GCC, MSP430, Programming, Projects | Tagged , , , , , , , , , , | Leave a comment

Getting started with Cygwin.

Print Friendly, PDF & Email

Recently I was inundated with queries, asking me how to run shell scripts using Cygwin. This illustrated guide is primarily meant to address the issue of running shell scripts posted on this site on a WindoZe machine.
I assume that your machine has a working internet connection and is connected to internet. I am going to be economical on words and let the screenshot speak.

Step 1.  Download Cygwin.
Step 2.  Execute the Cygwin executable (setup.exe)





Step 3.  After the install is complete, copy the script to Cygwin home folder. By default home folder is located at C:\cygwin\home\User

Step 4. Run Cygwin terminal from the Start menu -> Programs -> Cygwin

Step 5. Chmod +x the script to make it executable
Step 6. Run the script.

Step 7. The script throws up errors as there is no Desktop folder in the home directory
Step 8. mkdir Desktop and run script again.

 


Step 9.  Thats all.

 

 

 

 

Posted in DIY | Tagged , , , , , , , | 69 Comments

Fun with TI Launchpad : Read LM35 sensor using Launchpad MSP430G2 (MSP430G2553)

Print Friendly, PDF & Email

Small code to read temperature from an LM35 sensor and output the temperature to Serial port. Uses hardware UART module and ADC 10 on launchpad.I do not claim originality for this code.  Serial functions are borrowed from Nathan Zimmerman hardware UART example.

Hardware :TI MSP430G2 Launchpad

Software : Code Composer Studio Version: 4.2.3.00004
Connections are very simple.Hookup the LM35 sensor as follows
Vin->P2.0
GND->P1.4
Dout->P1.5

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
#include <msp430g2553.h>
#include <stdbool.h>
////////////////Defines////////////////
#define LED1        BIT6
#define LED0        BIT0
#define Error       BIT0
#define GND         BIT4
#define DAT         BIT5
#define VCC         BIT0 //port P2.0
char buffer[4];
float temp;
int n_temp,r_temp;
////////////////Function Protos////////////////
void TX(char *tx_message);
static char *i2a(unsigned i, char *a, unsigned r);
char *itoa(int i, char *a, int r);
void ADC_init(void);

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 |= Error;
            break;  
        }
    } // End TX Main While Loop
} // End TX Function

void ADC_init()
{
    /* Configure ADC  Channel */
    ADC10CTL1 = INCH_5 + ADC10DIV_3 ;         // Channel 5, ADC10CLK/4
    ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE;  //Vcc & Vss as reference
    ADC10AE0 |= BIT5;                         //P1.4 ADC option
   
   
}

////////////////////////////////MAIN PROGRAM LOOP//////////////////////////////////////////////////
void main(void)
{
    WDTCTL = WDTPW + WDTHOLD;         // Stop WDT
    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 |= (LED1+GND+LED0);            //Define GPIOs as outputs else GPIOs are inputs
    P2DIR |=VCC;                
    P1OUT =0;                     //All Outputs off except for Green_LED
    P2OUT|=VCC;
    P1OUT&=~GND;
    //////////////SETUP ADC10 TO READ FROM CH A4 (P1.4)/////////////////////////////////////////////
    ADC_init();    
    __enable_interrupt();                     // Enable interrupts.
    int value=0;
   
////////////////Main Loop////////////////
    while(1)                    
    {
        __delay_cycles(1000);                   // Wait for ADC Ref to settle
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
        __bis_SR_register(CPUOFF + GIE);        // LPM0 with interrupts enabled    
        value = ADC10MEM;
        temp=value*0.317;  //temperature as float
        //split the float into integer and fraction part
        //C = A % B is equivalent to C = A ? B * (A / B)
        r_temp=((temp*10)-10) * ((temp*10)/10);
        itoa(temp, buffer, 10);
        TX("$LM35:");
        TX(buffer);
        TX(".");
        itoa(r_temp, buffer, 10);
        TX(buffer);
        TX("!\r\n");
        P1OUT ^= LED0;
        _delay_cycles(500000);
         
    } // End While
} // End Main Program

// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
  __bic_SR_register_on_exit(CPUOFF);        // Return to active mode
  P1OUT ^= LED1;
}
Posted in DIY, Electronics, MSP-GCC, MSP430, Projects | Tagged , , , , , , | 42 Comments