|
|
View previous topic :: View next topic |
Author |
Message |
weg22
Joined: 08 Jul 2005 Posts: 91
|
I've tried everything...please HELP! |
Posted: Wed Apr 26, 2006 5:48 pm |
|
|
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
|
|
Posted: Wed Apr 26, 2006 6:29 pm |
|
|
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
|
|
Posted: Thu Apr 27, 2006 2:06 am |
|
|
In your timer0 interrupt you haveAt 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
|
|
Posted: Thu Apr 27, 2006 8:01 am |
|
|
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
|
|
Posted: Thu Apr 27, 2006 2:06 pm |
|
|
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
|
|
Posted: Thu Apr 27, 2006 2:10 pm |
|
|
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
|
|
Posted: Tue Dec 25, 2007 10:04 am |
|
|
@weg22:
Very interesting code. Are you willing to point me to the theory behind the calculations?
Or if anyone else has some info |
|
|
Guest
|
|
Posted: Wed Jan 02, 2008 11:27 am |
|
|
The code was used to perform GPS waypoint navigation with an RC aircraft. |
|
|
|
|
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
|