Illustrated guide to interfacing Maxim OneWire (1-Wire) devices DS18B20 on TI Launchpad MSP-EXP430G2

Print Friendly, PDF & Email

Maxims proprietary one wire devices have been popular with hobbyist for a long time.  The small and incredibly accurate (for hobbist) DS18B20 and DS18S20 series of temperature sensors from this family of devices have been used on many platforms.

I too have used this temperature sensor, DS18B20 in particular in some of my hobby projects. But on all occasions the code routines that I used to interface the sensors, had been the hard-work of somebody else. However that did not matter as it solved my immediate purpose then. Comprehensive and easy to use routines are available for AVR, PICs and the Arduino Community so why rack your brains!

However as I ‘made the switch’ to TI MCU, I realised that there was no out of the box support for these devices and libraries /code snippets available were either arcane (in assembly) or difficult to understand and implement. Initially in my earlier exploits I used to just gloss over the sensors datasheet. So this time around, making an exception, I decided to read through the datasheet and try to make sense of the language and code routines all by your own self.

Thus coming to the moot point of the post, in this post I shall try to explain how to read a DS18B20 sensor using a TI Launchpad running MSP430G2452. This guide has been written to read only for single one wire device on bus. Do not expect me to give all ‘gyaan’ (knowledge) on the sensor in this post. Read the datasheet yourself to understand the specs. 

Hardware :TI MSP430G2 Launchpad running MSP430G2452, Maxim DS18B20,  4.7K ohm resistor
Software :Code Composer Studio Version: 4.2.3.00004
Test Equipment: Rigol 1052e DSO
Wiring The Sensor:

Launchpad Interface Matrix:

MCU Port Port Pin DS18B20 Pin
P2 0 GND
P2 1 Vdata
P2 2 VCC

Generation of delay period: A look at the datasheet will reveal that timing is of essence for establishing communication with limits being defined. So getting and setting up your delay period becomes an important task. The delay period varies from 1 ?s to 750ms.  Hence the minimum granularity for the delay is 1 ?s. So the clock speed of the MCU has to be in excess of 1MHz (1/1 ?s). MSP430G2452 is a fairly capable chip and a big brother of the MSP430 lineup and executes one instruction per clock cycle. In absence of any external crystal on the Launchpad in stock mode, we shall use the internal dco in the MCU to generate the required delays.

Be advised though , due to code overheads, in reality an instruction (as written in HLL) can take more than one clock cycle and also an internal clock can never replace an external crystal, so expect drifts in timings when you see the signals on oscilloscope.   i.e a delay of 16 cycles at 16MHz will not necessarily give you a 1 ?s delay, in reality it will give you more. However we can live with this till we are in the specified limits (they are fairly large). Following is the code snippet of functions  I have used to generate a milli-second and micro-second delay.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void delay_ms(int ms)
{
while (ms--)
{
__delay_cycles(16000); // set for 16Mhz change it to 1000 for 1 Mhz
}

}

void delay_us(int us)
{
while (us--)
{
__delay_cycles(8); // set for 16Mhz change it to 1000 for 1 Mhz
}

}

Transaction Sequence to read from a single sensor on bus:

There are three basic requests , as with any bidirectional  communication request.

  1. Initiation. This request establishes and acknowledges presence of both  (Master and Slave) devices on the bus. This is akin to saying ‘Hello’.
  2. Write Data. Either master or slave writes data on bus.
  3. Read DataEither master or slave read data from the  bus.

The Master in this case is the MCU on launchpad and the Slave is the 1-Wire device. It should be noted that all communication on the bus is half-duplex i.e there is only one talker on bus at any given point in time.

Caveats: Before I tread ahead, here are some things that I learnt the hard way and I am sharing so that you dont have to relearn them the harder way.

  • As we have a pull up resistor on data pin of the sensor (DQ), the bus floats high at the VCC potential, always. This is the idle state of the bus when it is not transacting.
  • When Master/Slave release the bus, it should float back to VCC.
  • Pulling the bus high to VCC is not the same as releasing the bus. I learnt this by burning the midnight oil.
  • Master on the bus is the Master, period. It initiates all requests, including bit wise transmission of data by slave.

I shall now attempt to explain each of them with a signal snapshot from oscilloscope and a code snippet.

Initiation: Initiation or Initialization as it is referred to in the datasheet consists of a reset pulse transmitted by the bus master. If there is a slave device on the bus (properly connected) it acknowledges its presence  to the bus master by transmitting a presence pulse.  A presence pulse is generated by the slave by pulling the bus low,

Master initiates a reset request by pulling the bus low for 480us. After that he releases the bus and waits for  60us-100us to read for a presence pulse. In the interim , after the Master releases the bus the Salve waits for 15 to 60 us before its pulls the bus low. The slave holds the bus low of 60 to 240 us  before it releases the bus again.

Following is an annotated screen capture of such a transaction from oscilloscope.

