diff --git a/platformio.ini b/platformio.ini index 222e59e3..f238e787 100644 --- a/platformio.ini +++ b/platformio.ini @@ -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 diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 1ea6b57b..afdaa897 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -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 diff --git a/reference/wiki/Speeduino logo.png b/reference/wiki/Speeduino logo.png new file mode 100644 index 00000000..7408481e Binary files /dev/null and b/reference/wiki/Speeduino logo.png differ diff --git a/reference/wiki/Speeduino logo_med.png b/reference/wiki/Speeduino logo_med.png new file mode 100644 index 00000000..7e0e2b8d Binary files /dev/null and b/reference/wiki/Speeduino logo_med.png differ diff --git a/reference/wiki/Speeduino logo_sml.png b/reference/wiki/Speeduino logo_sml.png new file mode 100644 index 00000000..51d2c455 Binary files /dev/null and b/reference/wiki/Speeduino logo_sml.png differ diff --git a/reference/wiki/boost/boost_map.png b/reference/wiki/boost/boost_map.png new file mode 100644 index 00000000..d45e2d54 Binary files /dev/null and b/reference/wiki/boost/boost_map.png differ diff --git a/reference/wiki/boost/boost_settings.png b/reference/wiki/boost/boost_settings.png new file mode 100644 index 00000000..c20c97b4 Binary files /dev/null and b/reference/wiki/boost/boost_settings.png differ diff --git a/reference/wiki/constants/triggerSettings.png b/reference/wiki/constants/triggerSettings.png index 4daee37e..4341592e 100644 Binary files a/reference/wiki/constants/triggerSettings.png and b/reference/wiki/constants/triggerSettings.png differ diff --git a/reference/wiki/flex/flex_settings.png b/reference/wiki/flex/flex_settings.png new file mode 100644 index 00000000..5a710bd3 Binary files /dev/null and b/reference/wiki/flex/flex_settings.png differ diff --git a/reference/wiki/idle/idle_settings.png b/reference/wiki/idle/idle_settings.png new file mode 100644 index 00000000..f41d4b81 Binary files /dev/null and b/reference/wiki/idle/idle_settings.png differ diff --git a/reference/wiki/tuning/accel.png b/reference/wiki/tuning/accel.png index eedbcad0..9813f171 100644 Binary files a/reference/wiki/tuning/accel.png and b/reference/wiki/tuning/accel.png differ diff --git a/speeduino/auxiliaries.ino b/speeduino/auxiliaries.ino index 594758fa..2cb9d778 100644 --- a/speeduino/auxiliaries.ino +++ b/speeduino/auxiliaries.ino @@ -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() { } diff --git a/speeduino/comms.h b/speeduino/comms.h index 6e71541e..ba0168f4 100644 --- a/speeduino/comms.h +++ b/speeduino/comms.h @@ -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; diff --git a/speeduino/comms.ino b/speeduino/comms.ino index cc162931..5a694a73 100644 --- a/speeduino/comms.ino +++ b/speeduino/comms.ino @@ -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; } diff --git a/speeduino/decoders.h b/speeduino/decoders.h index 7ed497f7..1b0eedce 100644 --- a/speeduino/decoders.h +++ b/speeduino/decoders.h @@ -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 diff --git a/speeduino/decoders.ino b/speeduino/decoders.ino index 9fc84250..33c9e018 100644 --- a/speeduino/decoders.ino +++ b/speeduino/decoders.ino @@ -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 } diff --git a/speeduino/globals.h b/speeduino/globals.h index 6a382069..13123a9e 100644 --- a/speeduino/globals.h +++ b/speeduino/globals.h @@ -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; diff --git a/speeduino/idle.h b/speeduino/idle.h index 158f9488..234eea06 100644 --- a/speeduino/idle.h +++ b/speeduino/idle.h @@ -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 diff --git a/speeduino/idle.ino b/speeduino/idle.ino index d39931c9..37be5793 100644 --- a/speeduino/idle.ino +++ b/speeduino/idle.ino @@ -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(¤tStatus.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(¤tStatus.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 diff --git a/speeduino/scheduler.h b/speeduino/scheduler.h index 950cce01..1b1e8b60 100644 --- a/speeduino/scheduler.h +++ b/speeduino/scheduler.h @@ -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(); diff --git a/speeduino/scheduler.ino b/speeduino/scheduler.ino index bd100bff..a1b9d616 100644 --- a/speeduino/scheduler.ino +++ b/speeduino/scheduler.ino @@ -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 { diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 827fb087..41824562 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -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 diff --git a/speeduino/timers.h b/speeduino/timers.h index f21727a2..c0981750 100644 --- a/speeduino/timers.h +++ b/speeduino/timers.h @@ -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(); diff --git a/speeduino/timers.ino b/speeduino/timers.ino index e7be9b5c..f21c7de4 100644 --- a/speeduino/timers.ino +++ b/speeduino/timers.ino @@ -18,10 +18,10 @@ Timers are typically low resolution (Compared to Schedulers), with maximum frequ #include #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 } diff --git a/speeduino/utils.ino b/speeduino/utils.ino index 686ac8d7..b8ee91e4 100644 --- a/speeduino/utils.ino +++ b/speeduino/utils.ino @@ -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: