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 > 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_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 > 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_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 >=29000) && (cap =1190) && (cap =1790) && (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 > 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_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 >=29000) && (cap =1190) && (cap =1790) && (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 < }

mask = 0x01;
for (j=7; j if (capture_array[j])
addr = addr | mask;
mask < }
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
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 Arduino, Default, DIY, MSP-GCC, MSP430, Programming, Projects and tagged , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

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