// Change the following to change the clock frequency
#define CRYSTAL_FREQ 16000000
// Speed units are "1" (nautical knots), "2" (metric kph), or "3" (statute mph)
#define SPEED_UNITS 1
/****************************************************************************
GPS21f.c
WORKING CODE
used with Garmin GPSMAP 172C
used on vessel "Northern Light" Malletts Bay, Vermont, USA
+5 +5 +5
| | |
20 15 2
---------- ----------
| |-24-----11-|DB4 A Vdd |
| |-28-----12-|DB5 |
ADC0 ------2-| |-26-----13-|DB6 |
ADC1 ------3-| 18F252 |-27-----14-|DB7 Vo|
ADC2 ------5-| | | LCD |
| |-14------6-|EN |
XTAL--9-| |-15------4-|R/S |
XTAL-10-| |-25-FET-16-|K |
| | | |
BUTTON 1---21-| |-12------3-|V0 |
BUTTON 2---22-| | | RW Vss |
BUTTON 3---23-| | ----------
| | 1 5
Tach In ----13-| | | |
~Seatalk In ----16-| | Gnd Gnd
~NMEA-0183 In ----18-| |
| |-11------- GARMIN/NMEA selector
----------
8 19
| |
Gnd Gnd
***************************************************************************/
#case
#include < 18F252.h >
#device *=16 ADC=10 /* allow RAM addresses over 255 */
#include < jonsinc.h >
#fuses HS, NOPROTECT, NOOSCSEN, BROWNOUT, NOWDT, BORV20, PUT, NOSTVREN, NODEBUG, NOLVP, NOWRT, NOWRTD, NOWRTB
#if ( ( CRYSTAL_FREQ < 4000000) || ( CRYSTAL_FREQ > 20000000 ) )
#error "CRYSTAL FREQ" not defined to between 8000000 and 20000000
#endif
// RMC_TIME = 1 per clock megahertz, rounded
#define RMC_TIME CRYSTAL_FREQ/1000000
// DISPLAY DEFINES ========================
#define LCD_D4 PIN_B3
#define LCD_D5 PIN_B7
#define LCD_D6 PIN_B5
#define LCD_D7 PIN_B6
#define LCD_EN PIN_C3
#define LCD_RS PIN_C4
#define RX_IN PIN_C7
#define LCD_BACKLITE PIN_B4
#define LINE_1 0x00
#define LINE_2 0x40
#define LINE_3 0x14
#define LINE_4 0x54
#define CLEAR_DISP 0x01
// MISC DEFINES ===========================
#define EOF 0x00
#define COMMA ','
#define CR 13
#define SPACE ' '
#define PERIOD '.'
#define DEGREE 0xdf
#define DOLLAR '$'
#define NULL 0
#define BUTTON_1 PIN_B0
#define BUTTON_2 PIN_B1
#define BUTTON_3 PIN_B2
#define USB_NMEA_GARMIN PIN_C0
#define NMEA_CONNECTED 0
#define GARMIN_CONNECTED 1
#define GPRMC_CODE 75
#define GPRMB_CODE 74
#define RX_BUFFER_SIZE 70
#define MAX_VOLTS 15
// EEPROM ASSIGNMENTS
#define EEPROM_CONTRAST 0
#define EEPROM_INITIAL 2
#define EEPROM_BACKLIGHT 4
#define EEPROM_SCREENROLL 6
#define EEPROM_USB_PROTOCOL 8
// First-time-write 16-bit eeprom
// partial contrast, first screen, backlight off, screen roll on, NMEA connected
#rom 0xf00000 = { 150, 1, 0, 0, 0 }
// SCREEN DEFINES ====================
#define POSITION_SCREEN 1
#define WAYPOINT_SCREEN 2
#define BATTERY_SCREEN 3
#define TACH_SCREEN 4
#define TANK_SCREEN 5
#define INST_SCREEN 6
#define HIDDEN_RMC 7
#define USB_SCREEN 8
#define WARNING_MSG 0
#define NODATA_MSG 1
#define USB_MSG 2
#define ACTIVITY_SYMBOL 0xFF
#define GPS_BUTTON 0
#define INST_BUTTON 1
#define SYSTEMS_BUTTON 2
#define NUL_BUTTON 0xff
// SCREEN ROLL DEFINES
// screen roll count = display seconds / 0.016
#define SCREEN_ROLL_COUNT 312
// button inactivity count = inactive seconds / 0.016
#define BUTTON_INACTIVITY_COUNT 37500
// TACHOMETER DEFINES ================
#define START_TACH 0
#define RUN_TACH 1
#define DONE_TACH 2
// Pulley factor is alternator pulley diameter divided by crank pulley diameter, plus a fudge factor
#define ENGINE_DIA 1.000
#define ALT_DIA 0.543
#define PULLEY_FACTOR ALT_DIA/ENGINE_DIA
// Obtain alternator poles from manufacturers data sheet (6, 8, 10, etc.)
#define ALTERNATOR_POLES 10
// SEATALK COMMON DEFINES ============
#define NINTH_BIT 7
#define SEATALK_OK 0
#define SEATALK_TIMEOUT 1
#define SEATALK_DATA_ERROR 2
#define SEATALK_RESET_TIME 312
// TANK LEVEL DEFINES ================
#define TANK_MSGNUM 0xFE
#define TANK_ADR 2
#define TANK_LEVEL 3
// INSTRUMENT LEVEL DEFINES ==========
#define INST_ACCUM_TIME 70
#define TEMP_MSGNUM 0x27
#define SPEED_MSGNUM 0x20
#define DEPTH_MSGNUM 0x00
#define HEADING_MSGNUM 0x9c
// DURING GPS CODING ONLY
/* Set the following define to "YES" to display XOR'ed GPS sentence code */
/* such as GPRMC and the display will read out the value of 74. */
#define GET_GPS_CODE NO
#separate void Display ( void );
#separate void LCD_Init ( void );
#separate void LCD_SetPosition ( unsigned int cX );
#separate void LCD_PutChar ( unsigned int cX );
#separate void LCD_PutCmd ( unsigned int cX );
#separate void LCD_PulseEnable ( void );
#separate void LCD_SetData ( unsigned int cX );
#separate void SkipField ( char cCnt );
#separate char GetField ( void );
#separate void InitRxBuffer ( char cCode );
#separate char GetRxChar ( void );
#separate void DisplayPosition ( void );
#separate void DisplayWaypoint ( void );
#separate void DisplayLatitude ( char cLine );
#separate void DisplayLongitude ( char cLine );
#separate void DisplayHeading ( char cLine );
#separate void DisplaySpeed ( char cLine );
#separate void DisplaySteer ( char cLine, char cX );
#separate void DisplayWaypointName ( char cLine, char cX );
#separate void DisplayDistance ( char cLine, char cX );
#separate void DisplayBearing ( char cLine, char cX );
#separate void GetUtcAndMagVar ( void );
#separate long TrueToMag ( long iH );
#separate long FieldFiveToLong ( void );
#separate void JustifyDecimalPoint ( void );
#separate void DisplayAnalog ( void );
#separate void DisplayScaledVoltage ( long iV, char cScale );
#separate void DisplayArrival ( char cLine );
#separate void DisplayMessage ( char cMsgNum );
#separate void DisplayTemplateLatLon ( void );
#separate void DisplayTemplateWaypoint ( void );
#separate void DisplayTemplateAnalog ( void );
#separate void Delay5mS ( char cCnt );
#separate void DisplayTemplateTach ( void );
#separate void DisplayTemplateTank ( void );
#separate void DisplayTemplateInstruments ( void );
#separate void DisplayTach ( void );
#separate void DisplayTank ( void );
#separate void DisplayInstruments ( void );
#separate char ReceiveSeatalkMsg ( void );
#separate void WriteBlanks ( char cCnt );
#separate void ToggleActivityIndicator ( void );
#use standard_io ( A )
#use standard_io ( B )
#use standard_io ( C )
#use delay ( clock = CRYSTAL_FREQ )
#use rs232 ( baud = 4800, xmit = PIN_C6, rcv = PIN_C7, errors, stream = 1 ) // for GPS interface, hardward USART
#use rs232 ( baud = 4800, rcv = PIN_C5, bits = 9, INVERT, errors, stream = 2 ) // for SeaTalk tank level interface
#priority RDA, RTCC
static char cC [ 10 ]; // local buffer
static char cNmeaDataTimeOut;
static char cRxBuffer [ RX_BUFFER_SIZE ]; // Fifo
static char cRxByteCnt; // Number of bytes in the recv fifo
static char *cRxBufferWritePtr; // Pointers for the Rx buffer
static char *cRxBufferReadPtr;
static char cRxIsrState, cRxMsgTypeReceived, cRxMsgTypeDesired;
static char cRxMsgReady, cReceiveFlag;
static long iVar, iLastRange, iTimeOut;
static char cVarDir, cScreenChanged, cAdcDone;
static char cSkip, cButton1Count, cButton2Count, cButton3Count;
static char cScreen, cSavedScreen, cRmcTimer1, cRmcTimer2;
static char cToFrom [ 5 ], cIndicator, cIllumination, cRxErrorFlag;
static char cDone, cContrast, cLastButton;
static char cLastGpsScreen, cLastInstScreen, cLastSystemsScreen;
static char cTachState;
static long iTachCount;
static char cSwitch1State, cSwitch2State, cSwitch3State;
static char cOldSwitch1State, cOldSwitch2State, cOldSwitch3State;
static char cNewSwitch1State, cNewSwitch2State, cNewSwitch3State;
static char cBusTimeout, cSeatalkChar [ 18 ];
static long iSeatalkTimeout0, iSeatalkTimeout1, iSeatalkTimeout2;
static long iSeatalkTimeout3, iSeatalkTimeout4;
static char cSeatalkDisplay;
static char cInstDepthHi, cInstDepthLo, cInstSpeedHi, cInstSpeedLo;
static char cInstHeadingHi, cInstHeadingLo, cInstTempHi, cInstTempLo;
static char cTankLevel0, cTankLevel1, cTankLevel2;
static long iScreenRollCount, iButtonInactivityCount;
static char cScreenRoll;
static char cUsbConnection;
/*******************************************************************/
#int_ad
void AdcInterrupt ( void )
{
/* Gets here when ADC is done conversion, sets flag */
cAdcDone = YES;
}
#int_timer1
void Timer1Interrupt ( void )
{
/* Periodic RMC data timer, gets here every 204mS */
/* This routine forces RMC to run every 10 minutes to update */
/* magnetic variation */
if ( cRmcTimer1-- == 0 )
{
cRmcTimer1 = 255; // 52 seconds @ 10.240MHz
if ( cRmcTimer2-- == 0 )
{
cRmcTimer2 = RMC_TIME; // triggers every 10 minutes
cSavedScreen = cScreen; // save current screen type
cScreen = HIDDEN_RMC; // force RMC to run
}
}
}
#int_ccp1
void CCP1Interrupt ( void )
{
// for tachometer
if ( cTachState == RUN_TACH ) // second edge
{
iTachCount = CCP_1; // get capture value
cTachState = DONE_TACH; // prevent further processing during this interrupt }
}
if ( cTachState == START_TACH ) // first edge
{
set_timer1 ( 0 ); // restart timer on this edge
cTachState = RUN_TACH;
}
}
#int_timer0
void Timer0Interrupt ( void )
{
// Timer interrupt gets here every 16.4mS
// Handles all timeouts and switch debounce.
// GPS DATA TIMEOUT TIMER
if ( cNmeaDataTimeOut != 0 )
{
cNmeaDataTimeOut--;
}
// HUNG LOOP TIMEOUT
// This timer is preset by the normal operating loop,
// unless the operating loop stops looping, at which
// point code finally decrements to zero and resets CPU.
if ( iTimeOut != 0 )
{
iTimeOut--;
}
else
{
reset_cpu(); // force reset
}
// SEATALK TIMEOUTS
// Gather seatalk data vs. display seatalk data timeout
if ( cSeatalkDisplay != 0 )
{
cSeatalkDisplay--;
}
// lack of data timeout
if ( iSeatalkTimeout0 != 0 )
{
iSeatalkTimeout0--;
}
if ( iSeatalkTimeout1 != 0 )
{
iSeatalkTimeout1--;
}
if ( iSeatalkTimeout2 != 0 )
{
iSeatalkTimeout2--;
}
if ( iSeatalkTimeout3 != 0 )
{
iSeatalkTimeout3--;
}
if ( iSeatalkTimeout4 != 0 )
{
iSeatalkTimeout4--;
}
// SCREEN ROLL
// determine how long since a button was pressed
if ( iButtonInactivityCount != 0 )
{
iButtonInactivityCount--;
}
if ( iScreenRollCount != 0 )
{
iScreenRollCount--;
}
if ( cScreenRoll == YES )
{
if ( ( iScreenRollCount == 0 ) && ( iButtonInactivityCount == 0 ) )
{
switch ( cScreen )
{
case POSITION_SCREEN:
{
cScreen = WAYPOINT_SCREEN;
break;
}
case WAYPOINT_SCREEN:
{
cScreen = INST_SCREEN;
break;
}
case INST_SCREEN:
{
cScreen = BATTERY_SCREEN;
break;
}
case BATTERY_SCREEN:
{
cScreen = TACH_SCREEN;
break;
}
case TACH_SCREEN:
{
cScreen = TANK_SCREEN;
break;
}
case TANK_SCREEN:
{
cScreen = POSITION_SCREEN;
break;
}
}
cScreenChanged = YES;
cSkip = YES;
iScreenRollCount = SCREEN_ROLL_COUNT;
}
}
// SWITCH 1 DEBOUNCE
if ( input ( BUTTON_1 ) == LOW ) // if button still pressed
{
if ( cButton1Count < 255 ) // hold at 255
{
cButton1Count++; // otherwise increment
}
}
else // if button is unpressed
{
if ( cButton1Count > 2 ) // filter out glitches
{
//If button press is greater than 4 seconds, cold reset
if ( cButton1Count == 255 )
{
reset_cpu();
}
// If button press is 1 second
if ( ( cButton1Count > 31 ) && ( cButton1Count < 255 ) )
{
if ( cScreen != HIDDEN_RMC ) // if not in the middle of getting magnetic variation
{
// cIllumination ^= ON;
output_bit ( LCD_BACKLITE, cIllumination ^= ON );
}
}
// If button press is less than 0.5 second
if ( cButton1Count <= 31 )
{
iButtonInactivityCount = BUTTON_INACTIVITY_COUNT;
if ( cScreen != HIDDEN_RMC ) // if not in the middle of getting magnetic variation
{
if ( cLastButton != GPS_BUTTON ) // if here from another screen
{
cSwitch1State = cOldSwitch1State; // use last displayed screen
}
else
{
cSwitch1State = cNewSwitch1State; // otherwise use next screen
}
switch ( cSwitch1State )
{
case 0:
{
cScreen = POSITION_SCREEN;
cNewSwitch1State = 1;
break;
}
case 1:
{
cScreen = WAYPOINT_SCREEN;
cNewSwitch1State = 0;
break;
}
}
cLastButton = GPS_BUTTON; // remember which button
cOldSwitch1State = cSwitch1State; // remember which screen
cSkip = YES; // skip out of anything in process
cScreenChanged = YES; // repaint complete screen
}
}
}
cButton1Count = 0; // restart
}
// SWITCH 2 DEBOUNCE
if ( input ( BUTTON_2 ) == LOW ) // if button still pressed
{
if ( cButton2Count < 255 ) // hold at 255
{
cButton2Count++; // otherwise increment
}
}
else // if button is unpressed
{
if ( cButton2Count > 2 ) // filter out glitches
{
iButtonInactivityCount = BUTTON_INACTIVITY_COUNT;
if ( cLastButton != INST_BUTTON ) // if here from another screen
{
cSwitch2State = cOldSwitch2State; // use last displayed screen
}
else
{
cSwitch2State = cNewSwitch2State; // otherwise use next screen
}
switch ( cSwitch2State )
{
case 0:
{
cScreen = INST_SCREEN;
cNewSwitch2State = 0;
break;
}
}
cLastButton = INST_BUTTON; // remember which button
cOldSwitch2State = cSwitch2State; // remember which screen
cSkip = YES; // skip out of anything in process
cScreenChanged = YES; // repaint complete screen
}
cButton2Count = 0; // restart
}
// SWITCH 3 DEBOUNCE
if ( input ( BUTTON_3 ) == LOW ) // if button still pressed
{
if ( cButton3Count < 255 ) // hold at 255
{
cButton3Count++; // otherwise increment
}
}
else // if button is unpressed
{
if ( cButton3Count > 2 ) // filter out glitches
{
// If button press is 1 second
if ( ( cButton3Count > 31 ) && ( cButton3Count < 255 ) )
{
if ( cScreen != HIDDEN_RMC ) // if not in the middle of getting magnetic variation
{
// flip USB connection state
output_bit ( USB_NMEA_GARMIN, cUsbConnection ^= 1 );
cSavedScreen = cScreen; // save current screen type
cScreen = USB_SCREEN; // force USB screen to show briefly
}
}
// If button press is less than 0.5 second
if ( cButton3Count <= 31 )
{
iButtonInactivityCount = BUTTON_INACTIVITY_COUNT;
if ( cLastButton != SYSTEMS_BUTTON ) // if here from another screen
{
cSwitch3State = cOldSwitch3State; // use last displayed screen
}
else
{
cSwitch3State = cNewSwitch3State; // otherwise use next screen
}
switch ( cSwitch3State )
{
case 0:
{
cScreen = BATTERY_SCREEN;
cNewSwitch3State = 1;
break;
}
case 1:
{
cScreen = TACH_SCREEN;
cNewSwitch3State = 2;
break;
}
case 2:
{
cScreen = TANK_SCREEN;
cNewSwitch3State = 0;
break;
}
}
}
cLastButton = SYSTEMS_BUTTON; // remember which button
cOldSwitch3State = cSwitch3State; // remember which screen
cSkip = YES; // skip out of anything in process
cScreenChanged = YES; // repaint complete screen
}
cButton3Count = 0; // restart
}
}
#int_rda
void SerialInterrupt ( void )
{
/*
Reads incoming data from the USART and puts in in a rolling buffer
( but in this application, it should never roll.)
If the buffer is full, this routine just discards the received byte.
Not checking the LRC byte at the end of the NMEA-0183 sentence.
*/
char cChar;
if ( rs232_errors & 0x04 ) // get framing error bit from Rx status reg
{
cRxErrorFlag = ON;
}
cChar = fgetc ( 1 ); // get char from UART, clear any errors
if ( cRxByteCnt == RX_BUFFER_SIZE ) // is recv fifo full ?
{
goto done;
}
switch ( cRxIsrState )
{
case 0:
{
if ( cChar == DOLLAR ) // if start of NMEA0183 message
{
cRxByteCnt = 0; // reset byte count
cReceiveFlag = OFF; // default to off
cRxMsgTypeReceived = NULL; // set hashed value to null
cRxIsrState++; // next state
}
break;
}
case 1: // five type characters to obtain
case 2:
case 3:
case 4:
case 5:
{
cRxMsgTypeReceived ^= cChar; // hash in msg type
if ( cRxIsrState++ == 5 ) // if time to check message type
{
if ( cRxMsgTypeReceived == cRxMsgTypeDesired ) // if good
{
cReceiveFlag = YES; // enable receiving
cRxBufferWritePtr = cRxBuffer; // reset to beginning of buffer
}
else // don't want this message
{
cRxIsrState = 0; // reset to look for next msg
}
}
break;
}
case 6:
{
/* Case 6 skips the comma character following msg type */
cRxIsrState++;
break;
}
default: // remainder of characters
{
if ( cReceiveFlag == YES ) // if this message is wanted
{
*cRxBufferWritePtr = cChar; // put char in fifo
cRxBufferWritePtr++; // increment pointer
if ( cRxBufferWritePtr == ( cRxBuffer + RX_BUFFER_SIZE ) ) // pointer past end ?
{
cRxBufferWritePtr = cRxBuffer; // set pointer to start of fifo
}
cRxByteCnt++; // Increment byte count
if ( cChar == CR )
{
cRxMsgReady = YES; // signal that message is ready
cReceiveFlag = NO; // no more receive
}
}
}
}
done:;
}
/*******************************************************************/
void main ( void )
{
char cX;
delay_ms ( 200 ); // wait enough time after Vdd rise
iTimeOut = 1800; // preset to ~30 seconds
iScreenRollCount = SCREEN_ROLL_COUNT;
iButtonInactivityCount = BUTTON_INACTIVITY_COUNT;
/* INITIALIZE */
output_float ( RX_IN ); // ensure Rx input is HiZ
output_float ( BUTTON_1 ); // ensure switch input is HiZ
output_float ( BUTTON_2 ); // ensure switch input is HiZ
output_float ( BUTTON_3 ); // ensure switch input is HiZ
output_float ( PIN_A0 ); // set analog input pins to float
output_float ( PIN_A1 );
output_float ( PIN_A3 );
port_b_pullups ( ON ); // enable pullups on switches
// GET SAVED SETTINGS
cContrast = read_eeprom ( ( long ) EEPROM_CONTRAST ); // get stored value
output_bit ( LCD_BACKLITE, read_eeprom ( ( long ) EEPROM_BACKLIGHT ) ); // set backlight
cScreenRoll = read_eeprom ( ( long ) EEPROM_SCREENROLL ); // get stored value
cUsbConnection = read_eeprom ( ( long ) EEPROM_USB_PROTOCOL ); // get initial USB connection
output_bit ( USB_NMEA_GARMIN, cUsbConnection ); // set the initial USB connection
// PWM is for display contrast
setup_ccp2 ( CCP_PWM ); // set for PWM mode
//The cycle time will be (1/clock)*4*t2div*(period+1)
// 1/8000000 * 4 * 1 * 128 = 51.2uS = 19.5KHz
setup_timer_2 ( T2_DIV_BY_1, 255, 1 ); // set PWM period
// duty cycle = value*(1/clock)*t2div
// 10 * 1/8000000 * 1 = 1.2uS
set_pwm2_duty ( cContrast ); // set contrast duty cycle
// SETUP TIMER 0
// Need 8-bit Timer0 to roll over every 13mS, approximately.
// Roll time = 256 * 1 / ( clock_freq / prescaler setting / 4 )
#if CRYSTAL_FREQ >= 15000000
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_256 | RTCC_8_BIT ); // ~13mS timer wrap
#elif CRYSTAL_FREQ >= 8000000
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128 | RTCC_8_BIT ); // ~13mS timer wrap
#elif CRYSTAL_FREQ < 8000000
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_64 | RTCC_8_BIT ); // ~13mS timer wrap
#endif
// Timer 1 roll time = 65536 * 1 / ( clock_freq / prescaler setting / 4 )
setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_8 ); // 16-bit timer
setup_adc_ports ( RA0_RA1_RA3_ANALOG ); /* these three statements set up the ADC */
setup_adc ( ADC_CLOCK_INTERNAL );
cIllumination = OFF;
// Setup CCP1 register for diesel tachometer
setup_ccp1 ( CCP_CAPTURE_RE ); // capture every rising edge on CCP1
LCD_Init(); // set up LCD for 4-wire bus, etc.
/* INIT MESSAGE */
LCD_SetPosition ( LINE_1 + 0 );
printf ( LCD_PutChar, " \"Northern Light\" " ); // welcome screen
LCD_SetPosition ( LINE_2 + 2 );
printf ( LCD_PutChar, "Systems Monitor " );
LCD_SetPosition ( LINE_3 + 3 );
printf ( LCD_PutChar, "v21f 08/11/06" );
LCD_SetPosition ( LINE_4 + 5 );
printf ( LCD_PutChar, "c Jon Fick" );
delay_ms ( 1000 );
// USB CONNECTION MESSAGE
DisplayMessage ( USB_MSG );
delay_ms ( 1000 );
/* SETUP MODE */
if ( input ( BUTTON_1 ) == LOW ) // if button is pressed
{
// SET DISPLAY CONTRAST
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( LINE_1 + 0 );
printf ( LCD_PutChar, "Set contrast:" );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "<-- More" );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, "<-- DONE" );
LCD_SetPosition ( LINE_4 + 0 );
printf ( LCD_PutChar, "<-- Less" );
while ( input ( BUTTON_1 ) == LOW ); // wait for switch to be released after entering SETUP mode
cContrast = 120; // start at near full contrast
cDone = NO;
while ( cDone == NO )
{
set_pwm2_duty ( cContrast ); // update contrast
if ( input ( BUTTON_1 ) == LOW )
{
if ( cContrast > 100 )
{
cContrast--; // more
}
}
if ( input ( BUTTON_2 ) == LOW )
{
cDone = YES; // done
}
if ( input ( BUTTON_3 ) == LOW )
{
if ( cContrast < 165 )
{
cContrast++; // less
}
}
delay_ms ( 30 ); // autorepeat
}
write_eeprom ( ( long ) EEPROM_CONTRAST, cContrast ); // save CONTRAST to EEPROM
// BOOTUP SCREEN
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "<- Press & hold " );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, " bootup screen" );
while ( input ( BUTTON_1 ) == LOW ); // wait until button not pressed
cX = POSITION_SCREEN;
while ( TRUE )
{
LCD_SetPosition ( LINE_4 + 6 );
switch ( cX )
{
case POSITION_SCREEN:
{
printf ( LCD_PutChar, "POSITION " );
break;
}
case WAYPOINT_SCREEN:
{
printf ( LCD_PutChar, "WAYPOINT " );
break;
}
case INST_SCREEN:
{
printf ( LCD_PutChar, "INSTRUMTS" );
break;
}
case BATTERY_SCREEN:
{
printf ( LCD_PutChar, "BATTERY " );
break;
}
case TACH_SCREEN:
{
printf ( LCD_PutChar, "TACHOMETR" );
break;
}
case TANK_SCREEN:
{
printf ( LCD_PutChar, "TANK LVL " );
break;
}
}
delay_ms ( 750 );
if ( input ( BUTTON_1 ) == LOW ) // if button is pressed
{
LCD_SetPosition ( LINE_4 + 3 );
printf ( LCD_PutChar, "OK--SAVED " );
write_eeprom ( ( long ) EEPROM_INITIAL, cX ); // save screen number to EEPROM
while ( input ( BUTTON_1 ) == LOW ); // wait until button is released
break;
}
if ( cX++ == INST_SCREEN ) // wrap
{
cX = POSITION_SCREEN;
}
}
// BOOTUP BACKLIGHT
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "<- Press & hold " );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, " bootup backlight" );
while ( input ( BUTTON_1 ) == LOW ); // wait until button not pressed
cX = OFF;
while ( TRUE )
{
LCD_SetPosition ( LINE_4 + 6 );
switch ( cX )
{
case OFF:
{
printf ( LCD_PutChar, "OFF" );
break;
}
case ON:
{
printf ( LCD_PutChar, "ON " );
break;
}
}
delay_ms ( 750 );
if ( input ( BUTTON_1 ) == LOW ) // if button is pressed
{
LCD_SetPosition ( LINE_4 + 3 );
printf ( LCD_PutChar, "OK--SAVED" );
write_eeprom ( ( long ) EEPROM_BACKLIGHT, cX ); // save backlight to EEPROM
while ( input ( BUTTON_1 ) == LOW ); // wait until button is released
break;
}
cX ^= 1; // wrap
}
// SCREEN ROLL
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "<- Press & hold " );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, " auto screen roll" );
while ( input ( BUTTON_1 ) == LOW ); // wait until button not pressed
cX = OFF;
while ( TRUE )
{
LCD_SetPosition ( LINE_4 + 6 );
switch ( cX )
{
case OFF:
{
printf ( LCD_PutChar, "OFF" );
break;
}
case ON:
{
printf ( LCD_PutChar, "ON " );
break;
}
}
delay_ms ( 750 );
if ( input ( BUTTON_1 ) == LOW ) // if button is pressed
{
LCD_SetPosition ( LINE_4 + 3 );
printf ( LCD_PutChar, "OK--SAVED" );
write_eeprom ( ( long ) EEPROM_SCREENROLL, cX ); // save backlight to EEPROM
while ( input ( BUTTON_1 ) == LOW ); // wait until button is released
break;
}
cX ^= 1; // wrap
}
reset_cpu();
}
/* BUTTON ASSIGNMENT MESSAGE */
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( LINE_1 + 0 );
printf ( LCD_PutChar, "BUTTONS:" );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "<-- GPS & BackLight" );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, "<-- Instruments" );
LCD_SetPosition ( LINE_4 + 0 );
printf ( LCD_PutChar, "<-- Systems" );
delay_ms ( 2000 );
/* This IF/ENDIF is a tool for getting the $GP... codes */
/* that are used in the switch/case in the main loop. */
#if ( GET_GPS_CODE == YES )
printf ( LCD_PutChar, "%u", 'G'^'P'^'R'^'M'^'B');
while ( TRUE );
#endif
/* INTERRUPTS */
ext_int_edge ( H_TO_L ); // set falling edge ext interrupt
enable_interrupts ( INT_TIMER1 ); // enable Timer1 interrupt
enable_interrupts ( INT_RDA ); // enable serial interrupt
enable_interrupts ( INT_RTCC ); // enable Timer0 interrupt
enable_interrupts ( INT_AD ); // enable ADC interrupt
enable_interrupts ( GLOBAL ); // enable all interrupts
/* VARIABLES */
iVar = NULL; // default, no variation yet
cVarDir = SPACE; // default, no variation yet
cRmcTimer1 = 255; // initialize to 52 seconds
cRmcTimer2 = RMC_TIME; // trigger forced RMC after 10 minutes
cScreen = HIDDEN_RMC; // default screen, get magnetic variation first
cSavedScreen = read_eeprom ( ( long ) EEPROM_INITIAL ); // restore initial screen
iLastRange = 65535; // make max by default
strcpy ( cToFrom, " " ); // blank by default
cScreenChanged = YES;
cIndicator = 0;
cButton1Count = 0;
cButton2Count = 0;
cButton3Count = 0;
cRxErrorFlag = OFF;
cSwitch1State = 0; // default screen
cSwitch2State = 0; // default screen
cSwitch3State = 0; // default screen
/* MAIN LOOP ****************************************************************************/
while ( TRUE )
{
cNmeaDataTimeOut = 188; // 188 * 16mS = 3 seconds
switch ( cScreen )
{
case USB_SCREEN:
{
if ( cScreenChanged == YES )
{
cScreenChanged = NO;
cSkip = NO;
DisplayMessage ( USB_MSG );
}
cScreen = cSavedScreen; // revert to previous screen
break;
}
case HIDDEN_RMC:
{
InitRxBuffer( GPRMC_CODE ); // set code and turn on serial interrupt
while ( ( cRxMsgReady == NO ) && ( cNmeaDataTimeOut != 0 ) );
disable_interrupts ( INT_RDA ); // ignore rest of messages
if ( cNmeaDataTimeOut != 0 ) // if not timed out
{
GetUtcAndMagVar(); // get and store the magnetic variation
}
cScreen = cSavedScreen; // revert to previous screen
break;
}
case POSITION_SCREEN:
{
if ( cScreenChanged == YES )
{
disable_interrupts ( INT_RDA );
cScreenChanged = NO;
cSkip = NO;
LCD_PutCmd ( CLEAR_DISP );
DisplayTemplateLatLon();
enable_interrupts ( INT_RDA );
}
InitRxBuffer( GPRMC_CODE ); // set code and turn on serial interrupt
while ( ( cRxMsgReady == NO ) && ( cNmeaDataTimeOut != 0 ) && ( cScreenChanged != YES ) );
disable_interrupts ( INT_RDA ); // ignore rest of messages
if ( cScreenChanged == NO )
{
if ( cNmeaDataTimeOut != 0 )
{
DisplayPosition();
}
else
{
DisplayMessage ( NODATA_MSG );
}
}
cRxErrorFlag = OFF;
ToggleActivityIndicator();
break;
}
case WAYPOINT_SCREEN:
{
if ( cScreenChanged == YES )
{
disable_interrupts ( INT_RDA );
cScreenChanged = NO;
cSkip = NO;
LCD_PutCmd ( CLEAR_DISP );
DisplayTemplateWaypoint();
enable_interrupts ( INT_RDA );
}
cSkip = NO;
InitRxBuffer( GPRMB_CODE ); // set code and turn on serial interrupt
while ( ( cRxMsgReady == NO ) && ( cNmeaDataTimeOut != 0 ) && ( cScreenChanged != YES ) );
disable_interrupts ( INT_RDA ); // ignore rest of messages
if ( cScreenChanged == NO )
{
if ( cNmeaDataTimeOut != 0 )
{
DisplayWaypoint();
}
else
{
DisplayMessage ( NODATA_MSG );
}
}
ToggleActivityIndicator();
break;
}
case INST_SCREEN:
{
if ( cScreenChanged == YES )
{
disable_interrupts ( INT_RDA );
cScreenChanged = NO;
cSkip = NO;
LCD_PutCmd ( CLEAR_DISP );
DisplayTemplateInstruments();
cSeatalkDisplay = INST_ACCUM_TIME;
}
DisplayInstruments();
break;
}
case BATTERY_SCREEN:
{
if ( cScreenChanged == YES )
{
disable_interrupts ( INT_RDA );
cScreenChanged = NO;
cSkip = NO;
LCD_PutCmd ( CLEAR_DISP );
DisplayTemplateAnalog();
}
DisplayAnalog();
break;
}
case TACH_SCREEN:
{
if ( cScreenChanged == YES )
{
disable_interrupts ( INT_RDA );
cScreenChanged = NO;
cSkip = NO;
LCD_PutCmd ( CLEAR_DISP );
DisplayTemplateTach();
}
DisplayTach();
break;
}
case TANK_SCREEN:
{
if ( cScreenChanged == YES )
{
disable_interrupts ( INT_RDA );
cScreenChanged = NO;
cSkip = NO;
LCD_PutCmd ( CLEAR_DISP );
DisplayTemplateTank();
}
DisplayTank();
break;
}
}
// Preset timeout counter each loop; RTCC interrupt decrements, resets if zero is reached
iTimeOut = 1800; // ~ 30 seconds
}
}
/**********************************************************************************************/
#separate void ToggleActivityIndicator ( void )
{
char cX;
/* Flashing activity indicator in lower right of screen. */
cIndicator ^= 1;
cX = cIndicator ? ACTIVITY_SYMBOL : SPACE;
LCD_SetPosition ( LINE_4 + 19 );
printf ( LCD_PutChar, "%c", cX );
}
#separate void DisplayTemplateLatLon ( void )
{
LCD_SetPosition ( LINE_1 );
printf ( LCD_PutChar, "Lat" );
LCD_SetPosition ( LINE_2 );
printf ( LCD_PutChar, "Lon" );
LCD_SetPosition ( LINE_3 );
printf ( LCD_PutChar, "Speed" );
LCD_SetPosition ( LINE_4 );
printf ( LCD_PutChar, "Heading" );
}
#separate void DisplayTemplateWaypoint ( void )
{
LCD_SetPosition ( LINE_1 );
printf ( LCD_PutChar, "Waypoint" );
LCD_SetPosition ( LINE_2 );
printf ( LCD_PutChar, "Steer" );
LCD_SetPosition ( LINE_3 );
printf ( LCD_PutChar, "Dist" );
LCD_SetPosition ( LINE_4 );
printf ( LCD_PutChar, "Bearing" );
}
#separate void DisplayTemplateAnalog ( void )
{
LCD_SetPosition ( LINE_1 + 3 );
printf ( LCD_PutChar, "BATTERY STATUS" );
LCD_SetPosition ( LINE_2 );
printf ( LCD_PutChar, "Primary" );
LCD_SetPosition ( LINE_3 );
printf ( LCD_PutChar, "Secondary" );
LCD_SetPosition ( LINE_4 );
printf ( LCD_PutChar, "Refrigerator" );
}
#separate void DisplayPosition ( void )
{
SkipField ( 1 ); // skip UTC
GetField(); // A = OK, V = warning
if ( ( cC [ 0 ] == 'A' ) && ( !cSkip ) )
{
GetField(); // LAT
if ( !cSkip )
{
DisplayLatitude ( LINE_1 );
}
GetField(); // LON
if ( !cSkip )
{
DisplayLongitude ( LINE_2 );
}
GetField(); // SPEED
if ( !cSkip )
{
DisplaySpeed ( LINE_3 );
}
GetField(); // HEADING
if ( !cSkip )
{
DisplayHeading ( LINE_4 );
}
}
else
{
DisplayMessage( WARNING_MSG );
}
}
#separate void DisplayWaypoint ( void )
{
char cX;
GetField(); // A = OK, V = warning
if ( ( cC [ 0 ] == 'A' ) && ( !cSkip ) )
{
cX = GetField(); // XTE
if ( !cSkip )
{
DisplaySteer ( LINE_2, cX );
}
SkipField ( 1 ); // skip origin WP ID
GetField(); // DEST WP ID
if ( !cSkip )
{
DisplayWaypointName ( LINE_1, cX );
}
SkipField ( 4 ); // skip LAT, NS, LON, EW
cX = GetField(); // RANGE
if ( !cSkip )
{
DisplayDistance ( LINE_3, cX );
}
cX = GetField(); // BEARING
if ( !cSkip )
{
DisplayBearing ( LINE_4, cX );
}
SkipField ( 1 ); // skip SPEED TO DEST
GetField(); // ARRIVAL FLAG
if ( !cSkip )
{
DisplayArrival ( LINE_1 ); // overwrite RANGE if arrived
}
}
else
{
DisplayMessage( WARNING_MSG );
}
}
#separate void DisplayAnalog ( void )
{
long iX;
char cCnt;
set_adc_channel ( 0 ); // set channel
delay_us ( 100 ); // wait aquisition time
cAdcDone = NO;
if ( !cSkip )
{
LCD_SetPosition ( LINE_2 + 13 );
DisplayScaledVoltage ( read_adc(), MAX_VOLTS );
printf ( LCD_PutChar, " V " );
}
set_adc_channel ( 1 );
delay_us ( 100 );
cAdcDone = NO;
if ( !cSkip )
{
LCD_SetPosition ( LINE_3 + 13 );
DisplayScaledVoltage ( read_adc(), MAX_VOLTS );
printf ( LCD_PutChar, " V " );
}
set_adc_channel ( 3 );
delay_us ( 100 );
cAdcDone = NO;
if ( !cSkip )
{
LCD_SetPosition ( LINE_4 + 13 );
DisplayScaledVoltage ( read_adc(), MAX_VOLTS );
printf ( LCD_PutChar, " V " );
}
Delay5mS ( 200 ); // delay 1.0 second
ToggleActivityIndicator();
}
#separate void GetUtcAndMagVar ( void )
{
/*
This is a non-display version of the RMC sentence
to get the A/V warning, the magnetic variation, and the
magnetic direction.
*/
GetField(); // get UTC
GetField(); // A = OK, V = warning
if ( cC [ 0 ] == 'A' )
{
SkipField ( 7 ); // skip fields
GetField(); // MAGNETIC VARIATION
iVar = FieldFiveToLong(); // save to global variable, used in other sentences
GetField(); // EW
cVarDir = cC [ 0 ]; // save direction
}
else
{
iVar = NULL; // invalid
cVarDir = SPACE;
}
}
/******************************************************************/
#separate void DisplayScaledVoltage ( long iV, char cScale )
{
float fX;
/*
0 to 5V input at pin 2 results in 0 - 1023. This routine
scales it to something else.
*/
while ( cAdcDone == NO ); // wait for completion by ADC interrupt
if ( iV == 1023 )
{
printf ( LCD_PutChar, "O/L" ); /* print it to the screen */
}
else
{
fX = ( ( float ) iV ) / 1023 * ( float ) cScale; // scale to proper range, 1023 leaves room for out-of-range
printf ( LCD_PutChar, "%02.1f", fX ); /* print it to the screen */
}
}
#separate void DisplayArrival ( char cLine )
{
LCD_SetPosition ( cLine + 11 );
if ( cC [ 0 ] == 'A' )
{
printf ( LCD_PutChar, "Arrived" );
}
else
{
printf ( LCD_PutChar, " " );
}
}
#separate void DisplayWaypointName ( char cLine, char cX )
{
/* Displays waypoint name, pads field with blanks */
char cChar, cI;
LCD_SetPosition ( cLine );
if ( cX != 0 )
{
printf ( LCD_PutChar, "\"" );
for ( cI = 0; cI < 6; cI++ )
{
cChar = cC [ cI ];
if ( cChar == EOF )
{
break;
}
printf ( LCD_PutChar, "%c", cChar );
}
printf ( LCD_PutChar, "\"" );
// Blank remainder of field
cChar = SPACE;
for ( ; cI < 6; cI++ )
{
printf ( LCD_PutChar, "%c", cChar );
}
}
else
{
printf ( LCD_PutChar, "- none -" );
}
}
#separate void DisplaySteer ( char cLine, char cX )
{
/*
Displays A.BC literals, appends 'L' or 'R'.
If less than 1.0, displays feet rather than nm.
Doesn't display distance if on track.
*/
long iX;
char cCnt;
if ( cX != 0 )
{
if ( ( cC [ 0 ] != '0' ) || ( cC [ 2 ] != '0' ) || ( cC [ 3 ] != '0' ) ) // if not 0.00
{
LCD_SetPosition ( cLine + 14 );
printf ( LCD_PutChar, " " ); // blank possible characters
LCD_SetPosition ( cLine + 11 );
if ( cC [ 0 ] == '0' ) // if less than 1.0 nm, display as feet
{
iX = ( 528 * ( long ) ( cC [ 2 ] - 0x30 ) ) + ( 52 * ( long ) ( cC [ 3 ] - 0x30 ) );
printf ( LCD_PutChar, "%luft ", iX );
}
else // if 1.0 nm or greater, display as nautical miles
{
printf ( LCD_PutChar, "%c%c%c%cmi ", cC [ 0 ], cC [ 1 ], cC [ 2 ] , cC [ 3 ] );
}
GetField(); // L or R
LCD_SetPosition ( cLine + 6 );
if ( cC [ 0 ] == 'L' )
{
printf ( LCD_PutChar, "PORT " );
}
else
{
printf ( LCD_PutChar, "STBD " );
}
}
else // if 0.00
{
LCD_SetPosition ( cLine + 11 );
printf ( LCD_PutChar, "On track " );
GetField(); // dummy L or R
}
}
else
{
LCD_SetPosition ( cLine + 6 );
printf ( LCD_PutChar, " " );
}
}
#separate void DisplayDistance ( char cLine, char cX )
{
/* Format: ABC.D nautical miles */
char cChar, cI;
long iThisRange;
if ( cX != 0 ) // if waypoint data to display
{
LCD_SetPosition ( cLine + 11 );
cI = 0;
for ( cI = 0; cI < 2; cI++ ) // find first non-zero
{
cChar = cC [ cI ];
if ( cChar != '0' )
{
break;
}
}
for ( ; cI < 5; cI++ ) // display from there on
{
printf ( LCD_PutChar, "%c", cC [ cI ] );
}
printf ( LCD_PutChar, "nm " ); // pad with blanks
/*
The least significant character from the GPS is 0.1 nm.
Multiply whole thing by 10 and make it type long.
Discern if increasing (FROM) or decreasing (TO).
*/
iThisRange = 1000 * ( long ) ( cC [ 0 ] - 0x30 );
iThisRange += 100 * ( long ) ( cC [ 1 ] - 0x30 );
iThisRange += 10 * ( long ) ( cC [ 2 ] - 0x30 );
iThisRange += ( long ) ( cC [ 4 ] - 0x30 );
if ( iThisRange < iLastRange )
{
strcpy ( cToFrom, "TO " );
}
if ( iThisRange > iLastRange )
{
strcpy ( cToFrom, "FROM" );
}
iLastRange = iThisRange; // save this range to compare next time
LCD_SetPosition ( cLine + 5 );
printf ( LCD_PutChar, cToFrom );
}
else
{
LCD_SetPosition ( cLine + 5 );
printf ( LCD_PutChar, " " );
}
}
#separate void DisplayBearing ( char cLine, char cX )
{
/*
Compass variation comes from RMC sentence. If RMC has not run yet
then "T" is displayed after bearing.
*/
long iHdg;
char cTrueIndicator;
if ( cX != 0 ) // if waypoint data to display
{
LCD_SetPosition ( cLine + 11 );
//iHdg = FieldFiveToLong(); // exceeds stack depth ???
JustifyDecimalPoint();
iHdg = 100 * ( long ) ( cC [ 0 ] - 0x30 );
iHdg += 10 * ( long ) ( cC [ 1 ] - 0x30 );
iHdg += ( long ) ( cC [ 2 ] - 0x30 );
if ( ( cC [ 3 ] == PERIOD ) && ( cC [ 4 ] >= '5' ) )
{
iHdg++; // round up
}
iHdg = TrueToMag ( iHdg ); // factor variation into heading
if ( ( iVar == NULL ) || ( cVarDir == SPACE ) )
{
cTrueIndicator = 'T';
}
else
{
cTrueIndicator = ' ';
}
printf ( LCD_PutChar, "%lu%c%c ", iHdg, DEGREE, cTrueIndicator ); // pad with blanks
}
else
{
LCD_SetPosition ( cLine + 11 );
printf ( LCD_PutChar, " " );
}
}
#separate void DisplayLatitude ( char cLine )
{
/* Displays latitude ABCD.EFG as AB CD.EFG, appends 'N' or 'S' */
LCD_SetPosition ( cLine + 8 );
if ( cC [ 0 ] == '0' )
{
cC [ 0 ] = SPACE;
}
printf ( LCD_PutChar, "%c%c%c", cC [ 0 ], cC [ 1 ], DEGREE );
printf ( LCD_PutChar, "%c%c%c%c%c%c", cC [ 2 ], cC [ 3 ], cC [ 4 ], cC [ 5 ], cC [ 6 ], cC [ 7 ] );
GetField(); // NS
printf ( LCD_PutChar, " %c", cC [ 0 ] );
}
#separate void DisplayLongitude ( char cLine )
{
/* Displays longitude ABCDE.FGH as ABC DE.FGH, appends 'E' or 'W' */
LCD_SetPosition ( cLine + 7 );
if ( cC [ 0 ] == '0' )
{
cC [ 0 ] = SPACE;
}
if ( cC [ 1 ] == '0' )
{
cC [ 1 ] = SPACE;
}
printf ( LCD_PutChar, "%c%c%c%c", cC [ 0 ], cC [ 1 ], cC [ 2 ], DEGREE );
printf ( LCD_PutChar, "%c%c%c%c%c%c", cC [ 3 ], cC [ 4 ], cC [ 5 ], cC [ 6 ], cC [ 7 ], cC [ 8 ] );
GetField(); // EW
printf ( LCD_PutChar, " %c", cC [ 0 ] );
}
#separate void DisplaySpeed ( char cLine )
{
float fX;
char cLoc;
LCD_SetPosition ( cLine + 8 );
JustifyDecimalPoint();
fX = 100 * ( cC [ 0 ] - 0x30 );
fX += 10 * ( cC [ 1 ] - 0x30 );
fX += 1 * ( cC [ 2 ] - 0x30 );
fX += 0.1 * ( cC [ 4 ] - 0x30 );
#if SPEED_UNITS == 2
fX *= 1.852; // convert knots to km/h
#endif
#if SPEED_UNITS == 3
fX *= 1.151; // convert knots to mi/h
#endif
printf ( LCD_PutChar, "%03.1f ", fX ); // print it to the screen
#if SPEED_UNITS == 1
printf ( LCD_PutChar, "kts " ); // print it to the screen
#endif
#if SPEED_UNITS == 2
printf ( LCD_PutChar, "kph " ); // print it to the screen
#endif
#if SPEED_UNITS == 3
printf ( LCD_PutChar, "mph " ); // print it to the screen
#endif
}
#separate void DisplayHeading ( char cLine )
{
long iHdg;
LCD_SetPosition ( cLine + 8 );
//iHdg = FieldFiveToLong(); // exceeds stack depth ???
JustifyDecimalPoint();
iHdg = 100 * ( long ) ( cC [ 0 ] - 0x30 );
iHdg += 10 * ( long ) ( cC [ 1 ] - 0x30 );
iHdg += ( long ) ( cC [ 2 ] - 0x30 );
if ( ( cC [ 3 ] == PERIOD ) && ( cC [ 4 ] >= '5' ) )
{
iHdg++; // round up
}
SkipField ( 1 ); // skip fix date
GetField(); // MAGNETIC VARIATION
//iVar = FieldFiveToLong(); // save to global variable, used in other sentences // exceeds stack depth ???
// presently looks for format: 016.2
// now gets 16.2
JustifyDecimalPoint();
iVar = 100 * ( long ) ( cC [ 0 ] - 0x30 );
iVar += 10 * ( long ) ( cC [ 1 ] - 0x30 );
iVar += ( long ) ( cC [ 2 ] - 0x30 );
if ( ( cC [ 3 ] == PERIOD ) && ( cC [ 4 ] >= '5' ) )
{
iVar++; // round up
}
GetField(); // EW
cVarDir = cC [ 0 ]; // save direction
iHdg = TrueToMag ( iHdg ); // factor variation into heading
printf ( LCD_PutChar, "%lu%c ", iHdg, DEGREE ); // pad with blanks
}
#separate long FieldFiveToLong ( void )
{
/* Converts ABC.D to long, rounds decimal up or down */
long iX;
iX = 100 * ( long ) ( cC [ 0 ] - 0x30 );
iX += 10 * ( long ) ( cC [ 1 ] - 0x30 );
iX += ( long ) ( cC [ 2 ] - 0x30 );
if ( ( cC [ 3 ] == PERIOD ) && ( cC [ 4 ] >= '5' ) )
{
iX++; // round up
}
return ( iX );
}
#separate void JustifyDecimalPoint ( void )
{
// Move the decimal point into position 3
if ( cC [ 1 ] == PERIOD ) // i.e. "1.2" becomes 001.2
{
cC [ 4 ] = cC [ 2 ];
cC [ 3 ] = cC [ 1 ];
cC [ 2 ] = cC [ 0 ];
cC [ 1 ] = '0';
cC [ 0 ] = '0';
}
// Move the decimal point into position 3
if ( cC [ 2 ] == PERIOD ) // i.e. "12.3" becomes 012.3
{
cC [ 4 ] = cC [ 3 ];
cC [ 3 ] = cC [ 2 ];
cC [ 2 ] = cC [ 1 ];
cC [ 1 ] = cC [ 0 ];
cC [ 0 ] = '0';
}
}
#separate long TrueToMag ( long iH )
{
/* Magnetic variation information comes from the RMC sentence */
if ( cVarDir == 'W' )
{
iH += iVar;
}
else
{
if ( iH >= iVar )
{
iH -= iVar; // OK as-is
}
else
{
iH = iH + 360 - iVar; // correct for below zero
}
}
if ( iH >= 360 )
{
iH -= 360;
}
return ( iH );
}
#separate void DisplayMessage ( char cMsgNum )
{
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( LINE_2 );
switch ( cMsgNum )
{
case WARNING_MSG:
{
printf ( LCD_PutChar, " GPS warning " );
break;
}
case NODATA_MSG:
{
if ( cRxErrorFlag == OFF ) // is it a framing error problem ?
{
printf ( LCD_PutChar, " No data from GPS" );
}
else
{
printf ( LCD_PutChar, " Baud error" );
cRxErrorFlag = OFF;
}
break;
}
case USB_MSG:
{
printf ( LCD_PutChar, " USB protocol:" );
LCD_SetPosition ( LINE_3 );
if ( cUsbConnection == NMEA_CONNECTED )
{
printf ( LCD_PutChar, " GARMIN" );
}
else
{
printf ( LCD_PutChar, " NMEA" );
}
write_eeprom ( ( long ) EEPROM_USB_PROTOCOL, cUsbConnection );
break;
}
}
Delay5mS ( 255 ); // delay 1.25 seconds
iVar = NULL;
cVarDir = SPACE; // signal "no magnetic variation" yet
cScreenChanged = YES;
}
#separate void Delay5mS ( char cCnt )
{
char cX;
/* This variable-count 5mS delay is interruptable by a button press */
for ( cX = 0; cX < cCnt; cX++ )
{
if ( cScreenChanged == YES )
{
break;
}
delay_ms ( 5 );
}
}
#separate char GetField ( void )
{
char cX, cIndex;
cX = NULL;
cIndex = 0;
while ( !cSkip )
{
cX = GetRxChar();
if ( ( cX == COMMA ) || ( cX == CR ) )
{
break;
}
cC [ cIndex++ ] = cX;
}
cC [ cIndex ] = EOF;
return ( cIndex ); // return number of characters in field
}
#separate void SkipField ( char cCnt )
{
char cX;
for ( cX = 0; cX < cCnt; cX++ )
{
while ( GetRxChar() != COMMA );
}
}
/* DIESEL TACHOMETER FUNCTIONS ========================================= */
#separate void DisplayTemplateTach ( void )
{
LCD_SetPosition ( LINE_1 + 1 );
printf ( LCD_PutChar, "DIESEL TACHOMETER" );
}
#separate void DisplayTach ( void )
{
char cCnt;
float fRpm;
int32 int32Count;
enable_interrupts ( INT_CCP1 ); // CCP1 interrupt
cTachState = DONE_TACH;
if ( !cSkip ) // do until button is pressed
{
int32Count = 0;
for ( cCnt = 0; cCnt < 100; cCnt++ ) // accumulate 100 readings
{
cTachState = START_TACH; // allow interrupt to start
while ( cTachState != DONE_TACH ) // wait for timing to complete
{
if ( cSkip )
{
break;
}
if ( get_timer1() > 60000 ) // timeout counter
{
iTachCount = 0; // zero everything out
int32Count = 0;
break; // don't wait any longer
}
}
int32Count += ( int32 ) iTachCount; // otherwise accumulate
}
int32Count /= 100; // get average of those 100 readings
fRpm = 1 / ( float ) int32Count; // period in uS
fRpm *= 1000000; // period in seconds
fRpm *= 60; // period in minutes
fRpm /= ALTERNATOR_POLES; // adjust for number of poles in alternator
fRpm *= PULLEY_FACTOR; // adjust for pulley ratio
if ( int32Count == 0 )
{
LCD_SetPosition ( LINE_3 + 5 );
printf ( LCD_PutChar, "Engine OFF" );
}
else
{
LCD_SetPosition ( LINE_3 + 5 );
printf ( LCD_PutChar, " %4.0f RPM ", fRpm );
}
}
disable_interrupts ( INT_CCP1 ); // CCP1 interrupt
Delay5mS ( 200 ); // delay 1.0 seconds
ToggleActivityIndicator();
}
/* SEATALK COMMON FUNCTIONS ========================================= */
#separate char ReceiveSeatalkMsg ( void )
{
char cCnt, cCharPtr, cMaxCnt;
iSeatalkTimeout0 = 61; // set first character for ~1 second timeout
// FIRST CHARACTER
while ( TRUE ) // wait until idle state
{
if ( !kbhit ( 2 ) )
{
break;
}
if ( ( iSeatalkTimeout0 == 0 ) || ( cSkip ) )
{
return ( SEATALK_TIMEOUT );
}
}
while ( TRUE ) // wait until start edge, then get character
{
if ( kbhit ( 2 ) ) // if character is ready
{
cSeatalkChar [ 0 ] = fgetc ( 2 ); // store first character in array
if ( bit_test ( rs232_errors, NINTH_BIT ) == HIGH ) // if it is the command character
{
break; // found cmd char, get remainder
}
}
if ( ( iSeatalkTimeout0 == 0 ) || ( cSkip ) ) // if timed out or key pressed
{
return ( SEATALK_TIMEOUT ); // return
}
}
// REMAINING CHARACTERS
while ( TRUE ) // get remaining characters
{
cMaxCnt = 17; // last possible character location (18th)
for ( cCharPtr = 1; cCharPtr < cMaxCnt; cCharPtr++ ) // from second to nth data character
{
for ( cCnt = 0;; cCnt++ ) // set new inter-character counter
{
if ( kbhit ( 2 ) ) // if character is ready
{
break; // jump out of for loop
}
delay_us ( 10 ); // wait for fractional bit period ( 1/21th bit)
}
if ( cCnt >= 230 ) // if timed out beyond one character period
{
return ( SEATALK_DATA_ERROR ); // waited too long for character, must be collision, leave
}
cSeatalkChar [ cCharPtr ] = fgetc ( 2 ); // store character in array
if ( cCharPtr == 1 ) // if this is the character count byte
{
cMaxCnt = ( cSeatalkChar [ 1 ] & 0x0f ) + 3; // modify byte count of this message (low nibble)
}
if ( bit_test ( rs232_errors, NINTH_BIT ) != LOW ) // if 9th bit is not low, it's not a data character
{
return ( SEATALK_DATA_ERROR ); // return
}
if ( cSkip ) // if key was pressed, skip out
{
return ( SEATALK_DATA_ERROR ); // return
}
}
return ( SEATALK_OK ); // return no errors
}
}
/* SEATALK TANK LEVEL FUNCTIONS ============================================= */
#separate void DisplayTemplateTank ( void )
{
LCD_SetPosition ( LINE_1 + 0 );
printf ( LCD_PutChar, "TANK LEVELS" );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "Water-Bow:" );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, "Water-Aft:" );
LCD_SetPosition ( LINE_4 + 0 );
printf ( LCD_PutChar, "Waste:" );
iSeatalkTimeout1 = 0; // start timeout timers at "timed out"
iSeatalkTimeout2 = 0;
iSeatalkTimeout3 = 0;
}
#separate void DisplayTank ( void )
{
char cStatus;
if ( !cSkip ) // do until button is pressed
{
if ( cSeatalkDisplay != 0 ) // if not time to display yet
{
cStatus = ReceiveSeatalkMsg();
if ( cStatus == SEATALK_OK ) // if no errors
{
switch ( cSeatalkChar [ 0 ] )
{
case TANK_MSGNUM:
{
switch ( cSeatalkChar [ TANK_ADR ] & 0x03 ) // mask on address bits 0 & 1
{
case 0x00:
{
cTankLevel0 = cSeatalkChar [ TANK_LEVEL ];
iSeatalkTimeout1 = SEATALK_RESET_TIME;
break;
}
case 0x01:
{
cTankLevel1 = cSeatalkChar [ TANK_LEVEL ];
iSeatalkTimeout2 = SEATALK_RESET_TIME;
break;
}
case 0x02:
{
cTankLevel2 = cSeatalkChar [ TANK_LEVEL ];
iSeatalkTimeout3 = SEATALK_RESET_TIME;
break;
}
}
}
}
}
}
else
{
ToggleActivityIndicator();
// Display Tank 0
LCD_SetPosition ( LINE_2 + 11 );
if ( iSeatalkTimeout1 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%3u%% ", cTankLevel0 );
}
// Display Tank 1
LCD_SetPosition ( LINE_3 + 11 );
if ( iSeatalkTimeout2 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%3u%% ", cTankLevel1 );
}
// Display Tank 2
LCD_SetPosition ( LINE_4 + 11 );
if ( iSeatalkTimeout3 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%3u%% ", cTankLevel2 );
}
cSeatalkDisplay = INST_ACCUM_TIME; // reset
}
}
}
/* SEATALK INSTRUMENTS FUNCTIONS =================================== */
#separate void DisplayTemplateInstruments ( void )
{
LCD_SetPosition ( LINE_1 + 0 );
printf ( LCD_PutChar, "Depth" );
LCD_SetPosition ( LINE_2 + 0 );
printf ( LCD_PutChar, "Speed" );
LCD_SetPosition ( LINE_3 + 0 );
printf ( LCD_PutChar, "Compass" );
LCD_SetPosition ( LINE_4 + 0 );
printf ( LCD_PutChar, "WaterTemp" );
iSeatalkTimeout1 = 0; // start timeout timers at "timed out"
iSeatalkTimeout2 = 0;
iSeatalkTimeout3 = 0;
iSeatalkTimeout4 = 0;
}
#separate void DisplayInstruments ( void )
{
char cStatus, cX;
long iX, iT;
float fX;
//LCD_SetPosition ( LINE_1 + 10 );
//printf ( LCD_PutChar, "%0x%0x%0x%0x", cSeatalkChar[0],cSeatalkChar[1],cSeatalkChar[2],cSeatalkChar[3] );
if ( !cSkip ) // do until button is pressed
{
if ( cSeatalkDisplay != 0 ) // if not time to display yet
{
cStatus = ReceiveSeatalkMsg();
if ( cStatus == SEATALK_OK ) // if no errors
{
switch ( cSeatalkChar [ 0 ] )
{
case DEPTH_MSGNUM:
{
cInstDepthHi = cSeatalkChar [ 4 ];
cInstDepthLo = cSeatalkChar [ 3 ];
iSeatalkTimeout1 = SEATALK_RESET_TIME;
break;
}
case SPEED_MSGNUM:
{
cInstSpeedHi = cSeatalkChar [ 3 ];
cInstSpeedLo = cSeatalkChar [ 2 ];
iSeatalkTimeout2 = SEATALK_RESET_TIME;
break;
}
case HEADING_MSGNUM:
{
cInstHeadingHi = cSeatalkChar [ 2 ];
cInstHeadingLo = cSeatalkChar [ 1 ];
iSeatalkTimeout3 = SEATALK_RESET_TIME;
break;
}
case TEMP_MSGNUM:
{
cInstTempHi = cSeatalkChar [ 3 ];
cInstTempLo = cSeatalkChar [ 2 ];
iSeatalkTimeout4 = SEATALK_RESET_TIME;
break;
}
}
}
}
else
{
ToggleActivityIndicator();
// Display depth
iX = ( ( long ) cInstDepthHi * 256 ) + cInstDepthLo;
fX = ( ( float ) iX ) / 10;
LCD_SetPosition ( LINE_1 + 10 );
if ( iSeatalkTimeout1 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%3.1f ft ", fX );
}
// Display Speed
iX = ( ( long ) cInstSpeedHi * 256 ) + cInstSpeedLo;
fX = ( ( float ) iX ) / 10;
LCD_SetPosition ( LINE_2 + 10 );
if ( iSeatalkTimeout2 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%01.1f kt ", fX );
}
// Display Heading
// 9c 41 19 00 = 50 deg
iX = ( long ) ( ( cInstHeadingLo >> 4 ) & 0x03 );
iT = iX * 90;
iX = ( long ) ( cInstHeadingHi & 0x3f );
iT = iT + ( iX * 2 );
iX = ( long ) ( ( cInstHeadingLo >> 4 ) & 0x0C );
iT = iT + ( iX / 8 );
LCD_SetPosition ( LINE_3 + 10 );
if ( iSeatalkTimeout3 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%03lu%c ", iT, DEGREE );
if ( ( iT > 338 ) || ( iT <= 22 ) )
{
printf ( LCD_PutChar, "N " );
}
if ( ( iT > 23 ) && ( iT <= 67 ) )
{
printf ( LCD_PutChar, "NE" );
}
if ( ( iT > 68 ) && ( iT <= 112 ) )
{
printf ( LCD_PutChar, "E " );
}
if ( ( iT > 113 ) && ( iT <= 157 ) )
{
printf ( LCD_PutChar, "SE" );
}
if ( ( iT > 158 ) && ( iT <= 202 ) )
{
printf ( LCD_PutChar, "S " );
}
if ( ( iT > 203 ) && ( iT <= 247 ) )
{
printf ( LCD_PutChar, "SW" );
}
if ( ( iT > 248 ) && ( iT <= 292 ) )
{
printf ( LCD_PutChar, "W " );
}
if ( ( iT > 293 ) && ( iT <= 337 ) )
{
printf ( LCD_PutChar, "NW" );
}
}
// Display Water Temperature
//27 01 06 01 = 61F also 23 01 10 e5 = 61F
iX = ( ( long ) cInstTempHi * 256 ) + cInstTempLo;
fX = ( ( float) iX - 100 ) / 10; // centigrade
fX = ( ( 9 * fX ) / 5 ) + 32; // fahrenheit
LCD_SetPosition ( LINE_4 + 10 );
if ( iSeatalkTimeout4 == 0 )
{
WriteBlanks ( 8 );
}
else
{
printf ( LCD_PutChar, "%2.0f%cF ", fX, DEGREE );
}
cSeatalkDisplay = INST_ACCUM_TIME; // reset
}
}
}
#separate void WriteBlanks ( char cCnt )
{
char cX;
for ( cX = 0; cX < cCnt; cX++ )
{
printf ( LCD_PutChar, " " );
}
}
/* RS232 FUNCTIONS ================================================== */
#separate void InitRxBuffer ( char cCode )
{
disable_interrupts ( INT_RDA );
cRxBufferWritePtr = cRxBuffer; // point to beginning of buffer
cRxBufferReadPtr = cRxBuffer;
cRxByteCnt = 0;
cRxIsrState = 0;
cRxMsgReady = NO;
cRxMsgTypeDesired = cCode;
enable_interrupts ( INT_RDA );
}
#separate char GetRxChar ( void )
{
// Get the next available byte in the recv fifo.
// Call this function ONLY if the recv fifo contains data.
char cValue;
cValue = 0;
if ( cRxByteCnt > 0 ) // For safety, check if there is any data
{
cValue = *cRxBufferReadPtr++; // Read byte from fifo
if ( cRxBufferReadPtr == ( cRxBuffer + RX_BUFFER_SIZE ) ) // Did tail ptr wrap ?
{
cRxBufferReadPtr = cRxBuffer; // If so, reset it to start of buffer
}
cRxByteCnt--; // Decrement byte count
}
return ( cValue );
}
/* LCD FUNCTIONS ================================= */
#separate void LCD_Init ( void )
{
LCD_SetData ( 0x00 );
delay_ms ( 200 ); /* wait enough time after Vdd rise */
output_low ( LCD_RS );
LCD_SetData ( 0x03 ); /* init with specific nibbles to start 4-bit mode */
LCD_PulseEnable();
LCD_PulseEnable();
LCD_PulseEnable();
LCD_SetData ( 0x02 ); /* set 4-bit interface */
LCD_PulseEnable(); /* send dual nibbles hereafter, MSN first */
LCD_PutCmd ( 0x2C ); /* function set (all lines, 5x7 characters) */
LCD_PutCmd ( 0x0C ); /* display ON, cursor off, no blink */
LCD_PutCmd ( 0x01 ); /* clear display */
LCD_PutCmd ( 0x06 ); /* entry mode set, increment & scroll left */
}
#separate void LCD_SetPosition ( unsigned int cX )
{
LCD_SetData ( swap ( cX ) | 0x08 );
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) );
LCD_PulseEnable();
}
#separate void LCD_PutChar ( unsigned int cX )
{
if ( !cSkip )
{
output_high ( LCD_RS );
LCD_SetData ( swap ( cX ) ); /* send high nibble */
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) ); /* send low nibble */
LCD_PulseEnable();
output_low ( LCD_RS );
}
}
#separate void LCD_PutCmd ( unsigned int cX )
{
LCD_SetData ( swap ( cX ) ); /* send high nibble */
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) ); /* send low nibble */
LCD_PulseEnable();
}
#separate void LCD_PulseEnable ( void )
{
output_high ( LCD_EN );
delay_us ( 3 ); // was 10
output_low ( LCD_EN );
delay_ms ( 3 ); // was 5
}
#separate void LCD_SetData ( unsigned int cX )
{
output_bit ( LCD_D4, cX & 0x01 );
output_bit ( LCD_D5, cX & 0x02 );
output_bit ( LCD_D6, cX & 0x04 );
output_bit ( LCD_D7, cX & 0x08 );
}