Home LPC2000 LPC2148 SIRC with the LPC-P2148

Main Menu

SIRC with the LPC-P2148

thumbTo decode Sony SIRC codes output by my Harmony 880 remote control, I connected a Panasonic PNA4602M infrared sensor to P0.4 (CAP0.1). It's a 38kHz type sensor, so it works great with standard remote controls. Rather than work blind, I connected a 16x2 character LCD in 4-bit mode with busy flag checking to display the codes.

 

It's a fairly simple project, but very useful for all kinds of things.


The sensor works inverted. It runs with a high idle. When it receives a signal the output pulses low. The program measures the pulse-widths and builds a 12-bit "word" in buffer that contains the 7-bit command and a 5-bit address (always 1, for TV, in my case). Remote controls send every command three times. The program detects and processes all three, but only the last one is used.

 

The code sets P0.4 up as CAP0.1 and watches for a falling edge. It interrupts on a falling edge, saves the timer counter value as start and sets to watch for a rising edge. When it detects that it calculates the width of the pulse, processes it accordingly and sets to watch for a falling edge again.

 

You can find a partial list of SIRC codes and pretty good information at this site.

 

Without my Saleae Logic I would have found this project incredibly difficult to get working right. My remote control's output pulse-widths are different enough from the spec that I wasted a lot of time trying to get it going, with very flaky results. Then I hooked up the Logic and could immediately see that the actual pulse-widths were a bit longer than the spec. Adjusted the code to suit and she works great now!

 

 

Here's an overview of the project (left). Wiring is ugly, but totally reliable. A closer view (right) shows the PNA4602M IR sensor behind the LCD. The sensor and the LCD both need 5V power, so the breadboard has its own power supply. The input pin (P0.4) on the ARM is 5V tolerant, so no worries there.

 

Couple more pics of the connections.

 

 

I'll draw a schematic soon, but this whole thing is pretty simple. You should be able to figure it out for yourself.

 

Here's the code, done in Eclipse/yagarto. Header file at the bottom.

 

sirc.c

#include "LPC214x.h"
#include <stdio.h>
#include "sirc.h"

int start,count,result,done = 0;
static unsigned int buffer = 0;
char number[] = " ";

int main(void)
{
int i,cmd,add;
init();
PINSEL0 = 0x00000200; //p0.4 = capture 0.1 (timer0)
IODIR0 = 0x107e3000;
IOCLR0 = 0x107e0000;
lcd_init();
T0TCR = 0x02; //reset counter
T0CCR = 0x0030; //capture & interrupt on 0.1 falling edge
VICVectCntl0 = 0x00000024; //use it for timer 0 interrupt:
VICVectAddr0 = (unsigned)isr_capture; //set interrupt vector in 0
VICIntEnable = 0x00000010; //enable timer0 interrupt
T0TCR = 0x01; //enable timer0
enableIRQ();

while(1){
if(done){
beep();
disableIRQ();
lcd_cmd(0x01); //clear display
cmd = buffer & 0x0000007f; //mask out non-command bits
add = buffer & 0x00000f80; //mask out non-address bits
add = add >> 7;
lcd_line1();
sprintf(number,"Command: %x",cmd);
lcd_string(number);
lcd_line2();
sprintf(number,"Address: %x",add);
lcd_string(number);
done = 0;
enableIRQ();
}
}
}

void beep(void)
{
int i,j;
IOCLR0 = 0x00002000; //buzzer2 low
IOSET0 = 0x00001000; //buzzer1 high
for(i=0;i<300;i++){
IOPIN0 ^= 0x00003000;
for(j=0;j<700;j++);
}
}

void isr_capture(void)
{
if(!(IOPIN0 & 0x00000010)){ //if falling edge
T0CCR = 0x0028; //set for rising edge capture & interrupt
start = T0CR1;
}
else{
T0CCR = 0x0030; //set for falling edge capture & interrupt
if(T0CR1 < start) //rollover occurred
result = (0xffffffff - start) + T0CR1;
else //no rollover
result = T0CR1 - start;

if(result > 0x22000){ //if start pulse
count = 0;
buffer = 0;
}
else if((result > 0x12000) & (result < 0x14000)){ //check for 1 pulse
buffer |= 0x10000000;
buffer = buffer >> 1;
count++;
}
else if (result < 0xb500){ //check for 0 pulse
buffer = buffer >> 1;
count++;
}
if(count == 12){
buffer = buffer >> 16;
count = 0;
done = 1;
}
}
T0IR = 0x20; //clear interrupt
VICVectAddr = 0; //end of interrupt - dummy write
}

