CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

IRDA via RS232 port without any encoder

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
simonb



Joined: 05 Feb 2007
Posts: 6

View user's profile Send private message

IRDA via RS232 port without any encoder
PostPosted: Tue Sep 25, 2007 9:23 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Oct 01, 2007 11:12 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Nov 13, 2007 4:43 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Nov 13, 2007 9:53 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Dec 10, 2007 9:36 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Dec 10, 2007 12:05 pm     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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