PIC16F876A and GPS; Overrun error

Thread Starter

Athul S Nair

Joined Dec 24, 2016
23
I am trying to configure a GPS receiver with PIC16F876A. In the code below i am receiving characters from UART, and checking whether received character is $. If condition is true, receive characters and store it in character array gps until ,.

If I type in characters through serail prompt in Linux, code works as it is supposed to. But if I connect it to the GPS receiver I only get first two characters and LCD will show OVERRUN. I am new to PIC. what should I do??

C:
char a, gps[50];
int i, j, k, flag = 0;

void command(char a) //LCD command function
{
char b;

RS = 0;
RW = 0;

PORTB = PORTB & 0X0F;
b = a & 0XF0;
PORTB = PORTB | b;
EN = 1;
__delay_ms(20);
EN = 0;

PORTB = PORTB & 0X0F;
b = a & 0X0F;
b = b << 4;
PORTB = PORTB | b;
EN = 1;
__delay_ms(20);
EN = 0;
}

void data(char a) //LCD dta function
{
char b;
RS = 1;
RW = 0;

PORTB = PORTB & 0X0F;
b = a & 0XF0;
PORTB = PORTB | b;
EN = 1;
__delay_ms(20);
EN = 0;

PORTB = PORTB & 0X0F;
b = a & 0X0F;
b = b << 4;
PORTB = PORTB | b;
EN = 1;
__delay_ms(20);
EN = 0;
}

void display(char *p) //LCD display function
{
while(*p)
{
data(*p);
p++;
}
}

void LCD_init()
{
TRISB = 0X00;
PORTB = 0XFF;

command(0X33);
command(0X32);
command(0X28);
command(0X0C);
command(0X01);
}
void GPS_init()
{
TXSTA = 0X24; //00100100
RCSTA = 0X90; //10010000
SPBRG = 25; //baud rate 9600
}

char uart_rx() //GPS receive function
{
while(!RCIF);
//RCIF=0;
a=RCREG;
return(a);

}

void GPS() //Function to fund require string
{
for(i=0; ;i++)
{
a = uart_rx();
TXREG = a; //view received data in serial terminal
while(TRMT == 0);
data(a);
if(OERR == 1)
{
command(0Xc0);
display("OVERRUN"); //Display string to LCD
CREN = 0;
}

if(a == '$')
{
gps[0] = '$';
for(k=1; ;k++)
{
gps[k] = uart_rx();
data(gps[k]); //display character to LCD
TXREG = gps[k]; //view received data in serial terminal
while(TRMT == 0);

if(gps[k] == ',')
{
gps[k] = '\0';
flag = 1;
break;
}
}
}

if(flag == 1)
{
flag = 0;
break;
}
}
}

main()
{
GPS_init(); //Intialize GPS
LCD_init(); //Intialize LCD in 4 bit mode
command(0x80);
display("Initializing");
command(0x01);
GPS();


while(1)
{
command(0XC0);
display(gps);
}

}
 
Last edited by a moderator:

Ian Rogers

Joined Dec 12, 2012
1,136
Overrun errors are normally caused when the BAUD rate is out!... You have a SPBRG of 25... What crystal is on your pic16f876a?
 

ericgibbs

Joined Jan 29, 2010
21,442
hi Athul,
I do not use C, I use asm or Arduino code for GPS work.

Query, why are you looking for a 'comma' rather than the end of message Cr Lf'
This is a typical demo GPS message.
$GPGGA,123459.00,5001.00000,N,00059.00000,W,1,04,1.80,1.2,M,47.7,M,,*7F<crlf>
E
 

JohnInTX

Joined Jun 26, 2012
4,787
An incorrect baud rate usually results in a framing error. That said, your code is set up for 9600 baud with a 4MHz oscillator - is that correct? The GPS modules I've seen are 9600.

An overrun error is generated when a new character is received before you read a previous one i.e. you don't read the UART often enough to keep up with the data rate. Your code indicates 9600 baud; that's about 1ms per character. On lines 37 and 45 you are delaying 20msec for each E signal, 40ms for each character displayed. That means that you won't be back to read the UART before it gets another character and it will generate an overrun error.

The reason you get 2 characters OK is that the UART is double buffered. You read the first one immediately after it is received and you go away for 40ms. During that time, the second char is read OK and stored in RXBUF. The problem happens because you are still delaying in the LCD routine and haven't read the 2ed character when the third character comes in. There is no place to put that one so OERR is set and the UART stops receiving.

The solution is to check and read the UART at no more than 1ms intervals.

A couple of things you can try. First, the LCD display does not need 20msec on the E line. Try just E = 1 then E=0. Note that you can make E a bit longer (and hopefully avoid some I/O port issues) by doing something useful in between. I usually do some necessary housekeeping like
C:
E=1;
b = a & 0X0F;  // spend a little time preparing for the next nibble
b = b << 4;
E=0;
That stretches E for free and allows the port pin to settle between operations.

Next, you have to keep the character processing time to a minimum - less than 1msec/char. I don't like having to worry about stuff like that (or polling the UART) so at a minimum, try reading the character string into a buffer (array) in a tight loop. Fill the buffer like this:
Read chars and discard until '$' found
Read and store chars in the array until you find the end of the line (CR LF).

Now you have a full second, the time between GPS sentences, to process the characters in the buffer and display what you want.

Check and clear any UART flags before the next sentence.

There are several threads on AAC on reading GPS sentences. If you still need help, search around and take a look at them.

Good luck and welcome to AAC!
 
Last edited:

MrChips

Joined Oct 2, 2009
34,810
The proper procedure is to receive all characters in an interrupt service routine (ISR) and store the characters in a buffer. The ISR reports the received message to the main program loop only when the last character has been detected. In this way, your main program does not have to poll on each pending character.
 
Top