View previous topic :: View next topic |
Author |
Message |
FoxtrotMike
Joined: 31 May 2007 Posts: 11
|
Help: How to receive serial data without losses |
Posted: Tue Jul 17, 2007 12:30 pm |
|
|
Hi:
I'm working on an application that requires to receive serial data, process it (som XOR operations), and then send it back. Also, i need to send a synchrony sequence in between, but without losing any bytes. This is my attempt at coding:
Code: |
#include <18F252.h>
#fuses HS,NOWDT,NOPROTECT
#use delay(clock=20000000)
#use rs232(baud=9600, parity=N, bits=8, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#define nop delay_cycles(1) // nop;
#byte PORTB = 0x0F81
int sync = 0, fullb = 0;
int data = 0;
int i = 0, j = 0, k = 0;
int inbuff[4],outbuff[4];
int32 ctr = 0;
#INT_RDA
void serial_isr()
{
inbuff[i] = getc();
i++;
if(i==4)
{
i = 0;
outbuff[0] = inbuff[0];
outbuff[1] = inbuff[1];
outbuff[2] = inbuff[2];
outbuff[3] = inbuff[3];
fullb = 1;
}
}
void main(void)
{
enable_interrupts(GLOBAL);
enable_interrupts(INT_RDA);
for(i=0;i<4;i++)
{
inbuff[i] = 0;
outbuff[i] = 0;
}
i = 0;
while(1)
{
nop;
if(sync == 1)
{
k++;
putc(0xFF);
putc(0xFF);
putc(0x00);
putc(k);
sync = 0;
}
if(fullb == 1)
{
putc(outbuff[j] ^ 0x20);
ctr++;
j++;
if(j==4)
{
j = 0;
fullb = 0;
}
if(!(ctr%16))
sync = 1;
}
}
}
|
·Basically, i should receive this sequence
Code: |
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H
|
and then send back this sequence:
Code: |
a b c d e f g h i j k l m n o p (0xFF) (0xFF) (ctr1) (ctr2) q r s t u v w x y z a b c d e f (0xFF) (0xFF) (ctr1) (ctr2)
|
*Last 2 bytes (G,H) should be stored until another 2 bytes are received.
·I've got two problems:
*When i simulate in MPLAB, it starts alright, but suddenly it stops transmitting (TXREG registers does not update!).
*When programming a PIC and running a serial terminal, the 4 byte routine works just fine, but the 16 byte sync secuence causes to lose 4 bytes. This is specially true when sending a sequence longer than 16 bytes.
·If anyone can give me a hand at this, i'll really appreciate it. I need to put that sync sequence in between, but i can't lose data.
Best regards,
Foxtrot Mike |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jul 17, 2007 4:01 pm |
|
|
Basic suggestion -- use a real receive buffer.
See the CCS example file, Ex_sisr.c. Here's the file location:
Quote: | c:\Program Files\PICC\Examples\Ex_sisr.c |
|
|
|
FoxtrotMike
Joined: 31 May 2007 Posts: 11
|
|
Posted: Tue Jul 17, 2007 7:36 pm |
|
|
Thanks... I'll look it up |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Wed Jul 18, 2007 11:54 am |
|
|
In general what your doing looks ok. But what happens if your the number of incoming bytes is not divisible by 4?. Then your 4 byte buffer will never be filled and you wont get those ones back - is this what you saw in the sim? |
|
|
Bill Boucher
Joined: 04 Feb 2005 Posts: 34 Location: Chatham,ON,CA
|
|
Posted: Wed Jul 18, 2007 7:04 pm |
|
|
I was reading somewhere that the compiler will automatically disable interrupts while doing certain functions that are timing sensitive such as putc(). That said, when the program tries to transmit 4 bytes in a row using sequential putc() lines, could it be that interrupts are off throughout them and that the incoming data interrupt is not being immediately serviced and so the 2-byte receive buffer overflows?
I didn't look at the 18F252 datasheet to check the uart pin numbers but I assume you're using the hardware Tx & Rx pins. The Tx buffer is also 2 bytes deep so when the four putc() lines run, the first 2 execute very fast because all they do is load the bytes into the hardware buffer, but the next 2 putc() lines each have to wait until the buffer is ready to accept more data, that is until the first 2 bytes are done transmitting. That causes a couple ms delay. Could a byte or two being received during this time be dropped? I wonder.
If you create a r_buffer[] array var, and a t_buffer[] array var, then set up the int_rda and int_tbe interrupts with code that simply moves hardware-received data into r_buffer[] and data from the t_buffer[] to the hardware-transmit register (using putc) just one byte at a time when the interrupts occur, then you won't lose any data. Pointers are used to tell the ISR's where to read/write data from/to.
Example:
Code: |
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, Parity=N, Bits=8, ERRORS)
//-----------------------------------------------------
// constants and vars used for commport communications:
#define R_BUFFER_SIZE 32
byte r_buffer[R_BUFFER_SIZE];
byte r_next_in = 0;
byte r_next_out = 0;
#define T_BUFFER_SIZE 32
byte t_buffer[T_BUFFER_SIZE];
byte t_next_in = 0;
byte t_next_out = 0;
//-----------------------------------------------------
// commport communications: buffered, interrupt & hardware driven
#INT_TBE
void t_serial_isr() {
putc(t_buffer[t_next_out]);
t_next_out = (t_next_out + 1) % T_BUFFER_SIZE;
if(t_next_in == t_next_out)
{
disable_interrupts(int_tbe);
}
}
// bputc() modified to prevent lockups:
void bputc(char c) {
short restart;
int ni;
restart = t_next_in == t_next_out;
t_buffer[t_next_in] = c;
ni=(t_next_in + 1) % T_BUFFER_SIZE;
while(ni == t_next_out)
{
enable_interrupts(int_tbe); //make sure int_tbe is enabled to provide exit (assuming this triggers int)
}
t_next_in = ni;
if(restart)
{
enable_interrupts(int_tbe);
}
}
//original copy of bputc() below, modified above:
//void bputc(char c) {
// short restart;
// int ni;
// restart = t_next_in == t_next_out;
// t_buffer[t_next_in] = c;
// ni=(t_next_in + 1) % T_BUFFER_SIZE;
// while(ni == t_next_out); //can this line freeze the system if int_tbe is off?
// t_next_in = ni;
// if(restart) enable_interrupts(int_tbe); //original location
//}
#INT_RDA
void r_serial_isr() {
int t;
r_buffer[r_next_in] = getc();
t = r_next_in;
r_next_in = (r_next_in + 1) % R_BUFFER_SIZE;
if(r_next_in == r_next_out)
r_next_in = t; // Buffer full !!
}
#define bkbhit (r_next_in != r_next_out)
BYTE bgetc() {
BYTE c;
// The following line is not needed so long as bkbhit() is polled before calling bgetc().
// while(!bkbhit) ; //this line can freeze the system if called when no data exists and none is coming in.
c = r_buffer[r_next_out];
r_next_out = (r_next_out + 1) % R_BUFFER_SIZE;
return(c);
}
//-----------------------------------------------------
somewhere in main()
int8 Temp_Data;
//if (bkbhit) //or
while (bkbhit)
{
Temp_Data = bgetc();
//blah blah blah I'm doing something with data here
}
// and to send some data out...
bputc(Temp_Data);
|
And the interrupts take care of getting the data in and out of the ram buffers and they can store up to the buffer size (in this example 32 bytes incoming and 32 outgoing). |
|
|
temopic
Joined: 20 Feb 2008 Posts: 27 Location: mexico
|
|
Posted: Thu Mar 06, 2008 10:03 pm |
|
|
Can I use similar buffers for SPI interrupts?
Ex_sisr.c |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Fri Mar 07, 2008 5:10 am |
|
|
Circular buffers are useful for any I/O both with and without flow control.
Flow control is always the complete answer.
The isr driven circular buffer is extremely useful and often the only way to get RS232 to work reliably. However the circular buffer is still no more than a shock absorber in that is smooths out bursts of Rs232 data both in and out(if you use it for output as well) and allows more flexiblity in that the PIC code doesn't need to be conformed to such a tight loop around kbhit(). Without flow control the PIC design must be able to do its work and not mess up the average ( no overflows of buffers on either input or output). Consider an example where for every character received two are sent out. If there is no flow control and the input is a continuous unbroken stream of characters then an output buffer overflow is guaranteed ( assuming input baud rate is equal to the output baud rate). |
|
|
temopic
Joined: 20 Feb 2008 Posts: 27 Location: mexico
|
|
Posted: Fri Mar 07, 2008 10:16 am |
|
|
Thank you Douglas
Could some body help make circular buffers for SPI in and out?
My code seems to get stucked after a SPI interrupt not allowing int_rda arrive |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Fri Mar 07, 2008 8:50 pm |
|
|
The examples above in this code show a circular buffer. I wouldn't use modulo % division. A simple test of the counter to see if it has reached the end of the buffer and a reset to zero if true is best practice. CCS has examples to look at that plus with the code above you should enable you to make progress. |
|
|
temopic
Joined: 20 Feb 2008 Posts: 27 Location: mexico
|
|
Posted: Sat Mar 08, 2008 1:33 pm |
|
|
thanks again douglas |
|
|
|