Merge branch 'master' into pr/73

# Conflicts:
#	reference/speeduino.ini
#	speeduino/comms.h
#	speeduino/comms.ino
#	speeduino/globals.h
This commit is contained in:
Josh Stewart 2017-03-25 09:17:06 +11:00
commit 4897e44f55
25 changed files with 406 additions and 195 deletions

View File

@ -34,7 +34,15 @@ framework = arduino
; framework-arduinoststm32
board = genericSTM32F103RB
lib_deps = EEPROM
build_flags = -fpermissive
build_flags = -fpermissive -std=gnu++11
[env:bluepill_f103c8]
platform = ststm32
framework = arduino
; framework-arduinoststm32
board = bluepill_f103c8
lib_deps = EEPROM
build_flags = -fpermissive -std=gnu++11
[platformio]
@ -44,3 +52,4 @@ env_default = megaatmega2560
;env_default = teensy35
;env_default = LaunchPad_tm4c1294ncpdt
;env_default = genericSTM32F103RB
;env_default = bluepill_f103c8

View File

@ -38,9 +38,12 @@
; name = type, min, max;
;
; type List: value will be index.
rpmhigh = scalar, U16, "rpm", 1, 0, 0, 30000, 0
rpmwarn = scalar, U16, "rpm", 1, 0, 0, 30000, 0
rpmdang = scalar, U16, "rpm", 1, 0, 0, 30000, 0
rpmhigh = scalar, U16, "rpm", 1, 0, 0, 30000, 0
rpmwarn = scalar, U16, "rpm", 1, 0, 0, 30000, 0
rpmdang = scalar, U16, "rpm", 1, 0, 0, 30000, 0
idleUnits = bits, U08, [0:2], "None", "On/Off", "Duty Cycle", "Duty Cycle", "Steps", "Steps"
[Constants]
;----------------------------------------------------------------------------
@ -215,11 +218,11 @@ page = 2
flexFuelLow = scalar, U08, 57, "%", 1.0, 0.0, 0.0, 250.0, 0
flexFuelHigh = scalar, U08, 58, "%", 1.0, 0.0, 0.0, 250.0, 0
flexAdvLow = scalar, U08, 59, "Deg", 1.0, 0.0, 0.0, 250.0, 0
flexAdvHigh = scalar, U08, 40, "Deg", 1.0, 0.0, 0.0, 250.0, 0
flexAdvHigh = scalar, U08, 60, "Deg", 1.0, 0.0, 0.0, 250.0, 0
iacCLminDuty = scalar, U08, 61, "%", 1.0, 0.0, 0.0, 100.0, 0 ; Minimum and maximum duty cycles when using closed loop idle
iacCLmaxDuty = scalar, U08, 62, "%", 1.0, 0.0, 0.0, 100.0, 0
boostMinDuty = scalar, U08, 62, "%", 1.0, 0.0, 0.0, 100.0, 0 ; Minimum and maximum duty cycles for boost control
boostMinDuty = scalar, U08, 63, "%", 1.0, 0.0, 0.0, 100.0, 0 ; Minimum and maximum duty cycles for boost control
@ -255,7 +258,7 @@ page = 4
TrigPattern= bits, U08, 5,[4:7], "Missing Tooth", "Basic Distributor", "Dual Wheel", "GM 7X", "4G63 / Miata", "GM 24X", "Jeep 2000", "Audi 135", "Honda D17", "Miata 99-05", "Mazda AU", "Non-360 Dual", "Nissan 360", "INVALID", "INVALID", "INVALID"
TrigEdgeSec= bits, U08, 6,[0:0], "Leading", "Trailing"
fuelPumpPin= bits , U08, 6,[1:6], "Board Default", "INVALID", "INVALID", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
unused4-6h = bits, U08, 6,[7:7], "No", "Yes"
useResync = bits, U08, 6,[7:7], "No", "Yes"
unused4-7 = scalar, U08, 7, "ADC", 1, 0, 0, 255, 0
IdleAdvRPM = scalar, U08, 8, "RPM", 100, 0, 0, 1200, 0
#if CELSIUS
@ -433,7 +436,7 @@ page = 7
iacCrankBins = array, U08, 48, [4], "F", 1.8, -22.23, -40, 215, 0
#endif
iacAlgorithm = bits , U08, 52, [0:2], "None", "On/Off", "PWM Open loop", "PWM Closed loop", "Stepper", "INVALID", "INVALID", "INVALID"
iacAlgorithm = bits , U08, 52, [0:2], "None", "On/Off", "PWM Open loop", "PWM Closed loop", "Stepper Open Loop", "Stepper Closed Loop", "INVALID", "INVALID"
iacStepTime = bits , U08, 52, [3:5], "1", "2", "3", "4", "5", "6"
iacChannels = bits, U08, 52, [6:6], "1", "2"
iacPWMdir = bits , U08, 52, [7:7], "Normal", "Reverse"
@ -683,6 +686,7 @@ page = 10
defaultValue = pinLayout, 1
defaultValue = TrigPattern, 0
defaultValue = useResync, 1
defaultValue = sparkMode, 0
defaultValue = indInjAng, 0
defaultValue = inj1Ang, 355
@ -788,11 +792,11 @@ menuDialog = main
subMenu = warmup, "Warmup Enrichment"
subMenu = std_separator
subMenu = idleSettings, "Idle Control"
subMenu = iacClosedLoop_curve, "Idle - Closed loop targets", 7, { iacAlgorithm == 3 }
subMenu = iacClosedLoop_curve, "Idle - Closed loop targets", 7, { iacAlgorithm == 3 || iacAlgorithm == 5 }
subMenu = iacPwm_curve, "Idle - PWM Duty Cycle", 7, { iacAlgorithm == 2 }
subMenu = iacPwmCrank_curve, "Idle - PWM Cranking Duty Cycle", 7, { iacAlgorithm == 2 }
subMenu = iacStep_curve, "Idle - Stepper Motor", 7, { iacAlgorithm == 4 || iacAlgorithm == 5 }
subMenu = iacStepCrank_curve, "Idle - Stepper Motor Cranking", 7, { iacAlgorithm == 4 || iacAlgorithm == 5 }
subMenu = iacStep_curve, "Idle - Stepper Motor", 7, { iacAlgorithm == 4 }
subMenu = iacStepCrank_curve, "Idle - Stepper Motor Cranking", 7, { iacAlgorithm == 4 }
menu = "&Accessories"
subMenu = fanSettings, "Thermo Fan"
@ -850,6 +854,7 @@ menuDialog = main
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."
useResync = "If enabled, sync will be rechecked once every full cycle from the cam input. This is good for accuracy, however if your cam input is noisy then this can cause issues."
numteeth = "Number of teeth on Primary Wheel."
TrigSpeed = "Primary trigger speed."
onetwo = "Number of Missing teeth on Primary Wheel."
@ -874,8 +879,8 @@ menuDialog = main
iacStepHyster = "The minimum number of steps to move in any one go."
iacAlgorithm = "Selects method of idle control.\nNone = no idle control valve.\nOn/Off valve.\nPWM valve (2,3 wire).\nStepper Valve (4,6,8 wire)."
iacPWMdir = "Normal PWM valves increase RPM with higher duty. If RPM decreases with higher duty then select Reverse"
iacCLminDuty= "When using closed loop idle control, this is the minimum duty cycle that the PID loop will allow. Combined with the maximum value, this specifies the working range of your idle valve
iacCLmaxDuty= "When using closed loop idle control, this is the maximum duty cycle that the PID loop will allow. Combined with the minimum value, this specifies the working range of your idle valve
iacCLminDuty= "When using closed loop idle control, this is the minimum duty cycle that the PID loop will allow. Combined with the maximum value, this specifies the working range of your idle valve"
iacCLmaxDuty= "When using closed loop idle control, this is the maximum duty cycle that the PID loop will allow. Combined with the minimum value, this specifies the working range of your idle valve"
oddfire2 = "The ATDC angle of channel 2 for oddfire engines. This is relative to the TDC angle of channel 1"
oddfire3 = "The ATDC angle of channel 3 for oddfire engines. This is relative to the TDC angle of channel 1 (NOT channel 2)"
@ -1105,9 +1110,9 @@ menuDialog = main
field = "Idle valve direction", iacPWMdir, { iacAlgorithm == 2 || iacAlgorithm == 3 }
dialog = closedloop_idle, "Closed loop Idle"
field = "P", idleKP, { iacAlgorithm == 3 }
field = "I", idleKI, { iacAlgorithm == 3 }
field = "D", idleKD, { iacAlgorithm == 3 }
field = "P", idleKP, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "I", idleKI, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "D", idleKD, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "Minimum valve duty", iacCLminDuty, { iacAlgorithm == 3 }
field = "Maximum valve duty", iacCLmaxDuty, { iacAlgorithm == 3 }
@ -1157,8 +1162,9 @@ menuDialog = main
field = "Note: This is the number of revolutions that will be skipped during"
field = "cranking before the injectors and coils are fired"
field = "Trigger edge", TrigEdge
field = "Secondary trigger edge", TrigEdgeSec, { TrigPattern == 0 || TrigPattern == 2 }
field = "Secondary trigger edge", TrigEdgeSec, { TrigPattern == 0 || TrigPattern == 2 } ;Missing tooth and dual wheel
field = "Trigger Filter", TrigFilter
field = "Re-sync every cycle", useResync, { TrigPattern == 2 || TrigPattern == 4 || TrigPattern == 7 } ;Dual wheel, 4G63 and Audi 135
dialog = sparkSettings,"Spark Settings",4
field = "Spark output mode", sparkMode
@ -1838,8 +1844,8 @@ cmdtestspk450dc = "E\x03\x0C"
deadValue = { 0 } ; Convenient unchanging value.
ochGetCommand = "A"
ochBlockSize = 41
ochBlockSize = 39
secl = scalar, U08, 0, "sec", 1.000, 0.000
squirt = scalar, U08, 1, "bits", 1.000, 0.000
@ -1902,11 +1908,12 @@ cmdtestspk450dc = "E\x03\x0C"
errorNum = bits, U08, 36, [0:1]
currentError = bits, U08, 36, [2:7]
boostTarget = scalar, U08, 37, "kPa", 2.000, 0.000
testoutputs = scalar, U08, 38, "bits", 1.000, 0.000
testenabled = bits, U08, 38, [0:0]
testactive = bits, U08, 38, [1:1]
;"", 1.0, 0.0
boostDuty = scalar, U08, 38, "%", 1.000, 0.000
idleLoad = scalar, U08, 39, { bitStringValue( idleUnits , iacAlgorithm ) }, 2.000, 0.000 ; This is a combined variable covering both PWM and stepper IACs. The units used depend on which idle algorithm is chosen
testoutputs = scalar, U08, 40, "bits", 1.000, 0.000
testenabled = bits, U08, 40, [0:0]
testactive = bits, U08, 40, [1:1]
; Computed output channels. See "megatuneExamples.ini" for all the
; pre-defined variables, search for "???" and you'll see them.
@ -1997,25 +2004,31 @@ cmdtestspk450dc = "E\x03\x0C"
entry = spark, "Spark", int, "%d"
entry = egoCorrection, "Gego", int, "%d"
entry = airCorrection, "Gair", int, "%d"
entry = batCorrection, "Gbattery", int, "%d"
entry = warmupEnrich, "Gwarm", int, "%d"
;entry = baroCorrection, "Gbaro", int, "%d"
entry = gammaEnrich, "Gammae", int, "%d"
entry = accelEnrich, "TPSacc", int, "%d"
entry = veCurr, "VE", int, "%d"
entry = pulseWidth, "PW", float, "%.1f"
entry = afrTarget, "AFR Target", float, "%.3f"
entry = pulseWidth, "PW2", float, "%.1f"
entry = dutyCycle, "DutyCycle1", float, "%.1f"
entry = dutyCycle, "DutyCycle2", float, "%.1f"
entry = TPSdot, "TPS DOT", int, "%d"
entry = advance, "Ignition Advance", int,"%d"
entry = dwell, "Dwell", int, "%d"
entry = batteryVoltage, "Battery V", float, "%.1f"
entry = rpmDOT, "rpm/s", int, "%d"
entry = flex, "%", int, "%d"
entry = gammaEnrich, "Gammae", int, "%d"
entry = accelEnrich, "TPSacc", int, "%d"
entry = veCurr, "VE", int, "%d"
entry = pulseWidth, "PW", float, "%.1f"
entry = afrTarget, "AFR Target", float, "%.3f"
entry = pulseWidth, "PW2", float, "%.1f"
entry = dutyCycle, "DutyCycle1", float, "%.1f"
entry = dutyCycle, "DutyCycle2", float, "%.1f"
entry = TPSdot, "TPS DOT", int, "%d"
entry = advance, "Advance", int, "%d"
entry = dwell, "Dwell", int, "%d"
entry = batteryVoltage, "Battery V", float, "%.1f"
entry = rpmDOT, "rpm/s", int, "%d"
entry = flex, "Eth %", int, "%d", { flexEnabled }
entry = errorNum, "Error #", int, "%d"
entry = currentError, "Error ID", int, "%d"
entry = boostTarget, "Boost Target",int, "%d"
entry = boostTarget, "Boost Target",int, "%d", { boostEnabled }
entry = boostDuty, "Boost Duty", int, "%d", { boostEnabled }
entry = boostCutOut , "Boost cut", int, "%d"
entry = idleLoad, "IAC value", int, "%d"
; Indicators
[LoggerDefinition]
; valid logger types: composite, tooth, trigger, csv

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -52,6 +52,7 @@ void initialiseAuxPWM()
boostPID.SetTunings(configPage3.boostKP, configPage3.boostKI, configPage3.boostKD);
boostPID.SetMode(AUTOMATIC); //Turn PID on
currentStatus.boostDuty = 0;
boostCounter = 0;
}
@ -70,7 +71,7 @@ void boostControl()
if( (boostCounter & 31) == 1) { boostPID.SetTunings(configPage3.boostKP, configPage3.boostKI, configPage3.boostKD); } //This only needs to be run very infrequently, once every 32 calls to boostControl(). This is approx. once per second
boostPID.Compute();
currentStatus.boostDuty = (unsigned long)(boost_pwm_target_value * 100UL) / boost_pwm_max_count;
TIMSK1 |= (1 << OCIE1A); //Turn on the compare unit (ie turn on the interrupt)
}
else { TIMSK1 &= ~(1 << OCIE1A); } // Disable timer channel
@ -126,7 +127,7 @@ ISR(TIMER1_COMPB_vect)
}
}
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
//YET TO BE IMPLEMENTED ON TEENSY
void initialiseAuxPWM() { }
void boostControl() { }

