|
|
View previous topic :: View next topic |
Author |
Message |
michaelb
Joined: 28 Nov 2005 Posts: 17
|
GPS buffer garbage |
Posted: Tue Dec 13, 2005 1:32 pm |
|
|
Hi,
I know this topic has been done a lot, but I couldn't find an answer to this specific problem. I'm trying to buffer NMEA sentences from a GPS to eventually extract the lat/long. At the moment I'm merely trying to send the first 5 characters of the sentence to the PC.
The problem I'm having seems to down to mistiming. The first sentence is outputted correctly, but subsequent ones are not. Here is my code:
Code: |
/*
GPS Stream Reader/Parser
Read GPS NMEA stream over serial interface.
GPS RX: C7
PC TX: C6
*/
#if defined(__PCM__)
#include <16F877.h> //PIC16F877A Device
#device *=16 //Use 16 Bit Pointers
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000) //20MHz Clock
#use rs232(baud=4800, xmit=PIN_C6, rcv=PIN_C7, ERRORS) //Configure RS232 Comms
#endif
#include <lcd.c>
#define esc 0x1B //Terminal Escape Character
//Function Prototypes
void cls();
//Global Variables
int line_complete; //Flag to show NMEA sentence complete
int data_valid; //Flag to show incoming data stream to be added to sentence
int buffer_pointer; //Points to next available buffer space
char gps_buffer[82]; //82 Byte buffer - maximum lenfth of an NMEA sentence
#INT_RDA //Serial RX Interrupt
RDA_isr()
{
char char_rcvd; //To store received char
char_rcvd = getc(); //Store received char
if(char_rcvd == '$') //$ Marks beginning of a NMEA sentence
{
data_valid = true; //Add incoming chars to the buffer
buffer_pointer = 0; //Reset buffer pointer
}
if(data_valid)
{
gps_buffer[buffer_pointer] = char_rcvd; //Add char to buffer
buffer_pointer++; //Advance buffer pointer
if((char_rcvd == '\r') || (char_rcvd == '\n')) //If end of NMEA sentence
{
data_valid = FALSE;
}
if(buffer_pointer >= 82) //If buffer is full
{
data_valid = FALSE;
}
if(!data_valid) //If data stream ended
{
line_complete = TRUE; //Flag as sentence completed
}
}
}
void main()
{
int i;
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
lcd_init();
delay_ms(6);
cls();
lcd_putc("GPS Parser");
line_complete = FALSE;
enable_interrupts(GLOBAL);
printf("Ready\n\r");
enable_interrupts(INT_RDA);
while(1)
{
while(!line_complete) //Wait for sentence to be completed
{
#asm
NOP
#endasm
}
if(line_complete) //If sentence is complete
{
disable_interrupts(INT_RDA);
for(i=0; i <6;i++) //Print first 5 chars to terminal
{
putc(gps_buffer[i]);
}
printf("\r\n");
line_complete = FALSE;
enable_interrupts(INT_RDA); //Get next sentence
}
}
}
void cls(void)
{
printf("%c[2J",esc);
}
|
Here is the terminal output for 2 sequences:
Code: |
$GPRMC
$B,V,,
$A,,,,
$A,A,1
$V,3,1
$V,3,2
$V,3,3
$L,,,,
$D,,T,
$E,,M,
$Z,,f,
$E,1,1
$GPRMC
$B,V,,
$A,,,,
$A,A,1
$V,3,1
$V,3,2
$V,3,3
$L,,,,
$D,,T,
$E,,M,
$Z,,f,
$E,1,1
|
This is what the GPS is actually outputting (in one sequence):
Code: |
$GPRMC,,V,,,,,,,251105,3.3,W,N*28
$GPRMB,V,,,,002,5223.700,N,00133.648,W,,,,V,N*15
$GPGGA,,,,,,0,00,,,M,,M,,*66
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,3,1,12,03,83,272,00,08,10,335,00,11,00,242,00,15,53,064,00*7F
$GPGSV,3,2,12,16,46,181,00,18,40,074,00,19,46,285,00,21,20,058,00*71
$GPGSV,3,3,12,22,41,128,00,26,08,024,00,27,19,297,00,29,07,013,00*71
$GPGLL,,,,,,V,N*64
$GPBOD,,T,,M,002,*75
$PGRME,,M,,M,,M*00
$PGRMZ,,f,1*29
$GPRTE,1,1,c,*37
|
As you can see the number of sentences is the same - but the data is corrupt. Any help would be greatly appreciated. I've been trying this all day and it's driving me insane.
Kind Regards,
Michael |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 13, 2005 2:02 pm |
|
|
The problem is that the NMEA sentences come in right after each other,
without any spacing. When you get one sentence, you then disable
interrupts. So you miss several characters from the next sentence.
Perhaps you should use an interrupt-driven transmit buffer, as shown
in the CCS example file EX_STISR.C. |
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 2:31 pm |
|
|
Thanks for that. I'll have a look through that example.
I was thinking it shouldn't matter though, as a new sentence is only ever created when a $ is detected, and the data following that should then be intact. It will only start putting together a new sentence from the beginning - by looking for the first $ after the interrupt has been enabled again. This may mean that not all sentences are read, but the ones that are should be intact. I'd be intested if you had any ideas on this. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 13, 2005 2:54 pm |
|
|
This is what's happening in detail, I believe:
You declare the first sentence has been received when you get
the carriage return. Shorty after that (maybe within 10 us), you
disable interrupts in your main loop.
However, there is also a linefeed character at the end of the first
sentence, that comes in. It gets loaded into the 2-deep output fifo
of the UART receiver. This fifo is in hardware. Then the next sentence
starts coming in immediately. The '$' char at the start of that sentence
get shifted in, and put into the 2nd free location in the 2-deep receive
fifo. Now the receive fifo is full. Any further characters that come
in while interrupts are disabled will just roll right over each other in
the input shift register and cause overrun errors. Because you have
the ERRORS directive in your #use rs232() statement, these errors will
be cleared. You'll still lose the chars, though.
While this is going on, you are sending 6 (not 5) characters with your
putc() statement. The UART transmitter will accept 2 characters
without delay. That's because it has the output shift register, and a
1-deep transmit "fifo", which feeds the shift register. So even though
you're sending 6 characters to the transmitter, putc() only has to wait
in a polling loop for 4 of them. Well, if you look at your list of displayed
characters, you'll notice that you're missing exactly 4 chars from the
2nd sentence. But anyway, after a delay of 4 char times, you
re-enable interrupts, and start executing the isr again.
Your code in the RDA_isr() ignores the linefeed left over from the last
sentence. It sees the '$' char from the 2nd sentence, because it was
in the 2nd slot in the hardware receive fifo. But since interrupts
disabled for 4 char times, you lost the next 4 chars after the '$' in the
2nd sentence. So instead of seeing this "$GPRMB", you see this "$B,V,,"
because you have lost the "GPRM".
That's why I suggest that you must do this in a way that doesn't shut
off interrupts. |
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 3:06 pm |
|
|
Thanks very much for that detailed explanation - helped a lot. I'll have a look at doing this as you mentioned beforehand. |
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 3:15 pm |
|
|
Just had a thought - is there a way of clearing the receiver FIFO? Clearing RCREG perhaps? Seems to be an easier solution. |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Tue Dec 13, 2005 3:23 pm |
|
|
It's unlikely you'll get this to work reliably without a circular buffer.
What I've done in the past is to select the sentence of interest ..don't let
anything get into the buffer unless it is $GPGLL for example.
The RDA triggers char by char but until you see $ followed by G P G L L
it doesn't take up space in your buffer. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 3:49 pm |
|
|
Thanks both. I've used the clear_usart_receiver() function and it receives the first sentence fine, but the interrupt does not seem to come back on. I've use d the clear function just before enabling the interrupt again in main(), and I've determined it gets past enabling the interrupt (by printing a char after that statement). I know that the interrupt does not seem to reactivate as I added a putc statement in the ISR to print a every time it's run. This happens before it displays the $GPRMC bit, but not after re-enabling the interrupt again. As i'm using RS232 with 'ERRORS' i assume there are no flags i need to clear to enable reception again? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 4:14 pm |
|
|
I've tried using that function now, but it hasn't fixed it I'm afraid. I'll tinker some more. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 13, 2005 4:15 pm |
|
|
At this point, I feel that I've given all the answers that I can
on this thread. |
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 4:21 pm |
|
|
Thanks very much for you help. I'll let you know if I get anywhere - might try the aforementioned alternative method. |
|
|
Bart
Joined: 12 Jul 2005 Posts: 49
|
|
Posted: Tue Dec 13, 2005 4:21 pm |
|
|
Can this example help you ?
http://www.ccsinfo.com/forum/viewtopic.php?t=24716
It is working at my site but it gets/gives only the selected string (not all GPS strings) _________________ I like Skype (www.skype.com), my username is BplotM |
|
|
michaelb
Joined: 28 Nov 2005 Posts: 17
|
|
Posted: Tue Dec 13, 2005 5:46 pm |
|
|
After resetting the CREN bit too, it finally works. Thanks to everyone for your help. I'm off to bed. |
|
|
|
|
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
|