CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

I've tried everything...please HELP!

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

I've tried everything...please HELP!
PostPosted: Wed Apr 26, 2006 5:48 pm     Reply with quote

Hi all,

I am reading in a GPS string ($GPRMC) and using it to calculate the heading to a waypoint. Based on this value I'll generate a pwm signal ("rud_pwm") to turn my vehicle. The F877 code works fine when I print it in Hyperterminal (i.e. I get exactly the value for "rud_pwm" I expect). However, when I try and relay this to a F87 (last line of the code) through RS232, I get the value I expect but every 10th line or so, I get a crazy value like 255, or some other random number. This has nothing to do with the calculations in this code. I proved this by uncommenting the next to last line (rud_pwm=15) and still getting the same result (i.e. when reading the value from the F87, I'll get 15, 15, 15, 15, 255, 15, 15, 15, 15, 5, etc.). It has something to do with the RDA interrupt, but I'm not sure how to fix it. If anyone has any clue, I would really appreciate the help. I attached both the F877 code and the F87...sorry so long.

Code:

#include <16F877A.H>
#include <stdlib.h>
#include <math.h>

#define LED PIN_C0

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, stream=GPS)
#use rs232(baud=19200, xmit=PIN_D6, rcv=PIN_D7, stream=PIC16F87)

// global variables
int i=0, j=0, k=0, commas=0;
long rud_pwm=128;
char tr1[6], LAT[11], LONGI[12];
char c;

#int_rda
void rda_isr(void)
{
   c = fgetc(GPS);
   c = (char)c;

   if(c=='$') commas=0;
    if(c==',') commas++;
   
   if(commas==3) // get latitude data (3)
   {
      if(c==',') k=0;
        else {LAT[k]=c; k++;}
   }

   if(commas==5) // get longtitude data (5)
   {
      if(c==',') k=0;
        else {LONGI[k]=c; k++;}
   }

   if(commas==8) // true heading (8)
   {
      if(c==',') k=0;
      else {tr1[k]=c; k++;}
   }
}


void main()
{
   char afterDecimal[5];
   float currentLat=0.0, currentLong=0.0;
   float diff_Lat=0.0, diff_Long=0.0;
   float currentWayptLat=0.0, currentWayptLong=0.0;
   float waypointHeading=0.0;
   long trueHeading=0;
    signed long error=0;
   float rud_pwm_man=0.0;

    // current waypoint (drexel parking lot)
    currentWayptLat = 39.95407*Pi/180.0;
    currentWayptLong = 75.18486*Pi/180.0;

   output_high(LED); delay_ms(1500);
   output_low(LED); delay_ms(1500);

   // enable interrupts
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   while(1)
   {
      afterDecimal[0]=0; afterDecimal[1]=0; afterDecimal[2]=0; afterDecimal[3]=0; afterDecimal[4]=0;
      trueHeading=0; waypointHeading=0.0;
   
      output_high(LED);

      // latitude
      afterDecimal[0] = LAT[5];
        afterDecimal[1] = LAT[6];
      afterDecimal[2] = LAT[7];
      afterDecimal[3] = LAT[8];
      afterDecimal[4] = LAT[9];
      
      currentLat = (float)(atol(LAT)/100) + ((float)(atol(LAT)%100) + atof(afterDecimal)/100000.0)/60.0;
      currentLat = currentLat*Pi/180.0;

      // longitude
      afterDecimal[0] = LONGI[6];
        afterDecimal[1] = LONGI[7];
      afterDecimal[2] = LONGI[8];
      afterDecimal[3] = LONGI[9];
      afterDecimal[4] = LONGI[10];

      currentLong = (float)(atol(LONGI)/100) + ((float)(atol(LONGI)%100) + atof(afterDecimal)/100000.0)/60.0;
      currentLong = currentLong*Pi/180.0;

      // true heading of aircraft
      trueHeading = atol(tr1); // measured CW from North (i.e. from North to East) in degrees

      // calculate heading to waypoint
      // pythagorean theorem - works for short distances...not enough RAM to factor in Earth's curvature
      diff_Lat = currentWayptLat - currentLat;
        diff_Long = currentWayptLong - currentLong;
        waypointHeading = atan2(diff_Long,diff_Lat);
        if(sin(currentWayptLong - currentLong)>0.0)
            waypointHeading = 360.0 - waypointHeading*180.0/Pi;
        else
            waypointHeading = -waypointHeading*180.0/Pi;

      
      error = (long)waypointHeading - trueHeading;

      // tell aircraft which way to turn
      if(error>0)
      {
         if(error<180) // turn right
         {
            //output_high(LED);
            rud_pwm = 127 - error; // 0-127
         }
         else // turn left
         {
            //output_low(LED);
            rud_pwm = 127 + (360-error); // 127-255
         }
      }
      else
      {
         if(error<-180) // turn right
         {
            //output_high(LED);
            rud_pwm = 127 - (360+error); // 0-127
         }
         else // turn left
         {
            //output_low(LED);
            rud_pwm = 127 - error; // 127-255
         }
      }

      if(rud_pwm<0) rud_pwm=0;
      if(rud_pwm>254) rud_pwm=254;

                               // rud_pwm = 15;

      fputc((unsigned int)rud_pwm, PIC16F87);
   }
}


