// RACE START CHECKS. These are defined at compile time. // 0 = no checks, 1 = finish line check only, 2 = start gate and finish line check #define START_CHECKS 2 /*************************************************************************** AGPV20170406a.C WORKING CODE, RACE VERSION!!! This program controls an Awana Grad Prix racetrack. Times out at 9.999 seconds. Uses a start switch (local N.O. pushbutton) and a remote gate switch (N.C.) and a remote gate actuator. This version can mask lanes. With serial communication. With First Place LED atop each lane. With response code for Reset command Elapsed time is 1mS interrupt driven. Serial communication is interrupt driven, 9600-8-E-1. +5 +5 | | 14 2 ---------- ---------- -Start---ProgM----4-| |-10-------------11-|DB4 Vdd | -StopR-----------17-| |-11-------------12-|DB5 | -StopB-----------18-| |-12----ProgC----13-|DB6 | -StopG------------1-| 16F648A |-13----ProgD----14-|DB7 Vo| 3----20K pot -StopY------------2-| |-9---------------6-|EN LCD | -RemSw------------3-| |-6---------------4-|RS | 6MHz XTAL-16-| |-7--Rx | | TAL-15-| |-8--Tx | RW Vss | ---------- ---------- 5 1 5 |------------ProgG | | Gnd Gnd Gnd **************************************************************************/ #include <16F648a.h> #include#fuses HS, NOPROTECT, PUT, NOWDT, BROWNOUT, NOMCLR, NOLVP #use standard_io ( A ) #use standard_io ( B ) #use delay ( clock = 6000000 ) #use rs232 ( baud = 9600, xmit = PIN_B2, rcv = PIN_B1 ) #byte PORT_A = 5 #define LCD_D7 PIN_B7 #define LCD_D6 PIN_B6 #define LCD_D5 PIN_B5 #define LCD_D4 PIN_B4 #define LCD_EN PIN_B3 #define LCD_RS PIN_B0 #define TRK_R_INPUT PIN_A0 #define TRK_B_INPUT PIN_A1 #define TRK_G_INPUT PIN_A2 #define TRK_Y_INPUT PIN_A3 #define SERVO_OUT PIN_A4 #define PB_SWITCH PIN_A5 #define LINE_1 0x00 #define LINE_2 0x40 #define LINE_3 0x10 #define LINE_4 0x50 #define CLEAR_DISP 0x01 #define TRACK_R 0b00000001 #define TRACK_B 0b00000010 #define TRACK_G 0b00000100 #define TRACK_Y 0b00001000 #define TEXT_DELAY 2000 #define CMD_NUL 0 #define GATE_OPEN 0 #define GATE_CLOSE 1 #define ERR_NONE 0 #define ERR_GATE 1 #define ERR_FINISH 2 #separate void SendTime ( char cLane, long iTime ); #separate char CheckFinishLine ( void ); #separate char CheckStartGate ( void ); #separate char GetSerial ( char cChar ); #separate void Servo ( char cX ); #separate void SendAllTimes ( void ); #separate void DisplayTie ( char cLoc ); #separate void Display ( char cTie, char cPlace, char cTrackColor, long iTime, char * cDisplayFlag ); #separate void StartTimer ( 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 ); static long iTime, iRTime, iBTime, iGTime, iYTime; static char cDisplayLine, cPlaceNum, cDisplayCnt; static char cRTie, cBTie, cGTie, cYTie, cStarted; static char cRPlace, cBPlace, cGPlace, cYPlace; static char cRDisplayFlag, cBDisplayFlag, cGDisplayFlag, cYDisplayFlag; static char cDisplayLocTable [ 4 ] = { 0x06, 0x46, 0x16, 0x56 }; static char cSerialCmd, cLaneMask, cMaskDisplayLine; #int_rda void SerialInterrupt ( void ) { // Reads incoming data from the USART cSerialCmd = getchar(); // get char from UART // command recipient resets cSerialCmd to CMD_NUL } #int_rtcc void TimerInterrupt ( void ) { /* Gets here every 1mS. Samples the track inputs, saves the current time for any tracks that have finished during this interrupt pass. Increments the time count if not overflowed past 9999mS. */ char cFinish; set_rtcc ( 73 ); // restart timer, adjusted to 1mS // GRAB ONE SAMPLE OF ALL FOUR TRACKS FOR THIS 1MS TIME SLICE cFinish = ( PORT_A ^ 0xff ) & 0x0f; // invert Port A and mask on lower four bits cFinish &= cLaneMask; // mask any unused lanes // EXAMINE THIS 1mS SAMPLE, ONE TRACK AT A TIME, PROCESS ANY CAR(S) THAT FINSIHED if ( ( ( cFinish & TRACK_R ) == TRACK_R ) && ( cRPlace == 0 ) ) // Track R finished { iRTime = iTime; // save the present time if ( ( iRTime == iBTime ) || ( iRTime == iGTime ) || ( iRTime == iYTime ) ) { cRTie = YES; // if this matches any other track, indicate TIE } cRDisplayFlag = YES; // time to display this track cRPlace = cPlaceNum++; // take the place number, increment place number } if ( ( ( cFinish & TRACK_B ) == TRACK_B ) && ( cBPlace == 0 ) ) // Track B finished { iBTime = iTime; // save the present time if ( ( iBTime == iRTime ) || ( iBTime == iGTime ) || ( iBTime == iYTime ) ) { cBTie = YES; // if this matches any other track, indicate TIE } cBDisplayFlag = YES; // time to display this track cBPlace = cPlaceNum++; // take the place number, increment place number } if ( ( ( cFinish & TRACK_G ) == TRACK_G ) && ( cGPlace == 0 ) ) // Track G finished { iGTime = iTime; // save the present time if ( ( iGTime == iRTime ) || ( iGTime == iBTime ) || ( iGTime == iYTime ) ) { cGTie = YES; // if this matches any other track, indicate TIE } cGDisplayFlag = YES; // time to display this track cGPlace = cPlaceNum++; // take the place number, increment place number } if ( ( ( cFinish & TRACK_Y ) == TRACK_Y ) && ( cYPlace == 0 ) ) // Track Y finished { iYTime = iTime; // save the present time if ( ( iYTime == iRTime ) || ( iYTime == iBTime ) || ( iYTime == iGTime ) ) { cYTie = YES; // if this matches any other track, indicate TIE } cYDisplayFlag = YES; // time to display this track cYPlace = cPlaceNum++; // take the place number, increment place number } if ( iTime < 9999) // if not overflow, increment the clock { iTime++; } else // make unfinished races = 9.999 seconds { if ( cRPlace == 0 ) { iRTime = 9999; cRPlace = cPlaceNum++; // save the available place number cRDisplayFlag = YES; // time to display this track } if ( cBPlace == 0 ) { iBTime = 9999; cBPlace = cPlaceNum++; // save the available place number cBDisplayFlag = YES; // time to display this track } if ( cGPlace == 0 ) { iGTime = 9999; cGPlace = cPlaceNum++; // save the available place number cGDisplayFlag = YES; // time to display this track } if ( cYPlace == 0 ) { iYTime = 9999; cYPlace = cPlaceNum++; // save the available place number cYDisplayFlag = YES; // time to display this track } } } void main ( void ) { char cX, cWaitForNextMaskChar; delay_ms ( 200 ); port_b_pullups ( ON ); output_float ( TRK_R_INPUT ); output_float ( TRK_B_INPUT ); output_float ( TRK_G_INPUT ); output_float ( TRK_Y_INPUT ); output_float ( SERVO_OUT ); setup_counters ( RTCC_INTERNAL, RTCC_DIV_8 ); // 256 * 4uS = 1.024mS timer wrap LCD_Init(); // set up LCD for 4-wire bus, etc. cLaneMask = 0b11111111; // default to all lanes working cWaitForNextMaskChar = NO; if ( input ( PB_SWITCH ) == LOW ) // diag mode if switch is down upon power up { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 + 0 ); printf ( LCD_PutChar, "DIAGNOSTICS MODE" ); // welcome screen LCD_SetPosition ( LINE_3 + 0 ); printf ( LCD_PutChar, "Start gates: " ); LCD_SetPosition ( LINE_4 + 0 ); printf ( LCD_PutChar, "Lane finish: " ); while ( input ( PB_SWITCH ) == LOW ) { LCD_SetPosition ( LINE_3 + 12 ); if ( input ( SERVO_OUT ) == HIGH ) { printf ( LCD_PutChar, "DOWN" ); } else { printf ( LCD_PutChar, "UP " ); } LCD_SetPosition ( LINE_4 + 12 ); if ( ( ( PORT_A ^ 0xff ) & TRACK_R ) != 0 ) { printf ( LCD_PutChar, "R" ); } else { printf ( LCD_PutChar, " " ); } LCD_SetPosition ( LINE_4 + 13 ); if ( ( ( PORT_A ^ 0xff ) & TRACK_B ) != 0 ) { printf ( LCD_PutChar, "B" ); } else { printf ( LCD_PutChar, " " ); } LCD_SetPosition ( LINE_4 + 14 ); if ( ( ( PORT_A ^ 0xff ) & TRACK_G ) != 0 ) { printf ( LCD_PutChar, "G" ); } else { printf ( LCD_PutChar, " " ); } LCD_SetPosition ( LINE_4 + 15 ); if ( ( ( PORT_A ^ 0xff ) & TRACK_Y ) != 0 ) { printf ( LCD_PutChar, "Y" ); } else { printf ( LCD_PutChar, " " ); } delay_ms ( 10 ); } } LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 + 0 ); printf ( LCD_PutChar, "AWANA GRAND PRIX" ); // welcome screen LCD_SetPosition ( LINE_2 + 0 ); printf ( LCD_PutChar, "Christ Memorial" ); LCD_SetPosition ( LINE_3 + 5 ); printf ( LCD_PutChar, "Church" ); LCD_SetPosition ( LINE_4 + 0 ); printf ( LCD_PutChar, "Jon Fick v040617" ); delay_ms ( TEXT_DELAY ); LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Press and hold" ); LCD_SetPosition ( LINE_3 + 0 ); printf ( LCD_PutChar, "for LANE MASKING" ); delay_ms ( TEXT_DELAY ); if ( input ( PB_SWITCH ) == LOW ) // lane masking if pushbutton is pressed after welcome screen { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_3 + 1 ); printf ( LCD_PutChar, "Release button" ); while ( input ( PB_SWITCH ) == LOW ); // wait until released LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Press and hold" ); LCD_SetPosition ( LINE_3 + 2 ); printf ( LCD_PutChar, "to mask RED" ); delay_ms ( TEXT_DELAY ); if ( input ( PB_SWITCH ) == LOW ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "OK, RED masked" ); LCD_SetPosition ( LINE_3 + 1 ); printf ( LCD_PutChar, "Release button" ); cLaneMask &= 0b11111110; cRPlace = 'M'; // masked lane } while ( input ( PB_SWITCH ) == LOW ); // wait until released LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Press and hold" ); LCD_SetPosition ( LINE_3 + 2 ); printf ( LCD_PutChar, "to mask BLU" ); delay_ms ( TEXT_DELAY ); if ( input ( PB_SWITCH ) == LOW ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "OK, BLU masked" ); LCD_SetPosition ( LINE_3 + 1 ); printf ( LCD_PutChar, "Release button" ); cLaneMask &= 0b11111101; cBPlace = 'M'; // masked lane } while ( input ( PB_SWITCH ) == LOW ); // wait until released LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Press and hold" ); LCD_SetPosition ( LINE_3 + 2 ); printf ( LCD_PutChar, "to mask GRN" ); delay_ms ( TEXT_DELAY ); if ( input ( PB_SWITCH ) == LOW ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "OK, GRN masked" ); LCD_SetPosition ( LINE_3 + 1 ); printf ( LCD_PutChar, "Release button" ); cLaneMask &= 0b11111011; cGPlace = 'M'; // masked lane } while ( input ( PB_SWITCH ) == LOW ); // wait until released LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Press and hold" ); LCD_SetPosition ( LINE_3 + 2 ); printf ( LCD_PutChar, "to mask YEL" ); delay_ms ( TEXT_DELAY ); if ( input ( PB_SWITCH ) == LOW ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "OK, YEL masked" ); LCD_SetPosition ( LINE_3 + 1 ); printf ( LCD_PutChar, "Release button" ); cLaneMask &= 0b11110111; cYPlace = 'M'; // masked lane } while ( input ( PB_SWITCH ) == LOW ); // wait until released } if ( cLaneMask == 0b11111111 ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 0 ); printf ( LCD_PutChar, "ALL LANES IN USE" ); delay_ms ( TEXT_DELAY ); } Servo ( GATE_CLOSE ); // reset gate LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 5 ); printf ( LCD_PutChar, "Ready" ); cStarted = FALSE; cWaitForNextMaskChar = NO; enable_interrupts ( GLOBAL ); // enable all interrupts enable_interrupts ( INT_RDA ); // enable serial interrupt cSerialCmd = CMD_NUL; // reset to no command while ( TRUE ) { while ( TRUE ) { if ( input ( PB_SWITCH ) == LOW ) // PUSHBUTTON MANUAL RACE START { output_float ( TRK_R_INPUT ); output_float ( TRK_B_INPUT ); output_float ( TRK_G_INPUT ); output_float ( TRK_Y_INPUT ); cX = ERR_NONE; // default #if START_CHECKS == 1 cX |= CheckFinishLine(); // check that all lanes are clear at the finish line #endif #if START_CHECKS == 2 cX |= CheckStartGate(); // check that the start gate is closed cX |= CheckFinishLine(); // check that all lanes are clear at the finish line #endif if ( cX == ERR_NONE ) { break; } if ( ( cX & ERR_GATE ) == ERR_GATE ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 3 ); printf ( LCD_PutChar, "CLOSE THE" ); LCD_SetPosition ( LINE_3 + 3 ); printf ( LCD_PutChar, "START GATE" ); } else { if ( ( cX & ERR_FINISH )== ERR_FINISH ) { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 2 ); printf ( LCD_PutChar, "FINISH LINE" ); LCD_SetPosition ( LINE_3 + 2 ); printf ( LCD_PutChar, "IS BLOCKED" ); } } } if ( GetSerial ( 'R' ) ) // "READY" command from computer { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 5 ); printf ( LCD_PutChar, "Ready" ); printf ( "RDY" ); cWaitForNextMaskChar = NO; } if ( GetSerial ( 'T' ) ) // "RACE START" command from computer { output_float ( TRK_R_INPUT ); output_float ( TRK_B_INPUT ); output_float ( TRK_G_INPUT ); output_float ( TRK_Y_INPUT ); cWaitForNextMaskChar = NO; break; // break out, time to start this race } if ( GetSerial ( 'C' ) ) // "CHECK" command from computer { #if START_CHECKS == 1 // signal if finish line photocell is blocked if ( CheckFinishLine() != ERR_NONE ) { printf ( "NG" ); } else { printf ( "GD" ); } #endif #if START_CHECKS == 2 // signal if start gate switch is not closed or finish line photocell is blocked if ( ( CheckStartGate() != ERR_NONE ) || ( CheckFinishLine() != ERR_NONE ) ) { printf ( "NG" ); } else { printf ( "GD" ); } #endif cWaitForNextMaskChar = NO; } if ( GetSerial ( 'A' ) ) // "GET ALL TIMES" command from computer { SendAllTimes(); cWaitForNextMaskChar = NO; } if ( GetSerial ( 'X' ) ) // "UNMASK ALL LANES" command from computer { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 0 ); printf ( LCD_PutChar, "Unmask all lanes" ); delay_ms ( TEXT_DELAY ); cLaneMask = 0b11111111; // bitmask all lanes on cRPlace = 0; // unmask lane cBPlace = 0; // unmask lane cGPlace = 0; // unmask lane cYPlace = 0; // unmask lane cWaitForNextMaskChar = NO; } if ( GetSerial ( 'M' ) ) // MASK command from computer { cWaitForNextMaskChar = YES; } if ( cWaitForNextMaskChar == YES ) { if ( GetSerial ( '1' ) ) // MASK LANE 1 command from computer { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Lane R masked" ); delay_ms ( TEXT_DELAY ); cLaneMask &= 0b11111110; // bitmask this lane bad cRPlace = 'M'; // masked lane cWaitForNextMaskChar = NO; } if ( GetSerial ( '2' ) ) // MASK LANE 2 command from computer { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Lane B masked" ); delay_ms ( TEXT_DELAY ); cLaneMask &= 0b11111101; // bitmask this lane bad cBPlace = 'M'; // masked lane cWaitForNextMaskChar = NO; } if ( GetSerial ( '3' ) ) // MASK LANE 3 command from computer { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Lane G masked" ); delay_ms ( TEXT_DELAY ); cLaneMask &= 0b11111011; // bitmask this lane bad cGPlace = 'M'; // masked lane cWaitForNextMaskChar = NO; } if ( GetSerial ( '4' ) ) // MASK LANE 4 command from computer { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 1 ); printf ( LCD_PutChar, "Lane Y masked" ); delay_ms ( TEXT_DELAY ); cLaneMask &= 0b11110111; // bitmask this lane bad cYPlace = 'M'; // masked lane cWaitForNextMaskChar = NO; } } } // GETS HERE EITHER BY MANUAL PUSHBUTTON OR BY COMPUTER COMMAND WHEN TIMING NEEDS TO START LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 3 ); printf ( LCD_PutChar, "STARTING!" ); // signal that the race has begun StartTimer(); // zeros everything except mask conditions Servo ( GATE_OPEN ); cStarted = FALSE; enable_interrupts ( INT_RTCC ); // otherwise just allow the timer interrupt, which will start timer later delay_ms ( 1000 ); // wait 1 second LCD_PutCmd ( CLEAR_DISP ); // clear display and put in 1st, 2nd, etc. LCD_SetPosition ( LINE_1 ); printf ( LCD_PutChar, "1st" ); LCD_SetPosition ( LINE_2 ); printf ( LCD_PutChar, "2nd" ); LCD_SetPosition ( LINE_3 ); printf ( LCD_PutChar, "3rd" ); LCD_SetPosition ( LINE_4 ); printf ( LCD_PutChar, "4th" ); Servo ( GATE_CLOSE ); while ( TRUE ) { Display ( cRTie, cRPlace, 'R', iRTime, &cRDisplayFlag ); // check and display Track 1 Display ( cBTie, cBPlace, 'B', iBTime, &cBDisplayFlag ); // check and display Track 2 Display ( cGTie, cGPlace, 'G', iGTime, &cGDisplayFlag ); // check and display Track 3 Display ( cYTie, cYPlace, 'Y', iYTime, &cYDisplayFlag ); // check and display Track 4 delay_ms ( 100 ); if ( GetSerial ( 'F' ) ) // "EARLY DONE" command from computer { cWaitForNextMaskChar = NO; if ( cRPlace == 0 ) { cRPlace = 5; iRTime = 9999; } if ( cBPlace == 0 ) { cBPlace = 5; iBTime = 9999; } if ( cGPlace == 0 ) { cGPlace = 5; iGTime = 9999; } if ( cYPlace == 0 ) { cYPlace = 5; iYTime = 9999; } SendAllTimes(); break; } if ( cDisplayCnt == 4 ) // if all lines actually written to display { SendAllTimes(); break; } } } } #separate char CheckStartGate ( void ) { // signal if start gate switch is not closed if ( input ( SERVO_OUT ) == HIGH ) { return ( ERR_GATE ); } return ( ERR_NONE ); // otherwise OK } #separate char CheckFinishLine ( void ) { // signal if photocell is blocked if ( ( ( PORT_A ^ 0xff ) & 0x0f & cLaneMask ) != 0 ) // invert Port A, mask the four lane bits on, and exclude any masked lanes { return ( ERR_FINISH ); } return ( ERR_NONE ); // otherwise OK } #separate char GetSerial ( char cChar ) { if ( cSerialCmd == cChar ) { cSerialCmd = CMD_NUL; return ( YES ); } else { return ( NO ); } } #separate void Servo ( char cX ) { char cCnt; switch ( cX ) { case GATE_OPEN: { for ( cCnt = 0; cCnt < 15; cCnt++ ) { output_high ( SERVO_OUT ); delay_us ( 1000 ); output_low ( SERVO_OUT ); delay_ms ( 30 ); } break; } case GATE_CLOSE: { for ( cCnt = 0; cCnt < 15; cCnt++ ) { output_high ( SERVO_OUT ); delay_us ( 2000 ); output_low ( SERVO_OUT ); delay_ms ( 30 ); } break; } } output_float ( SERVO_OUT ); // go back to hi-z } #separate void SendAllTimes ( void ) { // send lane time if that lane finished if ( cRPlace != 0 ) { SendTime ( 1, iRTime ); } if ( cBPlace != 0 ) { SendTime ( 2, iBTime ); } if ( cGPlace != 0 ) { SendTime ( 3, iGTime ); } if ( cYPlace != 0 ) { SendTime ( 4, iYTime ); } } #separate void SendTime ( char cLane, long iTime ) { printf ( "%u %01lu.%03lu ", cLane, iTime/1000, iTime%1000 ); } #separate void StartTimer ( void ) { disable_interrupts ( INT_RTCC ); // turn off timer interrupt while resetting time cRTie = NO; // preset to no tie conditions cBTie = NO; cGTie = NO; cYTie = NO; if ( cRPlace != 'M' ) // if lane is not masked { cRPlace = 0; // preset to no place iRTime = 65535; // set the individual track times off zero, to max cRDisplayFlag = OFF; // allow one-time display } else { cRDisplayFlag = ON; // allow one-time display iRTime = 9999; // set to max time } if ( cBPlace != 'M' ) // if lane is not masked { cBPlace = 0; // preset to no place iBTime = 65535; // set the individual track times off zero, to max cBDisplayFlag = OFF; // allow one-time display } else { cBDisplayFlag = ON; // allow one-time display iBTime = 9999; // set to max time } if ( cGPlace != 'M' ) // if lane is not masked { cGPlace = 0; // preset to no place iGTime = 65535; // set the individual track times off zero, to max cGDisplayFlag = OFF; // allow one-time display } else { cGDisplayFlag = ON; // allow one-time display iGTime = 9999; // set to max time } if ( cYPlace != 'M' ) // if lane is not masked { cYPlace = 0; // preset to no place iYTime = 65535; // set the individual track times off zero, to max cYDisplayFlag = OFF; // allow one-time display } else { cYDisplayFlag = ON; // allow one-time display iYTime = 9999; // set to max time } iTime = 0; // zero the count cDisplayLine = 1; // preset to first display line cMaskDisplayLine = 4; // any masked lane gets displayed from the bottom line and upward in the display cPlaceNum = 1; // start with first place cStarted = TRUE; // signal: running cDisplayCnt = 0; enable_interrupts ( INT_RTCC ); // turn on timer interrupt to begin timing } #separate void Display ( char cTie, char cPlace, char cTrackColor, long iTime, char *cDisplayFlag ) { char cTablePtr; if ( *cDisplayFlag == YES ) // if this track's done flag was turned on by the interrupt or in the START function (if this lane is masked) { if ( cPlace != 'M' ) { cTablePtr = cPlace - 1; // place value determines which display line from top row of display, downward LCD_SetPosition ( cDisplayLocTable [ cTablePtr ] ); printf ( LCD_PutChar, "%c %2lu.%03lu", cTrackColor, iTime/1000, iTime%1000 ); if ( cTie == YES ) { DisplayTie ( cDisplayLocTable [ cTablePtr ] ); // point to line DisplayTie ( cDisplayLocTable [ cTablePtr - 1 ] ); // point to line above } *cDisplayFlag = NO; // prevent displaying again cDisplayCnt += 1; } else { cTablePtr = cMaskDisplayLine-- - 1; // masked lanes show at bottom row of display, upward LCD_SetPosition ( cDisplayLocTable [ cTablePtr ] ); printf ( LCD_PutChar, "%c MASKED", cTrackColor ); *cDisplayFlag = NO; // prevent displaying again cDisplayCnt += 1; } } } #separate void DisplayTie ( char cLoc ) { LCD_SetPosition ( cLoc - 6 ); // point to beginning of this line printf ( LCD_PutChar, "TIE!" ); } #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 ) { // this subroutine works specifically for 4-bit Port A LCD_SetData ( swap ( cX ) | 0x08 ); LCD_PulseEnable(); LCD_SetData ( swap ( cX ) ); LCD_PulseEnable(); } #separate void LCD_PutChar ( unsigned int cX ) { // this subroutine works specifically for 4-bit Port A 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 ) { // this subroutine works specifically for 4-bit Port A 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 ( 10 ); output_low ( LCD_EN ); delay_ms ( 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 ); }