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 ; framework-arduinoststm32
board = genericSTM32F103RB board = genericSTM32F103RB
lib_deps = EEPROM 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] [platformio]
@ -44,3 +52,4 @@ env_default = megaatmega2560
;env_default = teensy35 ;env_default = teensy35
;env_default = LaunchPad_tm4c1294ncpdt ;env_default = LaunchPad_tm4c1294ncpdt
;env_default = genericSTM32F103RB ;env_default = genericSTM32F103RB
;env_default = bluepill_f103c8

View File

@ -38,9 +38,12 @@
; name = type, min, max; ; name = type, min, max;
; ;
; type List: value will be index. ; type List: value will be index.
rpmhigh = 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 rpmwarn = scalar, U16, "rpm", 1, 0, 0, 30000, 0
rpmdang = 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] [Constants]
;---------------------------------------------------------------------------- ;----------------------------------------------------------------------------
@ -215,11 +218,11 @@ page = 2
flexFuelLow = scalar, U08, 57, "%", 1.0, 0.0, 0.0, 250.0, 0 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 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 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 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 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" 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" 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" 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 unused4-7 = scalar, U08, 7, "ADC", 1, 0, 0, 255, 0
IdleAdvRPM = scalar, U08, 8, "RPM", 100, 0, 0, 1200, 0 IdleAdvRPM = scalar, U08, 8, "RPM", 100, 0, 0, 1200, 0
#if CELSIUS #if CELSIUS
@ -433,7 +436,7 @@ page = 7
iacCrankBins = array, U08, 48, [4], "F", 1.8, -22.23, -40, 215, 0 iacCrankBins = array, U08, 48, [4], "F", 1.8, -22.23, -40, 215, 0
#endif #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" iacStepTime = bits , U08, 52, [3:5], "1", "2", "3", "4", "5", "6"
iacChannels = bits, U08, 52, [6:6], "1", "2" iacChannels = bits, U08, 52, [6:6], "1", "2"
iacPWMdir = bits , U08, 52, [7:7], "Normal", "Reverse" iacPWMdir = bits , U08, 52, [7:7], "Normal", "Reverse"
@ -683,6 +686,7 @@ page = 10
defaultValue = pinLayout, 1 defaultValue = pinLayout, 1
defaultValue = TrigPattern, 0 defaultValue = TrigPattern, 0
defaultValue = useResync, 1
defaultValue = sparkMode, 0 defaultValue = sparkMode, 0
defaultValue = indInjAng, 0 defaultValue = indInjAng, 0
defaultValue = inj1Ang, 355 defaultValue = inj1Ang, 355
@ -788,11 +792,11 @@ menuDialog = main
subMenu = warmup, "Warmup Enrichment" subMenu = warmup, "Warmup Enrichment"
subMenu = std_separator subMenu = std_separator
subMenu = idleSettings, "Idle Control" 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 = iacPwm_curve, "Idle - PWM Duty Cycle", 7, { iacAlgorithm == 2 }
subMenu = iacPwmCrank_curve, "Idle - PWM Cranking 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 = iacStep_curve, "Idle - Stepper Motor", 7, { iacAlgorithm == 4 }
subMenu = iacStepCrank_curve, "Idle - Stepper Motor Cranking", 7, { iacAlgorithm == 4 || iacAlgorithm == 5 } subMenu = iacStepCrank_curve, "Idle - Stepper Motor Cranking", 7, { iacAlgorithm == 4 }
menu = "&Accessories" menu = "&Accessories"
subMenu = fanSettings, "Thermo Fan" 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. " 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." 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." numteeth = "Number of teeth on Primary Wheel."
TrigSpeed = "Primary trigger speed." TrigSpeed = "Primary trigger speed."
onetwo = "Number of Missing teeth on Primary Wheel." 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." 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)." 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" 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 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 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" 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)" 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 } field = "Idle valve direction", iacPWMdir, { iacAlgorithm == 2 || iacAlgorithm == 3 }
dialog = closedloop_idle, "Closed loop Idle" dialog = closedloop_idle, "Closed loop Idle"
field = "P", idleKP, { iacAlgorithm == 3 } field = "P", idleKP, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "I", idleKI, { iacAlgorithm == 3 } field = "I", idleKI, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "D", idleKD, { iacAlgorithm == 3 } field = "D", idleKD, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "Minimum valve duty", iacCLminDuty, { iacAlgorithm == 3 } field = "Minimum valve duty", iacCLminDuty, { iacAlgorithm == 3 }
field = "Maximum valve duty", iacCLmaxDuty, { 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 = "Note: This is the number of revolutions that will be skipped during"
field = "cranking before the injectors and coils are fired" field = "cranking before the injectors and coils are fired"
field = "Trigger edge", TrigEdge 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 = "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 dialog = sparkSettings,"Spark Settings",4
field = "Spark output mode", sparkMode field = "Spark output mode", sparkMode
@ -1838,8 +1844,8 @@ cmdtestspk450dc = "E\x03\x0C"
deadValue = { 0 } ; Convenient unchanging value. deadValue = { 0 } ; Convenient unchanging value.
ochGetCommand = "A" ochGetCommand = "A"
ochBlockSize = 41
ochBlockSize = 39
secl = scalar, U08, 0, "sec", 1.000, 0.000 secl = scalar, U08, 0, "sec", 1.000, 0.000
squirt = scalar, U08, 1, "bits", 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] errorNum = bits, U08, 36, [0:1]
currentError = bits, U08, 36, [2:7] currentError = bits, U08, 36, [2:7]
boostTarget = scalar, U08, 37, "kPa", 2.000, 0.000 boostTarget = scalar, U08, 37, "kPa", 2.000, 0.000
testoutputs = scalar, U08, 38, "bits", 1.000, 0.000 boostDuty = scalar, U08, 38, "%", 1.000, 0.000
testenabled = bits, U08, 38, [0:0] 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
testactive = bits, U08, 38, [1:1] testoutputs = scalar, U08, 40, "bits", 1.000, 0.000
;"", 1.0, 0.0 testenabled = bits, U08, 40, [0:0]
testactive = bits, U08, 40, [1:1]
; Computed output channels. See "megatuneExamples.ini" for all the ; Computed output channels. See "megatuneExamples.ini" for all the
; pre-defined variables, search for "???" and you'll see them. ; pre-defined variables, search for "???" and you'll see them.
@ -1997,25 +2004,31 @@ cmdtestspk450dc = "E\x03\x0C"
entry = spark, "Spark", int, "%d" entry = spark, "Spark", int, "%d"
entry = egoCorrection, "Gego", int, "%d" entry = egoCorrection, "Gego", int, "%d"
entry = airCorrection, "Gair", int, "%d" entry = airCorrection, "Gair", int, "%d"
entry = batCorrection, "Gbattery", int, "%d"
entry = warmupEnrich, "Gwarm", int, "%d" entry = warmupEnrich, "Gwarm", int, "%d"
;entry = baroCorrection, "Gbaro", int, "%d" ;entry = baroCorrection, "Gbaro", int, "%d"
entry = gammaEnrich, "Gammae", int, "%d" entry = gammaEnrich, "Gammae", int, "%d"
entry = accelEnrich, "TPSacc", int, "%d" entry = accelEnrich, "TPSacc", int, "%d"
entry = veCurr, "VE", int, "%d" entry = veCurr, "VE", int, "%d"
entry = pulseWidth, "PW", float, "%.1f" entry = pulseWidth, "PW", float, "%.1f"
entry = afrTarget, "AFR Target", float, "%.3f" entry = afrTarget, "AFR Target", float, "%.3f"
entry = pulseWidth, "PW2", float, "%.1f" entry = pulseWidth, "PW2", float, "%.1f"
entry = dutyCycle, "DutyCycle1", float, "%.1f" entry = dutyCycle, "DutyCycle1", float, "%.1f"
entry = dutyCycle, "DutyCycle2", float, "%.1f" entry = dutyCycle, "DutyCycle2", float, "%.1f"
entry = TPSdot, "TPS DOT", int, "%d" entry = TPSdot, "TPS DOT", int, "%d"
entry = advance, "Ignition Advance", int,"%d" entry = advance, "Advance", int, "%d"
entry = dwell, "Dwell", int, "%d" entry = dwell, "Dwell", int, "%d"
entry = batteryVoltage, "Battery V", float, "%.1f" entry = batteryVoltage, "Battery V", float, "%.1f"
entry = rpmDOT, "rpm/s", int, "%d" entry = rpmDOT, "rpm/s", int, "%d"
entry = flex, "%", int, "%d" entry = flex, "Eth %", int, "%d", { flexEnabled }
entry = errorNum, "Error #", int, "%d" entry = errorNum, "Error #", int, "%d"
entry = currentError, "Error ID", 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] [LoggerDefinition]
; valid logger types: composite, tooth, trigger, csv ; 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.SetTunings(configPage3.boostKP, configPage3.boostKI, configPage3.boostKD);
boostPID.SetMode(AUTOMATIC); //Turn PID on boostPID.SetMode(AUTOMATIC); //Turn PID on
currentStatus.boostDuty = 0;
boostCounter = 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 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(); 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) TIMSK1 |= (1 << OCIE1A); //Turn on the compare unit (ie turn on the interrupt)
} }
else { TIMSK1 &= ~(1 << OCIE1A); } // Disable timer channel 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 //YET TO BE IMPLEMENTED ON TEENSY
void initialiseAuxPWM() { } void initialiseAuxPWM() { }
void boostControl() { } void boostControl() { }

