update with josh2 13102016

This commit is contained in:
darren siepka 2016-10-13 23:24:22 +01:00
parent 501778a8fe
commit 6289f0cda2
18 changed files with 642 additions and 375 deletions

View File

@ -1,4 +1,9 @@
#ifndef AUX_H
#define AUX_H
void initialiseAuxPWM();
void boostControl();
void vvtControl();
volatile byte *boost_pin_port;
volatile byte boost_pin_mask;
@ -16,4 +21,4 @@ unsigned int vvt_pwm_max_count; //Used for variable PWM frequency
volatile unsigned int vvt_pwm_cur_value;
long vvt_pwm_target_value;
#endif

View File

@ -21,6 +21,7 @@ void fanControl()
else if (currentStatus.coolant <= (configPage4.fanSP - configPage4.fanHyster)) { digitalWrite(pinFan, fanLOW); }
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
void initialiseAuxPWM()
{
TCCR1B = 0x00; //Disbale Timer1 while we set it up
@ -53,9 +54,7 @@ void boostControl()
boostPID.Compute();
TIMSK1 |= (1 << OCIE1A); //Turn on the compare unit (ie turn on the interrupt)
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
else { TIMSK1 &= ~(1 << OCIE1A); } // Disable timer channel
#endif
}
void vvtControl()
@ -71,7 +70,6 @@ void vvtControl()
}
//The interrupt to control the Boost PWM
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ISR(TIMER1_COMPA_vect)
{
if (boost_pwm_state)
@ -107,6 +105,11 @@ ISR(TIMER1_COMPB_vect)
}
}
#elif defined(PROCESSOR_TEENSY_3_1) || defined(PROCESSOR_TEENSY_3_2)
#elif defined (CORE_TEENSY)
//YET TO BE IMPLEMENTED ON TEENSY
void initialiseAuxPWM() { }
void boostControl() { }
void vvtControl() { }
#endif

View File

@ -4,14 +4,14 @@
#define veMapPage 1
uint8_t currentcanPage = 1;//Not the same as the speeduino config page numbers
uint8_t currentCanPage = 1;//Not the same as the speeduino config page numbers
uint8_t nCanretry = 0; //no of retrys
uint8_t cancmdfail = 0; //command fail yes/no
uint8_t canlisten = 0;
uint8_t Lbuffer[8]; //8 byte buffer to store incomng can data
void Cancommand();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable.
void canCommand();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable.
void sendCancommand(uint8_t cmdtype , uint16_t canadddress, uint8_t candata1, uint8_t candata2);
void testCanComm();

View File