void lcd_string(char *senpoint)
{
while(*senpoint != '\0')
{
lcd_char(*senpoint);
senpoint++;
}
}

void lcd_line1(void){
lcd_cmd(0x80);
}
void lcd_line2(void){
lcd_cmd(0xc0);
}

void lcd_cmd(unsigned char letter)
{
unsigned char temp;
temp=letter;
temp=temp>>4;
lcd_nybble(temp,0);
temp=letter;
temp=temp&0x0f;
lcd_nybble(temp,0);
}

void lcd_char(unsigned char letter)
{
unsigned char temp;
temp=letter;
temp=temp>>4;
lcd_nybble(temp,1);
temp=letter;
temp=temp&0x0f;
lcd_nybble(temp,1);
}

void lcd_nybble(unsigned char nyb,unsigned char rs)
{
int i,dat;
if(rs)
IOSET0 = RS; //set RS pin
else
IOCLR0 = RS; //clear RS pin
dat = nyb; //get the nybble in an int
IOCLR0 = 0x001e0000; //clear D4-D7
IOPIN0 |= dat<<17; //OR the bits in there
strobe_e(); //latch data to LCD
}

void lcd_init(void)
{
delay_ms(500); //settle time delay
lcd_nybble(0x03,0); //reset LCD
strobe_e();
strobe_e();
lcd_nybble(0x02,0);
lcd_cmd(0x28); //set 4-bit mode and 2 lines
lcd_cmd(0x10); //cursor move & shift left
lcd_cmd(0x06); //entry mode = increment
lcd_cmd(0x0e); //display on - cursor blink on
lcd_cmd(0x01); //clear display
}

void lcd_busy(void)
{
IODIR0 &= 0xffefffff; //D7 is input
IOCLR0 = RS; //set RS low
IOSET0 = RW; //set R/W high
IOSET0 = E; //set E high
while(IOPIN0 & busyflag); //wait for busy flag to go low
IOCLR0 = E; //set E low
IOCLR0 = RW; //set R/W low
IODIR0 |= 0x00100000; //D7 is output again
}

void strobe_e(void)
{
IOSET0 = E;
delay_us(1);
IOCLR0 = E;
lcd_busy();
}

void delay_ms(int x)
{
int a,b;
for(a=0;a<x;a++){
for(b=0;b<3500;b++);
}
}

void delay_us(int x)
{
int a,b;
for(a=0;a<x;a++)
for(b=0;b<4;b++);
}

void init(void)
{
PLLCFG=0x24; //set multiplier and divider values
PLLFEED=0xAA;
PLLFEED=0x55;
PLLCON=0x01; //enable PLL
PLLFEED=0xAA;
PLLFEED=0x55;
while(!(PLLSTAT & PLOCK)); //wait for PLL to lock to set frequency
PLLCON=0x3; //connect PLL as clock source
PLLFEED=0xAA;
PLLFEED=0x55;
MAMCR=0x02; //enable MAM
MAMTIM=0x04; //set number of clocks used for flash memory fetch
VPBDIV=0x01; //set peripheral clock (pclk) to system clock (cclk)
}

void FIQ_Routine(void){
while(1);
}
void SWI_Routine(void){
while(1);
}
void UNDEF_Routine(void){
while(1);
}



 

sirc.h

#define PLOCK 0x400
#define E 0x00200000 //bit 21
#define RS 0x00400000 //bit 22
#define RW 0x10000000 //bit 28
#define busyflag 0x00100000 //D7 - bit 20

void beep(void);
void init(void);
void lcd_string(char *);
void lcd_line1(void);
void lcd_line2(void);
void lcd_line3(void);
void lcd_line4(void);
void lcd_cmd(unsigned char);
void lcd_char(unsigned char);
void lcd_nybble(unsigned char,unsigned char);
void lcd_init(void);
void strobe_e(void);
void delay_ms(int);
void delay_us(int);
void isr_capture (void) __attribute__ ((interrupt("IRQ")));
void FIQ_Routine (void) __attribute__ ((interrupt("FIQ")));
void SWI_Routine (void) __attribute__ ((interrupt("SWI")));
void UNDEF_Routine (void) __attribute__ ((interrupt("UNDEF")));