Here is the code I have on the F87...

Code:

#include <16F87.H>
#include <stdlib.h>
#include <math.h>

#define LED PIN_A2

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=19200, xmit=PIN_A1, rcv=PIN_A0, stream=PIC16F877)
#use rs232(baud=9600, xmit=PIN_A3, rcv=PIN_A4, stream=PC)

// global variables
unsigned int rud_pwm=127;
int i;

// Timer interrupt to generate PWM signals
#INT_TIMER0
void pwm_signal()
{

   //One of the parameters used to cause the interrupt to occur every 22.5 msec
   set_timer0(37); // 61 for 20 msec

   /**************** RUDDER ****************/
   output_high(PIN_B5); // generates initial high time ie 1 msec
   delay_us(1000);
   i=0;
   for(i=0;i<rud_pwm;i++) // generates remaining high time, varied by variable "pwm"
   {
      //use this to tune duty cycle
      delay_us(1);
   }
   output_low(PIN_B5);
}


void main()
{

   output_high(LED); delay_ms(1500);
   output_low(LED); delay_ms(1500);

   // setup interrupts
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);

   // enable interrupts
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);

   while(1)
   {
        output_high(LED);

      rud_pwm = (unsigned int)fgetc(PIC16F877);

      if(rud_pwm<127) fprintf(PC, "Right: %u\r\n", rud_pwm);
      else fprintf(PC, "Left: %u\r\n", rud_pwm);

   }
}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Apr 26, 2006 6:29 pm     Reply with quote

Quote:
#use rs232(baud=19200, xmit=PIN_A1, rcv=PIN_A0, stream=PIC16F877)

Just as a start, I would change the stream above, which comes from
the 16F87 code, to use the hardware UART. The 16F87 has a hardware
UART on pins B5 and B2. Why not use it ?
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Apr 27, 2006 2:06 am     Reply with quote

In your timer0 interrupt you have
Code:
   delay_us(1000);
At 19200 baud these 1000us equal 19.2 bit times during which your software based UARTs will be disabled --> data corruption.


Change 1 software UART to the hardware UART as suggested by PCM.
Change the remaining software UART so it will be synchronized with your timer interrupt, example:
Code:
#use rs232(baud=9600, xmit=PIN_A3, rcv=PIN_A4, stream=PC, DISABLE_INTS)   //  Added parameter to disable interrupts



Because you disable the interrupts it will sometimes take a bit longer before your interrupt handler is called. Improve the timing by changing
Code:
   set_timer0(37); // 61 for 20 msec
to
Code:
   set_timer0( get_timer(0) + 37); // 61 for 20 msec
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Thu Apr 27, 2006 8:01 am     Reply with quote

I tried simplifying my code so people could better understand the error. It appears that the solution PCM suggested of using a hardware interrupt on PIC #2 for the RS232 data might be the solution, but before I tried rewiring my circuit, I just wanted to make sure no one else saw anything obvious. What's happening is that it is correctly printing the value of "rud_pwm" to hyperterminal (rud_pwm = 190) when the GPS unit is not connected, but once I hookup the GPS unit and start triggering the RS232 interrupts on PIC #1, the value that's printed ranges from 240 - 250. Thanks again for all the help!

Is it a mistake to have the baud rate of my GPS device and the baud rate of the communications between the 2 PICs the same?

PIC #1 (16F877A)
Code:

#include <16F877A.H>
#include <stdlib.h>
#include <math.h>

#define LED PIN_C0

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, stream=GPS)
#use rs232(baud=9600, xmit=PIN_D6, rcv=PIN_D7, stream=PIC16F87)

