/********************************************************************************* DATALOG5.C With interrupt button on dump routine (both buttons pressed.) This is a data logger. It has a 5V and 14V ADC input and collects (with the 24LC128 EEPROM memory) 8192 samples. Control and display is done via two pushbuttons and and LCD display. Data dump can be viewed on the display or sent to a terminal via 9600 baud RS232. WORKING CODE --------- --------- +5--20-|Vdd B0|-----21->-6-|SCL* | *Pullups required +5---1-|Mclr B1|-----22->-5-|SDA* | on SCL and SDA. Gnd---8-|Vss | | 24LC128 | Gnd--19-|Vss | +5-8-|Vdd | 4MHz--10-|Xtal | Gnd-4-|Vss | -------- ---9-|Xtal | Gnd-7-|Wp | Gnd-20-|EN | | | Gnd-1-|A0 | Gnd-21-|SD | | 16F876 | Gnd-2-|A1 | Gnd-11-|Vss | | | Gnd-3-|A2 | +5-12-|Vcc | POT | | --------- | | ©-10K--13-|C2 | | | In--10K-*-------2-|A0 B3|-24-->----9600 baud------------7-|IN OUT|-4-->-Tx | | | | -Menu/Dec --11-|C0 | | MAX235 | -Select/Inc --12-|C1 | -------- | | --------- | C3|-----14--11-|D4 | | C4|-----15--12-|D5 | | C5|-----16--13-|D6 | | C6|-----17--14-|D7 | | | | |-3--20K pot (contrast) | B5|-----26---6-|EN | | B6|-----27---4-|RS | | | | | | | +5-2-| | | | Gnd-1-| | | | Gnd-5-| | | | | DISPLAY | --------- --------- *********************************************************************************/ #include < 16F876.H > #device ADC=10 #include < jonsinc.h > #fuses XT, NOPROTECT, NOPUT, NOWDT, NOBROWNOUT, NOLVP, NOCPD, NOWRT // ADC #define VDD 5.00 // INTERNAL EEPROM ASSIGNMENTS #define SAMPLE_INTERVAL_HI 0 #define SAMPLE_INTERVAL_LO 1 #define SAMPLE_COUNT_HI 2 #define SAMPLE_COUNT_LO 3 #define LOGGING_STATE 4 #define RANGE 5 // EXTERNAL EEPROM ASSIGNMENTS 128Kbit EEPROM is 16384 bytes #define EEPROM_BYTE_SIZE 16384 #define EEPROM_SCL PIN_B0 #define EEPROM_SDA PIN_B1 // LCD STUFF #define LCD_D0 PIN_C3 #define LCD_D1 PIN_C4 #define LCD_D2 PIN_C5 #define LCD_D3 PIN_C6 #define LCD_EN PIN_B5 #define LCD_RS PIN_B6 #define LINE_1 0x00 #define LINE_2 0x40 #define CLEAR_DISP 0x01 #define MENU_DEC_SWITCH PIN_C0 #define SELECT_INC_SWITCH PIN_C1 #define RANGE_SHUNT PIN_C2 #define SEL0 PIN_B2 #define SEL1 PIN_B4 #define MINIMUM_INTERVAL 1 #define STATE_START 0 #define STATE_STOP 1 #define STATE_STATUS 2 #define STATE_RESET 3 #define STATE_RANGE 4 #define STATE_INTERVAL 5 #define STATE_VIEW 6 #define STATE_DUMP 7 #define MAX_MENU_STATE 7 #define hi(x) (*(&x+1)) #use delay ( clock=4000000 ) #use standard_io ( A ) #use standard_io ( B ) #use standard_io ( C ) #use rs232 ( baud=9600, xmit=PIN_B3 ) #use i2c ( master, scl=EEPROM_SCL, sda=EEPROM_SDA ) void PrintMenu ( void ); // protos void init_ext_eeprom ( void ); void write_ext_eeprom ( long int lngAddress, BYTE intData ); BYTE read_ext_eeprom ( long int lngAddress ); void SetTime ( void ); void CheckSample ( void ); void CheckSwitches ( void ); char GetEchoNumChar ( void ); void LCD_Init ( void ); void LCD_SetPosition ( unsigned int cX ); void LCD_PutChar ( unsigned int cX ); void LCD_PutCmd ( unsigned int cX ); void LCD_PulseEnable ( void ); void LCD_SetData ( unsigned int cX ); void DisplayVolts ( long iAdcValue, char cLoc ); float ScaleAdc ( long iValue ); void SetRange ( BYTE cDisplay ); static long iIntervalCount, iIntervalTrigger, iSampleCount; static char cLogging, cSampleFlag, cLedCount; static char cLoggingIndicatorFlag, cAdcFlag, cToggleFlag; static char cInterruptCount, cViewing; static char cMenuState, cSelectFlag, cRange; static char cMenuDecSwitchOn, cMenuSwitchCount; static char cSelIncSwitchOn, cSelectSwitchCount; //**************************************************************************** #int_rtcc void TimerInterrupt ( void ) // 32.768mS tic, ~30 interrupts per second { if ( cInterruptCount++ == 30 ) // if one second yet { cAdcFlag = ON; // allow write to display cInterruptCount = 0; if ( cLogging == YES ) { cLoggingIndicatorFlag = ON; // time to toggle "running" indicator on display } if ( ( iIntervalCount++ == iIntervalTrigger - 1 ) && ( cLogging == YES ) ) // if sample time yet { cSampleFlag = ON; // signal time to sample iIntervalCount = 0; // start count over } } if ( input ( MENU_DEC_SWITCH ) == LOW ) { if ( cMenuSwitchCount++ == 1 ) // debounce for 30mS, (was 2) { cMenuDecSwitchOn = YES; // signal that switch was pressed cMenuSwitchCount = cViewing ? 252 : 240; // set up for auto repeat (faster if viewing) } } else { cMenuSwitchCount = 0; // switch up, restart } if ( input ( SELECT_INC_SWITCH ) == LOW ) { if ( cSelectSwitchCount++ == 1 ) // debounce for 30mS (was 2) { cSelIncSwitchOn = YES; // signal that switch was pressed cSelectSwitchCount = cViewing ? 252 : 240; // set up for auto repeat (faster if viewing) } } else { cSelectSwitchCount = 0; // switch is up, restart count } set_rtcc ( 4 ); // restart at adjusted value for 1-second accuracy } //***************************************************************************** void main ( void ) { delay_ms ( 200 ); // wait enough time after VDD rise output_float ( RANGE_SHUNT ); init_ext_eeprom(); // set SDA and SCL to float setup_counters ( RTCC_INTERNAL, RTCC_DIV_128 ); // 31mS roll LCD_Init(); LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 + 2 ); printf ( LCD_PutChar, "DATA LOGGER" ); LCD_SetPosition ( LINE_2 + 2 ); printf ( LCD_PutChar, "%4lu samples", EEPROM_BYTE_SIZE / 2 ); delay_ms ( 1000 ); LCD_PutCmd ( CLEAR_DISP ); LCD_Init(); LCD_SetPosition ( LINE_1 + 4 ); printf ( LCD_PutChar, "Jon Fick" ); LCD_SetPosition ( LINE_2 + 4 ); printf ( LCD_PutChar, "01/12/07" ); delay_ms ( 1000 ); LCD_PutCmd ( CLEAR_DISP ); // SETUP setup_ccp1 ( CCP_OFF ); setup_ccp2 ( CCP_OFF ); setup_adc_ports ( RA0_ANALOG ); // these three statements set up the ADC setup_adc ( ADC_CLOCK_INTERNAL ); // clock source set_adc_channel ( 0 ); // select channel enable_interrupts ( INT_RTCC ); // turn on timer interrupt enable_interrupts ( GLOBAL ); // enable interrupts // RESTORE PREVIOUS STATE cRange = read_eeprom ( RANGE ); if ( cRange >= 2 ) { cRange = 0; // reset to default } SetRange ( NO ); // set range according to cRange, don't display iSampleCount = ( 256 * read_eeprom ( SAMPLE_COUNT_HI ) ) + read_eeprom ( SAMPLE_COUNT_LO ); cLogging = read_eeprom ( LOGGING_STATE ); // get existing state iIntervalTrigger = ( 256 * read_eeprom ( SAMPLE_INTERVAL_HI ) ) + read_eeprom ( SAMPLE_INTERVAL_LO ); if ( iIntervalTrigger == 0 ) { iIntervalTrigger = 1; // preset to at least 1 second sample interval } // INITIALIZE VARIABLES cSelectFlag = OFF; cToggleFlag = 0; cMenuDecSwitchOn = OFF; cSelIncSwitchOn = OFF; cMenuSwitchCount = 0; cSelectSwitchCount = 0; cMenuState = ( cLogging == YES ) ? STATE_STOP : STATE_START; // set first menu while ( TRUE ) // do forever { PrintMenu(); // display screens and voltage CheckSwitches(); // check and do any switch activity CheckSample(); // check if it's time to sample and store ADC } } //**************************************************************************** void PrintMenu ( void ) { // ACTIVITY INDICATOR LCD_SetPosition ( LINE_1 + 15 ); if ( cLogging == NO ) // if not logging at this time { printf ( LCD_PutChar, " " ); // blank symbol for activity indicator } else // if presently logging { if ( cLoggingIndicatorFlag == ON ) // turned on once per second by interrupt { cToggleFlag ^= 1; // toggle the activity indicator symbol if ( cToggleFlag == 1 ) { printf ( LCD_PutChar, "%c", 255 ); // 255 symbol } else { printf ( LCD_PutChar, " " ); // blank symbol } cLoggingIndicatorFlag = OFF; } } // PRINT LOWER LINE OF MENU LCD_SetPosition ( LINE_2 + 0 ); switch ( cMenuState ) { case STATE_START: { if ( cLogging == YES ) // don't display while logging { cMenuState++; // point to next menu break; } printf ( LCD_PutChar, "Next START" ); break; } case STATE_STOP: { if ( cLogging == NO ) // don't display if not logging { cMenuState++; // point to next menu break; } printf ( LCD_PutChar, "#%04lu", iSampleCount ); LCD_SetPosition ( LINE_2 + 11 ); printf ( LCD_PutChar, " STOP" ); break; } case STATE_INTERVAL: { if ( cLogging == YES ) // prevent changing while logging { cMenuState++; // point to next menu break; } printf ( LCD_PutChar, "Next INTERVAL" ); break; } case STATE_STATUS: { printf ( LCD_PutChar, "Next STATUS" ); break; } case STATE_VIEW: { printf ( LCD_PutChar, "Next VIEW" ); break; } case STATE_DUMP: { printf ( LCD_PutChar, "Next UPLOAD" ); break; } case STATE_RESET: { if ( cLogging == YES ) // prevent changing while logging { cMenuState++; // point to next menu break; } printf ( LCD_PutChar, "Next RESET" ); break; } case STATE_RANGE: { if ( cLogging == YES ) // prevent changing while logging { cMenuState++; // point to next menu break; } printf ( LCD_PutChar, "Next RANGE" ); break; } } // DISPLAY VOLTS if ( cAdcFlag == ON ) // if interrupt signalled an ADC reading { DisplayVolts ( read_adc(), 5 ); // read ADC, send raw data to display at location 5 cAdcFlag = OFF; } } void CheckSwitches ( void ) { char cX, cDigit, cDigitPointer, cDone; long iX, iY, iVal, iPlace; // INCREMENT/DECREMENT if ( cMenuDecSwitchOn == YES ) // if interrupt caught the switch press { if ( cMenuState++ >= MAX_MENU_STATE ) // if at maximum { cMenuState = 0; // roll } cMenuDecSwitchOn = NO; // turn back off } if ( cSelIncSwitchOn == YES ) // if interrupt caught the switch press { cSelectFlag = ON; cSelIncSwitchOn = NO; // turn back off } // PRINT MENU (upper line and sometimes overwrite lower line) switch ( cMenuState ) { case ( STATE_START ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off if ( iSampleCount >= ( EEPROM_BYTE_SIZE / 2 ) ) // already at end of memory { LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 + 2); printf ( LCD_PutChar, "MEMORY FULL" ); LCD_SetPosition ( LINE_2 + 2 ); printf ( LCD_PutChar, "%4lu samples", iSampleCount ); delay_ms ( 1000 ); LCD_PutCmd ( CLEAR_DISP ); cMenuDecSwitchOn = NO; cSelIncSwitchOn = NO; cMenuState = STATE_START; // menu displays "START" } else // if OK to start { cLogging = YES; write_eeprom ( LOGGING_STATE, YES ); write_eeprom ( RANGE, cRange ); cSampleFlag = ON; // immediate sample cInterruptCount = 0; // synchronize interrupt timing from here iIntervalCount = 0; // synchronize cMenuState = STATE_STOP; // menu displays "STOP" break; } } } case ( STATE_STOP ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off cLogging = NO; write_eeprom ( LOGGING_STATE, NO ); cMenuState = STATE_START; // menu displays "START" break; } } case ( STATE_RESET ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off write_eeprom ( SAMPLE_COUNT_HI, 0 ); write_eeprom ( SAMPLE_COUNT_LO, 0 ); iSampleCount = 0; cLogging = NO; LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 + 0 ); printf ( LCD_PutChar, "Reset..." ); delay_ms ( 1000 ); LCD_SetPosition ( LINE_1 + 8 ); printf ( LCD_PutChar, "complete" ); delay_ms ( 1000 ); LCD_PutCmd ( CLEAR_DISP ); cMenuDecSwitchOn = NO; cSelIncSwitchOn = NO; cMenuState = STATE_START; // menu displays "START" break; } } case ( STATE_STATUS ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 ); printf ( LCD_PutChar, "Interval: %lus", 256 * read_eeprom ( SAMPLE_INTERVAL_HI ) + read_eeprom ( SAMPLE_INTERVAL_LO ) ); LCD_SetPosition ( LINE_2 ); printf ( LCD_PutChar, "Samples: %lu", 256 * read_eeprom ( SAMPLE_COUNT_HI ) + read_eeprom ( SAMPLE_COUNT_LO ) ); delay_ms ( 2000 ); LCD_PutCmd ( CLEAR_DISP ); cMenuDecSwitchOn = NO; cSelIncSwitchOn = NO; cMenuState = STATE_START; // menu displays "LOG" break; } } case ( STATE_RANGE ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_2 + 0 ); printf ( LCD_PutChar, "CHANGE Return" ); SetRange ( YES ); // set and display present range according to cRange while ( TRUE ) { if ( cSelIncSwitchOn == YES ) // if RETURN button is pressed { cSelIncSwitchOn = NO; break; // done selecting } if ( cMenuDecSwitchOn == YES ) // if CHANGE button is pressed { cMenuDecSwitchOn = NO; if ( ++cRange >= 2 ) // increment and wrap { cRange = 0; } LCD_SetPosition ( LINE_1 + 7 ); SetRange ( YES ); // set range, display } } write_eeprom ( RANGE, cRange ); // save final selected range LCD_PutCmd ( CLEAR_DISP ); cMenuState = STATE_START; // menu displays "START" break; } } case ( STATE_INTERVAL ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off LCD_PutCmd ( CLEAR_DISP ); LCD_SetPosition ( LINE_1 ); printf ( LCD_PutChar, "Presently %05lus", iIntervalTrigger ); LCD_SetPosition ( LINE_2 + 0 ); printf ( LCD_PutChar, "DIGIT 00000s INC" ); cX = LINE_2 + 6; // point to beginning of zeros LCD_SetPosition ( cX ); LCD_PutCmd ( 0x0E ); // display ON, cursor on, no blink cDigit = 0; cDigitPointer = 0; iX = 0; iPlace = 10000; while ( TRUE ) { if ( cSelIncSwitchOn == YES ) { if ( ++cDigit > 9 ) // increment digit { cDigit = 0; // roll } cSelIncSwitchOn = NO; cSelIncSwitchOn = NO; LCD_SetPosition ( cX + cDigitPointer ); // set cursor to this digit printf ( LCD_PutChar, "%u", cDigit ); // display the digit LCD_SetPosition ( cX + cDigitPointer ); // set cursor back to this digit } if ( cMenuDecSwitchOn == YES ) { cMenuDecSwitchOn = NO; iX += cDigit * iPlace; // add in to total iPlace /= 10; // point to next place value down cDigit = 0; // zero digit again if ( ++cDigitPointer == 5 ) // point to next digit { break; } LCD_SetPosition ( cX + cDigitPointer ); } } if ( iX != 0 ) // if number was updated { write_eeprom ( SAMPLE_INTERVAL_HI, iX / 256 ); // store high byte write_eeprom ( SAMPLE_INTERVAL_LO, iX % 256 ); // store low byte iIntervalTrigger = iX; // update interval } LCD_PutCmd ( 0x0E ); // display ON, cursor off, no blink LCD_PutCmd ( CLEAR_DISP ); cMenuState = STATE_START; // menu displays "LOG" break; } } case ( STATE_VIEW ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off iX = 0; // zero sample number iY = ( read_eeprom ( SAMPLE_COUNT_HI ) * 256 ) + read_eeprom ( SAMPLE_COUNT_LO ); cRange = read_eeprom ( RANGE ); // used stored range LCD_PutCmd ( CLEAR_DISP ); cDone = NO; if ( iY != 0 ) // if any samples at all { LCD_SetPosition ( LINE_1 ); printf ( LCD_PutChar, "#" ); LCD_SetPosition ( LINE_2 ); printf ( LCD_PutChar, "DEC BothDone INC" ); while ( cDone == NO ) { LCD_SetPosition ( LINE_1 + 1 ); printf ( LCD_PutChar, "%04lu", iX ); // display sample number iPlace = ( read_ext_eeprom ( iX * 2 ) * 256 ) + read_ext_eeprom ( ( iX * 2 ) + 1 ); // retrieve data from EEPROM DisplayVolts ( iPlace, 10 ); // display data at location 10 while ( TRUE ) { cViewing = ON; if ( ( input ( MENU_DEC_SWITCH ) == LOW ) && ( input ( SELECT_INC_SWITCH ) == LOW ) ) { cDone = YES; break; } if ( cMenuDecSwitchOn == YES ) { cMenuDecSwitchOn = NO; if ( iX-- == 0 ) { iX = iY - 1; // roll negative } break; } if ( cSelIncSwitchOn == YES ) { cSelIncSwitchOn = NO; if ( iX++ >= ( iY - 1 ) ) { iX = 0; // roll positive } break; } } cViewing = OFF; } } else { LCD_SetPosition ( LINE_1 ); printf ( LCD_PutChar, "No samples yet!" ); delay_ms ( 1000 ); } LCD_PutCmd ( CLEAR_DISP ); cMenuState = STATE_START; // menu displays "LOG" break; } } case ( STATE_DUMP ): { if ( cSelectFlag == ON ) // if switch is pressed { cSelectFlag = OFF; // turn flag off iY = ( read_eeprom ( SAMPLE_COUNT_HI ) * 256 ) + read_eeprom ( SAMPLE_COUNT_LO ); // get number of samples cRange = read_eeprom ( RANGE ); // used stored range LCD_PutCmd ( CLEAR_DISP ); cDone = NO; if ( iY != 0 ) // if any samples at all { LCD_SetPosition ( LINE_1 + 0 ); printf ( LCD_PutChar, "Dump 9600-8-N-1 " ); LCD_SetPosition ( LINE_2 ); printf ( LCD_PutChar, " Both=Stop " ); disable_interrupts ( GLOBAL ); // turn off during async printf ( "\r\n\r\n Sample interval -- %lu seconds", 256 * read_eeprom ( SAMPLE_INTERVAL_HI ) + read_eeprom ( SAMPLE_INTERVAL_LO ) ); printf ( "\r\n Number of samples- %lu", iY ); printf ( "\r\n\r\nSample\tVolts" ); printf ( "\r\n------\t-----\r\n" ); for ( iX = 0; iX < iY; iX++ ) { if ( ( input ( MENU_DEC_SWITCH ) == LOW ) && ( input ( SELECT_INC_SWITCH ) == LOW ) ) { cDone = YES; break; } iVal = ( read_ext_eeprom ( iX * 2 ) * 256 ) + read_ext_eeprom ( ( iX * 2 ) + 1 ); // get sample data if ( iVal == 0x3FF ) { printf ( "%lu\tO/L\r\n", iX ); // out-of-range } else { printf ( "%lu\t%1.2f\r\n", iX, ScaleAdc ( iVal ) ); // send data sample } delay_ms ( 1 ); // avoids text glitches in output stream } enable_interrupts ( GLOBAL ); // turn on again } else { LCD_SetPosition ( LINE_1 ); printf ( LCD_PutChar, "No samples yet!" ); delay_ms ( 1000 ); } delay_ms ( 1000 ); // allow time to view 9600 baud msg, etc. LCD_PutCmd ( CLEAR_DISP ); cMenuState = STATE_START; // menu displays "LOG" break; } } } } void SetRange ( BYTE cDisplay ) { LCD_SetPosition ( LINE_1 + 3 ); switch ( cRange ) { case ( 0 ): // if 5V range { output_float ( RANGE_SHUNT ); // let shunt resistor float if ( cDisplay == YES ) { printf ( LCD_PutChar, "Range: 5V " ); // 5V scale } break; } case ( 1 ): // if 14V range { output_low ( RANGE_SHUNT ); // pull shunt resistor down if ( cDisplay == YES ) { printf ( LCD_PutChar, "Range: 14V" ); // 14V scale } break; } } } void CheckSample ( void ) { long iVal; if ( cSampleFlag == ON ) // if time to sample { if ( iSampleCount >= ( EEPROM_BYTE_SIZE / 2 ) ) // if at end of memory { cLogging = NO; // stop any further logging write_eeprom ( LOGGING_STATE, NO ); cMenuState = STATE_STATUS; // display status menu next } else // if not at end of memory { iVal = read_adc(); write_ext_eeprom ( iSampleCount * 2, iVal / 256 ); // write high data to external EEPROM write_ext_eeprom ( ( iSampleCount * 2 ) + 1, iVal % 256 ); // write low data iSampleCount++; // point to next memory location write_eeprom ( SAMPLE_COUNT_HI, ( char ) ( iSampleCount / 256 ) ); // save sample count to internal EEPROM write_eeprom ( SAMPLE_COUNT_LO, ( char ) ( iSampleCount % 256 ) ); } cSampleFlag = OFF; // reset flag, interrupt turns on again later } } void init_ext_eeprom ( void ) { output_float ( EEPROM_SCL ); output_float ( EEPROM_SDA ); } void write_ext_eeprom ( long int lngAddress, BYTE intData ) { i2c_start(); i2c_write ( 0xa0 ); // set up for writing address and data, chip address 000 i2c_write ( hi ( lngAddress ) ); // write high address i2c_write ( lngAddress ); // write low address i2c_write ( intData ); // write data i2c_stop(); delay_ms ( 11 ); } BYTE read_ext_eeprom ( long int lngAddress ) { BYTE intData; i2c_start(); i2c_write ( 0xa0 ); // set up for writing address, chip address 000 i2c_write ( hi ( lngAddress ) ); // write high address i2c_write ( lngAddress ); // write low address i2c_start(); i2c_write ( 0xa1 ); // set up for reading data, chip address 000 intData = i2c_read ( 0 ); // read data i2c_stop(); return ( intData ); } char GetEchoNumChar ( void ) { char cX; while ( TRUE ) { // cX = getc(); // wait for character cX = getc(); if ( ( cX >= '0' ) && ( cX <= '9' ) ) // check bounds { break; } } putc ( cX ); // echo to screen return ( cX - 0x30 ); // adjust to numeric } void DisplayVolts ( long iAdcValue, char cLoc ) { float fX; LCD_SetPosition ( LINE_1 + cLoc ); if ( iAdcValue == 0x3FF ) { printf ( LCD_PutChar, " O/L " ); // out of range } else { fX = ScaleAdc ( iAdcValue ); printf ( LCD_PutChar, "%2.2fV ", fX ); // display data sample } } float ScaleAdc ( long iValue ) { float fScale, fX; switch ( cRange ) { case ( 0 ): { fScale = VDD; // 5V scale break; } case ( 1 ): { fScale = 14; // 14V scale break; } } return ( ( ( float ) iValue ) / 1023 * fScale ); // scale to proper range, 1023 leaves room for out-of-range } // LCD FUNCTIONS ================================= 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 } 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(); } 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 ); } 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(); } void LCD_PulseEnable ( void ) { output_high ( LCD_EN ); delay_us ( 10 ); output_low ( LCD_EN ); delay_ms ( 5 ); } void LCD_SetData ( unsigned int cX ) { output_bit ( LCD_D0, cX & 0x01 ); output_bit ( LCD_D1, cX & 0x02 ); output_bit ( LCD_D2, cX & 0x04 ); output_bit ( LCD_D3, cX & 0x08 ); }