Keyboard Shortcuts
ctrl + shift + ? :
Show all keyboard shortcuts
ctrl + g :
Navigate to a group
ctrl + shift + f :
Find
ctrl + / :
Quick actions
esc to dismiss
Likes
Search
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:
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 == '\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.
toggle quoted message
Show quoted text
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:
|
"neil_manc"
--- In NEONIXIE-L@..., David Forbes <dforbes@...> wrote:
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:
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. Datain:Sample code >>>>> 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 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.End of Sample code >>> 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!
toggle quoted message
Show quoted text
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: |
"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 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. |
to navigate to use esc to dismiss