Subversion Repositories svnkaklik

Rev

Rev 6 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log

Rev Author Line No. Line
6 kaklik 1
// Change the following to change the clock frequency
2
#define CRYSTAL_FREQ    16000000
3
// Change the following to change between 16 or 20 column display
4
#define DISPLAY_COLS    20
5
// Speed units are "1" (nautical knots), "2" (metric kph), or "3" (statute mph)
6
#define SPEED_UNITS     1
7
 
8
/****************************************************************************
9
GPS18.c
10
 
11
This program receives NMEA-0183 data from a GPS and displays it.
12
Meant for large display version still in 16F876.
13
Three buttons
14
Automicaly resets if main loop stops (not the best solution, still don't know why it's stopping)
15
 
16
Next: don't display GPS screens unless GPS is active
17
      detect display needing reset
18
      preset data eeprom for first-time operation
19
      don't display init stuff if reseting from main loop
20
 
21
 
22
 
23
                 +5                       +5+5
24
                  |                        | |
25
                  20                      15 2
26
                ----------             ----------
27
~SerIn -----18-|          |-24-----11-|DB4 A Vdd |
28
               |          |-25-----12-|DB5       |
29
  ADC0 ------2-|          |-26-----13-|DB6       |
30
  ADC1 ------3-|  16F876  |-27-----14-|DB7     Vo| 3--
31
  ADC2 ------5-|          |           |    LCD   |    |
32
               |          |-14------6-|EN        |    |
33
       XTAL--9-|          |-15------4-|R/S       |    |
34
       XTAL-10-|          |-28-FET-16-|K         |    |
35
               |          |           |  RW  Vss |    |
36
 BUTTON 1---21-|          |            ----------     |
37
 BUTTON 2---22-|          |              1   5        |
38
 BUTTON 3---23-|          |              |   |        |
39
               |          |             Gnd Gnd       |
40
               |          |                           |
41
               |          |-11----------R/C-----------
42
               |          |
43
               |          |
44
                ----------
45
                  8    19
46
                  |     |
47
                 Gnd   Gnd
48
 
49
***************************************************************************/
50
#case
51
#include <16F876.h>
52
#include <jonsinc.h>
53
#device = *=16 ADC=10                /* allow RAM addresses over 255 */
54
 
55
#if ( ( CRYSTAL_FREQ < 4000000) || ( CRYSTAL_FREQ > 20000000 ) )
56
#error "CRYSTAL FREQ" not defined to between 8000000 and 20000000
57
#endif
58
#if ( ( DISPLAY_COLS != 16 ) && ( DISPLAY_COLS != 20 ) )
59
#error "DISPLAY COLS" not defined to 16 or 20
60
#endif
61
 
62
// RMC_TIME = 1 per clock megahertz, rounded
63
#define RMC_TIME        CRYSTAL_FREQ/1000000
64
 
65
#define LCD_D0          PIN_B3
66
#define LCD_D1          PIN_B4
67
#define LCD_D2          PIN_B5
68
#define LCD_D3          PIN_B6
69
#define LCD_EN          PIN_C3
70
#define LCD_RS          PIN_C4
71
#define RX_IN           PIN_C7
72
#define BUTTON_1        PIN_B0
73
#define BUTTON_2        PIN_B1
74
#define BUTTON_3        PIN_B2
75
#define LCD_BACKLITE    PIN_B7
76
#define LINE_1          0x00
77
#define LINE_2          0x40
78
#if DISPLAY_COLS == 20
79
#define LINE_3          0x14
80
#define LINE_4          0x54
81
#endif
82
#if DISPLAY_COLS == 16
83
#define LINE_3          0x10
84
#define LINE_4          0x50
85
#endif
86
#define CLEAR_DISP      0x01
87
#define EOF             0x00
88
#define COMMA           ','
89
#define CR              13
90
#define SPACE           ' '
91
#define PERIOD          '.'
92
#define DEGREE          0xdf
93
#define DOLLAR          '$'
94
#define NULL            0
95
#define GPRMC_CODE      75
96
#define GPRMB_CODE      74
97
#define RX_BUFFER_SIZE  70
98
#define POSITION_SCREEN 1
99
#define WAYPOINT_SCREEN 2
100
#define BATTERY_SCREEN  3
101
#define HIDDEN_RMC      5
102
#define WARNING_MSG     0
103
#define NODATA_MSG      1
104
#define ACTIVITY_SYMBOL 0xFF
105
#define MAX_VOLTS       15
106
#define EEPROM_CONTRAST 0
107
#define EEPROM_INITIAL  1
108
 
109
/* Set the following define to "YES" to display XOR'ed GPS sentence code */
110
/* such as GPRMC and the display will read out the value of 74. */
111
#define GET_GPS_CODE    NO
112
 
113
#separate void Display ( void );
114
#separate void LCD_Init ( void );
115
#separate void LCD_SetPosition ( unsigned int cX );
116
#separate void LCD_PutChar ( unsigned int cX );
117
#separate void LCD_PutCmd ( unsigned int cX );
118
#separate void LCD_PulseEnable ( void );
119
#separate void LCD_SetData ( unsigned int cX );
120
#separate void SkipField ( char cCnt );
121
#separate char GetField ( void );
122
#separate void InitRxBuffer ( char cCode );
123
#separate char GetRxChar ( void );
124
#separate void DisplayLatLon ( void );
125
#separate void DisplayWaypoint ( void );
126
#separate void DisplayLatitude ( char cLine );
127
#separate void DisplayLongitude ( char cLine );
128
#separate void DisplayHeading ( char cLine );
129
#separate void DisplaySpeed ( char cLine );
130
#separate void DisplaySteer ( char cLine, char cX );
131
#separate void DisplayWaypointName ( char cLine, char cX );
132
#separate void DisplayDistance ( char cLine, char cX );
133
#separate void DisplayBearing ( char cLine, char cX );
134
#separate void GetUtcAndMagVar ( void );
135
#separate long TrueToMag ( long iH );
136
#separate long FieldFiveToLong ( void );
137
#separate void DisplayAnalog ( void );
138
#separate void DisplayScaledVoltage ( long iV, char cScale );
139
#separate void DisplayArrival ( char cLine );
140
#separate void DisplayMessage ( char cMsgNum );
141
#separate void DisplayTemplateLatLon ( void );
142
#separate void DisplayTemplateWaypoint ( void );
143
#separate void DisplayTemplateAnalog ( void );
144
#separate void Delay5mS ( char cCnt );
145
 
146
#fuses HS, NOPROTECT, PUT, NOWDT, BROWNOUT, NOLVP, NOCPD
147
#use standard_io ( A )
148
#use standard_io ( B )
149
#use standard_io ( C )
150
#use delay ( clock = CRYSTAL_FREQ )
151
#use rs232 ( baud=4800, xmit=PIN_C6, rcv=PIN_C7, ERRORS )    // XMIT must be assigned to enable hardward USART
152
#priority RDA, RTCC, EXT
153
 
154
static char cC [ 10 ];      // local buffer
155
static char cTimeOut;
156
static char cRxBuffer [ RX_BUFFER_SIZE ];    // Fifo
157
static char cRxByteCnt;         // Number of bytes in the recv fifo
158
static char *cRxBufferWritePtr;    // Pointers for the Rx buffer
159
static char *cRxBufferReadPtr;
160
static char cRxIsrState, cRxMsgTypeReceived, cRxMsgTypeDesired;
161
static char cRxMsgReady, cReceiveFlag;
162
static long iVar, iLastRange, iTimeOut;
163
static char cVarDir, cScreenChanged, cAdcDone;
164
static char cButtonPressed, cSkip, cButtonCount;
165
static char cScreen, cSavedScreen, cRmcTimer1, cRmcTimer2;
166
static char cToFrom [ 5 ], cIndicator, cIllumination, cRxErrorFlag;
167
static char cDone, cContrast;
168
 