Following code will accomplish such a request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned int ResetDS1820 ( void )
{
/* Steps to reset one wire bus
* Pull bus low
* hold condition for 480us
* release bus
* wait for 60us
* read bus
* if bus low then device present set / return var accordingly
* wait for balance period (480-60)
*/

int device_present=0;
DS1820_LO(); // Drive bus low
delay_us (480); // hold for 480us
DS1820_DIR &= ~DS1820_DATA_IN_PIN; //release bus. set port in input mode
delay_us(60); //wait for slave to pull bus low
if(DS1820_IN & DS1820_DATA_IN_PIN)
{
device_present=0;
}
delay_us (480); //wait for 480us
return device_present;
}

Since earlier I mentioned about that releasing bus is not as same as pushing the bus high, illustrated below is a screen shot of a reset request initiated by a master, but the difference being instead of releasing the bus and letting the bus float high, I pulled up the bus programmatically, the result below is self explanatory

Incorrect Reset Pulse

Bit-Banging: Before we start exchanging  pleasantries on the bus we need to get our ones and zeros correct . In One Wire communication parlance  bit wise transmission and reception is done in a concept of slots . So there is a read slot and a write slot. Master initiates slots for its own transmission and for slaves transmission.

Depicted below is an illustration of Write Slot. Remember duration of each individual write slot (either zero or one) is 60us(tSlot)

Illustration of Write Slots

Write 0 Slot (Master Transmits Bit Wise Zero)Master pulls the bus low and just holds low for 60us and then releases it.  In the interim after about 15us after Master pulls the bus low, the bus is sampled by the slave, since we are holding the bus low, it reads a logical zero.

Write 1 Slot (Master Transmits Bit Wise One) Master pulls the bus low and releases the bus within 15us (that the period after which the slave sample the bus). After pulling the bus low, after 15us the slave samples the bus and assuming that the master has released the bus, the bus is now floating high and the slave reads a logical zero. However since the slot window is 60us and one 1us window for recovers , the Master waits for the balance period before it exits the function.

In the screen capture above the blue trace is the Write 1 slot also shown is the yellow trace which shows the total slot windows width for the sake of comparison.

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
void WriteZero(void)
{
/*Steps for master to transmit logical zero to slave device on bus
* pull bus low
* hold for 60us
* release bus
* wait for 1us for recovery
*/

DS1820_LO(); // Drive bus low
delay_us (60); //sample time slot for the slave
DS1820_DIR &= ~DS1820_DATA_IN_PIN; //release bus. set port in input mode
delay_us (1); //recovery time slot

}
void WriteOne(void)
{
/*Steps for master to transmit logical one to slave device on bus
* pull bus low
* hold for 5us
* release bus
* wait for 1us for recovery
*/

DS1820_LO(); // Drive bus low
delay_us (5);
DS1820_DIR &= ~DS1820_DATA_IN_PIN; //release bus. set port in input mode
delay_us (55); //sample time slot for the slave
delay_us (1); //recovery time slot

}

Reading data from the Slave:Now we shall come to the second half of bit-banging. This method deals with Master reading data put on bus by the Slave device i.e Slave Tx and Master Rx. As as I mentioned above the Master initiates all bit wise data transactions on the bus. So how does master come into picture when slave is transmitting. Consider Slave to be the cow and Master to be the Milkman or a Maid (;-p) . So the Master milks the cow. Also it is important to note that slave does not put data on bus on its whim and fancy but has to be explicitly addressed and commanded to do so. We shall deal with this a subsequently . For the time being assume that the Slave has been commanded to put data on bus. Since the master has issued the command the master is aware that there is going to be data available on the bus.

So in order to retrieve the data from the slave the master issues a read slot. It does so by pulling the bus low for a short period and then releasing the bus.

Illustration of Read Slot

As with write slots, all read time slots are of a minimum of 60µs in duration with a minimum of a 1µs recovery time. So slot width is 61us.

Read Slot (Slave Transmits Bit Wise Zero or One)Master issues as read slot by pulling the bus low for a minimum of 1us and then releases it. If the slave wants to transmit Zero, it will continue to hold the bus low for the slot window period. If the slave is transmitting One, once the master releases the bus, the slave will allow the bus to float high and will take no action.  Also data transmitted by 1-Wire device on the bus is only valid for 15us after the bus has been pulled low by the Master. So in effect to read the data the master has to poll the bus within 15us of the bus being pulled low.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned int ReadBit (void)
{

/*Steps for master to issue a read request to slave device on bus aka milk slave device
* pull bus low
* hold for 5us
* release bus
* wait for 45us for recovery (46us+5us=61us)
*/

int bit=0;
DS1820_LO(); // Drive bus low
delay_us (5); //hold for 5us
DS1820_DIR &= ~DS1820_DATA_IN_PIN; //release bus. set port in input mode
delay_us (10); //wait for slave to drive port either high or low
if(DS1820_IN & DS1820_DATA_IN_PIN) //read bus
{
bit=1; //if read high set bit high
}
delay_us (46); //recovery time slot
return bit;

}

Till now we saw how to initialize communication between Master and Slave and do bit-banging and this is almost 80% of the battle.  The next logical step is  to do byte wise communication, thats where the juice lies.
Commands for the One wore bus as one byte(8bit) in data width and come in two flavours

      1. ROM Commands
      2. Function Commands

