/* ----------------------------------------------------------------- */ /* gps.c for Garmin eTrex 23 May 05 dpa Created. interrupt-driven TPU UART is used to read strings from Garmin eTrex GPS in "NMEA" mode, 4800 baud 06 Jun 07 dpa Comments. /* ----------------------------------------------------------------- */ /* The garmin sends a "paragraph" of NMEA "sentences" at 4800 baud (fixed!) every 2 seconds. This is set by the manufacturer and all we can do is enable and disable the data stream. It looks like this: $GPRMC,225358,A,3250.7847,N,09647.0117,W,0.0,0.0,230505,4.6,E,A*02 $GPRMB,A,,,,,,,,,,,,A,A*0B $GPGGA,225358,3250.7847,N,09647.0117,W,1,03,6.6,248.0,M,-24.8,M,,*76 $GPGSA,A,2,,,,14,,,,22,,30,,,6.7,6.6,1.0*35 $GPGSV,3,1,10,01,59,347,00,05,00,034,00,11,26,277,00,14,44,053,42*76 $GPGSV,3,2,10,16,12,175,00,18,00,133,00,20,13,317,00,22,30,130,48*7E $GPGSV,3,3,10,25,83,294,00,30,26,063,48*75 $GPGLL,3250.7847,N,09647.0117,W,225358,A,A*5D $GPBOD,,T,,M,,*47 $PGRME,61.9,M,50.0,M,79.6,M*1D $PGRMZ,814,f,2*17 $GPRTE,1,1,c,*37 The one we're interested in is the first, $GPRMC, the Global Positioning Recommended Minimum C (as opposed to A and B): $GPRMC,225358,A,3250.7847,N,09647.0117,W,0.0,0.0,230505,4.6,E,A*02 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 $GPRMC NMEA sentence identifier 1 22:53:58 UTC 2 A means data is good (V means inValid) 3 32 deg 50.7847 minutes latitude 4 North latitude 5 096 degrees 47.0117 minutes longitude 6 West longitude 7 0.0 ground speed in knots (Nautical mph) 8 0.0 true course 9 23 May 05 10 4.6 degrees magnetic variation 11 east magnetic variation (add to true course) 12 auto, 2D, 3D or differential mode 13 *02 checksum Here is the C structure where we want the data to ultimately end up, after it is read and converted from ASCII strings to binary. Robot tasks that need the GPS data read it from this global structure. // include "gps.h" struct GPRMC { int time; // UTC as 32 bit integer char valid; // 'A' is data valid float lat; // latitude char ns; // N/S for lat float lon; // longitude char ew; // E/W for lon float knots; // speed float north; // true course int date; // date as 32 bit integer float magvar; // Local Magnetic variation char ew2; // E/W for magvar }; #define GPS_BAUD 4800 #define MAXGPS_BUF 128 // end of "gps.h" The Strategy is an interrupt driven UART TPU channel FSM. state = 0 look for $, else drop state = 1 look for G, else state = 0 state = 2 look for P, else state = 0 state = 3 look for R, else state = 0 state = 4 look for M, else state = 0 state = 5 look for C, else state = 0 state = 6 read chars into *buf++ if not newline (if buf overflow, state = 0) gps_task convert buffer to input struct (ascii to binary) */ /* ----------------------------------------------------------------- */ #include "param.h" #include "../include/tpu.h" #include "gps.h" /* ----------------------------------------------------------------- */ /* globals */ struct GPRMC gprmc; /* output structure */ int gps_rdy; /* gps buffer update flag */ int gps_frame_err; /* gps underrun error */ /* ----------------------------------------------------------------- */ /* locals */ unsigned char gps_buf[MAXGPS_BUF]; /* ASCII input buffer */ unsigned char *gps = (unsigned char *)&gps_buf; /* input buffer pointer */ int gps_byte; /* byte read from gps */ int gps_chan; /* gps UART receiver channel, interrupt driven */ void (*gps_state)(); /* gps state vector */ /* ----------------------------------------------------------------- */ /* gps_int() gps receive interrupt. This code is called whenever the serial UART channel connected to the Garmin GPS receives a character. */ /* ----------------------------------------------------------------- */ __attribute__((__interrupt__)) void gps_int(void) { /* 68332 specific, clear interrupt flag for chan */ TPU_CISR &= ~(0x1<= (gps_buf + MAXGPS_BUF)) { // if buf overflow gps_state = gps_state0; // then start over } } } /* ----------------------------------------------------------------- */ /* gps_task() This task is run from the cooperative scheduler 3 times per second. It polls the gps_rdy flag to determine if a new and valid buffer of $GPRMC data is available to be converted from ASCII strings, and does the conversion if the data is available. It also maintains a frame error count and flags the data as unreliable if more than two seconds passes without a new valid GPS update, so the robot will know the data is not current. This task is instantiated at system startup. */ #define GPS_FRAME_ERR 6 void gps_task(int x) { while (1) { // loop endlessly if (gps_rdy) { // while polling ready flag sscanf(gps_buf,"%d %c %f %c %f %c %f %f %d %f %c", &gprmc.time, // convert from ascii to binary &gprmc.valid, &gprmc.lat, &gprmc.ns, &gprmc.lon, &gprmc.ew, &gprmc.knots, &gprmc.north, &gprmc.date, &gprmc.magvar, &gprmc.ew2); gps_rdy = 0; // reset ready flag gps_frame_err = 0; // and frame error } else { gps_frame_err++; // else no valid frame yet if (gps_frame_err > GPS_FRAME_ERR) { gprmc.valid = 'F'; // error after 2 secs gps_frame_err = 0; // and start over } } msleep(333); // Suspend this task for 1/3 second } } /* ----------------------------------------------------------------- */ /* Set up a TPU channel for a 4800 baud serial input, and init a receive interrupt on that port. This is mostly 68332 specific stuff. */ int gps_init(int chan) { int extern vbraddr; // 68332 hardware vector addr gps_chan = chan; // chan is 1 of 16 TPU channels // init the FSM gps = (unsigned char *)&gps_buf; // init buf pointer gps_state = gps_state0; // init execution vector gprmc.valid = 'F'; // mark data struct as not valid // init the TPU tpu_uart_init(gps_chan,1,GPS_BAUD); /* TPU chan, UART RX, 4800 BAUD */ /* set CPU interrupt vector for TPU channel chan, (the receive interrupt) */ *(long*)(vbraddr+(64*4)+(gps_chan*4)) = (long)gps_int; /* enable TPU interrupt for chan (receive interrupt) */ tpu_set_cier(gps_chan,1); return 0; } /* ----------------------------------------------------------------- */ /* Turn off interrupt */ int gps_deinit() { void extern sci_init(); tpu_set_cpr(gps_chan,0); /* disable channel function */ tpu_set_cier(gps_chan,0); /* disable channel interrupt */ TPU_CISR &= ~(0x1<