169
/*******************************************************************/
170
#int_ad
171
void AdcInterrupt ( void )
172
    {
173
    /* Gets here when ADC is done conversion, sets flag */
174
    cAdcDone = YES;
175
    }
176
 
177
#int_timer1
178
void Timer1Interrupt ( void )
179
    {
180
    /* Periodic RMC data timer, gets here every 204mS */
181
    /* This routine forces RMC to run every 10 minutes to update */
182
    /* magnetic variation */
183
    if ( cRmcTimer1-- == 0 )
184
        {
185
        cRmcTimer1 = 255;               // 52 seconds @ 10.240MHz
186
        if ( cRmcTimer2-- == 0 )
187
            {
188
            cRmcTimer2 = RMC_TIME;      // triggers every 10 minutes
189
            cSavedScreen = cScreen;     // save current screen type
190
            cScreen = HIDDEN_RMC;       // force RMC to run
191
            }
192
        }
193
    }
194
 
195
#int_rtcc
196
void Timer0Interrupt ( void )
197
    {
198
    // Gets here every 16.4mS at 8MHz, 8.2mS at 16MHz
199
    // Handles data timeout and switch debounce.
200
 
201
    // DATA TIMEOUT TIMER
202
    if ( cTimeOut != 0 )
203
        {
204
        cTimeOut--;
205
        }
206
 
207
    // This timer is preset by the normal operating loop, unless the operating
208
    // loop stops looping, at which point iTimeOut finally decrements to zero
209
    // and resets CPU.
210
    if ( iTimeOut != 0 )
211
        {
212
        iTimeOut--;
213
        }
214
    else
215
        {
216
        reset_cpu();                // force reset
217
        }
218
 
219
    if ( input ( BUTTON_2 ) == LOW )  // if button still pressed
220
        {
221
        cScreen = WAYPOINT_SCREEN;
222
        cSkip = YES;                // skip out of anything in process
223
        cScreenChanged = YES;       // repaint complete screen
224
        }
225
 
226
    if ( input ( BUTTON_3 ) == LOW )  // if button still pressed
227
        {
228
        cScreen = BATTERY_SCREEN;
229
        cSkip = YES;                // skip out of anything in process
230
        cScreenChanged = YES;       // repaint complete screen
231
        }
232
 
233
    // SWITCH DEBOUNCE
234
    if ( input ( BUTTON_1 ) == LOW )  // if button still pressed
235
        {
236
        if ( cButtonCount < 255 )   // hold at 255
237
            {
238
            cButtonCount++;         // otherwise increment
239
            }
240
        }
241
    else            // if button is unpressed
242
        {
243
        if ( cButtonCount > 2 )     // filter out glitches
244
            {
245
            //If button press is greater than 3.3 seconds, cold reset
246
            if ( cButtonCount == 255 )
247
                {
248
                reset_cpu();
249
                }
250
            if ( ( cButtonCount > 57 ) && ( cButtonCount < 255 ) )
251
                {
252
                if ( cScreen != HIDDEN_RMC )       // if not in the middle of getting magnetic variation
253
                    {
254
                    // cIllumination ^= ON;
255
                    output_bit ( LCD_BACKLITE, cIllumination ^= ON );
256
                    }
257
                }
258
            // If button press is less than 0.5 second
259
            if ( cButtonCount <= 57 )
260
                {
261
                if ( cScreen != HIDDEN_RMC )       // if not in the middle of getting magnetic variation
262
                    {
263
                    //if ( cScreen++ >= BATTERY_SCREEN )   // increment to next screen
264
                        {
265
                        cScreen = POSITION_SCREEN;                    // wrap
266
                        }
267
                    cSkip = YES;                // skip out of anything in process
268
                    cScreenChanged = YES;       // repaint complete screen
269
                    }
270
                }
271
            }
272
        cButtonCount = 0;       // restart
273
        }
274
    }
275
 
276
#int_rda
277
void SerialInterrupt ( void )
278
    {
279
    /*
280
    Reads incoming data from the USART and puts in in a rolling buffer
281
    ( but in this application, it should never roll.)
282
    If the buffer is full, this routine just discards the received byte.
283
    Not checking the LRC byte at the end of the NMEA-0183 sentence.
284
    */
285
    char cChar;
286
 
287
    if ( rs232_errors & 0x04 )  // get framing error bit from Rx status reg
288
        {
289
        cRxErrorFlag = ON;
290
        }
291
    cChar = getchar();       // get char from UART, clear any errors
292
 
293
    if ( cRxByteCnt == RX_BUFFER_SIZE ) // is recv fifo full ???
294
        {
295
        goto done;
296
        }
297
    switch ( cRxIsrState )
298
        {
299
        case 0:
300
            {
301
            if ( cChar == DOLLAR )  // if start of NMEA0183 message
302
                {
303
                cRxByteCnt = 0;     // reset byte count
304
                cReceiveFlag = OFF;     // default to off
305
                cRxMsgTypeReceived = NULL;  // set hashed value to null
306
                cRxIsrState++;                 // next state
307
                }
308
            break;
309
            }
310
        case 1:                           // five type characters to obtain
311
        case 2:
312
        case 3:
313
        case 4:
314
        case 5:
315
            {
316
            cRxMsgTypeReceived ^= cChar;      // hash in msg type
317
            if ( cRxIsrState++ == 5 )        // if time to check message type
318
                {
319
                if ( cRxMsgTypeReceived == cRxMsgTypeDesired )  // if good
320
                    {
321
                    cReceiveFlag = YES;            // enable receiving
322
                    cRxBufferWritePtr = cRxBuffer;    // reset to beginning of buffer
323
                    }
324
                else                    // don't want this message
325
                    {
326
                    cRxIsrState = 0;    // reset to look for next msg
327
                    }
328
                }
329
            break;
330
            }
331
        case 6:
332
            {
333
            /* Case 6 skips the comma character following msg type */
334
            cRxIsrState++;
335
            break;
336
            }
337
        default:                          // remainder of characters
338
            {
339
            if ( cReceiveFlag == YES )        // if this message is wanted
340
                {
341
                *cRxBufferWritePtr = cChar;     // put char in fifo
342
                cRxBufferWritePtr++;            // increment pointer
343
                if ( cRxBufferWritePtr == ( cRxBuffer + RX_BUFFER_SIZE ) ) // pointer past end ?
344
                    {
345
                    cRxBufferWritePtr = cRxBuffer;      // set pointer to start of fifo
346
                    }
347
                cRxByteCnt++;              // Increment byte count
348
                if ( cChar == CR )
349
                    {
350
                    cRxMsgReady = YES;         // signal that message is ready
351
                    cReceiveFlag = NO;      // no more receive
352
                    }
353
                }
354
            }
355
        }
356
    done:;
357
    }
358
 
359
/*******************************************************************/
360
 
