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

Passing serial messages between UARTs (PIC24F)

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



Joined: 19 Dec 2012
Posts: 43
Location: Connecticut, USA

View user's profile Send private message

Passing serial messages between UARTs (PIC24F)
PostPosted: Tue Feb 11, 2014 1:40 pm     Reply with quote

Hi all.

I've got an interesting conundrum I've been working on and I need some help on getting the UARTs working properly.

I am using a PIC24FV32KA304 on PCWHD 4.132. The clock is a 18.432MHz crystal.

I need to take any serial communication coming in from UART1 and pass it through to UART2 and do the same thing from UART2 to UART1.
In short, I need to connect the device on UART1 to UART2 with my board as a "middleman" of sorts.

The issue is that the baud rate is pretty quick at 115200 baud. It's a serial programming stream about 1MB in size, so it's sending a constant stream of characters for a relatively extended amount of time (about 90 seconds is typical). My normal way of accomplishing this would be to take the received character in the RDA_ISR and put it out via fputc(). My code for this is below:
Code:

#USE RS232 (STREAM=PCCTRL, BAUD=115200, BITS=8, PARITY=N, XMIT=PIN_B0, RCV=PIN_B1, ERRORS)
#USE RS232 (STREAM=DVCTRL, BAUD=115200, BITS=8, PARITY=N, UART1, ERRORS)

#int_RDA
void RDA_isr(void)
{
#USE FAST_IO(A)
#USE FAST_IO(B)
   char c;
   restart_wdt();
   c = fgetc(DVCTRL);
   fputc(c,PCCTRL);
}

#INT_RDA2
void RDA2_isr(void)
{
#USE FAST_IO(A)
#USE FAST_IO(B)
   char c;
   restart_wdt();
      c = fgetc(PCCTRL);
      fputc(c,DVCTRL);
}


The problem with this is after a short amount of time, the transmissions begin to fail resulting in a bad flash of the device on the other end. My assumption is that in the time it takes to jump to the ISR, read the character, and put it out over the other UART, additional character(s) have come in and are possibly overflowing the buffer or otherwise causing issues with the timing of the messages.

My new idea is to try and trim out the fat and just put my code into a barebones loop that simply looks for data on the RX line of each UART and places any bytes onto the TX buffer of the other UART. my code for this is not working and I need some help figuring out why:
Code:

//Disables interrupts, hangs in passthrough loop
void dv_comm_loop(void){
               char pc_byte = 0;
               char dv_byte = 0;
               DV_Pgm = TRUE;
               disable_interrupts(INT_TIMER1);
               disable_interrupts(INT_TIMER4);
               disable_interrupts(INT_TIMER5);
               disable_interrupts(INT_RDA2);
               disable_interrupts(INT_RDA);
               UTXEN1 = 1;
               UTXEN2 = 1;
               
               while(DV_Pgm){
                  if (URXDA1 == 1){
                     pc_byte = U2RXREG;
                     U2TXREG = pc_byte;
                  }
                 
                  if (URXDA2 == 1){          //is there a received byte?
                     dv_byte = U1RXREG;   //Copy received byte
                     U1TXREG = dv_byte;   //Put character into TX register to PC
                  }
               }
}


From what I can see from the spec of this chip, reading the RXREG of the UART should clear the byte from RXREG. Does the same thing occur for TXREG? Do I need to clear it manually or will the MCU clear it as it is transmitted out?

Is something like this even possible without having issues with timing and buffer overflow?

Thanks in advance for any help!
jeremiah



Joined: 20 Jul 2010
Posts: 1355

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 1:52 pm     Reply with quote

Check the assembly, but I would guess your original issue was with the fputc() calls in the interrupts. Those would probably cause unwanted delays in the receive interrupts which would propagate over time. Since the PIC24's have a 4 character buffer, when they fill, fputc typically waits for the buffer to have space before finishing, which would delay your ISR from receiving more data. Also, check the errata on that chip. I seem to remember there being some TX related issues.

Have you tried a set of buffered serial receive ISRs with the main generating the output streams after reading the buffers (or possibly interrupt driven TX as well)?

The ex_sisr.c file that comes with your compiler in the example's folder shows a pretty decent example of how to do buffered serial receive ISR. The only thing to watch for is they use a % operator to handle the indexing. This is only fast for buffer sizes that are powers of two. For other buffer sizes, you would need to use a different approach.

The idea is you make a buffer just large enough to give you time to transmit out everything before the buffer fills up.
KTrenholm



Joined: 19 Dec 2012
Posts: 43
Location: Connecticut, USA

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 2:02 pm     Reply with quote