View File

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

View File

@ -215,8 +215,13 @@ void sendValues(int packetlength, byte portNum)
if (portNum == 3) if (portNum == 3)
{ {
//CAN serial //CAN serial
Serial3.write("A"); //confirm cmd type #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //ATmega2561 does not have Serial3
Serial3.write(packetlength); //confirm no of byte to be sent 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 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[35] = currentStatus.flexIgnCorrection; //Ignition correction (Increased degrees of advance) for flex fuel
response[36] = getNextError(); response[36] = getNextError();
response[37] = currentStatus.boostTarget; response[37] = currentStatus.boostTarget;
response[38] = currentStatus.testOutputs; response[38] = currentStatus.boostDuty;
response[39] = currentStatus.idleLoad;
response[40] = currentStatus.testOutputs;
//cli(); //cli();
if (portNum == 0) { Serial.write(response, (size_t)packetlength); } 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(); //sei();
return; 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 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 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 int triggerActualTeeth;
volatile unsigned long triggerFilterTime; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering) 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; // 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 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) 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 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() void triggerSetup_missingTooth()
{ {
triggerToothAngle = 360 / configPage2.triggerTeeth; //The number of degrees that passes from tooth to tooth 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 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 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; secondDerivEnabled = false;
@ -232,15 +232,15 @@ void triggerSec_DualWheel()
toothLastSecToothTime = curTime2; toothLastSecToothTime = curTime2;
triggerSecFilterTime = curGap2 >> 2; //Set filter at 25% of the current speed triggerSecFilterTime = curGap2 >> 2; //Set filter at 25% of the current speed
toothCurrentCount = configPage2.triggerTeeth;
if(!currentStatus.hasSync) if(!currentStatus.hasSync)
{ {
toothLastToothTime = micros(); toothLastToothTime = micros();
toothLastMinusOneToothTime = (toothOneTime - 6000000) / configPage2.triggerTeeth; //Fixes RPM at 10rpm until a full revolution has taken place toothLastMinusOneToothTime = (toothOneTime - 6000000) / configPage2.triggerTeeth; //Fixes RPM at 10rpm until a full revolution has taken place
toothCurrentCount = configPage2.triggerTeeth;
currentStatus.hasSync = true; currentStatus.hasSync = true;
} }
else if (configPage2.useResync) { toothCurrentCount = configPage2.triggerTeeth; }
revolutionOne = 1; //Sequential revolution reset 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. 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 = (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() void triggerPri_4G63()
@ -561,20 +563,40 @@ void triggerPri_4G63()
void triggerSec_4G63() void triggerSec_4G63()
{ {
//byte crankState = READ_PRI_TRIGGER(); //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(); curTime2 = micros();
curGap2 = curTime2 - toothLastSecToothTime; curGap2 = curTime2 - toothLastSecToothTime;
if ( curGap2 < triggerSecFilterTime ) { return; } if ( curGap2 < triggerSecFilterTime ) { return; }
toothLastSecToothTime = curTime2; 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) 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). triggerFilterTime = 1500; //If this is removed, can have trouble getting sync again after the engine is turned off (but ECU not reset).
if(READ_PRI_TRIGGER())// && (crankState == digitalRead(pinTrigger)))
//Check the status of the crank trigger
//bool crank = digitalRead(pinTrigger);
if(READ_PRI_TRIGGER())
{ {
toothCurrentCount = 4; //If the crank trigger is currently HIGH, it means we're on tooth #1 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 /* 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 toothCurrentCount = 7; //If the crank trigger is currently HIGH, it means we're on the falling edge of the narrow crank tooth
toothLastMinusOneToothTime = toothLastToothTime; 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; return;
} }
@ -866,11 +875,12 @@ void triggerPri_Audi135()
curGap = curTime - toothSystemLastToothTime; curGap = curTime - toothSystemLastToothTime;
if ( curGap < triggerFilterTime ) { return; } if ( curGap < triggerFilterTime ) { return; }
toothSystemCount++; toothSystemCount++;
toothSystemLastToothTime = curTime;
addToothLogEntry(curGap);
if ( !currentStatus.hasSync ) { toothLastToothTime = curTime; return; } if ( !currentStatus.hasSync ) { toothLastToothTime = curTime; return; }
if ( toothSystemCount < 3 ) { return; } //We only proceed for every third tooth if ( toothSystemCount < 3 ) { return; } //We only proceed for every third tooth
addToothLogEntry(curGap);
toothSystemLastToothTime = curTime;
toothSystemCount = 0; toothSystemCount = 0;
toothCurrentCount++; //Increment the tooth counter toothCurrentCount++; //Increment the tooth counter
@ -904,9 +914,7 @@ void triggerSec_Audi135()
currentStatus.hasSync = true; currentStatus.hasSync = true;
toothSystemCount = 3; //Need to set this to 3 so that the next primary tooth is counted toothSystemCount = 3; //Need to set this to 3 so that the next primary tooth is counted
} }
else{ else if (configPage2.useResync) { toothCurrentCount = 0; }
toothCurrentCount = 0;
}
revolutionOne = 1; //Sequential revolution reset revolutionOne = 1; //Sequential revolution reset
} }

