Merge pull request #57 from noisymime/ADC-interrupt

ADC interrupt merge
Closes #56
This commit is contained in:
Josh Stewart 2016-11-18 13:44:48 +11:00 committed by GitHub
commit 34780378c6
6 changed files with 321 additions and 59 deletions

View File

@ -1,12 +0,0 @@
#ifndef FASTANALOG_H
#define FASTANALOG_H
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#endif // FASTANALOG_H

View File

@ -406,7 +406,7 @@ byte pinInjector1; //Output pin injector 1
byte pinInjector2; //Output pin injector 2 byte pinInjector2; //Output pin injector 2
byte pinInjector3; //Output pin injector 3 is on byte pinInjector3; //Output pin injector 3 is on
byte pinInjector4; //Output pin injector 4 is on byte pinInjector4; //Output pin injector 4 is on
byte pinInjector5; //Placeholder only - NOT USED byte pinInjector5; //Output pin injector 5 NOT USED YET
byte pinInjector6; //Placeholder only - NOT USED byte pinInjector6; //Placeholder only - NOT USED
byte pinInjector7; //Placeholder only - NOT USED byte pinInjector7; //Placeholder only - NOT USED
byte pinInjector8; //Placeholder only - NOT USED byte pinInjector8; //Placeholder only - NOT USED
@ -414,10 +414,10 @@ byte pinCoil1; //Pin for coil 1
byte pinCoil2; //Pin for coil 2 byte pinCoil2; //Pin for coil 2
byte pinCoil3; //Pin for coil 3 byte pinCoil3; //Pin for coil 3
byte pinCoil4; //Pin for coil 4 byte pinCoil4; //Pin for coil 4
byte pinCoil5; //Pin for coil 4 byte pinCoil5; //Pin for coil 5
byte pinCoil6; //Pin for coil 4 byte pinCoil6; //Pin for coil 6
byte pinCoil7; //Pin for coil 4 byte pinCoil7; //Pin for coil 7
byte pinCoil8; //Pin for coil 4 byte pinCoil8; //Pin for coil 8
byte pinTrigger; //The CAS pin byte pinTrigger; //The CAS pin
byte pinTrigger2; //The Cam Sensor pin byte pinTrigger2; //The Cam Sensor pin
byte pinTrigger3; //the 2nd cam sensor pin byte pinTrigger3; //the 2nd cam sensor pin

View File

@ -12,7 +12,10 @@
#define BARO_MIN 87 #define BARO_MIN 87
#define BARO_MAX 108 #define BARO_MAX 108
#define ANALOG_ISR
volatile byte flexCounter = 0; volatile byte flexCounter = 0;
volatile int AnChannel[15];
/* /*
* Simple low pass IIR filter macro for the analog inputs * Simple low pass IIR filter macro for the analog inputs
@ -27,4 +30,38 @@ void flexPulse();
unsigned int tempReading; unsigned int tempReading;
#if defined(ANALOG_ISR)
//Analog ISR interrupt routine
ISR(ADC_vect)
{
byte nChannel;
int result = ADCL | (ADCH << 8);
//ADCSRA = 0x6E; // ADC disabled by clearing bit 7(ADEN)
//BIT_CLEAR(ADCSRA, ADIE);
nChannel = ADMUX & 0x07;
#if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
if (nChannel==7) { ADMUX = 0x40; }
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if(ADCSRB & 0x08) { nChannel += 8; } //8 to 15
if(nChannel == 15)
{
ADMUX = 0x40; //channel 0
ADCSRB = 0x00; //clear MUX5 bit
}
else if (nChannel == 7) //channel 7
{
ADMUX = 0x40;
ADCSRB = 0x08; //Set MUX5 bit
}
#endif
else { ADMUX++; }
AnChannel[nChannel-1] = result;
//BIT_SET(ADCSRA, ADIE);
//ADCSRA = 0xEE; // ADC Interrupt Flag enabled
}
#endif
#endif // SENSORS_H #endif // SENSORS_H

View File

@ -4,12 +4,55 @@ Copyright (C) Josh Stewart
A full copy of the license may be found in the projects root directory A full copy of the license may be found in the projects root directory
*/ */
void initialiseADC()
{
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
#if defined(ANALOG_ISR)
//This sets the ADC (Analog to Digitial Converter) to run at 250KHz, greatly reducing analog read times (MAP/TPS)
//the code on ISR run each conversion every 25 ADC clock, conversion run about 100KHz effectively
//making a 6250 conversions/s on 16 channels and 12500 on 8 channels devices.
noInterrupts(); //Interrupts should be turned off when playing with any of these registers
ADCSRB = 0x00; //ADC Auto Trigger Source is in Free Running mode
ADMUX = 0x40; //Select AREF as reference, ADC Left Adjust Result, Starting at channel 0
//All of the below is the longhand version of: ADCSRA = 0xEE;
#define ADFR 5 //Why the HELL isn't this defined in the same place as everything else (wiring.h)?!?!
BIT_SET(ADCSRA,ADFR); //Set free running mode
BIT_SET(ADCSRA,ADIE); //Set ADC interrupt enabled
BIT_CLEAR(ADCSRA,ADIF); //Clear interrupt flag
// Set ADC clock to 125KHz (Prescaler = 128)
BIT_SET(ADCSRA,ADPS2);
BIT_SET(ADCSRA,ADPS1);
BIT_SET(ADCSRA,ADPS0);
BIT_SET(ADCSRA,ADEN); //Enable ADC
interrupts();
BIT_SET(ADCSRA,ADSC); //Start conversion
#else
//This sets the ADC (Analog to Digitial Converter) to run at 1Mhz, greatly reducing analog read times (MAP/TPS) when using the standard analogRead() function
//1Mhz is the fastest speed permitted by the CPU without affecting accuracy
//Please see chapter 11 of 'Practical Arduino' (http://books.google.com.au/books?id=HsTxON1L6D4C&printsec=frontcover#v=onepage&q&f=false) for more detail
BIT_SET(ADCSRA,ADPS2);
BIT_CLEAR(ADCSRA,ADPS1);
BIT_CLEAR(ADCSRA,ADPS0);
#endif
#endif
}
void instanteneousMAPReading() void instanteneousMAPReading()
{ {
//Instantaneous MAP readings //Instantaneous MAP readings
tempReading = analogRead(pinMAP); #if defined(ANALOG_ISR)
tempReading = analogRead(pinMAP); tempReading = AnChannel[pinMAP-A0];
#else
tempReading = analogRead(pinMAP);
tempReading = analogRead(pinMAP);
#endif
//Error checking //Error checking
if(tempReading >= VALID_MAP_MAX || tempReading <= VALID_MAP_MIN) { mapErrorCount += 1; } if(tempReading >= VALID_MAP_MAX || tempReading <= VALID_MAP_MIN) { mapErrorCount += 1; }
else { currentStatus.mapADC = tempReading; mapErrorCount = 0; } else { currentStatus.mapADC = tempReading; mapErrorCount = 0; }
@ -34,8 +77,12 @@ void readMAP()
if( (MAPcurRev == startRevolutions) || (MAPcurRev == startRevolutions+1) ) //2 revolutions are looked at for 4 stroke. 2 stroke not currently catered for. if( (MAPcurRev == startRevolutions) || (MAPcurRev == startRevolutions+1) ) //2 revolutions are looked at for 4 stroke. 2 stroke not currently catered for.
{ {
tempReading = analogRead(pinMAP); #if defined(ANALOG_ISR)
tempReading = analogRead(pinMAP); tempReading = AnChannel[pinMAP-A0];
#else
tempReading = analogRead(pinMAP);
tempReading = analogRead(pinMAP);
#endif
//Error check //Error check
if(tempReading < VALID_MAP_MAX && tempReading > VALID_MAP_MIN) if(tempReading < VALID_MAP_MAX && tempReading > VALID_MAP_MIN)
@ -62,8 +109,12 @@ void readMAP()
if( (MAPcurRev == startRevolutions) || (MAPcurRev == startRevolutions+1) ) //2 revolutions are looked at for 4 stroke. 2 stroke not currently catered for. if( (MAPcurRev == startRevolutions) || (MAPcurRev == startRevolutions+1) ) //2 revolutions are looked at for 4 stroke. 2 stroke not currently catered for.
{ {
tempReading = analogRead(pinMAP); #if defined(ANALOG_ISR)
tempReading = analogRead(pinMAP); tempReading = AnChannel[pinMAP-A0];
#else
tempReading = analogRead(pinMAP);
tempReading = analogRead(pinMAP);
#endif
//Error check //Error check
if(tempReading < VALID_MAP_MAX && tempReading > VALID_MAP_MIN) if(tempReading < VALID_MAP_MAX && tempReading > VALID_MAP_MIN)
{ {
@ -87,8 +138,12 @@ void readTPS()
{ {
currentStatus.TPSlast = currentStatus.TPS; currentStatus.TPSlast = currentStatus.TPS;
currentStatus.TPSlast_time = currentStatus.TPS_time; currentStatus.TPSlast_time = currentStatus.TPS_time;
analogRead(pinTPS); #if defined(ANALOG_ISR)
byte tempTPS = fastMap1023toX(analogRead(pinTPS), 255); //Get the current raw TPS ADC value and map it into a byte byte tempTPS = fastMap1023toX(AnChannel[pinTPS-A0], 255); //Get the current raw TPS ADC value and map it into a byte
#else
analogRead(pinTPS);
byte tempTPS = fastMap1023toX(analogRead(pinTPS), 255); //Get the current raw TPS ADC value and map it into a byte
#endif
currentStatus.tpsADC = ADC_FILTER(tempTPS, ADCFILTER_TPS, currentStatus.tpsADC); currentStatus.tpsADC = ADC_FILTER(tempTPS, ADCFILTER_TPS, currentStatus.tpsADC);
//Check that the ADC values fall within the min and max ranges (Should always be the case, but noise can cause these to fluctuate outside the defined range). //Check that the ADC values fall within the min and max ranges (Should always be the case, but noise can cause these to fluctuate outside the defined range).
byte tempADC = currentStatus.tpsADC; //The tempADC value is used in order to allow TunerStudio to recover and redo the TPS calibration if this somehow gets corrupted byte tempADC = currentStatus.tpsADC; //The tempADC value is used in order to allow TunerStudio to recover and redo the TPS calibration if this somehow gets corrupted
@ -100,24 +155,36 @@ void readTPS()
void readCLT() void readCLT()
{ {
tempReading = analogRead(pinCLT); #if defined(ANALOG_ISR)
tempReading = fastMap1023toX(analogRead(pinCLT), 511); //Get the current raw CLT value tempReading = fastMap1023toX(AnChannel[pinCLT-A0], 511); //Get the current raw CLT value
#else
tempReading = analogRead(pinCLT);
tempReading = fastMap1023toX(analogRead(pinCLT), 511); //Get the current raw CLT value
#endif
currentStatus.cltADC = ADC_FILTER(tempReading, ADCFILTER_CLT, currentStatus.cltADC); currentStatus.cltADC = ADC_FILTER(tempReading, ADCFILTER_CLT, currentStatus.cltADC);
currentStatus.coolant = cltCalibrationTable[currentStatus.cltADC] - CALIBRATION_TEMPERATURE_OFFSET; //Temperature calibration values are stored as positive bytes. We subtract 40 from them to allow for negative temperatures currentStatus.coolant = cltCalibrationTable[currentStatus.cltADC] - CALIBRATION_TEMPERATURE_OFFSET; //Temperature calibration values are stored as positive bytes. We subtract 40 from them to allow for negative temperatures
} }
void readIAT() void readIAT()
{ {
tempReading = analogRead(pinIAT); #if defined(ANALOG_ISR)
tempReading = fastMap1023toX(analogRead(pinIAT), 511); //Get the current raw IAT value tempReading = fastMap1023toX(AnChannel[pinIAT-A0], 511); //Get the current raw IAT value
#else
tempReading = analogRead(pinIAT);
tempReading = fastMap1023toX(analogRead(pinIAT), 511); //Get the current raw IAT value
#endif
currentStatus.iatADC = ADC_FILTER(tempReading, ADCFILTER_IAT, currentStatus.iatADC); currentStatus.iatADC = ADC_FILTER(tempReading, ADCFILTER_IAT, currentStatus.iatADC);
currentStatus.IAT = iatCalibrationTable[currentStatus.iatADC] - CALIBRATION_TEMPERATURE_OFFSET; currentStatus.IAT = iatCalibrationTable[currentStatus.iatADC] - CALIBRATION_TEMPERATURE_OFFSET;
} }
void readO2() void readO2()
{ {
tempReading = analogRead(pinO2); #if defined(ANALOG_ISR)
tempReading = fastMap1023toX(analogRead(pinO2), 511); //Get the current O2 value. tempReading = fastMap1023toX(AnChannel[pinO2-A0], 511); //Get the current O2 value.
#else
tempReading = analogRead(pinO2);
tempReading = fastMap1023toX(analogRead(pinO2), 511); //Get the current O2 value.
#endif
currentStatus.O2ADC = ADC_FILTER(tempReading, ADCFILTER_O2, currentStatus.O2ADC); currentStatus.O2ADC = ADC_FILTER(tempReading, ADCFILTER_O2, currentStatus.O2ADC);
currentStatus.O2 = o2CalibrationTable[currentStatus.O2ADC]; currentStatus.O2 = o2CalibrationTable[currentStatus.O2ADC];
} }
@ -130,8 +197,12 @@ void readO2()
void readBat() void readBat()
{ {
tempReading = analogRead(pinBat); #if defined(ANALOG_ISR)
tempReading = fastMap1023toX(analogRead(pinBat), 245); //Get the current raw Battery value. Permissible values are from 0v to 24.5v (245) tempReading = fastMap1023toX(AnChannel[pinBat-A0], 245); //Get the current raw Battery value. Permissible values are from 0v to 24.5v (245)
#else
tempReading = analogRead(pinBat);
tempReading = fastMap1023toX(analogRead(pinBat), 245); //Get the current raw Battery value. Permissible values are from 0v to 24.5v (245)
#endif
currentStatus.battery10 = ADC_FILTER(tempReading, ADCFILTER_BAT, currentStatus.battery10); currentStatus.battery10 = ADC_FILTER(tempReading, ADCFILTER_BAT, currentStatus.battery10);
} }

View File

@ -35,7 +35,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "decoders.h" #include "decoders.h"
#include "idle.h" #include "idle.h"
#include "auxiliaries.h" #include "auxiliaries.h"
#include "fastAnalog.h"
#include "sensors.h" #include "sensors.h"
#include "src/PID_v1/PID_v1.h" #include "src/PID_v1/PID_v1.h"
//#include "src/DigitalWriteFast/digitalWriteFast.h" //#include "src/DigitalWriteFast/digitalWriteFast.h"
@ -210,18 +209,18 @@ void setup()
//Need to check early on whether the coil charging is inverted. If this is not set straight away it can cause an unwanted spark at bootup //Need to check early on whether the coil charging is inverted. If this is not set straight away it can cause an unwanted spark at bootup
if(configPage2.IgInv == 1) { coilHIGH = LOW, coilLOW = HIGH; } if(configPage2.IgInv == 1) { coilHIGH = LOW, coilLOW = HIGH; }
else { coilHIGH = HIGH, coilLOW = LOW; } else { coilHIGH = HIGH, coilLOW = LOW; }
digitalWrite(pinCoil1, coilLOW); endCoil1Charge();
digitalWrite(pinCoil2, coilLOW); endCoil2Charge();
digitalWrite(pinCoil3, coilLOW); endCoil3Charge();
digitalWrite(pinCoil4, coilLOW); endCoil4Charge();
digitalWrite(pinCoil5, coilLOW); endCoil5Charge();
//Similar for injectors, make sure they're turned off //Similar for injectors, make sure they're turned off
digitalWrite(pinInjector1, LOW); closeInjector1();
digitalWrite(pinInjector2, LOW); closeInjector2();
digitalWrite(pinInjector3, LOW); closeInjector3();
digitalWrite(pinInjector4, LOW); closeInjector4();
digitalWrite(pinInjector5, LOW); closeInjector5();
//Set the tacho output default state //Set the tacho output default state
digitalWrite(pinTachOut, HIGH); digitalWrite(pinTachOut, HIGH);
@ -254,6 +253,7 @@ void setup()
initialiseFan(); initialiseFan();
initialiseAuxPWM(); initialiseAuxPWM();
initialiseCorrections(); initialiseCorrections();
initialiseADC();
//Check whether the flex sensor is enabled and if so, attach an interupt for it //Check whether the flex sensor is enabled and if so, attach an interupt for it
if(configPage1.flexEnabled) if(configPage1.flexEnabled)
@ -484,20 +484,6 @@ void setup()
//Initial values for loop times //Initial values for loop times
previousLoopTime = 0; previousLoopTime = 0;
currentLoopTime = micros(); currentLoopTime = micros();
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
//This sets the ADC (Analog to Digitial Converter) to run at 1Mhz, greatly reducing analog read times (MAP/TPS)
//1Mhz is the fastest speed permitted by the CPU without affecting accuracy
//Please see chapter 11 of 'Practical Arduino' (http://books.google.com.au/books?id=HsTxON1L6D4C&printsec=frontcover#v=onepage&q&f=false) for more details
//Can be disabled by removing the #include "fastAnalog.h" above
#ifdef sbi
sbi(ADCSRA,ADPS2);
cbi(ADCSRA,ADPS1);
cbi(ADCSRA,ADPS0);
#endif
#endif
mainLoopCount = 0; mainLoopCount = 0;
ignitionCount = 0; ignitionCount = 0;
@ -1452,6 +1438,37 @@ void loop()
//************************************************************************************************ //************************************************************************************************
//Interrupts //Interrupts
#if defined(ANALOG_H)
//Analog ISR interrupt routine
ISR(ADC_vect)
{
byte nChannel;
int result = ADCL | (ADCH << 8);
ADCSRA = 0x6E; // ADC Auto Trigger disabled by clearing bit 7(ADEN)
nChannel = ADMUX & 0x07;
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if(ADCSRB & 0x08) { nChannel+=8; } //8 to 15
if(nChannel==15)
{
ADMUX = 0x40; //channel 0
ADCSRB = 0x00; //clear MUX5 bit
}
else if (nChannel==7) //channel 7
{
ADMUX = 0x40;
ADCSRB = 0x08; //Set MUX5 bit
}
#elif defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
if (nChannel==7) { ADMUX = 0x40; }
#endif
else { ADMUX++; }
AnChannel[nChannel] = result;
ADCSRA = 0xEE; // ADC Interrupt Flag enabled
}
#endif
//These functions simply trigger the injector/coil driver off or on. //These functions simply trigger the injector/coil driver off or on.
//NOTE: squirt status is changed as per http://www.msextra.com/doc/ms1extra/COM_RS232.htm#Acmd //NOTE: squirt status is changed as per http://www.msextra.com/doc/ms1extra/COM_RS232.htm#Acmd
/* /*

149
table.ino
View File

@ -379,3 +379,152 @@ int get3DTableValue(struct table3D *fromTable, int Y, int X)
int r = (p * q) >> 8; int r = (p * q) >> 8;
return ( (A * m) + (B * n) + (C * o) + (D * r) ) >> 8; return ( (A * m) + (B * n) + (C * o) + (D * r) ) >> 8;
} }
/* Executed a benchmark on all options and this is the results
* Stadard:226224 91 |FP Math:32240 91.89 |Clean code:34056 91, Number of loops:2500
*
//This function pulls a value from a 3D table given a target for X and Y coordinates.
//It performs a 2D linear interpolation as descibred in: http://www.megamanual.com/v22manual/ve_tuner.pdf
float get3DTableValueF(struct table3D *fromTable, int Y, int X)
{
float m, n, o ,p, q, r;
byte xMin, xMax;
byte yMin, yMax;
int yMaxValue, yMinValue;
int xMaxValue, xMinValue;
if(fromTable->lastXMin==0) {fromTable->lastXMin = fromTable->xSize-1;}
else {xMin = fromTable->lastXMin;}
if(fromTable->lastYMin==0) {fromTable->lastYMin = fromTable->ySize-1;}
else {yMin = fromTable->lastYMin;}
//yMin = fromTable->lastYMin;
if(xMin>fromTable->xSize-1)
{
fromTable->lastXMin = fromTable->xSize-1;
xMin = fromTable->xSize-1;
}
if(yMin>fromTable->ySize-1)
{
fromTable->lastYMin = fromTable->ySize-1;
yMin = fromTable->ySize-1;
}
do //RPM axis
{
if(X>=fromTable->axisX[xMin]) {break;}
xMin--;
}while(1);
fromTable->lastXMin = xMin + 1;
do //MAP axis
{
if(Y<=fromTable->axisY[yMin]) {break;}
yMin--;
}while(1);
fromTable->lastYMin = yMin + 1;
xMax = xMin + 1;
yMax = yMin + 1;
if (xMax>fromTable->xSize-1) //Overflow protection
{
xMax = fromTable->xSize-1;
xMin = xMax - 1;
}
if (yMax>fromTable->ySize-1) //Overflow protection
{
yMax = fromTable->ySize-1;
yMin = yMax - 1;
}
yMaxValue = fromTable->axisY[yMax];
yMinValue = fromTable->axisY[yMin];
xMaxValue = fromTable->axisX[xMax];
xMinValue = fromTable->axisX[xMin];
int A = fromTable->values[yMin][xMin];
int B = fromTable->values[yMin][xMax];
int C = fromTable->values[yMax][xMin];
int D = fromTable->values[yMax][xMax];
p = float(X - xMinValue) / (xMaxValue - xMinValue); //(RPM - RPM[1])/(RPM[2]- RPM[1])
q = float(Y - yMinValue) / (yMaxValue - yMinValue); //(MAP - MAP[1])/(MAP[2]- MAP[1])
m = (1.0-p) * (1.0-q);
n = p * (1-q);
o = (1-p) * q;
r = p * q;
return ( (A * m) + (B * n) + (C * o) + (D * r) );
}
//This function pulls a value from a 3D table given a target for X and Y coordinates.
//It performs a 2D linear interpolation as descibred in: http://www.megamanual.com/v22manual/ve_tuner.pdf
int get3DTableValueS(struct table3D *fromTable, int Y, int X)
{
byte xMin, xMax;
byte yMin, yMax;
long p, q;
int yMaxValue, yMinValue;
int xMaxValue, xMinValue;
if(fromTable->lastXMin==0) {fromTable->lastXMin=fromTable->xSize-1;}
else {xMin = fromTable->lastXMin;}
if(fromTable->lastYMin==0) {fromTable->lastYMin=fromTable->ySize-1;}
else {yMin = fromTable->lastYMin;}
if(xMin>fromTable->xSize-1)
{
fromTable->lastXMin = fromTable->xSize-1;
xMin = fromTable->xSize-1;
}
if(yMin>fromTable->ySize-1)
{
fromTable->lastYMin = fromTable->ySize-1;
yMin = fromTable->ySize-1;
}
do //RPM axis
{
if(X>=fromTable->axisX[xMin]) {break;}
xMin--;
}while(1);
fromTable->lastXMin = xMin + 1;
do //MAP axis
{
if(Y<=fromTable->axisY[yMin]) {break;}
yMin--;
}while(1);
fromTable->lastYMin = yMin + 1;
xMax = xMin + 1;
yMax = yMin + 1;
if (xMax>fromTable->xSize-1) //Overflow protection
{
xMax = fromTable->xSize-1;
xMin = xMax - 1;
}
if (yMax>fromTable->ySize-1) //Overflow protection
{
yMax = fromTable->ySize-1;
yMin = yMax - 1;
}
yMaxValue = fromTable->axisY[yMax];
yMinValue = fromTable->axisY[yMin];
xMaxValue = fromTable->axisX[xMax];
xMinValue = fromTable->axisX[xMin];
int A = fromTable->values[yMin][xMin];
int B = fromTable->values[yMin][xMax];
int C = fromTable->values[yMax][xMin];
int D = fromTable->values[yMax][xMax];
p = ((long)(X - xMinValue) << 8) / (xMaxValue - xMinValue); //(RPM - RPM[1])/(RPM[2]- RPM[1])
q = 256 - (((long)(Y - yMaxValue) << 8) / (yMinValue - yMaxValue)); //(MAP - MAP[2])/(MAP[2]- MAP[1])
int m = ((256-p) * (256-q)) >> 8;
int n = (p * (256-q)) >> 8;
int o = ((256-p) * q) >> 8;
int r = (p * q) >> 8;
return ( (A * m) + (B * n) + (C * o) + (D * r) ) >> 8;
}
*/