#include #include "EESD.h" /* * This is a starting point of a library * of functions that are useful. THese * fuctions drive the LCD dispaly. * * It is recommended that you create a * similar set of fucntions that work with * HyperTerminal * */ /* * Setup LCD display * */ void LCD_init(void) { trisd = trisd & 0x0f; //Top 4 pins configured for output trise = trise & 0xf8; //Bottom 3 pins configured for output adcon1 |= 0x0f; // port E digital mode delay_ms(40); LCD_icmd( 0x30 ) ; /* 8 Bit mode */ delay_ms(5); // DH - min delay here of 4.1 ms LCD_icmd( 0x30 ) ; /* 8 Bit mode */ LCD_icmd( 0x30 ) ; /* 8 Bit mode */ LCD_icmd( 0x20 ) ; /* 4-BIT Mode */ LCD_cmd( 0x28 ); /* Function Set: 4-bit,2-line,5X7 */ LCD_cmd( 0x0C ); /* Display on, Cursor off */ LCD_cmd( 0x06 ); /* Entry mode: INC addr, NO SHIFT display */ LCD_cmd( 0x01 ); /* Clear Display */ delay_ms( 20 ); } /******************************************************* * LCD_cmd - Write ASCII character to LCD peripheral * * configured as a 4 bit interface * *******************************************************/ void LCD_char(char data) { char dsave; dsave = latd; LCD_DATA = (data & 0xF0) | (LCD_DATA & 0x0F); /* Write HI nibble word to LCD */ write_data; LCD_DATA = ((data << 4) & 0xF0) | (LCD_DATA & 0x0F); /* Write LO nibble word to LCD */ write_data; delay_ms(1); latd = dsave; } /***************************************************** * LCD_icmd - Write control word to LCD peripheral * *****************************************************/ void LCD_icmd(char data) { char dsave; dsave = portd; LCD_DATA = data | (LCD_DATA & 0x0F) ; write_cmd; delay_ms(1); portd = dsave; return; } /*************************************************** * LCD_cmd - Write control word to LCD peripheral * * configured as a 4 bit interface * **************************************************/ void LCD_cmd(char data) { char dsave; dsave = portd; LCD_DATA = (data & 0xF0) | (LCD_DATA & 0x0F); /* Write HI nibble word to LCD */ write_cmd; LCD_DATA = ((data << 4) & 0xF0) | (LCD_DATA & 0x0F); /* Write LO nibble word to LCD */ write_cmd; if (data < 0x03) delay_ms(20); else delay_ms(1); portd = dsave; return; } /* set the cursor position */ void LCD_setpos(char line, char pos) { LCD_cmd(0x80 + line * 0x40 + pos); return; } /********************** * routine to clear display screen * */ void LCD_clear(void) { LCD_cmd( 0x01 ); /* Clear Display */ delay_ms(20); return; } /******************************** * Display char in HEX - format * ********************************/ void LCD_hex(char data) { char n; n = ((data >> 4) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = (data & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); return; } /******************************** * Display unsigned short in HEX - format * ********************************/ void LCD_hex(unsigned short data) { char n; n = ((data >> 12) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = ((data >> 8) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = ((data >> 4) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = (data & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); return; } /******************************** * Display short in HEX - format * ********************************/ void LCD_hex(short data) { char n; n = ((data >> 12) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = ((data >> 8) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = ((data >> 4) & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); n = (data & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); return; } /******************************** * Display low nibble in HEX - format * ********************************/ void LCD_nib(char data) { char n; n = (data & 0x0F) + 0x30; if (n > 0x39) n = n+7; LCD_char(n); return; } /******************************** * Display byte in binary - format * ********************************/ void LCD_bin(char data) { char n; char temp; temp =0x80; for( n=1; n<=8; ++n) { if(temp & data) LCD_char(0x31); else LCD_char(0x30); temp = temp >> 1; } return; } /******************************** * Display unsigned short in binary - format * ********************************/ void LCD_bin(unsigned short data) { char n; unsigned short temp; temp =0x8000; for( n=1; n<=16; ++n) { if(temp & data) LCD_char(0x31); else LCD_char(0x30); temp = temp >> 1; } } void LCD_dec(short dat) { unsigned short val; // ascii results unsigned short temp; unsigned short div; unsigned short data; char i; char digit; data = dat; // make it unsigned div = 10000; for(i=0; i <= 4; ++i) // get all 5 digits { val = data/div; // get most signif. digit LCD_char(val + '0'); // print digit data -= val * div; // what we’ve printed div=div/10; // adjust divisor } return; } void LCD_dec(unsigned short dat) { unsigned short val; // ascii results unsigned short temp; unsigned short div; unsigned short data; char i; char digit; data = dat; // make it unsigned div = 10000; for(i=0; i <= 4; ++i) // get all 5 digits { val = data/div; // get most signif. digit LCD_char(val + '0'); // print digit data -= val * div; // what we’ve printed div=div/10; // adjust divisor } return; } /********************************** * Display byte as decimal number * **********************************/ void LCD_dec(char data) { LCD_char(((data /100) & 255) + 0x30); data = data % 100; LCD_char( ((data / 10) & 255) + 0x30 ); LCD_char( ((data % 10) & 255) + 0x30 ); return; } /* * display char as a signed int * */ void LCD_int(char data) { char val; // ascii results char temp; char div; char i; bool dozero; dozero = false; div = 100; if(data == 0) // always print 0 { LCD_char('0'); return; } /* * adjust for sign * */ if(data & 10000000b) { LCD_char('-'); data = ~data + 1; } for(i=0; i <= 2; ++i) // get all 3 digits { val = data/div; // get most signif. digit if(val != 0 || dozero) { LCD_char(val + '0'); // print digit dozero = true; } temp = val * div; // subtract off data = data - temp; // what we’ve printed div=div/10; // adjust divisor } } /* * display unsigned short as a signed int * */ void LCD_int(short dat) { short val; // ascii results short temp; short div; char i; unsigned short data; bool dozero; dozero = false; data = dat; if(dat == 0) // always print 0 { LCD_char('0'); return; } div = 10000; /* * adjust for sign * */ if(dat < 0) { LCD_char('-'); data = ~data + 1; } for(i=0; i <= 4; ++i) // get all 3 digits { val = data/div; // get most signif. digit if(val != 0 || dozero) { LCD_char(val + '0'); // print digit dozero = true; } data -= val*div; // subtract off what we’ve printed div=div/10; // adjust divisor } } void LCD_int(unsigned short dat) { short val; // ascii results short temp; short div; char i; unsigned short data; bool dozero; dozero = false; data = dat; if(dat == 0) // always print 0 { LCD_char('0'); return; } div = 10000; /* * adjust for sign * */ for(i=0; i <= 4; ++i) // get all 3 digits { val = data/div; // get most signif. digit if(val != 0 || dozero) { LCD_char(val + '0'); // print digit dozero = true; } data -= val*div; // subtract off what we’ve printed div=div/10; // adjust divisor } } void LCD_printf( const char* text ) { char i = 0; while( text[i] != 0 ) LCD_char( text[i++] ); return; } /******************************************************* putc - puts a character in the microcontroller on the the terminal *******************************************************/ void putc (char value) { volatile bit thing@PIR1.4; while (!thing); txreg = value; } /******************************************************* getc - gets a character from terminal input and places it in the serial transmission register. *******************************************************/ char getc(void) { volatile bit thing@PIR1.5; while (!thing); putc(rcreg); return rcreg; } /******************************************************* term_char--prints a character to the terminal *******************************************************/ //char term_char(char data){ // while(1){ // data = getc(); // } //} /******************************************************* term_printf--prints a string to the terminal using term_ char. *******************************************************/ void term_printf( const char* text ) { char i = 0; while( text[i] != 0 ){ putc(text[i++]); } } /******************************************************* term_dec prints the value of an unsigned short variable to the terminal. *******************************************************/ void term_dec(unsigned short dat) { unsigned short val; // ascii results unsigned short temp; unsigned short div; unsigned short data; char i; char digit; data = dat; // make it unsigned div = 10000; for(i=0; i <= 4; ++i) // get all 5 digits { val = data/div; // get most signif. digit putc(val + '0'); // print digit data -= val * div; // what we’ve printed div=div/10; // adjust divisor } return; } /******************************************************* getnum--prints the value of an unsigned short val- ue. *******************************************************/ unsigned short getnum(void) { char val; char temp; char j; val = 0; j = 0; do { temp = getc(); // get value //putc(temp); //echo that, dude if(temp == '\r') return(val); val *= 10; val += (temp - '0'); } while(1); } /*********************************************************************************/ /**********************5-0 DEFINED FUNCTIONS START HERE***************************/ /*********************************************************************************/ /*INITIALIZATION COMMANDS**************************************************/ /**************************************************** Initializes the I2C serial protocol, which connects the memory and time chip to the microcontroller *****************************************************/ void i2c_init(void){ sspstat.7 = 1; sspstat.6 = 0; sspcon1.5 = 1; /* SPEN */ sspcon1.3 = 1; /*These 4 bits set the mc to i2c master mode*/ sspcon1.2 = 0; sspcon1.1 = 0; sspcon1.0 = 0; sspadd = 0x63; // For 100 kHz serial transmission } /******************************************************* * term_init - Initializes the drivers on the microcont-* * roller to prepare for serial transmission of data. * (This was written for our taskwork, but serial transmission * will become important in the future if we want to do a * serial dump of the data in memory to provide feedback on * car performance.) *******************************************************/ void term_init(void) { txsta |= 0x24; //00100100 rcsta |= 0x90; //10010000 baudcon |= 0x08; //00001000 spbrgh = 0x00; //00000000 spbrg = 42; //00101010 trise.2 = 0; //11111000 late.2 = 1; //11111111 } /********************************************** This function enables all interrupts, eliminating the need to constantly research these critically important registers. (This has actually become a bit redundant--the interrupts are disabled at the beginning of the program to prevent a malignant interrupt from disturbing the system, and are then set properly immediately before entering sleep mode. We need GIE and PIE to do I2C stuff, which explains the presence of those values here at the beginning **********************************************/ void interrupt_init(void) { intcon.7 = 1; //global interrupt enable intcon.6 = 1; //peripheral interrupt enable //these instructions are for consistency and reference; we'll turn them into interrupts at an appropriate time in the //program intcon.4 = 0; //INT0 external interrupt DISABLED. flag @intcon.1 intcon3.3 = 0; //INT1 external interrupt DISABLED. flag @intcon3.0 osccon = 0xF0; //will go to idle when sleep command is executed //PR_IDLE--continues to clock peripherals after sleep is executed intcon2.6 = 1; //Rising edge on the input will trigger int0 and int1. intcon2.5 = 1; } /*This function activates and appropriately orients the alternator and override interrupts (high-to-low, low-to-high) before the program enters idle mode.*/ void interrupt_init_sleep(void) { volatile bit override@PORTB.0; //(pin B0) volatile bit alt@PORTB.1; //(pin B1) if(override == 1){ //if override is on intcon2.6 = 0; //triggers on high-low transition intcon.4 = 1; //enable interrupt } if(override == 0){ //if override is off intcon2.6 = 1; //trigger on low-high transition intcon.4 = 1; } if(alt == 1){ //if car is on intcon2.5 = 0; //trigger on high-low transition intcon3.3 = 1; //enable interrupt } if(alt == 0){ //if car is off intcon2.5 = 1; //trigger on low-high transition intcon3.3 = 1; //enable interrupt } } /******************************************************* * initialize_ad--sets register bits relevant to performing* * AD conversion on the 4620.* *******************************************************/ void ad_init(void){ // Configure the A/D module: adcon1 = 0x09; // -Configure analog pins, voltage reference and digital I/O (ADCON1) // 00|00|1001 // - Select A/D input channel (ADCON0) // do this specifically elsewhere adcon2 = 0x0F; // - Select A/D acquisition time (ADCON2) // - Select A/D conversion clock (ADCON2) // 00|001|111 adcon0.0 = 1; adcon0.1 = 0; // - Turn on A/D module (ADCON0) } /********************************************************************** Initializes settings to ready all digital in/out ports to act as outputs, for the purpose of turning on / switching off devices. ***********************************************************************/ void io_init(void){ short switchoff = 0; //switch off device short switchon = 1; //switch on device /*These settings define output ports that will enable or disable various various devices*/ trisd = 0; latd = switchoff; /*d.0 = out0, 2wayrad d.1 = out1, cgpsrm d.2 = out2, lightbar d.3 = out3, peripherals d.4 = out4, warning light etc... or however we want to do it pretty simple to change.*/ /*The only two up/down inputs are the alternator and override. That's set below, although, of course, it's entirely possible we may need more settings like this in the future. I have used port B because it will be possible to use this to trigger an interrupt*/ trisb.1 = 1; /*AN10-RB1: input for alternator. volatile bit alt@portb.1*/ trisb.0 = 1; /*AN11-RB4: input for override switch. volatile bit override@portb.0*/ } /********************************** Initializes the watchdog timer for use in the idling function. Poetry is fun! (this function is now redundant; it's handled in the first interrupt command ********************************/ /*void sleep_mode(void){ osccon.7 = 1; //RC_IDLE mode; peripheral devices continue to operate. osccon.1 = 1; } */ /*AD FUNCTIONS********************************************************/ /******************************************************* * check_batt_voltage--runs the ad converter on 4620* * to check the voltage on the battery (duh).* *******************************************************/ unsigned short check_batt_voltage(void){ //pin 2 unsigned short voltage; volatile bit go_done@ADCON0.1; //initialize_ad(); // 00|0000|01; adcon0 = 0x01; delay_ms(100); go_done = 1; while(go_done){} voltage = adresh*256 + adresl; adcon0.0 = 0; return voltage; } /******************************************************* * check_batt_current--runs the ad converter on 4620* * to check the voltage on the battery (duh).* *******************************************************/ unsigned short check_batt_current(void){ //pin 3 unsigned short current; volatile bit go_done@ADCON0.1; //initialize_ad(); // 00|0001|01; adcon0 = 0x05; delay_ms(100); go_done = 1; while(go_done){} current = adresh*256 + adresl; adcon0.0 = 0; return current; } /********************************************* Scales unsigned short raw data to range of 0-5 Volts *********************************************/ unsigned short volt_scaler(unsigned short raw){ unsigned short volts; unsigned short vones; unsigned short vtenths; unsigned short vhunds; vones = raw/13094; //13094 = 65470 / 5 (approx) raw = raw % 13094; vtenths = raw/1309; //1309 = 13094 / 10 (approx) raw = raw % 1309; vhunds = raw/131; //131 = 1390 / 10 (approx) volts = (100 * vones) + (10 * vtenths) + vhunds; //combine to form number from 000-500 return volts; } /************************************************ Prints the voltage measured from the AD converter to the LCD screen, after downconverting it into ones, tenths, hundredths *************************************************/ void print_volt(unsigned short dingus){ //dingus is a value ranged 000 - 500, interpreted as 0.00 - 5.00 V unsigned short vones; unsigned short vtenths; unsigned short vhunds; vones = dingus / 100; //Extracts ones place from input LCD_int(vones); LCD_printf("."); //Decimal place gives impression of FP number dingus = dingus % 100; vtenths = dingus / 10; //Extract tenths place LCD_int(vtenths); dingus = dingus % 10; //Extract hundredths place vhunds = dingus; LCD_int(vhunds); LCD_printf(" V"); } /*********************************************** The same basic project as the volt scaler, this time with currents up to 20 A, and again up to hundredth of an ampere (we anticipate accuracy being a gerater challenge here because of the greater range of values the AD converter is being asked to represent. *************************************************/ unsigned short current_scaler(unsigned short raw){ unsigned short amps; unsigned short atens; unsigned short aones; unsigned short atenths; unsigned short ahunds; atens = raw/32735; // 32735 = 65470 / 2 raw = raw % 32735; aones = raw/3274; // 3273 = 32735 / 10 (approx) raw = raw % 3274; atenths = raw/327; // 327 = 3273 / 10 (approx) raw = raw % 327; ahunds = raw / 33; // 33 = 327 / 10 (approx) amps = (1000 * atens) + (100 * aones) + (10 * atenths) + ahunds; //combine to form number from 0000-2000 return amps; } /************************************************ Prints the current measured from the AD converter to the LCD screen, after downconverting it into tens, ones, tenths, hundredths *************************************************/ void print_amps(unsigned short dingus){ //dingus is a value ranged 000 - 500, interpreted as 0.00 - 5.00 V unsigned short atens; unsigned short aones; unsigned short atenths; unsigned short ahunds; atens = dingus / 1000; //Extracts tens place from input LCD_int(atens); //print dingus = dingus % 1000; aones = dingus / 100; //Extracts ones place from input LCD_int(aones); LCD_printf("."); //Decimal place gives impression of FP number dingus = dingus % 100; atenths = dingus / 10; //Extract tenths place LCD_int(atens); dingus = dingus % 10; //Extract hundredths place ahunds = dingus; LCD_int(ahunds); LCD_printf(" A"); //Aesthetic Touch.com } /*I2C FUNCTIONS*******************************************************/ /*this set of functions is meant to replace some basic operations in doing I2C transmission. After integrating them, I had to take them out in an attempt to debug something and haven't reintegrated them (there was nothing wrong with the functions themselves), but that will be accomplished eventually*/ void i2c_start(void){ volatile bit thing@PIR1.3; //set to one whenever an event happens sspcon2.0 = 1; //initializes start mode while ( thing == 0) {} //waits for flag, then resets. thing = 0; } void i2c_stop(void){ volatile bit thing@PIR1.3; sspcon2.2 = 1; //initializes stop mode while( thing == 0) {} thing = 0; } /*these two functions are used in serial reads, to acknowledge a received bit--it might hae to be modified to allow a variable to be passed through it, because the value read from the time chip or memory might need to be passed to a variable before the acknowledge sequence is begun.*/ void i2c_send_ack(void){ volatile bit thing@PIR1.3; sspcon2.4 = 1; //send an interrupt sspcon2.5 = 0; //send acknowledge while (thing == 0) {} thing = 0; } void i2c_send_not_ack(void){ volatile bit thing@PIR1.3; sspcon2.4 = 1; //send an interrupt sspcon2.5 = 1; //send not acknowledge while (thing == 0) {} thing = 0; } /*This needs to be set before any value is received off the I2C line*/ void i2c_receive_mode(void){ volatile bit thing@PIR1.3; sspcon2.3 = 1; //receive mode enable while (thing == 0) {} thing = 0; } /*Sets the time registers we're concerned with. At some point in the future, this will have to be done with some sort of user input, like a touchpad, but for now, this demonstrates how a write works, for the sake of demonstration*/ void i2c_time_write(short addr, short data){ volatile bit thing@PIR1.3; i2c_start(); sspbuf = 0xD0; /* 11010000, code for time chip WRITE*/ while( thing == 0) {} thing = 0; sspbuf = addr; while( thing == 0) {} thing = 0; sspbuf = data; while( thing == 0) {} thing = 0; i2c_stop(); } /*Old test function to read seconds off time chip; now redundant*/ short i2c_time_read_seconds(void){ short seconds; volatile bit thing@PIR1.3; i2c_start(); sspbuf = 0xD0; /*slave address WRITE*/ while( thing == 0) {} thing = 0; sspbuf = 0x00; /*seconds register*/ while ( thing == 0) {} thing = 0; sspcon2.1 = 1; /*restart condition*/ while ( thing == 0) {} thing = 0; sspbuf = 0xD1; /*slave address READ*/ while ( thing == 0) {} thing = 0; i2c_receive_mode(); i2c_send_not_ack(); i2c_stop(); seconds = sspbuf; return seconds; } /*basic digital clock function; reads seconds, minutes, hours, date, and month; processes results into numerically sensical values, and prints them on the LCD.*/ void i2c_time(void){ /*PARAMETERS*/ short minutes; short hours; short date; short month; short seconds; volatile bit thing@PIR1.3; /*RETREIVAL SEQUENCE*/ sspcon2.0 = 1; while ( thing == 0) {} thing = 0; sspbuf = 0xD0; /*slave address WRITE*/ while( thing == 0) {} thing = 0; sspbuf = 0x00; /*seconds register*/ while ( thing == 0) {} thing = 0; sspcon2.1 = 1; /*restart condition*/ while ( thing == 0) {} thing = 0; sspbuf = 0xD1; /*slave address READ*/ while ( thing == 0) {} thing = 0; sspcon2.3 = 1; while (thing == 0) {} thing = 0; sspcon2.4 = 1; sspcon2.5 = 0; seconds = sspbuf; /*send ack. bit, store seconds*/ while (thing == 0) {} thing = 0; sspcon2.3 = 1; while (thing == 0) {} thing = 0; sspcon2.4 = 1; sspcon2.5 = 0; minutes = sspbuf; /*send ack. bit, store minutes*/ while (thing == 0) {} thing = 0; sspcon2.3 = 1; while (thing == 0) {} thing = 0; sspcon2.4 = 1; sspcon2.5 = 0; hours = sspbuf; /*send ack. bit, store hours*/ while (thing == 0) {} thing = 0; sspcon2.3 = 1; while (thing == 0) {} thing = 0; sspcon2.4 = 1; sspcon2.5 = 0; /*send ack. bit, continue but don't store anything here*/ while (thing == 0) {} thing = 0; sspcon2.3 = 1; while (thing == 0) {} thing = 0; sspcon2.4 = 1; sspcon2.5 = 0; date = sspbuf; /*send ack. bit, store date*/ while (thing == 0) {} thing = 0; sspcon2.3 = 1; while (thing == 0) {} thing = 0; /*send NOT ack. bit, store month*/ sspcon2.4 = 1; sspcon2.5 = 1; month = sspbuf; while (thing == 0) {} thing = 0; sspcon2.2 = 1; while( thing == 0) {} thing = 0; /*PROCESSING SEQUENCE*/ /*SECONDS*/ short tenseconds; short oneseconds; tenseconds = seconds / 16; oneseconds = seconds % 16; seconds = (tenseconds * 10) + oneseconds; /*MINUTES*/ short tenminutes; short oneminutes; tenminutes = minutes / 16; oneminutes = minutes % 16; minutes = (tenminutes * 10) + oneminutes; /*HOURS--assume 24-hour mode*/ short tenhours; short onehours; tenhours = hours / 16; onehours = hours % 16; hours = (tenhours * 10) + onehours; /*DATE*/ short onedate; short tendate; tendate = date / 16; onedate = date % 16; date = (tendate * 10) + onedate; /*MONTH*/ short tenmonth; short onemonth; tenmonth = (month / 16) % 2; onemonth = month % 16; month = (tenmonth * 10) + onemonth; /*PRINT TO LCD*/ LCD_int(month); LCD_printf("/"); LCD_int(date); if (hours < 10){ LCD_printf(" 0"); LCD_int(hours); } else{ LCD_printf(" "); LCD_int(hours); } LCD_printf(":"); if (minutes < 10){ LCD_printf("0"); LCD_int(minutes); } else{ LCD_int(minutes); } LCD_printf(":"); if (seconds < 10){ LCD_printf("0"); LCD_int(seconds); } else{ LCD_int(seconds); } } void time_init_clk( short minutes, short hours, short date, short month ){ volatile bit thing@PIR1.3; short addr = 0x01; sspcon2.0 = 1; while ( thing == 0) {} thing = 0; sspbuf = 0xD0; /* 11010000, code for time chip WRITE*/ while( thing == 0) {} thing = 0; sspbuf = addr; /*start at the minute register*/ while( thing == 0) {} thing = 0; sspbuf = minutes; /*initialize minutes*/ while( thing == 0) {} thing = 0; sspbuf = hours; /*initialize hours*/ while( thing == 0) {} thing = 0; sspbuf = date; /*initialize date*/ while( thing == 0) {} thing = 0; sspbuf = month; /*initialize month*/ while( thing == 0) {} thing = 0; sspcon2.2 = 1; while( thing == 0) {} thing = 0; } /*end of time initialization sequence--now to chip settings These commands enable the timer to output an interrupt on the */ void time_init_set(void){ volatile bit thing@PIR1.3; short stat = 1; while(stat == 1){ sspcon2.0 = 1; while (thing == 0) {} thing = 0; sspbuf = 0xD0; while (thing == 0) {} if(sspcon2.6 == 0){ stat = 0; } thing = 0; } sspbuf = 0x0B; while (thing == 0) {} thing = 0; sspbuf = 0x00; while (thing == 0) {} thing = 0; sspbuf = 0x00; while (thing == 0) {} thing = 0; sspbuf = 0x00; while (thing == 0) {} thing = 0; sspbuf = 0x00; while (thing == 0) {} thing = 0; sspcon2.2 = 1; while (thing == 0) {} thing = 0; } /*time write hasn't been updated to reflect our work with voltage write, but we don't see an issue integrating this with what we've learned.*/ unsigned short mem_write_time(unsigned short oldmem, short minute, short hours){ /*MEMORY SLAVE ADDRESS: 000*/ volatile bit thing@PIR1.3; short spacing = 2; //correspoding to # of bytes we're writing, obvy. unsigned short newmem; unsigned short addrlow; unsigned short addrhigh; addrlow = oldmem % 256; addrhigh = oldmem / 256; i2c_start(); sspbuf = 0xA0; //control byte for the memory chip while( thing == 0){} thing = 0; sspbuf = addrhigh; while( thing == 0) {} thing = 0; sspbuf = addrlow; while( thing == 0) {} thing = 0; sspbuf = hours; while( thing == 0) {} thing = 0; sspbuf = minute; while( thing == 0) {} thing = 0; /*... etcetera, etcetera...*/ sspcon2.2 = 1; /*Retrieve present pointer address in memory*/ sspcon2.0 = 1; while ( thing == 0){} thing = 0; sspbuf = 0xA1; while ( thing == 0){} thing = 0; newmem = sspbuf; sspcon2.2 = 1; /*Update memory for future writes*/ if (newmem << addrlow){ newmem = ((addrhigh + 1) * 256) + newmem; } else{ newmem = (addrhigh * 256) + newmem; } return newmem; } /*this functions writes the measured voltage to memory (at the moment, it's the only functional memory write command, although what's done here is easily extendable to whatever other types of data we'd like to write to the memory chip. The front end includes a waiting scheme that allows the uc to finish whatever it is doing before performing a write*/ void mem_write_voltage(unsigned short oldmem, short voltage){ /*MEMORY SLAVE ADDRESS: 000*/ short stat = 1; volatile bit thing@PIR1.3; short spacing = 2; //correspoding to # of bytes we're writing, obvy. short volthigh; short voltlow; short addrlow; short addrhigh; volthigh = voltage % 256; voltlow = voltage / 256; addrlow = oldmem % 256; addrhigh = oldmem / 256; while(stat == 1){ //Wait for slave to finish what it's doing i2c_start(); sspbuf = 0xA0; //Write to memory address command while (thing == 0) {} if (sspcon2.6 == 1){ //If NOT ACK (still writing) thing = 0; sspcon2.2 = 1; //Send STOP while(thing == 0){} thing = 0; } else{ //If ACK (sspcon2.6 = 0) stat = 0; //Break while loop } } thing = 0; sspbuf = addrhigh; //Send top half of address while( thing == 0) {} thing = 0; sspbuf = addrlow; //Send bottom half of address while( thing == 0) {} thing = 0; sspbuf = volthigh; //send top half of voltage measurement while( thing == 0) {} thing = 0; sspbuf = voltlow; //send bottom half of voltage measurement while (thing == 0){} thing = 0; /*... etcetera, etcetera...*/ sspcon2.2 = 1; //stop while (thing == 0) {} thing = 0; } /*Finds the next open address in memory, and updates the memory value in light of this. In the future, some type of mechanism to check how close we're getting to the end of the page, and updating the high register of the memory to reflect that, will become necessary, but since we're still trying to figure out exactly what we need to write, we're holding off on this for now (it won't take long to implement once we know)*/ short address_update(unsigned short oldmem){ volatile bit thing@PIR1.3; unsigned short newmem; short stat = 1; short addrlow; short addrhigh; addrlow = oldmem % 256; addrhigh = oldmem / 256; while(stat == 1){ //Wait for slave write to finish i2c_start(); sspbuf = 0xA1; //Read from memory address command while (thing == 0) {} if (sspcon2.6 == 1){ //If NOT ACK (still writing) thing = 0; sspcon2.2 = 1; //Send STOP while(thing == 0){} thing = 0; } else{ //If ACK (sspcon2.6 = 0) stat = 0; //Break while loop } } thing = 0; sspcon2.3 = 1; //set to receive mode while(thing == 0){} thing = 0; newmem = sspbuf; //write adress to variable sspcon2.4 = 1; //send "NOT ACK" signal to slave sspcon2.5 = 1; while (thing == 0){} thing = 0; sspcon2.2 = 1; //STOP transmission command while (thing ==0){} thing = 0; } //////////////////Mode Selection and Switching//////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// /*This function will use the battery voltage and current readings off of the car battery (and possibly current being drawn by various devices, if we get that far?), and combined with the on/off status of the car, return a numeric code to the main program which determines which devices will be allowed off or on.*/ short mode_select(short Vbatt, short Ibatt){ volatile bit override@PORTB.0; //(pin B0) volatile bit alt@PORTB.1; //(pin B1) short Vcrit_on = 300; //complete guesses on all numbers, simply for demonstration's sake. short Icrit_on = 1500; //The voltage value is for the sake of the current 0-5 volt A-D converter. //these voltages are being expressed in terms of the values our voltage scaler //actually outputs; a V=300 is actually 3.0 volts; an I = 1500 is 15.00 A, etc. short Vcrit_off = 400; short Icrit_off = 1000; short mode; /*In here will come some sort of battery checking regimen, which is largely dependent on battery experimentation we haven't been able to do as yet. This will come later when we integrate the hardware and software aspects of the project. For now, I am using a simple algorithm to give the program the appearance of intelligent battery operation. It may end up being as simple as this--some V and I threshold that we can't get below, or else we enter critical mode--or something more subtle, but we'll find that out. V is set from 0 to 5 at the moment simply for demonstration purposes*/ if(override == 1){ mode = 0; //0--override mode--will send system into an idle state while //waiting for any important status change that might alter the //system. } if(alt == 1){ if ((Vbatt >= Vcrit_on) && (Ibatt >= Icrit_on)){ mode = 1; //1--stable on mode. } else{ mode = 2; //2--critical on mode, ie battery is dying and devices need } //to be shut down rapidly to prevent further depletion, esp- //ecially if it turns out one of our problems is an overdrawn //battery. } else{ if ((Vbatt >= Vcrit_off) && (Ibatt >= Icrit_off)){ mode = 3; //3--stable off mode. } else{ mode = 4; //4--critical off mode. } } return mode; } /*This function takes the mode passed from the above function and switches devices (or device lines) accordingly. Some modes, predictably, have the same procedure in terms of which switches are thrown. They are determined separately above for reporting reasons.*/ short switch_devices(short mode){ volatile bit device1@LATD.0; volatile bit device2@LATD.1; volatile bit device3@LATD.2; volatile bit device4@LATD.3; volatile bit warnlight@LATD.4; short switchoff = 0; //switch off device short switchon = 1; //switch on device /*May need to be reversed at some point depend on our switch demands. Writing the code this way allows the most flexibility, in terms of ports and on/off values.*/ short status; if(mode == 0 || mode == 1){ //override, or car on, high battery device1 = switchon; device2 = switchon; device3 = switchon; device4 = switchon; warnlight = 0; } if(mode == 2){ //car on, battery failing device1 = switchon; device2 = switchon; device3 = switchon; device4 = switchoff; warnlight = 1; } if(mode == 3){ //car off, battery fine device1 = switchon; device2 = switchon; device3 = switchoff; device4 = switchoff; warnlight = 0; } if(mode == 4){ //car off, battery failing device1 = switchoff; device2 = switchoff; device3 = switchoff; device4 = switchoff; warnlight = 1; } status = latd; return status; }