// global variables
int i=0, j=0, k=0, commas=0;
long rud_pwm=128;
char tr1[6], LAT[11], LONGI[12];
char c;

#int_rda
void rda_isr(void)
{
   c = fgetc(GPS);
   c = (char)c;

   if(c=='$') commas=0
    if(c==',') commas++;
   
   if(commas==3) // get latitude data (3)
   {
      if(c==',') k=0;
        else {LAT[k]=c; k++;}
   }

   if(commas==5) // get longtitude data (5)
   {
      if(c==',') k=0;
        else {LONGI[k]=c; k++;}
   }

   if(commas==8) // true heading (8)
   {
      if(c==',') k=0;
      else {tr1[k]=c; k++;}
   }

   counter = counter + 1;
}


void main()
{
   output_high(LED); delay_ms(1500);
   output_low(LED); delay_ms(1500);

   // enable interrupts
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   while(1)
   {
      rud_pwm = 190;

        fputc((unsigned int)rud_pwm, PIC16F87);
   }

}




PIC #2 (16F87)
Code:

#include <16F87.H>
#include <rdaDefine.c>
#include <stdlib.h>
#include <math.h>

#define LED PIN_A2

/*--------------------------------------------------------*/
/* No Watch Dog Timer (NOWDT)
/* No Protect
/* Power Up Timer (PUT)
/* No Low Voltage Programming (LVP) - allows I/O on PIN_B3
/* 10 MHz Clock
/* RS232 define for PIC-GPS communication
/*--------------------------------------------------------*/
#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_A1, rcv=PIN_A0, stream=PIC16F877)
#use rs232(baud=9600, xmit=PIN_A3, rcv=PIN_A4, stream=PC)

// global variables
unsigned int rud_pwm=127;
int i;
long pulseLength=0;
int reset=0;

// hardware interrupt to detect manual override
#int_ccp1
void isr()
{
   reset=0;
   set_timer1(0);
   while(input(PIN_B0));

   // if SW interrupt triggers during HW interrupt, prevent bogus pulseLength value
   if(reset==0) pulseLength = get_timer1();
   else
   {
      pulseLength = pulseLength;
      reset=0;
   }
}

void main()
{

   output_high(LED); delay_ms(1500);
   output_low(LED); delay_ms(1500);

   // setup interrupts
   setup_ccp1(CCP_CAPTURE_RE); // on rising edge
    setup_timer_1(T1_INTERNAL);

   // enable interrupts
   enable_interrupts(INT_CCP1);
   enable_interrupts(GLOBAL);

   while(1)
   {
        output_high(LED);

      rud_pwm = (unsigned int)fgetc(PIC16F877);

      if(rud_pwm<127) fprintf(PC, "Right: %u\r\n", rud_pwm);
      else fprintf(PC, "Left: %u\r\n", rud_pwm);

   }
}
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Apr 27, 2006 2:06 pm     Reply with quote

Next time please post the real code you have been testing with. I know you didn't because this version is missing the declaration for the counter variable in pic1....

I spot two weaknesses:

1) In pic1 you increase index k without checking possible array overflows. In case one of the fields is larger than you anticipated this will result into overwriting RAM with unknown behaviour as a result.
Never assume the data you receive will always be of the same length, for example a receive error might cause you to miss a comma character.

2) In pic2 the getc function might give unexpected results. Receiving data on RS232 with the software UART's implementation is very basic, it waits for a start bit (high to low level input change) and then simply starts sampling the incomming data until a complete byte is received. In your current setup it is very well possible you call the getc function when data is already comming in, a high-to-low transistion halfway the data stream could then errounously be interpreted as a startbit.
A solution to this could be to make sure that no data is comming in before calling getc. Do this by scanning the input to be high for at least 10 bit times (1 start bit, 8 data bits, 1 stop bit) before calling getc.
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Thu Apr 27, 2006 2:10 pm     Reply with quote

I rewired my circuit to include the hardware UART...and everything is now working!!!!!!!!! A big thanks to everyone, especially PCM, for helping me through this :-) This forum rocks!!
Guest








PostPosted: Tue Dec 25, 2007 10:04 am     Reply with quote

@weg22:


Very interesting code. Are you willing to point me to the theory behind the calculations?
Or if anyone else has some info Rolling Eyes
Guest








PostPosted: Wed Jan 02, 2008 11:27 am     Reply with quote

The code was used to perform GPS waypoint navigation with an RC aircraft.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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