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

115200 to 57600 conversion

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



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

115200 to 57600 conversion
PostPosted: Thu Aug 25, 2005 6:53 pm     Reply with quote

I am using a 16F88 to buffer the 115200 baud output of a device. The PIC receives data at 115200 baud and then transmits it to a PC at 57600 baud. HW details: PIC running at 18.432Mhz, the 115200 is going into the hardware UART, and I am using a software UART at 57600 to send to the PC.

I am using INT_RDA to store the incoming bytes into a 64 byte circular buffer. There is a loop in main() that checks for new data in the buffer and transmits it if found at the slower rate.

Since the hardware UART has a 2 byte FIFO plus the RSR this gives almost 3 bytes of hardware buffering. I figured that would be enough to cover the slower xmit rate, but I knew the code would have to be very tight.

In my test case, I send 20 bytes with no delays and the PIC is dropping 3 bytes at 57600. So just for grins I raised the xmit rate and at 65000 baud no bytes were dropped. (I don't plan on using 65000 baud, just wanted to see how high I had to go to not drop bytes.)

I instrumented the interrupt handler and the fputc() command to toggle output pins and looked at the timing on a DSO. PIN_B4 for the fputc() shows a time of 213uS. Transmitting a byte at 57600 should take only 174 uS, so I am loosing 39 uS somewhere.

Questions:

1. Is 115200 to 57600 feasible? There seems to be more overhead than I expected. Would a hand written ASM output routine be efficient enough?

2. I can go to 20Mhz and gain another 8.5% of clock speed. Worthwhile? An extra 8.5% gain in clock speed dosen't seem enough.

3. I am using the 16F88 because I am familiar with it, but would consider going to a 40Mhz 18F if that would work.

Code:
include <16F88.h>

#fuses NOWDT, NOPROTECT, NOLVP, HS
#use standard_io(A)
#use standard_io(B)
#use delay(clock=18432000)
#use rs232(FORCE_SW, INVERT, BAUD=57600, XMIT=PIN_A0, RCV=PIN_A1, DISABLE_INTS, PARITY=N, BITS=8, STREAM=HOST)
#use rs232(BAUD=115200, XMIT=PIN_B5, RCV=PIN_B2, PARITY=N, BITS=8, ERRORS, STREAM=DUT)

#define BUF_SIZE 64
char b0[BUF_SIZE];
int in_ptr = 0;
int out_ptr = 0;



#int_RDA
void RDA_isr()
{
  char inchar;
  output_high(PIN_B7);
  inchar = fgetc(DUT);
  if (inchar) {                       //ignore nulls
    b0[in_ptr] = inchar;
    in_ptr = (in_ptr + 1) % 64;
    }
  output_low(PIN_B7);
}



void main(){
  output_low(PIN_B7);
  output_low(PIN_B4);
  enable_interrupts(INT_RDA);
  enable_interrupts(global);
  while(TRUE) {
   if (out_ptr != in_ptr) {
     output_high(PIN_B4);
     fputc(b0[out_ptr], HOST);
     output_low(PIN_B4);
     out_ptr = (out_ptr + 1) % 64;
      }
  }
}

Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Thu Aug 25, 2005 7:51 pm     Reply with quote

Quote:
1. Is 115200 to 57600 feasible? There seems to be more overhead than I expected. Would a hand written ASM output routine be efficient enough?


If you are transmitting continously, then the obivous answer would be no.

The amount of time it takes to receive 64 bytes must be larger than the amount of time it takes to transmit the data. That means you will need on average 1 character time of dead time.

I suspect that interrupts have to be disabled to transmit. Since two bytes can be received during the transmission and then account for the interrupt overhead plus the transmit overhead and you've got problems. You would be better off polling the hardware than trying to use the int.
Code:

#fuses NOWDT, NOPROTECT, NOLVP, HS
#use standard_io(A)
#use standard_io(B)
#use delay(clock=18432000)
#use rs232(FORCE_SW, INVERT, BAUD=57600, XMIT=PIN_A0, RCV=PIN_A1, DISABLE_INTS, PARITY=N, BITS=8, STREAM=HOST)
#use rs232(BAUD=115200, XMIT=PIN_B5, RCV=PIN_B2, PARITY=N, BITS=8, ERRORS, STREAM=DUT)

#define BUF_SIZE 64

void main(void)
{

  int in_ptr = 0;
  int out_ptr = 0;
  char inchar;
  char b0[BUF_SIZE];

  while(TRUE)
  {
    while (kbhit())
    {
      inchar = fgetc(DUT);
      //ignore nulls
      if (inchar)
      {                           
        b0[in_ptr] = inchar;
        in_ptr++;
        // Limit the buffer size to 64
        bit_clear(in_ptr,6);
      }
    }
    if (out_ptr != in_ptr)
    {
      fputc(b0[out_ptr], HOST);
      out_ptr++;
      // Limit the buffer size to 64
      bit_clear(out_ptr,6);
    }
  }
}

ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Fri Aug 26, 2005 7:51 am     Reply with quote

Mark,
Can you explain why you are ignoring the incomming nulls? If the incomming data stream is binary, then I think it is incorrect to throw away incomming null data.
bkamen



Joined: 07 Jan 2004
Posts: 1615
Location: Central Illinois, USA

View user's profile Send private message

Re: 115200 to 57600 conversion
PostPosted: Fri Aug 26, 2005 8:19 am     Reply with quote

Barney wrote:

3. I am using the 16F88 because I am familiar with it, but would consider going to a 40Mhz 18F if that would work.


An 18F would certainly work better as the 18F6520 and 8520 both come with
(2) AUSART's. HOWEVER, because you are converting a faster rate to a slower one, you MUST have some sort of handshaking going on with the faster host or you will lose bytes if the transmit is sending for a period longer than the buffer can hold while sending at the 1/2 speed on the "output" port at 57600.

But and 18F w/2 hardware serial ports would be a better fit.

-Ben
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Fri Aug 26, 2005 8:37 am     Reply with quote

ckielstra wrote:
Mark,
Can you explain why you are ignoring the incomming nulls? If the incomming data stream is binary, then I think it is incorrect to throw away incomming null data.


Look at his code. He was doing that and had a comment about it thus I did as well for him. Normally I wouldn't have and indeed it takes a couple of extra instructions.
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

Amplifications
PostPosted: Fri Aug 26, 2005 10:17 am     Reply with quote

1. The data comes in bursts so the 64 byte buffer should be long enough to handle the situation. I agree that the INT_RDA must be disabled while using a software UART to transmit so I am using the DISABLE_INT parameter in the #use rs232(). I also tried bracketing the fputc() with disable, enable interrupt commands and it worked about the same.

2. I am ignoring nulls because the cretin that designed the external device uses the TX pin for other things, like an index pulse. The index pulse causes the PIC UART to see <NULL> characters. The real data is ASCII text so throwing away the <NULL>s is OK.

3. I tried polling with kbhit() and it was even worse for dropping characters.

Using the bit_clear() function to implement the circular buffer is cute. Any idea on how much time it saves?

4. No handshaking available. The PIC has to be able to keep up with 115200 baud characters. Yeah the design bites, but that is what I have to deal with.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Fri Aug 26, 2005 10:42 am     Reply with quote

So you tried the code that I posted and it dropped more chars?
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

PostPosted: Fri Aug 26, 2005 2:08 pm     Reply with quote

Yes I tried your code. It was very similar to a polling routine I had tried earlier and both were signifcantly worse than the interrupt version.

I think this mid-line CPU just doesn't have the horsepower to do the job reliably so I am moving up the food chain. I found some 18F6227 with dual EUSARTs that should give me a lot more head room.

I am not familiary with the 18F6XXX series, in particular the data addressing. How big a byte array will the CCS compiler support on this chip?
newguy



Joined: 24 Jun 2004
Posts: 1908

View user's profile Send private message

PostPosted: Fri Aug 26, 2005 2:33 pm     Reply with quote

Barney wrote:
I am not familiary with the 18F6XXX series, in particular the data addressing. How big a byte array will the CCS compiler support on this chip?


Can't comment on the 18F6xxx series, but I have a buffer of 500 integers running on a 18F4610 with no issues.
Ttelmah
Guest







PostPosted: Sat Aug 27, 2005 5:20 am     Reply with quote

The dual UART chip, will get rid of the problem, provided the data really can be shifted out in the time. However looking at it, I'd suspect you might actually get better results using the two UARTs the other way round!. This is the reverse to what seems 'good logic', since using the hardware to handle the faster rate makes good sense, but if you are running the 'polling' approach, using the software input for the higher rate, potentially allows the slower data to be sending while the faster data is being received. If you reversed the UARTS, and then used the polling code posted, but modified with:
Code:

#fuses NOWDT, NOPROTECT, NOLVP, HS
#use standard_io(A)
#use standard_io(B)
#use delay(clock=18432000)
#use rs232(INVERT, BAUD=115200, XMIT=PIN_A0, RCV=PIN_A1, PARITY=N, BITS=8, STREAM=DUT)
#use rs232(BAUD=57600, XMIT=PIN_B5, RCV=PIN_B2, PARITY=N, BITS=8, ERRORS, STREAM=HOST)

#define BUF_SIZE 64
#bit TRMT=0x98.1
#byte TXREG=0x19

void main(void)
{

  int in_ptr = 0;
  int out_ptr = 0;
  char inchar;
  char b0[BUF_SIZE];

  while(TRUE)
  {
    if (kbhit(DUT))
    {
      inchar = fgetc(DUT);
      //ignore nulls
      if (inchar)
      {                           
        b0[in_ptr] = inchar;
        in_ptr++;
        // Limit the buffer size to 64
        bit_clear(in_ptr,6);
      }
    }
    if (TRMT)
        if (out_ptr != in_ptr) {
          TXREG=b0[out_ptr];
          out_ptr++;
          // Limit the buffer size to 64
          bit_clear(out_ptr,6);
    }
  }
}

The key here, is that you sit in a loop, effectively polling for the start bit. If a start bit is found, you use the sofware UART to retrieve the character. Whenever the start bit is not present, you test for the hardware holding register being empty, then if it is, check if any data is waiting to send, and put it directly into the register if it is. The longest time in this loop, would be in this case. The slowest thing will be the handling of the array access. I'd suspect the loop time would be in the order of 25 instructions. If so, the worst case 'error' on finding the start bit, would only be about 6uSc, and this _might_ just about work. You might make this work reliably, by a really 'brutal' piece of programming (ugly/bulky), using this approach:
Code:

    if (TRMT)
        if (out_ptr != in_ptr) {
          switch (out_ptr) {
          case 0:
              TXREG=b0[0];
              break
          case 1:
              TXREG=b0[1];
              break;
//repeat for 62 more cases!...

          }
          out_ptr++;
          // Limit the buffer size to 64
          bit_clear(out_ptr,6);
    }

Provided you have no default statement, this should be coded by the compiler as a 'jump table'. Though this takes a while to set up, it means that each access to the array, will be hard coded, rather than using a variable. Potentially this _may_ be quicker than the array access!.

Best Wishes
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

PostPosted: Mon Aug 29, 2005 8:09 am     Reply with quote

Interesting out of the box thinking. While I am waiting for the 18F parts, I will give it a whirl and report on the results.

I also realized that the UART TX section is sitting idle. Right now, all of the communication is one way, so I could use the set_uart_speed to switch back and forth between the different receive and xmit rates. Any words of wisdom?
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Mon Aug 29, 2005 9:12 am     Reply with quote

Barney wrote:
Interesting out of the box thinking. While I am waiting for the 18F parts, I will give it a whirl and report on the results.

I also realized that the UART TX section is sitting idle. Right now, all of the communication is one way, so I could use the set_uart_speed to switch back and forth between the different receive and xmit rates. Any words of wisdom?


No you can't unless you receive a "packet" of data and can transmit this "packet" before the next "packet" starts to be received. There are not separate baud rate generators for the rx and tx. If there were, then you could do what you stated.
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

PostPosted: Mon Aug 29, 2005 9:48 am     Reply with quote

Right, bad idea. Back to the drawing board.
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

PostPosted: Tue Aug 30, 2005 10:03 am     Reply with quote

I tried a couple of other ideas and they didn't work. Sadly, I have to conclude that the 16F88 doesn't have the horsepower to do the job reliabily.

1. The 115200 software UART: Looking at the timing requirements, it should have worked, but didn't. I cut the polling loop to just the kbdhit() and fgetc() and toggled an output bit before and after the fgec(). I didn't even store the data or output it. The oscilloscope shows a lot of data is being missed. Don't know why.

2. External Interrupt: Since the software UART was missing bytes, I thought to use the EI to "kick start" it by triggering on a falling signal to detect the start bit. There is a 8.5 uSec delay between the falling signal and a bit toggle at the start of the interrupt handler. This means the interrupt handler misses the entire start bit at 115200 baud. I could maybe write a custom routine to pick up the 8 data bits, but that seems too hokey.

So now I am waiting for some 18FXXX parts with dual H/W UARTS & a PCH compiler. My new motto is "Do it in hardware".

Thanks for the suggestions.
Barney



Joined: 18 Oct 2004
Posts: 41
Location: Newark, CA

View user's profile Send private message

PostPosted: Fri Sep 16, 2005 10:31 am     Reply with quote

The 18F6627 with the dual UARTS works like a champ. No more dropped characters. I set it up with a 2KB buffer and just for grins ran some tests to see how much buffer was required for various output bauds. These results depend on the burst timing characteristics of my incoming data (YMMV). When graphed it shows a pretty good quadratic curve. In each case a total of 519 bytes was received at 115KB. Since I can't figure out a way to do tables, the data is ordered pairs of:

Xmit Rate:Max #Chars Buffered

9600:245
14400:218
19200:190
28800:150
38400:127
57600:93
115200:2
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