@ -15,7 +15,7 @@ sendcancommand is called when a comman d is to be sent via serial3 to the Can in
//#include "globals.h"
//#include "storage.h"
void Cancommand()
void canCommand()
{
switch (Serial3.read())
{

128
comms.ino
View File

@ -18,7 +18,7 @@ void command()
switch (Serial.read())
{
case 'A': // send x bytes of realtime values
sendValues(packetSize,0); //send values to serial0
sendValues(packetSize, 0); //send values to serial0
break;
case 'B': // Burn current values to eeprom
@ -62,12 +62,12 @@ void command()
break;
case 'S': // send code version
Serial.print("Speeduino 2016.09");
Serial.print("Speeduino 2016.10-dev");
currentStatus.secl = 0; //This is required in TS3 due to its stricter timings
break;
case 'Q': // send code version
Serial.print("speeduino 201609-dev");
Serial.print("speeduino 201610-dev");
break;
case 'V': // send VE table and constants in binary
@ -75,7 +75,7 @@ void command()
break;
case 'W': // receive new VE obr constant at 'W'+<offset>+<newbyte>
int offset;
int valueOffset; //cannot use offset as a variable name, it is a reserved word for several teensy libraries
while (Serial.available() == 0) { }
if (isMap)
@ -84,15 +84,15 @@ void command()
offset1 = Serial.read();
while (Serial.available() == 0) { }
offset2 = Serial.read();
offset = word(offset2, offset1);
valueOffset = word(offset2, offset1);
}
else
{
offset = Serial.read();
valueOffset = Serial.read();
}
while (Serial.available() == 0) { }
receiveValue(offset, Serial.read());
receiveValue(valueOffset, Serial.read());
break;
case 't': // receive new Calibration info. Command structure: "t", <tble_idx> <data array>. This is an MS2/Extra command, NOT part of MS1 spec
@ -198,20 +198,21 @@ void command()
/*
This function returns the current values of a fixed group of variables
*/
void sendValues(int packetlength, byte portnum)
void sendValues(int packetlength, byte portNum)
{
byte response[packetlength];
if (portnum == 3){ //if port number is 3
if (portNum == 3)
{
//CAN serial
Serial3.write("A"); //confirm cmd type
Serial3.write(packetlength); //confirm no of byte to be sent
}
}
else
{
if(requestCount == 0) { currentStatus.secl = 0; }
requestCount++;
}
{
if(requestCount == 0) { currentStatus.secl = 0; }
requestCount++;
}
currentStatus.spark ^= (-currentStatus.hasSync ^ currentStatus.spark) & (1 << BIT_SPARK_SYNC); //Set the sync bit of the Spark variable to match the hasSync variable
@ -260,14 +261,13 @@ void sendValues(int packetlength, byte portnum)
response[34] = getNextError();
//cli();
if (portnum == 0){Serial.write(response, (size_t)packetlength);}
else if (portnum == 3){Serial3.write(response, (size_t)packetlength);}
//Serial.flush();
if (portNum == 0) { Serial.write(response, (size_t)packetlength); }
else if (portNum == 3) { Serial3.write(response, (size_t)packetlength); }
//sei();
return;
}
void receiveValue(int offset, byte newValue)
void receiveValue(int valueOffset, byte newValue)
{
void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size
@ -275,24 +275,24 @@ void receiveValue(int offset, byte newValue)
switch (currentPage)
{
case veMapPage:
if (offset < 256) //New value is part of the fuel map
if (valueOffset < 256) //New value is part of the fuel map
{
fuelTable.values[15 - offset / 16][offset % 16] = newValue;
fuelTable.values[15 - valueOffset / 16][valueOffset % 16] = newValue;
return;
}
else
{
//Check whether this is on the X (RPM) or Y (MAP/TPS) axis
if (offset < 272)
if (valueOffset < 272)
{
//X Axis
fuelTable.axisX[(offset - 256)] = ((int)(newValue) * 100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct
fuelTable.axisX[(valueOffset - 256)] = ((int)(newValue) * 100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct
}
else
{
//Y Axis
offset = 15 - (offset - 272); //Need to do a translation to flip the order (Due to us using (0,0) in the top left rather than bottom right
fuelTable.axisY[offset] = (int)(newValue);
valueOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order (Due to us using (0,0) in the top left rather than bottom right
fuelTable.axisY[valueOffset] = (int)(newValue);
}
return;
}
@ -301,31 +301,31 @@ void receiveValue(int offset, byte newValue)
case veSetPage:
pnt_configPage = &configPage1; //Setup a pointer to the relevant config page
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if ( offset < page_size)
if (valueOffset < page_size)
{
*((byte *)pnt_configPage + (byte)offset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
*((byte *)pnt_configPage + (byte)valueOffset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
}
break;
case ignMapPage: //Ignition settings page (Page 2)
if (offset < 256) //New value is part of the ignition map
if (valueOffset < 256) //New value is part of the ignition map
{
ignitionTable.values[15 - offset / 16][offset % 16] = newValue;
ignitionTable.values[15 - valueOffset / 16][valueOffset % 16] = newValue;
return;
}
else
{
//Check whether this is on the X (RPM) or Y (MAP/TPS) axis
if (offset < 272)
if (valueOffset < 272)
{
//X Axis
ignitionTable.axisX[(offset - 256)] = (int)(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct
ignitionTable.axisX[(valueOffset - 256)] = (int)(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct
}
else
{
//Y Axis
offset = 15 - (offset - 272); //Need to do a translation to flip the order
ignitionTable.axisY[offset] = (int)(newValue);
valueOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order
ignitionTable.axisY[valueOffset] = (int)(newValue);
}
return;
}
@ -333,31 +333,31 @@ void receiveValue(int offset, byte newValue)
case ignSetPage:
pnt_configPage = &configPage2;
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if ( offset < page_size)
if (valueOffset < page_size)
{
*((byte *)pnt_configPage + (byte)offset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
*((byte *)pnt_configPage + (byte)valueOffset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
}
break;
case afrMapPage: //Air/Fuel ratio target settings page
if (offset < 256) //New value is part of the afr map
if (valueOffset < 256) //New value is part of the afr map
{
afrTable.values[15 - offset / 16][offset % 16] = newValue;
afrTable.values[15 - valueOffset / 16][valueOffset % 16] = newValue;
return;
}
else
{
//Check whether this is on the X (RPM) or Y (MAP/TPS) axis
if (offset < 272)
if (valueOffset < 272)
{
//X Axis
afrTable.axisX[(offset - 256)] = int(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiply it back by 100 to make it correct
afrTable.axisX[(valueOffset - 256)] = int(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiply it back by 100 to make it correct
}
else
{
//Y Axis
offset = 15 - (offset - 272); //Need to do a translation to flip the order
afrTable.axisY[offset] = int(newValue);
valueOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order
afrTable.axisY[valueOffset] = int(newValue);
}
return;
@ -366,52 +366,52 @@ void receiveValue(int offset, byte newValue)
case afrSetPage:
pnt_configPage = &configPage3;
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if ( offset < page_size)
if (valueOffset < page_size)
{
*((byte *)pnt_configPage + (byte)offset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
*((byte *)pnt_configPage + (byte)valueOffset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
}
break;
case iacPage: //Idle Air Control settings page (Page 4)
pnt_configPage = &configPage4;
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if ( offset < page_size)
if (valueOffset < page_size)
{
*((byte *)pnt_configPage + (byte)offset) = newValue;
*((byte *)pnt_configPage + (byte)valueOffset) = newValue;
}
break;
case boostvvtPage: //Boost and VVT maps (8x8)
if (offset < 64) //New value is part of the boost map
if (valueOffset < 64) //New value is part of the boost map
{
boostTable.values[7 - offset / 8][offset % 8] = newValue;
boostTable.values[7 - valueOffset / 8][valueOffset % 8] = newValue;
return;
}
else if (offset < 72) //New value is on the X (RPM) axis of the boost table
else if (valueOffset < 72) //New value is on the X (RPM) axis of the boost table
{
boostTable.axisX[(offset - 64)] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct
boostTable.axisX[(valueOffset - 64)] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct
return;
}
else if (offset < 80) //New value is on the Y (TPS) axis of the boost table
else if (valueOffset < 80) //New value is on the Y (TPS) axis of the boost table
{
boostTable.axisY[(7 - (offset - 72))] = int(newValue);
boostTable.axisY[(7 - (valueOffset - 72))] = int(newValue);
return;
}
else if (offset < 144) //New value is part of the vvt map
else if (valueOffset < 144) //New value is part of the vvt map
{
offset = offset - 80;
vvtTable.values[7 - offset / 8][offset % 8] = newValue;
valueOffset = valueOffset - 80;
vvtTable.values[7 - valueOffset / 8][valueOffset % 8] = newValue;
return;
}
else if (offset < 152) //New value is on the X (RPM) axis of the vvt table
else if (valueOffset < 152) //New value is on the X (RPM) axis of the vvt table
{
offset = offset - 144;
vvtTable.axisX[offset] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct
valueOffset = valueOffset - 144;
vvtTable.axisX[valueOffset] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct
return;
}
else //New value is on the Y (Load) axis of the vvt table
{
offset = offset - 152;
vvtTable.axisY[(7 - offset)] = int(newValue);
valueOffset = valueOffset - 152;
vvtTable.axisY[(7 - valueOffset)] = int(newValue);
return;
}
default:
@ -777,7 +777,7 @@ This function is used to store calibration data sent by Tuner Studio.
void receiveCalibration(byte tableID)
{
byte* pnt_TargetTable; //Pointer that will be used to point to the required target table
int OFFSET, DIVISION_FACTOR, BYTES_PER_VALUE;
int OFFSET, DIVISION_FACTOR, BYTES_PER_VALUE, EEPROM_START;
switch (tableID)
{
@ -787,6 +787,7 @@ void receiveCalibration(byte tableID)
OFFSET = CALIBRATION_TEMPERATURE_OFFSET; //
DIVISION_FACTOR = 10;
BYTES_PER_VALUE = 2;
EEPROM_START = EEPROM_CALIBRATION_CLT;
break;
case 1:
//Inlet air temp table
@ -794,6 +795,7 @@ void receiveCalibration(byte tableID)
OFFSET = CALIBRATION_TEMPERATURE_OFFSET;
DIVISION_FACTOR = 10;
BYTES_PER_VALUE = 2;
EEPROM_START = EEPROM_CALIBRATION_IAT;
break;
case 2:
//O2 table
@ -801,6 +803,7 @@ void receiveCalibration(byte tableID)
OFFSET = 0;
DIVISION_FACTOR = 1;
BYTES_PER_VALUE = 1;
EEPROM_START = EEPROM_CALIBRATION_O2;
break;
default:
@ -848,7 +851,10 @@ void receiveCalibration(byte tableID)
}
pnt_TargetTable[(x / 2)] = (byte)tempValue;
int y = EEPROM_CALIBRATION_O2 + counter;
//From TS3.x onwards, the EEPROM must be written here as TS restarts immediately after the process completes which is before the EEPROM write completes
int y = EEPROM_START + (x / 2);
EEPROM.update(y, (byte)tempValue);
every2nd = false;
analogWrite(13, (counter % 50) );

View File

@ -15,7 +15,7 @@ volatile unsigned long toothLastSecToothTime = 0; //The time (micros()) that the
volatile unsigned long toothLastMinusOneToothTime = 0; //The time (micros()) that the tooth before the last tooth was registered
volatile unsigned long toothOneTime = 0; //The time (micros()) that tooth 1 last triggered
volatile unsigned long toothOneMinusOneTime = 0; //The 2nd to last time (micros()) that tooth 1 last triggered
volatile bool revolutionOne; // For sequential operation, this tracks whether the current revolution is 1 or 2 (not 1)
volatile bool revolutionOne = 0; // For sequential operation, this tracks whether the current revolution is 1 or 2 (not 1)
volatile unsigned int toothHistory[TOOTH_LOG_BUFFER];
volatile unsigned int toothHistoryIndex = 0;

View File

@ -107,6 +107,7 @@ void triggerPri_missingTooth()
if ( curGap > targetGap || toothCurrentCount > triggerActualTeeth)
{
toothCurrentCount = 1;
revolutionOne = !revolutionOne; //Flip sequential revolution tracker
toothOneMinusOneTime = toothOneTime;
toothOneTime = curTime;
currentStatus.hasSync = true;
@ -123,7 +124,10 @@ void triggerPri_missingTooth()
toothLastToothTime = curTime;
}
void triggerSec_missingTooth(){ return; } //This function currently is not used
void triggerSec_missingTooth()
{
if(!currentStatus.hasSync) { revolutionOne = 0; } //Sequential revolution reset
}
int getRPM_missingTooth()
{
@ -147,6 +151,9 @@ int getCrankAngle_missingTooth(int timePerDegree)
if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime
else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; }
//Sequential check (simply sets whether we're on the first or 2nd revoltuion of the cycle)
if (revolutionOne) { crankAngle += 360; }
if (crankAngle >= 720) { crankAngle -= 720; }
else if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; }
if (crankAngle < 0) { crankAngle += 360; }

View File

@ -55,9 +55,15 @@ const byte packetSize = 35;
#define TOOTH_LOG_SIZE 128
#define TOOTH_LOG_BUFFER 256
#define INJ_SIMULTANEOUS 0
#define INJ_PAIRED 0
#define INJ_SEMISEQUENTIAL 1
#define INJ_SEQUENTIAL 2
#define INJ_BANKED 2
#define INJ_SEQUENTIAL 3
#define IGN_MODE_WASTED 0
#define IGN_MODE_SINGLE 1
#define IGN_MODE_WASTEDCOP 2
#define IGN_MODE_SEQUENTIAL 3
#define SIZE_BYTE 8
#define SIZE_INT 16
@ -141,7 +147,7 @@ struct statuses {
unsigned int PW; //In uS
volatile byte runSecs; //Counter of seconds since cranking commenced (overflows at 255 obviously)
volatile byte secl; //Continous
volatile int loopsPerSecond;
volatile unsigned int loopsPerSecond;
boolean launchingSoft; //True when in launch control soft limit mode
boolean launchingHard; //True when in launch control hard limit mode
int freeRAM;
@ -215,7 +221,7 @@ struct config1 {
byte algorithm : 1; //"Speed Density", "Alpha-N"
byte baroCorr : 1;
byte injLayout : 2;
byte canenable : 1; //is can interface enabled
byte canEnable : 1; //is can interface enabled
byte primePulse;
byte dutyLim;

3
idle.h
View File

@ -36,4 +36,5 @@ long idle_pwm_target_value;
long idle_cl_target_rpm;
void initialiseIdle();
static inline void disableIdle();
static inline void enableIdle();

View File

@ -53,7 +53,7 @@ void initialiseIdle()
idle2_pin_port = portOutputRegister(digitalPinToPort(pinIdle2));
idle2_pin_mask = digitalPinToBitMask(pinIdle2);
idle_pwm_max_count = 1000000L / (16 * configPage3.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz
TIMSK4 |= (1 << OCIE4C); //Turn on the C compare unit (ie turn on the interrupt)
enableIdle();
break;
case 3:
@ -141,8 +141,8 @@ void idleControl()
{
//Standard running
currentStatus.idleDuty = table2D_getValue(&iacPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees
if( currentStatus.idleDuty == 0 ) { TIMSK4 &= ~(1 << OCIE4C); digitalWrite(pinIdle1, LOW); break; }
TIMSK4 |= (1 << OCIE4C); //Turn on the C compare unit (ie turn on the interrupt)
if( currentStatus.idleDuty == 0 ) { disableIdle(); break; }
enableIdle();
idle_pwm_target_value = percentage(currentStatus.idleDuty, idle_pwm_max_count);
idleOn = true;
}
@ -154,8 +154,8 @@ void idleControl()
//idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD);
idlePID.Compute();
if( idle_pwm_target_value == 0 ) { TIMSK4 &= ~(1 << OCIE4C); digitalWrite(pinIdle1, LOW); }
else{ TIMSK4 |= (1 << OCIE4C); } //Turn on the C compare unit (ie turn on the interrupt)
if( idle_pwm_target_value == 0 ) { disableIdle(); }
else{ enableIdle(); } //Turn on the C compare unit (ie turn on the interrupt)
//idle_pwm_target_value = 104;
break;
@ -240,6 +240,20 @@ void homeStepper()
//The interrupt to turn off the idle pwm
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
//This function simply turns off the idle PWM and sets the pin low
static inline void disableIdle()
{
TIMSK4 &= ~(1 << OCIE4C); //Turn off interrupt
digitalWrite(pinIdle1, LOW);
}
//Any common functions associated with starting the Idle
//Typically this is enabling the PWM interrupt
static inline void enableIdle()
{
TIMSK4 |= (1 << OCIE4C); //Turn on the C compare unit (ie turn on the interrupt)
}
ISR(TIMER4_COMPC_vect)
{
if (idle_pwm_state)
@ -279,6 +293,12 @@ ISR(TIMER4_COMPC_vect)
}
}
#elif defined(PROCESSOR_TEENSY_3_1) || defined(PROCESSOR_TEENSY_3_2)
void idle_off() { }
#elif defined (CORE_TEENSY)
//This function simply turns off the idle PWM and sets the pin low
static inline void disableIdle()
{
digitalWrite(pinIdle1, LOW);
}
static inline void enableIdle() { }
#endif

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<msq xmlns="http://www.msefi.com/:msq">
<bibliography author="TunerStudio MS(Beta) 3.0.08 - EFI Analytics, Inc." tuneComment="" writeDate="Mon Sep 26 22:13:44 BST 2016"/>
<versionInfo fileFormat="5.0" firmwareInfo="Speeduino+2016.08" nPages="8" signature="speeduino 201609-dev"/>
<bibliography author="TunerStudio MS(Beta) 3.0.10.04 - EFI Analytics, Inc." tuneComment="" writeDate="Fri Sep 30 22:35:03 AEST 2016"/>
<versionInfo fileFormat="5.0" firmwareInfo="Speeduino+2016.09" nPages="8" signature="speeduino 201609"/>
<page>
<pcVariable name="tsCanId">"0"</pcVariable>
</page>
@ -79,7 +79,7 @@
100.0
</constant>
<constant digits="0" name="crankingPct" units="%">20.0</constant>
<constant name="pinLayout">"Speeduino v0.3"</constant>
<constant name="pinLayout">"Speeduino v0.4"</constant>
<constant name="tachoPin">"Board Default"</constant>
<constant name="unused2-16f">"One"</constant>
<constant digits="1" name="tdePct" units="ms">3.2</constant>
@ -94,7 +94,7 @@
<constant name="display5">"RPM"</constant>
<constant name="displayB1">"RPM"</constant>
<constant name="displayB2">"RPM"</constant>
<constant digits="1" name="reqFuel" units="ms">14.4</constant>
<constant digits="1" name="reqFuel" units="ms">12.8</constant>
<constant digits="0" name="divider">2.0</constant>
<constant name="alternate">"Alternating"</constant>
<constant name="multiplyMAP">"No"</constant>
@ -116,7 +116,6 @@
<constant name="algorithm">"Speed Density"</constant>
<constant name="baroCorr">"Off"</constant>
<constant name="injLayout">"Bank"</constant>
<constant name="canenable">"Enable"</constant>
<constant digits="1" name="primePulse" units="ms">4.0</constant>
<constant digits="0" name="dutyLim" units="%">85.0</constant>
<constant digits="0" name="unused41" units="RPM">4200.0</constant>
@ -128,6 +127,9 @@
<constant digits="0" name="mapMax" units="kpa">260.0</constant>
<constant digits="0" name="fpPrime" units="s">3.0</constant>
<constant digits="1" name="stoich" units=":1">14.7</constant>
<constant digits="0" name="oddfire2">0.0</constant>
<constant digits="0" name="oddfire3">0.0</constant>
<constant digits="0" name="oddfire4">0.0</constant>
<constant digits="0" name="unused2-57" units="RPM">0.0</constant>
<constant digits="0" name="unused2-58" units="RPM">0.0</constant>
<constant digits="0" name="unused2-59" units="RPM">0.0</constant>
@ -196,16 +198,14 @@
<constant digits="0" name="TrigAng" units="Deg">0.0</constant>
<constant digits="0" name="FixAng" units="Deg">0.0</constant>
<constant digits="0" name="CrankAng" units="Deg">5.0</constant>
<constant digits="0" name="IgHold">10.0</constant>
<constant digits="0" name="TrigAngMul">4.0</constant>
<constant name="TrigEdge">"Leading"</constant>
<constant name="TrigSpeed">"Crank Speed"</constant>
<constant name="IgInv">"Going Low"</constant>
<constant name="oddfire">"Yes"</constant>
<constant name="TrigPattern">"Missing Tooth"</constant>
<constant digits="0" name="IdleAdv" units="Deg">-1.9008</constant>
<constant digits="0" name="IdleAdvTPS" units="ADC">65.0</constant>
<constant digits="0" name="IdleAdvRPM" units="RPM">3200.0</constant>
<constant digits="0" name="IdleAdvCLT" units="F">-5.814</constant>
<constant digits="1" name="IdleAdvCLT" units="C">-21.0</constant>
<constant digits="0" name="IdleDelayTime" units="sec">38.0</constant>
<constant digits="0" name="StgCycles" units="cycles">2.0</constant>
<constant name="dwellcont">"Dwell control"</constant>
@ -236,17 +236,17 @@
17.0
32.0
</constant>
<constant cols="1" digits="0" name="wueBins" rows="10" units="F">
-40.014
-14.814
17.586
48.186
78.786
100.386
120.186
139.986
156.186
175.986
<constant cols="1" digits="0" name="wueBins" rows="10" units="C">
-40.0
-26.0
-8.0
9.0
26.0
38.0
49.0
60.0
69.0
80.0
</constant>
<constant digits="0" name="dwellLim" units="ms">8.0</constant>
<constant cols="1" digits="0" name="dwellRates" rows="6" units="%">
@ -257,13 +257,13 @@
91.0
85.0
</constant>
<constant cols="1" digits="0" name="iatRetBins" rows="6" units="F">
136.386
179.586
199.386
219.186
240.786
256.986
<constant cols="1" digits="0" name="iatRetBins" rows="6" units="C">
58.0
82.0
93.0
104.0
116.0
140.0
</constant>
<constant cols="1" digits="0" name="iatRetRates" rows="6" units="deg">
0.0
@ -271,7 +271,7 @@
2.0
4.0
6.0
8.0
10.0
</constant>
<constant digits="0" name="dfcoRPM" units="RPM">1500.0</constant>
<constant digits="0" name="dfcoHyster" units="RPM">200.0</constant>
@ -345,7 +345,7 @@
<constant digits="0" name="egoKP" units="%">100.0</constant>
<constant digits="0" name="egoKI" units="%">20.0</constant>
<constant digits="0" name="egoKD" units="%">0.0</constant>
<constant digits="0" name="egoTemp" units="°F">157.986</constant>
<constant digits="0" name="egoTemp" units="C">70.0</constant>
<constant digits="0" name="egoCount">16.0</constant>
<constant digits="0" name="egoDelta" units="%">1.0</constant>
<constant digits="0" name="egoLimit">15.0</constant>
@ -372,16 +372,16 @@
100.0
98.0
</constant>
<constant cols="1" digits="0" name="airDenBins" rows="9" units="F">
-40.014
-4.014
31.986
67.986
94.986
121.986
139.986
193.986
247.986
<constant cols="1" digits="0" name="airDenBins" rows="9" units="C">
-40.0
-20.0
0.0
20.0
35.0
50.0
60.0
90.0
120.0
</constant>
<constant cols="1" digits="0" name="airDenRates" rows="9" units="%">
126.0
@ -411,7 +411,8 @@
<constant digits="0" name="boostKP" units="%">100.0</constant>
<constant digits="0" name="boostKI" units="%">0.0</constant>
<constant digits="0" name="boostKD" units="%">0.0</constant>
<constant digits="0" name="unused6-60" units="RPM">24300.0</constant>
<constant name="lnchPullRes">"Float"</constant>
<constant name="unused6-60">"ONE"</constant>
<constant digits="0" name="unused6-61" units="RPM">4500.0</constant>
<constant digits="0" name="unused6-62" units="RPM">3000.0</constant>
<constant digits="0" name="unused6-63" units="RPM">6000.0</constant>
@ -453,17 +454,17 @@
16.0
9.0
</constant>
<constant cols="1" digits="0" name="iacBins" rows="10" units="F">
-36.414
-2.214
33.786
62.586
93.186
121.986
145.386
174.186
208.386
289.386
<constant cols="1" digits="0" name="iacBins" rows="10" units="C">
-38.0
-19.0
1.0
17.0
34.0
50.0
63.0
79.0
98.0
143.0
</constant>
<constant cols="1" digits="0" name="iacCrankSteps" rows="4" units="Steps">
123.0
@ -477,17 +478,17 @@
44.0
60.0
</constant>
<constant cols="1" digits="0" name="iacCrankBins" rows="4" units="F">
-18.414
42.786
111.186
168.786
<constant cols="1" digits="0" name="iacCrankBins" rows="4" units="C">
-28.0
6.0
44.0
76.0
</constant>
<constant name="iacAlgorithm">"None"</constant>
<constant name="iacStepTime">"3"</constant>
<constant name="iacChannels">"1"</constant>
<constant name="iacPWMdir">"Normal"</constant>
<constant digits="0" name="iacFastTemp" units="F">67.986</constant>
<constant digits="0" name="iacFastTemp" units="C">20.0</constant>
<constant digits="0" name="iacStepHome" units="Steps">240.0</constant>
<constant digits="0" name="iacStepHyster" units="Steps">4.0</constant>
<constant name="fanInv">"No"</constant>
@ -497,14 +498,14 @@
<constant name="unused7-55d">"No"</constant>
<constant name="unused7-55e">"No"</constant>
<constant name="unused7-55f">"No"</constant>
<constant digits="0" name="fanSP" units="°F">166.986</constant>
<constant digits="0" name="fanHyster" units="°F">35.586</constant>
<constant digits="0" name="fanSP" units="C">75.0</constant>
<constant digits="0" name="fanHyster" units="C">2.0</constant>
<constant digits="0" name="fanFreq" units="Hz">6.0</constant>
<constant cols="1" digits="0" name="fanPWMBins" rows="4" units="F">
139.986
-4.014
-40.014
316.386
<constant cols="1" digits="0" name="fanPWMBins" rows="4" units="C">
60.0
-20.0
-40.0
158.0
</constant>
</page>
<page number="7" size="160">
@ -570,9 +571,9 @@
</constant>
</page>
<settings Comment="These setting are only used if this msq is opened without a project.">
<setting name="FAHRENHEIT" value="FAHRENHEIT"/>
<setting name="SPEED_DENSITY" value="SPEED_DENSITY"/>
<setting name="CAN_COMMANDS_OFF" value="CAN_COMMANDS_OFF"/>
<setting name="CELSIUS" value="CELSIUS"/>
</settings>
<userComments Comment="These are user comments that can be related to a particular setting or dialog."/>
</msq>
</msq>

View File

@ -6,7 +6,7 @@
queryCommand = "Q"
;signature = 20
signature = "speeduino 201609-dev"
signature = "speeduino 201610-dev"
versionInfo = "S" ; Put this in the title bar.
@ -160,7 +160,6 @@ page = 2
reqFuel = scalar, U08, 24, "ms", 0.1, 0.0, 0.0, 25.5, 1
divider = scalar, U08, 25, "", 1.0, 0.0
;injTiming = bits, U08, 26, [0:1], "Simultaneous", "Semi-Sequential", "Sequential"
alternate = bits, U08, 26, [0:0], "Simultaneous", "Alternating"
multiplyMAP= bits, U08, 26, [1:1], "No", "Yes"
includeAFR = bits, U08, 26, [2:2], "No", "Yes"
@ -186,8 +185,8 @@ page = 2
flexEnabled= bits, U08, 38, [1:1], "Off", "On"
algorithm = bits, U08, 38, [2:2], "Speed Density", "Alpha-N"
baroCorr = bits, U08, 38, [3:3], "Off", "On"
injLayout = bits, U08, 38, [4:5], "Bank", "Semi-Sequential", "INVALID", "INVALID"
canenable = bits, U08, 38, [6:6], "Disable", "Enable"
injLayout = bits, U08, 38, [4:5], "Paired", "Semi-Sequential", "INVALID", "Sequential"
canEnable = bits, U08, 38, [6:6], "Disable", "Enable"
primePulse = scalar, U08, 39, "ms", 0.1, 0.0, 0.0, 25.5, 1
dutyLim = scalar, U08, 40, "%", 1.0, 0.0, 0.0, 100.0, 0
@ -336,9 +335,9 @@ page = 6
egoKI = scalar, U08, 2, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
egoKD = scalar, U08, 3, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
#if CELSIUS
egoTemp = scalar, U08, 4, "°C", 1.0, -40, -40, 102.0, 0
egoTemp = scalar, U08, 4, "C", 1.0, -40, -40, 102.0, 0
#else
egoTemp = scalar, U08, 4, "°F", 1.8, -22.23, -40, 215.0, 0
egoTemp = scalar, U08, 4, "F", 1.8, -22.23, -40, 215.0, 0
#endif
egoCount = scalar, U08, 5, "", 4.0, 0.0, 4.0, 255.0, 0 ; * ( 1 byte)
egoDelta = scalar, U08, 6, "%", 1.0, 0.0, 0.0, 255.0, 0 ; * ( 1 byte)
@ -431,11 +430,11 @@ page = 7
unused7-55e = bits, U08, 56, [6:6], "No", "Yes"
unused7-55f = bits, U08, 56, [7:7], "No", "Yes"
#if CELSIUS
fanSP = scalar, U08, 57, "°C", 1.0, -40, -40, 215.0, 0
fanHyster = scalar, U08, 58, "°C", 1.0, -40, -40, 215.0, 0
fanSP = scalar, U08, 57, "C", 1.0, -40, -40, 215.0, 0
fanHyster = scalar, U08, 58, "C", 1.0, -40, -40, 215.0, 0
#else
fanSP = scalar, U08, 57, "°F", 1.8, -22.23, -40, 215.0, 0
fanHyster = scalar, U08, 58, "°F", 1.8, -22.23, -40, 215.0, 0
fanSP = scalar, U08, 57, "F", 1.8, -22.23, -40, 215.0, 0
fanHyster = scalar, U08, 58, "F", 1.8, -22.23, -40, 215.0, 0
#endif
fanFreq = scalar, U08 , 59, "Hz", 2.0, 0.0, 10, 511, 0
#if CELSIUS
@ -590,8 +589,8 @@ menuDialog = main
subMenu = vvtTbl, "VVT duty cycle", 8, { vvtEnabled }
subMenu = std_separator
subMenu = tacho, "Tacho Output"
subMenu = std_separator
subMenu = canio, "Canbus Interface"
subMenu = std_separator
subMenu = canIO, "Canbus Interface"
@ -619,11 +618,13 @@ menuDialog = main
; tool tips tooltips
;Ensure all settings are defined as some MS2/BG words shipped with TS are not applicable.
nCylinders = "The number of cylinders in your engine."
alternate = ""
engineType = "Most engines are Even Fire. Typical odd-fire engines are V-twin, some V4, Vmax, some V6, V10."
twoStroke = "Four-Stroke (most engines), Two-stroke."
nInjectors = "Number of primary injectors."
mapSample = "The method used for calculating the MAP reading\nFor 1-2 Cylinder engines, Cycle Minimum is recommended.\nFor more than 2 cylinders Cycle Average is recommended"
stoich = "The stoichiometric ration of the fuel being used. For flex fuel, choose the primary fuel"
injLayout = "The injector layout and timing to be used. Options are: \n 1. Paired - 2 injectors per output. Outputs active is equal to half the number of cylinders. Outputs are timed over 1 crank revolution. \n 2. Semi-sequential: Same as paired except that injector channels are mirrored (1&4, 2&3) meaning the number of outputs used are equal to the number of cylinders. Only valid for 4 cylinders or less. \n 3. Banked: 2 outputs only used. \n 4. Sequential: 1 injector per output and outputs used equals the number of cylinders. Injection is timed over full cycle.
TrigPattern = "The type of input trigger decoder to be used."
numteeth = "Number of teeth on Primary Wheel."
@ -716,8 +717,8 @@ menuDialog = main
dialog = tacho, "Tacho"
field = "Output pin", tachoPin
dialog = canio, "CanBus interface"
field = "Enable/Disable", canenable
dialog = canIO, "CanBus interface"
field = "Enable/Disable", canEnable
dialog = accelEnrichments_center, ""
field = "TPSdot Threshold", tpsThresh
@ -732,12 +733,18 @@ menuDialog = main
field = "Cutoff RPM", dfcoRPM, { dfcoEnabled }
field = "RPM Hysteresis", dfcoHyster, { dfcoEnabled }
dialog = accelEnrichments_north_south, ""
liveGraph = pump_ae_Graph, "AE Graph"
graphLine = afr
graphLine = TPSdot, "%", -2000, 2000, auto, auto
dialog = accelEnrichments_north, "", xAxis
panel = time_accel_tpsdot_curve
;panel = time_accel_tpsdot_tbl
dialog = accelEnrichments, "Acceleration Enrichment"
panel = accelEnrichments_north, North
panel = accelEnrichments_north_south, Center
panel = accelEnrichments_center, Center
panel = accelEnrichments_south, South
@ -1189,7 +1196,7 @@ menuDialog = main
gaugeCategory = "Other"
clockGauge = secl, "Clock", "Seconds", 0, 255, 10, 10, 245, 245, 0, 0
deadGauge = deadValue, "---", "", 0, 1, -1, -1, 2, 2, 0, 0
loopGauge = loopsPerSecond,"Main loop speed", "Loops/S" , 0, 20000, -1, 500,1800, 4000, 0, 0
loopGauge = loopsPerSecond,"Main loop speed", "Loops/S" , 0, 70000, -1, 500,1800, 4000, 0, 0
memoryGauge = freeRAM, "Free memory", "bytes" , 0, 8000, -1, 1000,8000, 1000, 0, 0
;-------------------------------------------------------------------------------
@ -1356,7 +1363,7 @@ menuDialog = main
TPSdot = scalar, U08, 21, "%/s", 10.00, 0.000
advance = scalar, U08, 22, "deg", 1.000, 0.000
tps = scalar, U08, 23, "%", 1.000, 0.000
loopsPerSecond = scalar, S16, 24, "loops", 1.000, 0.000
loopsPerSecond = scalar, U16, 24, "loops", 1.000, 0.000
freeRAM = scalar, S16, 26, "bytes", 1.000, 0.000
batCorrection = scalar, U08, 28, "%", 1.000, 0.000
spark = scalar, U08, 29, "bits", 1.000, 0.000

View File

@ -32,6 +32,91 @@ See page 136 of the processors datasheet: http://www.atmel.com/Images/doc2549.pd
#include <avr/io.h>
#endif
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
//Refer to http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/include/avr/iomxx0_1.h?root=avr-libc&view=markup
#define FUEL1_COUNTER TCNT3
#define FUEL2_COUNTER TCNT3
#define FUEL3_COUNTER TCNT3
#define FUEL4_COUNTER TCNT4
#define IGN1_COUNTER TCNT5
#define IGN2_COUNTER TCNT5
#define IGN3_COUNTER TCNT5
#define IGN4_COUNTER TCNT4
#define FUEL1_COMPARE OCR3A
#define FUEL2_COMPARE OCR3B
#define FUEL3_COMPARE OCR3C
#define FUEL4_COMPARE OCR4B
#define IGN1_COMPARE OCR5A
#define IGN2_COMPARE OCR5B
#define IGN3_COMPARE OCR5C
#define IGN4_COMPARE OCR4A
#define FUEL1_TIMER_ENABLE() TIMSK3 |= (1 << OCIE3A) //Turn on the A compare unit (ie turn on the interrupt)
#define FUEL2_TIMER_ENABLE() TIMSK3 |= (1 << OCIE3B) //Turn on the B compare unit (ie turn on the interrupt)
#define FUEL3_TIMER_ENABLE() TIMSK3 |= (1 << OCIE3C) //Turn on the C compare unit (ie turn on the interrupt)
#define FUEL4_TIMER_ENABLE() TIMSK4 |= (1 << OCIE4B) //Turn on the B compare unit (ie turn on the interrupt)
#define FUEL1_TIMER_DISABLE() TIMSK3 &= ~(1 << OCIE3A); //Turn off this output compare unit
#define FUEL2_TIMER_DISABLE() TIMSK3 &= ~(1 << OCIE3B); //Turn off this output compare unit
#define FUEL3_TIMER_DISABLE() TIMSK3 &= ~(1 << OCIE3C); //Turn off this output compare unit
#define FUEL4_TIMER_DISABLE() TIMSK4 &= ~(1 << OCIE4B); //Turn off this output compare unit
#define IGN1_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5A) //Turn on the A compare unit (ie turn on the interrupt)
#define IGN2_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5B) //Turn on the B compare unit (ie turn on the interrupt)
#define IGN3_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5C) //Turn on the C compare unit (ie turn on the interrupt)
#define IGN4_TIMER_ENABLE() TIMSK4 |= (1 << OCIE4A) //Turn on the A compare unit (ie turn on the interrupt)
#define IGN1_TIMER_DISABLE() TIMSK5 &= ~(1 << OCIE5A) //Turn off this output compare unit
#define IGN2_TIMER_DISABLE() TIMSK5 &= ~(1 << OCIE5B) //Turn off this output compare unit
#define IGN3_TIMER_DISABLE() TIMSK5 &= ~(1 << OCIE5C) //Turn off this output compare unit
#define IGN4_TIMER_DISABLE() TIMSK4 &= ~(1 << OCIE4A) //Turn off this output compare unit
#elif defined(CORE_TEENSY)
//http://shawnhymel.com/661/learning-the-teensy-lc-interrupt-service-routines/
#define FUEL1_COUNTER FTM0_CNT
#define FUEL2_COUNTER FTM0_CNT
#define FUEL3_COUNTER FTM0_CNT
#define FUEL4_COUNTER FTM0_CNT
#define IGN1_COUNTER FTM0_CNT
#define IGN2_COUNTER FTM0_CNT
#define IGN3_COUNTER FTM0_CNT
#define IGN4_COUNTER FTM0_CNT
#define FUEL1_COMPARE FTM0_C0V
#define FUEL2_COMPARE FTM0_C1V
#define FUEL3_COMPARE FTM0_C2V
#define FUEL4_COMPARE FTM0_C3V
#define IGN1_COMPARE FTM0_C4V
#define IGN2_COMPARE FTM0_C5V
#define IGN3_COMPARE FTM0_C6V
#define IGN4_COMPARE FTM0_C7V
#define FUEL1_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL2_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL3_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL4_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL1_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL2_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL3_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define FUEL4_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN1_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN2_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN3_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN4_TIMER_ENABLE() NVIC_ENABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN1_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN2_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN3_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#define IGN4_TIMER_DISABLE() NVIC_DISABLE_IRQ(IRQ_FTM1) //THIS IS NOT RIGHT! PLACEHOLDER ONLY!
#endif
void initialiseSchedulers();
void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
@ -63,7 +148,7 @@ struct Schedule {
unsigned int endCompare;
};
Schedule *timer3Aqueue[4];
volatile Schedule *timer3Aqueue[4];
Schedule *timer3Bqueue[4];
Schedule *timer3Cqueue[4];
@ -86,27 +171,50 @@ Schedule ignitionSchedule8;
Schedule nullSchedule; //This is placed at the end of the queue. It's status will always be set to OFF and hence will never perform any action within an ISR
static inline unsigned int setQueue(Schedule *queue[], Schedule *schedule1, Schedule *schedule2, unsigned int CNT)
static inline unsigned int setQueue(volatile Schedule *queue[], Schedule *schedule1, Schedule *schedule2, unsigned int CNT)
{
//Create an array of all the upcoming targets, relative to the current count on the timer
unsigned int tmpQueue[4];
tmpQueue[0] = schedule1->startCompare - CNT;
tmpQueue[1] = schedule1->endCompare - CNT;
tmpQueue[2] = schedule2->startCompare - CNT;
tmpQueue[3] = schedule2->endCompare - CNT;
//Set the initial queue state. This order matches the tmpQueue order
queue[0] = schedule1;
queue[1] = schedule1;
queue[2] = schedule2;
queue[3] = schedule2;
if(schedule1->Status == OFF)
{
queue[0] = schedule2;
queue[1] = schedule2;
tmpQueue[0] = schedule2->startCompare - CNT;
tmpQueue[1] = schedule2->endCompare - CNT;
}
else
{
queue[0] = schedule1;
queue[1] = schedule1;
tmpQueue[0] = schedule1->startCompare - CNT;
tmpQueue[1] = schedule1->endCompare - CNT;
}
if(schedule2->Status == OFF)
{
queue[2] = schedule1;
queue[3] = schedule1;
tmpQueue[2] = schedule1->startCompare - CNT;
tmpQueue[3] = schedule1->endCompare - CNT;
}
else
{
queue[2] = schedule2;
queue[3] = schedule2;
tmpQueue[2] = schedule2->startCompare - CNT;
tmpQueue[3] = schedule2->endCompare - CNT;
}
//Sort the queues. Both queues are kept in sync.
//This implementes a sorting networking based on the Bose-Nelson swap algorithm
//See:
#define SWAP(x,y) if(tmpQueue[y] < tmpQueue[x]) { unsigned int tmp = tmpQueue[x]; tmpQueue[x] = tmpQueue[y]; tmpQueue[y] = tmp; Schedule *tmpS = queue[x]; queue[x] = queue[y]; queue[y] = tmpS; }
//This implementes a sorting networking based on the Bose-Nelson sorting network
//See: http://pages.ripco.net/~jgamble/nw.html
#define SWAP(x,y) if(tmpQueue[y] < tmpQueue[x]) { unsigned int tmp = tmpQueue[x]; tmpQueue[x] = tmpQueue[y]; tmpQueue[y] = tmp; volatile Schedule *tmpS = queue[x]; queue[x] = queue[y]; queue[y] = tmpS; }
//SWAP(0, 1); //Likely not needed
//SWAP(2, 3); //Likely not needed
SWAP(0, 2);
SWAP(1, 3);
SWAP(1, 2);
@ -119,7 +227,7 @@ static inline unsigned int setQueue(Schedule *queue[], Schedule *schedule1, Sche
* The current item (0) is discarded
* The final queue slot is set to nullSchedule to indicate that no action should be taken
*/
static inline unsigned int popQueue(Schedule *queue[])
static inline unsigned int popQueue(volatile Schedule *queue[])
{
queue[0] = queue[1];
queue[1] = queue[2];

View File

@ -10,7 +10,8 @@ A full copy of the license may be found in the projects root directory
void initialiseSchedulers()
{
nullSchedule.Status = OFF;
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
// Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html
//Fuel Schedules, which uses timer 3
TCCR3B = 0x00; //Disable Timer3 while we set it up
@ -19,13 +20,6 @@ void initialiseSchedulers()
TCCR3A = 0x00; //Timer3 Control Reg A: Wave Gen Mode normal
TCCR3B = (1 << CS12); //Timer3 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
//TCCR3B = 0x03; //Timer3 Control Reg B: Timer Prescaler set to 64. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
fuelSchedule1.Status = OFF;
fuelSchedule2.Status = OFF;
fuelSchedule3.Status = OFF;
fuelSchedule1.schedulesSet = 0;
fuelSchedule2.schedulesSet = 0;
fuelSchedule3.schedulesSet = 0;
//Ignition Schedules, which uses timer 5
TCCR5B = 0x00; //Disable Timer3 while we set it up
@ -34,26 +28,40 @@ void initialiseSchedulers()
TCCR5A = 0x00; //Timer5 Control Reg A: Wave Gen Mode normal
//TCCR5B = (1 << CS12); //Timer5 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TCCR5B = 0x03; //aka Divisor = 64 = 490.1Hz
ignitionSchedule1.Status = OFF;
ignitionSchedule2.Status = OFF;
ignitionSchedule3.Status = OFF;
ignitionSchedule1.schedulesSet = 0;
ignitionSchedule2.schedulesSet = 0;
ignitionSchedule3.schedulesSet = 0;
//The remaining Schedules (Schedules 4 for fuel and ignition) use Timer4
TCCR4B = 0x00; //Disable Timer4 while we set it up
TCNT4 = 0; //Reset Timer Count
TIFR4 = 0x00; //Timer4 INT Flag Reg: Clear Timer Overflow Flag
TCCR4A = 0x00; //Timer4 Control Reg A: Wave Gen Mode normal
TCCR4B = (1 << CS12); //Timer4 Control Reg B: aka Divisor = 256 = 122.5HzTimer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
ignitionSchedule4.Status = OFF;
fuelSchedule4.Status = OFF;
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
//Configure ARM timers here
#endif
ignitionSchedule4.schedulesSet = 0;
fuelSchedule1.Status = OFF;
fuelSchedule2.Status = OFF;
fuelSchedule3.Status = OFF;
fuelSchedule4.Status = OFF;
fuelSchedule5.Status = OFF;
fuelSchedule1.schedulesSet = 0;
fuelSchedule2.schedulesSet = 0;
fuelSchedule3.schedulesSet = 0;
fuelSchedule4.schedulesSet = 0;
//Note that timer4 compare channel C is used by the idle control
fuelSchedule5.schedulesSet = 0;
ignitionSchedule1.Status = OFF;
ignitionSchedule2.Status = OFF;
ignitionSchedule3.Status = OFF;
ignitionSchedule4.Status = OFF;
ignitionSchedule1.schedulesSet = 0;
ignitionSchedule2.schedulesSet = 0;
ignitionSchedule3.schedulesSet = 0;
ignitionSchedule4.schedulesSet = 0;
}
/*
@ -81,14 +89,14 @@ void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned l
* unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
*/
noInterrupts();
fuelSchedule1.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule1.startCompare = FUEL1_COUNTER + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule1.endCompare = fuelSchedule1.startCompare + (duration >> 4);
fuelSchedule1.Status = PENDING; //Turn this schedule on
fuelSchedule1.schedulesSet++; //Increment the number of times this schedule has been set
if(channel5InjEnabled) { OCR3A = setQueue(timer3Aqueue, &fuelSchedule1, &fuelSchedule5, TCNT3); } //Schedule 1 shares a timer with schedule 5
else { timer3Aqueue[0] = &fuelSchedule1; timer3Aqueue[1] = &fuelSchedule1; timer3Aqueue[2] = &fuelSchedule1; timer3Aqueue[3] = &fuelSchedule1; OCR3A = fuelSchedule1.startCompare; }
if(channel5InjEnabled) { FUEL1_COMPARE = setQueue(timer3Aqueue, &fuelSchedule1, &fuelSchedule5, FUEL1_COUNTER); } //Schedule 1 shares a timer with schedule 5
else { timer3Aqueue[0] = &fuelSchedule1; timer3Aqueue[1] = &fuelSchedule1; timer3Aqueue[2] = &fuelSchedule1; timer3Aqueue[3] = &fuelSchedule1; FUEL1_COMPARE = fuelSchedule1.startCompare; }
interrupts();
TIMSK3 |= (1 << OCIE3A); //Turn on the A compare unit (ie turn on the interrupt)
FUEL1_TIMER_ENABLE();
}
void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
@ -105,47 +113,59 @@ void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned l
* unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
*/
noInterrupts();
fuelSchedule2.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule2.startCompare = FUEL2_COUNTER + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule2.endCompare = fuelSchedule2.startCompare + (duration >> 4);
OCR3B = fuelSchedule2.startCompare; //Use the B copmare unit of timer 3
FUEL2_COMPARE = fuelSchedule2.startCompare; //Use the B copmare unit of timer 3
fuelSchedule2.Status = PENDING; //Turn this schedule on
fuelSchedule2.schedulesSet++; //Increment the number of times this schedule has been set
interrupts();
TIMSK3 |= (1 << OCIE3B); //Turn on the B compare unit (ie turn on the interrupt)
FUEL2_TIMER_ENABLE();
}
void setFuelSchedule3(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(fuelSchedule3.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
//unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as require
fuelSchedule3.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule3.endCompare = fuelSchedule3.startCompare + (duration >> 4);
OCR3C = fuelSchedule3.startCompare; //Use the C copmare unit of timer 3
fuelSchedule3.duration = duration;
fuelSchedule3.StartCallback = startCallback; //Name the start callback function
fuelSchedule3.EndCallback = endCallback; //Name the end callback function
fuelSchedule3.duration = duration;
/*
* The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set
* We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
* As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
* unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
*/
noInterrupts();
fuelSchedule3.startCompare = FUEL3_COUNTER + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule3.endCompare = fuelSchedule3.startCompare + (duration >> 4);
FUEL3_COMPARE = fuelSchedule3.startCompare; //Use the C copmare unit of timer 3
fuelSchedule3.Status = PENDING; //Turn this schedule on
fuelSchedule3.schedulesSet++; //Increment the number of times this schedule has been set
TIMSK3 |= (1 << OCIE3C); //Turn on the C compare unit (ie turn on the interrupt)
interrupts();
FUEL3_TIMER_ENABLE();
}
void setFuelSchedule4(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)()) //Uses timer 4 compare B
{
if(fuelSchedule4.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
//unsigned int absoluteTimeout = TCNT4 + (timeout / 4); //Each tick occurs every 4uS with the 128 prescaler, so divide the timeout by 4 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
fuelSchedule4.startCompare = TCNT4 + (timeout >> 4);
fuelSchedule4.endCompare = fuelSchedule4.startCompare + (duration >> 4);
OCR4B = fuelSchedule4.startCompare; //Use the C copmare unit of timer 3
fuelSchedule4.duration = duration;
fuelSchedule4.StartCallback = startCallback; //Name the start callback function
fuelSchedule4.EndCallback = endCallback; //Name the end callback function
fuelSchedule4.duration = duration;
/*
* The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set
* We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
* As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
* unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
*/
noInterrupts();
fuelSchedule4.startCompare = FUEL4_COUNTER + (timeout >> 4);
fuelSchedule4.endCompare = fuelSchedule4.startCompare + (duration >> 4);
FUEL4_COMPARE = fuelSchedule4.startCompare; //Use the C copmare unit of timer 3
fuelSchedule4.Status = PENDING; //Turn this schedule on
fuelSchedule4.schedulesSet++; //Increment the number of times this schedule has been set
TIMSK4 |= (1 << OCIE4B); //Turn on the B compare unit (ie turn on the interrupt)
interrupts();
FUEL4_TIMER_ENABLE();
}
void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
@ -161,6 +181,7 @@ void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned l
/*
* The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set
*/
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
noInterrupts();
fuelSchedule5.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule5.endCompare = fuelSchedule5.startCompare + (duration >> 4);
@ -169,80 +190,103 @@ void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned l
OCR3A = setQueue(timer3Aqueue, &fuelSchedule1, &fuelSchedule5, TCNT3); //Schedule 1 shares a timer with schedule 5
interrupts();
TIMSK3 |= (1 << OCIE3A); //Turn on the A compare unit (ie turn on the interrupt)
#endif
}
//Ignition schedulers use Timer 5
void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule1.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
ignitionSchedule1.StartCallback = startCallback; //Name the start callback function
ignitionSchedule1.EndCallback = endCallback; //Name the start callback function
ignitionSchedule1.duration = duration;
//As the timer is ticking every 4uS (Time per Tick = (Prescale)*(1/Frequency))
if (timeout > 262140) { timeout = 262100; } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking.
OCR5A = TCNT5 + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule1.duration = duration;
ignitionSchedule1.StartCallback = startCallback; //Name the start callback function
ignitionSchedule1.EndCallback = endCallback; //Name the start callback function
noInterrupts();
ignitionSchedule1.startCompare = IGN1_COUNTER + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule1.endCompare = ignitionSchedule1.startCompare + (duration >> 2);
IGN1_COMPARE = ignitionSchedule1.startCompare;
ignitionSchedule1.Status = PENDING; //Turn this schedule on
TIMSK5 |= (1 << OCIE5A); //Turn on the A compare unit (ie turn on the interrupt)
interrupts();
IGN1_TIMER_ENABLE();
}
void setIgnitionSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule2.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//As the timer is ticking every 4uS (Time per Tick = (Prescale)*(1/Frequency))
if (timeout > 262140) { timeout = 262100; } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when applied causing erratic behaviour such as erroneous sparking. This must be set slightly lower than the max of 262140 to avoid strangeness
OCR5B = TCNT5 + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule2.duration = duration;
ignitionSchedule2.StartCallback = startCallback; //Name the start callback function
ignitionSchedule2.EndCallback = endCallback; //Name the start callback function
ignitionSchedule2.duration = duration;
//As the timer is ticking every 4uS (Time per Tick = (Prescale)*(1/Frequency))
if (timeout > 262140) { timeout = 262100; } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when applied causing erratic behaviour such as erroneous sparking. This must be set slightly lower than the max of 262140 to avoid strangeness
noInterrupts();
ignitionSchedule2.startCompare = IGN2_COUNTER + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule2.endCompare = ignitionSchedule2.startCompare + (duration >> 2);
IGN2_COMPARE = ignitionSchedule2.startCompare;
ignitionSchedule2.Status = PENDING; //Turn this schedule on
TIMSK5 |= (1 << OCIE5B); //Turn on the B compare unit (ie turn on the interrupt)
interrupts();
IGN1_TIMER_ENABLE();
}
void setIgnitionSchedule3(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule3.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
ignitionSchedule3.StartCallback = startCallback; //Name the start callback function
ignitionSchedule3.EndCallback = endCallback; //Name the start callback function
ignitionSchedule3.duration = duration;
//The timer is ticking every 4uS (Time per Tick = (Prescale)*(1/Frequency))
if (timeout > 262140) { timeout = 262100; } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when applied causing erratic behaviour such as erroneous sparking. This must be set slightly lower than the max of 262140 to avoid strangeness
OCR5C = TCNT5 + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule3.duration = duration;
ignitionSchedule3.StartCallback = startCallback; //Name the start callback function
ignitionSchedule3.EndCallback = endCallback; //Name the start callback function
noInterrupts();
ignitionSchedule3.startCompare = IGN3_COUNTER + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule3.endCompare = ignitionSchedule3.startCompare + (duration >> 2);
IGN3_COMPARE = ignitionSchedule3.startCompare;
ignitionSchedule3.Status = PENDING; //Turn this schedule on
TIMSK5 |= (1 << OCIE5C); //Turn on the C compare unit (ie turn on the interrupt)
interrupts();
IGN3_TIMER_ENABLE();
}
void setIgnitionSchedule4(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule4.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
ignitionSchedule4.StartCallback = startCallback; //Name the start callback function
ignitionSchedule4.EndCallback = endCallback; //Name the start callback function
ignitionSchedule4.duration = duration;
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//The timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
//Note this is different to the other ignition timers
unsigned int absoluteTimeout = TCNT4 + (timeout >> 4); //As above, but with bit shift instead of / 16
OCR4A = absoluteTimeout;
ignitionSchedule4.duration = duration;
ignitionSchedule4.StartCallback = startCallback; //Name the start callback function
ignitionSchedule4.EndCallback = endCallback; //Name the start callback function
noInterrupts();
ignitionSchedule4.startCompare = IGN4_COUNTER + (timeout >> 4); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule4.endCompare = ignitionSchedule4.startCompare + (duration >> 4);
IGN4_COMPARE = ignitionSchedule4.startCompare;
ignitionSchedule4.Status = PENDING; //Turn this schedule on
TIMSK4 |= (1 << OCIE4A); //Turn on the A compare unit (ie turn on the interrupt)
interrupts();
IGN4_TIMER_ENABLE();
}
void setIgnitionSchedule5(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
return;
if(ignitionSchedule1.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
ignitionSchedule5.StartCallback = startCallback; //Name the start callback function
ignitionSchedule5.EndCallback = endCallback; //Name the start callback function
ignitionSchedule5.duration = duration;
//As the timer is ticking every 4uS (Time per Tick = (Prescale)*(1/Frequency))
if (timeout > 262140) { timeout = 262100; } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when applied causing erratic behaviour such as erroneous sparking. This must be set slightly lower than the max of 262140 to avoid strangeness
OCR5A = TCNT5 + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule5.duration = duration;
ignitionSchedule5.StartCallback = startCallback; //Name the start callback function
ignitionSchedule5.EndCallback = endCallback; //Name the start callback function
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
OCR5A = TCNT5 + (timeout >> 2); //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule5.Status = PENDING; //Turn this schedule on
TIMSK5 |= (1 << OCIE5A); //Turn on the A compare unit (ie turn on the interrupt)
#endif
}
/*******************************************************************************************************************************************************************************************************/
@ -250,44 +294,52 @@ void setIgnitionSchedule5(void (*startCallback)(), unsigned long timeout, unsign
//This calls the relevant callback function (startCallback or endCallback) depending on the status of the schedule.
//If the startCallback function is called, we put the scheduler into RUNNING state
//Timer3A (fuel schedule 1) Compare Vector
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPA_vect, ISR_NOBLOCK) //fuelSchedules 1 and 5
#elif defined (CORE_TEENSY)
void timer3compareAinterrupt() //Most ARM chips can simply call a function
#endif
{
if (timer3Aqueue[0]->Status == OFF) { TIMSK3 &= ~(1 << OCIE3A); return; } //Safety check. Turn off this output compare unit and return without performing any action
if (timer3Aqueue[0]->Status == OFF) { FUEL1_TIMER_DISABLE(); return; } //Safety check. Turn off this output compare unit and return without performing any action
if (timer3Aqueue[0]->Status == PENDING) //Check to see if this schedule is turn on
{
timer3Aqueue[0]->StartCallback();
timer3Aqueue[0]->Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
OCR3A = popQueue(timer3Aqueue);
FUEL1_COMPARE = popQueue(timer3Aqueue);
}
else if (timer3Aqueue[0]->Status == RUNNING)
{
timer3Aqueue[0]->EndCallback();
timer3Aqueue[0]->Status = OFF; //Turn off the schedule
timer3Aqueue[0]->schedulesSet = 0;
OCR3A = popQueue(timer3Aqueue);
FUEL1_COMPARE = popQueue(timer3Aqueue);
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPB_vect, ISR_NOBLOCK) //fuelSchedule2
#elif defined (CORE_TEENSY)
void timer3compareBinterrupt() //Most ARM chips can simply call a function
#endif
{
if (fuelSchedule2.Status == PENDING) //Check to see if this schedule is turn on
{
fuelSchedule2.StartCallback();
fuelSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
OCR3B = fuelSchedule2.endCompare;
FUEL2_COMPARE = fuelSchedule2.endCompare;
}
else if (fuelSchedule2.Status == RUNNING)
{
fuelSchedule2.EndCallback();
fuelSchedule2.Status = OFF; //Turn off the schedule
fuelSchedule2.schedulesSet = 0;
TIMSK3 &= ~(1 << OCIE3B); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
FUEL2_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPC_vect, ISR_NOBLOCK) //fuelSchedule3
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
#elif defined (CORE_TEENSY)
void timer3compareCinterrupt() //Most ARM chips can simply call a function
#endif
{
@ -295,20 +347,20 @@ void timer3compareCinterrupt() //Most ARM chips can simply call a function
{
fuelSchedule3.StartCallback();
fuelSchedule3.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
OCR3C = fuelSchedule3.endCompare;
FUEL3_COMPARE = fuelSchedule3.endCompare;
}
else if (fuelSchedule3.Status == RUNNING)
{
fuelSchedule3.EndCallback();
fuelSchedule3.Status = OFF; //Turn off the schedule
fuelSchedule3.schedulesSet = 0;
TIMSK3 &= ~(1 << OCIE3C); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
FUEL3_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPB_vect, ISR_NOBLOCK) //fuelSchedule4
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
#elif defined (CORE_TEENSY)
void timer4compareBinterrupt() //Most ARM chips can simply call a function
#endif
{
@ -316,109 +368,105 @@ void timer4compareBinterrupt() //Most ARM chips can simply call a function
{
fuelSchedule4.StartCallback();
fuelSchedule4.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
OCR4B = fuelSchedule4.endCompare;
FUEL4_COMPARE = fuelSchedule4.endCompare;
}
else if (fuelSchedule4.Status == RUNNING)
{
fuelSchedule4.EndCallback();
fuelSchedule4.Status = OFF; //Turn off the schedule
fuelSchedule4.schedulesSet = 0;
TIMSK4 &= ~(1 << OCIE4B); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
FUEL4_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPA_vect, ISR_NOBLOCK) //ignitionSchedule1
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
#elif defined (CORE_TEENSY)
void timer5compareAinterrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule1.Status == PENDING) //Check to see if this schedule is turn on
{
//if ( ign1LastRev == startRevolutions ) { return; }
ignitionSchedule1.StartCallback();
ignitionSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule1.startTime = micros();
ignitionSchedule1.StartCallback();
ign1LastRev = startRevolutions;
OCR5A = TCNT5 + (ignitionSchedule1.duration >> 2); //Divide by 4
IGN1_COMPARE = ignitionSchedule1.endCompare; //OCR5A = TCNT5 + (ignitionSchedule1.duration >> 2); //Divide by 4
}
else if (ignitionSchedule1.Status == RUNNING)
{
ignitionSchedule1.Status = OFF; //Turn off the schedule
ignitionSchedule1.EndCallback();
ignitionCount += 1; //Increment the igintion counter
TIMSK5 &= ~(1 << OCIE5A); //Turn off this output compare unit
IGN1_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPB_vect, ISR_NOBLOCK) //ignitionSchedule2
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
#elif defined (CORE_TEENSY)
void timer5compareBinterrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule2.Status == PENDING) //Check to see if this schedule is turn on
{
//if ( ign2LastRev == startRevolutions ) { return; }
ignitionSchedule2.StartCallback();
ignitionSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule2.startTime = micros();
ignitionSchedule2.StartCallback();
ign2LastRev = startRevolutions;
OCR5B = TCNT5 + (ignitionSchedule2.duration >> 2);
IGN2_COMPARE = ignitionSchedule2.endCompare; //OCR5B = TCNT5 + (ignitionSchedule2.duration >> 2);
}
else if (ignitionSchedule2.Status == RUNNING)
{
ignitionSchedule2.Status = OFF; //Turn off the schedule
ignitionSchedule2.EndCallback();
ignitionCount += 1; //Increment the igintion counter
TIMSK5 &= ~(1 << OCIE5B); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
IGN2_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPC_vect, ISR_NOBLOCK) //ignitionSchedule3
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
#elif defined (CORE_TEENSY)
void timer5compareCinterrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule3.Status == PENDING) //Check to see if this schedule is turn on
{
//if ( ign3LastRev == startRevolutions ) { return; }
ignitionSchedule3.StartCallback();
ignitionSchedule3.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule3.startTime = micros();
ignitionSchedule3.StartCallback();
ign3LastRev = startRevolutions;
OCR5C = TCNT5 + (ignitionSchedule3.duration >> 2);
IGN3_COMPARE = ignitionSchedule3.endCompare; //OCR5C = TCNT5 + (ignitionSchedule3.duration >> 2);
}
else if (ignitionSchedule3.Status == RUNNING)
{
ignitionSchedule3.Status = OFF; //Turn off the schedule
ignitionSchedule3.EndCallback();
ignitionCount += 1; //Increment the igintion counter
TIMSK5 &= ~(1 << OCIE5C); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
IGN3_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPA_vect, ISR_NOBLOCK) //ignitionSchedule4
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
#elif defined (CORE_TEENSY)
void timer4compareAinterrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule4.Status == PENDING) //Check to see if this schedule is turn on
{
//if ( ign4LastRev == startRevolutions ) { return; }
ignitionSchedule4.StartCallback();
ignitionSchedule4.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule4.startTime = micros();
ignitionSchedule4.StartCallback();
ign4LastRev = startRevolutions;
OCR4A = TCNT4 + (ignitionSchedule4.duration >> 4); //Divide by 16
IGN4_COMPARE = ignitionSchedule4.endCompare; //OCR4A = TCNT4 + (ignitionSchedule4.duration >> 4); //Divide by 16
}
else if (ignitionSchedule4.Status == RUNNING)
{
ignitionSchedule4.Status = OFF; //Turn off the schedule
ignitionSchedule4.EndCallback();
ignitionCount += 1; //Increment the igintion counter
TIMSK4 &= ~(1 << OCIE4A); //Turn off this output compare unit (This simply writes 0 to the OCIE4A bit of TIMSK4)
IGN4_TIMER_DISABLE();
}
}

View File

@ -97,9 +97,10 @@ unsigned long MAPrunningValue; //Used for tracking either the total of all MAP r
unsigned int MAPcount; //Number of samples taken in the current MAP cycle
byte MAPcurRev = 0; //Tracks which revolution we're sampling on
int CRANK_ANGLE_MAX = 360; // The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential
bool useSequentialFuel; // Whether sequential fueling is to be used (1 squirt per cycle)
bool useSequentialIgnition; // Whether sequential ignition is used (1 spark per cycle)
int CRANK_ANGLE_MAX = 720;
int CRANK_ANGLE_MAX_IGN, CRANK_ANGLE_MAX_INJ = 360; // The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential
//bool useSequentialFuel; // Whether sequential fueling is to be used (1 squirt per cycle)
//bool useSequentialIgnition; // Whether sequential ignition is used (1 spark per cycle)
static byte coilHIGH = HIGH;
static byte coilLOW = LOW;
@ -149,7 +150,10 @@ volatile bool fpPrimed = false; //Tracks whether or not the fuel pump priming ha
void setup()
{
Serial.begin(115200);
Serial.begin(115200);
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //ATmega2561 does not have Serial3
if (configPage1.canEnable) { Serial3.begin(115200); }
#endif
//Setup the dummy fuel and ignition tables
//dummyFuelTable(&fuelTable);
@ -161,7 +165,6 @@ void setup()
table3D_setSize(&vvtTable, 8);
loadConfig();
if (configPage1.canenable ==1){Serial3.begin(115200);}
//Repoint the 2D table structs to the config pages that were just loaded
taeTable.valueSize = SIZE_BYTE; //Set this table to use byte values
@ -457,7 +460,7 @@ void setup()
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
@ -467,6 +470,7 @@ void setup()
cbi(ADCSRA,ADPS1);
cbi(ADCSRA,ADPS0);
#endif
#endif
mainLoopCount = 0;
@ -483,11 +487,14 @@ void setup()
case 2:
channel1IgnDegrees = 0;
if (configPage1.engineType == EVEN_FIRE ) { channel2IgnDegrees = 180; }
if (configPage1.engineType == EVEN_FIRE )
{
channel2IgnDegrees = 180;
}
else { channel2IgnDegrees = configPage1.oddfire2; }
//For alternatiing injection, the squirt occurs at different times for each channel
if(configPage1.injTiming == INJ_SEMISEQUENTIAL)
//For alternating injection, the squirt occurs at different times for each channel
if(configPage1.injLayout == INJ_SEMISEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = channel2IgnDegrees; //Set to the same as the ignition degrees (Means there's no need for another if to check for oddfire)
@ -512,17 +519,19 @@ void setup()
}
//For alternatiing injection, the squirt occurs at different times for each channel
if(configPage1.injTiming == INJ_SEMISEQUENTIAL)
if(configPage1.injLayout == INJ_SEMISEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = channel2IgnDegrees;
channel3InjDegrees = channel2IgnDegrees;
}
else if (configPage1.injTiming == INJ_SEQUENTIAL)
else if (configPage1.injLayout == INJ_SEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = 240;
channel3InjDegrees = 480;
CRANK_ANGLE_MAX_INJ = 720;
req_fuel_uS = req_fuel_uS * 2;
}
else { channel1InjDegrees = channel2InjDegrees = channel3InjDegrees = 0; } //For simultaneous, all squirts happen at the same time
@ -537,10 +546,12 @@ void setup()
{
channel2IgnDegrees = 180;
if(useSequentialIgnition)
if(configPage2.sparkMode == IGN_MODE_SEQUENTIAL)
{
channel3IgnDegrees = 360;
channel4IgnDegrees = 540;
CRANK_ANGLE_MAX_IGN = 720;
}
}
else
@ -551,17 +562,23 @@ void setup()
}
//For alternatiing injection, the squirt occurs at different times for each channel
if(configPage1.injTiming == INJ_SEMISEQUENTIAL)
if(configPage1.injLayout == INJ_SEMISEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = channel2IgnDegrees;
}
else if (useSequentialFuel)
else if (configPage1.injLayout == INJ_SEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = channel2IgnDegrees;
channel3InjDegrees = channel3IgnDegrees;
channel4InjDegrees = channel4IgnDegrees;
channel2InjDegrees = 180;
channel3InjDegrees = 360;
channel4InjDegrees = 540;
channel3InjEnabled = true;
channel4InjEnabled = true;
CRANK_ANGLE_MAX_INJ = 720;
req_fuel_uS = req_fuel_uS * 2;
}
else { channel1InjDegrees = channel2InjDegrees = 0; } //For simultaneous, all squirts happen at the same time
@ -575,16 +592,18 @@ void setup()
channel4IgnDegrees = 216;
channel5IgnDegrees = 288;
if(useSequentialIgnition)
if(configPage2.sparkMode == IGN_MODE_SEQUENTIAL)
{
channel2IgnDegrees = 144;
channel3IgnDegrees = 288;
channel4IgnDegrees = 432;
channel5IgnDegrees = 576;
CRANK_ANGLE_MAX_IGN = 720;
}
//For alternatiing injection, the squirt occurs at different times for each channel
if(configPage1.injTiming == INJ_SEMISEQUENTIAL)
if(configPage1.injLayout == INJ_SEMISEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = 72;
@ -592,13 +611,15 @@ void setup()
channel4InjDegrees = 216;
channel5InjDegrees = 288;
}
else if (useSequentialFuel)
else if (configPage1.injLayout == INJ_SEQUENTIAL)
{
channel1InjDegrees = 0;
channel2InjDegrees = 144;
channel3InjDegrees = 288;
channel4InjDegrees = 432;
channel5InjDegrees = 576;
CRANK_ANGLE_MAX_INJ = 720;
}
else { channel1InjDegrees = channel2InjDegrees = channel3InjDegrees = channel4InjDegrees = channel5InjDegrees = 0; } //For simultaneous, all squirts happen at the same time
@ -614,7 +635,7 @@ void setup()
channel3IgnDegrees = 240;
//For alternatiing injection, the squirt occurs at different times for each channel
if(configPage1.injTiming == INJ_SEMISEQUENTIAL || configPage1.injTiming == INJ_SEQUENTIAL) //No full sequential for more than 4 cylinders
if(configPage1.injLayout == INJ_SEMISEQUENTIAL || configPage1.injLayout == INJ_SEQUENTIAL) //No full sequential for more than 4 cylinders
{
channel1InjDegrees = 0;
channel2InjDegrees = 120;
@ -635,7 +656,7 @@ void setup()
channel4IgnDegrees = 270;
//For alternatiing injection, the squirt occurs at different times for each channel
if(configPage1.injTiming == INJ_SEMISEQUENTIAL || configPage1.injTiming == INJ_SEQUENTIAL) //No full sequential for more than 4 cylinders
if(configPage1.injLayout == INJ_SEMISEQUENTIAL || configPage1.injTiming == INJ_SEQUENTIAL) //No full sequential for more than 4 cylinders
{
channel1InjDegrees = 0;
channel2InjDegrees = 90;
@ -754,17 +775,19 @@ void loop()
command();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //ATmega2561 does not have Serial3
//if Can interface is enabled then check for serial3 requests.
if (configPage1.canenable == 1)
if (configPage1.canEnable)
{
if ( ((mainLoopCount & 31) == 1) or (Serial3.available() > SERIAL_BUFFER_THRESHOLD) )
{
if (Serial3.available() > 0)
{
Cancommand();
canCommand();
}
}
}
#endif
// if (configPage1.displayType && (mainLoopCount & 255) == 1) { updateDisplay();} //Displays currently disabled
@ -795,7 +818,7 @@ void loop()
fuelOn = false;
if (fpPrimed) { digitalWrite(pinFuelPump, LOW); } //Turn off the fuel pump, but only if the priming is complete
fuelPumpOn = false;
TIMSK4 &= ~(1 << OCIE4C); digitalWrite(pinIdle1, LOW); //Turns off the idle control PWM. This REALLY needs to be cleaned up into a general PWM controller class
disableIdle(); //Turn off the idle PWM
}
//Uncomment the following for testing
@ -989,53 +1012,66 @@ void loop()
//Determine next firing angles
int PWdivTimerPerDegree = div(currentStatus.PW, timePerDegree).quot; //How many crank degrees the calculated PW will take at the current speed
injector1StartAngle = configPage1.inj1Ang - ( PWdivTimerPerDegree ); //This is a little primitive, but is based on the idea that all fuel needs to be delivered before the inlet valve opens. See http://www.extraefi.co.uk/sequential_fuel.html for more detail
if(injector1StartAngle < 0) {injector1StartAngle += CRANK_ANGLE_MAX;}
if(injector1StartAngle < 0) {injector1StartAngle += CRANK_ANGLE_MAX_INJ;}
//Repeat the above for each cylinder
switch (configPage1.nCylinders)
{
//2 cylinders
case 2:
injector2StartAngle = (configPage1.inj2Ang + channel2InjDegrees - ( PWdivTimerPerDegree ));
if(injector2StartAngle > CRANK_ANGLE_MAX) {injector2StartAngle -= CRANK_ANGLE_MAX;}
if(injector2StartAngle > CRANK_ANGLE_MAX_INJ) {injector2StartAngle -= CRANK_ANGLE_MAX_INJ;}
break;
//3 cylinders
case 3:
injector2StartAngle = (configPage1.inj2Ang + channel2InjDegrees - ( PWdivTimerPerDegree ));
if(injector2StartAngle > CRANK_ANGLE_MAX) {injector2StartAngle -= CRANK_ANGLE_MAX;}
if(injector2StartAngle > CRANK_ANGLE_MAX_INJ) {injector2StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector3StartAngle = (configPage1.inj3Ang + channel3InjDegrees - ( PWdivTimerPerDegree ));
if(injector3StartAngle > CRANK_ANGLE_MAX) {injector3StartAngle -= CRANK_ANGLE_MAX;}
if(injector3StartAngle > CRANK_ANGLE_MAX_INJ) {injector3StartAngle -= CRANK_ANGLE_MAX_INJ;}
break;
//4 cylinders
case 4:
injector2StartAngle = (configPage1.inj2Ang + channel2InjDegrees - ( PWdivTimerPerDegree ));
if(injector2StartAngle > CRANK_ANGLE_MAX) {injector2StartAngle -= CRANK_ANGLE_MAX;}
if(injector2StartAngle > CRANK_ANGLE_MAX_INJ) {injector2StartAngle -= CRANK_ANGLE_MAX_INJ;}
if(configPage1.injLayout == INJ_SEQUENTIAL)
{
injector3StartAngle = (configPage1.inj3Ang + channel3InjDegrees - ( PWdivTimerPerDegree ));
if(injector3StartAngle > CRANK_ANGLE_MAX_INJ) {injector3StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector4StartAngle = (configPage1.inj4Ang + channel4InjDegrees - ( PWdivTimerPerDegree ));
if(injector4StartAngle > CRANK_ANGLE_MAX_INJ) {injector4StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector1StartAngle += 360;
injector2StartAngle += 360;
injector3StartAngle += 360;
injector4StartAngle += 360;
}
break;
//5 cylinders
case 5:
injector2StartAngle = (configPage1.inj2Ang + channel2InjDegrees - ( PWdivTimerPerDegree ));
if(injector2StartAngle > CRANK_ANGLE_MAX) {injector2StartAngle -= CRANK_ANGLE_MAX;}
if(injector2StartAngle > CRANK_ANGLE_MAX_INJ) {injector2StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector3StartAngle = (configPage1.inj3Ang + channel3InjDegrees - ( PWdivTimerPerDegree ));
if(injector3StartAngle > CRANK_ANGLE_MAX) {injector3StartAngle -= CRANK_ANGLE_MAX;}
if(injector3StartAngle > CRANK_ANGLE_MAX_INJ) {injector3StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector4StartAngle = (configPage1.inj4Ang + channel4InjDegrees - ( PWdivTimerPerDegree ));
if(injector4StartAngle > CRANK_ANGLE_MAX) {injector4StartAngle -= CRANK_ANGLE_MAX;}
if(injector4StartAngle > CRANK_ANGLE_MAX_INJ) {injector4StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector5StartAngle = (configPage1.inj1Ang + channel5InjDegrees - ( PWdivTimerPerDegree ));
if(injector5StartAngle > CRANK_ANGLE_MAX) {injector5StartAngle -= CRANK_ANGLE_MAX;}
if(injector5StartAngle > CRANK_ANGLE_MAX_INJ) {injector5StartAngle -= CRANK_ANGLE_MAX_INJ;}
break;
//6 cylinders
case 6:
injector2StartAngle = (configPage1.inj2Ang + channel2InjDegrees - ( PWdivTimerPerDegree ));
if(injector2StartAngle > CRANK_ANGLE_MAX) {injector2StartAngle -= CRANK_ANGLE_MAX;}
if(injector2StartAngle > CRANK_ANGLE_MAX_INJ) {injector2StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector3StartAngle = (configPage1.inj3Ang + channel3InjDegrees - ( PWdivTimerPerDegree ));
if(injector3StartAngle > CRANK_ANGLE_MAX) {injector3StartAngle -= CRANK_ANGLE_MAX;}
if(injector3StartAngle > CRANK_ANGLE_MAX_INJ) {injector3StartAngle -= CRANK_ANGLE_MAX_INJ;}
break;
//8 cylinders
case 8:
injector2StartAngle = (configPage1.inj2Ang + channel2InjDegrees - ( PWdivTimerPerDegree ));
if(injector2StartAngle > CRANK_ANGLE_MAX) {injector2StartAngle -= CRANK_ANGLE_MAX;}
if(injector2StartAngle > CRANK_ANGLE_MAX_INJ) {injector2StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector3StartAngle = (configPage1.inj3Ang + channel3InjDegrees - ( PWdivTimerPerDegree ));
if(injector3StartAngle > CRANK_ANGLE_MAX) {injector3StartAngle -= CRANK_ANGLE_MAX;}
if(injector3StartAngle > CRANK_ANGLE_MAX_INJ) {injector3StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector4StartAngle = (configPage1.inj4Ang + channel4InjDegrees - ( PWdivTimerPerDegree ));
if(injector4StartAngle > CRANK_ANGLE_MAX) {injector4StartAngle -= CRANK_ANGLE_MAX;}
if(injector4StartAngle > CRANK_ANGLE_MAX_INJ) {injector4StartAngle -= CRANK_ANGLE_MAX_INJ;}
break;
//Will hit the default case on 1 cylinder or >8 cylinders. Do nothing in these cases
default:
@ -1061,45 +1097,53 @@ void loop()
//Calculate start angle for each channel
//1 cylinder (Everyone gets this)
ignition1StartAngle = CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle; // 360 - desired advance angle - number of degrees the dwell will take
if(ignition1StartAngle < 0) {ignition1StartAngle += CRANK_ANGLE_MAX;}
ignition1StartAngle = CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle; // 360 - desired advance angle - number of degrees the dwell will take
if(ignition1StartAngle < 0) {ignition1StartAngle += CRANK_ANGLE_MAX_IGN;}
//This test for more cylinders and do the same thing
switch (configPage1.nCylinders)
{
//2 cylinders
case 2:
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX) {ignition2StartAngle -= CRANK_ANGLE_MAX;}
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX_IGN) {ignition2StartAngle -= CRANK_ANGLE_MAX_IGN;}
break;
//3 cylinders
case 3:
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX) {ignition2StartAngle -= CRANK_ANGLE_MAX;}
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX_IGN) {ignition2StartAngle -= CRANK_ANGLE_MAX_IGN;}
ignition3StartAngle = channel3IgnDegrees + 360 - currentStatus.advance - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX) {ignition3StartAngle -= CRANK_ANGLE_MAX;}
if(ignition3StartAngle > CRANK_ANGLE_MAX_IGN) {ignition3StartAngle -= CRANK_ANGLE_MAX_IGN;}
break;
//4 cylinders
case 4:
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX) {ignition2StartAngle -= CRANK_ANGLE_MAX;}
if(ignition2StartAngle < 0) {ignition2StartAngle += CRANK_ANGLE_MAX;}
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX_IGN) {ignition2StartAngle -= CRANK_ANGLE_MAX_IGN;}
if(ignition2StartAngle < 0) {ignition2StartAngle += CRANK_ANGLE_MAX_IGN;}
if(configPage2.sparkMode == IGN_MODE_SEQUENTIAL)
{
ignition3StartAngle = channel3IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX_IGN) {ignition3StartAngle -= CRANK_ANGLE_MAX_IGN;}
ignition4StartAngle = channel4IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition4StartAngle > CRANK_ANGLE_MAX_IGN) {ignition4StartAngle -= CRANK_ANGLE_MAX_IGN;}
}
break;
//6 cylinders
case 6:
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX) {ignition2StartAngle -= CRANK_ANGLE_MAX;}
ignition3StartAngle = channel3IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX) {ignition3StartAngle -= CRANK_ANGLE_MAX;}
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX_IGN) {ignition2StartAngle -= CRANK_ANGLE_MAX_IGN;}
ignition3StartAngle = channel3IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX_IGN) {ignition3StartAngle -= CRANK_ANGLE_MAX_IGN;}
break;
//8 cylinders
case 8:
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX) {ignition2StartAngle -= CRANK_ANGLE_MAX;}
ignition2StartAngle = channel2IgnDegrees + CRANK_ANGLE_MAX_IGN - currentStatus.advance - dwellAngle;
if(ignition2StartAngle > CRANK_ANGLE_MAX_IGN) {ignition2StartAngle -= CRANK_ANGLE_MAX_IGN;}
ignition3StartAngle = channel3IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX) {ignition3StartAngle -= CRANK_ANGLE_MAX;}
if(ignition3StartAngle > CRANK_ANGLE_MAX_IGN) {ignition3StartAngle -= CRANK_ANGLE_MAX_IGN;}
ignition4StartAngle = channel4IgnDegrees + CRANK_ANGLE_MAX - currentStatus.advance - dwellAngle;
if(ignition4StartAngle > CRANK_ANGLE_MAX) {ignition4StartAngle -= CRANK_ANGLE_MAX;}
if(ignition4StartAngle > CRANK_ANGLE_MAX_IGN) {ignition4StartAngle -= CRANK_ANGLE_MAX_IGN;}
break;
//Will hit the default case on 1 cylinder or >8 cylinders. Do nothing in these cases
@ -1115,13 +1159,14 @@ void loop()
//Determine the current crank angle
int crankAngle = getCrankAngle(timePerDegree);
if (crankAngle > CRANK_ANGLE_MAX_INJ ) { crankAngle -= 360; }
if (fuelOn && currentStatus.PW > 0 && !BIT_CHECK(currentStatus.squirt, BIT_SQUIRT_BOOSTCUT))
{
if (injector1StartAngle <= crankAngle && fuelSchedule1.schedulesSet == 0) { injector1StartAngle += 360; }
if (injector1StartAngle <= crankAngle && fuelSchedule1.schedulesSet == 0) { injector1StartAngle += CRANK_ANGLE_MAX_INJ; }
if (injector1StartAngle > crankAngle)
{
if (configPage1.injLayout == 1)
if (configPage1.injLayout == INJ_SEMISEQUENTIAL)
{
setFuelSchedule1(openInjector1and4,
((unsigned long)(injector1StartAngle - crankAngle) * (unsigned long)timePerDegree),
@ -1153,10 +1198,10 @@ void loop()
if(channel2InjEnabled)
{
tempCrankAngle = crankAngle - channel2InjDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_INJ; }
tempStartAngle = injector2StartAngle - channel2InjDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule2.schedulesSet == 0) { tempStartAngle += 360; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule2.schedulesSet == 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if ( tempStartAngle > tempCrankAngle )
{
if (configPage1.injLayout == 1)
@ -1181,10 +1226,10 @@ void loop()
if(channel3InjEnabled)
{
tempCrankAngle = crankAngle - channel3InjDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_INJ; }
tempStartAngle = injector3StartAngle - channel3InjDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule3.schedulesSet == 0) { tempStartAngle += 360; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule3.schedulesSet == 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if ( tempStartAngle > tempCrankAngle )
{
setFuelSchedule3(openInjector3,
@ -1198,10 +1243,10 @@ void loop()
if(channel4InjEnabled)
{
tempCrankAngle = crankAngle - channel4InjDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_INJ; }
tempStartAngle = injector4StartAngle - channel4InjDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule4.schedulesSet == 0) { tempStartAngle += 360; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule4.schedulesSet == 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if ( tempStartAngle > tempCrankAngle )
{
setFuelSchedule4(openInjector4,
@ -1212,13 +1257,13 @@ void loop()
}
}
//if(channel5InjEnabled)
if(channel5InjEnabled)
{
tempCrankAngle = crankAngle - channel5InjDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_INJ; }
tempStartAngle = injector5StartAngle - channel5InjDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule5.schedulesSet == 0) { tempStartAngle += 360; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if (tempStartAngle <= tempCrankAngle && fuelSchedule5.schedulesSet == 0) { tempStartAngle += CRANK_ANGLE_MAX_INJ; }
if ( tempStartAngle > tempCrankAngle )
{
setFuelSchedule5(openInjector5,
@ -1232,14 +1277,15 @@ void loop()
//***********************************************************************************************
//| BEGIN IGNITION SCHEDULES
//Likewise for the ignition
//Perform an initial check to see if the ignition is turned on (Ignition only turns on after a preset number of cranking revolutions and:
//Check for hard cut rev limit (If we're above the hardcut limit, we simply don't set a spark schedule)
//crankAngle = getCrankAngle(timePerDegree); //Refresh with the latest crank angle
crankAngle = getCrankAngle(timePerDegree); //Refresh with the latest crank angle
if (crankAngle > CRANK_ANGLE_MAX_IGN ) { crankAngle -= 360; }
//fixedCrankingOverride is used to extend the dwell during cranking so that the decoder can trigger the spark upon seeing a certain tooth. Currently only available on the basic distributor and 4g63 decoders.
if ( configPage2.ignCranklock && BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK)) { fixedCrankingOverride = currentStatus.dwell; }
else { fixedCrankingOverride = 0; }
//Perform an initial check to see if the ignition is turned on (Ignition only turns on after a preset number of cranking revolutions and:
//Check for hard cut rev limit (If we're above the hardcut limit, we simply don't set a spark schedule)
if(ignitionOn && !currentStatus.launchingHard && !BIT_CHECK(currentStatus.spark, BIT_SPARK_BOOSTCUT) && !BIT_CHECK(currentStatus.spark, BIT_SPARK_HRDLIM))
{
@ -1262,9 +1308,9 @@ void loop()
}
tempCrankAngle = crankAngle - channel2IgnDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_IGN; }
tempStartAngle = ignition2StartAngle - channel2IgnDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_IGN; }
//if ( (tempStartAngle > tempCrankAngle) && ign2LastRev != startRevolutions)
//if ( ign2LastRev != startRevolutions )
{
@ -1283,9 +1329,9 @@ void loop()
}
tempCrankAngle = crankAngle - channel3IgnDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_IGN; }
tempStartAngle = ignition3StartAngle - channel3IgnDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_IGN; }
//if (tempStartAngle > tempCrankAngle)
{
long ignition3StartTime = 0;
@ -1303,9 +1349,9 @@ void loop()
}
tempCrankAngle = crankAngle - channel4IgnDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_IGN; }
tempStartAngle = ignition4StartAngle - channel4IgnDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_IGN; }
//if (tempStartAngle > tempCrankAngle)
{
@ -1324,9 +1370,9 @@ void loop()
}
tempCrankAngle = crankAngle - channel5IgnDegrees;
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX; }
if( tempCrankAngle < 0) { tempCrankAngle += CRANK_ANGLE_MAX_IGN; }
tempStartAngle = ignition5StartAngle - channel5IgnDegrees;
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX; }
if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_IGN; }
//if (tempStartAngle > tempCrankAngle)
{

View File

@ -24,7 +24,9 @@ volatile int loopSec;
volatile unsigned long targetOverdwellTime;
volatile unsigned long targetTachoPulseTime;
#if defined (CORE_TEENSY)
IntervalTimer lowResTimer;
#endif
void initialiseTimers();
#endif // TIMERS_H

View File

@ -26,6 +26,9 @@ void initialiseTimers()
/* Now configure the prescaler to CPU clock divided by 128 = 125Khz */
TCCR2B |= (1<<CS22) | (1<<CS20); // Set bits
TCCR2B &= ~(1<<CS21); // Clear bit
#elif defined (CORE_TEENSY)
//Uses the PIT timer on Teensy.
lowResTimer.begin(oneMSInterval, 1000);
#endif
}
@ -34,8 +37,8 @@ void initialiseTimers()
//Executes every ~1ms.
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
ISR(TIMER2_OVF_vect, ISR_NOBLOCK)
#elif defined (CORE_TEENSY) && defined (__MK20DX256__)
void timer2Overflowinterrupt() //Most ARM chips can simply call a function
#elif defined (CORE_TEENSY)
void oneMSInterval() //Most ARM chips can simply call a function
#endif
{

View File

@ -12,9 +12,13 @@ Returns how much free dynamic memory exists (between heap and stack)
int freeRam ()
{
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
#elif defined(CORE_TEENSY)
return 0;
#endif
}
void setPinMapping(byte boardID)