|
|
View previous topic :: View next topic |
Author |
Message |
simonb
Joined: 05 Feb 2007 Posts: 6
|
IRDA via RS232 port without any encoder |
Posted: Tue Sep 25, 2007 9:23 am |
|
|
A few PICs have IRDA support built in however this limits choice if other PICs are more suitable in other respects. Alternatively there are IRDA driver chips that convert RS232 to IRDA but they usually need a BAUD clock which is not available since the RS232 hardware is internal to the PIC. Deriving it from elsewhere is yet another overhead.
If there is no requirement to adhere to IRDA standards then I found a workaround that seems to give me reliable results using a PIC18F2520.
The reason one can't just put RS232 into an IRDA transceiver is because it sends fixed width pulses, not the arbitrary length of pulses required for standard RS232. An IRDA driver chops the serial data into a pulses when transmitting and joins the pulses when receiving. The length of the pulses, for the Vishay TFDU4300 I was using, is specified as being between 1.65 and 3.0 micro seconds.
If we make the BAUD rate 500kbps then the width of each bit is 2us. We just need to ensure that no consecutive pulses are sent and that we account for the wide tolerance of the received pulse width. This is done by only using two bits of every byte sent and masking out the other bits. The Vishay chip requires an inverted TX signal so a simple logic inverter is used to accomplish this.
First is code using the hardware RS232 interface for a 4MHz clock. Note that only certain clock frequencies will divide to make the clock for 500kbps. 4MHz so happens to be one of them. I had no use for non-printable characters in this application so I filter the received codes. It should be easy enough to change this. It sends one line at a time which are null terminated.
All this code does is capture a string and spit it back out 200ms later. This code is pulled from a larger working application. I believe it should work but I haven't included the detail such as setting the fuses etc. I hope it is enough to at least illustrate the principal.
Code: |
/*****************************************************************
IRDA related functions
*****************************************************************/
//Input string buffer length
#define BUFFLEN 100
#define CLOCKFREQ 4M
#use delay(clock=CLOCKFREQ, RESTART_WDT)
#use rs232 (baud=500000, errors, xmit=PIN_C6, rcv=PIN_C7)
//IRDA timeout flag
unsigned int1 gfIrdaTimeout=FALSE;
void dputc(unsigned int8 x) {
delay_us(30);
putc(x);
}
unsigned int8 dgetc() {
unsigned int16 timeout=RXTIMEOUT;
gfIrdaTimeout=FALSE;
while (!kbhit() && (--timeout > 0))
delay_us(5);
if (kbhit())
return(getc());
else
gfIrdaTimeout=TRUE;
return(0);
}
void wait_irda_pre_amble()
{
unsigned int8 j;
do
{
do
{
while (!kbhit()) delay_us(5);
j=getc() & 0b00100100;
}
while (j == 0b00100100);
//output_a(j);
} while (j != 0);
}
unsigned int8 get_irda_line(char *buffer)
{
unsigned int8 i=0,j,retChar;
gfIrdaTimeout=FALSE;
for (i=0; (i < BUFFLEN) && (!gfIrdaTimeout);)
{
retChar=0;
j=dgetc(); if (gfIrdaTimeout) break;
if (bit_test(j,2)) bit_set(retChar,0);
if (bit_test(j,5)) bit_set(retChar,1);
j=dgetc(); if (gfIrdaTimeout) break;
if (bit_test(j,2)) bit_set(retChar,2);
if (bit_test(j,5)) bit_set(retChar,3);
j=dgetc(); if (gfIrdaTimeout) break;
if (bit_test(j,2)) bit_set(retChar,4);
if (bit_test(j,5)) bit_set(retChar,5);
j=dgetc(); if (gfIrdaTimeout) break;
if (bit_test(j,2)) bit_set(retChar,6);
if (bit_test(j,5)) bit_set(retChar,7);
//Mask out all non-printable characters
if ((retChar >= ' ') && (retChar <= '~'))
buffer[i++]=retChar;
//Quit on end of string
if (retChar == 0) break;
}
buffer[i]=0; //Terminate string
return(i);
}
void put_irda_line(char *buffer)
{
unsigned int8 j,k,l;
//TX
//Send pre-amble.
for (j=20; j > 0; --j) dputc(0b11111111);
//End pre-amble
dputc(0b11011011);
delay_us(30);
//Send message
for (k=0; k < BUFFLEN; k++)
{
l=buffer[k];
//Force end of string of reached end of buffer
if (k == (BUFFLEN - 1)) l=0;
//Encode byte
j=0b11011011;
if (bit_test(l,0)) bit_set(j,2);
if (bit_test(l,1)) bit_set(j,5);
dputc(j);
j=0b11011011;
if (bit_test(l,2)) bit_set(j,2);
if (bit_test(l,3)) bit_set(j,5);
dputc(j);
j=0b11011011;
if (bit_test(l,4)) bit_set(j,2);
if (bit_test(l,5)) bit_set(j,5);
dputc(j);
j=0b11011011;
if (bit_test(l,6)) bit_set(j,2);
if (bit_test(l,7)) bit_set(j,5);
dputc(j);
//End of string
if (l == 0) break;
}
}
void main(void)
{
unsigned int8 ubLineRcv[BUFFLEN];
while(TRUE)
{
wait_irda_pre_amble();
get_irda_line(ubLineRcv);
delay_ms(200);
put_irda_line(ubLineRcv);
}
}
|
It is possible to implement IRDA using software RS232 if the clock is running at 40MHz. I had to write my own routines since the compiler-generated code was somewhat off-BAUD rate. Not surprising at 500kbps. This code uses timer1 to implement the RS232 timeout.
Code: |
/*****************************************************************
IRDA related functions
*****************************************************************/
#define CLOCKFREQ 40M
#use delay(clock=CLOCKFREQ, RESTART_WDT)
#define PREAMBLE_UNCHECKED 15 //Number of preable symbols to ignore
#define IRDA_PREAMPLE 20 //Number of pre-amble symbols
#define IRDA_BYTE_DELAY 30 //Length in us of space between symbols
#define IRDA_IN PIN_A4
#define IRDA_OUT PIN_A5
#use fixed_io(a_outputs=IRDA_OUT)
#define BUFFLEN 255 //Max buffer length
//IRDA timeout flag
unsigned int1 gfIrdaTimeout=FALSE;
unsigned int8 irda_timed_getbits(void) {
unsigned int8 retChar;
//Start timer
gfIrdaTimeout=FALSE;
//Set up time-out interrupt timer
set_timer1(0);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
//Wait for byte to arrive or time-out
while (input_state(IRDA_IN) && (!gfIrdaTimeout));
delay_cycles(23); //Delay till middle of first bit
if (input(IRDA_IN)) bit_set(retChar,0); else { delay_cycles(1); bit_clear(retChar,0);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,1); else { delay_cycles(1); bit_clear(retChar,1);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,2); else { delay_cycles(1); bit_clear(retChar,2);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,3); else { delay_cycles(1); bit_clear(retChar,3);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,4); else { delay_cycles(1); bit_clear(retChar,4);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,5); else { delay_cycles(1); bit_clear(retChar,5);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,6); else { delay_cycles(1); bit_clear(retChar,6);}
delay_cycles(13);
if (input(IRDA_IN)) bit_set(retChar,7); else { delay_cycles(1); bit_clear(retChar,7);}
if (gfIrdaTimeout) return(0xFF); else return(retChar & 0b00100100);
}
unsigned int8 irda_getc(void) {
unsigned int8 j, retChar=0;
j=irda_timed_getbits(); if (gfIrdaTimeout) return(0x00);
if (bit_test(j,2)) bit_set(retChar,0);
if (bit_test(j,5)) bit_set(retChar,1);
j=irda_timed_getbits(); if (gfIrdaTimeout) return(0x00);
if (bit_test(j,2)) bit_set(retChar,2);
if (bit_test(j,5)) bit_set(retChar,3);
j=irda_timed_getbits(); if (gfIrdaTimeout) return(0x00);
if (bit_test(j,2)) bit_set(retChar,4);
if (bit_test(j,5)) bit_set(retChar,5);
j=irda_timed_getbits(); if (gfIrdaTimeout) return(0x00);
if (bit_test(j,2)) bit_set(retChar,6);
if (bit_test(j,5)) bit_set(retChar,7);
return(retChar);
}
unsigned int1 irda_wait_preamble(void) {
unsigned int8 j;
//Reset timeout
gfIrdaTimeout=FALSE;
//Allow time to sync. & for IR to adjust it's gain
for (j=0; ((j<PREAMBLE_UNCHECKED) && !gfIrdaTimeout); j++) irda_timed_getbits();
if (gfIrdaTimeout) return(FALSE);
//Wait for correct end of pre-amble
do j=irda_timed_getbits(); while ((j == 0b00100100) && !gfIrdaTimeout);
return((j == 0b00000000) ? TRUE:FALSE);
}
unsigned int8 get_irda_line(char *buffer)
{
unsigned int8 i=0,j;
if (irda_wait_preamble())
{
//Grab string
for (i=0; i<255;)
{
j=irda_getc();
//Mask out non printable chars
if ((j >= ' ') && (J <= '~'))
buff[i++]=j;
//Don't increment if timed out
if (j == 0) break;
}
}
buff[i]=0; //Terminate string
return(i);
}
void irda_put_bits(unsigned int8 putChar)
{
//Designed to output byte at 500kbps with a clock frequency of 40MHz
//The compiler-produced RS232 code was not time-accurate enough
output_low(IRDA_OUT); //Start bit
delay_cycles(14);
output_bit(IRDA_OUT, bit_test(putChar,0));
if (bit_test(putChar,0)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,1));
if (bit_test(putChar,1)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,2));
if (bit_test(putChar,2)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,3));
if (bit_test(putChar,3)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,4));
if (bit_test(putChar,4)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,5));
if (bit_test(putChar,5)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,6));
if (bit_test(putChar,6)) delay_cycles(2);
delay_cycles(11);
output_bit(IRDA_OUT, bit_test(putChar,7));
if (bit_test(putChar,7)) delay_cycles(2);
delay_cycles(12);
output_high(IRDA_OUT); //Stop bit
delay_us(IRDA_BYTE_DELAY);
}
void irda_putc(unsigned int8 putChar) {
unsigned int8 j;
//Encode byte
j=0b11011011;
if (bit_test(putChar,0)) bit_set(j,2);
if (bit_test(putChar,1)) bit_set(j,5);
irda_put_bits(j);
j=0b11011011;
if (bit_test(putChar,2)) bit_set(j,2);
if (bit_test(putChar,3)) bit_set(j,5);
irda_put_bits(j);
j=0b11011011;
if (bit_test(putChar,4)) bit_set(j,2);
if (bit_test(putChar,5)) bit_set(j,5);
irda_put_bits(j);
j=0b11011011;
if (bit_test(putChar,6)) bit_set(j,2);
if (bit_test(putChar,7)) bit_set(j,5);
irda_put_bits(j);
}
void preample_irda(void) {
unsigned int8 i;
//Send pre-amble
for (i=IRDA_PREAMPLE; i > 0; --i) {
irda_put_bits(0b11111111);
}
//End of pre-amble
irda_put_bits(0b11011011);
}
put_irda_line(char *buff)
{
unsigned int8 j;
preample_irda();
for (j=0;; j++)
{
irda_putc(buff[j]);
if (buff[j] == 0) break;
}
}
#int_timer1
void timer1_int(void) {
//Flag timeout and disable timer
gfIrdaTimeout=TRUE;
setup_timer_1(T1_DISABLED);
}
void main(void)
{
unsigned int8 ubLineRcv[BUFFLEN];
setup_timer_1(T1_DISABLED);
enable_interrupts(INT_TIMER1);
enable_interrupts(global);
while(TRUE)
{
wait_irda_pre_amble();
get_irda_line(ubLineRcv);
delay_ms(200);
put_irda_line(ubLineRcv);
}
}
|
Last edited by simonb on Mon Dec 10, 2007 9:40 am; edited 6 times in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Oct 01, 2007 11:12 am |
|
|
Simon,
You need to fix the code in your post. Notice how the for()
loop is messed up:
Code: |
//Allow time to sync. & for IR to adjust it's gain
for (j=0; ((j<PREAMBLE_UNCHECKED> 0; --i) {
|
See this post at the top of the Code Library for help in fixing this problem:
http://www.ccsinfo.com/forum/viewtopic.php?t=32189 |
|
|
simonb
Joined: 05 Feb 2007 Posts: 6
|
|
Posted: Tue Nov 13, 2007 4:43 am |
|
|
Thanks for pointing out the problem. It wasn't "Disable HTML" it was just a bad copy/paste operation. Somehow the "put" code got left out too. Should make more sense now. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Nov 13, 2007 9:53 am |
|
|
Quote: | for (i=0; (i <BUFFLEN>= ' ') && (retChar <buffer> 0; --j) dputc(0b11111111); |
It's still messed up. Please copy and pasted it in again. |
|
|
simonb
Joined: 05 Feb 2007 Posts: 6
|
|
Posted: Mon Dec 10, 2007 9:36 am |
|
|
PCM programmer wrote: | Quote: | for (i=0; (i <BUFFLEN>= ' ') && (retChar <buffer> 0; --j) dputc(0b11111111); |
It's still messed up. Please copy and pasted it in again. |
Ugh. This is like trying to herd cats. Hopefully it is fixed now. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Dec 10, 2007 12:05 pm |
|
|
That got rid of the HTML tag errors. But I did a test compile and there
are some compiler errors.
In a couple places in your code, you call this routine:
Quote: | wait_irda_pre_amble(); |
But the function is actually declared with a different name:
Quote: | void wait_irda_pre_amble()
{ |
In this function, you have a parameter called "buffer":
Quote: | unsigned int8 get_irda_line(char *buffer) |
But inside the routine, the variable is called 'buff'.
Quote: | buff[i++]=j;
//Don't increment if timed out
if (j == 0) break;
}
}
buff[i]=0; //Terminate string
|
The constant 'RXTIMEOUT' is not defined:
Quote: | unsigned int16 timeout=RXTIMEOUT; |
The compiler won't accept the 500K baud rate with a 4 MHz oscillator.
Quote: | #define CLOCKFREQ 4M
#use delay(clock=CLOCKFREQ, RESTART_WDT)
#use rs232 (baud=500000, errors, xmit=PIN_C6, rcv=PIN_C7) |
I'm going to quit at this point. I'm not going to worry about it anymore. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|