The MPGuino - Arduino Code

From Combustory
Jump to: navigation, search

Welcome to Combustory


Any questions or comments:

  • Send them to - combustor@combustory.com

Contents


Check here for any updates http://opengauge.googlecode.com/svn/trunk/mpguino/mpguino.pde

MPGuino.pde Code v.701

#define ver=701
/*

*/
//GPL Software    
//#define debuguino youbetyourbippy  
#include <avr/pgmspace.h>  
byte brightness[]={0,42,85,128}; //middle button cycles through these brightness settings      
#define brightnessLength (sizeof(brightness)/sizeof(byte)) //array size      
byte brightnessIdx=1;      


#define contrastIdx 0  //do contrast first to get display dialed in
#define vssPulsesPerMileIdx 1
#define microSecondsPerGallonIdx 2
#define injPulsesPer2Revolutions 3
#define currentTripResetTimeoutUSIdx 4
#define tankSizeIdx 5 
#define injectorSettleTimeIdx 6
#define weightIdx 7
#define scratchpadIdx 8
char *  parmLabels[]={"Contrast","VSS Pulses/Mile", "MicroSec/Gallon","Pulses/2 revs","Timout(microSec)","Tank Gal * 1000","Injector DelayuS","Weight (lbs)","scratchpad(odo?)"};
//unsigned long  parms[]={15ul,16408ul,684968626ul,3ul,420000000ul,13300ul,500ul};//default values
unsigned long  parms[]={15ul,10000ul,304409714ul,4ul,420000000ul,13300ul,500ul,2400ul,0ul,};//default values
#define parmsLength (sizeof(parms)/sizeof(unsigned long)) //array size      

#define nil 3999999999ul
 
#define guinosig B10100101
#include <EEPROM.h>
//Vehicle Interface Pins      
#define InjectorOpenPin 2      
#define InjectorClosedPin 3      
#define VSSPin 14 //analog 0      
 
//LCD Pins      
#define DIPin 4 // register select RS      
#define DB4Pin 7       
#define DB5Pin 8       
#define DB6Pin 12       
#define DB7Pin 13      
#define ContrastPin 6      
#define EnablePin 5       
#define BrightnessPin 9      
 
#define lbuttonPin 17 // Left Button, on analog 3,        
#define mbuttonPin 18 // Middle Button, on analog 4       
#define rbuttonPin 19 // Right Button, on analog 5       
 
#define vssBit 1     //  pin14 is a bitmask 1 on port C        
#define lbuttonBit 8 //  pin17 is a bitmask 8 on port C        
#define mbuttonBit 16 // pin18 is a bitmask 16 on port C        
#define rbuttonBit 32 // pin19 is a bitmask 32 on port C        
#define loopsPerSecond 2 // how many times will we try and loop in a second     

typedef void (* pFunc)(void);//type for display function pointers      

volatile unsigned long timer2_overflow_count;

/*** Set up the Events ***
We have our own ISR for timer2 which gets called about once a millisecond.
So we define certain event functions that we can schedule by calling addEvent
with the event ID and the number of milliseconds to wait before calling the event. 
The milliseconds is approximate.

Keep the event functions SMALL!!!  This is an interrupt!

*/
//event functions

void enableLButton(){PCMSK1 |= (1 << PCINT11);}
void enableMButton(){PCMSK1 |= (1 << PCINT12);}
void enableRButton(){PCMSK1 |= (1 << PCINT13);}
//array of the event functions
pFunc eventFuncs[] ={enableVSS, enableLButton,enableMButton,enableRButton};
#define eventFuncSize (sizeof(eventFuncs)/sizeof(pFunc)) 
//define the event IDs
#define enableVSSID 0
#define enableLButtonID 1
#define enableMButtonID 2
#define enableRButtonID 3
//ms counters
unsigned int eventFuncCounts[eventFuncSize];

//schedule an event to occur ms milliseconds from now
void addEvent(byte eventID, unsigned int ms){
  eventFuncCounts[eventID]=ms;
}

/* this ISR gets called every 1.024 milliseconds, we will call that a millisecond for our purposes
go through all the event counts, 
  if any are non zero subtract 1 and call the associated function if it just turned zero.  */
ISR(TIMER2_OVF_vect){
  timer2_overflow_count++;
  for(byte eventID = 0; eventID < eventFuncSize; eventID++){
    if(eventFuncCounts[eventID]!= 0){
      eventFuncCounts[eventID]--;
      if(eventFuncCounts[eventID] == 0)
          eventFuncs[eventID](); 
    }  
  }
}

 
 
unsigned long maxLoopLength = 0; //see if we are overutilizing the CPU      
 
 
#define buttonsUp   lbuttonBit + mbuttonBit + rbuttonBit  // start with the buttons in the right state      
byte buttonState = buttonsUp;      
 
 
//overflow counter used by millis2()      
unsigned long lastMicroSeconds=millis2() * 1000;   
unsigned long microSeconds (void){     
  unsigned long tmp_timer2_overflow_count;    
  unsigned long tmp;    
  byte tmp_tcnt2;    
  cli(); //disable interrupts    
  tmp_timer2_overflow_count = timer2_overflow_count;    
  tmp_tcnt2 = TCNT2;    
  sei(); // enable interrupts    
  tmp = ((tmp_timer2_overflow_count << 8) + tmp_tcnt2) * 4;     
  if((tmp<=lastMicroSeconds) && (lastMicroSeconds<4290560000ul))    
    return microSeconds();     
  lastMicroSeconds=tmp;   
  return tmp;     
}    
 
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds, unsigned long currentMicroseconds ){      
  if(currentMicroseconds >= startMicroSeconds)      
    return currentMicroseconds-startMicroSeconds;      
  return 4294967295 - (startMicroSeconds-currentMicroseconds);      
}      

unsigned long elapsedMicroseconds(unsigned long startMicroSeconds ){      
  return elapsedMicroseconds(startMicroSeconds, microSeconds());
}      
 
//Trip prototype      
class Trip{      
public:      
  unsigned long loopCount; //how long has this trip been running      
  unsigned long injPulses; //rpm      
  unsigned long injHiSec;// seconds the injector has been open      
  unsigned long injHius;// microseconds, fractional part of the injectors open       
  unsigned long vssPulses;//from the speedo      
  unsigned long vssPulseLength; // only used by instant
  //these functions actually return in thousandths,       
  unsigned long miles();        
  unsigned long gallons();      
  unsigned long mpg();        
  unsigned long mph();        
  unsigned long time(); //mmm.ss        
  void update(Trip t);      
  void reset();      
  Trip();      
};      
 
//LCD prototype      
namespace LCD{      
  void gotoXY(byte x, byte y);      
  void print(char * string);      
  void init();      
  void tickleEnable();      
  void cmdWriteSet();      
  void LcdCommandWrite(byte value);      
  void LcdDataWrite(byte value);      
  byte pushNibble(byte value);      
};      
 
 
//main objects we will be working with:      
unsigned long injHiStart; //for timing injector pulses      
Trip tmpTrip;      
Trip instant;      
Trip current;      
Trip tank;      


unsigned volatile long instInjStart=nil; 
unsigned volatile long tmpInstInjStart=nil; 
unsigned volatile long instInjEnd; 
unsigned volatile long tmpInstInjEnd; 
unsigned volatile long instInjTot; 
unsigned volatile long tmpInstInjTot;     
unsigned volatile long instInjCount; 
unsigned volatile long tmpInstInjCount;     


 
void processInjOpen(void){      
  injHiStart = microSeconds();  
  if (tmpInstInjStart == nil)
    tmpInstInjStart=injHiStart;
  tmpInstInjEnd=injHiStart;
}      
 
void processInjClosed(void){      
  long x = elapsedMicroseconds(injHiStart)- parms[injectorSettleTimeIdx];       
  if(x >0)
    tmpTrip.injHius += x;       
  tmpTrip.injPulses++;      

  if (tmpInstInjStart != nil){
    if(x >0)
      tmpInstInjTot += x;     
      tmpInstInjCount++;
  }  
}      

volatile boolean vssFlop = 0;

void enableVSS(){
//    tmpTrip.vssPulses++; 
    vssFlop = !vssFlop;
}

unsigned volatile long lastVSS1;
unsigned volatile long lastVSSTime;
unsigned volatile long lastVSS2;

volatile boolean lastVssFlop = vssFlop;

//attach the vss/buttons interrupt      
ISR( PCINT1_vect ){   
  static byte vsspinstate=0;      
  byte p = PINC;//bypassing digitalRead for interrupt performance      
  if ((p & vssBit) != (vsspinstate & vssBit)){      
    addEvent(enableVSSID,2 ); //check back in a couple milli
  }
  if(lastVssFlop != vssFlop){
    lastVSS1=lastVSS2;
    unsigned long t = microSeconds();
    lastVSS2=elapsedMicroseconds(lastVSSTime,t);
    lastVSSTime=t;
    tmpTrip.vssPulses++; 
    tmpTrip.vssPulseLength += lastVSS2;
    lastVssFlop = vssFlop;
  }
  vsspinstate = p;      
  buttonState &= p;      
}       
 
 
pFunc displayFuncs[] ={ 
  doDisplayCustom, 
  doDisplayInstantCurrent, 
  doDisplayInstantTank, 
#ifndef debuguino  //need to make room for Serial, so BigNums is out in debug mode
  doDisplayBigInstant, 
  doDisplayBigCurrent, 
  doDisplayBigTank, 
#endif  
  doDisplay2, 
  doDisplay3, 
  doDisplay4, 
  doDisplay5, 
  doDisplay6, 
  doDisplay7};      