ROM Commands:  If you have gone through the datasheet you will realise that you can have multiple one wire devices on a single bus and each device has a unique, addressable 64 bit serial code (ROM Code), which allows multiple DS18B20s to function on the same 1-Wire bus. Thus the ROM commands are used primarily to either search devices on a bus or target a specific device on bus to push or pull data to/from it  or if any device on the bus has experienced an alarm condition.There are five ROM commands for DS18B20

  • Search ROM: This command is used to search devices on the one wire bus. Will not be discussed here as we are going to use only one device on bus and hencenot going to use it.
  • Skip ROMWe shall be using this command. Used when there is only one device on the bus. Instructs the Slave that it shall not be uniquely addressed.
  • Match ROM: Not used in this guide. Master uses this command to target a specific device in a multi-node scenario.
  • Read ROM: Not Used.  Useful to read ROM code from a device. Used only in a single device scenario.
  • Alarm Search: Not Used. The command allows the master device to determine if any DS18B20s has experienced an alarm condition during the most recent temperature conversion. Usage similar to Search ROM.

Function Commands:  These commands allow the master to write to and read from the DS18B20’s scratchpad memory, initiate temperature conversions and determine the power supply mode. It is important to note that the master can issue one of the DS18B20 function commands.  Following are list of function commands relevant to DS18B20

  • CONVERT T: Used by Master to instruct the Slave to initiate temperature conversion. If the DS18B20 is powered by an external supply, the master can issue read time slots after the Convert T command and the DS18B20 will respond by transmitting a 0 while the temperature conversion is in progress and a 1 when the conversion is done. Temperature conversion takes a minimum of 750ms .. yes that is millisecond , you read that correctly. So after issuing the command the master has to wait for minimum 750ms before its polls the bus for slave’s reply.
  • WRITE SCRATCHPAD: Not Used. This command allows the master to write 3 bytes of data to the DS18B20’s scratchpad.
  • READ SCRATCHPAD: This command allows the master to read the contents of the scratchpad. Data is read LSB first. The master may issue a reset to terminate reading at any time if only part of the scratchpad data is needed. He does so by issuing a reset command.
  • COPY SCRATCHPAD: This command copies the contents of the scratchpad in the Slave device to EEPROM also in the Slave device.
  • RECALL E2: Not Used. This command recalls the alarm trigger values and configuration data from EEPROM and places the data in bytes 2, 3, and 4, respectively, in the scratchpad memory.
  • READ POWER SUPPLY: Not Used. The master device issues this command followed by a read time slot to determine if any DS18B20s on the bus are using parasite power (Read datasheet to understand what it means).

Note that The master can interrupt the transmission of data by Slave at any time by issuing a Reset Command. Below are some screen shots of  a byte full of data transmission in progress

Screen Captire of Skip ROM Command

As you can see in the above screen shot the data is being transmitted LSB first. The data being transmitted is 0xCC

So in a nutshell we are going to issue the following commands to read the temperature from the sensor

  • Reset / Init
  • Skip ROM
  • Convert T
  • Read Scratchpad
As per one-wire scheme of things it has to be noted that after issuing a ROM command a reset command is required to be issued. So the sequence of commands will now look like

  • Reset
  • Skip ROM
  • Convert T
  • Wait for 750us
  • Reset
  • Skip ROM
  • Read Scratchpad
  • Master loops a read slot to receive data from the device LSB First

Interpreting Data: Once data is received bit wise by the master, the next step is to digest the data in such a way that you get current temperature read by the device. The default resolution of temperature data at power-up is 12-bit and is calibrated in Celsius and not Fahrenheit  . However options are available to get data at lower resolutions at 9,10,11 bits corresponding to increments of 0.5°C, 0.25°C, 0.125°C, and 0.0625°C, respectively.

. But I am sticking with higher 12 bit resolution here.

The temperature data is stored as a 16-bit sign-extended two’s complement number in the temperature register in the slave device with last 4 bits of the MSB (15,14,13,12) containing the sign bit for the reading. This is useful while reading sub-zero temperatures. For positive temperatures the sign bit is not set but is set to 1 for sub zero temperatures. Lets attempt to interpret data using a screen shot below

The blue trace is the data trace while the yellow trace is the byte window width (just for reference. Interpreting bits from the left and side of the screen we get

LSB End 0010001001000000  MSB End

 Since I had mentioned earlier data is read LSB first we will have to flip it around to get it MSB first

MSB End 0000001001000100 LSB End

Converting the above bit pattern to a 16 bit hex we get the value of 0x244h = 580
As the bit resolution is 0.0625 deg/bit in 12 bit mode we will multiply the value by 0.0625
Hence 580*0.0625=36.25 C which is rightly the temperature here where I stay. Yes thats indoor temperature.

Mission Accomplished.
You can download the complete source here (code is for MSP430G2452, you can adapt it for any MSP430MCU)
David also has made a much more capable library for One Wire devices  here
Posted in DIY, Electronics, MSP-GCC, MSP430 | Tagged , , , , , , , , | 26 Comments

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