GPS configuring with PIC16F876A

Find out how to setup your programmer's software and how to solve many common problems.

Moderator: phalanx

Post Reply
Athul
Posts: 13
Joined: Thu Apr 07, 2016 1:00 am

GPS configuring with PIC16F876A

Post by Athul » Fri Mar 16, 2018 1:02 am

Hi,

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??

Code: Select all

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);
    }

}

User avatar
phalanx
Non-SFE Guru
Posts: 1983
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: GPS configuring with PIC16F876A

Post by phalanx » Thu Mar 22, 2018 12:09 pm

Hi Athul,

The overrun bit (OERR) is set when the receive FIFO is full and you try to receive another serial byte. The FIFO can hold 2 bytes so if a 3rd byte is shifted in before you read out at least 1 byte from the RCREG, the overrun bit will be set and will block any further reception until you reset the receiver.

I took a quick look at your code and I have a pretty good idea of why you are having this issue. Including the start and stop bit overhead, at 9600 baud a single byte takes about 1ms to transmit. In both your command() and data() functions, you have multiple 20ms delays in each one. Command() is called every time your primary loop starts over and data() can be called multiple times within your GPS() function. These delays block any other code execution until they complete which prevents the rest of your code from reading bytes out of the receive register and eventually causing the overrun bit to be set.

When you are typing characters in Linux, the rate at which you are typing is slow enough for your firmware to respond in a timely manner. The GPS, on the other hand, outputs bytes as fast as the 9600 baud rate will allow which means it will overrun the receive FIFO before your first 20ms delay even completes.

The best way to fix this is to use interrupts. The UART has a receive interrupt you can implement. I would enable receive interrupts and write an interrupt service routine (ISR) that does nothing more than read the receive buffer and maybe set a flag bit that the rest of your firmware would look at to see if a character was received. You would then change your main loop to look for the flag bit and process the characters after they have been removed from the receive registers. This will dramatically change the layout of your code but it is the path to get everything working correctly.

If you need additional help with interrupts, we're here to help!

-Bill

Athul
Posts: 13
Joined: Thu Apr 07, 2016 1:00 am

Re: GPS configuring with PIC16F876A

Post by Athul » Fri Mar 23, 2018 4:32 am

Hi,

I've already asked this question in several forums, and one guy told me the same thing. I removed following lines

Code: Select all

TXREG = a
,

Code: Select all

while(TRMT==0)
and

Code: Select all

data()
, but it still ddn't work.

I've also tried to configure UART as interrupt, but faced same problem there too. I don't have that code in my hand and will update that soon.
Command() is called every time your primary loop starts over and data() can be called multiple times within your GPS() function

Code: Select all

while(1)
    {
        command(0XC0);
        display(gps);
    }
I understand the case of data(), but this loop happens after I get the string from GPS function. So how could delay in this could cause Overrun error???

In my project I only need to find location when the distance measured is less that 10 cm. When this conditon becomes true, GPS function will be called and find the required string and break from the function. That's why I've configured it as a normal function rather than an interrrupt.

Is it neceaasry to configure UART as an interrupt always??

User avatar
phalanx
Non-SFE Guru
Posts: 1983
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: GPS configuring with PIC16F876A

Post by phalanx » Fri Mar 23, 2018 8:02 pm

Athul wrote:
Fri Mar 23, 2018 4:32 am
I understand the case of data(), but this loop happens after I get the string from GPS function. So how could delay in this could cause Overrun error???
You still have data() being called deeper in your GPS() function that will bring additional 20ms delays. You may receive a character or two but you will continue to run into the OVERRUN condition until you address the large latency in your code. You also can't gaurantee that the GPS won't be sending characters while you are still initializing the hardware. As soon as you enable the UART receiver, you have the potential to overrun since your code doesn't imediately handle characters being received from the GPS.
In my project I only need to find location when the distance measured is less that 10 cm. When this conditon becomes true, GPS function will be called and find the required string and break from the function. That's why I've configured it as a normal function rather than an interrrupt.
Once the UART is enabled, you have no choice but to be receiving data all the time. If you ignore characters being sent because you are in a part of your code that doesn't require GPS data, the hardware FIFO will still fill up and cause an overrun. Every single character received needs to be read out of the FIFO, whether needed or not, to maintain proper operation.
Is it neceaasry to configure UART as an interrupt always??
It's not necessary but it would be up to you as the firmware designer to make sure all your code executes in a timely manner. This would entail checking the receive buffer at a rate faster than characters are being sent to it. This is not easy to do especially as programs become more complex and is why interrupts are invaluable. With interrupts, it doesn't matter what your code is currently doing because the program flow immediately jumps to a small section of code dedicated to reading a character out of the receive buffer.