#define displayFuncSize (sizeof(displayFuncs)/sizeof(pFunc)) //array size      
prog_char  * displayFuncNames[displayFuncSize]; 
byte newRun = 0;
void setup (void){
  init2();
  #ifdef debuguino  
  Serial.begin(9600);  
  Serial.println("OpenGauge MPGuino online");  
  #endif      
  newRun = load();//load the default parameters
  byte x = 0;
  displayFuncNames[x++]=  PSTR("Custom "); 
  displayFuncNames[x++]=  PSTR("Instant/Current "); 
  displayFuncNames[x++]=  PSTR("Instant/Tank "); 
#ifndef debuguino  
  displayFuncNames[x++]=  PSTR("BIG Instant "); 
  displayFuncNames[x++]=  PSTR("BIG Current "); 
  displayFuncNames[x++]=  PSTR("BIG Tank "); 
#endif
  displayFuncNames[x++]=  PSTR("Current "); 
  displayFuncNames[x++]=  PSTR("Tank "); 
  displayFuncNames[x++]=  PSTR("Instant raw Data"); 
  displayFuncNames[x++]=  PSTR("Current raw Data"); 
  displayFuncNames[x++]=  PSTR("Tank raw Data "); 
  displayFuncNames[x++]= PSTR("CPU Monitor ");      
 
  pinMode(BrightnessPin,OUTPUT);      
  analogWrite(BrightnessPin,255-brightness[brightnessIdx]);      
  pinMode(EnablePin,OUTPUT);       
  pinMode(DIPin,OUTPUT);       
  pinMode(DB4Pin,OUTPUT);       
  pinMode(DB5Pin,OUTPUT);       
  pinMode(DB6Pin,OUTPUT);       
  pinMode(DB7Pin,OUTPUT);       
  delay2(500);      
 
  pinMode(ContrastPin,OUTPUT);      
  analogWrite(ContrastPin,parms[contrastIdx]);  
  LCD::init();      
  LCD::LcdCommandWrite(B00000001);  // clear display, set cursor position to zero         
  LCD::LcdCommandWrite(B10000000);  // set dram to zero
  LCD::gotoXY(0,0); 
  LCD::print(getStr(PSTR("OpenGauge       ")));      
  LCD::gotoXY(0,1);      
  LCD::print(getStr(PSTR("  MPGuino  v0.71")));      

  pinMode(InjectorOpenPin, INPUT);       
  pinMode(InjectorClosedPin, INPUT);       
  pinMode(VSSPin, INPUT);            
  attachInterrupt(0, processInjOpen, FALLING);      
  attachInterrupt(1, processInjClosed, RISING);      
 
  pinMode( lbuttonPin, INPUT );       
  pinMode( mbuttonPin, INPUT );       
  pinMode( rbuttonPin, INPUT );      
 
 
  //"turn on" the internal pullup resistors      
  digitalWrite( lbuttonPin, HIGH);       
  digitalWrite( mbuttonPin, HIGH);       
  digitalWrite( rbuttonPin, HIGH);       
//  digitalWrite( VSSPin, HIGH);       
 
  //low level interrupt enable stuff      
  PCMSK1 |= (1 << PCINT8);
  enableLButton();
  enableMButton();
  enableRButton();
  PCICR |= (1 << PCIE1);       
 
  delay2(1500);       
}       
 
byte screen=0;      
byte holdDisplay = 0; 



#define looptime 1000000ul/loopsPerSecond //1/2 second      
void loop (void){       
  if(newRun !=1)
    initGuino();//go through the initialization screen
  unsigned long lastActivity =microSeconds();
  unsigned long tankHold;      //state at point of last activity
  while(true){      
    unsigned long loopStart=microSeconds();      
    instant.reset();           //clear instant      
    cli();
    instant.update(tmpTrip);   //"copy" of tmpTrip in instant now      
    tmpTrip.reset();           //reset tmpTrip first so we don't lose too many interrupts      
    instInjStart=tmpInstInjStart; 
    instInjEnd=tmpInstInjEnd; 
    instInjTot=tmpInstInjTot;     
    instInjCount=tmpInstInjCount;
    
    tmpInstInjStart=nil; 
    tmpInstInjEnd=nil; 
    tmpInstInjTot=0;     
    tmpInstInjCount=0;

    sei();
    #ifdef debuguino  
//    Serial.print("instant: ");Serial.print(instant.injHiSec);Serial.print(",");Serial.print(instant.injHius);  
//    Serial.print(",");Serial.print(instant.injPulses);Serial.print(",");Serial.println(instant.vssPulses);      
    #endif  
    current.update(instant);   //use instant to update current      
    tank.update(instant);      //use instant to update tank      
    #ifdef debuguino  
//    Serial.print("current: ");Serial.print(current.injHiSec);Serial.print(",");Serial.print(current.injHius);  
//    Serial.print(",");Serial.print(current.injPulses);Serial.print(",");Serial.println(current.vssPulses);      
    #endif  

//currentTripResetTimeoutUS
    if(instant.vssPulses == 0 && instant.injPulses == 0 && holdDisplay==0){
      if(elapsedMicroseconds(lastActivity) > parms[currentTripResetTimeoutUSIdx] && lastActivity != nil){
        analogWrite(BrightnessPin,255-brightness[0]);    //nitey night
        lastActivity = nil;
      }
    }else{
      if(lastActivity == nil){//wake up!!!
        analogWrite(BrightnessPin,255-brightness[brightnessIdx]);    
        lastActivity=loopStart;
        current.reset();
        tank.loopCount = tankHold;
        current.update(instant); 
        tank.update(instant); 
      }else{
        lastActivity=loopStart;
        tankHold = tank.loopCount;
      }
    }
    


 
 if(holdDisplay==0){
    displayFuncs[screen]();    //call the appropriate display routine      
    LCD::gotoXY(0,0);        
    
//see if any buttons were pressed, display a brief message if so      
      if(!(buttonState&lbuttonBit) && !(buttonState&mbuttonBit)&& !(buttonState&rbuttonBit)){// left and middle and right = initialize      
          LCD::print(getStr(PSTR("Setup ")));    
          initGuino();  
      //}else if(!(buttonState&lbuttonBit) && !(buttonState&rbuttonBit)){// left and right = run lcd init = tank reset      
      //    LCD::print(getStr(PSTR("Init LCD "))); 
      //    LCD::init();
      }else if (!(buttonState&lbuttonBit) && !(buttonState&mbuttonBit)){// left and middle = tank reset      
          tank.reset();      
          LCD::print(getStr(PSTR("Tank Reset ")));      
      }else if(!(buttonState&mbuttonBit) && !(buttonState&rbuttonBit)){// right and middle = current reset      
          current.reset();      
          LCD::print(getStr(PSTR("Current Reset ")));      
      }else if(!(buttonState&lbuttonBit)){ //left is rotate through screeens to the left      
        if(screen!=0)      
          screen=(screen-1);       
        else      
          screen=displayFuncSize-1;      
        LCD::print(getStr(displayFuncNames[screen]));      
      }else if(!(buttonState&mbuttonBit)){ //middle is cycle through brightness settings      
        brightnessIdx = (brightnessIdx + 1) % brightnessLength;      
        analogWrite(BrightnessPin,255-brightness[brightnessIdx]);      
        LCD::print(getStr(PSTR("Brightness ")));      
        LCD::LcdDataWrite('0' + brightnessIdx);      
        LCD::print(" ");      
      }else if(!(buttonState&rbuttonBit)){//right is rotate through screeens to the left      
        screen=(screen+1)%displayFuncSize;      
        LCD::print(getStr(displayFuncNames[screen]));      
      }      
      if(buttonState!=buttonsUp)
         holdDisplay=1;
     }else{
        holdDisplay=0;
    } 
    buttonState=buttonsUp;//reset the buttons      
 
      //keep track of how long the loops take before we go int waiting.      
      unsigned long loopX=elapsedMicroseconds(loopStart);      
      if(loopX>maxLoopLength) maxLoopLength = loopX;      
 
      while (elapsedMicroseconds(loopStart) < (looptime));//wait for the end of a second to arrive      
  }      
 
}       
 
 
char fBuff[7];//used by format      
//format a number into NNN.NN  the number should already be representing thousandths      
char* format(unsigned long num){      
  unsigned long d = 10000;      
  long t;      
  byte dp=3;      
  byte l=6;      
 
  //123456 = 123.46      
  if(num>9999999){      
    d=100000;      
    dp=99;      
    num/=100;      
  }else if(num>999999){      
    dp=4;      
    num/=10;      
  }      
 
  unsigned long val = num/10;      
  if ((num - (val * 10)) >= 5)  //will the first unprinted digit be greater than 4?      
    val += 1;   //round up val      
 
  for(byte x = 0; x < l; x++){      
    if(x==dp)      //time to poke in the decimal point?      
      fBuff[x]='.';      
    else{      
      t = val/d;        
      fBuff[x]= '0' + t%10;//poke the ascii character for the digit.      
      val-= t*d;      
      d/=10;            
    }      
  }      
  fBuff[6]= 0;         //good old zero terminated strings       
  return fBuff;      
}  
 
 
//get a string from flash 
char mBuff[17];//used by getStr 
char * getStr(prog_char * str){ 
  strcpy_P(mBuff, str); 
  return mBuff; 
} 

 

 
 
void doDisplayCustom(){displayTripCombo('I','M',instantmpg(),'S',instantmph(),'R','P',instantrpm(),'C',current.mpg());}      
void doDisplayInstantCurrent(){displayTripCombo('I','M',instantmpg(),'S',instantmph(),'C','M',current.mpg(),'D',current.miles());}      
 
void doDisplayInstantTank(){displayTripCombo('I','M',instantmpg(),'S',instantmph(),'T','M',tank.mpg(),'D',tank.miles());}      

#ifndef debuguino  
void doDisplayBigInstant() {bigNum(instant.mpg(),"INST","MPG ");}      
void doDisplayBigCurrent() {bigNum(current.mpg(),"CURR","MPG ");}      
void doDisplayBigTank()    {bigNum(tank.mpg(),"TANK","MPG ");}      
#endif 
 
void doDisplay2(void){tDisplay(&current);}   //display current trip formatted data.        
void doDisplay3(void){tDisplay(&tank);}      //display tank trip formatted data.        
void doDisplay4(void){rawDisplay(&instant);} //display instant trip "raw" injector and vss data.        
void doDisplay5(void){rawDisplay(&current);} //display current trip "raw" injector and vss data.        
void doDisplay6(void){rawDisplay(&tank);}    //display tank trip "raw" injector and vss data.        
void doDisplay7(void){      
  LCD::gotoXY(0,0);LCD::print("C%");LCD::print(format(maxLoopLength*1000/(looptime/100)));LCD::print(" T"); LCD::print(format(tank.time()));     
  unsigned long mem = memoryTest();      
  mem*=1000;      
  LCD::gotoXY(0,1);LCD::print("FREE MEM: ");LCD::print(format(mem));      
}    //display max cpu utilization and ram.        
 
void displayTripCombo(char t1, char t1L1, unsigned long t1V1, char t1L2, unsigned long t1V2,  char t2, char t2L1, unsigned long t2V1, char t2L2, unsigned long t2V2){ 
  LCD::gotoXY(0,0);LCD::LcdDataWrite(t1);LCD::LcdDataWrite(t1L1);LCD::print(format(t1V1));LCD::LcdDataWrite(' '); 
      LCD::LcdDataWrite(t1L2);LCD::print(format(t1V2)); 
  LCD::gotoXY(0,1);LCD::LcdDataWrite(t2);LCD::LcdDataWrite(t2L1);LCD::print(format(t2V1));LCD::LcdDataWrite(' '); 
      LCD::LcdDataWrite(t2L2);LCD::print(format(t2V2)); 
}      
 
//arduino doesn't do well with types defined in a script as parameters, so have to pass as void * and use -> notation.      
void tDisplay( void * r){ //display trip functions.        
  Trip *t = (Trip *)r;      
  LCD::gotoXY(0,0);LCD::print("MH");LCD::print(format(t->mph()));LCD::print("MG");LCD::print(format(t->mpg()));      
  LCD::gotoXY(0,1);LCD::print("MI");LCD::print(format(t->miles()));LCD::print("GA");LCD::print(format(t->gallons()));      
}      
 
void rawDisplay(void * r){      
  Trip *t = (Trip *)r;      
  LCD::gotoXY(0,0);LCD::print("IJ");LCD::print(format(t->injHiSec*1000));LCD::print("uS");LCD::print(format(t->injHius*1000));      
  LCD::gotoXY(0,1);LCD::print("IC");LCD::print(format(t->injPulses*1000));LCD::print("VC");LCD::print(format(t->vssPulses*1000));      
}      
 
 
    
//x=0..16, y= 0..1      
void LCD::gotoXY(byte x, byte y){      
  byte dr=x+0x80;      
  if (y==1)       
    dr += 0x40;      
  if (y==2)       
    dr += 0x14;      
  if (y==3)       
    dr += 0x54;      
  LCD::LcdCommandWrite(dr);        
}      
 
void LCD::print(char * string){      
  byte x = 0;      
  char c = string[x];      
  while(c != 0){      
    LCD::LcdDataWrite(c);       
    x++;      
    c = string[x];      
  }      
}      
 
 
void LCD::init(){
  delay2(16);                    // wait for more than 15 msec
  pushNibble(B00110000);  // send (B0011) to DB7-4
  cmdWriteSet();
  tickleEnable();
  delay2(5);                     // wait for more than 4.1 msec
  pushNibble(B00110000);  // send (B0011) to DB7-4
  cmdWriteSet();
  tickleEnable();
  delay2(1);                     // wait for more than 100 usec
  pushNibble(B00110000);  // send (B0011) to DB7-4
  cmdWriteSet();
  tickleEnable();
  delay2(1);                     // wait for more than 100 usec
  pushNibble(B00100000);  // send (B0010) to DB7-4 for 4bit
  cmdWriteSet();
  tickleEnable();
  delay2(1);                     // wait for more than 100 usec
  // ready to use normal LcdCommandWrite() function now!
  LcdCommandWrite(B00101000);   // 4-bit interface, 2 display lines, 5x8 font
  LcdCommandWrite(B00001100);   // display control:
  LcdCommandWrite(B00000110);   // entry mode set: increment automatically, no display shift
#ifndef debuguino 
//creating the custom fonts:
  LcdCommandWrite(B01001000);  // set cgram
  static byte chars[] PROGMEM ={
    B11111,B00000,B11111,B11111,B00000,
    B11111,B00000,B11111,B11111,B00000,
    B11111,B00000,B11111,B11111,B00000,
    B00000,B00000,B00000,B11111,B00000,
    B00000,B00000,B00000,B11111,B00000,
    B00000,B11111,B11111,B11111,B01110,
    B00000,B11111,B11111,B11111,B01110,
    B00000,B11111,B11111,B11111,B01110};

    for(byte x=0;x<5;x++)
      for(byte y=0;y<8;y++)
          LcdDataWrite(pgm_read_byte(&chars[y*5+x])); //write the character data to the character generator ram
#endif
  LcdCommandWrite(B00000001);  // clear display, set cursor position to zero
  LcdCommandWrite(B10000000);  // set dram to zero

}

void  LCD::tickleEnable(){       
  // send a pulse to enable       
  digitalWrite(EnablePin,HIGH);       
  delayMicroseconds2(1);  // pause 1 ms according to datasheet       
  digitalWrite(EnablePin,LOW);       
  delayMicroseconds2(1);  // pause 1 ms according to datasheet       
}        
 
void LCD::cmdWriteSet(){       
  digitalWrite(EnablePin,LOW);       
  delayMicroseconds2(1);  // pause 1 ms according to datasheet       
  digitalWrite(DIPin,0);       
}       
 
byte LCD::pushNibble(byte value){       
  digitalWrite(DB7Pin, value & 128);       
  value <<= 1;       
  digitalWrite(DB6Pin, value & 128);       
  value <<= 1;       
  digitalWrite(DB5Pin, value & 128);       
  value <<= 1;       
  digitalWrite(DB4Pin, value & 128);       
  value <<= 1;       
  return value;      
}      
 
void LCD::LcdCommandWrite(byte value){       
  value=pushNibble(value);      
  cmdWriteSet();       
  tickleEnable();       
  value=pushNibble(value);      
  cmdWriteSet();       
  tickleEnable();       
  delay2(5);       
}       
 
void LCD::LcdDataWrite(byte value){       
  digitalWrite(DIPin, HIGH);       
  value=pushNibble(value);      
  tickleEnable();       
  value=pushNibble(value);      
  tickleEnable();       
  delay2(5);       
}       
 
 
 
// this function will return the number of bytes currently free in RAM      
extern int  __bss_end; 
extern int  *__brkval; 
int memoryTest(){ 
  int free_memory; 
  if((int)__brkval == 0) 
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  else 
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  return free_memory; 
} 
 
 
Trip::Trip(){      
}      
 
//for display computing
unsigned long tmp1[2];
unsigned long tmp2[2];
unsigned long tmp3[2];

unsigned long instantmph(){      
  //unsigned long vssPulseTimeuS = (lastVSS1 + lastVSS2) / 2;
  unsigned long vssPulseTimeuS = instant.vssPulseLength/instant.vssPulses;
  
  init64(tmp1,0,1000000000ul);
  init64(tmp2,0,parms[vssPulsesPerMileIdx]);
  div64(tmp1,tmp2);
  init64(tmp2,0,3600);
  mul64(tmp1,tmp2);
  init64(tmp2,0,vssPulseTimeuS);
  div64(tmp1,tmp2);
  return tmp1[1];
}

unsigned long instantmpg(){     
  unsigned long imph=instantmph();
  unsigned long igph=instantgph();
  init64(tmp1,0,1000ul);
  init64(tmp2,0,imph);
  mul64(tmp1,tmp2);
  init64(tmp2,0,igph);
  div64(tmp1,tmp2);
  return tmp1[1];
}


unsigned long instantgph(){      
//  unsigned long vssPulseTimeuS = instant.vssPulseLength/instant.vssPulses;
  
//  unsigned long instInjStart=nil; 
//unsigned long instInjEnd; 
//unsigned long instInjTot; 
  init64(tmp1,0,instInjTot);
  init64(tmp2,0,3600000000ul);
  mul64(tmp1,tmp2);
  init64(tmp2,0,1000ul);
  mul64(tmp1,tmp2);
  init64(tmp2,0,parms[microSecondsPerGallonIdx]);
  div64(tmp1,tmp2);
  init64(tmp2,0,instInjEnd-instInjStart);
  div64(tmp1,tmp2);
  return tmp1[1];      
}

unsigned long instantrpm(){      
//    instInjCount=tmpInstInjCount;

  //  unsigned long vssPulseTimeuS = instant.vssPulseLength/instant.vssPulses;
  
//  unsigned long instInjStart=nil; 
//unsigned long instInjEnd; 
//unsigned long instInjTot; 
  init64(tmp1,0,instInjCount);
  init64(tmp2,0,120000000ul);
  mul64(tmp1,tmp2);
  init64(tmp2,0,1000ul);
  mul64(tmp1,tmp2);
  init64(tmp2,0,parms[injPulsesPer2Revolutions]);
  div64(tmp1,tmp2);
  init64(tmp2,0,instInjEnd-instInjStart);
  div64(tmp1,tmp2);
  return tmp1[1];      
}





unsigned long Trip::miles(){      
  init64(tmp1,0,vssPulses);
  init64(tmp2,0,1000);
  mul64(tmp1,tmp2);
  init64(tmp2,0,parms[vssPulsesPerMileIdx]);
  div64(tmp1,tmp2);
  return tmp1[1];      
}      
 
unsigned long Trip::mph(){      
  if(loopCount == 0)     
     return 0;     
  init64(tmp1,0,loopsPerSecond);
  init64(tmp2,0,vssPulses);
  mul64(tmp1,tmp2);
  init64(tmp2,0,3600000);
  mul64(tmp1,tmp2);
  init64(tmp2,0,parms[vssPulsesPerMileIdx]);
  div64(tmp1,tmp2);
  init64(tmp2,0,loopCount);
  div64(tmp1,tmp2);
  return tmp1[1];      
}      
 
unsigned long  Trip::gallons(){      
  init64(tmp1,0,injHiSec);
  init64(tmp2,0,1000000);
  mul64(tmp1,tmp2);
  init64(tmp2,0,injHius);
  add64(tmp1,tmp2);
  init64(tmp2,0,1000);
  mul64(tmp1,tmp2);
  init64(tmp2,0,parms[microSecondsPerGallonIdx]);
  div64(tmp1,tmp2);
  return tmp1[1];      
}      
 
unsigned long  Trip::mpg(){      
  if(vssPulses==0) return 0;      
  if(injPulses==0) return 999999000; //who doesn't like to see 999999?  :)      
 
  init64(tmp1,0,injHiSec);
  init64(tmp3,0,1000000);
  mul64(tmp3,tmp1);
  init64(tmp1,0,injHius);
  add64(tmp3,tmp1);
  init64(tmp1,0,parms[vssPulsesPerMileIdx]);
  mul64(tmp3,tmp1);
 
  init64(tmp1,0,parms[microSecondsPerGallonIdx]);
  init64(tmp2,0,1000);
  mul64(tmp1,tmp2);
  init64(tmp2,0,vssPulses);
  mul64(tmp1,tmp2);
 
  div64(tmp1,tmp3);
  return tmp1[1];      
}      
 
//return the seconds as a time mmm.ss, eventually hhh:mm too      
unsigned long Trip::time(){      
//  return seconds*1000;      
  byte d = 60;      
  unsigned long seconds = loopCount/loopsPerSecond;     
//  if(seconds/60 > 999) d = 3600; //scale up to hours.minutes if we get past 999 minutes      
  return ((seconds/d)*1000) + ((seconds%d) * 10);       
}      
 
 
void Trip::reset(){      
  loopCount=0;      
  injPulses=0;      
  injHius=0;      
  injHiSec=0;      
  vssPulses=0;  
  vssPulseLength=0;
}      
 
void Trip::update(Trip t){     
  loopCount++;  //we call update once per loop     
  vssPulses+=t.vssPulses;      
  vssPulseLength+=t.vssPulseLength;
  if(t.injPulses > 2 && t.injHius<500000){//chasing ghosts      
    injPulses+=t.injPulses;      
    injHius+=t.injHius;      
    if (injHius>=1000000){  //rollover into the injHiSec counter      
      injHiSec++;      
      injHius-=1000000;      
    }      
  }      
}   
 
 
 
#ifndef debuguino  
char bignumchars1[]={4,1,4,0, 1,4,32,0, 3,3,4,0, 1,3,4,0, 4,2,4,0,   4,3,3,0, 4,3,3,0, 1,1,4,0,   4,3,4,0, 4,3,4,0}; 
char bignumchars2[]={4,2,4,0, 2,4,2,0,  4,2,2,0, 2,2,4,0, 32,32,4,0, 2,2,4,0, 4,2,4,0, 32,4,32,0, 4,2,4,0, 2,2,4,0};  
 
void bigNum (unsigned long t, char * txt1, char * txt2){      
//  unsigned long t = 98550ul;//number in thousandths 
//  unsigned long t = 9855ul;//number in thousandths 
//  char * txt1="INST"; 
//  char * txt2="MPG "; 
  char  dp = 32; 
 
  char * r = "009.99"; //"009.99" "000.99" "000.09" 
  if(t<=99500){ 
    r=format(t/10); //009.86 
    dp=5; 
  }else if(t<=999500){ 
    r=format(t/100); //009.86 
  }   
 
  LCD::gotoXY(0,0); 
  LCD::print(bignumchars1+(r[2]-'0')*4); 
  LCD::print(" "); 
  LCD::print(bignumchars1+(r[4]-'0')*4); 
  LCD::print(" "); 
  LCD::print(bignumchars1+(r[5]-'0')*4); 
  LCD::print(" "); 
  LCD::print(txt1); 
 
  LCD::gotoXY(0,1); 
  LCD::print(bignumchars2+(r[2]-'0')*4); 
  LCD::print(" "); 
  LCD::print(bignumchars2+(r[4]-'0')*4); 
  LCD::LcdDataWrite(dp); 
  LCD::print(bignumchars2+(r[5]-'0')*4); 
  LCD::print(" "); 
  LCD::print(txt2); 
}      
#endif 
//the standard 64 bit math brings in  5000+ bytes
//these bring in 1214 bytes, and everything is pass by reference
unsigned long zero64[]={0,0};
 
void init64(unsigned long  an[], unsigned long bigPart, unsigned long littlePart ){
  an[0]=bigPart;
  an[1]=littlePart;
}
 
//left shift 64 bit "number"
void shl64(unsigned long  an[]){
 an[0] <<= 1; 
 if(an[1] & 0x80000000)
   an[0]++; 
 an[1] <<= 1; 
}
 
//right shift 64 bit "number"
void shr64(unsigned long  an[]){
 an[1] >>= 1; 
 if(an[0] & 0x1)
   an[1]+=0x80000000; 
 an[0] >>= 1; 
}
 
//add ann to an
void add64(unsigned long  an[], unsigned long  ann[]){
  an[0]+=ann[0];
  if(an[1] + ann[1] < ann[1])
    an[0]++;
  an[1]+=ann[1];
}
 
//subtract ann from an
void sub64(unsigned long  an[], unsigned long  ann[]){
  an[0]-=ann[0];
  if(an[1] < ann[1]){
    an[0]--;
  }
  an[1]-= ann[1];
}
 
//true if an == ann
boolean eq64(unsigned long  an[], unsigned long  ann[]){
  return (an[0]==ann[0]) && (an[1]==ann[1]);
}
 
//true if an < ann
boolean lt64(unsigned long  an[], unsigned long  ann[]){
  if(an[0]>ann[0]) return false;
  return (an[0]<ann[0]) || (an[1]<ann[1]);
}
 
//divide num by den
void div64(unsigned long num[], unsigned long den[]){
  unsigned long quot[2];
  unsigned long qbit[2];
  unsigned long tmp[2];
  init64(quot,0,0);
  init64(qbit,0,1);
 
  if (eq64(num, zero64)) {  //numerator 0, call it 0
    init64(num,0,0);
    return;        
  }
 
  if (eq64(den, zero64)) { //numerator not zero, denominator 0, infinity in my book.
    init64(num,0xffffffff,0xffffffff);
    return;        
  }
 
  init64(tmp,0x80000000,0);
  while(lt64(den,tmp)){
    shl64(den);
    shl64(qbit);
  } 
 
  while(!eq64(qbit,zero64)){
    if(lt64(den,num) || eq64(den,num)){
      sub64(num,den);
      add64(quot,qbit);
    }
    shr64(den);
    shr64(qbit);
  }
 
  //remainder now in num, but using it to return quotient for now  
  init64(num,quot[0],quot[1]); 
}
 
 
//multiply num by den
void mul64(unsigned long an[], unsigned long ann[]){
  unsigned long p[2] = {0,0};
  unsigned long y[2] = {ann[0], ann[1]};
  while(!eq64(y,zero64)) {
    if(y[1] & 1) 
      add64(p,an);
    shl64(an);
    shr64(y);
  }
  init64(an,p[0],p[1]);
} 
  
void save(){
  EEPROM.write(0,guinosig);
  byte p = 0;
  for(int x=4; p < parmsLength; x+= 4){
    unsigned long v = parms[p];
    EEPROM.write(x ,(v>>24)&255);
    EEPROM.write(x + 1,(v>>16)&255);
    EEPROM.write(x + 2,(v>>8)&255);
    EEPROM.write(x + 3,(v)&255);
    p++;
  }
}

byte load(){ //return 1 if loaded ok
  byte b = EEPROM.read(0);
  if(b == guinosig){
    byte p = 0;

    for(int x=4; p < parmsLength; x+= 4){
      unsigned long v = EEPROM.read(x);
      v = (v << 8) + EEPROM.read(x+1);
      v = (v << 8) + EEPROM.read(x+2);
      v = (v << 8) + EEPROM.read(x+3);
      parms[p]=v;
      p++;
    }
    return 1;
  }
  return 0;
}


char * uformat(unsigned long val){ 
  unsigned long d = 1000000000ul;
  for(byte p = 0; p < 10 ; p++){
    mBuff[p]='0' + (val/d);
    val=val-(val/d*d);
    d/=10;
  }
  mBuff[10]=0;
  return mBuff;
} 

unsigned long rformat(char * val){ 
  unsigned long d = 1000000000ul;
  unsigned long v = 0ul;
  for(byte p = 0; p < 10 ; p++){
    v=v+(d*(val[p]-'0'));
    d/=10;
  }
  return v;
} 


#ifndef debuguino
void editParm(byte parmIdx){
  unsigned long v = parms[parmIdx];
  byte p=9;  //right end of 10 digit number
  //display label on top line
  //set cursor visible
  //set pos = 0
  //display v

    LCD::gotoXY(8,0);        
    LCD::print("        ");
    LCD::gotoXY(0,0);        
    LCD::print(parmLabels[parmIdx]);
    LCD::gotoXY(0,1);    
    char * fmtv=    uformat(v);
    LCD::print(fmtv);
    LCD::print(" OK XX");
    LCD::LcdCommandWrite(B00001110);

    for(int x=9 ; x>=0 ;x--){ //do a nice thing and put the cursor at the first non zero number
      if(fmtv[x] != '0')
         p=x; 
    }
  byte keyLock=1;    
  while(true){

    if(p<10)
      LCD::gotoXY(p,1);   
    if(p==10)     
      LCD::gotoXY(11,1);   
    if(p==11)     
      LCD::gotoXY(14,1);   

     if(keyLock == 0){ 
       if(!(buttonState&lbuttonBit)){// left
            p=p-1;
            if(p==255)p=11;
        }else if(!(buttonState&rbuttonBit)){// right
             p=p+1;
            if(p==12)p=0;
        }else if(!(buttonState&mbuttonBit)){// middle
             if(p==11){  //cancel selected
                LCD::LcdCommandWrite(B00001100);
                return;
             }
             if(p==10){  //ok selected
                LCD::LcdCommandWrite(B00001100);
                parms[parmIdx]=rformat(fmtv);
                return;
             }
             
             byte n = fmtv[p]-'0';
             n++;
             if (n > 9) n=0;
             if(p==0 && n > 3) n=0;
             fmtv[p]='0'+ n;
             LCD::gotoXY(0,1);        
             LCD::print(fmtv);
             LCD::gotoXY(p,1);        
             if(parmIdx==contrastIdx)//adjust contrast dynamically
                 analogWrite(ContrastPin,rformat(fmtv));  


        }

      if(buttonState!=buttonsUp)
         keyLock=1;
     }else{
        keyLock=0;
     }
      buttonState=buttonsUp;
      delay2(125);
  }      
  
}
#endif

void initGuino(){ //edit all the parameters
#ifndef debuguino
  for(int x = 0;x<parmsLength;x++)
    editParm(x);
  save();
  holdDisplay=1;
#endif
}  

unsigned long millis2(){
	return timer2_overflow_count * 64UL * 2 / (16000000UL / 128000UL);
}

void delay2(unsigned long ms){
	unsigned long start = millis2();
	while (millis2() - start < ms);
}

/* Delay for the given number of microseconds.  Assumes a 16 MHz clock. 
 * Disables interrupts, which will disrupt the millis2() function if used
 * too frequently. */
void delayMicroseconds2(unsigned int us){
	uint8_t oldSREG;
	if (--us == 0)	return;
	us <<= 2;
	us -= 2;
	oldSREG = SREG;
	cli();
	// busy wait
	__asm__ __volatile__ (
		"1: sbiw %0,1" "\n\t" // 2 cycles
		"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
	);
	// reenable interrupts.
	SREG = oldSREG;
}

void init2(){
	// this needs to be called before setup() or some functions won't
	// work there
	sei();
	
	// timer 0 is used for millis2() and delay2()
	timer2_overflow_count = 0;
	// on the ATmega168, timer 0 is also used for fast hardware pwm
	// (using phase-correct PWM would mean that timer 0 overflowed half as often
	// resulting in different millis2() behavior on the ATmega8 and ATmega168)
        TCCR2A=1<<WGM20|1<<WGM21;
	// set timer 2 prescale factor to 64
        TCCR2B=1<<CS22;


//      TCCR2A=TCCR0A;
//      TCCR2B=TCCR0B;
	// enable timer 2 overflow interrupt
	TIMSK2|=1<<TOIE2;
	// disable timer 0 overflow interrupt
	TIMSK0&=!(1<<TOIE0);
}


Welcome to Combustory


Any questions or comments:

  • Send them to - combustor@combustory.com
Personal tools
Sponsers
Your Ad Here
Your Ad Here