View File

@ -12,7 +12,7 @@
#define seqFuelPage 9
#define canbusPage 10//Config Page 10
#define packetSize 39
#define packetSize 41
byte currentPage = 1;//Not the same as the speeduino config page numbers
boolean isMap = true;

View File

@ -215,8 +215,13 @@ void sendValues(int packetlength, byte portNum)
if (portNum == 3)
{
//CAN serial
Serial3.write("A"); //confirm cmd type
Serial3.write(packetlength); //confirm no of byte to be sent
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //ATmega2561 does not have Serial3
Serial3.write("A"); //confirm cmd type
Serial3.write(packetlength); //confirm no of byte to be sent
#elif defined(CORE_STM32)
Serial2.write("A"); //confirm cmd type
Serial2.write(packetlength); //confirm no of byte to be sent
#endif
}
else
{
@ -272,11 +277,17 @@ void sendValues(int packetlength, byte portNum)
response[35] = currentStatus.flexIgnCorrection; //Ignition correction (Increased degrees of advance) for flex fuel
response[36] = getNextError();
response[37] = currentStatus.boostTarget;
response[38] = currentStatus.testOutputs;
response[38] = currentStatus.boostDuty;
response[39] = currentStatus.idleLoad;
response[40] = currentStatus.testOutputs;
//cli();
if (portNum == 0) { Serial.write(response, (size_t)packetlength); }
else if (portNum == 3) { Serial3.write(response, (size_t)packetlength); }
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //ATmega2561 does not have Serial3
else if (portNum == 3) { Serial3.write(response, (size_t)packetlength); }
#elif defined(CORE_STM32)
else if (portNum == 3) { Serial2.write(response, (size_t)packetlength); }
#endif
//sei();
return;
}

