¿ªÔÆÌåÓý

ctrl + shift + ? for shortcuts
© 2025 Groups.io

RS232 for GPS on PIC (help!)


"msilv3r"
 

I've established a few things:

-The GPS I just purchased uses the NMEA 0183 data protocol (via RS232)
-I need a MAX232 to convert the RS232 logic levels to TTL levels
-I will be using the RX/TX pins on my PIC16F876
-The data I want is format: $GPZDA,hhmmss.ss,xx,xx,xxxx,xx,xx
-Each line ends with <CR><LF>
-I can request info from the GPS using: $ttllQ,sss,[CR][LF]
--It will then output the information once per second until another query is requested.

Source:


Now my question to you guys, on the PIC side of things.
-From my current understanding, I will have a byte by byte buffer. This means I will have to do some type of compare with each character. I'm not sure how to do this.

-I'm having trouble doing this in C. I've found in the PIC16F876 datasheet how to enable all the pins, set the baud rate etc. I'm still not sure how to initialize it and how the buffer works. Does a byte simply sit in the buffer until it is read, then a flag occurs and the next byte is written? That doesn't sound very logical to me. I guess what I'm really asking is how does RS232 communication work in a PIC? I see a lot of information on setup but not on implementation.

-There is an example serial.c in examples for the HI-TECH C compiler (used with MPLAB IDE). I'm not sure how to modify this to work with my PIC model, and since I can't answer the last question I'm even more lost when looking at the code.


David Forbes
 

On 6/29/2010 8:12 PM, msilv3r wrote:

Now my question to you guys, on the PIC side of things.
-From my current understanding, I will have a byte by byte buffer.
This means I will have to do some type of compare with each character.
I'm not sure how to do this.
I've done serial communication with the 18F4520 in MCC18, but not with a 16F in C. I have programmed a 16F873 in assembly language to do serial I/O.

The serial port is simply a data register that contains the last byte received, and a status bit in the status register indicating that a data byte is available in the data register.

When the data register is read, then the status flag is automatically cleared until another byte is received. So you only have to test the status bit and read in the character to move data into your string array.


Here's a rough idea of the C code:

char the_char, string[80]; // storage for the string
char *p; // point to where the next char goes

p = string[0]; // point at first character's location
while (!timeout) { // prevent hanging on missing EOL
if (USART_status_bit) // reads the status register bit
the_char = *p++ = USART_data_reg; // get the character
[some timeout code]
if (the_char == '&#92;n') // detect end of line
break;
}

Don't assume that this will compile - it's rather off-the-cuff.

The timeout code can just increment a counter and trigger the timeout thing when the count hits some big value corresponding to more than a second of real time. Without a timeout, your code will hang forever if there's a communication error.

--
David Forbes, Tucson, AZ


"ghpicard"
 

One has to remember too that if the byte in the buffer is not read before the next one arrives, it will be overwritten, and thus, lost.
Of course at low baud rates as the usual for NMEA GPSs (9600 bauds), the uC can run circles while waiting for the next byte to come in, but a bad implemented loop on another routine can also thrash everything.
I used to use the UART interrupt to circumvent this. That was when I was used to use assembler instead of high level cr*p for sissies :D
But new compilers also allow for interrupt usage so not all is lost.

³Ò²¹²õ³Ù¨®²Ô

--- In NEONIXIE-L@..., David Forbes <dforbes@...> wrote:

On 6/29/2010 8:12 PM, msilv3r wrote:

Now my question to you guys, on the PIC side of things.
-From my current understanding, I will have a byte by byte buffer.
This means I will have to do some type of compare with each character.
I'm not sure how to do this.
I've done serial communication with the 18F4520 in MCC18, but not with a 16F in
C. I have programmed a 16F873 in assembly language to do serial I/O.

The serial port is simply a data register that contains the last byte received,
and a status bit in the status register indicating that a data byte is available
in the data register.

When the data register is read, then the status flag is automatically cleared
until another byte is received. So you only have to test the status bit and read
in the character to move data into your string array.


Here's a rough idea of the C code:

char the_char, string[80]; // storage for the string
char *p; // point to where the next char goes

p = string[0]; // point at first character's location
while (!timeout) { // prevent hanging on missing EOL
if (USART_status_bit) // reads the status register bit
the_char = *p++ = USART_data_reg; // get the character
[some timeout code]
if (the_char == '&#92;n') // detect end of line
break;
}

Don't assume that this will compile - it's rather off-the-cuff.

The timeout code can just increment a counter and trigger the timeout thing when
the count hits some big value corresponding to more than a second of real time.
Without a timeout, your code will hang forever if there's a communication error.

--
David Forbes, Tucson, AZ


"neil_manc"
 

--- In NEONIXIE-L@..., David Forbes <dforbes@...> wrote:

On 6/29/2010 8:12 PM, msilv3r wrote:

Now my question to you guys, on the PIC side of things.
-From my current understanding, I will have a byte by byte buffer.
This means I will have to do some type of compare with each character.
I'm not sure how to do this.
I've done serial communication with the 18F4520 in MCC18, but not with a 16F in
C. I have programmed a 16F873 in assembly language to do serial I/O.

The serial port is simply a data register that contains the last byte received,
and a status bit in the status register indicating that a data byte is available
in the data register.

When the data register is read, then the status flag is automatically cleared
until another byte is received. So you only have to test the status bit and read
in the character to move data into your string array.
You don't actually need to buffer the string at all. You can parse the characters as they appear at the serial port. I have a little project which parses GPS NMEA strings and generates a square wave where frequency is proportional to speed, done on an Atmel ATtiny15 which has no RAM. The strategy is to be constantly looking for the string preamble (e.g. "$GPRMC"). Once you spot it, start counting comma characters ignoring everything else until you get to the field you want then do a simple atoi routine to convert numbers to an integer.

If you're interested I can email you my code, though it's in C and a bit AVR specific (uses the proguchar library for string storage).

Neil S


"fixitsan2"
 

--- In NEONIXIE-L@..., "msilv3r" <msilv3r@...> wrote:


Now my question to you guys, on the PIC side of things.
-From my current understanding, I will have a byte by byte buffer. This means I will have to do some type of compare with each character. I'm not sure how to do this.
When a byte is received by the pic an interrupt bit is set. If you have the serial data interrupt enable flag set the program execution can be made to jump to your own interrupt routine. If the serial data interrupt enable flag is not set then the relevant interrupt bit is still set and you can check for it being set using a wait loop, or at least polling at regular enough intervals to make sure you capture every bit.

on my pic the received data interrupt flag is PIR1,5
The corresponding enable bit is PIE1,5

Not that I've ever had to rely on it, the pic's receive buffer is three bytes deep. The first byte to arrive sets the interrupt bit and two more bytes can still be received and read by successive reads of the RXREG register.

