// module08.c RGD 3/10/04 // // This program will use the ICD-2 in circuit debugger to program the PIC16F887 chip // on the LCD-SD-USB Board for ME499 // // Use PCW (14-bit compiler) to compile. // Circuit configuration: // HS crystal Oscillator, crystal=4 MHz, 1 MHz instruction cycle, watch dog timer ON, Code Protect OFF #include <16F877.H> #include #include #fuses HS, WDT, NOPROTECT // This sets up the internal device fuses: HS crystal, WDT ON, Code Protect OFF #use delay (clock=4000000) // 4 MHz crystal, 1 microsecond instruction cycle time. #byte PORTA = 0x05 #byte PORTB = 0x06 #byte PORTC = 0x07 #byte PORTD = 0x08 #byte PORTE = 0x09 #use FAST_IO(A) #use FAST_IO(C) unsigned int i; // buffer variable for the detected state of PORTb unsigned int b; // bit counter for the 16-bit ADC unsigned int c; // SClk count down counter for ADCs unsigned int d; // A digit (0 1 2 3 4 5 6 7 8 9 : ; < = . /) to be displayed on the LCD module unsigned long int j; unsigned long int btd; // long 16-bit binary number for use in binary-to-decimal conversions unsigned int k; // just a useful variable unsigned int m; // counter for calculation of median values of ADC voltages unsigned int n; // index variable for calculation of median values of ADC voltages unsigned int t; // long time delay variable (0 < t <= 255), in increments of 1/4 second unsigned int p; // a character (from the list below) to be printed to the LCD display unsigned int address; unsigned int step; // this is the counter for keeping track of which step is current short int flip; // variable for flashing LED unsigned int digit10k = 48; // digit value for 16-bit binary-to-decimal conversions, 48 = ASCII "0" unsigned int digit1k = 48; // digit value for 16-bit binary-to-decimal conversions unsigned int digit100 = 48; // digit value for 16-bit binary-to-decimal conversions unsigned int digit10 = 48; // digit value for 16-bit binary-to-decimal conversions unsigned int digit1 = 48; // digit value for 16-bit binary-to-decimal conversions // Variables for calculating 16-bit median values: unsigned long int v0[9] = {0,0,0,0,0,0,0,0,0}; unsigned long int v0_carry = 0; unsigned long int v1[9] = {0,0,0,0,0,0,0,0,0}; unsigned long int v1_carry = 0; // Variables for carrying out floating point calculations for calibrations etc... float x_float = 0; // Character Set for use with LDC display: // Digit character constants: byte const d0 = 48; // ASCII character "0" byte const d1 = 49; byte const d2 = 50; byte const d3 = 51; byte const d4 = 52; byte const d5 = 53; byte const d6 = 54; byte const d7 = 55; byte const d8 = 56; byte const d9 = 57; byte const EQ = 61; // "=" equals sign byte const SP = 32; // " "blank space byte const DP = 46; // "." decimal point // Upper case character constants: byte const A_ = 65; // ASCII character "A" byte const B_ = 66; byte const C_ = 67; byte const D_ = 68; byte const E_ = 69; byte const F_ = 70; byte const G_ = 71; byte const H_ = 72; byte const I_ = 73; byte const J_ = 74; byte const K_ = 75; byte const L_ = 76; byte const M_ = 77; byte const N_ = 78; byte const O_ = 79; byte const P_ = 80; byte const Q_ = 81; byte const R_ = 82; byte const S_ = 83; byte const T_ = 84; byte const U_ = 85; byte const V_ = 86; byte const W_ = 87; byte const X_ = 88; byte const Y_ = 89; byte const Z_ = 90; // Lower case character constants: byte const aa = 97; // ASCII character "a" byte const bb = 98; byte const cc = 99; byte const dd = 100; byte const ee = 101; byte const ff = 102; byte const gg = 103; byte const hh = 104; byte const ii = 105; byte const jj = 106; byte const kk = 107; byte const ll = 108; byte const mm = 109; byte const nn = 110; byte const oo = 111; byte const pp = 112; byte const qq = 113; byte const rr = 114; byte const ss = 115; byte const tt = 116; byte const uu = 117; byte const vv = 118; byte const ww = 119; byte const xx = 120; byte const yy = 121; byte const zz = 122; // ADC variables: unsigned long int setpoint; // position set point for servo, 16-bits unsigned long int error; // position error, 16-bits unsigned long int volts0; // 16-bit voltage value from ADC# 1 input A0 (0 - 65,535) unsigned long int volts1; // 16-bit voltage value from ADC# 2 input A1 // 0 = 0.0 V // 13107 = 1.0 V // 26214 = 2.0 V // 39321 = 3.0 V // 52428 = 4.0 V // 65535 = 5.0 V /////////////////////////////////////// // SUBROUTINES void init_ports(void) { SET_TRIS_A(0b00000011); // PORTA = all outputs SET_TRIS_B(0b00000000); // PORTB = all outputs SET_TRIS_C(0b00000000); // PORTC = all outputs (motor drivers and PWMs) SET_TRIS_D(0b00000000); // PORTD = all outputs SET_TRIS_E(0b00000000); // PORTE = all outputs SETUP_COUNTERS(RTCC_INTERNAL, WDT_1152MS); // Set Watch Dog Timer prescaler to 1152 ms // The Watch Dog Timer (WDT) is just an internal timer for the microprocessor that runs off // on the side independently. If the microprocessor hangs up or gets confused, the WDT // will reset the microprocessor to the first line of the executable code "main()". // To stop this from happening accidentally, we need to reset the WDT very frequently. // in this case, every second or less. Otherwise, the controller will reset automatically. // Default output values: PORT_B_PULLUPS(TRUE); // enable PORT B pullups // May need to do more than this to stabilize B3 sensitivity after ICSP // see the PIC16F8X programming PDF for more information: see section 3.4.1 on LVP mode and bit B3 PORTC = 0b00000000; // set all motor drivers and PWM outputs to OFF PORTD = 0b00000000; // set DATA bus to OFF PORTE = 0b00000000; OUTPUT_BIT(PIN_A2, 1); // ADC Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // ADC Serial Clock (active on FALLING Edge) // Default variable values step = 0; flip = 0; i = 0; j = 0; k = 0; b = 0; t = 0; // This will be the value of the RTCC timer address = 0; } ///////////////////////////////////////////////////// void binary_to_decimal(void){ // convert a 16-bit binary number to a string of decimals // 0 <= btd <= 65535 // Decimal= Binary values: // 1 = 0b0000000000000001 // 10 = 0b0000000000001010 // 100 = 0b0000000001100100 // 1000 = 0b0000001111101000 // 10,000 = 0b0010011100010000 // pre-set all decimal place values to ASCII "0": digit10k = 48; digit1k = 48; digit100 = 48; digit10 = 48; digit1 = 48; // get place value for 10,000s for(k = 0; k < 11; k++) { if(btd >= 0b0010011100010000){ digit10k = digit10k + 1; btd = btd - 0b0010011100010000; } } // get place value for 1000s for(k = 0; k < 11; k++) { if(btd >= 0b0000001111101000){ digit1k = digit1k + 1; btd = btd - 0b0000001111101000; } } // get place value for 100s for(k = 0; k < 11; k++) { if(btd >= 0b0000000001100100){ digit100 = digit100 + 1; btd = btd - 0b0000000001100100; } } // get place value for 10s for(k = 0; k < 11; k++) { if(btd >= 0b0000000000001010){ digit10 = digit10 + 1; btd = btd - 0b0000000000001010; } } // get place value for 1s for(k = 0; k < 11; k++) { if(btd >= 0b0000000000000001){ digit1 = digit1 + 1; btd = btd - 0b0000000000000001; } } restart_wdt(); } ///////////////////////////////////////////////////// void delay_one_sec(t){ // delay for a long period, "t" quarters of a second, 0 < t < 255 while(t > 0){ delay_ms(250); restart_wdt(); // Strobe the amber LED: OUTPUT_BIT(PIN_A5, flip); // strobe the amber LED flip = !flip; // this is now the ENABLE pin OUTPUT_BIT(PIN_E0, flip); // strobe the green LED t = t - 1; } } /////////////////////////////////////////////////////////////////////////// void LCD_enable(void) { // send one ENABLE pulse to the LCD Display delay_ms(1); // allow data bus (PORTD) to settle OUTPUT_BIT(PIN_E0, 1); // cycle Enable pulse ON for 1 ms delay_ms(1); OUTPUT_BIT(PIN_E0, 0); delay_ms(1); restart_wdt(); } ///////////////////////////////////////////////////// void print(p){ // print a character to the LCD display OUTPUT_BIT(PIN_B6, 0); // set R/W = 0, keep set to 0 permanently unless reading from LCD OUTPUT_BIT(PIN_B7, 1); // set RS = 1 to enable writing to display PORTD = p; // set character value on data lines LCD_enable(); // send command to display the character restart_wdt(); } //////////////////////////////////////////////////////////////////////////////// void line2(void){ // set LCD cursor to the start of Line 2 OUTPUT_BIT(PIN_B7, 0); // set RS for command output PORTD = 0b11000000; LCD_enable(); // send command OUTPUT_BIT(PIN_B7, 1); // re-set RS for character output restart_wdt(); } ///////////////////////////////////////////////////////////////////////////////// void clear_display(void){ // clear the display and return cursor to the HOME position OUTPUT_BIT(PIN_B7, 0); // set RS for command output PORTD = 0b00000001; LCD_enable(); // send command OUTPUT_BIT(PIN_B7, 1); // re-set RS for character output restart_wdt(); } /////////////////////////////////////////////////////////////////////////// void init_LCD(void) { // Initialize the LCD Display (DMC16204) in 8-bit mode // Data = PORTD // E = PIN_E0 // R/W = PIN_B6 SHOULD ALWAYS BE SET R/W = 0 // RS = PIN_B7 COMMAND = 0, CHARACTER = 1 OUTPUT_BIT(PIN_E0, 0); // set LCD ENABLE = 0 delay_one_sec(2); // allow power-up // 8-bit Function Set Command, [DMC16204 User Manual, pg 31-32, example sequence pg 44] OUTPUT_BIT(PIN_B6, 0); // set RW OUTPUT_BIT(PIN_B7, 0); // set RS PORTD = 0b00111000; // set interface for 8-bit, 2-line display, 5 x 7 dot character font LCD_enable(); // cycle Enable pulse delay_ms(10); LCD_enable(); // cycle Enable pulse again to repeat command after a delay delay_ms(10); PORTD = 0b00001110; // display ON, cursor ON LCD_enable(); // cycle Enable pulse clear_display(); // clear display and place cursor at HOME position PORTD = 0b00000110; // Entry Set Mode (Increment Mode and Display Shift Mode) LCD_enable(); // cycle Enable pulse OUTPUT_BIT(PIN_B6, 0); // set R/W = 0, keep set to 0 permanently unless reading from LCD OUTPUT_BIT(PIN_B7, 1); // set RS = 1 to enable writing to display clear_display(); // clear display and place cursor at HOME position // Initialization is complete, display is ready for use } /////////////////////////////////////////////////////// void message(void){ // sends a pre-set message to the LCD OUTPUT_BIT(PIN_B7, 1); // set RS = 1 to enable writing to display clear_display(); print(I_); print(nn); print(ii); print(tt); print(ii); print(aa); print(ll); print(ii); print(zz); print(ii); print(nn); print(gg); line2(); // send cursor to the beginning of line 2 of the LCD display print(H_); print(aa); print(rr); print(dd); print(ww); print(aa); print(rr); print(ee); } ////////////////////////////////////////////////////////////////////////////// void init_adc(void) { // Initialize the TLC4545 16-bit ADC // A0 & A1 = SDO Data // A2 = /CS (chip select, active LOW) // A3 = CLK restart_wdt(); OUTPUT_BIT(PIN_A2, 1); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); // Select ADC Chip OUTPUT_BIT(PIN_A2, 0); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); // Cycle Serial Clock 4 times with CS = LOW to reset ADC: OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(4); OUTPUT_BIT(PIN_A2, 1); // Deselect Chip Select (active LOW) restart_wdt(); } /////////////////////////////////////////////////////////////////////// void get_volts(void) { // Read both 16-bit ADC chips, store data into volts0 and volts1 // A0 & A1 = SDO Data // A2 = /CS (chip select, active LOW) // A3 = CLK restart_wdt(); volts0 = 0b0000000000000000; // reset the voltage variable, then set each bit during conversion volts1 = 0b0000000000000000; // reset the voltage variable, then set each bit during conversion // Start with ADC INACTIVE OUTPUT_BIT(PIN_A2, 1); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(1); // Run 24 cycles of SClk to complete the next conversion for (c = 1; c <=25; c = c + 1) { OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(1); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) delay_cycles(1); } // Select ADC Chip: initiate conversion cycle OUTPUT_BIT(PIN_A2, 0); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(1); // Convert and load data. // Conversion begins on 1st falling edge with MSB first // ... then add 8 falling edges on SClk to complete the next conversion b = 15; // pre-set the bit counter for the ADC restart_wdt(); READ_ADC: OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(1); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) if (INPUT(PIN_A0)) BIT_SET(volts0, b); if (INPUT(PIN_A1)) BIT_SET(volts1, b); delay_cycles(1); b = b - 1; if (b < 16) goto READ_ADC; // add 8 falling edges on SCLK to complete the conversion for the next cycle for (c = 1; c <=9; c = c + 1) { OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(1); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) delay_cycles(1); } //De-select the ADC chip OUTPUT_BIT(PIN_A2, 1); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) delay_cycles(1); restart_wdt(); } /////////////////////////////////////////////////////////////////////// void get_volts_median_5(void) { // Read both 16-bit ADC chips, calculate medians of rank = 5, store data into volts0 and volts1 m = 0; n = 0; for(n = 0; n < 9; n = n + 1) { // read the ADCs 9 times and fill the arrays restart_wdt(); v0[n] = 0b0000000000000000; // reset the voltage variable, then set each bit during conversion v1[n] = 0b0000000000000000; // reset the voltage variable, then set each bit during conversion // Start with ADC INACTIVE OUTPUT_BIT(PIN_A2, 1); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) // delay_cycles(1); // Run 24 cycles of SClk to complete the next conversion for (c = 1; c <=25; c = c + 1) { OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) // delay_cycles(1); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) // delay_cycles(1); } // Select ADC Chip: initiate conversion cycle OUTPUT_BIT(PIN_A2, 0); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) // delay_cycles(1); // Convert and load data. // Conversion begins on 1st falling edge with MSB first // ... then add 8 falling edges on SClk to complete the next conversion b = 15; // pre-set the bit counter for the ADC restart_wdt(); READ_ADC: OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) // delay_cycles(1); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) if (INPUT(PIN_A0)) BIT_SET(v0[n], b); if (INPUT(PIN_A1)) BIT_SET(v1[n], b); // delay_cycles(1); b = b - 1; if (b < 16) goto READ_ADC; // add 8 falling edges on SCLK to complete the conversion for the next cycle for (c = 1; c <=9; c = c + 1) { OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) // delay_cycles(1); OUTPUT_BIT(PIN_A3, 0); // Serial Clock (active on FALLING Edge) // delay_cycles(1); } //De-select the ADC chip OUTPUT_BIT(PIN_A2, 1); // Chip Select (active LOW) OUTPUT_BIT(PIN_A3, 1); // Serial Clock (active on FALLING Edge) // delay_cycles(1); restart_wdt(); } // end for n... The arrays v0[n] and v1[n] have been filled // Now, rank order the voltages in each array: for(m = 0; m < 9; m = m + 1) { // ripple through 9 times to rank order each array for(n = 1; n < 9; n = n + 1) { if(v0[n] < v0[n - 1]) { // swap the two variables if they are not in the correct rank order v0_carry = v0[n]; v0[n] = v0[n - 1]; v0[n - 1] = v0_carry; } if(v1[n] < v1[n - 1]) { // swap the two variables if they are not in the correct rank order v1_carry = v1[n]; v1[n] = v1[n - 1]; v1[n - 1] = v1_carry; } } // end for n... } // end for m... // Assign median values to volts0 and volts1: volts0 = v0[4]; volts1 = v1[4]; } void decimal_to_volts(void) { // convert the 16-bit decimal number (0 - 65535) to volts (0 to 5000) x_float = (float)volts0 / 13.107; // convert 16-bit number to millivolts (0 to 5000) volts0 = (unsigned long int) x_float; x_float = (float)volts1 / 13.107; volts1 = (unsigned long int) x_float; } ///////////////////////////////////////////////////// void display_volts_binary(void){ // Display the ADC voltages on the LCD in Binary OUTPUT_BIT(PIN_B7, 1); // set RS = 1 to enable writing to display for(b = 15; b < 255; b--) { // Display the 16-bit voltage for ADC #1 on line 1 of the LCD if(BIT_TEST(volts0, (b))) print(d1); else print(d0); } line2(); for(b = 15; b < 255; b--) { // Display the 16-bit voltage for ADC #2 on line 2 of the LCD if(BIT_TEST(volts1, (b))) print(d1); else print(d0); } } ///////////////////////////////////////////////////// void display_volts_decimal(void){ // display the 16-bit binary as a decimal number 0 - 65,535 OUTPUT_BIT(PIN_B7, 1); // set RS = 1 to enable writing to display restart_wdt(); btd = volts0; binary_to_decimal(); // convert the 16-bit value "volts0" to digital ASCII characters clear_display(); print(vv); print(oo); print(ll); print(tt); print(ss); print(d0); print(SP); print(EQ); print(SP); print(digit10k); print(digit1k); print(digit100); print(digit10); print(digit1); line2(); btd = volts1; binary_to_decimal(); // convert the 16-bit value "volts0" to digital ASCII characters print(vv); print(oo); print(ll); print(tt); print(ss); print(d1); print(SP); print(EQ); print(SP); print(digit10k); print(digit1k); print(digit100); print(digit10); print(digit1); } ///////////////////////////////////////////////////// void display_volts_actual(void){ // display the 16-bit binary as a decimal number 0 - 65,535 OUTPUT_BIT(PIN_B7, 1); // set RS = 1 to enable writing to display restart_wdt(); btd = volts0; binary_to_decimal(); // convert the 16-bit value "volts0" to digital ASCII characters clear_display(); print(vv); print(oo); print(ll); print(tt); print(ss); print(d0); print(SP); print(EQ); print(SP); print(digit1k); print(DP); print(digit100); print(digit10); print(digit1); line2(); btd = volts1; binary_to_decimal(); // convert the 16-bit value "volts0" to digital ASCII characters print(vv); print(oo); print(ll); print(tt); print(ss); print(d1); print(SP); print(EQ); print(SP); print(digit1k); print(DP); print(digit100); print(digit10); print(digit1); } /////////////////////////////////////////////////////////////////////// ////////////////////////////////////// ////////////////////////////////////// // PIC16F877 goes here at RESET void main() { restart_wdt(); // Reset the WDT init_ports(); // Initialize ports init_LCD(); // Initialize LCD message(); // send initialization message to the LCD init_adc(); // Initialize the 16-bit ADC get_volts(); // get volts from ADCs to assure proper initialization get_volts(); // get volts from ADCs to assure proper initialization restart_wdt(); // Reset the WDT CYCLE: // Run continuously restart_wdt(); // Reset the WDT delay_one_sec(2); // clear_display(); get_volts_median_5(); //display_volts_binary(); // delay_one_sec(1); decimal_to_volts(); // convert 16-bit binary (0 - 65535) to millivots (0 - 5000) // display_volts_decimal(); flip = flip - 1; // strobe LEDs OUTPUT_BIT(PIN_C7, flip); OUTPUT_BIT(PIN_C0, !flip); clear_display(); display_volts_actual(); // display actual volts: 0 - 5.000 goto CYCLE; } // main