View File

@ -45,10 +45,12 @@ volatile bool toothLogRead = false; //Flag to indicate whether the current tooth
volatile unsigned int secondaryToothCount; //Used for identifying the current secondary (Usually cam) tooth for patterns with multiple secondary teeth
volatile unsigned long secondaryLastToothTime = 0; //The time (micros()) that the last tooth was registered (Cam input)
volatile unsigned long secondaryLastToothTime1 = 0; //The time (micros()) that the last tooth was registered (Cam input)
volatile int triggerActualTeeth;
volatile unsigned long triggerFilterTime; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering)
unsigned int triggerSecFilterTime; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering) for the secondary input
unsigned int triggerSecFilterTime_duration; // The shortest valid time (in uS) pulse DURATION
volatile int triggerToothAngle; //The number of crank degrees that elapse per tooth
unsigned long revolutionTime; //The time in uS that one revolution would take at current speed (The time tooth 1 was last seen, minus the time it was seen prior to that)
bool secondDerivEnabled; //The use of the 2nd derivative calculation is limited to certain decoders. This is set to either true or false in each decoders setup routine

View File

@ -88,7 +88,7 @@ Note: This does not currently support dual wheel (ie missing tooth + single toot
void triggerSetup_missingTooth()
{
triggerToothAngle = 360 / configPage2.triggerTeeth; //The number of degrees that passes from tooth to tooth
if(configPage2.TrigSpeed) { triggerToothAngle = triggerToothAngle * 2; } //Account for cam speed missing tooth
if(configPage2.TrigSpeed) { triggerToothAngle = 720 / configPage2.triggerTeeth; } //Account for cam speed missing tooth
triggerActualTeeth = configPage2.triggerTeeth - configPage2.triggerMissingTeeth; //The number of physical teeth on the wheel. Doing this here saves us a calculation each time in the interrupt
triggerFilterTime = (int)(1000000 / (MAX_RPM / 60 * configPage2.triggerTeeth)); //Trigger filter time is the shortest possible time (in uS) that there can be between crank teeth (ie at max RPM). Any pulses that occur faster than this time will be disgarded as noise
secondDerivEnabled = false;
@ -232,15 +232,15 @@ void triggerSec_DualWheel()
toothLastSecToothTime = curTime2;
triggerSecFilterTime = curGap2 >> 2; //Set filter at 25% of the current speed
toothCurrentCount = configPage2.triggerTeeth;
if(!currentStatus.hasSync)
{
toothLastToothTime = micros();
toothLastMinusOneToothTime = (toothOneTime - 6000000) / configPage2.triggerTeeth; //Fixes RPM at 10rpm until a full revolution has taken place
toothCurrentCount = configPage2.triggerTeeth;
currentStatus.hasSync = true;
}
else if (configPage2.useResync) { toothCurrentCount = configPage2.triggerTeeth; }
revolutionOne = 1; //Sequential revolution reset
}
@ -504,6 +504,8 @@ void triggerSetup_4G63()
triggerFilterTime = 1500; //10000 rpm, assuming we're triggering on both edges off the crank tooth.
triggerSecFilterTime = (int)(1000000 / (MAX_RPM / 60 * 2)) / 2; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed)
triggerSecFilterTime_duration = 4000;
secondaryLastToothTime = 0;
}
void triggerPri_4G63()
@ -561,20 +563,40 @@ void triggerPri_4G63()
void triggerSec_4G63()
{
//byte crankState = READ_PRI_TRIGGER();
//First filter is a duration based one to ensure the pulse was of sufficient length (time)
//if(READ_SEC_TRIGGER()) { secondaryLastToothTime1 = micros(); return; }
if(currentStatus.hasSync)
{
//if ( (micros() - secondaryLastToothTime1) < triggerSecFilterTime_duration ) { return; } //1166 is the time taken to cross 70 degrees at 10k rpm
//triggerSecFilterTime_duration = (micros() - secondaryLastToothTime1) >> 1;
}
curTime2 = micros();
curGap2 = curTime2 - toothLastSecToothTime;
if ( curGap2 < triggerSecFilterTime ) { return; }
toothLastSecToothTime = curTime2;
triggerSecFilterTime = curGap2 >> 1; //Basic 50% filter for the secondary reading
//triggerSecFilterTime = (curGap2 * 9) >> 5; //62.5%
//triggerSecFilterTime = (curGap2 * 6) >> 3; //75%
if(BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) || !currentStatus.hasSync)
{
triggerFilterTime = 1500; //If this is removed, can have trouble getting sync again after the engine is turned off (but ECU not reset).
//Check the status of the crank trigger
//bool crank = digitalRead(pinTrigger);
if(READ_PRI_TRIGGER())
if(READ_PRI_TRIGGER())// && (crankState == digitalRead(pinTrigger)))
{
toothCurrentCount = 4; //If the crank trigger is currently HIGH, it means we're on tooth #1
}
}
if ( (micros() - secondaryLastToothTime1) < triggerSecFilterTime_duration )
{
triggerSecFilterTime_duration = (micros() - secondaryLastToothTime1) >> 1;
if(READ_PRI_TRIGGER())// && (crankState == digitalRead(pinTrigger)))
{
//toothCurrentCount = 4; //If the crank trigger is currently HIGH, it means we're on tooth #1
/* High-res mode
toothCurrentCount = 7; //If the crank trigger is currently HIGH, it means we're on the falling edge of the narrow crank tooth
toothLastMinusOneToothTime = toothLastToothTime;
@ -582,20 +604,7 @@ void triggerSec_4G63()
*/
}
}
/*
else
{
//triggerSecFilterTime = curGap2 >> 1; //Only set the filter when we have sync
//if(toothCurrentCount != 2)
{
if(READ_PRI_TRIGGER())// && (crankState == digitalRead(pinTrigger)))
{
toothCurrentCount = 4; //If the crank trigger is currently HIGH, it means we're on tooth #1
}
}
}
*/
//else { triggerFilterTime = 1500; } //reset filter time (ugly)
return;
}
@ -866,11 +875,12 @@ void triggerPri_Audi135()
curGap = curTime - toothSystemLastToothTime;
if ( curGap < triggerFilterTime ) { return; }
toothSystemCount++;
toothSystemLastToothTime = curTime;
addToothLogEntry(curGap);
if ( !currentStatus.hasSync ) { toothLastToothTime = curTime; return; }
if ( toothSystemCount < 3 ) { return; } //We only proceed for every third tooth
addToothLogEntry(curGap);
toothSystemLastToothTime = curTime;
toothSystemCount = 0;
toothCurrentCount++; //Increment the tooth counter
@ -904,9 +914,7 @@ void triggerSec_Audi135()
currentStatus.hasSync = true;
toothSystemCount = 3; //Need to set this to 3 so that the next primary tooth is counted
}
else{
toothCurrentCount = 0;
}
else if (configPage2.useResync) { toothCurrentCount = 0; }
revolutionOne = 1; //Sequential revolution reset
}

View File

@ -5,6 +5,12 @@
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
#define CORE_AVR
#elif defined(STM32_MCU_SERIES)
#define CORE_STM32
inline unsigned char digitalPinToInterrupt(unsigned char Interrupt_pin) { return Interrupt_pin; } //This isn't included in the stm32duino libs (yet)
#define portOutputRegister(port) (volatile byte *)( &(port->regs->ODR) ) //These are defined in STM32F1/variants/generic_stm32f103c/variant.h but return a non byte* value
#define portInputRegister(port) (volatile byte *)( &(port->regs->IDR) ) //These are defined in STM32F1/variants/generic_stm32f103c/variant.h but return a non byte* value
#endif
//Handy bitsetting macros
@ -208,6 +214,8 @@ struct statuses {
byte boostTarget;
byte testOutputs;
bool testActive;
byte boostDuty;
byte idleLoad; //Either the current steps or current duty cycle for the idle control.
//Helpful bitwise operations:
//Useful reference: http://playground.arduino.cc/Code/BitMath
@ -326,7 +334,7 @@ struct config2 {
byte TrigEdgeSec : 1;
byte fuelPumpPin : 6;
byte unused4_6b : 1;
byte useResync : 1;
byte unused4_7;
byte IdleAdvRPM;

View File

@ -4,6 +4,13 @@
#include "globals.h"
#include "table.h"
#define IAC_ALGORITHM_NONE 0
#define IAC_ALGORITHM_ONOFF 1
#define IAC_ALGORITHM_PWM_OL 2
#define IAC_ALGORITHM_PWM_CL 3
#define IAC_ALGORITHM_STEP_OL 4
#define IAC_ALGORITHM_STEP_CL 5
#define STEPPER_FORWARD 0
#define STEPPER_BACKWARD 1
#define IDLE_TABLE_SIZE 10
@ -12,8 +19,8 @@ enum StepperStatus {SOFF, STEPPING, COOLING}; //The 2 statuses that a stepper ca
struct StepperIdle
{
unsigned int curIdleStep; //Tracks the current location of the stepper
unsigned int targetIdleStep; //What the targetted step is
int curIdleStep; //Tracks the current location of the stepper
int targetIdleStep; //What the targetted step is
volatile StepperStatus stepperStatus;
volatile unsigned long stepStartTime; //The time the curren
};
@ -32,6 +39,15 @@ struct StepperIdle
#define IDLE_TIMER_ENABLE() FTM2_C0SC |= FTM_CSC_CHIE
#define IDLE_TIMER_DISABLE() FTM2_C0SC &= ~FTM_CSC_CHIE
#elif defined(CORE_STM32)
//Placeholders only
#define IDLE_COUNTER 0
#define IDLE_COMPARE 0
#define IDLE_TIMER_ENABLE()
#define IDLE_TIMER_DISABLE()
#endif
struct table2D iacClosedLoopTable;
@ -54,11 +70,16 @@ volatile byte idle2_pin_mask;
volatile bool idle_pwm_state;
unsigned int idle_pwm_max_count; //Used for variable PWM frequency
volatile unsigned int idle_pwm_cur_value;
long idle_pid_target_value;
long idle_pwm_target_value;
long idle_cl_target_rpm;
byte idleCounter; //Used for tracking the number of calls to the idle control function
void initialiseIdle();
static inline void disableIdle();
static inline void enableIdle();
static inline byte isStepperHomed();
static inline byte checkForStepping();
static inline void doStep();
#endif

View File

@ -13,7 +13,7 @@ These functions cover the PWM and stepper idle control
Idle Control
Currently limited to on/off control and open loop PWM and stepper drive
*/
integerPID idlePID(&currentStatus.longRPM, &idle_pwm_target_value, &idle_cl_target_rpm, configPage3.idleKP, configPage3.idleKI, configPage3.idleKD, DIRECT); //This is the PID object if that algorithm is used. Needs to be global as it maintains state outside of each function call
integerPID idlePID(&currentStatus.longRPM, &idle_pid_target_value, &idle_cl_target_rpm, configPage3.idleKP, configPage3.idleKI, configPage3.idleKD, DIRECT); //This is the PID object if that algorithm is used. Needs to be global as it maintains state outside of each function call
void initialiseIdle()
{
@ -75,16 +75,17 @@ void initialiseIdle()
// enable IRQ Interrupt
NVIC_ENABLE_IRQ(IRQ_FTM2);
#elif defined(MCU_STM32F103RB)
#endif
//Initialising comprises of setting the 2D tables with the relevant values from the config pages
switch(configPage4.iacAlgorithm)
{
case 0:
//Case 0 is no idle control ('None')
case IAC_ALGORITHM_NONE: //Case 0 is no idle control ('None')
break;
case 1:
case IAC_ALGORITHM_ONOFF:
//Case 1 is on/off idle control
if (currentStatus.coolant < configPage4.iacFastTemp)
{
@ -92,13 +93,14 @@ void initialiseIdle()
}
break;
case 2:
case IAC_ALGORITHM_PWM_OL:
//Case 2 is PWM open loop
iacPWMTable.xSize = 10;
iacPWMTable.valueSize = SIZE_BYTE;
iacPWMTable.values = configPage4.iacOLPWMVal;
iacPWMTable.axisX = configPage4.iacBins;
iacCrankDutyTable.xSize = 4;
iacCrankDutyTable.valueSize = SIZE_BYTE;
iacCrankDutyTable.values = configPage4.iacCrankDuty;
@ -112,7 +114,7 @@ void initialiseIdle()
enableIdle();
break;
case 3:
case IAC_ALGORITHM_PWM_CL:
//Case 3 is PWM closed loop
iacClosedLoopTable.xSize = 10;
iacClosedLoopTable.valueSize = SIZE_BYTE;
@ -132,9 +134,11 @@ void initialiseIdle()
idlePID.SetOutputLimits(percentage(configPage1.iacCLminDuty, idle_pwm_max_count), percentage(configPage1.iacCLmaxDuty, idle_pwm_max_count));
idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD);
idlePID.SetMode(AUTOMATIC); //Turn PID on
idleCounter = 0;
break;
case 4:
case IAC_ALGORITHM_STEP_OL:
//Case 2 is Stepper open loop
iacStepTable.xSize = 10;
iacStepTable.valueSize = SIZE_BYTE;
@ -148,12 +152,14 @@ void initialiseIdle()
//homeStepper(); //Returns the stepper to the 'home' position
completedHomeSteps = 0;
idleStepper.curIdleStep = 0;
idleStepper.stepperStatus = SOFF;
break;
case 5:
case IAC_ALGORITHM_STEP_CL:
//Case 5 is Stepper closed loop
iacClosedLoopTable.xSize = 10;
iacClosedLoopTable.valueSize = SIZE_BYTE;
iacClosedLoopTable.values = configPage4.iacCLValues;
iacClosedLoopTable.axisX = configPage4.iacBins;
@ -162,11 +168,18 @@ void initialiseIdle()
iacCrankStepsTable.axisX = configPage4.iacCrankBins;
iacStepTime = configPage4.iacStepTime * 1000;
homeStepper(); //Returns the stepper to the 'home' position
completedHomeSteps = 0;
idleCounter = 0;
idleStepper.curIdleStep = 0;
idleStepper.stepperStatus = SOFF;
idlePID.SetOutputLimits(0, (configPage4.iacStepHome * 3)); //Maximum number of steps probably needs its own setting
idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD);
idlePID.SetMode(AUTOMATIC); //Turn PID on
break;
}
idleInitComplete = configPage4.iacAlgorithm; //Sets which idle method was initialised
currentStatus.idleLoad = 0;
}
void idleControl()
@ -175,10 +188,10 @@ void idleControl()
switch(configPage4.iacAlgorithm)
{
case 0: //Case 0 is no idle control ('None')
case IAC_ALGORITHM_NONE: //Case 0 is no idle control ('None')
break;
case 1: //Case 1 is on/off idle control
case IAC_ALGORITHM_ONOFF: //Case 1 is on/off idle control
if ( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < configPage4.iacFastTemp) //All temps are offset by 40 degrees
{
digitalWrite(pinIdle1, HIGH);
@ -187,7 +200,7 @@ void idleControl()
else if (idleOn) { digitalWrite(pinIdle1, LOW); idleOn = false; }
break;
case 2: //Case 2 is PWM open loop
case IAC_ALGORITHM_PWM_OL: //Case 2 is PWM open loop
//Check for cranking pulsewidth
if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
{
@ -203,70 +216,37 @@ void idleControl()
if( currentStatus.idleDuty == 0 ) { disableIdle(); break; }
enableIdle();
idle_pwm_target_value = percentage(currentStatus.idleDuty, idle_pwm_max_count);
currentStatus.idleLoad = currentStatus.idleDuty >> 1;
idleOn = true;
}
break;
case 3: //Case 3 is PWM closed loop
case IAC_ALGORITHM_PWM_CL: //Case 3 is PWM closed loop
//No cranking specific value for closed loop (yet?)
idle_cl_target_rpm = table2D_getValue(&iacClosedLoopTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) * 10; //All temps are offset by 40 degrees
//idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD);
if( (idleCounter & 31) == 1) { idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD); } //This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
idlePID.Compute();
idle_pwm_target_value = idle_pid_target_value;
if( idle_pwm_target_value == 0 ) { disableIdle(); }
else{ enableIdle(); } //Turn on the C compare unit (ie turn on the interrupt)
currentStatus.idleLoad = ((unsigned long)(idle_pwm_target_value * 100UL) / idle_pwm_max_count) >> 1;
//idle_pwm_target_value = 104;
idleCounter++;
break;
case 4: //Case 4 is open loop stepper control
case IAC_ALGORITHM_STEP_OL: //Case 4 is open loop stepper control
//First thing to check is whether there is currently a step going on and if so, whether it needs to be turned off
if(idleStepper.stepperStatus == STEPPING || idleStepper.stepperStatus == COOLING)
{
if(micros() > (idleStepper.stepStartTime + iacStepTime) )
{
if(idleStepper.stepperStatus == STEPPING)
{
//Means we're currently in a step, but it needs to be turned off
digitalWrite(pinStepperStep, LOW); //Turn off the step
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = COOLING; //'Cooling' is the time the stepper needs to sit in LOW state before the next step can be made
return;
}
else
{
//Means we're in COOLING status but have been in this state long enough to
idleStepper.stepperStatus = SOFF;
}
}
else
{
//Means we're in a step, but it doesn't need to turn off yet. No further action at this time
return;
}
}
if( checkForStepping() ) { return; } //If this is true it means there's either a step taking place or
if( !isStepperHomed() ) { return; } //Check whether homing is completed yet.
if( completedHomeSteps < (configPage4.iacStepHome * 3) ) //Home steps are divided by 3 from TS
{
digitalWrite(pinStepperDir, STEPPER_BACKWARD); //Sets stepper direction to backwards
digitalWrite(pinStepperStep, HIGH);
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = STEPPING;
completedHomeSteps++;
idleOn = true;
}
//Check for cranking pulsewidth
else if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
{
//Currently cranking. Use the cranking table
idleStepper.targetIdleStep = table2D_getValue(&iacCrankStepsTable, (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET)) * 3; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
if ( idleStepper.targetIdleStep > (idleStepper.curIdleStep - configPage4.iacStepHyster) && idleStepper.targetIdleStep < (idleStepper.curIdleStep + configPage4.iacStepHyster) ) { return; } //Hysteris check
else if(idleStepper.targetIdleStep < idleStepper.curIdleStep) { digitalWrite(pinStepperDir, STEPPER_BACKWARD); idleStepper.curIdleStep--; }//Sets stepper direction to backwards
else if (idleStepper.targetIdleStep > idleStepper.curIdleStep) { digitalWrite(pinStepperDir, STEPPER_FORWARD); idleStepper.curIdleStep++; }//Sets stepper direction to forwards
digitalWrite(pinStepperStep, HIGH);
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = STEPPING;
idleOn = true;
doStep();
}
else if( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < iacStepTable.axisX[IDLE_TABLE_SIZE-1])
{
@ -276,59 +256,137 @@ void idleControl()
//Only do a lookup of the required value around 4 times per second. Any more than this can create too much jitter and require a hyster value that is too high
idleStepper.targetIdleStep = table2D_getValue(&iacStepTable, (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET)) * 3; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
}
if ( idleStepper.targetIdleStep > (idleStepper.curIdleStep - configPage4.iacStepHyster) && idleStepper.targetIdleStep < (idleStepper.curIdleStep + configPage4.iacStepHyster) ) { return; } //Hysteris check
else if(idleStepper.targetIdleStep < idleStepper.curIdleStep) { digitalWrite(pinStepperDir, STEPPER_BACKWARD); idleStepper.curIdleStep--; }//Sets stepper direction to backwards
else if (idleStepper.targetIdleStep > idleStepper.curIdleStep) { digitalWrite(pinStepperDir, STEPPER_FORWARD); idleStepper.curIdleStep++; }//Sets stepper direction to forwards
digitalWrite(pinStepperStep, HIGH);
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = STEPPING;
idleOn = true;
doStep();
}
currentStatus.idleLoad = idleStepper.curIdleStep >> 1; //Current step count (Divided by 2 for byte)
break;
case IAC_ALGORITHM_STEP_CL://Case 5 is closed loop stepper control
//First thing to check is whether there is currently a step going on and if so, whether it needs to be turned off
if( checkForStepping() ) { return; } //If this is true it means there's either a step taking place or
if( !isStepperHomed() ) { return; } //Check whether homing is completed yet.
if( (idleCounter & 31) == 1) { idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD); } //This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
idle_cl_target_rpm = table2D_getValue(&iacClosedLoopTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) * 10; //All temps are offset by 40 degrees
idlePID.Compute();
idleStepper.targetIdleStep = idle_pid_target_value;
doStep();
currentStatus.idleLoad = idleStepper.curIdleStep >> 1; //Current step count (Divided by 2 for byte)
idleCounter++;
break;
}
}
/*
A simple function to home the stepper motor (If in use)
Checks whether the stepper has been homed yet. If it hasn't, will handle the next step
Returns:
True: If the system has been homed. No other action is taken
False: If the motor has not yet been homed. Will also perform another homing step.
*/
void homeStepper()
static inline byte isStepperHomed()
{
//Need to 'home' the stepper on startup
digitalWrite(pinStepperDir, STEPPER_BACKWARD); //Sets stepper direction to backwards
for(int x=0; x < (configPage4.iacStepHome * 3); x++) //Step counts are divided by 3 in TS. Multiply back out here
{
digitalWrite(pinStepperStep, HIGH);
delayMicroseconds(iacStepTime);
digitalWrite(pinStepperStep, LOW);
delayMicroseconds(iacStepTime);
}
digitalWrite(pinStepperDir, STEPPER_FORWARD);
idleStepper.curIdleStep = 0;
idleStepper.targetIdleStep = 0;
idleStepper.stepperStatus = SOFF;
if( completedHomeSteps < (configPage4.iacStepHome * 3) ) //Home steps are divided by 3 from TS
{
digitalWrite(pinStepperDir, STEPPER_BACKWARD); //Sets stepper direction to backwards
digitalWrite(pinStepperStep, HIGH);
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = STEPPING;
completedHomeSteps++;
idleOn = true;
return false;
}
return true;
}
/*
Checks whether a step is currently underway or whether the motor is in 'cooling' state (ie whether it's ready to begin another step or not)
Returns:
True: If a step is underway or motor is 'cooling'
False: If the motor is ready for another step
*/
static inline byte checkForStepping()
{
if(idleStepper.stepperStatus == STEPPING || idleStepper.stepperStatus == COOLING)
{
if(micros() > (idleStepper.stepStartTime + iacStepTime) )
{
if(idleStepper.stepperStatus == STEPPING)
{
//Means we're currently in a step, but it needs to be turned off
digitalWrite(pinStepperStep, LOW); //Turn off the step
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = COOLING; //'Cooling' is the time the stepper needs to sit in LOW state before the next step can be made
return true;
}
else
{
//Means we're in COOLING status but have been in this state long enough to
idleStepper.stepperStatus = SOFF;
}
}
else
{
//Means we're in a step, but it doesn't need to turn off yet. No further action at this time
return true;
}
}
return false;
}
/*
Performs a step
*/
static inline void doStep()
{
if ( idleStepper.targetIdleStep > (idleStepper.curIdleStep - configPage4.iacStepHyster) && idleStepper.targetIdleStep < (idleStepper.curIdleStep + configPage4.iacStepHyster) ) { return; } //Hysteris check
else if(idleStepper.targetIdleStep < idleStepper.curIdleStep) { digitalWrite(pinStepperDir, STEPPER_BACKWARD); idleStepper.curIdleStep--; }//Sets stepper direction to backwards
else if (idleStepper.targetIdleStep > idleStepper.curIdleStep) { digitalWrite(pinStepperDir, STEPPER_FORWARD); idleStepper.curIdleStep++; }//Sets stepper direction to forwards
digitalWrite(pinStepperStep, HIGH);
idleStepper.stepStartTime = micros();
idleStepper.stepperStatus = STEPPING;
idleOn = true;
}
//This function simply turns off the idle PWM and sets the pin low
static inline void disableIdle()
{
IDLE_TIMER_DISABLE();
digitalWrite(pinIdle1, LOW);
if(configPage4.iacAlgorithm == IAC_ALGORITHM_PWM_CL || configPage4.iacAlgorithm == IAC_ALGORITHM_PWM_OL)
{
IDLE_TIMER_DISABLE();
digitalWrite(pinIdle1, LOW);
}
else if (configPage4.iacAlgorithm == IAC_ALGORITHM_STEP_CL || configPage4.iacAlgorithm == IAC_ALGORITHM_STEP_OL)
{
idleStepper.targetIdleStep = 1; //Home the stepper
if( checkForStepping() ) { return; } //If this is true it means there's either a step taking place or
if( !isStepperHomed() ) { return; } //Check whether homing is completed yet.
doStep();
}
}
//Any common functions associated with starting the Idle
//Typically this is enabling the PWM interrupt
static inline void enableIdle()
{
IDLE_TIMER_ENABLE();
if(configPage4.iacAlgorithm == IAC_ALGORITHM_PWM_CL || configPage4.iacAlgorithm == IAC_ALGORITHM_PWM_OL)
{
IDLE_TIMER_ENABLE();
}
else if (configPage4.iacAlgorithm == IAC_ALGORITHM_STEP_CL || configPage4.iacAlgorithm == IAC_ALGORITHM_STEP_OL)
{
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPC_vect)
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined (CORE_STM32)
static inline void idleInterrupt() //Most ARM chips can simply call a function
#endif
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) || defined (CORE_TEENSY)
{
if (idle_pwm_state)
{
@ -367,3 +425,8 @@ static inline void idleInterrupt() //Most ARM chips can simply call a function
}
}
#elif defined (CORE_STM32)
{
//No PWM idle for STM32 yet
}
#endif

View File

@ -157,8 +157,56 @@ See page 136 of the processors datasheet: http://www.atmel.com/Images/doc2549.pd
#elif defined(STM32_MCU_SERIES)
//Placeholders ONLY!
//https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F4/cores/maple/libmaple/timer.h#L51
#define MAX_TIMER_PERIOD 139808 // 2.13333333uS * 65535
#define uS_TO_TIMER_COMPARE(uS) ((uS * 15) >> 5) //Converts a given number of uS into the required number of timer ticks until that time has passed.
#define FUEL1_COUNTER (TIMER2->regs).gen->CNT
#define FUEL2_COUNTER (TIMER2->regs).gen->CNT
#define FUEL3_COUNTER (TIMER2->regs).gen->CNT
#define FUEL4_COUNTER (TIMER2->regs).gen->CNT
#define IGN1_COUNTER (TIMER3->regs).gen->CNT
#define IGN2_COUNTER (TIMER3->regs).gen->CNT
#define IGN3_COUNTER (TIMER3->regs).gen->CNT
#define IGN4_COUNTER (TIMER3->regs).gen->CNT
#define IGN5_COUNTER (TIMER1->regs).gen->CNT
#define FUEL1_COMPARE (TIMER2->regs).gen->CCR1
#define FUEL2_COMPARE (TIMER2->regs).gen->CCR2
#define FUEL3_COMPARE (TIMER2->regs).gen->CCR3
#define FUEL4_COMPARE (TIMER2->regs).gen->CCR4
#define IGN1_COMPARE (TIMER3->regs).gen->CCR1
#define IGN2_COMPARE (TIMER3->regs).gen->CCR2
#define IGN3_COMPARE (TIMER3->regs).gen->CCR3
#define IGN4_COMPARE (TIMER3->regs).gen->CCR4
#define IGN5_COMPARE (TIMER1->regs).gen->CCR1
//https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/754bc2969921f1ef262bd69e7faca80b19db7524/STM32F1/system/libmaple/include/libmaple/timer.h#L444
#define FUEL1_TIMER_ENABLE() (TIMER2->regs).gen->CCER |= TIMER_CCER_CC1E
#define FUEL2_TIMER_ENABLE() (TIMER2->regs).gen->CCER |= TIMER_CCER_CC2E
#define FUEL3_TIMER_ENABLE() (TIMER2->regs).gen->CCER |= TIMER_CCER_CC3E
#define FUEL4_TIMER_ENABLE() (TIMER2->regs).gen->CCER |= TIMER_CCER_CC4E
#define IGN1_TIMER_ENABLE() (TIMER3->regs).gen->CCER |= TIMER_CCER_CC1E
#define IGN2_TIMER_ENABLE() (TIMER3->regs).gen->CCER |= TIMER_CCER_CC2E
#define IGN3_TIMER_ENABLE() (TIMER3->regs).gen->CCER |= TIMER_CCER_CC3E
#define IGN4_TIMER_ENABLE() (TIMER3->regs).gen->CCER |= TIMER_CCER_CC4E
#define IGN5_TIMER_ENABLE() (TIMER1->regs).gen->CCER |= TIMER_CCER_CC1E
#define FUEL1_TIMER_DISABLE() (TIMER2->regs).gen->CCER &= ~TIMER_CCER_CC1E
#define FUEL2_TIMER_DISABLE() (TIMER2->regs).gen->CCER &= ~TIMER_CCER_CC2E
#define FUEL3_TIMER_DISABLE() (TIMER2->regs).gen->CCER &= ~TIMER_CCER_CC3E
#define FUEL4_TIMER_DISABLE() (TIMER2->regs).gen->CCER &= ~TIMER_CCER_CC4E
#define IGN1_TIMER_DISABLE() (TIMER3->regs).gen->CCER &= ~TIMER_CCER_CC1E
#define IGN2_TIMER_DISABLE() (TIMER3->regs).gen->CCER &= ~TIMER_CCER_CC2E
#define IGN3_TIMER_DISABLE() (TIMER3->regs).gen->CCER &= ~TIMER_CCER_CC3E
#define IGN4_TIMER_DISABLE() (TIMER3->regs).gen->CCER &= ~TIMER_CCER_CC4E
#define IGN5_TIMER_DISABLE() (TIMER1->regs).gen->CCER &= ~TIMER_CCER_CC1E
#endif
void initialiseSchedulers();

View File

@ -428,7 +428,7 @@ void setIgnitionSchedule5(void (*startCallback)(), unsigned long timeout, unsign
//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)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule1Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -450,7 +450,7 @@ static inline void fuelSchedule1Interrupt() //Most ARM chips can simply call a f
#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)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule2Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -471,7 +471,7 @@ static inline void fuelSchedule2Interrupt() //Most ARM chips can simply call a f
#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)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule3Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -492,7 +492,7 @@ static inline void fuelSchedule3Interrupt() //Most ARM chips can simply call a f
#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)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule4Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -513,7 +513,7 @@ static inline void fuelSchedule4Interrupt() //Most ARM chips can simply call a f
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPA_vect) //ignitionSchedule1
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -537,7 +537,7 @@ static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPB_vect) //ignitionSchedule2
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule2Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -561,7 +561,7 @@ static inline void ignitionSchedule2Interrupt() //Most ARM chips can simply call
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPC_vect) //ignitionSchedule3
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule3Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -585,7 +585,7 @@ static inline void ignitionSchedule3Interrupt() //Most ARM chips can simply call
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPA_vect) //ignitionSchedule4
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule4Interrupt() //Most ARM chips can simply call a function
#endif
{
@ -609,7 +609,7 @@ static inline void ignitionSchedule4Interrupt() //Most ARM chips can simply call
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER1_COMPC_vect) //ignitionSchedule5
#elif defined (CORE_TEENSY)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule5Interrupt() //Most ARM chips can simply call a function
#endif
{

View File

@ -914,7 +914,7 @@ void loop()
vvtControl();
idleControl(); //Perform any idle related actions. Even at higher frequencies, running 4x per second is sufficient.
}
if(configPage4.iacAlgorithm == 4) { idleControl(); } //Run idlecontrol every loop for stepper idle.
if(configPage4.iacAlgorithm == IAC_ALGORITHM_STEP_OL || configPage4.iacAlgorithm == IAC_ALGORITHM_STEP_CL) { idleControl(); } //Run idlecontrol every loop for stepper idle.
//Always check for sync
//Main loop runs within this clause