361
void main ( void )
362
    {
363
    char cX;
364
 
365
    iTimeOut = 65535;                       // default to very long to get by init
366
    /* INITIALIZE */
367
    output_float ( RX_IN );             // ensure Rx input is HiZ
368
    output_float ( BUTTON_1 );          // ensure switch input is HiZ
369
    output_float ( BUTTON_2 );          // ensure switch input is HiZ
370
    output_float ( BUTTON_3 );          // ensure switch input is HiZ
371
    output_low ( LCD_BACKLITE );        // turn off backlighting
372
    port_b_pullups ( ON );              // enable pullups on switches
373
 
374
    // GET SAVED SETTINGS
375
    cContrast = read_eeprom ( EEPROM_CONTRAST );        // get stored value
376
 
377
    // PWM is for display contrast
378
    setup_ccp2 ( CCP_PWM );                     // set for PWM mode
379
    //The cycle time will be (1/clock)*4*t2div*(period+1)
380
    // 1/8000000 * 4 * 1 * 128 = 51.2uS = 19.5KHz
381
    setup_timer_2 ( T2_DIV_BY_1, 255, 1 );      // set PWM period
382
    // duty cycle = value*(1/clock)*t2div
383
    // 10 * 1/8000000 * 1 = 1.2uS
384
    set_pwm2_duty ( cContrast );                // set contrast duty cycle
385
 
386
    // SETUP TIMER 0
387
    // Need 8-bit Timer0 to roll over every 13mS, approximately.
388
    // Roll time = 256 * 1 / ( clock_freq / prescaler setting / 4 )
389
    #if CRYSTAL_FREQ >= 15000000
390
    setup_counters ( RTCC_INTERNAL, RTCC_DIV_256 );   // ~13mS timer wrap
391
    #elif CRYSTAL_FREQ >= 8000000
392
    setup_counters ( RTCC_INTERNAL, RTCC_DIV_128 );   // ~13mS timer wrap
393
    #elif CRYSTAL_FREQ < 8000000
394
    setup_counters ( RTCC_INTERNAL, RTCC_DIV_64 );    // ~13mS timer wrap
395
    #endif
396
 
397
    // Timer 1 roll time = 65536 * 1 / ( clock_freq / prescaler setting / 4 )
398
    setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_8 );    // 16-bit timer
399
 
400
    setup_adc_ports ( RA0_RA1_RA3_ANALOG );  /* these three statements set up the ADC */
401
    setup_adc ( ADC_CLOCK_INTERNAL );
402
    cIllumination = OFF;
403
 
404
    LCD_Init();                        // set up LCD for 4-wire bus, etc.
405
 
406
    /* INIT MESSAGE */
407
    #if ( DISPLAY_COLS == 20 )
408
    LCD_SetPosition ( LINE_1 + 0 );
409
    printf ( LCD_PutChar, "   Northern Light   " );   // welcome screen
410
    LCD_SetPosition ( LINE_2 + 2 );
411
    printf ( LCD_PutChar, "Monitor/Repeater" );
412
    LCD_SetPosition ( LINE_3 + 3 );
413
    printf ( LCD_PutChar, "v18   06/21/03" );
414
    LCD_SetPosition ( LINE_4 + 5 );
415
    printf ( LCD_PutChar, "c Jon Fick" );
416
    #elif ( DISPLAY_COLS == 16 )
417
    LCD_SetPosition ( LINE_1 + 0);
418
    printf ( LCD_PutChar, " Northern Light " );       // welcome screen
419
    LCD_SetPosition ( LINE_2 + 2 );
420
    printf ( LCD_PutChar, "GPS Repeater" );
421
    LCD_SetPosition ( LINE_3 + 1 );
422
    printf ( LCD_PutChar, "v18   06/21/03" );
423
    LCD_SetPosition ( LINE_4 + 3 );
424
    printf ( LCD_PutChar, "c Jon Fick" );
425
    #endif
426
    delay_ms ( 1000 );
427
 
428
    /* INSTRUCTION MESSAGE */
429
    LCD_PutCmd ( CLEAR_DISP );
430
    LCD_SetPosition ( LINE_1 + 0 );
431
    printf ( LCD_PutChar, "BUTTONS:" );
432
    LCD_SetPosition ( LINE_2 + 0 );
433
    printf ( LCD_PutChar, "<-- Lat/Lon" );
434
    LCD_SetPosition ( LINE_3 + 0 );
435
    printf ( LCD_PutChar, "<-- Waypoint" );
436
    LCD_SetPosition ( LINE_4 + 0 );
437
    printf ( LCD_PutChar, "<-- Battery" );
438
    delay_ms ( 2000 );
439
 
440
    /* SETUP MODE */
441
    if ( input ( BUTTON_1 ) == LOW )  // if button is pressed
442
        {
443
        LCD_PutCmd ( CLEAR_DISP );
444
        LCD_SetPosition ( LINE_1 + 0 );
445
        printf ( LCD_PutChar, "Set contrast:" );
446
        LCD_SetPosition ( LINE_2 + 0 );
447
        printf ( LCD_PutChar, "<-- More" );
448
        LCD_SetPosition ( LINE_3 + 0 );
449
        printf ( LCD_PutChar, "<-- DONE" );
450
        LCD_SetPosition ( LINE_4 + 0 );
451
        printf ( LCD_PutChar, "<-- Less" );
452
        while ( input ( BUTTON_1 ) == LOW );    // wait for switch to be released after entering SETUP mode
453
        cContrast = 120;          // start at full contrast
454
        cDone = NO;
455
        while ( cDone == NO )
456
            {
457
            set_pwm2_duty ( cContrast );        // update contrast
458
            if ( input ( BUTTON_1 ) == LOW )
459
                {
460
                if ( cContrast > 0 )
461
                    {
462
                    cContrast--;            // more
463
                    }
464
                }
465
            if ( input ( BUTTON_2 ) == LOW )
466
                {
467
                cDone = YES;                // done
468
                }
469
            if ( input ( BUTTON_3 ) == LOW )
470
                {
471
                if ( cContrast < 255 )
472
                    {
473
                    cContrast++;        // less
474
                    }
475
                }
476
            delay_ms ( 30 );                   // autorepeat
477
            }
478
        write_eeprom ( EEPROM_CONTRAST, cContrast );    // save CONTRAST to EEPROM
479
 
480
        LCD_PutCmd ( CLEAR_DISP );
481
        LCD_SetPosition ( LINE_2 + 0 );
482
        printf ( LCD_PutChar, "<- Press initial" );
483
        LCD_SetPosition ( LINE_3 + 0 );
484
        printf ( LCD_PutChar, "   bootup screen" );
485
        while ( input ( BUTTON_1 ) == LOW );  // wait until button not pressed
486
        cX = POSITION_SCREEN;
487
        while ( TRUE )
488
            {
489
            LCD_SetPosition ( LINE_4 + 3 );
490
            switch ( cX )
491
                {
492
                case POSITION_SCREEN:
493
                    {
494
                    printf ( LCD_PutChar, "POSITION " );
495
                    break;
496
                    }
497
                case WAYPOINT_SCREEN:
498
                    {
499
                    printf ( LCD_PutChar, "WAYPOINT " );
500
                    break;
501
                    }
502
                case BATTERY_SCREEN:
503
                    {
504
                    printf ( LCD_PutChar, "BATTERY  " );
505
                    break;
506
                    }
507
                }
508
            delay_ms ( 750 );
509
            if ( input ( BUTTON_1 ) == LOW )  // if button is pressed
510
                {
511
                write_eeprom ( EEPROM_INITIAL, cX );   // save screen number to EEPROM
512
                break;
513
                }
514
            if ( cX++ == BATTERY_SCREEN )
515
                {
516
                cX = POSITION_SCREEN;
517
                }
518
            }
519
        LCD_PutCmd ( CLEAR_DISP );
520
        }
521
 
522
    /* This IF/ENDIF is a tool for getting the $GP... codes */
523
    /* that are used in the switch/case in the main loop. */
524
    #if ( GET_GPS_CODE == YES )
525
    printf ( LCD_PutChar, "%u", 'G'^'P'^'R'^'M'^'B');
526
    while ( TRUE );
527
    #endif
528
 
529
    /* INTERRUPTS */
530
    ext_int_edge ( H_TO_L );            // set falling edge ext interrupt
531
    enable_interrupts ( INT_TIMER1 );   // enable Timer1 interrupt
532
    enable_interrupts ( INT_RDA );      // enable serial interrupt
533
    enable_interrupts ( INT_RTCC );     // enable Timer0 interrupt
534
    enable_interrupts ( INT_AD );       // enable ADC interrupt
535
    enable_interrupts ( GLOBAL );       // enable all interrupts
536
 
537
    /* VARIABLES */
538
    iVar = NULL;                        // default, no variation yet
539
    cVarDir = SPACE;                    // default, no variation yet
540
    cRmcTimer1 = 255;                   // initialize to 52 seconds
541
    cRmcTimer2 = RMC_TIME;              // trigger forced RMC after 10 minutes
542
    cScreen = HIDDEN_RMC;               // default screen, get magnetic variation first
543
    cSavedScreen = read_eeprom ( EEPROM_INITIAL ); // restore initial screen
544
    iLastRange = 65535;                 // make max by default
545
    strcpy ( cToFrom, "  " );           // blank by default
546
    cScreenChanged = YES;
547
    cIndicator = 0;
548
    cButtonCount = 0;
549
    cButtonPressed = NO;
550
    cRxErrorFlag = OFF;
551
 
552
    /* MAIN LOOP */
553
    while ( TRUE )
554
        {
555
        cTimeOut = 188;        // 231 * 0.013mS = 3 seconds
556
        switch ( cScreen )
557
            {
558
            case HIDDEN_RMC:
559
                {
560
                InitRxBuffer( GPRMC_CODE );     // set code and turn on serial interrupt
561
                while ( ( cRxMsgReady == NO ) && ( cTimeOut != 0 ) );
562
                disable_interrupts ( INT_RDA );         // ignore rest of messages
563
                if ( cTimeOut != 0 )            // if not timed out
564
                    {
565
                    GetUtcAndMagVar();             // get and store the magnetic variation
566
                    }
567
                cScreen = cSavedScreen;         // revert to previous screen
568
                break;
569
                }
570
            case POSITION_SCREEN:
571
                {
572
                if ( cScreenChanged == YES )
573
                    {
574
                    disable_interrupts ( INT_RDA );
575
                    cScreenChanged = NO;
576
                    cSkip = NO;
577
                    LCD_PutCmd ( CLEAR_DISP );
578
                    DisplayTemplateLatLon();
579
                    enable_interrupts ( INT_RDA );
580
                    }
581
                InitRxBuffer( GPRMC_CODE );     // set code and turn on serial interrupt
582
                while ( ( cRxMsgReady == NO ) && ( cTimeOut != 0 ) && ( cScreenChanged != YES ) );
583
                disable_interrupts ( INT_RDA );         // ignore rest of messages
584
                if ( cScreenChanged == NO )
585
                    {
586
                    if ( cTimeOut != 0 )
587
                        {
588
                        DisplayLatLon();
589
                        }
590
                    else
591
                        {
592
                        DisplayMessage ( NODATA_MSG );
593
                        }
594
                    }
595
                cRxErrorFlag = OFF;
596
                break;
597
                }
598
            case WAYPOINT_SCREEN:
599
                {
600
                if ( cScreenChanged == YES )
601
                    {
602
                    disable_interrupts ( INT_RDA );
603
                    cScreenChanged = NO;
604
                    cSkip = NO;
605
                    LCD_PutCmd ( CLEAR_DISP );
606
                    DisplayTemplateWaypoint();
607
                    enable_interrupts ( INT_RDA );
608
                    }
609
                cSkip = NO;
610
                InitRxBuffer( GPRMB_CODE );     // set code and turn on serial interrupt
611
                while ( ( cRxMsgReady == NO ) && ( cTimeOut != 0 ) && ( cScreenChanged != YES ) );
612
                disable_interrupts ( INT_RDA );         // ignore rest of messages
613
                if ( cScreenChanged == NO )
614
                    {
615
                    if ( cTimeOut != 0 )
616
                        {
617
                        DisplayWaypoint();
618
                        }
619
                    else
620
                        {
621
                        DisplayMessage ( NODATA_MSG );
622
                        }
623
                    }
624
                break;
625
                }
626
            case BATTERY_SCREEN:
627
                {
628
                if ( cScreenChanged == YES )
629
                    {
630
                    disable_interrupts ( INT_RDA );
631
                    cScreenChanged = NO;
632
                    cSkip = NO;
633
                    LCD_PutCmd ( CLEAR_DISP );
634
                    DisplayTemplateAnalog();
635
                    }
636
                DisplayAnalog();
637
                break;
638
                }
639
            }
640
 
641
        // Preset timeout counter each loop; RTCC interrupt decrements, resets if zero is reached
642
        iTimeOut = 2000;     // ~ 30 seconds
643
 
644
        /* Flashing activity indicator in lower right of screen. */
645
        cIndicator ^= 1;
646
        #if ( DISPLAY_COLS == 20 )
647
        LCD_SetPosition ( LINE_4 + 19 );
648
        #elif ( DISPLAY_COLS == 16 )
649
        LCD_SetPosition ( LINE_4 + 15 );
650
        #endif
651
        if ( cIndicator == 1 )
652
            {
653
            printf ( LCD_PutChar, "%c", ACTIVITY_SYMBOL );
654
            }
655
        else
656
            {
657
            printf ( LCD_PutChar, " " );
658
            }
659
        }
660
    }
661
 
662
#separate void DisplayTemplateLatLon ( void )
663
    {
664
    LCD_SetPosition ( LINE_1 );
665
    printf ( LCD_PutChar, "LAT" );
666
    LCD_SetPosition ( LINE_2 );
667
    printf ( LCD_PutChar, "LON" );
668
    LCD_SetPosition ( LINE_3 );
669
    #if ( DISPLAY_COLS == 20 )
670
    printf ( LCD_PutChar, "SPEED" );
671
    LCD_SetPosition ( LINE_4 );
672
    printf ( LCD_PutChar, "HEADING" );
673
    #elif ( DISPLAY_COLS == 16 )
674
    printf ( LCD_PutChar, "SPD" );
675
    LCD_SetPosition ( LINE_4 );
676
    printf ( LCD_PutChar, "HDG" );
677
    #endif
678
    }
679
 
680
#separate void DisplayTemplateWaypoint ( void )
681
    {
682
    LCD_SetPosition ( LINE_1 );
683
    #if ( DISPLAY_COLS == 20 )
684
    printf ( LCD_PutChar, "WAYPOINT" );
685
    #elif ( DISPLAY_COLS == 16 )
686
    printf ( LCD_PutChar, "WAYPT" );
687
    #endif
688
    LCD_SetPosition ( LINE_2 );
689
    printf ( LCD_PutChar, "STEER" );
690
    LCD_SetPosition ( LINE_3 );
691
    printf ( LCD_PutChar, "DIST" );
692
    LCD_SetPosition ( LINE_4 );
693
    printf ( LCD_PutChar, "BEARING" );
694
    }
695
 
696
#separate void DisplayTemplateAnalog ( void )
697
    {
698
    #if ( DISPLAY_COLS == 20 )
699
    LCD_SetPosition ( LINE_1 + 3 );
700
    #elif ( DISPLAY_COLS == 16 )
701
    LCD_SetPosition ( LINE_1 + 1 );
702
    #endif
703
    printf ( LCD_PutChar, "BATTERY STATUS" );
704
    #if ( DISPLAY_COLS == 20 )
705
    LCD_SetPosition ( LINE_2 );
706
    printf ( LCD_PutChar, "Primary" );
707
    LCD_SetPosition ( LINE_3 );
708
    printf ( LCD_PutChar, "Secondary" );
709
    LCD_SetPosition ( LINE_4 );
710
    printf ( LCD_PutChar, "Refrigerator" );
711
    #elif ( DISPLAY_COLS == 16 )
712
    LCD_SetPosition ( LINE_2 );
713
    printf ( LCD_PutChar, "Main#1 " );
714
    LCD_SetPosition ( LINE_3 );
715
    printf ( LCD_PutChar, "Main#2 " );
716
    LCD_SetPosition ( LINE_4 );
717
    printf ( LCD_PutChar, "Refrig " );
718
    #endif
719
    }
720
 
721
#separate void DisplayLatLon ( void )
722
    {
723
    SkipField ( 1 );   // skip UTC
724
    GetField();        // A = OK, V = warning
725
    if ( ( cC [ 0 ] == 'A' ) && ( !cSkip ) )
726
        {
727
        GetField();                   // LAT
728
        if ( !cSkip )
729
            {
730
            DisplayLatitude ( LINE_1 );
731
            }
732
        GetField();                   // LON
733
        if ( !cSkip )
734
            {
735
            DisplayLongitude ( LINE_2 );
736
            }
737
        GetField();                   // SPEED
738
        if ( !cSkip )
739
            {
740
            DisplaySpeed ( LINE_3 );
741
            }
742
        GetField();                   // HEADING
743
        if ( !cSkip )
744
            {
745
            DisplayHeading ( LINE_4 );
746
            }
747
        }
748
    else
749
        {
750
        DisplayMessage( WARNING_MSG );
751
        }
752
    }
753
 
754
#separate void DisplayWaypoint ( void )
755
    {
756
    char cX;
757
 
758
    GetField();        // A = OK, V = warning
759
    if ( ( cC [ 0 ] == 'A' ) && ( !cSkip ) )
760
        {
761
        cX = GetField();        // XTE
762
        if ( !cSkip )
763
            {
764
            DisplaySteer ( LINE_2, cX );
765
            }
766
        SkipField ( 1 );        // skip origin WP ID
767
        GetField();                     // DEST WP ID
768
        if ( !cSkip )
769
            {
770
            DisplayWaypointName ( LINE_1, cX );
771
            }
772
        SkipField ( 4 );        // skip LAT, NS, LON, EW
773
        cX = GetField();                     // RANGE
774
        if ( !cSkip )
775
            {
776
            DisplayDistance ( LINE_3, cX );
777
            }
778
        cX = GetField();                     // BEARING
779
        if ( !cSkip )
780
            {
781
            DisplayBearing ( LINE_4, cX );
782
            }
783
        SkipField ( 1 );        // skip SPEED TO DEST
784
        GetField();                     // ARRIVAL FLAG
785
        if ( !cSkip )
786
            {
787
            DisplayArrival ( LINE_1 );    // overwrite RANGE if arrived
788
            }
789
        }
790
    else
791
        {
792
        DisplayMessage( WARNING_MSG );
793
        }
794
    }
795
 
796
#separate void DisplayAnalog ( void )
797
    {
798
    long iX;
799
    char cCnt;
800
 
801
    set_adc_channel ( 0 );                      // set channel
802
    delay_us ( 100 );                           // wait aquisition time
803
    cAdcDone = NO;
804
    if ( !cSkip )
805
        {
806
        #if ( DISPLAY_COLS == 20 )
807
        LCD_SetPosition ( LINE_2 + 13 );
808
        #elif ( DISPLAY_COLS == 16 )
809
        LCD_SetPosition ( LINE_2 + 8 );
810
        #endif
811
        DisplayScaledVoltage ( read_adc(), MAX_VOLTS );
812
        printf ( LCD_PutChar, " V " );
813
        }
814
    set_adc_channel ( 1 );
815
    delay_us ( 100 );
816
    cAdcDone = NO;
817
    if ( !cSkip )
818
        {
819
        #if ( DISPLAY_COLS == 20 )
820
        LCD_SetPosition ( LINE_3 + 13 );
821
        #elif ( DISPLAY_COLS == 16 )
822
        LCD_SetPosition ( LINE_3 + 8 );
823
        #endif
824
        DisplayScaledVoltage ( read_adc(), MAX_VOLTS );
825
        printf ( LCD_PutChar, " V " );
826
        }
827
    set_adc_channel ( 3 );
828
    delay_us ( 100 );
829
    cAdcDone = NO;
830
    if ( !cSkip )
831
        {
832
        #if ( DISPLAY_COLS == 20 )
833
        LCD_SetPosition ( LINE_4 + 13 );
834
        #elif ( DISPLAY_COLS == 16 )
835
        LCD_SetPosition ( LINE_4 + 8 );
836
        #endif
837
        DisplayScaledVoltage ( read_adc(), MAX_VOLTS );
838
        printf ( LCD_PutChar, " V " );
839
        }
840
    Delay5mS ( 100 );         // slow loop down a bit
841
    }
842
 
843
#separate void GetUtcAndMagVar ( void )
844
    {
845
    /*
846
    This is a non-display version of the RMC sentence
847
    to get the A/V warning, the magnetic variation, and the
848
    magnetic direction.
849
    */
850
 
851
    GetField();              // get UTC
852
    GetField();        // A = OK, V = warning
853
    if ( cC [ 0 ] == 'A' )
854
        {
855
        SkipField ( 7 );   // skip fields
856
        GetField();             // MAGNETIC VARIATION
857
        iVar = FieldFiveToLong();     // save to global variable, used in other sentences
858
        GetField();     // EW
859
        cVarDir = cC [ 0 ];     // save direction
860
        }
861
    else
862
        {
863
        iVar = NULL;              // invalid
864
        cVarDir = SPACE;
865
        }
866
    }
867
 
868
/******************************************************************/
869
 
870
#separate void DisplayScaledVoltage ( long iV, char cScale )
871
    {
872
    float fX;
873
 
874
    /*
875
 
876
    scales it to something else.
877
    */
878
    while ( cAdcDone == NO );         // wait for completion by ADC interrupt
879
    if ( iV == 1023 )
880
        {
881
        printf ( LCD_PutChar, "O/L" );  /* print it to the screen */
882
        }
883
    else
884
        {
885
        fX = ( ( float ) iV ) / 1023 * ( float ) cScale;   // scale to proper range, 1023 leaves room for out-of-range
886
        printf ( LCD_PutChar, "%2.1f", fX );  /* print it to the screen */
887
        }
888
    }
889
 
890
#separate void DisplayArrival ( char cLine )
891
    {
892
    #if ( DISPLAY_COLS == 20 )
893
    LCD_SetPosition ( cLine + 11 );
894
    #elif ( DISPLAY_COLS == 16 )
895
    LCD_SetPosition ( cLine + 9 );
896
    #endif
897
    if ( cC [ 0 ] == 'A' )
898
        {
899
        printf ( LCD_PutChar, "Arrived" );
900
        }
901
    else
902
        {
903
        printf ( LCD_PutChar, "       " );
904
        }
905
    }
906
 
907
#separate void DisplayWaypointName ( char cLine, char cX )
908
    {
909
    /* Displays waypoint name, pads field with blanks */
910
    char cChar, cI;
911
 
912
    LCD_SetPosition ( cLine );
913
    if ( cX != 0 )
914
        {
915
        printf ( LCD_PutChar, "\"" );
916
        for ( cI = 0; cI < 6; cI++ )
917
            {
918
            cChar = cC [ cI ];
919
            if ( cChar == EOF )
920
                {
921
                break;
922
                }
923
            printf ( LCD_PutChar, "%c", cChar );
924
            }
925
        printf ( LCD_PutChar, "\"" );
926
        // Blank remainder of field
927
        cChar = SPACE;
928
        for ( ; cI < 6; cI++ )
929
            {
930
            printf ( LCD_PutChar, "%c", cChar );
931
            }
932
        }
933
    else
934
        {
935
        printf ( LCD_PutChar, "- none -" );
936
        }
937
    }
938
 
939
#separate void DisplaySteer ( char cLine, char cX )
940
    {
941
    /*
942
    Displays A.BC literals, appends 'L' or 'R'.
943
    If less than 1.0, displays feet rather than nm.
944
    Doesn't display distance if on track.
945
    */
946
    long iX;
947
    char cCnt;
948
 
949
    if ( cX != 0 )
950
        {
951
        if ( ( cC [ 0 ] != '0' ) || ( cC [ 2 ] != '0' ) || ( cC [ 3 ] != '0' ) )   // if not 0.00
952
            {
953
            LCD_SetPosition ( cLine + 14 );
954
            #if ( DISPLAY_COLS == 20 )
955
            printf ( LCD_PutChar, "      " );         // blank possible characters
956
            LCD_SetPosition ( cLine + 11 );
957
            #elif ( DISPLAY_COLS == 16 )
958
            printf ( LCD_PutChar, "  " );         // blank possible characters
959
            LCD_SetPosition ( cLine + 8);
960
            #endif
961
            if ( cC [ 0 ] == '0' )          // if less than 1.0 nm, display as feet
962
                {
963
                iX = ( 528 * ( long ) ( cC [ 2 ] - 0x30 ) ) + ( 52 * ( long ) ( cC [ 3 ] - 0x30 ) );
964
                printf ( LCD_PutChar, "%luft  ", iX );
965
                }
966
            else                             // if 1.0 nm or greater, display as nautical miles
967
                {
968
                printf ( LCD_PutChar, "%c%c%c%cmi  ", cC [ 0 ], cC [ 1 ], cC [ 2 ] , cC [ 3 ] );
969
                }
970
            GetField();              // L or R
971
            LCD_SetPosition ( cLine + 6 );
972
            if ( cC [ 0 ] == 'L' )
973
                {
974
                #if ( DISPLAY_COLS == 20 )
975
                printf ( LCD_PutChar, "PORT " );
976
                #elif ( DISPLAY_COLS == 16 )
977
                printf ( LCD_PutChar, "L" );
978
                #endif
979
                }
980
            else
981
                {
982
                #if ( DISPLAY_COLS == 20 )
983
                printf ( LCD_PutChar, "STBD " );
984
                #elif ( DISPLAY_COLS == 16 )
985
                printf ( LCD_PutChar, "R" );
986
                #endif
987
                }
988
            }
989
        else           // if 0.00
990
            {
991
            #if ( DISPLAY_COLS == 20 )
992
            LCD_SetPosition ( cLine + 11 );
993
            printf ( LCD_PutChar, "On track " );
994
            #elif ( DISPLAY_COLS == 16 )
995
            LCD_SetPosition ( cLine + 6 );
996
            printf ( LCD_PutChar, "  On track" );
997
            #endif
998
            GetField();              // dummy L or R
999
            }
1000
        }
1001
    else
1002
        {
1003
        LCD_SetPosition ( cLine + 6 );
1004
        printf ( LCD_PutChar, "              " );
1005
        }
1006
    }
1007
 
1008
#separate void DisplayDistance ( char cLine, char cX )
1009
    {
1010
    /* Format: ABC.D nautical miles */
1011
    char cChar, cI;
1012
    long iThisRange;
1013
 
1014
    if ( cX != 0 )           // if waypoint data to display
1015
        {
1016
        #if ( DISPLAY_COLS == 20 )
1017
        LCD_SetPosition ( cLine + 11 );
1018
        #elif ( DISPLAY_COLS == 16 )
1019
        LCD_SetPosition ( cLine + 8 );
1020
        #endif
1021
        cI = 0;
1022
        for ( cI = 0; cI < 2; cI++ )    // find first non-zero
1023
            {
1024
            cChar = cC [ cI ];
1025
            if ( cChar != '0' )
1026
                {
1027
                break;
1028
                }
1029
            }
1030
        for ( ; cI < 5; cI++ )    // display from there on
1031
            {
1032
            printf ( LCD_PutChar, "%c", cC [ cI ] );
1033
            }
1034
        printf ( LCD_PutChar, "nm  " );     // pad with blanks
1035
 
1036
        /*
1037
        The least significant character from the GPS is 0.1 nm.
1038
        Multiply whole thing by 10 and make it type long.
1039
        Discern if increasing (FROM) or decreasing (TO).
1040
        */
1041
        iThisRange = 1000 * ( long ) ( cC [ 0 ] - 0x30 );
1042
        iThisRange += 100 * ( long ) ( cC [ 1 ] - 0x30 );
1043
        iThisRange += 10 * ( long ) ( cC [ 2 ] - 0x30 );
1044
        iThisRange += ( long ) ( cC [ 4 ] - 0x30 );
1045
        if ( iThisRange < iLastRange )
1046
            {
1047
            #if ( DISPLAY_COLS == 20 )
1048
            strcpy ( cToFrom, "TO  " );
1049
            #elif ( DISPLAY_COLS == 16 )
1050
            strcpy ( cToFrom, "TO" );
1051
            #endif
1052
            }
1053
        if ( iThisRange > iLastRange )
1054
            {
1055
            #if ( DISPLAY_COLS == 20 )
1056
            strcpy ( cToFrom, "FROM" );
1057
            #elif ( DISPLAY_COLS == 16 )
1058
            strcpy ( cToFrom, "FM" );
1059
            #endif
1060
            }
1061
        iLastRange = iThisRange;    // save this range to compare next time
1062
        LCD_SetPosition ( cLine + 5 );
1063
        printf ( LCD_PutChar, cToFrom );
1064
        }
1065
    else
1066
        {
1067
        LCD_SetPosition ( cLine + 5 );
1068
        #if ( DISPLAY_COLS == 20 )
1069
        printf ( LCD_PutChar, "               " );
1070
        #elif ( DISPLAY_COLS == 16 )
1071
        printf ( LCD_PutChar, "          " );
1072
        #endif
1073
        }
1074
    }
1075
 
1076
#separate void DisplayBearing ( char cLine, char cX )
1077
    {
1078
    /*
1079
    Compass variation comes from RMC sentence.  If RMC has not run yet
1080
    then "T" is displayed after bearing.
1081
    */
1082
    long iHdg;
1083
    char cTrueIndicator;
1084
 
1085
    if ( cX != 0 )           // if waypoint data to display
1086
        {
1087
        #if ( DISPLAY_COLS == 20 )
1088
        LCD_SetPosition ( cLine + 11 );
1089
        #elif ( DISPLAY_COLS == 16 )
1090
        LCD_SetPosition ( cLine + 8 );
1091
        #endif
1092
        iHdg = FieldFiveToLong();
1093
        iHdg = TrueToMag ( iHdg );    // factor variation into heading
1094
        if ( ( iVar == NULL ) || ( cVarDir == SPACE ) )
1095
            {
1096
            cTrueIndicator = 'T';
1097
            }
1098
        else
1099
            {
1100
            cTrueIndicator = ' ';
1101
            }
1102
        printf ( LCD_PutChar, "%lu%c%c  ", iHdg, DEGREE, cTrueIndicator );   // pad with blanks
1103
        }
1104
    else
1105
        {
1106
        #if ( DISPLAY_COLS == 20 )
1107
        LCD_SetPosition ( cLine + 11 );
1108
        printf ( LCD_PutChar, "         " );
1109
        #elif ( DISPLAY_COLS == 16 )
1110
        LCD_SetPosition ( cLine + 8 );
1111
        printf ( LCD_PutChar, "        " );
1112
        #endif
1113
        }
1114
    }
1115
 
1116
#separate void DisplayLatitude ( char cLine )
1117
    {
1118
    /* Displays latitude ABCD.EF as AB CD.EF, appends 'N' or 'S' */
1119
    #if ( DISPLAY_COLS == 20 )
1120
    LCD_SetPosition ( cLine + 8 );
1121
    #elif ( DISPLAY_COLS == 16 )
1122
    LCD_SetPosition ( cLine + 5 );
1123
    #endif
1124
    if ( cC [ 0 ] == '0' )
1125
        {
1126
        cC [ 0 ] = SPACE;
1127
        }
1128
    printf ( LCD_PutChar, "%c%c%c", cC [ 0 ], cC [ 1 ], DEGREE );
1129
    printf ( LCD_PutChar, "%c%c%c%c%c%c", cC [ 2 ], cC [ 3 ], cC [ 4 ], cC [ 5 ], cC [ 6 ], cC [ 7 ] );
1130
    GetField();              // NS
1131
    printf ( LCD_PutChar, " %c", cC [ 0 ] );
1132
    }
1133
 
1134
#separate void DisplayLongitude ( char cLine )
1135
    {
1136
    /* Displays longitude ABCDE.FG as ABC DE.FG, appends 'E' or 'W' */
1137
    #if ( DISPLAY_COLS == 20 )
1138
    LCD_SetPosition ( cLine + 7 );
1139
    #elif ( DISPLAY_COLS == 16 )
1140
    LCD_SetPosition ( cLine + 4 );
1141
    #endif
1142
    if ( cC [ 0 ] == '0' )
1143
        {
1144
        cC [ 0 ] = SPACE;
1145
        }
1146
    if ( cC [ 1 ] == '0' )
1147
        {
1148
        cC [ 1 ] = SPACE;
1149
        }
1150
    printf ( LCD_PutChar, "%c%c%c%c", cC [ 0 ], cC [ 1 ], cC [ 2 ], DEGREE );
1151
    printf ( LCD_PutChar, "%c%c%c%c%c%c", cC [ 3 ], cC [ 4 ], cC [ 5 ], cC [ 6 ], cC [ 7 ], cC [ 8 ] );
1152
    GetField();              // EW
1153
    printf ( LCD_PutChar, " %c", cC [ 0 ] );
1154
    }
1155
 
1156
#separate void DisplaySpeed ( char cLine )
1157
    {
1158
    float fX;
1159
 
1160
    // Format ABC.D
1161
    #if ( DISPLAY_COLS == 20 )
1162
    LCD_SetPosition ( cLine + 8 );
1163
    #elif ( DISPLAY_COLS == 16 )
1164
    LCD_SetPosition ( cLine + 5 );
1165
    #endif
1166
    fX = 100 * ( cC [ 0 ] - 0x30 );
1167
    fX += 10 * ( cC [ 1 ] - 0x30 );
1168
    fX += 1 * ( cC [ 2 ] - 0x30 );
1169
    fX += 0.1 * ( cC [ 4 ] - 0x30 );
1170
    #if SPEED_UNITS == 2
1171
    fX *= 1.852;        // convert knots to km/h
1172
    #endif
1173
    #if SPEED_UNITS == 3
1174
    fX *= 1.151;        // convert knots to mi/h
1175
    #endif
1176
    printf ( LCD_PutChar, "%3.1f ", fX );  // print it to the screen
1177
    #if SPEED_UNITS == 1
1178
    printf ( LCD_PutChar, "kts    " );     // print it to the screen
1179
    #endif
1180
    #if SPEED_UNITS == 2
1181
    printf ( LCD_PutChar, "kph    " );     // print it to the screen
1182
    #endif
1183
    #if SPEED_UNITS == 3
1184
    printf ( LCD_PutChar, "mph    " );     // print it to the screen
1185
    #endif
1186
    }
1187
 
1188
#separate void DisplayHeading ( char cLine )
1189
    {
1190
    long iHdg;
1191
 
1192
    #if ( DISPLAY_COLS == 20 )
1193
    LCD_SetPosition ( cLine + 8 );
1194
    #elif ( DISPLAY_COLS == 16 )
1195
    LCD_SetPosition ( cLine + 5 );
1196
    #endif
1197
    iHdg = FieldFiveToLong();
1198
    SkipField ( 1 );     // skip fix date
1199
    GetField();             // MAGNETIC VARIATION
1200
    iVar = FieldFiveToLong();     // save to global variable, used in other sentences
1201
    GetField();     // EW
1202
    cVarDir = cC [ 0 ];     // save direction
1203
    iHdg = TrueToMag ( iHdg );    // factor variation into heading
1204
    printf ( LCD_PutChar, "%lu%c  ", iHdg, DEGREE );    // pad with blanks
1205
    }
1206
 
1207
#separate long FieldFiveToLong ( void )
1208
    {
1209
    /* Converts ABC.D to long, rounds decimal up or down */
1210
    long iX;
1211
 
1212
    iX = 100 * ( long ) ( cC [ 0 ] - 0x30 );
1213
    iX += 10 * ( long ) ( cC [ 1 ] - 0x30 );
1214
    iX += ( long ) ( cC [ 2 ] - 0x30 );
1215
    if ( ( cC [ 3 ] == PERIOD ) && ( cC [ 4 ] >= '5' ) )
1216
        {
1217
        iX++;           // round up
1218
        }
1219
    return ( iX );
1220
    }
1221
 
1222
#separate long TrueToMag ( long iH )
1223
    {
1224
    /* Magnetic variation information comes from the RMC sentence */
1225
 
1226
    if ( cVarDir == 'W' )
1227
        {
1228
        iH += iVar;
1229
        }
1230
    else
1231
        {
1232
        if ( iH >= iVar )
1233
            {
1234
            iH -= iVar;     // OK as-is
1235
            }
1236
        else
1237
            {
1238
            iH = iH + 360 - iVar;   // correct for below zero
1239
            }
1240
        }
1241
    if ( iH >= 360 )
1242
        {
1243
        iH -= 360;
1244
        }
1245
    return ( iH );
1246
    }
1247
 