-Bill

Athul
Posts: 13
Joined: Thu Apr 07, 2016 1:00 am

Re: GPS configuring with PIC16F876A

Post by Athul » Tue Mar 27, 2018 3:06 am

Code: Select all

// PIC16F876A Configuration Bit Settings

// 'C' source line config statements

#include <xc.h>
#include <string.h>
#include <stdio.h>


// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)


#define _XTAL_FREQ 4000000

char name1gps[] = "GPGGA,", namegps[7], gpsdat[63], a;
int h,f, checkgps;


void interrupt_init()
{
    GIE = 1;                //INTERRUPT
    PEIE = 1;
    //TMR0IE = 1;
    RCIE = 1;
    //RCIF = 0;
}

void interrupt_dis()
{
    GIE = 0;                //INTERRUPT
    PEIE = 0;
    //TMR0IE = 1;
    RCIE = 0;
}


void uart_init()
{
    TXSTA = 0X24;           //UART
    RCSTA = 0X90;
    SPBRG = 25;
}

void uart_tx(char *p)
{
    while(*p)
    {
        TXREG = *p;
        while(TRMT == 0);
        p++;
    }
}


int uart_ready()
{
    if(RCIF)
    return(1);
    else
    return(0);
}



void interrupt ISR()
{
     if(uart_ready()) 
     {
        a=RCREG;
        RCIF = 0;
        
        if(a=='$') 
        {  
            for(f=0;f<=5;f++) 
            {
                while(!RCIF);
                RCIF = 0;
                namegps[f]=RCREG;
            }
            namegps[f] = '\0';
            
            checkgps=strcmp(namegps,name1gps);
            
            if(checkgps==0) 
            {
                uart_tx("$GPGGA");
                //RCIE=0;
                RCIF=0;
            }
  
        }

    }
}



void main()
{
    uart_init();
    interrupt_init();
    
    while(1)
    {   

       
    }    
}
Above is the code for GPS configured as interrupt. It only checks whether received string matches to $GPGGA., if matches display $GPGGA in Serial Prompt.
TX of PIC is connected to PC

Program works in Proteus and when i type in through serial prompt

User avatar
phalanx
Non-SFE Guru
Posts: 1983
Joined: Sun Nov 30, 2003 8:57 am
Location: Candia, NH

Re: GPS configuring with PIC16F876A

Post by phalanx » Tue Mar 27, 2018 7:00 am

I still don't see a properly formatted ISR in your code. I'm assuming you are using Microchip's XC8 compiler so here is a quick reference on how to format one in your code: http://microchipdeveloper.com/faq:31. It's also well documented in the users guide that comes with XC8.

Also, ISRs are supposed to be very short in order to not cause delays in other areas of code. Your ISR should do nothing more than receive the serial character, clear the interrupt flag, and maybe set a status variable to indicated to your main loop that further processing is required.

Here is some quick pseudo-code to give you an idea what I'm talking about. You ultimately need to completely rethink how you implemented your receive algorithm.

Code: Select all

void interrupt PROPERLY_FORMATTED_INTERRUPT (void)
{
   RX_CHAR = RCREG;	//Move received character
   RCIF = 0;		//clear interrupt flag
   RX_CHAR_FLAG = 1;	//Set a variable flag for your main loop to recognize.
}


void main (void)
{
   Setup_MCU();			//Setup your MCU
   Do_Other_Housekeeping();	//Do any other initialization

   While(1);			//Start your control loop
   {
      if(RX_CHAR_FLAG == 1)	//Check if you received a character
      {
         Proccess_RX_Char();	//If Received, process the character.
      }

      if(some_other_flag == 1); //Check other flags
      {
         Process_next_task();	//If other flag is set, process its related information
      }

      //Add more tasks here as necessary

   }      //Close while loop
}         //Close main()
-Bill

Post Reply