04-17-2011, 11:42 PM
|
#21 (permalink)
|
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
|
I like what you're coming up with! I've been a bit confused on things and reading from the wiki, and the first workspace and then the other guy who made do it yourself kits.
I'm a fairly tech smart person and I was having some issues figuring it all out. To non tech people, this could be very confusing and just stop them from being interested. Mpguino is awesome, and I for one want to see it continue to develop and evolve.
|
|
|
Today
|
|
|
Other popular topics in this forum...
|
|
|
04-18-2011, 12:04 AM
|
#22 (permalink)
|
EcoModding Lurker
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78
Thanks: 4
Thanked 9 Times in 7 Posts
|
Thanks! Yeah, the goal here is openness on both code-side and user-side... certainly a clean display with a visible interface would be more inviting than one plugged with "0"'s in all but a few places It's definitely an interesting task...
|
|
|
04-18-2011, 04:53 AM
|
#23 (permalink)
|
EcoModding Lurker
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78
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!
|
|
|
04-18-2011, 12:49 PM
|
#24 (permalink)
|
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
|
Did you do all that last night?
|
|
|
04-18-2011, 01:04 PM
|
#25 (permalink)
|
EcoModding Lurker
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78
Thanks: 4
Thanked 9 Times in 7 Posts
|
Yep. xD
Hopefully more tonight... finally getting down to a sane work schedule (part time that still pays the bills - yay!), so I should have a functional UI framework set up tonight...
|
|
|
04-18-2011, 02:15 PM
|
#26 (permalink)
|
EcoModding Apprentice
Join Date: Jan 2010
Location: Newark, DE
Posts: 143
Thanks: 0
Thanked 14 Times in 14 Posts
|
Any interest in merging projects? I've been slow in writing the UI pages themselves, but the framework is all in place on the MPGshield project. Right now it just has a VSS info page, injector info page, and instant/average MPG page. All I ask is that the code remain hardware-neutral (standard libraries where available, #define pin numbers and such) and is reasonably readable and/or well-commented, which IIRC is among your goals as well.
Here's the current code, including stuff to control an external 4 digit 7-segment display via an SAA1064 chip via I2C. The display comms are one-way, so it doesn't hurt anything if there's no chip to talk to.
http://dl.dropbox.com/u/9882625/MPGshield_0.3.pde
Last edited by bobski; 04-18-2011 at 02:22 PM..
Reason: added link
|
|
|
04-18-2011, 02:32 PM
|
#27 (permalink)
|
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
|
i searched google and couldn't find anything about MPGshield project. Do you have a link or something for those interested in your work?
|
|
|
04-18-2011, 03:01 PM
|
#28 (permalink)
|
EcoModding Lurker
Join Date: Mar 2010
Location: TN-USA
Posts: 61
Thanks: 0
Thanked 1 Time in 1 Post
|
steffen, what you're looking for is here: http://ecomodder.com/forum/showthrea...-v2-16364.html
You guys are clearly much better at programming than myself, keep up the good work!
Are you planning on adding any code modifications like the rounder large font, the eeprom tank data save or others? Personally I'd also like to see a clock added, and a miles/time till empty
|
|
|
04-18-2011, 03:03 PM
|
#29 (permalink)
|
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
|
Thanks, i'll check out your thread.
|
|
|
04-18-2011, 05:11 PM
|
#30 (permalink)
|
EcoModding Apprentice
Join Date: Jan 2010
Location: Newark, DE
Posts: 143
Thanks: 0
Thanked 14 Times in 14 Posts
|
Quote:
Originally Posted by mcmancuso
Are you planning on adding any code modifications like the rounder large font, the eeprom tank data save or others? Personally I'd also like to see a clock added, and a miles/time till empty
|
I can't speak for this project, but all of the above have been on my to-do list. I just haven't gotten to them between school (finishing up my EE AAS degree) and other projects I have kicking around.
Figuring out storing variables to internal EEPROM is my planned next step in development (see below).
Making a trip log on external EEPROM might be a little more involved (deciding what a trip is, formatting and storing the data, programming a way to read it back), but still on the list... I already have such a chip hooked up on my shield.
I have a DS1337 real time clock and backup battery all soldered up and ready to go on some strip board. I also got some DS3231M high-accuracy real time clocks as (free) samples from Maxim Semiconductor (yay for being an engineering student!), so i'll be playing with those at some point as well.
At this point, miles/time to empty should be a simple matter of figuring out the calculations and writing a text page to display them.
In addition to the above, I would like to depart from the MPGuino's always-on method by using a voltage regulator with an on/off lead such as the Sharp PQ05RD21J00H. These regulators have the standard Vin, Vout and Ground pins, plus a fourth control pin that shuts off Vout and puts the regulator in a very low power draw state. In our case, Vin would be constant battery power from the car, Vout goes to the 5V+ power bus of the MPGuino/MPGshield/(/Fork name TBD), replacing any stock regulator and Ground gets shared by the two voltage systems... This is exactly the same way the MPGuino and most Arduino systems are presently set up AFAIK. The difference is that fourth control pin.
When you switch the ignition on, the car's switched 12V+ would send 5V+ to the regulator's control pin via one of the typical resistor-zener pairs used for input conditioning on the MPGuino and MPGshield. The regulator kicks on and powers up the system. Part of the system's setup routine would drive high a digital output that feeds the regulator's control lead in tandem with ignition switched power (diode isolation would be used). Setup would continue on to read various variables (distance traveled, fuel dispensed, similar trip values and such) out of the EEPROM storage on the ATMEGA chip. For those of you who see where I'm going, the EEPROM memory is rated for way more read-write cycles than flash, so I don't see any problem using it this way and is actually preferable to somehow writing the variables to flash.
When the ignition is turned off, the switched 12V+ line to the regulator cuts off, but the system keeps the regulator active with its own line. The system would read that the ignition had been turned off either by a second line, or possibly by reading that fact over the driving regulator line (it would have to be an analog pin), and kick into a power-down routine. This routine would save any necessary variables into EEPROM, do any other power-down stuff that is needed, then let the regulator control line drop, shutting down the regulator and cutting power to the system.
|
|
|
|