08-17-2013, 12:30 PM
|
#11 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 Posts
|
Quote:
Originally Posted by nickdigger
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
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.
|
|
|
Today
|
|
|
Other popular topics in this forum...
|
|
|
08-17-2013, 12:51 PM
|
#12 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 Posts
|
Quote:
Originally Posted by nickdigger
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.
|
|
|
08-17-2013, 02:28 PM
|
#13 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 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.
|
|
|
08-22-2013, 01:32 AM
|
#14 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 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.
|
|
|
08-23-2013, 04:04 AM
|
#15 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 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).
|
|
|
08-25-2013, 05:00 AM
|
#16 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 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.
|
|
|
The Following User Says Thank You to t vago For This Useful Post:
|
|
08-25-2013, 12:31 PM
|
#17 (permalink)
|
In Lean Burn Mode
Join Date: Apr 2009
Location: Pacific NW
Posts: 1,553
Thanks: 1,315
Thanked 602 Times in 391 Posts
|
Nice Work!!!
How many hours so far do you have in this???
__________________
Pressure Gradient Force
The Positive Side of the Number Line
|
|
|
08-25-2013, 04:26 PM
|
#18 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 Posts
|
Quote:
Originally Posted by pgfpro
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.
|
|
|
08-29-2013, 05: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
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.
|
|
|
08-30-2013, 01:03 PM
|
#20 (permalink)
|
MPGuino Supporter
Join Date: Oct 2010
Location: Hungary
Posts: 1,808
iNXS - '10 Opel Zafira 111 Anniversary Suzi - '02 Suzuki Swift GL
Thanks: 831
Thanked 709 Times in 457 Posts
|
Quote:
Originally Posted by nickdigger
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
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.
|
|
|
|