1248
#separate void DisplayMessage ( char cMsgNum )
1249
    {
1250
    LCD_PutCmd ( CLEAR_DISP );
1251
    LCD_SetPosition ( LINE_2 );
1252
    switch ( cMsgNum )
1253
        {
1254
        case WARNING_MSG:
1255
            {
1256
            #if ( DISPLAY_COLS == 20 )
1257
            printf ( LCD_PutChar,     "    GPS warning   " );
1258
            #elif ( DISPLAY_COLS == 16 )
1259
            printf ( LCD_PutChar, "  GPS warning" );
1260
            #endif
1261
            break;
1262
            }
1263
        case NODATA_MSG:
1264
            {
1265
            if ( cRxErrorFlag == OFF )    // is it a framing error problem ?
1266
                {
1267
                #if ( DISPLAY_COLS == 20 )
1268
                printf ( LCD_PutChar, "  No data from GPS" );
1269
                #elif ( DISPLAY_COLS == 16 )
1270
                printf ( LCD_PutChar, "No data from GPS" );
1271
                #endif
1272
                }
1273
            else
1274
                {
1275
                #if ( DISPLAY_COLS == 20 )
1276
                printf ( LCD_PutChar, "     Baud error" );
1277
                #elif ( DISPLAY_COLS == 16 )
1278
                printf ( LCD_PutChar, "   Baud error" );
1279
                #endif
1280
                cRxErrorFlag = OFF;
1281
                }
1282
            break;
1283
            }
1284
        }
1285
    Delay5mS ( 255 );                   // delay 1.25 seconds
1286
    iVar = NULL;
1287
    cVarDir = SPACE;                     // signal "no magnetic variation" yet
1288
    cScreenChanged = YES;
1289
    }
1290
 
1291
#separate void Delay5mS ( char cCnt )
1292
    {
1293
    char cX;
1294
 
1295
    /* This variable-count 5mS delay is interruptable by a button press */
1296
    for ( cX = 0; cX < cCnt; cX++ )
1297
        {
1298
        if ( cScreenChanged == YES )
1299
            {
1300
            break;
1301
            }
1302
        delay_ms ( 5 );
1303
        }
1304
    }
1305
 
1306
#separate char GetField ( void )
1307
    {
1308
    char cX, cIndex;
1309
 
1310
    cX = NULL;
1311
    cIndex = 0;
1312
    while ( !cSkip )
1313
        {
1314
        cX = GetRxChar();
1315
        if ( ( cX == COMMA ) || ( cX == CR ) )
1316
            {
1317
            break;
1318
            }
1319
        cC [ cIndex++ ] = cX;
1320
        }
1321
    cC [ cIndex ] = EOF;
1322
    return ( cIndex );         // return number of characters in field
1323
    }
1324
 
1325
#separate void SkipField ( char cCnt )
1326
    {
1327
    char cX;
1328
 
1329
    for ( cX = 0; cX < cCnt; cX++ )
1330
        {
1331
        while ( GetRxChar() != COMMA );
1332
        }
1333
    }
1334
 
1335
/* RS232 FUNCTIONS ================================================== */
1336
 
1337
#separate void InitRxBuffer ( char cCode )
1338
    {
1339
    disable_interrupts ( INT_RDA );
1340
    cRxBufferWritePtr = cRxBuffer;      // point to beginning of buffer
1341
    cRxBufferReadPtr = cRxBuffer;
1342
    cRxByteCnt = 0;
1343
    cRxIsrState = 0;
1344
    cRxMsgReady = NO;
1345
    cRxMsgTypeDesired = cCode;
1346
    enable_interrupts ( INT_RDA );
1347
    }
1348
 
1349
#separate char GetRxChar ( void )
1350
    {
1351
    // Get the next available byte in the recv fifo.
1352
    // Call this function ONLY if the recv fifo contains data.
1353
    char cValue;
1354
 
1355
    cValue = 0;
1356
    if ( cRxByteCnt > 0 )       // For safety, check if there is any data
1357
        {
1358
        cValue = *cRxBufferReadPtr++;     // Read byte from fifo
1359
        if ( cRxBufferReadPtr == ( cRxBuffer + RX_BUFFER_SIZE ) ) // Did tail ptr wrap ?
1360
            {
1361
            cRxBufferReadPtr = cRxBuffer;    // If so, reset it to start of buffer
1362
            }
1363
        cRxByteCnt--; // Decrement byte count
1364
        }
1365
    return ( cValue );
1366
    }
1367
 
1368
/* LCD FUNCTIONS ================================= */
1369
 
1370
#separate void LCD_Init ( void )
1371
    {
1372
    LCD_SetData ( 0x00 );
1373
    delay_ms ( 200 );       /* wait enough time after Vdd rise */
1374
    output_low ( LCD_RS );
1375
    LCD_SetData ( 0x03 );   /* init with specific nibbles to start 4-bit mode */
1376
    LCD_PulseEnable();
1377
    LCD_PulseEnable();
1378
    LCD_PulseEnable();
1379
    LCD_SetData ( 0x02 );   /* set 4-bit interface */
1380
    LCD_PulseEnable();      /* send dual nibbles hereafter, MSN first */
1381
    LCD_PutCmd ( 0x2C );    /* function set (all lines, 5x7 characters) */
1382
    LCD_PutCmd ( 0x0C );    /* display ON, cursor off, no blink */
1383
    LCD_PutCmd ( 0x01 );    /* clear display */
1384
    LCD_PutCmd ( 0x06 );    /* entry mode set, increment & scroll left */
1385
    }
1386
 
1387
#separate void LCD_SetPosition ( unsigned int cX )
1388
    {
1389
    /* this subroutine works specifically for 4-bit Port A */
1390
    LCD_SetData ( swap ( cX ) | 0x08 );
1391
    LCD_PulseEnable();
1392
    LCD_SetData ( swap ( cX ) );
1393
    LCD_PulseEnable();
1394
    }
1395
 
1396
#separate void LCD_PutChar ( unsigned int cX )
1397
    {
1398
    /* this subroutine works specifically for 4-bit Port A */
1399
    if ( !cSkip )
1400
        {
1401
        output_high ( LCD_RS );
1402
        LCD_SetData ( swap ( cX ) );     /* send high nibble */
1403
        LCD_PulseEnable();
1404
        LCD_SetData ( swap ( cX ) );     /* send low nibble */
1405
        LCD_PulseEnable();
1406
        output_low ( LCD_RS );
1407
        }
1408
    }
1409
 
1410
#separate void LCD_PutCmd ( unsigned int cX )
1411
    {
1412
    /* this subroutine works specifically for 4-bit Port A */
1413
    LCD_SetData ( swap ( cX ) );     /* send high nibble */
1414
    LCD_PulseEnable();
1415
    LCD_SetData ( swap ( cX ) );     /* send low nibble */
1416
    LCD_PulseEnable();
1417
    }
1418
 
1419
#separate void LCD_PulseEnable ( void )
1420
    {
1421
    output_high ( LCD_EN );
1422
    delay_us ( 3 );         // was 10
1423
    output_low ( LCD_EN );
1424
    delay_ms ( 3 );         // was 5
1425
    }
1426
 
1427
#separate void LCD_SetData ( unsigned int cX )
1428
    {
1429
    output_bit ( LCD_D0, cX & 0x01 );
1430
    output_bit ( LCD_D1, cX & 0x02 );
1431
    output_bit ( LCD_D2, cX & 0x04 );
1432
    output_bit ( LCD_D3, cX & 0x08 );
1433
    }
1434