jeremiah wrote:
Check the assembly, but I would guess your original issue was with the fputc() calls in the interrupts. Those would probably cause unwanted delays in the receive interrupts which would propagate over time. Since the PIC24's have a 4 character buffer, when they fill, fputc typically waits for the buffer to have space before finishing, which would delay your ISR from receiving more data. Also, check the errata on that chip. I seem to remember there being some TX related issues.

Have you tried a set of buffered serial receive ISRs with the main generating the output streams after reading the buffers (or possibly interrupt driven TX as well)?

The ex_sisr.c file that comes with your compiler in the example's folder shows a pretty decent example of how to do buffered serial receive ISR. The only thing to watch for is they use a % operator to handle the indexing. This is only fast for buffer sizes that are powers of two. For other buffer sizes, you would need to use a different approach.

The idea is you make a buffer just large enough to give you time to transmit out everything before the buffer fills up.


I had considered buffering the data but I don't think I have enough memory to handle buffering any significant part of the stream. The stream is at least 1MB of data.

If I'm thinking about this correctly, there is no way for me to "catch up" on the data if it's a constant stream. I can only fall behind. A buffer might give me some additional time but unless the procedure of taking in characters and sending them out is quick enough, there's going to be an overflow of the buffer eventually with this amount of data.

Ideally I want to be able to take in a character and have it sending out before the next one comes in. But I don't know if that's feasible.


Last edited by KTrenholm on Tue Feb 11, 2014 2:11 pm; edited 1 time in total
temtronic



Joined: 01 Jul 2010
Posts: 9244
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 2:11 pm     Reply with quote

I've used the ex_sisr.c example at 115k200 and have never lost a byte of data doing what you want to do. in-uart1---->out-uart2 using the 18F46k22.I use a 128 byte buffer.Depending on what else the PIC has to do,you might need a bigger buffer.

Always test with known data though, just because it 'looks' like it works doesn't mean it really does!

hth
jay
jeremiah



Joined: 20 Jul 2010
Posts: 1355

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 2:30 pm     Reply with quote

As an additional note, you shouldn't need to buffer the entire 1MB data, just enough of it to keep up.

In your previous example, you were losing time because you were delaying the interrupt. If you take the fputc calls out of the ISR and use buffered serial input, then those will run faster than the incoming serial data. From that point, your main just needs to get data out faster than it can service the buffered data. That is more dependent on your other non serial tasks in main.

I have no clue what the rest of your program does, so as an example, say that your program does 20ms worth of processing not related to the serial in/out. That means your buffer needs to be large enough to cover 20ms + a little bit to handle the time it takes to move data from the software buffer to the hardware UART (not very long). In that case, you would want a buffer that is higher than 231 bytes (20ms of down time at 115200 BPS...10bits per byte). If your processing in main takes only 10ms, it would take a buffer only half the size.

On the flip side if your program main does nothing else but output the buffered serial data, then the buffer doesn't need to be very large at all (maybe 16 bytes? just guessing there).

EDIT: alternately, if your main is too slow to have a big enough buffer, you can try interrupt driven transmits as well. That would reduce your overall buffer needs in a really slow main() scenario.

You are right in that you do not have a lot of RAM. Those chips are not really designed for high RAM apps. They are designed for low low power and very very small board space applications. I use the F versions of these a lot. You aren't going to be making use of the super low power since you are using UART and you cannot perform UART in sleep mode. Other chips have similar sizes (aside from needing an external EEPROM).

If you are truly worried about RAM, you might consider a different chip. The PIC24FJ256GA106 has around 16k RAM if I remember right (maybe it is 8k, but you can verify either way). Having sub 2k RAM on that chip is very limiting.
KTrenholm



Joined: 19 Dec 2012
Posts: 43
Location: Connecticut, USA

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 2:36 pm     Reply with quote

jeremiah wrote:
As an additional note, you shouldn't need to buffer the entire 1MB data, just enough of it to keep up.

In your previous example, you were losing time because you were delaying the interrupt. If you take the fputc calls out of the ISR and use buffered serial input, then those will run faster than the incoming serial data. From that point, your main just needs to get data out faster than it can service the buffered data. That is more dependent on your other non serial tasks in main.

I have no clue what the rest of your program does, so as an example, say that your program does 20ms worth of processing not related to the serial in/out. That means your buffer needs to be large enough to cover 20ms + a little bit to handle the time it takes to move data from the software buffer to the hardware UART (not very long). In that case, you would want a buffer that is higher than 231 bytes (20ms of down time at 115200 BPS...10bits per byte). If your processing in main takes only 10ms, it would take a buffer only half the size.

On the flip side if your program main does nothing else but output the buffered serial data, then the buffer doesn't need to be very large at all (maybe 16 bytes? just guessing there).