I use a similar header trapping technique with my Smartsockets as you do with your NMEA header (I've also used this technique for NMEA data), where I detect the existence of the header "$B7" before each instruction. The following assembler code is in an interrupt and if you are not up to speed with assembler just bear in mind that the receive progress is recorded by setting bits in the 'RX_progress' register. If the first byte matches a comparison with character "$" I set bit 0 or rx_progress. The next byte to arrive is compared with character "B" and if it is a match I set bit 1, then bit two with the next correct match "7".

I use the set bits to control program flow and keep a track of how far into the received string we are. once I get the first three correct bytes I set bit 3.

Sample code >>>>>
Datain:
btfss PIR1,5 ; is the received data flag set ?
goto do_something_else
movf RCREG,w
movwf _RX_BUF ; save the received byte immediately


; progrss register bits 0-3 get set as each element of the header
; is received. If a bad byte is detected then progress is reset
; and the code starts waiting for another $ sign


prog0:
btfsc _RX_PROGRESS,0 ; has the progress,0 step been completed

bra prog1 ; yes, test for progress,1
movf _RX_BUF,w
xorlw '$' ; test if this byte is a $ sign
btfss STATUS,Z
goto dump ; not a $ sign, clear the progress register
bsf _RX_PROGRESS,0 ; no, set this bit now
movf TMR1L,w
subwf _randnum,f
goto out ; is a $ sign, don't clear progress, allow more

prog1: btfsc _RX_PROGRESS,1 ; same as above, validate the received data/header
bra prog2
movf _RX_BUF,w
xorlw 'B'
btfss STATUS,Z
goto dump
bsf _RX_PROGRESS,1
goto out

prog2: btfsc _RX_PROGRESS,2
bra prog3

movf _RX_BUF,w
xorlw '7'
btfss STATUS,Z
goto dump
bsf _RX_PROGRESS,2
goto out



; The fourth byte to be received is the 'type of message' character
; as for prog0 to prog2, if the fourth byte doesn't meet the test
; criteria of being a valid message type this data sentence is aborted

prog3: btfsc _RX_PROGRESS,3
bra prog4
clrf _M_type
clrf _M_type1


End of Sample code >>>
I'll take a look to see if I can find my NMEA code, but from memory it was pretty much the same techniques I used to trap headers and make comparisons.

Chris


"yhbcoggs"
 

True, you don't need to buffer up the whole line; I did it by just looking at the last 3 characters; Just write a state machine that goes something like:

Say the NMEA sentence with the time 00:31:07 looks like this:

$GPGGA,003107<don't care about the rest>

Each time you see a character (every time you get a serial rx interrupt):

If current character is a $, reset state to 0

If state is 0 and the next to last character was a G,
and the last character is a G, and the current character is an A set state to 1

If state is 1, watch for a comma, set state to 2

If state is 2, set H tens, set state to 3
If state is 3, set H ones, set state to 4
If state is 4, set M tens, set state to 5
If state is 5, set M ones, set state to 6
If state is 6, set S tens, set state to 7
If state is 7, set S ones, set state to 8

no matter what the state do these things:

next to last character = last character

last character = current character


"msilv3r"
 

Thank you all. Great insights. I'm def gonna end up using the interrupt. This isn't that hard after all! I was actually just thinking of a similar algorithm to yours yhbcoggs. Awesome!

On a slight side note, so since this is 4800bps, I could safely assume that I will receive more than once time code per second. And that, I assume, is the ".ss" part of "hhmmss.ss" - So I could, assuming I was always connected, use a compare within this string to know when a second in time has occurred. Also, it seems like many instructions on the pic are executed within either the nanosecond range, or low microsecond range. Since I don't have that much code for the interrupts I could perhaps say its negligible time. I plan on using a 32.whatever oscillator and using an interrupt count to compare for each second (I stil have some research to do in that area). But then when should I have the clock sync? I think it makes more sense to use an oscillator as the timing mechanism then the string compare mentioned above. Or maybe moth? I was thinking maybe every five minutes I would have it sync with the GPS, just incase its off by a second or two. A bit of scattered thoughts here, sorry. Any thoughts?

--- In NEONIXIE-L@..., "yhbcoggs" <rdcjr@...> wrote:
True, you don't need to buffer up the whole line; I did it by just looking at the last 3 characters; Just write a state machine that goes something like:

Say the NMEA sentence with the time 00:31:07 looks like this:

$GPGGA,003107<don't care about the rest>

Each time you see a character (every time you get a serial rx interrupt):

If current character is a $, reset state to 0

If state is 0 and the next to last character was a G,
and the last character is a G, and the current character is an A set state to 1

If state is 1, watch for a comma, set state to 2

If state is 2, set H tens, set state to 3
If state is 3, set H ones, set state to 4
If state is 4, set M tens, set state to 5
If state is 5, set M ones, set state to 6
If state is 6, set S tens, set state to 7
If state is 7, set S ones, set state to 8

no matter what the state do these things:

next to last character = last character

last character = current character


"Alex Rubli &#92;(YHO&#92;)"
 

My five cents:

i would use the $GPRMC protocol it looks like this

$GPRMC,140003.242,A,4717.1126,N,00833.7862,E,0.03,80.59,010201,,*36

this has a flag to tell you that the GPS is "locked" and so you can be sure that the time is correct
(don?t rememeber which one the "flag" is), I also remember that this protocol shows the correct reading only after having info for at least 3 satelites.

I am no expert, and did this long tome ago, I recall having faulty readings with other protocols

make a NMEA $GPRMC google search.

I also encourage you to disable the other protocols on your reciever, it might flood your buffer

regards


Alex

[Non-text portions of this message have been removed]


"jerry"
 

_____
From: NEONIXIE-L@... On Behalf Of Alex Rubli (YHO)

My five cents:

i would use the $GPRMC protocol it looks like this

$GPRMC,140003.242,A,4717.1126,N,00833.7862,E,0.03,80.59,010201,,*36

this has a flag to tell you that the GPS is "locked" and so you can be sure
that the time is correct
(don?t rememeber which one the "flag" is), I also remember that this
protocol shows the correct reading only after having info for at least 3
satelites.

I am no expert, and did this long tome ago, I recall having faulty readings
with other protocols

make a NMEA $GPRMC google search.

I also encourage you to disable the other protocols on your reciever, it
might flood your buffer
_____


A means valid

V means invalid



Jerry

Wb9jfr






No virus found in this incoming message.
Checked by AVG - www.avg.com
Version: 9.0.830 / Virus Database: 271.1.1/2982 - Release Date: 07/04/10
13:35:00


"fixitsan2"
 

--- In NEONIXIE-L@..., "msilv3r" <msilv3r@...> wrote:
So I could, assuming I was always connected, use a compare within this string to know when a second in time has occurred.


More than likely you will find that the NMEA sentence is transmitted once per second, on each one second epoch.
Some GPS modules also output a 1pps signal with a high degree of accuracy associated with it, but generally your eye wouldn't be able to determine any difference, so sticking with receiving the NMEA sentence at the rate of 1Hz ought to keep you spot on.