Go Back   EcoModder Forum > EcoModding > Instrumentation > OpenGauge / MPGuino FE computer
Register Now
 Register Now
 

Reply  Post New Thread
 
Submit Tools LinkBack Thread Tools
Old 08-17-2013, 11:30 AM   #11 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Quote:
Originally Posted by nickdigger View Post
Mine uses delay_us (20 bytes) and delay_ms (48 bytes), which calls milliseconds (48 bytes). Now that i look at it, i will probably merge milliseconds() into delay_ms(), since it basically just returns the timer count, and it is only ever called by delay_ms. (Edit: so far, no good. It's "so efficient" that the compiler wants to inline the new delay_ticks everywhere, costing me 300 extra bytes)
I suspect this is what happened to me the other night. The delay routines themselves were pretty small, but they added too much to the code. Oh, well...

Quote:
Originally Posted by nickdigger View Post
I had assumed you were already doing something like that. It might be trivial to add another "event" chain to the handler, for each output.
Yah, it actually is fairly trivial. My main reason for not doing so was because the overflow timer already has a lot on its plate. It does fuel injector timeout tracking, program delay tracking, loop tracking, loop CPU utilization tracking, VSS debouncing, button debouncing (and long-press detection), inactivity tracking, and wake-up tracking.

In the end, I decided to use the ADC interrupt, instead. In ADC freewheeling mode at a divider value of 128, the ADC interrupt runs about every 82 microseconds, instead of every 819.2 microseconds for the timer overflow (102 microseconds and 1024 microseconds for a 16 MHz AtMega). The LCD buffer will clear out that much faster, and the main overflow timer will not get bogged down as much. It also splits up the interrupt latency, as compared to having the timer overflow handle both its existing duties and handling the LCD buffer, so that VSS and injector pulses will be captured more reliably.

I ran some tests using the overflow timer, and CPU loading went down to 2.95%. Using the ADC freewheeling interrupt, I got that down to 2.46%. The other screens also showed improvement - the most heavily processor-intensive screens, oddly enough, were the big FE screens. Their utilization was at 9.51% with the overflow timer, and they went down to 2.95% with the ADC freewheeling interrupt.

The code now stands at 20230 bytes, and there are 1079 bytes of free RAM. I can live with that.

  Reply With Quote
Alt Today
Popular topics

Other popular topics in this forum...

   
Old 08-17-2013, 11:51 AM   #12 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Quote:
Originally Posted by nickdigger View Post
This all makes me wonder if a multi-threading scheme could be based on the avr timers. I'll have to think about that some more later.
That'd be several shades of awesome. However, I can't think right now of any way to save more than one context in the Arduino environment.
  Reply With Quote
Old 08-17-2013, 01:28 PM   #13 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
The serial character output routine is now using the USART_UDRE interrupt to clear the buffer. There is no change in either the amount of free RAM or the CPU utilization, as it should be. The code shrank to 20178 bytes, which of course is a good thing.
  Reply With Quote
Old 08-22-2013, 12:32 AM   #14 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Added a two-stage filter in the freewheeling ADC interrupt routine. The first stage is a 16-element decimating CIC filter, which provides a 16x sampling frequency reduction. The second stage is a simple 1st order IIR filter. This should provide an adequate low-pass filter for the MAP sensor signal.

Did some code optimization, and got total code size down to 20158 bytes.
  Reply With Quote
Old 08-23-2013, 03:04 AM   #15 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Re-wrote the fuel injector pulse capture code to use only INT0. Converted raw measurement variables from separately named InjCycles, VSScycles, etc; to an array rawData[18] with specified constant indexes (rvInjCycleIdx, rvVSScycleIdx, etc.). Re-wrote Trip class routines to take advantage of using an array of raw values.

Code size is now at 17002. CPU loading is at 2.20%. Free RAM is at 1051.

(Yes - I am indeed shocked. That's smaller than the original 0.86 code size, which is 17160).
  Reply With Quote
Old 08-25-2013, 04:00 AM   #16 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Finally came up with a fairly quick integer square root function. Basically, it uses the Babylonian Method to calculate the square root of an integer representation of a decimal number that has about 3 decimal places, as shown.

d = i / 4096

so, for d = 1, i = 4096. for d = 1.5, i = 6144. And so on...

Anyway, the square root function finds the square root of a number, given the above rule for i. So, for the square root of i = 6144 (d = 1.5), sqrt(i) would return 5016 (5016 / 4096 = 1.225. It uses up about 28 microseconds to do this, too, and adds 108 bytes to the code to do so.

The reason I coded the square root this way was so I could then do this:

Code:
i = manifoldPressure + fuelPressure; // calculate pressure differential seen across injector
i <<= 12; // multiply by 4096
i /= fuelPressure; // divide by fuel system pressure to generate pressure ratio
injOpenPulseTime = injOpenPulseTime * iSqrt(i); // multiply fuel injector open time by integer square root of pressure ratio
injOpenPulseTime >>= 12; // shift right to complete factor adjustment
I estimate that this little bit of code would take about 36 microseconds to execute in total. This is well within the time span between injector firings.
  Reply With Quote
The Following User Says Thank You to t vago For This Useful Post:
pgfpro (08-25-2013)
Old 08-25-2013, 11:31 AM   #17 (permalink)
In Lean Burn Mode
 
pgfpro's Avatar
 
Join Date: Apr 2009
Location: Pacific NW
Posts: 1,528

MisFit Talon - '91 Eagle Talon TSi
Team Turbocharged!
90 day: 39.03 mpg (US)

Warlock - '71 Chevy Camaro

Fe Eclipse - '97 Mitsubishi Eclipse GS
Thanks: 1,243
Thanked 580 Times in 372 Posts
Nice Work!!!

How many hours so far do you have in this???
__________________
Pressure Gradient Force
The Positive Side of the Number Line

  Reply With Quote
Old 08-25-2013, 03:26 PM   #18 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Quote:
Originally Posted by pgfpro View Post
Nice Work!!!

How many hours so far do you have in this???
Thank you!

I think that I have put in about 6 hours, actually coding and debugging the pressure correction code. However, I spent about a week, looking at square root (and other math) algorithms, trying to get some inspiration. In the end, I figured that a simple approach was best. Coding the filtering algorithms for reading MAP voltage took about another couple of hours.

Here's the code that I am going to test later on today, on the road.

Code:
void readMAP(void) {

	static unsigned long wp;
	static unsigned int sample;

	sampleCount = sampleTickLength - 1; // reset sample timer counter

	sample = sample + 3 * analogValue; // first order IIR filter - filt = filt + 3/4 * (reading - filt)
	sample >>= 2;

	wp = (unsigned long)(sample - analogFloor);
	wp *= (unsigned long)analogSlope;
	wp /= 1000ul;
	pressure[MAPpressureIdx] = (unsigned int)wp;

	// if vehicle is not running and is not moving, it's a fair bet that the intake manifold contains atmospheric pressure
	if (!(dirty & dirtyGoodVSS) && !(dirty & dirtyGoodInj)) pressure[baroPressureIdx] = pressure[MAPpressureIdx];

	// calculate differential pressure seen across the fuel injector
	wp = (unsigned long)pressure[fuelPressureIdx] + (unsigned long)pressure[baroPressureIdx] - (unsigned long)pressure[MAPpressureIdx];
	pressure[injPressureIdx] = (unsigned int)wp;

	// to get fuel pressure ratio, multiply differential pressure by denominator factor (1 << 12), then divide by fuel system pressure
	wp <<= 12;
	wp /= (unsigned long)pressure[fuelPressureIdx];

	// calculate square root of fuel pressure ratio
	pressure[injCorrectionIdx] = iSqrt((unsigned int)wp);

}

unsigned int iSqrt(unsigned int n) {

	unsigned long w = 4096; // square factor guess
	unsigned int t = 4096; // proposed square root
	int d; // difference between guess and proposed
	int od = 0;

	for (uint8_t x = 0; x < 5; x++) {

		od = d;
		d = n - (unsigned int)w;
		d >>= 1;
		t += d;

		od += d;

		if ((d == 0) || (od == 0)) break;

		w = (unsigned long)t * (unsigned long)t;
		w >>= 12;

	}

	return t;

}
Bemchmark testing reveals that readMap() takes about 110 us to execute, and iSqrt() takes about 33 us.
  Reply With Quote
Old 08-29-2013, 04:12 PM   #19 (permalink)
EcoModding Apprentice
 
Join Date: Aug 2009
Location: terra firma
Posts: 138
Thanks: 4
Thanked 24 Times in 22 Posts
Quote:
Originally Posted by t vago View Post
Re-wrote the fuel injector pulse capture code to use only INT0.
Did you have to do any debouncing-type of trick for this? I tried switching to the CHANGE trigger, and wondered if it failed because the level hadn't yet fully risen to "high" when the int fired.

I think the space savings was only about 80 bytes, by combining the interrupts, but potentially the free pin could be more valuable.
  Reply With Quote
Old 08-30-2013, 12:03 PM   #20 (permalink)
MPGuino Supporter
 
t vago's Avatar
 
Join Date: Oct 2010
Location: Hungary
Posts: 1,807

iNXS - '10 Opel Zafira 111 Anniversary

Suzi - '02 Suzuki Swift GL
Thanks: 828
Thanked 706 Times in 455 Posts
Quote:
Originally Posted by nickdigger View Post
Did you have to do any debouncing-type of trick for this? I tried switching to the CHANGE trigger, and wondered if it failed because the level hadn't yet fully risen to "high" when the int fired.
Nope. All I had to do was to be able to read pin D, and act on it.

Code:
	uint8_t injDir = (PIND ^ injDirection) & (1 << PIND2);

	if (injDir) { // handle fuel injector opening event

...

	} else { // handle fuel injector closing event

...

	}
injDirection is a volatile uint8_t which is set to either 0x00 or 0xFF, depending on the state of the injector edge trigger flag parameter.

Quote:
Originally Posted by nickdigger View Post
I think the space savings was only about 80 bytes, by combining the interrupts, but potentially the free pin could be more valuable.
Indeed. Every little bit helps, and that interrupt pin is now freed up for other things.

  Reply With Quote
Reply  Post New Thread


Thread Tools




Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Content Relevant URLs by vBSEO 3.5.2
All content copyright EcoModder.com