You are right in that you do not have a lot of RAM. Those chips are not really designed for high RAM apps. They are designed for low low power and very very small board space applications. I use the F versions of these a lot. You aren't going to be making use of the super low power since you are using UART and you cannot perform UART in sleep mode. Other chips have similar sizes (aside from needing an external EEPROM).

If you are truly worried about RAM, you might consider a different chip. The PIC24FJ256GA106 has around 16k RAM if I remember right (maybe it is 8k, but you can verify either way). Having sub 2k RAM on that chip is very limiting.


Thanks for the explanation. Hopefully I can buffer the receive lines of both UARTS (since I need to echo transmissions on both ends) and eventually get something going. The good news is that the system needs to be power cycled after this transfer is done so I can pretty much minimize other operations as much as I want. The only thing I would really need to keep going is the PWM module.
jeremiah



Joined: 20 Jul 2010
Posts: 1355

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 2:47 pm     Reply with quote

That can run automatically if you use the OCx pins. Check the output compare section for the PWM.
Gabriel



Joined: 03 Aug 2009
Posts: 1067
Location: Panama

View user's profile Send private message

PostPosted: Tue Feb 11, 2014 3:06 pm     Reply with quote

Is flow control not an option?
_________________
CCS PCM 5.078 & CCS PCH 5.093
KTrenholm



Joined: 19 Dec 2012
Posts: 43
Location: Connecticut, USA

View user's profile Send private message

PostPosted: Wed Feb 12, 2014 8:14 am     Reply with quote

Nah can't do flow control. This application needs to drop right into an existing environment which doesn't use it.

I did get it working though!
At the reception of a serial command the program drops into a limited process loop that keeps some of the attached buttons responsive and passes all transmissions from UART2 to UART1 (and vice-versa) via 2 buffers. The whole stream went through no problem. Pretty much followed the EX_SISR.C example as recommended earlier.

Code:


#INT_RDA2
void RDA2_isr(void)
{
int t
      pc_buffer[pc_next_in]=fgetc(PCCTRL);
      t=pc_next_in;
      pc_next_in=(pc_next_in+1) % BUFFER_SIZE;
      if(pc_next_in==pc_next_out)
         pc_next_in=t;           // Buffer full !!
}

#INT_RDA
void RDA_isr(void)
{
int t
      dv_buffer[dv_next_in]=fgetc(DVCTRL);
      t=dv_next_in;
      dv_next_in=(dv_next_in+1) % BUFFER_SIZE;
      if(dv_next_in==dv_next_out)
         dv_next_in=t;           // Buffer full !!
}

void comm_loop(void){
               DV_Pgm = TRUE;
               setup_uart(115200,DVCTRL);
               disable_interrupts(INT_TIMER1);
               disable_interrupts(INT_TIMER4);
               disable_interrupts(INT_TIMER5);
               enable_interrupts(INT_RDA2);
               enable_interrupts(INT_RDA);
               enable_interrupts(INTR_GLOBAL);

               while (DV_Pgm){
               if (dv_bkbhit)
                  fputc(dv_bgetc(),PCCTRL);
               
               if (pc_bkbhit)
                  fputc(pc_bgetc(),DVCTRL);
               }
}

/*When called, returns next character from the DV buffer*/
BYTE dv_bgetc() {
   BYTE c;

   while(!dv_bkbhit) ;
   c=dv_buffer[dv_next_out];
   dv_next_out=(dv_next_out+1) % BUFFER_SIZE;
   return(c);
}

/*When called, returns next character from the PC buffer*/
BYTE pc_bgetc() {
   BYTE c;
   while(!pc_bkbhit) ;
   c=pc_buffer[pc_next_out];
   pc_next_out=(pc_next_out+1) % BUFFER_SIZE;
   return(c);
}

jeremiah



Joined: 20 Jul 2010
Posts: 1355

View user's profile Send private message

PostPosted: Wed Feb 12, 2014 8:37 am     Reply with quote

Just remember to keep BUFFER_SIZE a power of 2 (and make sure that is highlighted in your comments next to the #define). The % operator takes a long time to execute otherwise and could slow you down too much.
KTrenholm



Joined: 19 Dec 2012
Posts: 43
Location: Connecticut, USA

View user's profile Send private message

PostPosted: Wed Feb 12, 2014 8:40 am     Reply with quote

jeremiah wrote:
Just remember to keep BUFFER_SIZE a power of 2 (and make sure that is highlighted in your comments next to the #define). The % operator takes a long time to execute otherwise and could slow you down too much.


Thanks for the tip.
I kept the buffer size at 32 bytes like it was in the example and it looks to be enough.
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