View File

@ -5,6 +5,12 @@
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
#define CORE_AVR #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 #endif
//Handy bitsetting macros //Handy bitsetting macros
@ -208,6 +214,8 @@ struct statuses {
byte boostTarget; byte boostTarget;
byte testOutputs; byte testOutputs;
bool testActive; bool testActive;
byte boostDuty;
byte idleLoad; //Either the current steps or current duty cycle for the idle control.
//Helpful bitwise operations: //Helpful bitwise operations:
//Useful reference: http://playground.arduino.cc/Code/BitMath //Useful reference: http://playground.arduino.cc/Code/BitMath
@ -326,7 +334,7 @@ struct config2 {
byte TrigEdgeSec : 1; byte TrigEdgeSec : 1;
byte fuelPumpPin : 6; byte fuelPumpPin : 6;
byte unused4_6b : 1; byte useResync : 1;
byte unused4_7; byte unused4_7;
byte IdleAdvRPM; byte IdleAdvRPM;

View File

@ -4,6 +4,13 @@
#include "globals.h" #include "globals.h"
#include "table.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_FORWARD 0
#define STEPPER_BACKWARD 1 #define STEPPER_BACKWARD 1
#define IDLE_TABLE_SIZE 10 #define IDLE_TABLE_SIZE 10
@ -12,8 +19,8 @@ enum StepperStatus {SOFF, STEPPING, COOLING}; //The 2 statuses that a stepper ca
struct StepperIdle struct StepperIdle
{ {
unsigned int curIdleStep; //Tracks the current location of the stepper int curIdleStep; //Tracks the current location of the stepper
unsigned int targetIdleStep; //What the targetted step is int targetIdleStep; //What the targetted step is
volatile StepperStatus stepperStatus; volatile StepperStatus stepperStatus;
volatile unsigned long stepStartTime; //The time the curren 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_ENABLE() FTM2_C0SC |= FTM_CSC_CHIE
#define IDLE_TIMER_DISABLE() 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 #endif
struct table2D iacClosedLoopTable; struct table2D iacClosedLoopTable;
@ -54,11 +70,16 @@ volatile byte idle2_pin_mask;
volatile bool idle_pwm_state; volatile bool idle_pwm_state;
unsigned int idle_pwm_max_count; //Used for variable PWM frequency unsigned int idle_pwm_max_count; //Used for variable PWM frequency
volatile unsigned int idle_pwm_cur_value; volatile unsigned int idle_pwm_cur_value;
long idle_pid_target_value;
long idle_pwm_target_value; long idle_pwm_target_value;
long idle_cl_target_rpm; long idle_cl_target_rpm;
byte idleCounter; //Used for tracking the number of calls to the idle control function
void initialiseIdle(); void initialiseIdle();
static inline void disableIdle(); static inline void disableIdle();
static inline void enableIdle(); static inline void enableIdle();
static inline byte isStepperHomed();
static inline byte checkForStepping();
static inline void doStep();
#endif #endif

View File

@ -13,7 +13,7 @@ These functions cover the PWM and stepper idle control
Idle Control Idle Control
Currently limited to on/off control and open loop PWM and stepper drive 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() void initialiseIdle()
{ {
@ -75,16 +75,17 @@ void initialiseIdle()
// enable IRQ Interrupt // enable IRQ Interrupt
NVIC_ENABLE_IRQ(IRQ_FTM2); NVIC_ENABLE_IRQ(IRQ_FTM2);
#elif defined(MCU_STM32F103RB)
#endif #endif
//Initialising comprises of setting the 2D tables with the relevant values from the config pages //Initialising comprises of setting the 2D tables with the relevant values from the config pages
switch(configPage4.iacAlgorithm) switch(configPage4.iacAlgorithm)
{ {
case 0: case IAC_ALGORITHM_NONE: //Case 0 is no idle control ('None')
//Case 0 is no idle control ('None')
break; break;
case 1: case IAC_ALGORITHM_ONOFF:
//Case 1 is on/off idle control //Case 1 is on/off idle control
if (currentStatus.coolant < configPage4.iacFastTemp) if (currentStatus.coolant < configPage4.iacFastTemp)
{ {
@ -92,13 +93,14 @@ void initialiseIdle()
} }
break; break;
case 2: case IAC_ALGORITHM_PWM_OL:
//Case 2 is PWM open loop //Case 2 is PWM open loop
iacPWMTable.xSize = 10; iacPWMTable.xSize = 10;
iacPWMTable.valueSize = SIZE_BYTE; iacPWMTable.valueSize = SIZE_BYTE;
iacPWMTable.values = configPage4.iacOLPWMVal; iacPWMTable.values = configPage4.iacOLPWMVal;
iacPWMTable.axisX = configPage4.iacBins; iacPWMTable.axisX = configPage4.iacBins;
iacCrankDutyTable.xSize = 4; iacCrankDutyTable.xSize = 4;
iacCrankDutyTable.valueSize = SIZE_BYTE; iacCrankDutyTable.valueSize = SIZE_BYTE;
iacCrankDutyTable.values = configPage4.iacCrankDuty; iacCrankDutyTable.values = configPage4.iacCrankDuty;
@ -112,7 +114,7 @@ void initialiseIdle()
enableIdle(); enableIdle();
break; break;
case 3: case IAC_ALGORITHM_PWM_CL:
//Case 3 is PWM closed loop //Case 3 is PWM closed loop
iacClosedLoopTable.xSize = 10; iacClosedLoopTable.xSize = 10;
iacClosedLoopTable.valueSize = SIZE_BYTE; 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.SetOutputLimits(percentage(configPage1.iacCLminDuty, idle_pwm_max_count), percentage(configPage1.iacCLmaxDuty, idle_pwm_max_count));
idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD); idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD);
idlePID.SetMode(AUTOMATIC); //Turn PID on idlePID.SetMode(AUTOMATIC); //Turn PID on
idleCounter = 0;
break; break;
case 4: case IAC_ALGORITHM_STEP_OL:
//Case 2 is Stepper open loop //Case 2 is Stepper open loop
iacStepTable.xSize = 10; iacStepTable.xSize = 10;
iacStepTable.valueSize = SIZE_BYTE; iacStepTable.valueSize = SIZE_BYTE;
@ -148,12 +152,14 @@ void initialiseIdle()
//homeStepper(); //Returns the stepper to the 'home' position //homeStepper(); //Returns the stepper to the 'home' position
completedHomeSteps = 0; completedHomeSteps = 0;
idleStepper.curIdleStep = 0;
idleStepper.stepperStatus = SOFF; idleStepper.stepperStatus = SOFF;
break; break;
case 5: case IAC_ALGORITHM_STEP_CL:
//Case 5 is Stepper closed loop //Case 5 is Stepper closed loop
iacClosedLoopTable.xSize = 10; iacClosedLoopTable.xSize = 10;
iacClosedLoopTable.valueSize = SIZE_BYTE;
iacClosedLoopTable.values = configPage4.iacCLValues; iacClosedLoopTable.values = configPage4.iacCLValues;
iacClosedLoopTable.axisX = configPage4.iacBins; iacClosedLoopTable.axisX = configPage4.iacBins;
@ -162,11 +168,18 @@ void initialiseIdle()
iacCrankStepsTable.axisX = configPage4.iacCrankBins; iacCrankStepsTable.axisX = configPage4.iacCrankBins;
iacStepTime = configPage4.iacStepTime * 1000; iacStepTime = configPage4.iacStepTime * 1000;
homeStepper(); //Returns the stepper to the 'home' position completedHomeSteps = 0;
idleCounter = 0;
idleStepper.curIdleStep = 0;
idleStepper.stepperStatus = SOFF; 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; break;
} }
idleInitComplete = configPage4.iacAlgorithm; //Sets which idle method was initialised idleInitComplete = configPage4.iacAlgorithm; //Sets which idle method was initialised
currentStatus.idleLoad = 0;
} }
void idleControl() void idleControl()
@ -175,10 +188,10 @@ void idleControl()
switch(configPage4.iacAlgorithm) switch(configPage4.iacAlgorithm)
{ {
case 0: //Case 0 is no idle control ('None') case IAC_ALGORITHM_NONE: //Case 0 is no idle control ('None')
break; 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 if ( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < configPage4.iacFastTemp) //All temps are offset by 40 degrees
{ {
digitalWrite(pinIdle1, HIGH); digitalWrite(pinIdle1, HIGH);
@ -187,7 +200,7 @@ void idleControl()
else if (idleOn) { digitalWrite(pinIdle1, LOW); idleOn = false; } else if (idleOn) { digitalWrite(pinIdle1, LOW); idleOn = false; }
break; break;
case 2: //Case 2 is PWM open loop case IAC_ALGORITHM_PWM_OL: //Case 2 is PWM open loop
//Check for cranking pulsewidth //Check for cranking pulsewidth
if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) ) if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
{ {
@ -203,70 +216,37 @@ void idleControl()
if( currentStatus.idleDuty == 0 ) { disableIdle(); break; } if( currentStatus.idleDuty == 0 ) { disableIdle(); break; }
enableIdle(); enableIdle();
idle_pwm_target_value = percentage(currentStatus.idleDuty, idle_pwm_max_count); idle_pwm_target_value = percentage(currentStatus.idleDuty, idle_pwm_max_count);
currentStatus.idleLoad = currentStatus.idleDuty >> 1;
idleOn = true; idleOn = true;
} }
break; 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?) //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 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(); idlePID.Compute();
idle_pwm_target_value = idle_pid_target_value;
if( idle_pwm_target_value == 0 ) { disableIdle(); } if( idle_pwm_target_value == 0 ) { disableIdle(); }
else{ enableIdle(); } //Turn on the C compare unit (ie turn on the interrupt) 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; //idle_pwm_target_value = 104;
idleCounter++;
break; 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 //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( 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(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( 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 //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 //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 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 doStep();
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;
} }
else if( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < iacStepTable.axisX[IDLE_TABLE_SIZE-1]) 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 //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 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 doStep();
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;
} }
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; 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 if( completedHomeSteps < (configPage4.iacStepHome * 3) ) //Home steps are divided by 3 from TS
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(pinStepperDir, STEPPER_BACKWARD); //Sets stepper direction to backwards
{ digitalWrite(pinStepperStep, HIGH);
digitalWrite(pinStepperStep, HIGH); idleStepper.stepStartTime = micros();
delayMicroseconds(iacStepTime); idleStepper.stepperStatus = STEPPING;
digitalWrite(pinStepperStep, LOW); completedHomeSteps++;
delayMicroseconds(iacStepTime); idleOn = true;
} return false;
digitalWrite(pinStepperDir, STEPPER_FORWARD); }
idleStepper.curIdleStep = 0; return true;
idleStepper.targetIdleStep = 0; }
idleStepper.stepperStatus = SOFF;
/*
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 //This function simply turns off the idle PWM and sets the pin low
static inline void disableIdle() static inline void disableIdle()
{ {
IDLE_TIMER_DISABLE(); if(configPage4.iacAlgorithm == IAC_ALGORITHM_PWM_CL || configPage4.iacAlgorithm == IAC_ALGORITHM_PWM_OL)
digitalWrite(pinIdle1, LOW); {
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 //Any common functions associated with starting the Idle
//Typically this is enabling the PWM interrupt //Typically this is enabling the PWM interrupt
static inline void enableIdle() 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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPC_vect) 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 static inline void idleInterrupt() //Most ARM chips can simply call a function
#endif #endif
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) || defined (CORE_TEENSY)
{ {
if (idle_pwm_state) 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) #elif defined(STM32_MCU_SERIES)
//Placeholders ONLY! //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 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 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 #endif
void initialiseSchedulers(); void initialiseSchedulers();

View File

@ -428,7 +428,7 @@ void setIgnitionSchedule5(void (*startCallback)(), unsigned long timeout, unsign
//Timer3A (fuel schedule 1) Compare Vector //Timer3A (fuel schedule 1) Compare Vector
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //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_COMPA_vect, ISR_NOBLOCK) //fuelSchedules 1 and 5 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 static inline void fuelSchedule1Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPB_vect, ISR_NOBLOCK) //fuelSchedule2 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 static inline void fuelSchedule2Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPC_vect, ISR_NOBLOCK) //fuelSchedule3 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 static inline void fuelSchedule3Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPB_vect, ISR_NOBLOCK) //fuelSchedule4 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 static inline void fuelSchedule4Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPA_vect) //ignitionSchedule1 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 static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPB_vect) //ignitionSchedule2 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 static inline void ignitionSchedule2Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPC_vect) //ignitionSchedule3 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 static inline void ignitionSchedule3Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPA_vect) //ignitionSchedule4 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 static inline void ignitionSchedule4Interrupt() //Most ARM chips can simply call a function
#endif #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 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER1_COMPC_vect) //ignitionSchedule5 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 static inline void ignitionSchedule5Interrupt() //Most ARM chips can simply call a function
#endif #endif
{ {

View File

@ -914,7 +914,7 @@ void loop()
vvtControl(); vvtControl();
idleControl(); //Perform any idle related actions. Even at higher frequencies, running 4x per second is sufficient. 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 //Always check for sync
//Main loop runs within this clause //Main loop runs within this clause

View File

@ -4,7 +4,7 @@
NOTE - This file and it's associated functions need a CLEARER NAME NOTE - This file and it's associated functions need a CLEARER NAME
//Purpose //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 //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) Max Period = (Prescale)*(1/Frequency)*(2^8)
(See http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html) (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). 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) #if defined (CORE_TEENSY)
IntervalTimer lowResTimer; IntervalTimer lowResTimer;
void oneMSInterval(); void oneMSInterval();
#elif defined(CORE_STM32)
void oneMSInterval();
#endif #endif
void initialiseTimers(); void initialiseTimers();

View File

@ -18,10 +18,10 @@ Timers are typically low resolution (Compared to Schedulers), with maximum frequ
#include <avr/wdt.h> #include <avr/wdt.h>
#endif #endif
void initialiseTimers() void initialiseTimers()
{ {
#if defined(CORE_AVR) //AVR chips use the ISR for this #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 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 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 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) //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 :( //wdt_enable(WDTO_2S); //Boooooooooo WDT is currently broken on Mega 2560 bootloaders :(
#elif defined (CORE_TEENSY) #elif defined (CORE_TEENSY)
//Uses the PIT timer on Teensy. //Uses the PIT timer on Teensy.
lowResTimer.begin(oneMSInterval, 1000); lowResTimer.begin(oneMSInterval, 1000);
#elif defined(CORE_STM32)
Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE);
Timer4.setPeriod(1000);
Timer4.attachCompare1Interrupt(oneMSInterval);
#endif #endif
dwellLimit_uS = (1000 * configPage2.dwellLimit); dwellLimit_uS = (1000 * configPage2.dwellLimit);
@ -47,27 +52,27 @@ void initialiseTimers()
//Timer2 Overflow Interrupt Vector, called when the timer overflows. //Timer2 Overflow Interrupt Vector, called when the timer overflows.
//Executes every ~1ms. //Executes every ~1ms.
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
ISR(TIMER2_OVF_vect, ISR_NOBLOCK) ISR(TIMER2_OVF_vect, ISR_NOBLOCK)
#elif defined (CORE_TEENSY) #elif defined (CORE_TEENSY) || defined(CORE_STM32)
void oneMSInterval() //Most ARM chips can simply call a function void oneMSInterval() //Most ARM chips can simply call a function
#endif #endif
{ {
//Increment Loop Counters //Increment Loop Counters
loop100ms++; loop100ms++;
loop250ms++; loop250ms++;
loopSec++; loopSec++;
unsigned long targetOverdwellTime; unsigned long targetOverdwellTime;
//Overdwell check //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 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 //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(ignitionSchedule1.Status == RUNNING) { if(ignitionSchedule1.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil1Charge(); } }
if(ignitionSchedule2.Status == RUNNING) { if(ignitionSchedule2.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil2Charge(); } } if(ignitionSchedule2.Status == RUNNING) { if(ignitionSchedule2.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil2Charge(); } }
if(ignitionSchedule3.Status == RUNNING) { if(ignitionSchedule3.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil3Charge(); } } 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(); } } if(ignitionSchedule5.Status == RUNNING) { if(ignitionSchedule5.startTime < targetOverdwellTime && configPage2.useDwellLim) { endCoil5Charge(); } }
//Loop executed every 100ms loop //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 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 lastRPM_100ms = currentStatus.RPM; //Record the current RPM for next calc
} }
//Loop executed every 250ms loop (1ms x 250 = 250ms) //Loop executed every 250ms loop (1ms x 250 = 250ms)
//Anything inside this if statement will run every 250ms. //Anything inside this if statement will run every 250ms.
if (loop250ms == 250) if (loop250ms == 250)
{ {
loop250ms = 0; //Reset Counter. loop250ms = 0; //Reset Counter.
#if defined(CORE_AVR) #if defined(CORE_AVR)
//wdt_reset(); //Reset watchdog timer //wdt_reset(); //Reset watchdog timer
#endif #endif
} }
//Loop executed every 1 second (1ms x 1000 = 1000ms) //Loop executed every 1 second (1ms x 1000 = 1000ms)
if (loopSec == 1000) if (loopSec == 1000)
{ {
loopSec = 0; //Reset counter. loopSec = 0; //Reset counter.
dwellLimit_uS = (1000 * configPage2.dwellLimit); //Update uS value incase setting has changed 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 //This updates the runSecs variable
//If the engine is running or cranking, we need ot update the run time counter. //If the engine is running or cranking, we need ot update the run time counter.
if (BIT_CHECK(currentStatus.engine, BIT_ENGINE_RUN)) 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) 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. { 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 //Check the fan output status
if (configPage4.fanEnable == 1) 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 //Check whether fuel pump priming is complete
if(!fpPrimed) if(!fpPrimed)
{ {
if(currentStatus.secl >= configPage1.fpPrime) if(currentStatus.secl >= configPage1.fpPrime)
{ {
fpPrimed = true; //Mark the priming as being completed 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 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 else if (flexCounter > 151) //1 pulse buffer
{ {
if(flexCounter < 169) if(flexCounter < 169)
{ {
currentStatus.ethanolPct = 100; currentStatus.ethanolPct = 100;
@ -161,13 +166,13 @@ void oneMSInterval() //Most ARM chips can simply call a function
//Off by 1 error check //Off by 1 error check
if (currentStatus.ethanolPct == 1) { currentStatus.ethanolPct = 0; } if (currentStatus.ethanolPct == 1) { currentStatus.ethanolPct = 0; }
} }
} }
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this #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. TCNT2 = 131; //Preload timer2 with 100 cycles, leaving 156 till overflow.
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag 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. // The difference is the free, available ram.
return (uint16_t)stackTop - heapTop; return (uint16_t)stackTop - heapTop;
#elif defined(CORE_STM32)
//Figure this out some_time
return 0;
#endif #endif
} }
void setPinMapping(byte boardID) 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) switch (boardID)
{ {
case 0: case 0: