View Single Post
Old 04-18-2011, 03:53 AM   #23 (permalink)
FalconFour
EcoModding Lurker
 
FalconFour's Avatar
 
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78

LEAF - '11 Nissan LEAF
Thanks: 4
Thanked 9 Times in 7 Posts
As promised, progress is being made on the code side! I've managed to re-implement the core of MPGuino - monitoring and looping - with a fully restructured block of code. It's been a ton of fun going through each line and cleaning up the syntax and combining/sorting declarations and definitions...

Code:
// mpguino release two
// implemented in native Arduino code

// using Arduino libraries here
#include <EEPROM.h>
#include <LiquidCrystal.h>
// ... mostly.
#include <avr/interrupt.h>

#define loopsPerSecond 2 // how many times will we try and loop in a second

//use with 20mhz (mpguino board)
//#define cyclesperhour		4500
//#define dispadj			800
//#define dispadj2			1250
//#define looptime			1250000ul/loopsPerSecond // time is skewed on 20mhz by 125%, so 1 sec = 1.25 perceived seconds (1,250,000 uSec)
//#define myubbr			(20000000/16/9600-1)
//#define injhold			(parms[injectorSettleTimeIdx]*5)/4

//use with 16mhz (arduino board)
#define cyclesperhour	3600
#define dispadj			1000
#define dispadj2		1250
#define looptime		1000000ul/loopsPerSecond // 1 sec divided by loopsPerSecond
#define myubbr			(16000000/16/9600-1)
#define injhold			parms[injectorSettleTimeIdx]

#define contrastIdx						0
#define vssPulsesPerMileIdx				1
#define microSecondsPerGallonIdx		2
#define injPulsesPer2Revolutions		3
#define currentTripResetTimeoutUSIdx	4
#define tankSizeIdx						5
#define injectorSettleTimeIdx			6
#define weightIdx						7
#define scratchpadIdx					8
#define vsspauseIdx						9
#define injEdgeIdx						10
#define nil			3999999999ul
#define guinosigold	0b10100101
#define guinosig	0b11100111
#define vssBit		0b00000001
#define lbuttonBit	0b00001000
#define mbuttonBit	0b00010000
#define rbuttonBit	0b00100000
#define buttonsUp   lbuttonBit + mbuttonBit + rbuttonBit
#define contrastPin		6
#define brightnessPin	9
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

typedef void (* pFunc)(void); // how we refer to function pointers
typedef uint8_t boolean;
typedef uint8_t byte;

unsigned long microSeconds(void);
unsigned long millis2(void);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds, unsigned long currentMicroseconds);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds);
unsigned long injHiStart;
unsigned long thisLoop, longestLoop, totalLoops;
volatile unsigned long timer2_overflow_count;
volatile unsigned long instInjStart = nil;
volatile unsigned long tmpInstInjStart = nil;
volatile unsigned long instInjEnd;
volatile unsigned long tmpInstInjEnd;
volatile unsigned long instInjTot;
volatile unsigned long tmpInstInjTot;
volatile unsigned long instInjCount;
volatile unsigned long tmpInstInjCount;
volatile unsigned long lastVSS1;
volatile unsigned long lastVSSTime;
volatile unsigned long lastVSS2;

volatile boolean vssFlop;
volatile boolean lastVssFlop = vssFlop;

volatile static pFunc int0Func, int1Func;

byte buttonState = buttonsUp;
byte newRun;
byte load();

void processInjOpen(void);
void processInjClosed(void);
void initGuino();
void save();

unsigned long parms[] = { 
	55ul, 4000ul,251500000ul, 3ul, 420000000ul, 10300ul,
	500ul, 2400ul, 0ul, 2ul, 0ul};//default values
char * parmLabels[] = { 
	"Contrast", "VSS Pulses/Mile", "MicroSec/Gallon",
	"Pulses/2 revs", "Timout(microSec)", "Tank Gal * 1000",
	"Injector DelayuS", "Weight (lbs)", "Scratchpad(odo?)", "VSS Delay ms",
	"InjTrg 0-Dn 1-Up" };
#define parmsLength (sizeof(parms)/sizeof(unsigned long))
#define distancefactor parms[vssPulsesPerMileIdx];
#define fuelfactor parms[microSecondsPerGallonIdx];
unsigned long injectorSettleTime = injhold;

//event functions
void enableLButton() { PCMSK1 |= (1 << PCINT11); }
void enableMButton() { PCMSK1 |= (1 << PCINT12); }
void enableRButton() { PCMSK1 |= (1 << PCINT13); }
void enableVSS() { vssFlop = !vssFlop; } // tmpTrip.vssPulses++;

//array of the event functions
pFunc eventFuncs[] = { 
	enableVSS, enableLButton, enableMButton, enableRButton };
#define eventFuncSize (sizeof(eventFuncs)/sizeof(pFunc))
#define enableVSSID		0
#define enableLButtonID	1
#define enableMButtonID	2
#define enableRButtonID	3
// msec counters for event triggers
unsigned int eventFuncCounts[eventFuncSize];

byte brightness[] = { 0, 41, 84, 128 };
#define brightnessLength (sizeof(brightness)/sizeof(byte))
byte brightnessIdx = 1;

// structures
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 injIdleHiSec;// seconds the injector has been open
	unsigned long injIdleHius;// microseconds, fractional part of the injectors open
	unsigned long vssPulses;//from the speedo
	unsigned long vssEOCPulses;//from the speedo
	unsigned long vssPulseLength; // only used by instant
	//these functions actually return in thousandths,
	unsigned long miles();
	unsigned long gallons();
	// ** F4 edit: metric
	//	unsigned long lkm();
	unsigned long mpg();
	unsigned long mph();
	unsigned long time(); //mmm.ss
	unsigned long eocMiles(); //how many "free" miles?
	unsigned long idleGallons(); //how many gallons spent at 0 mph?
	void update(Trip t);
	void reset();
	Trip();
};
Trip tmpTrip;
Trip instant;
Trip current;
Trip tank;

// cheap hack?
Trip::Trip() {
}

LiquidCrystal lcd(4,5,7,8,12,13);

// utility functions
//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;
	//disable interrupts
	cli();
	tmp_timer2_overflow_count = timer2_overflow_count;
	tmp_tcnt2 = TCNT2;
	// enable interrupts
	sei();
	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());
}
unsigned long millis2() {
	return timer2_overflow_count * 64UL * 2 / (16000000UL / 128000UL);
}
void delay2(unsigned long ms) {
	unsigned long start = millis2();
	while (millis2() - start < ms)
	;
}
//schedule an event to occur ms milliseconds from now
void addEvent(byte eventID, unsigned int ms) {
  if (ms == 0)
    eventFuncs[eventID]();
  else
    eventFuncCounts[eventID] = ms;
}
byte load() {
#ifdef usedefaults
  return 1;
#endif
  byte b = EEPROM.read(0);
  byte c = EEPROM.read(1);
  // upgrayedd yer parms plzkthx
  if (b == guinosig) {
    byte p = 0;
    for (int x = 4; p < c; 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;
}
void save() {
  EEPROM.write(0, guinosig);
	EEPROM.write(1, parmsLength);
  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++;
  }
}

// interrupts!
/* 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]();
		}
	}
}

ISR(INT0_vect) { int0Func(); } //processInjOpen by default
ISR(INT1_vect) { int1Func(); } //processInjClosed
//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, parms[vsspauseIdx]); //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;
}

void processInjOpen(void) { injHiStart = microSeconds(); }
void processInjClosed(void) {
	long t = microSeconds();
	long x = elapsedMicroseconds(injHiStart, t) - injectorSettleTime;
	if (x > 0) tmpTrip.injHius += x;
	tmpTrip.injPulses++;
	if (tmpInstInjStart != nil) {
		if (x > 0) tmpInstInjTot += x;
		tmpInstInjCount++;
	} else tmpInstInjStart = t;
	tmpInstInjEnd = t;
}

void setup() {
	sei();
	sbi(TCCR0A, WGM01);
	sbi(TCCR0A, WGM00);
	sbi(TCCR0B, CS01);
	sbi(TCCR0B, CS00);
	sbi(TIMSK0, TOIE0);

	// set timer 1 prescale factor to 64
	sbi(TCCR1B, CS11);
	sbi(TCCR1B, CS10);
	// put timer 1 in 8-bit phase correct pwm mode
	sbi(TCCR1A, WGM10);
	// set timer 2 prescale factor to 64
	sbi(TCCR2B, CS22);
	// configure timer 2 for phase correct pwm (8-bit)
	sbi(TCCR2A, WGM20);

	// set a2d prescale factor to 128
	sbi(ADCSRA, ADPS2);
	sbi(ADCSRA, ADPS1);
	sbi(ADCSRA, ADPS0);

	// enable a2d conversions
	sbi(ADCSRA, ADEN);

	UCSR0B = 0;

	sei();

	timer2_overflow_count = 0;

	TCCR2A = 1 << WGM20 | 1 << WGM21;
	// set timer 2 prescale factor to 64
	TCCR2B = 1 << CS22;
	TIMSK2 |= 1 << TOIE2;
	TIMSK0 &= !(1 << TOIE0);

	//load the default parameters
	newRun = load();
	// start up the LCD and announce we're here
	analogWrite(brightnessPin,brightness[brightnessIdx]);
	analogWrite(contrastPin,parms[contrastIdx]);
	lcd.begin(16,2);
	lcd.cursor();
	lcd.blink();
	
	lcd.setCursor(0, 0);
	lcd.print("FalconFour's    ");
	lcd.setCursor(0, 1);
	lcd.print("  MPGuino  v2.00");
	int0Func = processInjOpen;
	int1Func = processInjClosed;

	//set up the external interrupts
	// (a mouthful!)
	EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | ((parms[injEdgeIdx] == 1 ? RISING : FALLING) << ISC00);
	EIMSK |= (1 << INT0);
	EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | ((parms[injEdgeIdx] == 1 ? FALLING : RISING) << ISC10);
	EIMSK |= (1 << INT1);
	PORTC |= (1 << 5) | (1 << 4) | (1 << 3); //button pullup resistors

	//low level interrupt enable stuff
	PCMSK1 |= (1 << PCINT8);
	enableLButton();
	enableMButton();
	enableRButton();
	PCICR |= (1 << PCIE1);

	delay2(1500);
}

void loop() {
	unsigned long lastActivity = microSeconds();
	unsigned long tankHold;
	while (true) {
		// loop gets called every looptime microsec
		unsigned long loopStart = microSeconds();
		totalLoops++;
		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
		// copy tmp to current
		instInjStart = tmpInstInjStart;
		instInjEnd = tmpInstInjEnd;
		instInjTot = tmpInstInjTot;
		instInjCount = tmpInstInjCount;
		// and reset tmp values
		tmpInstInjStart = nil;
		tmpInstInjEnd = nil;
		tmpInstInjTot = 0;
		tmpInstInjCount = 0;
		sei();
		
		current.update(instant); //use instant to update current
		tank.update(instant); //use instant to update tank
		lcd.setCursor(0,0);
		lcd.print("This loop: ");
		lcd.print(totalLoops, DEC);
		//keep track of how long the loops take before we go int waiting.
		thisLoop = elapsedMicroseconds(loopStart);
		lcd.setCursor(0,1);
		lcd.print("msec: ");
		lcd.print(thisLoop,DEC);
		if (thisLoop > longestLoop) longestLoop = thisLoop;
		//wait for the end of a second to arrive
		while (elapsedMicroseconds(loopStart) < looptime)
		;
	}
}

// trip class functions
void Trip::reset() {
  loopCount = 0;
  injPulses = 0;
  injHius = 0;
  injHiSec = 0;
  vssPulses = 0;
  vssPulseLength = 0;
  injIdleHiSec = 0;
  injIdleHius = 0;
  vssEOCPulses = 0;
}

void Trip::update(Trip t) {
  loopCount++; //we call update once per loop
  vssPulses += t.vssPulses;
  vssPulseLength += t.vssPulseLength;
  if (t.injPulses == 0) //track distance traveled with engine off
    vssEOCPulses += t.vssPulses;

  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;
    }
    if (t.vssPulses == 0) { //track gallons spent sitting still

      injIdleHius += t.injHius;
      if (injIdleHius >= 1000000) { //r
        injIdleHiSec++;
        injIdleHius -= 1000000;
      }
    }
  }
}
NOT WORKING CODE - DO NOT USE - JUST SAY "Ooh shiny!" AND REVERT TO STABLE CODE!

Right now it does nothing at all... it monitors "in memory" and displays a loop count on the LCD (as wired in the MPGuino device). No button inputs, no math, no calculations... but those are all part of the UI that's going to be redesigned

Currently compiles to about 7kb, which isn't half bad considering we have most utility functions and structures in place already!
  Reply With Quote