View File

@ -4,7 +4,7 @@
NOTE - This file and it's associated functions need a CLEARER NAME
//Purpose
We're implementing a lower frequency interrupt loop to perform calculations that are needed less often, some of which depend on time having passed (delta/time) to be meaningful.
We're implementing a lower frequency interrupt loop to perform calculations that are needed less often, some of which depend on time having passed (delta/time) to be meaningful.
//Technical
@ -12,7 +12,7 @@ Timer2 is only 8bit so we are setting the prescaler to 128 to get the most out o
Max Period = (Prescale)*(1/Frequency)*(2^8)
(See http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html)
We're after a 1ms interval so we'll need 131 intervals to reach this ( 1ms / 0.008ms per tick = 125).
We're after a 1ms interval so we'll need 131 intervals to reach this ( 1ms / 0.008ms per tick = 125).
Hence we will preload the timer with 131 cycles to leave 125 until overflow (1ms).
*/
@ -29,6 +29,8 @@ volatile uint16_t lastRPM_100ms; //Need to record this for rpmDOT calculation
#if defined (CORE_TEENSY)
IntervalTimer lowResTimer;
void oneMSInterval();
#elif defined(CORE_STM32)
void oneMSInterval();
#endif
void initialiseTimers();

View File

@ -18,10 +18,10 @@ Timers are typically low resolution (Compared to Schedulers), with maximum frequ
#include <avr/wdt.h>
#endif
void initialiseTimers()
{
void initialiseTimers()
{
#if defined(CORE_AVR) //AVR chips use the ISR for this
//Configure Timer2 for our low-freq interrupt code.
//Configure Timer2 for our low-freq interrupt code.
TCCR2B = 0x00; //Disbale Timer2 while we set it up
TCNT2 = 131; //Preload timer2 with 131 cycles, leaving 125 till overflow. As the timer runs at 125Khz, this causes overflow to occur at 1Khz = 1ms
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
@ -33,10 +33,15 @@ void initialiseTimers()
//Enable the watchdog timer for 2 second resets (Good reference: https://tushev.org/articles/arduino/5/arduino-and-watchdog-timer)
//wdt_enable(WDTO_2S); //Boooooooooo WDT is currently broken on Mega 2560 bootloaders :(
#elif defined (CORE_TEENSY)
//Uses the PIT timer on Teensy.
lowResTimer.begin(oneMSInterval, 1000);
#elif defined(CORE_STM32)
Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE);
Timer4.setPeriod(1000);
Timer4.attachCompare1Interrupt(oneMSInterval);
#endif
dwellLimit_uS = (1000 * configPage2.dwellLimit);
@ -47,27 +52,27 @@ void initialiseTimers()
//Timer2 Overflow Interrupt Vector, called when the timer overflows.
//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)
ISR(TIMER2_OVF_vect, ISR_NOBLOCK)
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
void oneMSInterval() //Most ARM chips can simply call a function
#endif
{
//Increment Loop Counters
loop100ms++;
loop250ms++;
loopSec++;
unsigned long targetOverdwellTime;
//Overdwell check
targetOverdwellTime = micros() - dwellLimit_uS; //Set a target time in the past that all coil charging must have begun after. If the coil charge began before this time, it's been running too long
//Check first whether each spark output is currently on. Only check it's dwell time if it is
if(ignitionSchedule1.Status == RUNNING) { if(ignitionSchedule1.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil1Charge(); } }
if(ignitionSchedule2.Status == RUNNING) { if(ignitionSchedule2.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil2Charge(); } }
if(ignitionSchedule3.Status == RUNNING) { if(ignitionSchedule3.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil3Charge(); } }
if(ignitionSchedule4.Status == RUNNING) { if(ignitionSchedule4.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil4Charge(); } }
if(ignitionSchedule4.Status == RUNNING) { if(ignitionSchedule4.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil4Charge(); } }
if(ignitionSchedule5.Status == RUNNING) { if(ignitionSchedule5.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil5Charge(); } }
//Loop executed every 100ms loop
@ -79,30 +84,30 @@ void oneMSInterval() //Most ARM chips can simply call a function
currentStatus.rpmDOT = (currentStatus.RPM - lastRPM_100ms) * 10; //This is the RPM per second that the engine has accelerated/decelleratedin the last loop
lastRPM_100ms = currentStatus.RPM; //Record the current RPM for next calc
}
//Loop executed every 250ms loop (1ms x 250 = 250ms)
//Anything inside this if statement will run every 250ms.
if (loop250ms == 250)
if (loop250ms == 250)
{
loop250ms = 0; //Reset Counter.
#if defined(CORE_AVR)
//wdt_reset(); //Reset watchdog timer
#endif
}
//Loop executed every 1 second (1ms x 1000 = 1000ms)
if (loopSec == 1000)
if (loopSec == 1000)
{
loopSec = 0; //Reset counter.
dwellLimit_uS = (1000 * configPage2.dwellLimit); //Update uS value incase setting has changed
if ( configPage2.ignCranklock && BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK)) { dwellLimit_uS = dwellLimit_uS * 3; } //Make sure the overdwell doesn't clobber the fixed ignition cranking if enabled.
if ( configPage2.ignCranklock && BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK)) { dwellLimit_uS = dwellLimit_uS * 3; } //Make sure the overdwell doesn't clobber the fixed ignition cranking if enabled.
//**************************************************************************************************************************************************
//This updates the runSecs variable
//If the engine is running or cranking, we need ot update the run time counter.
if (BIT_CHECK(currentStatus.engine, BIT_ENGINE_RUN))
{ //NOTE - There is a potential for a ~1sec gap between engine crank starting and ths runSec number being incremented. This may delay ASE!
{ //NOTE - There is a potential for a ~1sec gap between engine crank starting and ths runSec number being incremented. This may delay ASE!
if (currentStatus.runSecs <= 254) //Ensure we cap out at 255 and don't overflow. (which would reset ASE)
{ currentStatus.runSecs++; } //Increment our run counter by 1 second.
}
@ -116,14 +121,14 @@ void oneMSInterval() //Most ARM chips can simply call a function
//**************************************************************************************************************************************************
//Check the fan output status
if (configPage4.fanEnable == 1)
{
fanControl(); // Fucntion to turn the cooling fan on/off
{
fanControl(); // Fucntion to turn the cooling fan on/off
}
//Check whether fuel pump priming is complete
if(!fpPrimed)
{
if(currentStatus.secl >= configPage1.fpPrime)
if(currentStatus.secl >= configPage1.fpPrime)
{
fpPrimed = true; //Mark the priming as being completed
if(currentStatus.RPM == 0) { digitalWrite(pinFuelPump, LOW); fuelPumpOn = false; } //If we reach here then the priming is complete, however only turn off the fuel pump if the engine isn't running
@ -140,7 +145,7 @@ void oneMSInterval() //Most ARM chips can simply call a function
}
else if (flexCounter > 151) //1 pulse buffer
{
if(flexCounter < 169)
{
currentStatus.ethanolPct = 100;
@ -161,13 +166,13 @@ void oneMSInterval() //Most ARM chips can simply call a function
//Off by 1 error check
if (currentStatus.ethanolPct == 1) { currentStatus.ethanolPct = 0; }
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
//Reset Timer2 to trigger in another ~1ms
//Reset Timer2 to trigger in another ~1ms
TCNT2 = 131; //Preload timer2 with 100 cycles, leaving 156 till overflow.
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
#endif
#endif
}

View File

@ -30,11 +30,31 @@ int freeRam ()
// The difference is the free, available ram.
return (uint16_t)stackTop - heapTop;
#elif defined(CORE_STM32)
//Figure this out some_time
return 0;
#endif
}
void setPinMapping(byte boardID)
{
//This is dumb, but it'll do for now to get things compiling
#if defined(CORE_STM32)
#define A0 0
#define A1 1
#define A2 2
#define A3 3
#define A4 4
#define A5 5
#define A6 6
#define A7 7
#define A8 8
#define A9 9
#define A13 13
#define A14 14
#define A15 15
#endif
switch (boardID)
{
case 0: