From 9bbd16c81b9c6ae1954c6056fa6e5a7c7aa7c976 Mon Sep 17 00:00:00 2001 From: tx_haggis <13982343+adbancroft@users.noreply.github.com> Date: Sun, 5 Nov 2023 16:10:08 -0600 Subject: [PATCH] Performance: optimize division (#1082) * Add udiv_32_16 * Apply udiv_32_16() where possible * Convert udiv_32_16 to assembler It's worth 20 loop/s * Remove unused functions * Remove degreesPeruSx2048 - unused * Remove angleToTime - replace with direct calls 1. Drop angleToTime() It's slow, only partially implemented and adds zero value (and has MISRA violations) 2. Consistent function naming 3. Doxygen * triggerPri_Nissan360 shouldn't set timePerDegree. It will be overwritten every loop by doCrankSpeedCalcs() * Use angleToTimeMicroSecPerDegree() instead of timePerDegree No loss in performance Increased injection open/close time accuracy (so unit test values must change) Can remove timePerDegree global. * Hide (encapsulate) crank math globals. * Base all angle to time conversions on decoder computed variables. This is within 2us of the revolution based method and is much faster - which is essentially zero percent change. * Performance: move calculation of degreesPeruSx32768 into decoders. Remove doCrankSpeedCalcs() - it's doing nothing at the moment. * Apply libdivide to triggerSetEndTeeth functions. Since triggerToothAngle is set once at initialization time, we can generate the libdivide struct once and reuse it many times. * Remove lastToothCalcAdvance - unused * Replace 16-bit division with shift * Replace 32-bit divison with 16-bit division * Avoid 32-bit division; use div100() * inline percentage() * Optimize div100() * MISRA fixes * Replace magic numbers with #defs * Replace libdivide structs with inline constants No perf or memory changes * Use fixed types for PWM max count variables * Accurate rounded integer division * Formalise rounding behavior (DIV_ROUND_CORRECT) * Apply DIV_ROUND_CORRECT to DIV_ROUND_CLOSEST(), UDIV_ROUND_CLOSEST(), div100(), div360(), percentage() & halfPercentage() * Add, fix & improve unit tests * Add udiv_32_16_closest() * Perf: Limit percentage calculations to 16-bits * MISRA fixes * Add compare_executiontime() to encapsulate common perf testing code * Signed to unsigned division * Convert ignitionLimits() to an inline function. Slight speed up, probably due to removing multiple evaluations of macro arguments. * Split unit tests up. * udiv_32_16 - check for valid parameters --- platformio.ini | 4 +- speeduino/TS_CommandButtonHandler.cpp | 2 +- speeduino/auxiliaries.cpp | 20 +- speeduino/auxiliaries.h | 7 +- speeduino/board_avr2560.cpp | 4 +- speeduino/board_stm32_official.cpp | 9 +- speeduino/board_teensy35.cpp | 8 +- speeduino/board_teensy41.cpp | 6 +- speeduino/corrections.cpp | 8 +- speeduino/crankMaths.cpp | 166 ++-- speeduino/crankMaths.h | 93 ++- speeduino/decoders.cpp | 860 ++++++++------------ speeduino/decoders.h | 28 +- speeduino/engineProtection.cpp | 5 +- speeduino/globals.h | 45 +- speeduino/idle.cpp | 22 +- speeduino/idle.h | 2 +- speeduino/maths.cpp | 70 +- speeduino/maths.h | 347 ++++++-- speeduino/schedule_calcs.h | 10 +- speeduino/schedule_calcs.hpp | 26 +- speeduino/secondaryTables.cpp | 2 +- speeduino/sensors.cpp | 16 +- speeduino/speeduino.ino | 73 +- speeduino/src/libdivide/libdivide.h | 26 +- speeduino/storage.h | 2 +- speeduino/table3d_interpolate.cpp | 3 +- test/test_math/main.cpp | 8 +- test/test_math/test_division.cpp | 222 +++++ test/test_math/test_fp_support.cpp | 14 + test/test_math/test_fp_support.h | 8 + test/test_math/tests_crankmaths.cpp | 22 +- test/test_math/tests_maths.cpp | 151 ---- test/test_math/tests_maths.h | 18 - test/test_math/tests_percent.cpp | 112 +++ test/test_schedule_calcs/test_ign_calcs.cpp | 329 ++++---- test/test_schedule_calcs/test_inj_calcs.cpp | 405 ++++----- test/timer.hpp | 103 +++ 38 files changed, 1782 insertions(+), 1474 deletions(-) create mode 100644 test/test_math/test_division.cpp create mode 100644 test/test_math/test_fp_support.cpp create mode 100644 test/test_math/test_fp_support.h delete mode 100644 test/test_math/tests_maths.cpp delete mode 100644 test/test_math/tests_maths.h create mode 100644 test/test_math/tests_percent.cpp create mode 100644 test/timer.hpp diff --git a/platformio.ini b/platformio.ini index df226088..434978d9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,9 @@ board=megaatmega2560 framework=arduino build_unflags = -Os build_flags = -DUSE_LIBDIVIDE -O3 -ffast-math -fshort-enums -funroll-loops -Wall -Wextra -std=c99 -lib_deps = EEPROM, Time +; Note that fp64lib is only used by unit tests. It isn't referenced by the firmware & will be +; ignored. +lib_deps = EEPROM, Time, fp64lib ;test_build_project_src = true test_build_src = yes debug_tool = simavr diff --git a/speeduino/TS_CommandButtonHandler.cpp b/speeduino/TS_CommandButtonHandler.cpp index c23965fa..2061ea69 100644 --- a/speeduino/TS_CommandButtonHandler.cpp +++ b/speeduino/TS_CommandButtonHandler.cpp @@ -305,7 +305,7 @@ bool TS_CommandButtonsHandler(uint16_t buttonCommand) uint32_t calibrationGap = vssGetPulseGap(0); if( calibrationGap > 0 ) { - configPage2.vssPulsesPerKm = 60000000UL / calibrationGap; + configPage2.vssPulsesPerKm = MICROS_PER_MIN / calibrationGap; writeConfig(1); // Need to manually save the new config value as it will not trigger a burn in tunerStudio due to use of ControllerPriority BIT_SET(currentStatus.status3, BIT_STATUS3_VSS_REFRESH); //Set the flag to trigger the UI reset } diff --git a/speeduino/auxiliaries.cpp b/speeduino/auxiliaries.cpp index 3a06948a..dabea0b9 100644 --- a/speeduino/auxiliaries.cpp +++ b/speeduino/auxiliaries.cpp @@ -49,7 +49,7 @@ volatile PINMASK_TYPE fan_pin_mask; #if defined(PWM_FAN_AVAILABLE)//PWM fan not available on Arduino MEGA volatile bool fan_pwm_state; -unsigned int fan_pwm_max_count; //Used for variable PWM frequency +uint16_t fan_pwm_max_count; //Used for variable PWM frequency volatile unsigned int fan_pwm_cur_value; long fan_pwm_value; #endif @@ -69,8 +69,8 @@ volatile unsigned int boost_pwm_cur_value = 0; uint32_t vvtWarmTime; bool vvtIsHot; bool vvtTimeHold; -unsigned int vvt_pwm_max_count; //Used for variable PWM frequency -unsigned int boost_pwm_max_count; //Used for variable PWM frequency +uint16_t vvt_pwm_max_count; //Used for variable PWM frequency +uint16_t boost_pwm_max_count; //Used for variable PWM frequency //Old PID method. Retained in case the new one has issues //integerPID boostPID(&MAPx100, &boost_pwm_target_value, &boostTargetx100, configPage6.boostKP, configPage6.boostKI, configPage6.boostKD, DIRECT); @@ -325,7 +325,7 @@ void initialiseFan(void) if ( configPage2.fanEnable == 2 ) // PWM Fan control { #if defined(CORE_TEENSY) - fan_pwm_max_count = 1000000L / (32 * configPage6.fanFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + fan_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (32U * configPage6.fanFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #endif fan_pwm_value = 0; } @@ -481,11 +481,11 @@ void initialiseAuxPWM(void) currentStatus.vvt2Angle = 0; #if defined(CORE_AVR) - vvt_pwm_max_count = 1000000L / (16 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + vvt_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (16U * configPage6.vvtFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY35) - vvt_pwm_max_count = 1000000L / (32 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + vvt_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (32U * configPage6.vvtFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY41) - vvt_pwm_max_count = 1000000L / (2 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming fro TS to allow for up to 512hz + vvt_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (2U * configPage6.vvtFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming fro TS to allow for up to 512hz #endif if(configPage6.vvtMode == VVT_MODE_CLOSED_LOOP) @@ -515,11 +515,11 @@ void initialiseAuxPWM(void) { // config wmi pwm output to use vvt output #if defined(CORE_AVR) - vvt_pwm_max_count = 1000000L / (16 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + vvt_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (16U * configPage6.vvtFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY35) - vvt_pwm_max_count = 1000000L / (32 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + vvt_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (32U * configPage6.vvtFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY41) - vvt_pwm_max_count = 1000000L / (2 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + vvt_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (2U * configPage6.vvtFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #endif BIT_CLEAR(currentStatus.status4, BIT_STATUS4_WMI_EMPTY); currentStatus.wmiPW = 0; diff --git a/speeduino/auxiliaries.h b/speeduino/auxiliaries.h index 7e5cd135..56f359b6 100644 --- a/speeduino/auxiliaries.h +++ b/speeduino/auxiliaries.h @@ -101,13 +101,12 @@ extern volatile PORT_TYPE *fan_pin_port; extern volatile PINMASK_TYPE fan_pin_mask; #if defined(PWM_FAN_AVAILABLE)//PWM fan not available on Arduino MEGA -extern unsigned int fan_pwm_max_count; //Used for variable PWM frequency +extern uint16_t fan_pwm_max_count; //Used for variable PWM frequency void fanInterrupt(void); #endif -extern unsigned int vvt_pwm_max_count; //Used for variable PWM frequency -extern unsigned int boost_pwm_max_count; //Used for variable PWM frequency - +extern uint16_t vvt_pwm_max_count; //Used for variable PWM frequency +extern uint16_t boost_pwm_max_count; //Used for variable PWM frequency void boostInterrupt(void); void vvtInterrupt(void); diff --git a/speeduino/board_avr2560.cpp b/speeduino/board_avr2560.cpp index 24e1ed09..f00471b0 100644 --- a/speeduino/board_avr2560.cpp +++ b/speeduino/board_avr2560.cpp @@ -34,8 +34,8 @@ void initBoard(void) TCCR1B = TIMER_PRESCALER_256; //Timer1 Control Reg B: Timer Prescaler set to 256. 1 tick = 16uS. TIFR1 = (1 << OCF1A) | (1< revolutionTime) { //Possibly need some method of reducing spark duration here as well, but this is a start - uint16_t adjustedSparkDur = (sparkDur_uS * revolutionTime) / dwellPerRevolution; - tempDwell = (revolutionTime / pulsesPerRevolution) - adjustedSparkDur; + uint16_t adjustedSparkDur = udiv_32_16(sparkDur_uS * revolutionTime, dwellPerRevolution); + tempDwell = udiv_32_16(revolutionTime, (uint16_t)pulsesPerRevolution) - adjustedSparkDur; } return tempDwell; diff --git a/speeduino/crankMaths.cpp b/speeduino/crankMaths.cpp index 44ccf909..d408c749 100644 --- a/speeduino/crankMaths.cpp +++ b/speeduino/crankMaths.cpp @@ -1,104 +1,63 @@ #include "globals.h" #include "crankMaths.h" #include "decoders.h" -#include "timers.h" -#include "maths.h" -volatile uint16_t timePerDegree; -volatile uint16_t timePerDegreex16; -volatile uint16_t degreesPeruSx2048; -volatile unsigned long degreesPeruSx32768; +#define SECOND_DERIV_ENABLED 0 //These are only part of the experimental 2nd deriv calcs +#if SECOND_DERIV_ENABLED!=0 byte deltaToothCount = 0; //The last tooth that was used with the deltaV calc int rpmDelta; +#endif -/* -* Converts a crank angle into a time from or since that angle occurred. -* Positive angles are assumed to be in the future, negative angles in the past: -* * Future angle calculations will use a predicted speed/acceleration -* * Past angle calculations will use the known speed -* -* Currently 4 methods are planned and/or available: -* 1) Last interval based on a full revolution -* 2) Last interval based on the time between the last 2 teeth (Crank Pattern dependent) -* 3) Closed loop error correction (Alpha-beta filter) -* 4) 2nd derivative prediction (Speed + acceleration) -*/ -unsigned long angleToTime(uint16_t angle, byte method) -{ - unsigned long returnTime = 0; - - if( (method == CRANKMATH_METHOD_INTERVAL_REV) || (method == CRANKMATH_METHOD_INTERVAL_DEFAULT) ) - { - returnTime = angleToTimeIntervalRev(angle); - //returnTime = angle * (unsigned long)timePerDegree; - } - else if (method == CRANKMATH_METHOD_INTERVAL_TOOTH) - { - //Still uses a last interval method (ie retrospective), but bases the interval on the gap between the 2 most recent teeth rather than the last full revolution - if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) - { - noInterrupts(); - unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime); - uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts - interrupts(); - - returnTime = ( (toothTime * angle) / tempTriggerToothAngle ); - } - else { returnTime = angleToTime(angle, CRANKMATH_METHOD_INTERVAL_REV); } //Safety check. This can occur if the last tooth seen was outside the normal pattern etc - } - - return returnTime; +uint32_t angleToTimeMicroSecPerDegree(uint16_t angle) { + UQ24X8_t micros = (uint32_t)angle * (uint32_t)microsPerDegree; + return RSHIFT_ROUND(micros, microsPerDegree_Shift); } -/* -* Convert a time (uS) into an angle at current speed -* Currently 4 methods are planned and/or available: -* 1) Last interval based on a full revolution -* 2) Last interval based on the time between the last 2 teeth (Crank Pattern dependent) -* 3) Closed loop error correction (Alpha-beta filter) -* 4) 2nd derivative prediction (Speed + acceleration) -*/ -uint16_t timeToAngle(unsigned long time, byte method) -{ - uint16_t returnAngle = 0; - - if( (method == CRANKMATH_METHOD_INTERVAL_REV) || (method == CRANKMATH_METHOD_INTERVAL_DEFAULT) ) - { - //A last interval method of calculating angle that does not take into account any acceleration. The interval used is the time taken to complete the last full revolution - //degreesPeruSx2048 is the number of degrees the crank moves per uS. This value is almost always <1uS, so it is multiplied by 2048. This allows an angle calculation with only a multiply and a bitshift without any appreciable drop in accuracy - returnAngle = fastTimeToAngle(time); - } - else if (method == CRANKMATH_METHOD_INTERVAL_TOOTH) - { - //Still uses a last interval method (ie retrospective), but bases the interval on the gap between the 2 most recent teeth rather than the last full revolution - if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) - { - noInterrupts(); - unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime); - uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts - interrupts(); - - returnAngle = ( (unsigned long)(time * tempTriggerToothAngle) / toothTime ); - } - else { returnAngle = timeToAngle(time, CRANKMATH_METHOD_INTERVAL_REV); } //Safety check. This can occur if the last tooth seen was outside the normal pattern etc - } - else if (method == CRANKMATH_METHOD_ALPHA_BETA) - { - //Not yet implemented. Default to Rev - returnAngle = timeToAngle(time, CRANKMATH_METHOD_INTERVAL_REV); - } - else if (method == CRANKMATH_METHOD_2ND_DERIVATIVE) - { - //Not yet implemented. Default to Rev - returnAngle = timeToAngle(time, CRANKMATH_METHOD_INTERVAL_REV); - } - - return returnAngle; +uint32_t angleToTimeIntervalTooth(uint16_t angle) { + noInterrupts(); + if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) + { + unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime); + uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts + interrupts(); + return (toothTime * (uint32_t)angle) / tempTriggerToothAngle; + } + //Safety check. This can occur if the last tooth seen was outside the normal pattern etc + else { + interrupts(); + return angleToTimeMicroSecPerDegree(angle); + } } +uint16_t timeToAngleDegPerMicroSec(uint32_t time) { + uint32_t degFixed = time * (uint32_t)degreesPerMicro; + return RSHIFT_ROUND(degFixed, degreesPerMicro_Shift); +} + + +uint16_t timeToAngleIntervalTooth(uint32_t time) +{ + noInterrupts(); + //Still uses a last interval method (ie retrospective), but bases the interval on the gap between the 2 most recent teeth rather than the last full revolution + if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) + { + unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime); + uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts + interrupts(); + + return (unsigned long)(time * (uint32_t)tempTriggerToothAngle) / toothTime; + } + else { + interrupts(); + //Safety check. This can occur if the last tooth seen was outside the normal pattern etc + return timeToAngleDegPerMicroSec(time); + } +} + +#if SECOND_DERIV_ENABLED!=0 void doCrankSpeedCalcs(void) { //******************************************************** @@ -106,7 +65,7 @@ void doCrankSpeedCalcs(void) //We use a 1st Deriv acceleration prediction, but only when there is an even spacing between primary sensor teeth //Any decoder that has uneven spacing has its triggerToothAngle set to 0 //THIS IS CURRENTLY DISABLED FOR ALL DECODERS! It needs more work. - if( SECOND_DERIV_ENABLED && (BIT_CHECK(decoderState, BIT_DECODER_2ND_DERIV)) && (toothHistoryIndex >= 3) && (currentStatus.RPM < 2000) ) //toothHistoryIndex must be greater than or equal to 3 as we need the last 3 entries. Currently this mode only runs below 3000 rpm + if( (BIT_CHECK(decoderState, BIT_DECODER_2ND_DERIV)) && (toothHistoryIndex >= 3) && (currentStatus.RPM < 2000) ) //toothHistoryIndex must be greater than or equal to 3 as we need the last 3 entries. Currently this mode only runs below 3000 rpm { //Only recalculate deltaV if the tooth has changed since last time (DeltaV stays the same until the next tooth) //if (deltaToothCount != toothCurrentCount) @@ -129,7 +88,7 @@ void doCrankSpeedCalcs(void) } else { angle1 = triggerToothAngle; angle2 = triggerToothAngle; } - uint32_t toothDeltaV = (1000000L * angle2 / toothHistory[toothHistoryIndex]) - (1000000L * angle1 / toothHistory[toothHistoryIndex-1]); + uint32_t toothDeltaV = (MICROS_PER_SEC * angle2 / toothHistory[toothHistoryIndex]) - (MICROS_PER_SEC * angle1 / toothHistory[toothHistoryIndex-1]); uint32_t toothDeltaT = toothHistory[toothHistoryIndex]; //long timeToLastTooth = micros() - toothLastToothTime; @@ -137,33 +96,6 @@ void doCrankSpeedCalcs(void) } timePerDegreex16 = ldiv( 2666656L, currentStatus.RPM + rpmDelta).quot; //This gives accuracy down to 0.1 of a degree and can provide noticeably better timing results on low resolution triggers - timePerDegree = timePerDegreex16 / 16; } - else - { - //If we can, attempt to get the timePerDegree by comparing the times of the last two teeth seen. This is only possible for evenly spaced teeth - noInterrupts(); - if( (BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) && (toothLastToothTime > toothLastMinusOneToothTime) && (abs(currentStatus.rpmDOT) > 30) ) - { - //noInterrupts(); - unsigned long tempToothLastToothTime = toothLastToothTime; - unsigned long tempToothLastMinusOneToothTime = toothLastMinusOneToothTime; - uint16_t tempTriggerToothAngle = triggerToothAngle; - interrupts(); - timePerDegreex16 = (unsigned long)( (tempToothLastToothTime - tempToothLastMinusOneToothTime)*16) / tempTriggerToothAngle; - timePerDegree = timePerDegreex16 / 16; - } - else - { - //long timeThisRevolution = (micros_safe() - toothOneTime); - interrupts(); - //Take into account any likely acceleration that has occurred since the last full revolution completed: - //long rpm_adjust = (timeThisRevolution * (long)currentStatus.rpmDOT) / 1000000; - long rpm_adjust = 0; - timePerDegreex16 = ldiv( 2666656L, currentStatus.RPM + rpm_adjust).quot; //The use of a x16 value gives accuracy down to 0.1 of a degree and can provide noticeably better timing results on low resolution triggers - timePerDegree = timePerDegreex16 / 16; - } - } - degreesPeruSx2048 = 2048 / timePerDegree; - degreesPeruSx32768 = 524288 / timePerDegreex16; } +#endif \ No newline at end of file diff --git a/speeduino/crankMaths.h b/speeduino/crankMaths.h index d7a1260d..57d42d5f 100644 --- a/speeduino/crankMaths.h +++ b/speeduino/crankMaths.h @@ -4,31 +4,76 @@ #include "maths.h" #include "globals.h" -#define CRANKMATH_METHOD_INTERVAL_DEFAULT 0 -#define CRANKMATH_METHOD_INTERVAL_REV 1 -#define CRANKMATH_METHOD_INTERVAL_TOOTH 2 -#define CRANKMATH_METHOD_ALPHA_BETA 3 -#define CRANKMATH_METHOD_2ND_DERIVATIVE 4 - -#define SECOND_DERIV_ENABLED 0 - -//#define fastDegreesToUS(targetDegrees) ((targetDegrees) * (unsigned long)timePerDegree) -#define fastDegreesToUS(targetDegrees) (((targetDegrees) * (unsigned long)timePerDegreex16) >> 4) -/*#define fastTimeToAngle(time) (((unsigned long)time * degreesPeruSx2048) / 2048) */ //Divide by 2048 will be converted at compile time to bitshift -#define fastTimeToAngle(time) (((unsigned long)(time) * degreesPeruSx32768) / 32768) //Divide by 32768 will be converted at compile time to bitshift - -#define ignitionLimits(angle) ( (((int16_t)(angle)) >= CRANK_ANGLE_MAX_IGN) ? ((angle) - CRANK_ANGLE_MAX_IGN) : ( ((int16_t)(angle) < 0) ? ((angle) + CRANK_ANGLE_MAX_IGN) : (angle)) ) - - -unsigned long angleToTime(uint16_t angle, byte method); -inline unsigned long angleToTimeIntervalRev(uint16_t angle) { - return div360(angle * revolutionTime); +/** + * @brief Makes one pass at nudging the angle to within [0,CRANK_ANGLE_MAX_IGN] + * + * @param angle A crank angle in degrees + * @return int16_t + */ +static inline int16_t ignitionLimits(int16_t angle) { + return nudge(0, CRANK_ANGLE_MAX_IGN, angle, CRANK_ANGLE_MAX_IGN); } -uint16_t timeToAngle(unsigned long time, byte method); -void doCrankSpeedCalcs(void); -extern volatile uint16_t timePerDegree; -extern volatile uint16_t timePerDegreex16; -extern volatile unsigned long degreesPeruSx32768; +/** @brief At 1 RPM, each degree of angular rotation takes this many microseconds */ +#define MICROS_PER_DEG_1_RPM INT32_C(166667) + +/** @brief The maximum rpm that the ECU will attempt to run at. + * + * It is NOT related to the rev limiter, but is instead dictates how fast certain operations will be + * allowed to run. Lower number gives better performance + **/ +#define MAX_RPM INT16_C(18000) + +/** @brief Absolute minimum RPM that the crank math (& therefore all of Speeduino) can be used with + * + * This is dictated by the use of uint16_t as the base type for storing + * angle<->time conversion factor (degreesPerMicro) +*/ +#define MIN_RPM ((MICROS_PER_DEG_1_RPM/(UINT16_MAX/16UL))+1UL) + +/** + * @name Converts angular degrees to the time interval that amount of rotation + * will take at current RPM. + * + * Based on angle of [0,720] and min/max RPM, result ranges from + * 9 (MAX_RPM, 1 deg) to 2926828 (MIN_RPM, 720 deg) + * + * @param angle Angle in degrees + * @return Time interval in uS + */ +///@{ +/** @brief Converts based on the time one degree of rotation takes + * + * Inverse of timeToAngleDegPerMicroSec +*/ +uint32_t angleToTimeMicroSecPerDegree(uint16_t angle); + +/** @brief Converts based on the time interval between the 2 most recently detected decoder teeth + * + * Inverse of timeToAngleIntervalTooth +*/ +uint32_t angleToTimeIntervalTooth(uint16_t angle); +///@} + +/** + * @name Converts a time interval in microsecods to the equivalent degrees of angular (crank) + * rotation at current RPM. + * + * @param time Time interval in uS + * @return Angle in degrees + */ +///@{ +/** @brief Converts based on the the interval on time one degree of rotation takes + * + * Inverse of angleToTimeMicroSecPerDegree +*/ +uint16_t timeToAngleDegPerMicroSec(uint32_t time); + +/** @brief Converts based on the time interval between the 2 most recently detected decoder teeth + * + * Inverse of angleToTimeIntervalTooth +*/ +uint16_t timeToAngleIntervalTooth(uint32_t time); +///@} #endif \ No newline at end of file diff --git a/speeduino/decoders.cpp b/speeduino/decoders.cpp index 99b5e122..37d74763 100644 --- a/speeduino/decoders.cpp +++ b/speeduino/decoders.cpp @@ -63,7 +63,7 @@ volatile unsigned long curGap3; volatile unsigned long lastGap; volatile unsigned long targetGap; -unsigned long MAX_STALL_TIME = 500000UL; //The maximum time (in uS) that the system will continue to function before the engine is considered stalled/stopped. This is unique to each decoder, depending on the number of teeth etc. 500000 (half a second) is used as the default value, most decoders will be much less. +unsigned long MAX_STALL_TIME = MICROS_PER_SEC/2U; //The maximum time (in uS) that the system will continue to function before the engine is considered stalled/stopped. This is unique to each decoder, depending on the number of teeth etc. 500000 (half a second) is used as the default value, most decoders will be much less. volatile uint16_t toothCurrentCount = 0; //The current number of teeth (Once sync has been achieved, this can never actually be 0 volatile byte toothSystemCount = 0; //Used for decoders such as Audi 135 where not every tooth is used for calculating crank angle. This variable stores the actual number of teeth, not the number being used to calculate crank angle volatile unsigned long toothSystemLastToothTime = 0; //As below, but used for decoders where not every tooth count is used for calculation @@ -97,12 +97,14 @@ volatile unsigned long triggerThirdFilterTime; // The shortest time (in uS) that volatile uint8_t decoderState = 0; +UQ24X8_t microsPerDegree; +UQ1X15_t degreesPerMicro; + unsigned int triggerSecFilterTime_duration; // The shortest valid time (in uS) pulse DURATION volatile uint16_t triggerToothAngle; //The number of crank degrees that elapse per tooth byte checkSyncToothCount; //How many teeth must've been seen on this revolution before we try to confirm sync (Useful for missing tooth type decoders) unsigned long elapsedTime; unsigned long lastCrankAngleCalc; -int16_t lastToothCalcAdvance = 99; //Invalid value here forces calculation of this on first main loop unsigned long lastVVTtime; //The time between the vvt reference pulse and the last crank pulse uint16_t ignition1EndTooth = 0; @@ -116,6 +118,10 @@ uint16_t ignition8EndTooth = 0; int16_t toothAngles[24]; //An array for storing fixed tooth angles. Currently sized at 24 for the GM 24X decoder, but may grow later if there are other decoders that use this style +#ifdef USE_LIBDIVIDE +#include "src/libdivide/libdivide.h" +static libdivide::libdivide_s16_t divTriggerToothAngle; +#endif /** Universal (shared between decoders) decoder routines. * @@ -284,32 +290,62 @@ void loggerTertiaryISR(void) } } +static inline bool IsCranking(const statuses &status) { + return (status.RPM < status.crankRPM) && (status.startRevolutions == 0U); +} + +#if defined(UNIT_TEST) +bool SetRevolutionTime(uint32_t revTime) +#else +static __attribute__((noinline)) bool SetRevolutionTime(uint32_t revTime) +#endif +{ + if (revTime!=revolutionTime) { + revolutionTime = revTime; + microsPerDegree = div360(revolutionTime << microsPerDegree_Shift); + degreesPerMicro = (uint16_t)UDIV_ROUND_CLOSEST((UINT32_C(360) << degreesPerMicro_Shift), revolutionTime, uint32_t); + return true; + } + return false; +} + +static bool UpdateRevolutionTimeFromTeeth(bool isCamTeeth) { + noInterrupts(); + bool updatedRevTime = HasAnySync(currentStatus) + && !IsCranking(currentStatus) + && (toothOneMinusOneTime!=UINT32_C(0)) + && (toothOneTime>toothOneMinusOneTime) + //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) + && SetRevolutionTime((toothOneTime - toothOneMinusOneTime) >> (isCamTeeth ? 1U : 0U)); + + interrupts(); + return updatedRevTime; +} + +static inline uint16_t clampRpm(uint16_t rpm) { + return rpm>=MAX_RPM ? currentStatus.RPM : rpm; +} + +static inline uint16_t RpmFromRevolutionTimeUs(uint32_t revTime) { + if (revTime= MAX_RPM) { tempRPM = currentStatus.RPM; } //Sanity check - } + if (UpdateRevolutionTimeFromTeeth(isCamTeeth)) { + return RpmFromRevolutionTimeUs(revolutionTime); } - else { tempRPM = 0; } - return tempRPM; + return currentStatus.RPM; } /** @@ -332,23 +368,22 @@ It can only be used on patterns where the teeth are evenly spaced. It takes an argument of the full (COMPLETE) number of teeth per revolution. For a missing tooth wheel, this is the number if the tooth had NOT been missing (Eg 36-1 = 36) */ -static inline int crankingGetRPM(byte totalTeeth, uint16_t degreesOver) +static __attribute__((noinline)) int crankingGetRPM(byte totalTeeth, bool isCamTeeth) { - uint16_t tempRPM = 0; if( (currentStatus.startRevolutions >= configPage4.StgCycles) && ((currentStatus.hasSync == true) || BIT_CHECK(currentStatus.status3, BIT_STATUS3_HALFSYNC)) ) { - if( (toothLastToothTime > 0) && (toothLastMinusOneToothTime > 0) && (toothLastToothTime > toothLastMinusOneToothTime) ) + if((toothLastMinusOneToothTime > 0) && (toothLastToothTime > toothLastMinusOneToothTime) ) { noInterrupts(); - revolutionTime = (toothLastToothTime - toothLastMinusOneToothTime) * totalTeeth; + bool newRevtime = SetRevolutionTime(((toothLastToothTime - toothLastMinusOneToothTime) * totalTeeth) >> (isCamTeeth ? 1U : 0U)); interrupts(); - if(degreesOver == 720) { revolutionTime = revolutionTime / 2; } - tempRPM = (US_IN_MINUTE / revolutionTime); - if( tempRPM >= MAX_RPM ) { tempRPM = currentStatus.RPM; } //Sanity check. This can prevent spiking caused by noise on individual teeth. The new RPM should never be above 4x the cranking setting value (Remembering that this function is only called is the current RPM is less than the cranking setting) + if (newRevtime) { + return RpmFromRevolutionTimeUs(revolutionTime); + } } } - return tempRPM; + return currentStatus.RPM; } /** @@ -364,50 +399,50 @@ static inline void checkPerToothTiming(int16_t crankAngle, uint16_t currentTooth { if ( (currentTooth == ignition1EndTooth) ) { - if( (ignitionSchedule1.Status == RUNNING) ) { SET_COMPARE(IGN1_COMPARE, IGN1_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule1.endCompare = IGN1_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ); ignitionSchedule1.endScheduleSetByDecoder = true; } + if( (ignitionSchedule1.Status == RUNNING) ) { SET_COMPARE(IGN1_COMPARE, IGN1_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule1.endCompare = IGN1_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ); ignitionSchedule1.endScheduleSetByDecoder = true; } } else if ( (currentTooth == ignition2EndTooth) ) { - if( (ignitionSchedule2.Status == RUNNING) ) { SET_COMPARE(IGN2_COMPARE, IGN2_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule2.endCompare = IGN2_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ); ignitionSchedule2.endScheduleSetByDecoder = true; } + if( (ignitionSchedule2.Status == RUNNING) ) { SET_COMPARE(IGN2_COMPARE, IGN2_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule2.endCompare = IGN2_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ); ignitionSchedule2.endScheduleSetByDecoder = true; } } else if ( (currentTooth == ignition3EndTooth) ) { - if( (ignitionSchedule3.Status == RUNNING) ) { SET_COMPARE(IGN3_COMPARE, IGN3_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule3.endCompare = IGN3_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ); ignitionSchedule3.endScheduleSetByDecoder = true; } + if( (ignitionSchedule3.Status == RUNNING) ) { SET_COMPARE(IGN3_COMPARE, IGN3_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule3.endCompare = IGN3_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ); ignitionSchedule3.endScheduleSetByDecoder = true; } } else if ( (currentTooth == ignition4EndTooth) ) { - if( (ignitionSchedule4.Status == RUNNING) ) { SET_COMPARE(IGN4_COMPARE, IGN4_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule4.endCompare = IGN4_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ); ignitionSchedule4.endScheduleSetByDecoder = true; } + if( (ignitionSchedule4.Status == RUNNING) ) { SET_COMPARE(IGN4_COMPARE, IGN4_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule4.endCompare = IGN4_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ); ignitionSchedule4.endScheduleSetByDecoder = true; } } #if IGN_CHANNELS >= 5 else if ( (currentTooth == ignition5EndTooth) ) { - if( (ignitionSchedule5.Status == RUNNING) ) { SET_COMPARE(IGN5_COMPARE, IGN5_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition5EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule5.endCompare = IGN5_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition5EndAngle - crankAngle) ) ) ); ignitionSchedule5.endScheduleSetByDecoder = true; } + if( (ignitionSchedule5.Status == RUNNING) ) { SET_COMPARE(IGN5_COMPARE, IGN5_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition5EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule5.endCompare = IGN5_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition5EndAngle - crankAngle) ) ) ); ignitionSchedule5.endScheduleSetByDecoder = true; } } #endif #if IGN_CHANNELS >= 6 else if ( (currentTooth == ignition6EndTooth) ) { - if( (ignitionSchedule6.Status == RUNNING) ) { SET_COMPARE(IGN6_COMPARE, IGN6_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition6EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule6.endCompare = IGN6_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition6EndAngle - crankAngle) ) ) ); ignitionSchedule6.endScheduleSetByDecoder = true; } + if( (ignitionSchedule6.Status == RUNNING) ) { SET_COMPARE(IGN6_COMPARE, IGN6_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition6EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule6.endCompare = IGN6_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition6EndAngle - crankAngle) ) ) ); ignitionSchedule6.endScheduleSetByDecoder = true; } } #endif #if IGN_CHANNELS >= 7 else if ( (currentTooth == ignition7EndTooth) ) { - if( (ignitionSchedule7.Status == RUNNING) ) { SET_COMPARE(IGN7_COMPARE, IGN7_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition7EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule7.endCompare = IGN7_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition7EndAngle - crankAngle) ) ) ); ignitionSchedule7.endScheduleSetByDecoder = true; } + if( (ignitionSchedule7.Status == RUNNING) ) { SET_COMPARE(IGN7_COMPARE, IGN7_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition7EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule7.endCompare = IGN7_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition7EndAngle - crankAngle) ) ) ); ignitionSchedule7.endScheduleSetByDecoder = true; } } #endif #if IGN_CHANNELS >= 8 else if ( (currentTooth == ignition8EndTooth) ) { - if( (ignitionSchedule8.Status == RUNNING) ) { SET_COMPARE(IGN8_COMPARE, IGN8_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition8EndAngle - crankAngle) ) ) ) ); } - else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule8.endCompare = IGN8_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition8EndAngle - crankAngle) ) ) ); ignitionSchedule8.endScheduleSetByDecoder = true; } + if( (ignitionSchedule8.Status == RUNNING) ) { SET_COMPARE(IGN8_COMPARE, IGN8_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition8EndAngle - crankAngle) ) ) ) ); } + else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule8.endCompare = IGN8_COUNTER + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (ignition8EndAngle - crankAngle) ) ) ); ignitionSchedule8.endScheduleSetByDecoder = true; } } #endif } @@ -431,14 +466,14 @@ void triggerSetup_missingTooth(void) BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); } triggerActualTeeth = configPage4.triggerTeeth - configPage4.triggerMissingTeeth; //The number of physical teeth on the wheel. Doing this here saves us a calculation each time in the interrupt - triggerFilterTime = (1000000 / (MAX_RPM / 60 * configPage4.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 discarded as noise + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise if (configPage4.trigPatternSec == SEC_TRIGGER_4_1) { - triggerSecFilterTime = 1000000 * 60 / MAX_RPM / 4 / 2; + triggerSecFilterTime = MICROS_PER_MIN / MAX_RPM / 4U / 2U; } else { - triggerSecFilterTime = (1000000 / (MAX_RPM / 60)); + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U)); } BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); checkSyncToothCount = (configPage4.triggerTeeth) >> 1; //50% of the total teeth. @@ -448,10 +483,13 @@ void triggerSetup_missingTooth(void) thirdToothCount = 0; toothOneTime = 0; toothOneMinusOneTime = 0; - MAX_STALL_TIME = (3333UL * triggerToothAngle * (configPage4.triggerMissingTeeth + 1)); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle * (configPage4.triggerMissingTeeth + 1U)); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) if( (configPage4.TrigSpeed == CRANK_SPEED) && ( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) || (configPage2.injLayout == INJ_SEQUENTIAL) ) ) { BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); } else { BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); } +#ifdef USE_LIBDIVIDE + divTriggerToothAngle = libdivide::libdivide_s16_gen(triggerToothAngle); +#endif } void triggerPri_missingTooth(void) @@ -621,6 +659,8 @@ void triggerSec_missingTooth(void) currentStatus.vvt1Angle = ANGLE_FILTER( (curAngle << 1), configPage4.ANGLEFILTER_VVT, currentStatus.vvt1Angle); } + + } void triggerThird_missingTooth(void) @@ -659,15 +699,13 @@ uint16_t getRPM_missingTooth(void) { if(toothCurrentCount != 1) { - if(configPage4.TrigSpeed == CAM_SPEED) { tempRPM = crankingGetRPM(configPage4.triggerTeeth, 720); } //Account for cam speed - else { tempRPM = crankingGetRPM(configPage4.triggerTeeth, 360); } + tempRPM = crankingGetRPM(configPage4.triggerTeeth, configPage4.TrigSpeed==CAM_SPEED); //Account for cam speed } else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM if we're at tooth #1 as the missing tooth messes the calculation } else { - if(configPage4.TrigSpeed == CAM_SPEED) { tempRPM = stdGetRPM(720); } //Account for cam speed - else { tempRPM = stdGetRPM(360); } + tempRPM = stdGetRPM(configPage4.TrigSpeed==CAM_SPEED); //Account for cam speed } return tempRPM; } @@ -692,7 +730,7 @@ int getCrankAngle_missingTooth(void) lastCrankAngleCalc = micros(); elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } else if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -701,85 +739,52 @@ int getCrankAngle_missingTooth(void) return crankAngle; } +static inline uint16_t clampToToothCount(int16_t toothNum, uint8_t toothAdder) { + int16_t toothRange = (int16_t)configPage4.triggerTeeth + (int16_t)toothAdder; + return (uint16_t)nudge(1, toothRange, toothNum, toothRange); +} + +static inline uint16_t clampToActualTeeth(uint16_t toothNum, uint8_t toothAdder) { + if(toothNum > triggerActualTeeth && toothNum <= configPage4.triggerTeeth) { toothNum = triggerActualTeeth; } + return min(toothNum, (uint16_t)(triggerActualTeeth + toothAdder)); +} + +static uint16_t __attribute__((noinline)) calcEndTeeth_missingTooth(int endAngle, uint8_t toothAdder) { + //Temp variable used here to avoid potential issues if a trigger interrupt occurs part way through this function + int16_t tempEndTooth; +#ifdef USE_LIBDIVIDE + tempEndTooth = libdivide::libdivide_s16_do(endAngle - configPage4.triggerAngle, &divTriggerToothAngle); +#else + tempEndTooth = (endAngle - (int16_t)configPage4.triggerAngle) / (int16_t)triggerToothAngle; +#endif + //For higher tooth count triggers, add a 1 tooth margin to allow for calculation time. + if(configPage4.triggerTeeth > 12U) { tempEndTooth = tempEndTooth - 1; } + + // Clamp to tooth count + return clampToActualTeeth(clampToToothCount(tempEndTooth, toothAdder), toothAdder); +} + void triggerSetEndTeeth_missingTooth(void) { - byte toothAdder = 0; - byte toothMargin = 0; - if(configPage4.triggerTeeth > 12) { toothMargin = 1; } //For higher tooth count triggers, add a 1 tooth margin to allow for calculation time. - + uint8_t toothAdder = 0; if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) && (configPage2.strokes == FOUR_STROKE) ) { toothAdder = configPage4.triggerTeeth; } - //Temp variables are used here to avoid potential issues if a trigger interrupt occurs part way through this function - int16_t tempIgnition1EndTooth; - tempIgnition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition1EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition1EndTooth <= 0) { tempIgnition1EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition1EndTooth > triggerActualTeeth && tempIgnition1EndTooth <= configPage4.triggerTeeth) { tempIgnition1EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition1EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition1EndTooth = (triggerActualTeeth + toothAdder); } - ignition1EndTooth = tempIgnition1EndTooth; - - int16_t tempIgnition2EndTooth; - tempIgnition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition2EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition2EndTooth <= 0) { tempIgnition2EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition2EndTooth > triggerActualTeeth && tempIgnition2EndTooth <= configPage4.triggerTeeth) { tempIgnition2EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition2EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition2EndTooth = (triggerActualTeeth + toothAdder); } - ignition2EndTooth = tempIgnition2EndTooth; - - int16_t tempIgnition3EndTooth; - tempIgnition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition3EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition3EndTooth <= 0) { tempIgnition3EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition3EndTooth > triggerActualTeeth && tempIgnition3EndTooth <= configPage4.triggerTeeth) { tempIgnition3EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition3EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition3EndTooth = (triggerActualTeeth + toothAdder); } - ignition3EndTooth = tempIgnition3EndTooth; - - int16_t tempIgnition4EndTooth; - tempIgnition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition4EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition4EndTooth <= 0) { tempIgnition4EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition4EndTooth > triggerActualTeeth && tempIgnition4EndTooth <= configPage4.triggerTeeth) { tempIgnition4EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition4EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition4EndTooth = (triggerActualTeeth + toothAdder); } - ignition4EndTooth = tempIgnition4EndTooth; - + ignition1EndTooth = calcEndTeeth_missingTooth(ignition1EndAngle, toothAdder); + ignition2EndTooth = calcEndTeeth_missingTooth(ignition2EndAngle, toothAdder); + ignition3EndTooth = calcEndTeeth_missingTooth(ignition3EndAngle, toothAdder); + ignition4EndTooth = calcEndTeeth_missingTooth(ignition4EndAngle, toothAdder); #if IGN_CHANNELS >= 5 - int16_t tempIgnition5EndTooth; - tempIgnition5EndTooth = ( (ignition5EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition5EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition5EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition5EndTooth <= 0) { tempIgnition5EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition5EndTooth > triggerActualTeeth && tempIgnition5EndTooth <= configPage4.triggerTeeth) { tempIgnition5EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition5EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition5EndTooth = (triggerActualTeeth + toothAdder); } - ignition5EndTooth = tempIgnition5EndTooth; + ignition5EndTooth = calcEndTeeth_missingTooth(ignition5EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 6 - int16_t tempIgnition6EndTooth; - tempIgnition6EndTooth = ( (ignition6EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition6EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition6EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition6EndTooth <= 0) { tempIgnition6EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition6EndTooth > triggerActualTeeth && tempIgnition6EndTooth <= configPage4.triggerTeeth) { tempIgnition6EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition6EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition6EndTooth = (triggerActualTeeth + toothAdder); } - ignition6EndTooth = tempIgnition6EndTooth; + ignition6EndTooth = calcEndTeeth_missingTooth(ignition6EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 7 - int16_t tempIgnition7EndTooth; - tempIgnition7EndTooth = ( (ignition7EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition7EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition7EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition7EndTooth <= 0) { tempIgnition7EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition7EndTooth > triggerActualTeeth && tempIgnition7EndTooth <= configPage4.triggerTeeth) { tempIgnition7EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition7EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition7EndTooth = (triggerActualTeeth + toothAdder); } - ignition7EndTooth = tempIgnition7EndTooth; + ignition7EndTooth = calcEndTeeth_missingTooth(ignition7EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 8 - int16_t tempIgnition8EndTooth; - tempIgnition8EndTooth = ( (ignition8EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - toothMargin; - if(tempIgnition8EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition8EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition8EndTooth <= 0) { tempIgnition8EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition8EndTooth > triggerActualTeeth && tempIgnition8EndTooth <= configPage4.triggerTeeth) { tempIgnition8EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition8EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition8EndTooth = (triggerActualTeeth + toothAdder); } - ignition8EndTooth = tempIgnition8EndTooth; + ignition8EndTooth = calcEndTeeth_missingTooth(ignition8EndAngle, toothAdder); #endif - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -796,13 +801,16 @@ void triggerSetup_DualWheel(void) triggerToothAngle = 360 / configPage4.triggerTeeth; //The number of degrees that passes from tooth to tooth if(configPage4.TrigSpeed == CAM_SPEED) { triggerToothAngle = 720 / configPage4.triggerTeeth; } //Account for cam speed toothCurrentCount = 255; //Default value - triggerFilterTime = (1000000 / (MAX_RPM / 60 * configPage4.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 discarded as noise - triggerSecFilterTime = (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) + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 2U)) / 2U; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT); //This is always true for this pattern BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) +#ifdef USE_LIBDIVIDE + divTriggerToothAngle = libdivide::libdivide_s16_gen(triggerToothAngle); +#endif } /** Dual Wheel Primary. @@ -863,7 +871,7 @@ void triggerSec_DualWheel(void) if( (currentStatus.hasSync == false) || (currentStatus.startRevolutions <= configPage4.StgCycles) ) { toothLastToothTime = micros(); - toothLastMinusOneToothTime = micros() - (6000000 / configPage4.triggerTeeth); //Fixes RPM at 10rpm until a full revolution has taken place + toothLastMinusOneToothTime = micros() - ((MICROS_PER_MIN/10U) / configPage4.triggerTeeth); //Fixes RPM at 10rpm until a full revolution has taken place toothCurrentCount = configPage4.triggerTeeth; triggerFilterTime = 0; //Need to turn the filter off here otherwise the first primary tooth after achieving sync is ignored @@ -887,22 +895,21 @@ void triggerSec_DualWheel(void) * */ uint16_t getRPM_DualWheel(void) { - uint16_t tempRPM = 0; if( currentStatus.hasSync == true ) { + //Account for cam speed if( currentStatus.RPM < currentStatus.crankRPM ) { - if(configPage4.TrigSpeed == CAM_SPEED) { tempRPM = crankingGetRPM(configPage4.triggerTeeth, 720); } //Account for cam speed - else { tempRPM = crankingGetRPM(configPage4.triggerTeeth, 360); } + return crankingGetRPM(configPage4.triggerTeeth, configPage4.TrigSpeed==CAM_SPEED); } else { - if(configPage4.TrigSpeed == CAM_SPEED) { tempRPM = stdGetRPM(720); } //Account for cam speed - else { tempRPM = stdGetRPM(360); } + return stdGetRPM(configPage4.TrigSpeed==CAM_SPEED); } } - return tempRPM; + return 0U; } + /** Dual Wheel - Get Crank angle. * * */ @@ -926,7 +933,7 @@ int getCrankAngle_DualWheel(void) int crankAngle = ((tempToothCurrentCount - 1) * triggerToothAngle) + configPage4.triggerAngle; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth. elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); //Sequential check (simply sets whether we're on the first or 2nd revolution of the cycle) if ( (tempRevolutionOne == true) && (configPage4.TrigSpeed == CRANK_SPEED) ) { crankAngle += 360; } @@ -937,6 +944,17 @@ int getCrankAngle_DualWheel(void) return crankAngle; } + +static uint16_t __attribute__((noinline)) calcEndTeeth_DualWheel(int ignitionAngle, uint8_t toothAdder) { + int16_t tempEndTooth = +#ifdef USE_LIBDIVIDE + libdivide::libdivide_s16_do(ignitionAngle - configPage4.triggerAngle, &divTriggerToothAngle); +#else + (ignitionAngle - (int16_t)configPage4.triggerAngle) / (int16_t)triggerToothAngle; +#endif + return clampToToothCount(tempEndTooth, toothAdder); +} + /** Dual Wheel - Set End Teeth. * * */ @@ -946,61 +964,22 @@ void triggerSetEndTeeth_DualWheel(void) byte toothAdder = 0; if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) ) { toothAdder = configPage4.triggerTeeth; } - int16_t tempIgnition1EndTooth; - tempIgnition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition1EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition1EndTooth <= 0) { tempIgnition1EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition1EndTooth = tempIgnition1EndTooth; - - int16_t tempIgnition2EndTooth; - tempIgnition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition2EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition2EndTooth <= 0) { tempIgnition2EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition2EndTooth = tempIgnition2EndTooth; - - int16_t tempIgnition3EndTooth; - tempIgnition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition3EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition3EndTooth <= 0) { tempIgnition3EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition3EndTooth = tempIgnition3EndTooth; - - int16_t tempIgnition4EndTooth; - tempIgnition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition4EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition4EndTooth <= 0) { tempIgnition4EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition4EndTooth = tempIgnition4EndTooth; - + ignition1EndTooth = calcEndTeeth_DualWheel(ignition1EndAngle, toothAdder); + ignition2EndTooth = calcEndTeeth_DualWheel(ignition2EndAngle, toothAdder); + ignition3EndTooth = calcEndTeeth_DualWheel(ignition3EndAngle, toothAdder); + ignition4EndTooth = calcEndTeeth_DualWheel(ignition4EndAngle, toothAdder); #if IGN_CHANNELS >= 5 - int16_t tempIgnition5EndTooth; - tempIgnition5EndTooth = ( (ignition5EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition5EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition5EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition5EndTooth <= 0) { tempIgnition5EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition5EndTooth = tempIgnition5EndTooth; + ignition5EndTooth = calcEndTeeth_DualWheel(ignition5EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 6 - int16_t tempIgnition6EndTooth; - tempIgnition6EndTooth = ( (ignition6EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition6EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition6EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition6EndTooth <= 0) { tempIgnition6EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition6EndTooth = tempIgnition6EndTooth; + ignition6EndTooth = calcEndTeeth_DualWheel(ignition6EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 7 - int16_t tempIgnition7EndTooth; - tempIgnition7EndTooth = ( (ignition7EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition7EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition7EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition7EndTooth <= 0) { tempIgnition7EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition7EndTooth = tempIgnition7EndTooth; + ignition7EndTooth = calcEndTeeth_DualWheel(ignition7EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 8 - int16_t tempIgnition8EndTooth; - tempIgnition8EndTooth = ( (ignition8EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ); - if(tempIgnition8EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition8EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition8EndTooth <= 0) { tempIgnition8EndTooth += (configPage4.triggerTeeth + toothAdder); } - ignition8EndTooth = tempIgnition8EndTooth; + ignition8EndTooth = calcEndTeeth_DualWheel(ignition8EndAngle, toothAdder); #endif - - lastToothCalcAdvance = currentStatus.advance; - } /** @} */ @@ -1015,8 +994,8 @@ void triggerSetup_BasicDistributor(void) { triggerActualTeeth = configPage2.nCylinders; if(triggerActualTeeth == 0) { triggerActualTeeth = 1; } - triggerToothAngle = 720 / triggerActualTeeth; //The number of degrees that passes from tooth to tooth - triggerFilterTime = 60000000L / MAX_RPM / configPage2.nCylinders; // Minimum time required between teeth + triggerToothAngle = 720U / triggerActualTeeth; //The number of degrees that passes from tooth to tooth + triggerFilterTime = MICROS_PER_MIN / MAX_RPM / configPage2.nCylinders; // Minimum time required between teeth triggerFilterTime = triggerFilterTime / 2; //Safety margin triggerFilterTime = 0; BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); @@ -1025,8 +1004,8 @@ void triggerSetup_BasicDistributor(void) toothCurrentCount = 0; //Default value BIT_SET(decoderState, BIT_DECODER_HAS_FIXED_CRANKING); BIT_SET(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT); - if(configPage2.nCylinders <= 4) { MAX_STALL_TIME = (1851UL * triggerToothAngle); }//Minimum 90rpm. (1851uS is the time per degree at 90rpm). This uses 90rpm rather than 50rpm due to the potentially very high stall time on a 4 cylinder if we wait that long. - else { MAX_STALL_TIME = (3200UL * triggerToothAngle); } //Minimum 50rpm. (3200uS is the time per degree at 50rpm). + if(configPage2.nCylinders <= 4U) { MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/90U) * triggerToothAngle); }//Minimum 90rpm. (1851uS is the time per degree at 90rpm). This uses 90rpm rather than 50rpm due to the potentially very high stall time on a 4 cylinder if we wait that long. + else { MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); } //Minimum 50rpm. (3200uS is the time per degree at 50rpm). } @@ -1091,9 +1070,9 @@ uint16_t getRPM_BasicDistributor(void) uint16_t tempRPM; if( currentStatus.RPM < currentStatus.crankRPM || currentStatus.RPM < 1500) { - tempRPM = crankingGetRPM(triggerActualTeeth, 720); + tempRPM = crankingGetRPM(triggerActualTeeth, CAM_SPEED); } - else { tempRPM = stdGetRPM(720); } + else { tempRPM = stdGetRPM(CAM_SPEED); } MAX_STALL_TIME = revolutionTime << 1; //Set the stall time to be twice the current RPM. This is a safe figure as there should be no single revolution where this changes more than this if(triggerActualTeeth == 1) { MAX_STALL_TIME = revolutionTime << 1; } //Special case for 1 cylinder engines that only get 1 pulse every 720 degrees @@ -1119,8 +1098,8 @@ int getCrankAngle_BasicDistributor(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - //crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH); + //crankAngle += timeToAngleDegPerMicroSec(elapsedTime); + crankAngle += timeToAngleIntervalTooth(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } @@ -1202,11 +1181,6 @@ void triggerSetEndTeeth_BasicDistributor(void) } break; } - - - - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -1222,7 +1196,7 @@ void triggerSetup_GM7X(void) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) } void triggerPri_GM7X(void) @@ -1287,7 +1261,7 @@ void triggerPri_GM7X(void) void triggerSec_GM7X(void) { return; } //Not required uint16_t getRPM_GM7X(void) { - return stdGetRPM(360); + return stdGetRPM(CRANK_SPEED); } int getCrankAngle_GM7X(void) { @@ -1318,7 +1292,7 @@ int getCrankAngle_GM7X(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1329,9 +1303,6 @@ int getCrankAngle_GM7X(void) void triggerSetEndTeeth_GM7X(void) { - - lastToothCalcAdvance = currentStatus.advance; - if(currentStatus.advance < 18 ) { ignition1EndTooth = 7; @@ -1403,7 +1374,7 @@ void triggerSetup_4G63(void) } 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)(MICROS_PER_SEC / (MAX_RPM / 60U * 2U)) / 2U; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed) triggerSecFilterTime_duration = 4000; secondaryLastToothTime = 0; } @@ -1694,14 +1665,14 @@ uint16_t getRPM_4G63(void) toothTime = (toothLastToothTime - toothLastMinusOneToothTime); //Note that trigger tooth angle changes between 70 and 110 depending on the last tooth that was seen (or 70/50 for 6 cylinders) interrupts(); toothTime = toothTime * 36; - tempRPM = ((unsigned long)tempToothAngle * 6000000UL) / toothTime; - revolutionTime = (10UL * toothTime) / tempToothAngle; + tempRPM = ((unsigned long)tempToothAngle * (MICROS_PER_MIN/10U)) / toothTime; + SetRevolutionTime((10UL * toothTime) / tempToothAngle); MAX_STALL_TIME = 366667UL; // 50RPM } } else { - tempRPM = stdGetRPM(720); + tempRPM = stdGetRPM(CAM_SPEED); //EXPERIMENTAL! Add/subtract RPM based on the last rpmDOT calc //tempRPM += (micros() - toothOneTime) * currentStatus.rpmDOT MAX_STALL_TIME = revolutionTime << 1; //Set the stall time to be twice the current RPM. This is a safe figure as there should be no single revolution where this changes more than this @@ -1731,7 +1702,7 @@ int getCrankAngle_4G63(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH); + crankAngle += timeToAngleIntervalTooth(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1777,9 +1748,6 @@ void triggerSetEndTeeth_4G63(void) ignition4EndTooth = 2; //Not used } } - - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -1820,7 +1788,7 @@ void triggerSetup_24X(void) toothAngles[22] = 342; toothAngles[23] = 357; - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) if(initialisationComplete == false) { toothCurrentCount = 25; toothLastToothTime = micros(); } //Set a startup value here to avoid filter errors when starting. This MUST have the init check to prevent the fuel pump just staying on all the time BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); @@ -1868,7 +1836,7 @@ void triggerSec_24X(void) uint16_t getRPM_24X(void) { - return stdGetRPM(360); + return stdGetRPM(CRANK_SPEED); } int getCrankAngle_24X(void) { @@ -1889,7 +1857,7 @@ int getCrankAngle_24X(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); //Sequential check (simply sets whether we're on the first or 2nd revolution of the cycle) if (tempRevolutionOne == 1) { crankAngle += 360; } @@ -1903,9 +1871,6 @@ int getCrankAngle_24X(void) void triggerSetEndTeeth_24X(void) { - - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -1933,7 +1898,7 @@ void triggerSetup_Jeep2000(void) toothAngles[10] = 454; toothAngles[11] = 474; - MAX_STALL_TIME = (3333UL * 60); //Minimum 50rpm. (3333uS is the time per degree at 50rpm). Largest gap between teeth is 60 degrees. + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * 60U); //Minimum 50rpm. (3333uS is the time per degree at 50rpm). Largest gap between teeth is 60 degrees. if(initialisationComplete == false) { toothCurrentCount = 13; toothLastToothTime = micros(); } //Set a startup value here to avoid filter errors when starting. This MUST have the initial check to prevent the fuel pump just staying on all the time BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); @@ -1982,7 +1947,7 @@ void triggerSec_Jeep2000(void) uint16_t getRPM_Jeep2000(void) { - return stdGetRPM(360); + return stdGetRPM(CRANK_SPEED); } int getCrankAngle_Jeep2000(void) { @@ -2002,7 +1967,7 @@ int getCrankAngle_Jeep2000(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2013,8 +1978,6 @@ int getCrankAngle_Jeep2000(void) void triggerSetEndTeeth_Jeep2000(void) { - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -2029,9 +1992,9 @@ void triggerSetup_Audi135(void) triggerToothAngle = 8; //135/3 = 45, 360/45 = 8 degrees every 3 teeth toothCurrentCount = 255; //Default value toothSystemCount = 0; - triggerFilterTime = (unsigned long)(1000000 / (MAX_RPM / 60 * 135UL)); //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 discarded as noise - 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) - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + triggerFilterTime = (unsigned long)(MICROS_PER_SEC / (MAX_RPM / 60U * 135UL)); //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 discarded as noise + triggerSecFilterTime = (int)(MICROS_PER_SEC / (MAX_RPM / 60U * 2U)) / 2U; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT); @@ -2098,7 +2061,7 @@ void triggerSec_Audi135(void) uint16_t getRPM_Audi135(void) { - return stdGetRPM(360); + return stdGetRPM(CRANK_SPEED); } int getCrankAngle_Audi135(void) @@ -2122,7 +2085,7 @@ int getCrankAngle_Audi135(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); //Sequential check (simply sets whether we're on the first or 2nd revolution of the cycle) if (tempRevolutionOne) { crankAngle += 360; } @@ -2136,7 +2099,6 @@ int getCrankAngle_Audi135(void) void triggerSetEndTeeth_Audi135(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ /** Honda D17 (1.7 liter 4 cyl SOHC). @@ -2147,7 +2109,7 @@ void triggerSetEndTeeth_Audi135(void) void triggerSetup_HondaD17(void) { triggerToothAngle = 360 / 12; //The number of degrees that passes from tooth to tooth - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); @@ -2197,7 +2159,7 @@ void triggerPri_HondaD17(void) void triggerSec_HondaD17(void) { return; } //The 4+1 signal on the cam is yet to be supported. If this ever changes, update BIT_DECODER_HAS_SECONDARY in the setup() function uint16_t getRPM_HondaD17(void) { - return stdGetRPM(360); + return stdGetRPM(CRANK_SPEED); } int getCrankAngle_HondaD17(void) { @@ -2224,7 +2186,7 @@ int getCrankAngle_HondaD17(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2235,7 +2197,6 @@ int getCrankAngle_HondaD17(void) void triggerSetEndTeeth_HondaD17(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -2278,7 +2239,7 @@ void triggerSetup_Miata9905(void) toothAngles[6] = 530; // toothAngles[7] = 640; // - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) triggerFilterTime = 1500; //10000 rpm, assuming we're triggering on both edges off the crank tooth. triggerSecFilterTime = 0; //Need to figure out something better for this BIT_SET(decoderState, BIT_DECODER_HAS_FIXED_CRANKING); @@ -2416,14 +2377,14 @@ uint16_t getRPM_Miata9905(void) toothTime = (toothLastToothTime - toothLastMinusOneToothTime); //Note that trigger tooth angle changes between 70 and 110 depending on the last tooth that was seen interrupts(); toothTime = toothTime * 36; - tempRPM = ((unsigned long)tempToothAngle * 6000000UL) / toothTime; - revolutionTime = (10UL * toothTime) / tempToothAngle; + tempRPM = ((unsigned long)tempToothAngle * (MICROS_PER_MIN/10U)) / toothTime; + SetRevolutionTime((10UL * toothTime) / tempToothAngle); MAX_STALL_TIME = 366667UL; // 50RPM } } else { - tempRPM = stdGetRPM(720); + tempRPM = stdGetRPM(CAM_SPEED); MAX_STALL_TIME = revolutionTime << 1; //Set the stall time to be twice the current RPM. This is a safe figure as there should be no single revolution where this changes more than this if(MAX_STALL_TIME < 366667UL) { MAX_STALL_TIME = 366667UL; } //Check for 50rpm minimum } @@ -2450,7 +2411,7 @@ int getCrankAngle_Miata9905(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2464,8 +2425,8 @@ int getCamAngle_Miata9905(void) { int16_t curAngle; //lastVVTtime is the time between tooth #1 (10* BTDC) and the single cam tooth. - //All cam angles in in BTDC, so the actual advance angle is 370 - fastTimeToAngle(lastVVTtime) - - curAngle = 370 - fastTimeToAngle(lastVVTtime) - configPage10.vvtCL0DutyAng; + //All cam angles in in BTDC, so the actual advance angle is 370 - timeToAngleDegPerMicroSec(lastVVTtime) - + curAngle = 370 - timeToAngleDegPerMicroSec(lastVVTtime) - configPage10.vvtCL0DutyAng; currentStatus.vvt1Angle = ANGLE_FILTER( (curAngle << 1), configPage4.ANGLEFILTER_VVT, currentStatus.vvt1Angle); return currentStatus.vvt1Angle; @@ -2509,8 +2470,6 @@ void triggerSetEndTeeth_Miata9905(void) ignition4EndTooth = 3; //Not used } } - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -2533,9 +2492,9 @@ void triggerSetup_MazdaAU(void) toothAngles[2] = 168; //tooth #3 toothAngles[3] = 276; //tooth #4 - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) 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)(MICROS_PER_SEC / (MAX_RPM / 60U * 2U)) / 2U; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed) BIT_SET(decoderState, BIT_DECODER_HAS_FIXED_CRANKING); BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); } @@ -2622,12 +2581,11 @@ uint16_t getRPM_MazdaAU(void) int tempToothAngle; noInterrupts(); tempToothAngle = triggerToothAngle; - revolutionTime = (toothLastToothTime - toothLastMinusOneToothTime); //Note that trigger tooth angle changes between 72 and 108 depending on the last tooth that was seen + SetRevolutionTime(36*(toothLastToothTime - toothLastMinusOneToothTime)); //Note that trigger tooth angle changes between 72 and 108 depending on the last tooth that was seen interrupts(); - revolutionTime = revolutionTime * 36; - tempRPM = (tempToothAngle * 60000000L) / revolutionTime; + tempRPM = (tempToothAngle * MICROS_PER_MIN) / revolutionTime; } - else { tempRPM = stdGetRPM(360); } + else { tempRPM = stdGetRPM(CRANK_SPEED); } } return tempRPM; } @@ -2651,7 +2609,7 @@ int getCrankAngle_MazdaAU(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2663,7 +2621,6 @@ int getCrankAngle_MazdaAU(void) void triggerSetEndTeeth_MazdaAU(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -2674,14 +2631,14 @@ There can be no missing teeth on the primary wheel. */ void triggerSetup_non360(void) { - triggerToothAngle = (360 * configPage4.TrigAngMul) / configPage4.triggerTeeth; //The number of degrees that passes from tooth to tooth multiplied by the additional multiplier + triggerToothAngle = (360U * configPage4.TrigAngMul) / configPage4.triggerTeeth; //The number of degrees that passes from tooth to tooth multiplied by the additional multiplier toothCurrentCount = 255; //Default value - triggerFilterTime = (1000000 / (MAX_RPM / 60 * configPage4.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 discarded as noise - triggerSecFilterTime = (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) + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 2U)) / 2U; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) } @@ -2700,8 +2657,8 @@ uint16_t getRPM_non360(void) uint16_t tempRPM = 0; if( (currentStatus.hasSync == true) && (toothCurrentCount != 0) ) { - if(currentStatus.RPM < currentStatus.crankRPM) { tempRPM = crankingGetRPM(configPage4.triggerTeeth, 360); } - else { tempRPM = stdGetRPM(360); } + if(currentStatus.RPM < currentStatus.crankRPM) { tempRPM = crankingGetRPM(configPage4.triggerTeeth, CRANK_SPEED); } + else { tempRPM = stdGetRPM(CRANK_SPEED); } } return tempRPM; } @@ -2727,7 +2684,7 @@ int getCrankAngle_non360(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2738,7 +2695,6 @@ int getCrankAngle_non360(void) void triggerSetEndTeeth_non360(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -2749,15 +2705,15 @@ See http://wiki.r31skylineclub.com/index.php/Crank_Angle_Sensor . */ void triggerSetup_Nissan360(void) { - triggerFilterTime = (1000000 / (MAX_RPM / 60 * 360UL)); //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 discarded as noise - 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) + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 360UL)); //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 discarded as noise + triggerSecFilterTime = (int)(MICROS_PER_SEC / (MAX_RPM / 60U * 2U)) / 2U; //Same as above, but fixed at 2 teeth on the secondary input and divided by 2 (for cam speed) secondaryToothCount = 0; //Initially set to 0 prior to calculating the secondary window duration BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); toothCurrentCount = 1; triggerToothAngle = 2; - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) } @@ -2799,8 +2755,6 @@ void triggerPri_Nissan360(void) } } - - timePerDegree = curGap >> 1;; //The time per crank degree is simply the time between this tooth and the last one divided by 2 } } @@ -2907,17 +2861,16 @@ uint16_t getRPM_Nissan360(void) if(currentStatus.startRevolutions < 2) { noInterrupts(); - revolutionTime = (toothLastToothTime - toothLastMinusOneToothTime) * 180; //Each tooth covers 2 crank degrees, so multiply by 180 to get a full revolution time. + SetRevolutionTime((toothLastToothTime - toothLastMinusOneToothTime) * 180); //Each tooth covers 2 crank degrees, so multiply by 180 to get a full revolution time. interrupts(); } else { noInterrupts(); - revolutionTime = (toothOneTime - toothOneMinusOneTime) >> 1; //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) + SetRevolutionTime((toothOneTime - toothOneMinusOneTime) >> 1); //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) interrupts(); } - tempRPM = (US_IN_MINUTE / revolutionTime); //Calc RPM based on last full revolution time (Faster as /) - if(tempRPM >= MAX_RPM) { tempRPM = currentStatus.RPM; } //Sanity check + tempRPM = RpmFromRevolutionTimeUs(revolutionTime); //Calc RPM based on last full revolution time (Faster as /) MAX_STALL_TIME = revolutionTime << 1; //Set the stall time to be twice the current RPM. This is a safe figure as there should be no single revolution where this changes more than this } else { tempRPM = 0; } @@ -2968,8 +2921,6 @@ void triggerSetEndTeeth_Nissan360(void) else { ignition3EndTooth = ( (ignition3EndAngle + 720 - configPage4.triggerAngle) / 2 ) - offset_teeth; } if((ignition4EndAngle - offset_teeth) > configPage4.triggerAngle) { ignition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / 2 ) - offset_teeth; } else { ignition4EndTooth = ( (ignition4EndAngle + 720 - configPage4.triggerAngle) / 2 ) - offset_teeth; } - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -2980,7 +2931,7 @@ This seems to be present in late 90's Subaru. In 2001 Subaru moved to 36-2-2-2 ( */ void triggerSetup_Subaru67(void) { - triggerFilterTime = (1000000 / (MAX_RPM / 60 * 360UL)); //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 discarded as noise + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 360UL)); //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 discarded as noise triggerSecFilterTime = 0; secondaryToothCount = 0; //Initially set to 0 prior to calculating the secondary window duration BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); @@ -2990,7 +2941,7 @@ void triggerSetup_Subaru67(void) triggerToothAngle = 2; BIT_CLEAR(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT); toothSystemCount = 0; - MAX_STALL_TIME = (3333UL * 93); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * 93U); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) toothAngles[0] = 710; //tooth #1 toothAngles[1] = 83; //tooth #2 @@ -3172,7 +3123,7 @@ uint16_t getRPM_Subaru67(void) if(currentStatus.startRevolutions > 0) { //As the tooth count is over 720 degrees - tempRPM = stdGetRPM(720); + tempRPM = stdGetRPM(CAM_SPEED); } return tempRPM; } @@ -3196,7 +3147,7 @@ int getCrankAngle_Subaru67(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH); + crankAngle += timeToAngleIntervalTooth(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -3243,8 +3194,6 @@ void triggerSetEndTeeth_Subaru67(void) //ignition4EndTooth = 10; } } - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -3259,13 +3208,13 @@ void triggerSetup_Daihatsu(void) { triggerActualTeeth = configPage2.nCylinders + 1; triggerToothAngle = 720 / triggerActualTeeth; //The number of degrees that passes from tooth to tooth - triggerFilterTime = 60000000L / MAX_RPM / configPage2.nCylinders; // Minimum time required between teeth + triggerFilterTime = MICROS_PER_MIN / MAX_RPM / configPage2.nCylinders; // Minimum time required between teeth triggerFilterTime = triggerFilterTime / 2; //Safety margin BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (1851UL * triggerToothAngle)*4;//Minimum 90rpm. (1851uS is the time per degree at 90rpm). This uses 90rpm rather than 50rpm due to the potentially very high stall time on a 4 cylinder if we wait that long. + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/90U) * triggerToothAngle)*4U;//Minimum 90rpm. (1851uS is the time per degree at 90rpm). This uses 90rpm rather than 50rpm due to the potentially very high stall time on a 4 cylinder if we wait that long. if(configPage2.nCylinders == 3) { @@ -3368,16 +3317,15 @@ uint16_t getRPM_Daihatsu(void) else { noInterrupts(); - revolutionTime = (toothLastToothTime - toothLastMinusOneToothTime) * (triggerActualTeeth-1); + SetRevolutionTime((toothLastToothTime - toothLastMinusOneToothTime) * (triggerActualTeeth-1)); interrupts(); - tempRPM = (US_IN_MINUTE / revolutionTime); - if(tempRPM >= MAX_RPM) { tempRPM = currentStatus.RPM; } //Sanity check + tempRPM = RpmFromRevolutionTimeUs(revolutionTime); } //is tooth #2 } else { tempRPM = 0; } //No sync } else - { tempRPM = stdGetRPM(720); } //Tracking over 2 crank revolutions + { tempRPM = stdGetRPM(CAM_SPEED); } //Tracking over 2 crank revolutions return tempRPM; @@ -3399,7 +3347,7 @@ int getCrankAngle_Daihatsu(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -3410,7 +3358,6 @@ int getCrankAngle_Daihatsu(void) void triggerSetEndTeeth_Daihatsu(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -3426,7 +3373,7 @@ void triggerSetup_Harley(void) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (3333UL * 60); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * 60U); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) if(initialisationComplete == false) { toothLastToothTime = micros(); } //Set a startup value here to avoid filter errors when starting. This MUST have the initial check to prevent the fuel pump just staying on all the time triggerFilterTime = 1500; } @@ -3499,15 +3446,15 @@ uint16_t getRPM_Harley(void) if(toothCurrentCount == 1) { tempToothAngle = 129; } else { tempToothAngle = toothAngles[toothCurrentCount-1] - toothAngles[toothCurrentCount-2]; } */ - revolutionTime = (toothOneTime - toothOneMinusOneTime); //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) + SetRevolutionTime(toothOneTime - toothOneMinusOneTime); //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) toothTime = (toothLastToothTime - toothLastMinusOneToothTime); //Note that trigger tooth angle changes between 129 and 332 depending on the last tooth that was seen interrupts(); toothTime = toothTime * 36; - tempRPM = ((unsigned long)tempToothAngle * 6000000UL) / toothTime; + tempRPM = ((unsigned long)tempToothAngle * (MICROS_PER_MIN/10U)) / toothTime; } } else { - tempRPM = stdGetRPM(360); + tempRPM = stdGetRPM(CRANK_SPEED); } } return tempRPM; @@ -3538,7 +3485,7 @@ int getCrankAngle_Harley(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -3549,7 +3496,6 @@ int getCrankAngle_Harley(void) void triggerSetEndTeeth_Harley(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -3569,7 +3515,7 @@ void triggerSetup_ThirtySixMinus222(void) { triggerToothAngle = 10; //The number of degrees that passes from tooth to tooth triggerActualTeeth = 30; //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 * configPage4.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 discarded as noise + triggerFilterTime = (int)(MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); @@ -3578,7 +3524,7 @@ void triggerSetup_ThirtySixMinus222(void) toothCurrentCount = 0; toothOneTime = 0; toothOneMinusOneTime = 0; - MAX_STALL_TIME = (3333UL * triggerToothAngle * 2 ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle * 2U ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) } void triggerPri_ThirtySixMinus222(void) @@ -3685,17 +3631,17 @@ uint16_t getRPM_ThirtySixMinus222(void) if( (configPage2.nCylinders == 4) && (toothCurrentCount != 19) && (toothCurrentCount != 16) && (toothCurrentCount != 34) && (BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) ) { - tempRPM = crankingGetRPM(36, 360); + tempRPM = crankingGetRPM(36, CRANK_SPEED); } else if( (configPage2.nCylinders == 6) && (toothCurrentCount != 9) && (toothCurrentCount != 12) && (toothCurrentCount != 33) && (BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) ) { - tempRPM = crankingGetRPM(36, 360); + tempRPM = crankingGetRPM(36, CRANK_SPEED); } else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM if we're at and of the missing teeth as it messes the calculation } else { - tempRPM = stdGetRPM(360); + tempRPM = stdGetRPM(CRANK_SPEED); } return tempRPM; } @@ -3736,9 +3682,6 @@ void triggerSetEndTeeth_ThirtySixMinus222(void) else if(currentStatus.advance < 40) { ignition3EndTooth = 20; } else { ignition3EndTooth = 19; } } - - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -3752,7 +3695,7 @@ void triggerSetup_ThirtySixMinus21(void) { triggerToothAngle = 10; //The number of degrees that passes from tooth to tooth triggerActualTeeth = 33; //The number of physical teeth on the wheel. Doing this here saves us a calculation each time in the interrupt. Not Used - triggerFilterTime = (1000000 / (MAX_RPM / 60 * configPage4.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 discarded as noise + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); @@ -3761,7 +3704,7 @@ void triggerSetup_ThirtySixMinus21(void) toothCurrentCount = 0; toothOneTime = 0; toothOneMinusOneTime = 0; - MAX_STALL_TIME = (3333UL * triggerToothAngle * 2 ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle * 2U ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) } void triggerPri_ThirtySixMinus21(void) @@ -3846,13 +3789,13 @@ uint16_t getRPM_ThirtySixMinus21(void) { if( (toothCurrentCount != 20) && (BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) ) { - tempRPM = crankingGetRPM(36, 360); + tempRPM = crankingGetRPM(36, CRANK_SPEED); } else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM if we're at tooth #1 as the missing tooth messes the calculation } else { - tempRPM = stdGetRPM(360); + tempRPM = stdGetRPM(CRANK_SPEED); } return tempRPM; } @@ -3867,9 +3810,6 @@ void triggerSetEndTeeth_ThirtySixMinus21(void) { ignition1EndTooth = 10; ignition2EndTooth = 28; // Arbitrarily picked at 180°. - - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -3884,7 +3824,7 @@ void triggerSetEndTeeth_ThirtySixMinus21(void) */ void triggerSetup_420a(void) { - triggerFilterTime = (1000000 / (MAX_RPM / 60 * 360UL)); //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 discarded as noise + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 360UL)); //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 discarded as noise triggerSecFilterTime = 0; secondaryToothCount = 0; //Initially set to 0 prior to calculating the secondary window duration BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); @@ -3894,7 +3834,7 @@ void triggerSetup_420a(void) triggerToothAngle = 20; //Is only correct for the 4 short pulses before each TDC BIT_CLEAR(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT); toothSystemCount = 0; - MAX_STALL_TIME = (3333UL * 93); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * 93U); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) toothAngles[0] = 711; //tooth #1, just before #1 TDC toothAngles[1] = 111; @@ -4004,11 +3944,11 @@ uint16_t getRPM_420a(void) if( currentStatus.RPM < currentStatus.crankRPM) { //Possibly look at doing special handling for cranking in the future, but for now just use the standard method - tempRPM = stdGetRPM(720); + tempRPM = stdGetRPM(CAM_SPEED); } else { - tempRPM = stdGetRPM(720); + tempRPM = stdGetRPM(CAM_SPEED); } return tempRPM; } @@ -4030,7 +3970,7 @@ int getCrankAngle_420a(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -4055,8 +3995,6 @@ void triggerSetEndTeeth_420a(void) ignition3EndTooth = 8; ignition4EndTooth = 12; } - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -4182,9 +4120,9 @@ void triggerSetup_FordST170(void) triggerToothAngle = 360 / configPage4.triggerTeeth; //The number of degrees that passes from tooth to tooth triggerActualTeeth = configPage4.triggerTeeth - configPage4.triggerMissingTeeth; //The number of physical teeth on the wheel. Doing this here saves us a calculation each time in the interrupt - triggerFilterTime = (1000000 / (MAX_RPM / 60 * configPage4.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 discarded as noise + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise - triggerSecFilterTime = 1000000 * 60 / MAX_RPM / 8 / 2; //Cam pattern is 8-3, so 2 nearest teeth are 90 deg crank angle apart. Cam can be advanced by 60 deg, so going from fully retarded to fully advanced closes the gap to 30 deg. Zetec cam pulleys aren't keyed from factory, so I subtracted additional 10 deg to avoid filter to be too aggressive. And there you have it 720/20=36. + triggerSecFilterTime = MICROS_PER_MIN / MAX_RPM / 8U / 2U; //Cam pattern is 8-3, so 2 nearest teeth are 90 deg crank angle apart. Cam can be advanced by 60 deg, so going from fully retarded to fully advanced closes the gap to 30 deg. Zetec cam pulleys aren't keyed from factory, so I subtracted additional 10 deg to avoid filter to be too aggressive. And there you have it 720/20=36. BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); @@ -4195,7 +4133,10 @@ void triggerSetup_FordST170(void) secondaryToothCount = 0; toothOneTime = 0; toothOneMinusOneTime = 0; - MAX_STALL_TIME = (3333UL * triggerToothAngle * (1 + 1)); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle * (1U + 1U)); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) +#ifdef USE_LIBDIVIDE + divTriggerToothAngle = libdivide::libdivide_s16_gen(triggerToothAngle); +#endif } void triggerSec_FordST170(void) @@ -4252,13 +4193,13 @@ uint16_t getRPM_FordST170(void) { if(toothCurrentCount != 1) { - tempRPM = crankingGetRPM(36, 360); + tempRPM = crankingGetRPM(36, CRANK_SPEED); } else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM if we're at tooth #1 as the missing tooth messes the calculation } else { - tempRPM = stdGetRPM(360); + tempRPM = stdGetRPM(CRANK_SPEED); } return tempRPM; } @@ -4283,7 +4224,7 @@ int getCrankAngle_FordST170(void) lastCrankAngleCalc = micros(); elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } else if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -4292,43 +4233,28 @@ int getCrankAngle_FordST170(void) return crankAngle; } +static uint16_t __attribute__((noinline)) calcSetEndTeeth_FordST170(int ignitionAngle, uint8_t toothAdder) { + int16_t tempEndTooth = ignitionAngle - configPage4.triggerAngle; +#ifdef USE_LIBDIVIDE + tempEndTooth = libdivide::libdivide_s16_do(tempEndTooth, &divTriggerToothAngle); +#else + tempEndTooth = tempEndTooth / (int16_t)triggerToothAngle; +#endif + tempEndTooth = nudge(1, 36U + toothAdder, tempEndTooth - 1, 36U + toothAdder); + return clampToActualTeeth((uint16_t)tempEndTooth, toothAdder); +} + void triggerSetEndTeeth_FordST170(void) { byte toothAdder = 0; if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) ) { toothAdder = 36; } - //Temp variables are used here to avoid potential issues if a trigger interrupt occurs part way through this function + ignition1EndTooth = calcSetEndTeeth_FordST170(ignition1EndAngle, toothAdder); + ignition2EndTooth = calcSetEndTeeth_FordST170(ignition2EndAngle, toothAdder); + ignition3EndTooth = calcSetEndTeeth_FordST170(ignition3EndAngle, toothAdder); + ignition4EndTooth = calcSetEndTeeth_FordST170(ignition4EndAngle, toothAdder); - int16_t tempIgnition1EndTooth; - tempIgnition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition1EndTooth > (36 + toothAdder)) { tempIgnition1EndTooth -= (36 + toothAdder); } - if(tempIgnition1EndTooth <= 0) { tempIgnition1EndTooth += (36 + toothAdder); } - if((uint16_t)tempIgnition1EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition1EndTooth = (triggerActualTeeth + toothAdder); } - ignition1EndTooth = tempIgnition1EndTooth; - - int16_t tempIgnition2EndTooth; - tempIgnition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition2EndTooth > (36 + toothAdder)) { tempIgnition2EndTooth -= (36 + toothAdder); } - if(tempIgnition2EndTooth <= 0) { tempIgnition2EndTooth += (36 + toothAdder); } - if((uint16_t)tempIgnition2EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition2EndTooth = (triggerActualTeeth + toothAdder); } - ignition2EndTooth = tempIgnition2EndTooth; - - int16_t tempIgnition3EndTooth; - tempIgnition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition3EndTooth > (36 + toothAdder)) { tempIgnition3EndTooth -= (36 + toothAdder); } - if(tempIgnition3EndTooth <= 0) { tempIgnition3EndTooth += (36 + toothAdder); } - if((uint16_t)tempIgnition3EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition3EndTooth = (triggerActualTeeth + toothAdder); } - ignition3EndTooth = tempIgnition3EndTooth; - - int16_t tempIgnition4EndTooth; - tempIgnition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition4EndTooth > (36 + toothAdder)) { tempIgnition4EndTooth -= (36 + toothAdder); } - if(tempIgnition4EndTooth <= 0) { tempIgnition4EndTooth += (36 + toothAdder); } - if((uint16_t)tempIgnition4EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition4EndTooth = (triggerActualTeeth + toothAdder); } - ignition4EndTooth = tempIgnition4EndTooth; // Removed ign channels >4 as an ST170 engine is a 4 cylinder - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -4338,13 +4264,13 @@ void triggerSetup_DRZ400(void) triggerToothAngle = 360 / configPage4.triggerTeeth; //The number of degrees that passes from tooth to tooth if(configPage4.TrigSpeed == 1) { triggerToothAngle = 720 / configPage4.triggerTeeth; } //Account for cam speed toothCurrentCount = 255; //Default value - triggerFilterTime = (1000000 / (MAX_RPM / 60 * configPage4.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 discarded as noise - triggerSecFilterTime = (1000000 / (MAX_RPM / 60 * 2)); //Same as above, but fixed at 2 teeth on the secondary input + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * configPage4.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 discarded as noise + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 2U)); //Same as above, but fixed at 2 teeth on the secondary input BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_SET(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_SET(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT); //This is always true for this pattern BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) } void triggerSec_DRZ400(void) @@ -4358,7 +4284,7 @@ void triggerSec_DRZ400(void) if(currentStatus.hasSync == false) { toothLastToothTime = micros(); - toothLastMinusOneToothTime = micros() - (6000000 / configPage4.triggerTeeth); //Fixes RPM at 10rpm until a full revolution has taken place + toothLastMinusOneToothTime = micros() - ((MICROS_PER_MIN/10U) / configPage4.triggerTeeth); //Fixes RPM at 10rpm until a full revolution has taken place toothCurrentCount = configPage4.triggerTeeth; currentStatus.syncLossCounter++; currentStatus.hasSync = true; @@ -4393,19 +4319,19 @@ void triggerSetup_NGC(void) //Primary trigger configPage4.triggerTeeth = 36; //The number of teeth on the wheel incl missing teeth. triggerToothAngle = 10; //The number of degrees that passes from tooth to tooth - triggerFilterTime = 1000000 / (MAX_RPM/60) / (360/triggerToothAngle); //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 discarded as noise + triggerFilterTime = MICROS_PER_SEC / (MAX_RPM/60U) / (360U/triggerToothAngle); //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 discarded as noise toothCurrentCount = 0; toothOneTime = 0; toothOneMinusOneTime = 0; toothLastMinusOneToothTime = 0; toothLastToothRisingTime = 0; - MAX_STALL_TIME = (3333UL * triggerToothAngle * 2 ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle * 2U ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) //Secondary trigger if (configPage2.nCylinders == 4) { - triggerSecFilterTime = (1000000 / (MAX_RPM/60) / (360/36) * 2); //Two nearest edges are 36 degrees apart. Multiply by 2 for half cam speed. + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM/60U) / (360U/36U) * 2U); //Two nearest edges are 36 degrees apart. Multiply by 2 for half cam speed. } else { - triggerSecFilterTime = (1000000 / (MAX_RPM/60) / (360/21) * 2); //Two nearest edges are 21 degrees apart. Multiply by 2 for half cam speed. + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM/60U) / (360U/21U) * 2U); //Two nearest edges are 21 degrees apart. Multiply by 2 for half cam speed. } secondaryToothCount = 0; toothSystemCount = 0; @@ -4436,6 +4362,9 @@ void triggerSetup_NGC(void) toothAngles[8] = 3; toothAngles[9] = 1; // Pos 9 is required to be the same as group 1 for easier math } +#ifdef USE_LIBDIVIDE + divTriggerToothAngle = libdivide::libdivide_s16_gen(triggerToothAngle); +#endif } void triggerPri_NGC(void) @@ -4664,106 +4593,53 @@ uint16_t getRPM_NGC(void) uint16_t tempRPM = 0; if( currentStatus.RPM < currentStatus.crankRPM) { - if (BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) { tempRPM = crankingGetRPM(36, 360); } + if (BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT)) { tempRPM = crankingGetRPM(36, CRANK_SPEED); } else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM if we're at any of the missing teeth as it messes the calculation } else { - tempRPM = stdGetRPM(360); + tempRPM = stdGetRPM(CRANK_SPEED); } return tempRPM; } +static inline uint16_t calcSetEndTeeth_NGC_SkipMissing(uint16_t toothNum) { + if(toothNum == 17U || toothNum == 18U) { return 16U; } // These are missing teeth, so set the next one before instead + if(toothNum == 35U || toothNum == 36U) { return 34U; } // These are missing teeth, so set the next one before instead + if(toothNum == 53U || toothNum == 54U) { return 52U; } // These are missing teeth, so set the next one before instead + if(toothNum > 70U) { return 70U; } // These are missing teeth, so set the next one before instead + return toothNum; + +} + +static uint16_t __attribute__((noinline)) calcSetEndTeeth_NGC(int ignitionAngle, uint8_t toothAdder) { + int16_t tempEndTooth = ignitionAngle - configPage4.triggerAngle; +#ifdef USE_LIBDIVIDE + tempEndTooth = libdivide::libdivide_s16_do(tempEndTooth, &divTriggerToothAngle); +#else + tempEndTooth = tempEndTooth / (int16_t)triggerToothAngle; +#endif + return calcSetEndTeeth_NGC_SkipMissing(clampToToothCount(tempEndTooth - 1, toothAdder)); +} + void triggerSetEndTeeth_NGC(void) { byte toothAdder = 0; if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) ) { toothAdder = configPage4.triggerTeeth; } - int16_t tempIgnition1EndTooth; - tempIgnition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition1EndTooth < 1) { tempIgnition1EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition1EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition1EndTooth == 17 || tempIgnition1EndTooth == 18) { tempIgnition1EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition1EndTooth == 35 || tempIgnition1EndTooth == 36) { tempIgnition1EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition1EndTooth == 53 || tempIgnition1EndTooth == 54) { tempIgnition1EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition1EndTooth > 70) { tempIgnition1EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition1EndTooth = tempIgnition1EndTooth; - - int16_t tempIgnition2EndTooth; - tempIgnition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition2EndTooth < 1) { tempIgnition2EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition2EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition2EndTooth == 17 || tempIgnition2EndTooth == 18) { tempIgnition2EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition2EndTooth == 35 || tempIgnition2EndTooth == 36) { tempIgnition2EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition2EndTooth == 53 || tempIgnition2EndTooth == 54) { tempIgnition2EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition2EndTooth > 70) { tempIgnition2EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition2EndTooth = tempIgnition2EndTooth; - - int16_t tempIgnition3EndTooth; - tempIgnition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition3EndTooth < 1) { tempIgnition3EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition3EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition3EndTooth == 17 || tempIgnition3EndTooth == 18) { tempIgnition3EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition3EndTooth == 35 || tempIgnition3EndTooth == 36) { tempIgnition3EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition3EndTooth == 53 || tempIgnition3EndTooth == 54) { tempIgnition3EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition3EndTooth > 70) { tempIgnition3EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition3EndTooth = tempIgnition3EndTooth; - - int16_t tempIgnition4EndTooth; - tempIgnition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition4EndTooth < 1) { tempIgnition4EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition4EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition4EndTooth == 17 || tempIgnition4EndTooth == 18) { tempIgnition4EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition4EndTooth == 35 || tempIgnition4EndTooth == 36) { tempIgnition4EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition4EndTooth == 53 || tempIgnition4EndTooth == 54) { tempIgnition4EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition4EndTooth > 70) { tempIgnition4EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition4EndTooth = tempIgnition4EndTooth; - + ignition1EndTooth = calcSetEndTeeth_NGC(ignition1EndAngle, toothAdder); + ignition2EndTooth = calcSetEndTeeth_NGC(ignition2EndAngle, toothAdder); + ignition3EndTooth = calcSetEndTeeth_NGC(ignition3EndAngle, toothAdder); + ignition4EndTooth = calcSetEndTeeth_NGC(ignition4EndAngle, toothAdder); #if IGN_CHANNELS >= 6 - int16_t tempIgnition5EndTooth; - tempIgnition5EndTooth = ( (ignition5EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition5EndTooth < 1) { tempIgnition5EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition5EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition5EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition5EndTooth == 17 || tempIgnition5EndTooth == 18) { tempIgnition5EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition5EndTooth == 35 || tempIgnition5EndTooth == 36) { tempIgnition5EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition5EndTooth == 53 || tempIgnition5EndTooth == 54) { tempIgnition5EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition5EndTooth > 70) { tempIgnition5EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition5EndTooth = tempIgnition5EndTooth; - - int16_t tempIgnition6EndTooth; - tempIgnition6EndTooth = ( (ignition6EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition6EndTooth < 1) { tempIgnition6EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition6EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition6EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition6EndTooth == 17 || tempIgnition6EndTooth == 18) { tempIgnition6EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition6EndTooth == 35 || tempIgnition6EndTooth == 36) { tempIgnition6EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition6EndTooth == 53 || tempIgnition6EndTooth == 54) { tempIgnition6EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition6EndTooth > 70) { tempIgnition6EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition6EndTooth = tempIgnition6EndTooth; + ignition5EndTooth = calcSetEndTeeth_NGC(ignition5EndAngle, toothAdder); + ignition6EndTooth = calcSetEndTeeth_NGC(ignition6EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 8 - int16_t tempIgnition7EndTooth; - tempIgnition7EndTooth = ( (ignition7EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition7EndTooth < 1) { tempIgnition7EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition7EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition7EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition7EndTooth == 17 || tempIgnition7EndTooth == 18) { tempIgnition7EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition7EndTooth == 35 || tempIgnition7EndTooth == 36) { tempIgnition7EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition7EndTooth == 53 || tempIgnition7EndTooth == 54) { tempIgnition7EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition7EndTooth > 70) { tempIgnition7EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition7EndTooth = tempIgnition7EndTooth; - - int16_t tempIgnition8EndTooth; - tempIgnition8EndTooth = ( (ignition8EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition - if(tempIgnition8EndTooth < 1) { tempIgnition8EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 - else if(tempIgnition8EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition8EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count - if(tempIgnition8EndTooth == 17 || tempIgnition8EndTooth == 18) { tempIgnition8EndTooth = 16; } // These are missing teeth, so set the next one before instead - else if(tempIgnition8EndTooth == 35 || tempIgnition8EndTooth == 36) { tempIgnition8EndTooth = 34; } // These are missing teeth, so set the next one before instead - else if(tempIgnition8EndTooth == 53 || tempIgnition8EndTooth == 54) { tempIgnition8EndTooth = 52; } // These are missing teeth, so set the next one before instead - else if(tempIgnition8EndTooth > 70) { tempIgnition8EndTooth = 70; } // These are missing teeth, so set the next one before instead - ignition8EndTooth = tempIgnition8EndTooth; + ignition7EndTooth = calcSetEndTeeth_NGC(ignition7EndAngle, toothAdder); + ignition8EndTooth = calcSetEndTeeth_NGC(ignition8EndAngle, toothAdder); #endif - - lastToothCalcAdvance = currentStatus.advance; } /** Yamaha Vmax 1990+ with 6 uneven teeth, triggering on the wide lobe. @@ -4778,7 +4654,7 @@ void triggerSetup_Vmax(void) BIT_CLEAR(decoderState, BIT_DECODER_2ND_DERIV); BIT_CLEAR(decoderState, BIT_DECODER_IS_SEQUENTIAL); BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); - MAX_STALL_TIME = (3333UL * 60); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * 60U); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) if(initialisationComplete == false) { toothLastToothTime = micros(); } //Set a startup value here to avoid filter errors when starting. This MUST have the initi check to prevent the fuel pump just staying on all the time triggerFilterTime = 1500; BIT_SET(decoderState, BIT_DECODER_VALID_TRIGGER); // We must start with a valid trigger or we cannot start measuring the lobe width. We only have a false trigger on the lobe up event when it doesn't pass the filter. Then, the lobe width will also not be beasured. @@ -4864,8 +4740,8 @@ void triggerPri_Vmax(void) } else if( BIT_CHECK(decoderState, BIT_DECODER_VALID_TRIGGER) ) // Inverted due to vr conditioner. So this is the falling lobe. We only process if there was a valid trigger. { - unsigned long curGap3 = curTime - curGap2; - if (curGap3 > (lastGap * 2)){// Small lobe is 5 degrees, big lobe is 45 degrees. So this should be the wide lobe. + unsigned long curGapLocal = curTime - curGap2; + if (curGapLocal > (lastGap * 2)){// Small lobe is 5 degrees, big lobe is 45 degrees. So this should be the wide lobe. if (toothCurrentCount == 0 || toothCurrentCount == 6){//Wide should be seen with toothCurrentCount = 0, when there is no sync yet, or toothCurrentCount = 6 when we have done a full revolution. currentStatus.hasSync = true; } @@ -4881,7 +4757,7 @@ void triggerPri_Vmax(void) else{// Small lobe, just add 1 to the toothCurrentCount. toothCurrentCount++; } - lastGap = curGap3; + lastGap = curGapLocal; return; } else if( BIT_CHECK(decoderState, BIT_DECODER_VALID_TRIGGER) == false) @@ -4912,15 +4788,15 @@ uint16_t getRPM_Vmax(void) { noInterrupts(); tempToothAngle = triggerToothAngle; - revolutionTime = (toothOneTime - toothOneMinusOneTime); //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) + SetRevolutionTime(toothOneTime - toothOneMinusOneTime); //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) toothTime = (toothLastToothTime - toothLastMinusOneToothTime); interrupts(); toothTime = toothTime * 36; - tempRPM = ((unsigned long)tempToothAngle * 6000000UL) / toothTime; + tempRPM = ((unsigned long)tempToothAngle * (MICROS_PER_MIN/10U)) / toothTime; } } else { - tempRPM = stdGetRPM(360); + tempRPM = stdGetRPM(CRANK_SPEED); } } return tempRPM; @@ -4945,7 +4821,7 @@ int getCrankAngle_Vmax(void) //Estimate the number of degrees travelled since the last tooth} elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -4956,7 +4832,6 @@ int getCrankAngle_Vmax(void) void triggerSetEndTeeth_Vmax(void) { - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -4980,7 +4855,7 @@ void triggerSetup_Renix(void) configPage4.triggerTeeth = 4; // wheel has 44 teeth but we use these to work out which tooth angle to use, therefore speeduino thinks we only have 8 teeth. configPage4.triggerMissingTeeth = 0; triggerActualTeeth = 4; //The number of teeth we're pretending physically existing on the wheel. - triggerFilterTime = (1000000 / (MAX_RPM / 60 * 44)); //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 = (MICROS_PER_SEC / (MAX_RPM / 60U * 44U)); //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 } else if (configPage2.nCylinders == 6) { @@ -4988,15 +4863,18 @@ void triggerSetup_Renix(void) configPage4.triggerTeeth = 6; // wheel has 44 teeth but we use these to work out which tooth angle to use, therefore speeduino thinks we only have 6 teeth. configPage4.triggerMissingTeeth = 0; triggerActualTeeth = 6; //The number of teeth we're pretending physically existing on the wheel. - triggerFilterTime = (1000000 / (MAX_RPM / 60 * 66)); //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 = (MICROS_PER_SEC / (MAX_RPM / 60U * 66U)); //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 } - MAX_STALL_TIME = (3333UL * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm). Largest gap between teeth is 90 or 60 degrees depending on decoder. + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle); //Minimum 50rpm. (3333uS is the time per degree at 50rpm). Largest gap between teeth is 90 or 60 degrees depending on decoder. BIT_CLEAR(decoderState, BIT_DECODER_HAS_SECONDARY); toothSystemCount = 1; toothCurrentCount = 1; toothLastToothTime = 0; +#ifdef USE_LIBDIVIDE + divTriggerToothAngle = libdivide::libdivide_s16_gen(triggerToothAngle); +#endif } @@ -5079,6 +4957,18 @@ void triggerPri_Renix(void) } } +static uint16_t __attribute__((noinline)) calcEndTeeth_Renix(int ignitionAngle, uint8_t toothAdder) { + int16_t tempEndTooth = ignitionAngle - configPage4.triggerAngle; +#ifdef USE_LIBDIVIDE + tempEndTooth = libdivide::libdivide_s16_do(tempEndTooth, &divTriggerToothAngle); +#else + tempEndTooth = tempEndTooth / (int16_t)triggerToothAngle; +#endif + tempEndTooth = tempEndTooth - 1; + // Clamp to tooth count + return clampToActualTeeth(clampToToothCount(tempEndTooth, toothAdder), toothAdder); +} + void triggerSetEndTeeth_Renix(void) { byte toothAdder = 0; @@ -5086,78 +4976,23 @@ void triggerSetEndTeeth_Renix(void) //Temp variables are used here to avoid potential issues if a trigger interrupt occurs part way through this function - int16_t tempIgnition1EndTooth; - tempIgnition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition1EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition1EndTooth <= 0) { tempIgnition1EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition1EndTooth > triggerActualTeeth && tempIgnition1EndTooth <= configPage4.triggerTeeth) { tempIgnition1EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition1EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition1EndTooth = (triggerActualTeeth + toothAdder); } - ignition1EndTooth = tempIgnition1EndTooth; - - - int16_t tempIgnition2EndTooth; - tempIgnition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition2EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition2EndTooth <= 0) { tempIgnition2EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition2EndTooth > triggerActualTeeth && tempIgnition2EndTooth <= configPage4.triggerTeeth) { tempIgnition2EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition2EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition2EndTooth = (triggerActualTeeth + toothAdder); } - ignition2EndTooth = tempIgnition2EndTooth; + ignition1EndTooth = calcEndTeeth_Renix(ignition1EndAngle, toothAdder); + ignition2EndTooth = calcEndTeeth_Renix(ignition2EndAngle, toothAdder); currentStatus.canin[1] = ignition2EndTooth; - - int16_t tempIgnition3EndTooth; - tempIgnition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition3EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition3EndTooth <= 0) { tempIgnition3EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition3EndTooth > triggerActualTeeth && tempIgnition3EndTooth <= configPage4.triggerTeeth) { tempIgnition3EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition3EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition3EndTooth = (triggerActualTeeth + toothAdder); } - ignition3EndTooth = tempIgnition3EndTooth; - - int16_t tempIgnition4EndTooth; - tempIgnition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition4EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition4EndTooth <= 0) { tempIgnition4EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition4EndTooth > triggerActualTeeth && tempIgnition4EndTooth <= configPage4.triggerTeeth) { tempIgnition4EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition4EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition4EndTooth = (triggerActualTeeth + toothAdder); } - ignition4EndTooth = tempIgnition4EndTooth; - + ignition3EndTooth = calcEndTeeth_Renix(ignition3EndAngle, toothAdder); + ignition4EndTooth = calcEndTeeth_Renix(ignition4EndAngle, toothAdder); #if IGN_CHANNELS >= 5 - int16_t tempIgnition5EndTooth; - tempIgnition5EndTooth = ( (ignition5EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition5EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition5EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition5EndTooth <= 0) { tempIgnition5EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition5EndTooth > triggerActualTeeth && tempIgnition5EndTooth <= configPage4.triggerTeeth) { tempIgnition5EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition5EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition5EndTooth = (triggerActualTeeth + toothAdder); } - ignition5EndTooth = tempIgnition5EndTooth; + ignition5EndTooth = calcEndTeeth_Renix(ignition5EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 6 - int16_t tempIgnition6EndTooth; - tempIgnition6EndTooth = ( (ignition6EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition6EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition6EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition6EndTooth <= 0) { tempIgnition6EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition6EndTooth > triggerActualTeeth && tempIgnition6EndTooth <= configPage4.triggerTeeth) { tempIgnition6EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition6EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition6EndTooth = (triggerActualTeeth + toothAdder); } - ignition6EndTooth = tempIgnition6EndTooth; + ignition6EndTooth = calcEndTeeth_Renix(ignition6EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 7 - int16_t tempIgnition7EndTooth; - tempIgnition7EndTooth = ( (ignition7EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition7EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition7EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition7EndTooth <= 0) { tempIgnition7EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition7EndTooth > triggerActualTeeth && tempIgnition7EndTooth <= configPage4.triggerTeeth) { tempIgnition7EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition7EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition7EndTooth = (triggerActualTeeth + toothAdder); } - ignition7EndTooth = tempIgnition7EndTooth; + ignition7EndTooth = calcEndTeeth_Renix(ignition7EndAngle, toothAdder); #endif #if IGN_CHANNELS >= 8 - int16_t tempIgnition8EndTooth; - tempIgnition8EndTooth = ( (ignition8EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; - if(tempIgnition8EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition8EndTooth -= (configPage4.triggerTeeth + toothAdder); } - if(tempIgnition8EndTooth <= 0) { tempIgnition8EndTooth += (configPage4.triggerTeeth + toothAdder); } - if((uint16_t)tempIgnition8EndTooth > triggerActualTeeth && tempIgnition8EndTooth <= configPage4.triggerTeeth) { tempIgnition8EndTooth = triggerActualTeeth; } - if((uint16_t)tempIgnition8EndTooth > (triggerActualTeeth + toothAdder)) { tempIgnition8EndTooth = (triggerActualTeeth + toothAdder); } - ignition8EndTooth = tempIgnition8EndTooth; + ignition8EndTooth = calcEndTeeth_Renix(ignition8EndAngle, toothAdder); #endif - - lastToothCalcAdvance = currentStatus.advance; } /** @} */ @@ -5183,8 +5018,8 @@ void triggerSetup_RoverMEMS() for(toothOneTime = 0; toothOneTime < 10; toothOneTime++) // repurpose variable temporarily to help clear ToothAngles. { toothAngles[toothOneTime] = 0; }// Repurpose ToothAngles to store data needed for this implementation. - triggerFilterTime = (1000000 / (MAX_RPM / 60 * 36)); //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 - triggerSecFilterTime = (1000000 / (MAX_RPM / 60)); // only 1 tooth on the wheel not 36 + triggerFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U * 36U)); //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 + triggerSecFilterTime = (MICROS_PER_SEC / (MAX_RPM / 60U)); // only 1 tooth on the wheel not 36 configPage4.triggerTeeth = 36; triggerToothAngle = 360 / configPage4.triggerTeeth; //The number of degrees that passes from tooth to tooth 360 / 36 theortical teeth @@ -5197,7 +5032,7 @@ void triggerSetup_RoverMEMS() toothOneMinusOneTime = 0; revolutionOne=0; - MAX_STALL_TIME = (3333UL * triggerToothAngle * 2); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + MAX_STALL_TIME = ((MICROS_PER_DEG_1_RPM/50U) * triggerToothAngle * 2U); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) BIT_SET(decoderState, BIT_DECODER_HAS_SECONDARY); } @@ -5393,7 +5228,7 @@ int getCrankAngle_RoverMEMS() lastCrankAngleCalc = micros(); elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); - crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + crankAngle += timeToAngleDegPerMicroSec(elapsedTime); if (crankAngle >= 720) { crankAngle -= 720; } else if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -5494,12 +5329,12 @@ uint16_t getRPM_RoverMEMS() (toothCurrentCount != (unsigned int) toothAngles[SKIP_TOOTH2]) && (toothCurrentCount != (unsigned int) toothAngles[SKIP_TOOTH3]) && (toothCurrentCount != (unsigned int) toothAngles[SKIP_TOOTH4]) ) - { tempRPM = crankingGetRPM(36,360); } + { tempRPM = crankingGetRPM(36, CRANK_SPEED); } else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM as the missing tooth messes the calculation } else - { tempRPM = stdGetRPM(360); } + { tempRPM = stdGetRPM(CRANK_SPEED); } return tempRPM; } @@ -5563,9 +5398,6 @@ void triggerSetEndTeeth_RoverMEMS() ignition2EndTooth = tempIgnitionEndTooth[2]; ignition3EndTooth = tempIgnitionEndTooth[3]; ignition4EndTooth = tempIgnitionEndTooth[4]; - - lastToothCalcAdvance = currentStatus.advance; - } /** @} */ /** @} */ diff --git a/speeduino/decoders.h b/speeduino/decoders.h index 52528376..16265ec4 100644 --- a/speeduino/decoders.h +++ b/speeduino/decoders.h @@ -61,7 +61,7 @@ extern bool decoderHasFixedCrankingTiming; */ //This isn't to to filter out wrong pulses on triggers, but just to smooth out the cam angle reading for better closed loop VVT control. -#define ANGLE_FILTER(input, alpha, prior) (((long)input * (256 - alpha) + ((long)prior * alpha))) >> 8 +#define ANGLE_FILTER(input, alpha, prior) (((long)(input) * (256 - (alpha)) + ((long)(prior) * (alpha)))) >> 8 void loggerPrimaryISR(void); void loggerSecondaryISR(void); @@ -282,9 +282,25 @@ extern volatile uint16_t triggerToothAngle; //The number of crank degrees that e extern byte checkSyncToothCount; //How many teeth must've been seen on this revolution before we try to confirm sync (Useful for missing tooth type decoders) extern unsigned long elapsedTime; extern unsigned long lastCrankAngleCalc; -extern int16_t lastToothCalcAdvance; //Invalid value here forces calculation of this on first main loop extern unsigned long lastVVTtime; //The time between the vvt reference pulse and the last crank pulse +typedef uint32_t UQ24X8_t; +constexpr uint8_t UQ24X8_Shift = 8U; + +/** @brief uS per degree at current RPM in UQ24.8 fixed point */ +extern UQ24X8_t microsPerDegree; +constexpr uint8_t microsPerDegree_Shift = UQ24X8_Shift; + +typedef uint16_t UQ1X15_t; +constexpr uint8_t UQ1X15_Shift = 15U; + +/** @brief Degrees per uS in UQ1.15 fixed point. + * + * Ranges from 8 (0.000246) at MIN_RPM to 3542 (0.108) at MAX_RPM + */ +extern UQ1X15_t degreesPerMicro; +constexpr uint8_t degreesPerMicro_Shift = UQ1X15_Shift; + extern uint16_t ignition1EndTooth; extern uint16_t ignition2EndTooth; extern uint16_t ignition3EndTooth; @@ -296,12 +312,8 @@ extern uint16_t ignition8EndTooth; extern int16_t toothAngles[24]; //An array for storing fixed tooth angles. Currently sized at 24 for the GM 24X decoder, but may grow later if there are other decoders that use this style -//Used for identifying long and short pulses on the 4G63 (And possibly other) trigger patterns -#define LONG 0; -#define SHORT 1; - -#define CRANK_SPEED 0 -#define CAM_SPEED 1 +#define CRANK_SPEED 0U +#define CAM_SPEED 1U #define TOOTH_CRANK 0 #define TOOTH_CAM_SECONDARY 1 diff --git a/speeduino/engineProtection.cpp b/speeduino/engineProtection.cpp index e6c406aa..688a3f96 100644 --- a/speeduino/engineProtection.cpp +++ b/speeduino/engineProtection.cpp @@ -1,6 +1,7 @@ #include "globals.h" #include "engineProtection.h" +#include "maths.h" byte oilProtStartTime = 0; @@ -105,10 +106,10 @@ byte checkOilPressureLimit(void) if(currentStatus.oilPressure < oilLimit) { //Check if this is the first time we've been below the limit - if(oilProtStartTime == 0) { oilProtStartTime = (millis() / 100); } + if(oilProtStartTime == 0) { oilProtStartTime = div100(millis()); } /* Check if countdown has reached its target, if so then instruct to cut */ - if( (uint8_t(millis()/100) >= (uint16_t(oilProtStartTime + configPage10.oilPressureProtTime)) ) || (alreadyActive > 0) ) + if( (uint8_t(div100(millis())) >= (uint16_t(oilProtStartTime + configPage10.oilPressureProtTime)) ) || (alreadyActive > 0) ) { BIT_SET(currentStatus.engineProtectStatus, ENGINE_PROTECT_BIT_OIL); oilProtectActive = 1; diff --git a/speeduino/globals.h b/speeduino/globals.h index d9711486..d72ba1d2 100644 --- a/speeduino/globals.h +++ b/speeduino/globals.h @@ -120,8 +120,9 @@ #define interruptSafe(c) (noInterrupts(); {c} interrupts();) //Wraps any code between nointerrupt and interrupt calls -#define MS_IN_MINUTE 60000 -#define US_IN_MINUTE 60000000 +#define MICROS_PER_SEC INT32_C(1000000) +#define MICROS_PER_MIN INT32_C(MICROS_PER_SEC*60U) +#define MICROS_PER_HOUR INT32_C(MICROS_PER_MIN*60U) #define SERIAL_PORT_PRIMARY 0 #define SERIAL_PORT_SECONDARY 3 @@ -243,11 +244,11 @@ #define OUTPUT_CONTROL_DIRECT 0 #define OUTPUT_CONTROL_MC33810 10 -#define IGN_MODE_WASTED 0 -#define IGN_MODE_SINGLE 1 -#define IGN_MODE_WASTEDCOP 2 -#define IGN_MODE_SEQUENTIAL 3 -#define IGN_MODE_ROTARY 4 +#define IGN_MODE_WASTED 0U +#define IGN_MODE_SINGLE 1U +#define IGN_MODE_WASTEDCOP 2U +#define IGN_MODE_SEQUENTIAL 3U +#define IGN_MODE_ROTARY 4U #define SEC_TRIGGER_SINGLE 0 #define SEC_TRIGGER_4_1 1 @@ -345,14 +346,12 @@ #define MULTIPLY_MAP_MODE_BARO 1 #define MULTIPLY_MAP_MODE_100 2 -#define FOUR_STROKE 0 -#define TWO_STROKE 1 +#define FOUR_STROKE 0U +#define TWO_STROKE 1U #define GOING_LOW 0 #define GOING_HIGH 1 -#define MAX_RPM 18000 /**< The maximum rpm that the ECU will attempt to run at. It is NOT related to the rev limiter, but is instead dictates how fast certain operations will be allowed to run. Lower number gives better performance */ - #define BATTV_COR_MODE_WHOLE 0 #define BATTV_COR_MODE_OPENTIME 1 @@ -384,7 +383,7 @@ #define CALIBRATION_TABLE_SIZE 512 ///< Calibration table size for CLT, IAT, O2 #define CALIBRATION_TEMPERATURE_OFFSET 40 /**< All temperature measurements are stored offset by 40 degrees. This is so we can use an unsigned byte (0-255) to represent temperature ranges from -40 to 215 */ -#define OFFSET_FUELTRIM 127 ///< The fuel trim tables are offset by 128 to allow for -128 to +128 values +#define OFFSET_FUELTRIM 127U ///< The fuel trim tables are offset by 128 to allow for -128 to +128 values #define OFFSET_IGNITION 40 ///< Ignition values from the main spark table are offset 40 degrees downwards to allow for negative spark timing #define SERIAL_BUFFER_THRESHOLD 32 ///< When the serial buffer is filled to greater than this threshold value, the serial processing operations will be performed more urgently in order to avoid it overflowing. Serial buffer is 64 bytes long, so the threshold is set at half this as a reasonable figure @@ -581,11 +580,11 @@ struct statuses { uint16_t RPM; ///< RPM - Current Revs per minute byte RPMdiv100; ///< RPM value scaled (divided by 100) to fit a byte (0-255, e.g. 12000 => 120) long longRPM; ///< RPM as long int (gets assigned to / maintained in statuses.RPM as well) - int mapADC; + uint16_t mapADC; int baroADC; long MAP; ///< Manifold absolute pressure. Has to be a long for PID calcs (Boost control) int16_t EMAP; ///< EMAP ... (See @ref config6.useEMAP for EMAP enablement) - int16_t EMAPADC; + uint16_t EMAPADC; byte baro; ///< Barometric pressure is simply the initial MAP reading, taken before the engine is running. Alternatively, can be taken from an external sensor byte TPS; /**< The current TPS reading (0% - 100%). Is the tpsADC value after the calibration is applied */ byte tpsADC; /**< byte (valued: 0-255) representation of the TPS. Downsampled from the original 10-bit (0-1023) reading, but before any calibration is applied */ @@ -696,6 +695,10 @@ struct statuses { byte airConStatus; }; +static inline bool HasAnySync(const statuses &status) { + return status.hasSync || BIT_CHECK(status.status3, BIT_STATUS3_HALFSYNC); +} + /** Page 2 of the config - mostly variables that are required for fuel. * These are "non-live" EFI setting, engine and "system" variables that remain fixed once sent * (and stored to e.g. EEPROM) from configuration/tuning SW (from outside by USBserial/bluetooth). @@ -929,13 +932,13 @@ struct config4 { byte ignBypassPin : 6; //Pin the ignition bypass is activated on byte ignBypassHiLo : 1; //Whether this should be active high or low. - byte ADCFILTER_TPS; - byte ADCFILTER_CLT; - byte ADCFILTER_IAT; - byte ADCFILTER_O2; - byte ADCFILTER_BAT; - byte ADCFILTER_MAP; //This is only used on Instantaneous MAP readings and is intentionally very weak to allow for faster response - byte ADCFILTER_BARO; + uint8_t ADCFILTER_TPS; + uint8_t ADCFILTER_CLT; + uint8_t ADCFILTER_IAT; + uint8_t ADCFILTER_O2; + uint8_t ADCFILTER_BAT; + uint8_t ADCFILTER_MAP; //This is only used on Instantaneous MAP readings and is intentionally very weak to allow for faster response + uint8_t ADCFILTER_BARO; byte cltAdvBins[6]; /**< Coolant Temp timing advance curve bins */ byte cltAdvValues[6]; /**< Coolant timing advance curve values. These are translated by 15 to allow for negative values */ diff --git a/speeduino/idle.cpp b/speeduino/idle.cpp index 4f463221..3b90240a 100644 --- a/speeduino/idle.cpp +++ b/speeduino/idle.cpp @@ -22,7 +22,7 @@ unsigned int completedHomeSteps; volatile bool idle_pwm_state; bool lastDFCOValue; -unsigned int idle_pwm_max_count; //Used for variable PWM frequency +uint16_t idle_pwm_max_count; //Used for variable PWM frequency volatile unsigned int idle_pwm_cur_value; long idle_pid_target_value; long FeedForwardTerm; @@ -109,11 +109,11 @@ void initialiseIdle(bool forcehoming) iacCrankDutyTable.axisX = configPage6.iacCrankBins; #if defined(CORE_AVR) - idle_pwm_max_count = 1000000L / (16 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (16U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY35) - idle_pwm_max_count = 1000000L / (32 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 32uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (32U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 32uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY41) - idle_pwm_max_count = 1000000L / (2 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (2U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #endif enableIdle(); break; @@ -133,11 +133,11 @@ void initialiseIdle(bool forcehoming) iacCrankDutyTable.axisX = configPage6.iacCrankBins; #if defined(CORE_AVR) - idle_pwm_max_count = 1000000L / (16 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (16U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY) - idle_pwm_max_count = 1000000L / (32 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 32uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (32U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 32uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY41) - idle_pwm_max_count = 1000000L / (2 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (2U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #endif idlePID.SetOutputLimits(percentage(configPage2.iacCLminValue, idle_pwm_max_count<<2), percentage(configPage2.iacCLmaxValue, idle_pwm_max_count<<2)); idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD); @@ -157,11 +157,11 @@ void initialiseIdle(bool forcehoming) iacCrankDutyTable.axisX = configPage6.iacCrankBins; #if defined(CORE_AVR) - idle_pwm_max_count = 1000000L / (16 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (16U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY) - idle_pwm_max_count = 1000000L / (32 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 32uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (32U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 32uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #elif defined(CORE_TEENSY41) - idle_pwm_max_count = 1000000L / (2 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz + idle_pwm_max_count = (uint16_t)(MICROS_PER_SEC / (2U * configPage6.idleFreq * 2U)); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz #endif idlePID.SetOutputLimits(percentage(configPage2.iacCLminValue, idle_pwm_max_count<<2), percentage(configPage2.iacCLmaxValue, idle_pwm_max_count<<2)); idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD); @@ -573,7 +573,7 @@ void idleControl(void) // Now assign the real PWM value idle_pwm_target_value = TEMP_idle_pwm_target_value>>2; //increased resolution - currentStatus.idleLoad = ((unsigned long)(idle_pwm_target_value * 100UL) / idle_pwm_max_count); + currentStatus.idleLoad = udiv_32_16(idle_pwm_target_value * 100UL, idle_pwm_max_count); } idleCounter++; } diff --git a/speeduino/idle.h b/speeduino/idle.h index 213aa2aa..54a1da30 100644 --- a/speeduino/idle.h +++ b/speeduino/idle.h @@ -36,7 +36,7 @@ struct StepperIdle byte moreAirDirection; }; -extern unsigned int idle_pwm_max_count; //Used for variable PWM frequency +extern uint16_t idle_pwm_max_count; //Used for variable PWM frequency extern long FeedForwardTerm; void initialiseIdle(bool forcehoming); diff --git a/speeduino/maths.cpp b/speeduino/maths.cpp index a57d77e6..3d446bd7 100644 --- a/speeduino/maths.cpp +++ b/speeduino/maths.cpp @@ -1,72 +1,18 @@ +#include #include "maths.h" -#include - -#ifdef USE_LIBDIVIDE -#include "src/libdivide/constant_fast_div.h" - -// Constants used for libdivide. Using predefined constants saves flash and RAM (.bss) -// versus calling the libdivide generator functions (E.g. libdivide_s32_gen) -const libdivide::libdivide_s16_t libdiv_s16_100 = { .magic = S16_MAGIC(100), .more = S16_MORE(100) }; -const libdivide::libdivide_u16_t libdiv_u16_100 = { .magic = U16_MAGIC(100), .more = U16_MORE(100) }; -// 32-bit constants generated here: https://godbolt.org/z/vP8Kfejo9 -const libdivide::libdivide_u32_t libdiv_u32_100 = { .magic = 2748779070, .more = 6 }; -const libdivide::libdivide_s32_t libdiv_s32_100 = { .magic = 1374389535, .more = 5 }; -const libdivide::libdivide_u32_t libdiv_u32_200 = { .magic = 2748779070, .more = 7 }; -const libdivide::libdivide_u32_t libdiv_u32_360 = { .magic = 1813430637, .more = 72 }; -#endif - -//Replace the standard arduino map() function to use the div function instead -int fastMap(unsigned long x, int in_min, int in_max, int out_min, int out_max) -{ - unsigned long a = (x - (unsigned long)in_min); - int b = (out_max - out_min); - int c = (in_max - in_min); - int d = (ldiv( (a * (long)b) , (long)c ).quot); - return d + out_min; - //return ldiv( ((x - in_min) * (out_max - out_min)) , (in_max - in_min) ).quot + out_min; - //return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - -//Return x percent of y -//This is a relatively fast approximation of a percentage value. -unsigned long percentage(uint8_t x, unsigned long y) -{ - return div100(y * x); -} - -//Same as above, but 0.5% accuracy -unsigned long halfPercentage(uint8_t x, unsigned long y) -{ -#ifdef USE_LIBDIVIDE - return libdivide::libdivide_u32_do(y * x, &libdiv_u32_200); -#else - return (y * x) / 200; -#endif -} - -/* - * Calculates integer power values. Same as pow() but with ints - */ -inline long powint(int factor, unsigned int exponent) -{ - long product = 1; - unsigned int counter = exponent; - while ( (counter--) > 0) { product *= factor; } - return product; -} //Generates a random number from 1 to 100 (inclusive). //The initial seed used is always based on micros(), though this is unlikely to cause an issue as the first run is nearly random itself //Function requires 4 bytes to store state and seed, but operates very quickly (around 4uS per call) -uint8_t a, x, y, z; +static uint8_t a, x, y, z; uint8_t random1to100() { //Check if this is the first time being run. If so, seed the random number generator with micros() - if( (a == 0) && (x == 0) && (y == 0) && (z == 0) ) + if( (a == 0U) && (x == 0U) && (y == 0U) && (z == 0U) ) { - x = micros() >> 24; - y = micros() >> 16; - z = micros() >> 8; + x = micros() >> 24U; + y = micros() >> 16U; + z = micros() >> 8U; a = micros(); } @@ -78,6 +24,6 @@ uint8_t random1to100() z=a; a = z ^ t ^ ( z >> 1) ^ (t << 1); } - while(a >= 100); - return (a+1); + while(a >= 100U); + return (a+1U); } diff --git a/speeduino/maths.h b/speeduino/maths.h index ba0fd32c..65ea7d53 100644 --- a/speeduino/maths.h +++ b/speeduino/maths.h @@ -1,82 +1,307 @@ #ifndef MATH_H #define MATH_H +#include #include "globals.h" -unsigned long percentage(uint8_t x, unsigned long y); -unsigned long halfPercentage(uint8_t x, unsigned long y); -inline long powint(int factor, unsigned int exponent); -uint8_t random1to100(); - #ifdef USE_LIBDIVIDE +// We use pre-computed constant parameters with libdivide where possible. +// Using predefined constants saves flash and RAM (.bss) versus calling the +// libdivide generator functions (E.g. libdivide_s32_gen) +// 32-bit constants generated here: https://godbolt.org/z/vP8Kfejo9 #include "src/libdivide/libdivide.h" -extern const struct libdivide::libdivide_u16_t libdiv_u16_100; -extern const struct libdivide::libdivide_s16_t libdiv_s16_100; -extern const struct libdivide::libdivide_u32_t libdiv_u32_100; -extern const struct libdivide::libdivide_s32_t libdiv_s32_100; -extern const struct libdivide::libdivide_u32_t libdiv_u32_360; +#include "src/libdivide/constant_fast_div.h" #endif -inline uint8_t div100(uint8_t n) { - return n / (uint8_t)100U; -} -inline int8_t div100(int8_t n) { - return n / (int8_t)100U; -} -inline uint16_t div100(uint16_t n) { -#ifdef USE_LIBDIVIDE - return libdivide::libdivide_u16_do(n, &libdiv_u16_100); -#else - return n / (uint16_t)100U; -#endif -} -inline int16_t div100(int16_t n) { -#ifdef USE_LIBDIVIDE - return libdivide::libdivide_s16_do(n, &libdiv_s16_100); -#else - return n / (int16_t)100; -#endif -} -inline uint32_t div100(uint32_t n) { -#ifdef USE_LIBDIVIDE - return libdivide::libdivide_u32_do(n, &libdiv_u32_100); -#else - return n / (uint32_t)100U; -#endif -} -#if defined(__arm__) -inline int div100(int n) { -#ifdef USE_LIBDIVIDE - return libdivide::libdivide_s32_do(n, &libdiv_s32_100); -#else - return n / (int)100; -#endif -} -#else -inline int32_t div100(int32_t n) { -#ifdef USE_LIBDIVIDE - return libdivide::libdivide_s32_do(n, &libdiv_s32_100); -#else - return n / (int32_t)100; -#endif -} -#endif +extern uint8_t random1to100(void); -inline uint32_t div360(uint32_t n) { +/** + * @defgroup group-rounded-div Rounding integer division + * + * @brief Integer division returns the quotient. I.e. rounds to zero. This + * code will round the result to nearest integer. Rounding behavior is + * controlled by #DIV_ROUND_BEHAVIOR + * + * @{ + */ + +/** + * @defgroup group-rounded-div-behavior Rounding behavior + * @{ + */ + + +/** @brief Rounding behavior: always round down */ +#define DIV_ROUND_DOWN -1 + +/** @brief Rounding behavior: always round up */ +#define DIV_ROUND_UP 1 + +/** @brief Rounding behavior: round to nearest + * + * This rounds 0.5 away from zero. This is the same behavior + * as the standard library round() function. + */ +#define DIV_ROUND_NEAREST 0 + +/** @brief Integer division rounding behavior. */ +#define DIV_ROUND_BEHAVIOR DIV_ROUND_NEAREST +// (Unit tests expect DIV_ROUND_NEAREST behavior) + +/** + * @brief Computes the denominator correction for rounding division + * based on our rounding behavior. + * + * @param d The divisor (an integer) + * @param t The type of the result. E.g. uint16_t + */ +#define DIV_ROUND_CORRECT(d, t) ((t)(((d)>>1U)+(t)DIV_ROUND_BEHAVIOR)) +///@} + +/** + * @brief Rounded integer division + * + * Integer division returns the quotient. I.e. rounds to zero. This + * macro will round the result to nearest integer. Rounding behavior + * is controlled by #DIV_ROUND_BEHAVIOR + * + * @warning For performance reasons, this macro does not promote integers. + * So it will overflow if n>MAX(t)-(d/2). + * + * @param n The numerator (dividee) (an integer) + * @param d The denominator (divider) (an integer) + * @param t The type of the result. E.g. uint16_t + */ +#define DIV_ROUND_CLOSEST(n, d, t) ( \ + (((n) < (t)(0)) ^ ((d) < (t)(0))) ? \ + ((t)((n) - DIV_ROUND_CORRECT(d, t))/(t)(d)) : \ + ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d))) + +/** + * @brief Rounded \em unsigned integer division + * + * This is slighty faster than the signed version (DIV_ROUND_CLOSEST(n, d, t)) + * + * @warning For performance reasons, this macro does not promote integers. + * So it will overflow if n>MAX(t)-(d/2). + * + * @param n The numerator (dividee) (an \em unsigned integer) + * @param d The denominator (divider) (an \em unsigned integer) + * @param t The type of the result. E.g. uint16_t + */ +#define UDIV_ROUND_CLOSEST(n, d, t) ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d)) + +/** + * @brief Rounded arithmetic right shift + * + * Right shifting throws away bits. When use for fixed point division, this + * effecitvely rounds down (towards zero). To round-to-the-nearest-integer + * when right-shifting by S, just add in 2 power S−1 (which is the + * fixed-point equivalent of 0.5) first + * + * @param n The value to shift + * @param s The shift distance in bits + */ +#define RSHIFT_ROUND(n, s) (((n)+(1UL<<(s-1UL)))>>(s)) +///@} + +/** @brief Test whether the parameter is an integer or not. */ +#define IS_INTEGER(d) ((d) == (int32_t)(d)) + +/** + * @defgroup group-div100 Optimised integer divison by 100 + * @{ + */ +static inline uint16_t div100(uint16_t n) { + // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift + // (unlike the signed integer overload, where __divmodhi4 is still called + // see https://godbolt.org/z/c5bs5noT1) + return UDIV_ROUND_CLOSEST(n, UINT16_C(100), uint16_t); +} + +static inline int16_t div100(int16_t n) { #ifdef USE_LIBDIVIDE - return libdivide::libdivide_u32_do(n, &libdiv_u32_360); + // Try faster unsigned path first + if (n>0) { + return div100((uint16_t)n); + } + // Negative values here, so adjust pre-division to get same + // behavior as roundf(float) + return libdivide::libdivide_s16_do_raw(n - DIV_ROUND_CORRECT(UINT16_C(100), uint16_t), S16_MAGIC(100), S16_MORE(100)); #else - return n / 360U; + return DIV_ROUND_CLOSEST(n, UINT16_C(100), int16_t); #endif } -#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d))) -#define IS_INTEGER(d) (d == (int32_t)d) +static inline uint32_t div100(uint32_t n) { +#ifdef USE_LIBDIVIDE + if (n<=(uint32_t)UINT16_MAX) { + return div100((uint16_t)n); + } + return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(100), uint32_t), 2748779070L, 6); +#else + return UDIV_ROUND_CLOSEST(n, UINT32_C(100), uint32_t); +#endif +} + +#if defined(__arm__) +static inline int div100(int n) { + return DIV_ROUND_CLOSEST(n, 100U, int); +} +#else +static inline int32_t div100(int32_t n) { +#ifdef USE_LIBDIVIDE + if (n<=INT16_MAX && n>=INT16_MIN) { + return div100((int16_t)n); + } + return libdivide::libdivide_s32_do_raw(n + (DIV_ROUND_CORRECT(UINT16_C(100), uint32_t) * (n<0 ? -1 : 1)), 1374389535L, 5); +#else + return DIV_ROUND_CLOSEST(n, UINT32_C(100), int32_t); +#endif +} +#endif +///@} + +/** + * @brief Optimised integer division by 360 + * + * @param n The numerator (dividee) (an integer) + * @return uint32_t + */ +static inline uint32_t div360(uint32_t n) { +#ifdef USE_LIBDIVIDE + return libdivide::libdivide_u32_do_raw(n + DIV_ROUND_CORRECT(UINT32_C(360), uint32_t), 1813430637L, 72); +#else + return (uint32_t)UDIV_ROUND_CLOSEST(n, UINT32_C(360), uint32_t); +#endif +} + +/** + * @brief Integer based percentage calculation. + * + * @param percent The percent to calculate ([0, 100]) + * @param value The value to operate on + * @return uint32_t + */ +static inline uint16_t percentage(uint8_t percent, uint16_t value) { + return (uint16_t)div100((uint32_t)value * (uint32_t)percent); +} + +/** + * @brief Integer based half-percentage calculation. + * + * @param percent The percent to calculate ([0, 100]) + * @param value The value to operate on + * @return uint16_t + */ +static inline uint16_t halfPercentage(uint8_t percent, uint16_t value) { + uint32_t x200 = (uint32_t)percent * (uint32_t)value; +#ifdef USE_LIBDIVIDE + return (uint16_t)libdivide::libdivide_u32_do_raw(x200 + DIV_ROUND_CORRECT(UINT32_C(200), uint32_t), 2748779070L, 7); +#else + return (uint16_t)UDIV_ROUND_CLOSEST(x200, UINT16_C(200), uint32_t); +#endif +} + +/** + * @brief Make one pass at correcting the value into the range [min, max) + * + * @param min Minimum value (inclusive) + * @param max Maximum value (exclusive) + * @param value Value to nudge + * @param nudgeAmount Amount to change value by + * @return int16_t + */ +static inline int16_t nudge(int16_t min, int16_t max, int16_t value, int16_t nudgeAmount) +{ + if (valuemax) { return value - nudgeAmount; } + return value; +} //This is a dedicated function that specifically handles the case of mapping 0-1023 values into a 0 to X range //This is a common case because it means converting from a standard 10-bit analog input to a byte or 10-bit analog into 0-511 (Eg the temperature readings) -#define fastMap1023toX(x, out_max) ( ((unsigned long)x * out_max) >> 10) +#define fastMap1023toX(x, out_max) ( ((uint32_t)(x) * (out_max)) >> 10) //This is a new version that allows for out_min -#define fastMap10Bit(x, out_min, out_max) ( ( ((unsigned long)x * (out_max-out_min)) >> 10 ) + out_min) +#define fastMap10Bit(x, out_min, out_max) ( ( ((uint32_t)(x) * ((out_max)-(out_min))) >> 10 ) + (out_min)) + +#if defined(CORE_AVR) || defined(ARDUINO_ARCH_AVR) + +static inline bool udiv_is16bit_result(uint32_t dividend, uint16_t divisor) { + return divisor>(uint16_t)(dividend>>16U); +} + +#endif +/** + * @brief Optimised division: uint32_t/uint16_t => uint16_t + * + * Optimised division of unsigned 32-bit by unsigned 16-bit when it is known + * that the result fits into unsigned 16-bit. + * + * ~60% quicker than raw 32/32 => 32 division on ATMega + * + * @note Bad things will likely happen if the result doesn't fit into 16-bits. + * @note Copied from https://stackoverflow.com/a/66593564 + * + * @param dividend The dividend (numerator) + * @param divisor The divisor (denominator) + * @return uint16_t + */ +static inline uint16_t udiv_32_16 (uint32_t dividend, uint16_t divisor) +{ +#if defined(CORE_AVR) || defined(ARDUINO_ARCH_AVR) + + if (divisor==0U || !udiv_is16bit_result(dividend, divisor)) { return UINT16_MAX; } + + #define INDEX_REG "r16" + + asm( + " ldi " INDEX_REG ", 16 ; bits = 16\n\t" + "0:\n\t" + " lsl %A0 ; shift\n\t" + " rol %B0 ; rem:quot\n\t" + " rol %C0 ; left\n\t" + " rol %D0 ; by 1\n\t" + " brcs 1f ; if carry out, rem > divisor\n\t" + " cp %C0, %A1 ; is rem less\n\t" + " cpc %D0, %B1 ; than divisor ?\n\t" + " brcs 2f ; yes, when carry out\n\t" + "1:\n\t" + " sub %C0, %A1 ; compute\n\t" + " sbc %D0, %B1 ; rem -= divisor\n\t" + " ori %A0, 1 ; record quotient bit as 1\n\t" + "2:\n\t" + " dec " INDEX_REG " ; bits--\n\t" + " brne 0b ; until bits == 0" + : "=d" (dividend) + : "d" (divisor) , "0" (dividend) + : INDEX_REG + ); + + // Lower word contains the quotient, upper word contains the remainder. + return dividend & 0xFFFFU; +#else + // The non-AVR platforms are all fast enough (or have built in hardware dividers) + // so just fall back to regular 32-bit division. + return dividend / divisor; +#endif +} + + +/** + * @brief Same as udiv_32_16(), except this will round to nearest integer + * instead of truncating. + * + * Minor performance drop compared to non-rounding version. + **/ +static inline uint16_t udiv_32_16_closest(uint32_t dividend, uint16_t divisor) +{ +#if defined(CORE_AVR) || defined(ARDUINO_ARCH_AVR) + dividend = dividend + (uint32_t)(DIV_ROUND_CORRECT(divisor, uint16_t)); + return udiv_32_16(dividend, divisor); +#else + return (uint16_t)UDIV_ROUND_CLOSEST(dividend, (uint32_t)divisor, uint32_t); +#endif +} #endif \ No newline at end of file diff --git a/speeduino/schedule_calcs.h b/speeduino/schedule_calcs.h index a60f6887..fe854533 100644 --- a/speeduino/schedule_calcs.h +++ b/speeduino/schedule_calcs.h @@ -57,15 +57,15 @@ extern int channel7InjDegrees; /**< The number of crank degrees until cylinder 7 extern int channel8InjDegrees; /**< The number of crank degrees until cylinder 8 is at TDC */ #endif -inline uint16_t __attribute__((always_inline)) calculateInjectorStartAngle(uint16_t PWdivTimerPerDegree, int16_t injChannelDegrees, uint16_t injAngle); +static inline uint16_t __attribute__((always_inline)) calculateInjectorStartAngle(uint16_t PWdivTimerPerDegree, int16_t injChannelDegrees, uint16_t injAngle); -inline uint32_t __attribute__((always_inline)) calculateInjectorTimeout(const FuelSchedule &schedule, int channelInjDegrees, int injectorStartAngle, int crankAngle); +static inline uint32_t __attribute__((always_inline)) calculateInjectorTimeout(const FuelSchedule &schedule, int channelInjDegrees, int injectorStartAngle, int crankAngle); -inline void __attribute__((always_inline)) calculateIgnitionAngle(const int dwellAngle, const uint16_t channelIgnDegrees, int8_t advance, int *pEndAngle, int *pStartAngle); +static inline void __attribute__((always_inline)) calculateIgnitionAngle(const int dwellAngle, const uint16_t channelIgnDegrees, int8_t advance, int *pEndAngle, int *pStartAngle); // Ignition for rotary. -inline void __attribute__((always_inline)) calculateIgnitionTrailingRotary(int dwellAngle, int rotarySplitDegrees, int leadIgnitionAngle, int *pEndAngle, int *pStartAngle); +static inline void __attribute__((always_inline)) calculateIgnitionTrailingRotary(int dwellAngle, int rotarySplitDegrees, int leadIgnitionAngle, int *pEndAngle, int *pStartAngle); -inline uint32_t __attribute__((always_inline)) calculateIgnitionTimeout(const IgnitionSchedule &schedule, int startAngle, int channelIgnDegrees, int crankAngle); +static inline uint32_t __attribute__((always_inline)) calculateIgnitionTimeout(const IgnitionSchedule &schedule, int startAngle, int channelIgnDegrees, int crankAngle); #include "schedule_calcs.hpp" diff --git a/speeduino/schedule_calcs.hpp b/speeduino/schedule_calcs.hpp index 4b58134e..82347f99 100644 --- a/speeduino/schedule_calcs.hpp +++ b/speeduino/schedule_calcs.hpp @@ -4,7 +4,7 @@ #include "scheduler.h" #include "crankMaths.h" -inline uint16_t calculateInjectorStartAngle(uint16_t pwDegrees, int16_t injChannelDegrees, uint16_t injAngle) +static inline uint16_t calculateInjectorStartAngle(uint16_t pwDegrees, int16_t injChannelDegrees, uint16_t injAngle) { // 0<=injAngle<=720° // 0<=injChannelDegrees<=720° @@ -12,18 +12,18 @@ inline uint16_t calculateInjectorStartAngle(uint16_t pwDegrees, int16_t injChann // 45<=CRANK_ANGLE_MAX_INJ<=720 // (CRANK_ANGLE_MAX_INJ can be as small as 360/nCylinders. E.g. 45° for 8 cylinder) - uint16_t startAngle = injAngle + injChannelDegrees; + uint16_t startAngle = (uint16_t)injAngle + (uint16_t)injChannelDegrees; // Avoid underflow - while (startAngle=0. startAngle = startAngle - pwDegrees; // Clamp to 0<=startAngle<=CRANK_ANGLE_MAX_INJ - while (startAngle>(uint16_t)CRANK_ANGLE_MAX_INJ) { startAngle = startAngle - CRANK_ANGLE_MAX_INJ; } + while (startAngle>(uint16_t)CRANK_ANGLE_MAX_INJ) { startAngle = startAngle - (uint16_t)CRANK_ANGLE_MAX_INJ; } return startAngle; } -inline uint32_t _calculateInjectorTimeout(const FuelSchedule &schedule, uint16_t openAngle, uint16_t crankAngle) { +static inline uint32_t _calculateInjectorTimeout(const FuelSchedule &schedule, uint16_t openAngle, uint16_t crankAngle) { int16_t delta = openAngle - crankAngle; if (delta<0) { @@ -38,7 +38,7 @@ inline uint32_t _calculateInjectorTimeout(const FuelSchedule &schedule, uint16_t } } - return ((uint32_t)(delta) * (uint32_t)timePerDegree); + return angleToTimeMicroSecPerDegree((uint16_t)delta); } static inline int _adjustToInjChannel(int angle, int channelInjDegrees) { @@ -47,7 +47,7 @@ static inline int _adjustToInjChannel(int angle, int channelInjDegrees) { return angle; } -inline uint32_t calculateInjectorTimeout(const FuelSchedule &schedule, int channelInjDegrees, int openAngle, int crankAngle) +static inline uint32_t calculateInjectorTimeout(const FuelSchedule &schedule, int channelInjDegrees, int openAngle, int crankAngle) { if (channelInjDegrees==0) { return _calculateInjectorTimeout(schedule, openAngle, crankAngle); @@ -55,15 +55,15 @@ inline uint32_t calculateInjectorTimeout(const FuelSchedule &schedule, int chann return _calculateInjectorTimeout(schedule, _adjustToInjChannel(openAngle, channelInjDegrees), _adjustToInjChannel(crankAngle, channelInjDegrees)); } -inline void calculateIgnitionAngle(const int dwellAngle, const uint16_t channelIgnDegrees, int8_t advance, int *pEndAngle, int *pStartAngle) +static inline void calculateIgnitionAngle(const int dwellAngle, const uint16_t channelIgnDegrees, int8_t advance, int *pEndAngle, int *pStartAngle) { - *pEndAngle = (channelIgnDegrees==0 ? CRANK_ANGLE_MAX_IGN : channelIgnDegrees) - advance; + *pEndAngle = (int16_t)(channelIgnDegrees==0U ? (uint16_t)CRANK_ANGLE_MAX_IGN : channelIgnDegrees) - (int16_t)advance; if(*pEndAngle > CRANK_ANGLE_MAX_IGN) {*pEndAngle -= CRANK_ANGLE_MAX_IGN;} *pStartAngle = *pEndAngle - dwellAngle; if(*pStartAngle < 0) {*pStartAngle += CRANK_ANGLE_MAX_IGN;} } -inline void calculateIgnitionTrailingRotary(int dwellAngle, int rotarySplitDegrees, int leadIgnitionAngle, int *pEndAngle, int *pStartAngle) +static inline void calculateIgnitionTrailingRotary(int dwellAngle, int rotarySplitDegrees, int leadIgnitionAngle, int *pEndAngle, int *pStartAngle) { *pEndAngle = leadIgnitionAngle + rotarySplitDegrees; *pStartAngle = *pEndAngle - dwellAngle; @@ -71,7 +71,7 @@ inline void calculateIgnitionTrailingRotary(int dwellAngle, int rotarySplitDegre if(*pStartAngle < 0) {*pStartAngle += CRANK_ANGLE_MAX_IGN;} } -inline uint32_t _calculateIgnitionTimeout(const IgnitionSchedule &schedule, int16_t startAngle, int16_t crankAngle) { +static inline uint32_t _calculateIgnitionTimeout(const IgnitionSchedule &schedule, int16_t startAngle, int16_t crankAngle) { int16_t delta = startAngle - crankAngle; if (delta<0) { @@ -85,7 +85,7 @@ inline uint32_t _calculateIgnitionTimeout(const IgnitionSchedule &schedule, int1 return 0; } } - return angleToTimeIntervalRev(delta); + return angleToTimeMicroSecPerDegree(delta); } static inline uint16_t _adjustToIgnChannel(int angle, int channelInjDegrees) { @@ -94,7 +94,7 @@ static inline uint16_t _adjustToIgnChannel(int angle, int channelInjDegrees) { return angle; } -inline uint32_t calculateIgnitionTimeout(const IgnitionSchedule &schedule, int startAngle, int channelIgnDegrees, int crankAngle) +static inline uint32_t calculateIgnitionTimeout(const IgnitionSchedule &schedule, int startAngle, int channelIgnDegrees, int crankAngle) { if (channelIgnDegrees==0) { return _calculateIgnitionTimeout(schedule, startAngle, crankAngle); diff --git a/speeduino/secondaryTables.cpp b/speeduino/secondaryTables.cpp index 62999bd7..ba4e4ed5 100644 --- a/speeduino/secondaryTables.cpp +++ b/speeduino/secondaryTables.cpp @@ -181,7 +181,7 @@ byte getVE2(void) else if (configPage10.fuel2Algorithm == LOAD_SOURCE_IMAPEMAP) { //IMAP / EMAP - currentStatus.fuelLoad2 = (currentStatus.MAP * 100) / currentStatus.EMAP; + currentStatus.fuelLoad2 = ((int16_t)currentStatus.MAP * 100U) / currentStatus.EMAP; } else { currentStatus.fuelLoad2 = currentStatus.MAP; } //Fallback position tempVE = get3DTableValue(&fuelTable2, currentStatus.fuelLoad2, currentStatus.RPM); //Perform lookup into fuel map for RPM vs MAP value diff --git a/speeduino/sensors.cpp b/speeduino/sensors.cpp index 5dabbb65..e85651be 100644 --- a/speeduino/sensors.cpp +++ b/speeduino/sensors.cpp @@ -288,14 +288,14 @@ void readMAP(void) MAPlast_time = MAP_time; MAP_time = micros(); - currentStatus.mapADC = ldiv(MAPrunningValue, MAPcount).quot; + currentStatus.mapADC = udiv_32_16(MAPrunningValue, MAPcount); currentStatus.MAP = fastMap10Bit(currentStatus.mapADC, configPage2.mapMin, configPage2.mapMax); //Get the current MAP value validateMAP(); //If EMAP is enabled, the process is identical to the above if(configPage6.useEMAP == true) { - currentStatus.EMAPADC = ldiv(EMAPrunningValue, MAPcount).quot; //Note that the MAP count can be reused here as it will always be the same count. + currentStatus.EMAPADC = udiv_32_16(EMAPrunningValue, MAPcount); //Note that the MAP count can be reused here as it will always be the same count. currentStatus.EMAP = fastMap10Bit(currentStatus.EMAPADC, configPage2.EMAPMin, configPage2.EMAPMax); if(currentStatus.EMAP < 0) { currentStatus.EMAP = 0; } //Sanity check } @@ -396,7 +396,7 @@ void readMAP(void) MAPlast_time = MAP_time; MAP_time = micros(); - currentStatus.mapADC = ldiv(MAPrunningValue, MAPcount).quot; + currentStatus.mapADC = udiv_32_16(MAPrunningValue, MAPcount); currentStatus.MAP = fastMap10Bit(currentStatus.mapADC, configPage2.mapMin, configPage2.mapMax); //Get the current MAP value validateMAP(); } @@ -532,7 +532,7 @@ void readBaro(void) //Verify the engine isn't running by confirming RPM is 0 and it has been at least 1 second since the last tooth was detected unsigned long timeToLastTooth = (micros() - toothLastToothTime); - if((currentStatus.RPM == 0) && (timeToLastTooth > 1000000UL)) + if((currentStatus.RPM == 0) && (timeToLastTooth > MICROS_PER_SEC)) { instanteneousMAPReading(); //Get the current MAP value /* @@ -680,10 +680,10 @@ uint16_t getSpeed(void) } pulseTime = vssTotalTime / (VSS_SAMPLES - 1); - if ( (micros() - vssTimes[vssIndex]) > 1000000UL ) { tempSpeed = 0; } // Check that the car hasn't come to a stop. Is true if last pulse was more than 1 second ago + if ( (micros() - vssTimes[vssIndex]) > MICROS_PER_SEC ) { tempSpeed = 0; } // Check that the car hasn't come to a stop. Is true if last pulse was more than 1 second ago else { - tempSpeed = 3600000000UL / (pulseTime * configPage2.vssPulsesPerKm); //Convert the pulse gap into km/h + tempSpeed = MICROS_PER_HOUR / (pulseTime * configPage2.vssPulsesPerKm); //Convert the pulse gap into km/h tempSpeed = ADC_FILTER(tempSpeed, configPage2.vssSmoothing, currentStatus.vss); //Apply speed smoothing factor } if(tempSpeed > 1000) { tempSpeed = currentStatus.vss; } //Safety check. This usually occurs when there is a hardware issue @@ -700,7 +700,7 @@ byte getGear(void) //If the speed is non-zero, default to the last calculated gear tempGear = currentStatus.gear; - uint16_t pulsesPer1000rpm = (currentStatus.vss * 10000UL) / currentStatus.RPM; //Gives the current pulses per 1000RPM, multiplied by 10 (10x is the multiplication factor for the ratios in TS) + uint16_t pulsesPer1000rpm = udiv_32_16(currentStatus.vss * 10000UL, currentStatus.RPM); //Gives the current pulses per 1000RPM, multiplied by 10 (10x is the multiplication factor for the ratios in TS) //Begin gear detection if( (pulsesPer1000rpm > (configPage2.vssRatio1 - VSS_GEAR_HYSTERESIS)) && (pulsesPer1000rpm < (configPage2.vssRatio1 + VSS_GEAR_HYSTERESIS)) ) { tempGear = 1; } else if( (pulsesPer1000rpm > (configPage2.vssRatio2 - VSS_GEAR_HYSTERESIS)) && (pulsesPer1000rpm < (configPage2.vssRatio2 + VSS_GEAR_HYSTERESIS)) ) { tempGear = 2; } @@ -784,7 +784,7 @@ void knockPulse(void) //Check if this the start of a knock. if(knockCounter == 0) { - //knockAngle = crankAngle + fastTimeToAngle( (micros() - lastCrankAngleCalc) ); + //knockAngle = crankAngle + timeToAngleDegPerMicroSec( (micros() - lastCrankAngleCalc) ); knockStartTime = micros(); knockCounter = 1; } diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index f537b8a7..0f41428d 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -68,9 +68,8 @@ void setup(void) inline uint16_t applyFuelTrimToPW(trimTable3d *pTrimTable, int16_t fuelLoad, int16_t RPM, uint16_t currentPW) { - unsigned long pw1percent = 100 + get3DTableValue(pTrimTable, fuelLoad, RPM) - OFFSET_FUELTRIM; - if (pw1percent != 100) { return div100(uint32_t(pw1percent * currentPW)); } - return currentPW; + uint8_t pw1percent = 100U + get3DTableValue(pTrimTable, fuelLoad, RPM) - OFFSET_FUELTRIM; + return percentage(pw1percent, currentPW); } /** Speeduino main loop. @@ -138,7 +137,7 @@ void loop(void) } currentLoopTime = micros_safe(); - unsigned long timeToLastTooth = (currentLoopTime - toothLastToothTime); + uint32_t timeToLastTooth = (currentLoopTime - toothLastToothTime); if ( (timeToLastTooth < MAX_STALL_TIME) || (toothLastToothTime > currentLoopTime) ) //Check how long ago the last tooth was seen compared to now. If it was more than half a second ago then the engine is probably stopped. toothLastToothTime can be greater than currentLoopTime if a pulse occurs between getting the latest time and doing the comparison { currentStatus.longRPM = getRPM(); //Long RPM is included here @@ -460,13 +459,15 @@ void loop(void) uint16_t injector8StartAngle = 0; #endif - doCrankSpeedCalcs(); //In crankMaths.ino - //Check that the duty cycle of the chosen pulsewidth isn't too high. uint32_t pwLimit = percentage(configPage2.dutyLim, revolutionTime); //The pulsewidth limit is determined to be the duty cycle limit (Eg 85%) by the total time it takes to perform 1 revolution //Handle multiple squirts per rev - if (configPage2.strokes == FOUR_STROKE) { pwLimit = pwLimit * 2 / currentStatus.nSquirts; } - else { pwLimit = pwLimit / currentStatus.nSquirts; } + if (configPage2.strokes == FOUR_STROKE) { pwLimit = pwLimit * 2; } + // This requires 32-bit division, which is very slow on Mega 2560. + // So only divide if necessary - nSquirts is often only 1. + if (currentStatus.nSquirts!=1) { + pwLimit = pwLimit / currentStatus.nSquirts; + } //Apply the pwLimit if staging is disabled and engine is not cranking if( (!BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK)) && (configPage10.stagingEnabled == false) ) { if (currentStatus.PW1 > pwLimit) { currentStatus.PW1 = pwLimit; } } @@ -475,7 +476,7 @@ void loop(void) //*********************************************************************************************** //BEGIN INJECTION TIMING currentStatus.injAngle = table2D_getValue(&injectorAngleTable, currentStatus.RPMdiv100); - unsigned int PWdivTimerPerDegree = div(currentStatus.PW1, timePerDegree).quot; //How many crank degrees the calculated PW will take at the current speed + unsigned int PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW1); //How many crank degrees the calculated PW will take at the current speed injector1StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); @@ -487,7 +488,7 @@ void loop(void) //The only thing that needs to be done for single cylinder is to check for staging. if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW2, timePerDegree).quot; //Need to redo this for PW2 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW2); //Need to redo this for PW2 as it will be dramatically different to PW1 when staging //injector3StartAngle = calculateInjector3StartAngle(PWdivTimerPerDegree); injector2StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); } @@ -504,7 +505,7 @@ void loop(void) } else if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW3, timePerDegree).quot; //Need to redo this for PW3 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW3); //Need to redo this for PW3 as it will be dramatically different to PW1 when staging injector3StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector4StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); @@ -528,7 +529,7 @@ void loop(void) #if INJ_CHANNELS >= 6 if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW4, timePerDegree).quot; //Need to redo this for PW4 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW4); //Need to redo this for PW4 as it will be dramatically different to PW1 when staging injector4StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector5StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); injector6StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel3InjDegrees, currentStatus.injAngle); @@ -537,7 +538,7 @@ void loop(void) } else if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW4, timePerDegree).quot; //Need to redo this for PW3 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW4); //Need to redo this for PW3 as it will be dramatically different to PW1 when staging injector4StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); #if INJ_CHANNELS >= 6 injector5StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); @@ -559,7 +560,7 @@ void loop(void) #if INJ_CHANNELS >= 8 if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW5, timePerDegree).quot; //Need to redo this for PW5 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW5); //Need to redo this for PW5 as it will be dramatically different to PW1 when staging injector5StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector6StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); injector7StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel3InjDegrees, currentStatus.injAngle); @@ -577,7 +578,7 @@ void loop(void) } else if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW3, timePerDegree).quot; //Need to redo this for PW3 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW3); //Need to redo this for PW3 as it will be dramatically different to PW1 when staging injector3StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector4StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); } @@ -599,7 +600,7 @@ void loop(void) #if INJ_CHANNELS >= 6 if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW6, timePerDegree).quot; + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW6); injector6StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel6InjDegrees, currentStatus.injAngle); } #endif @@ -633,7 +634,7 @@ void loop(void) #if INJ_CHANNELS >= 8 if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW4, timePerDegree).quot; //Need to redo this for staging PW as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW4); //Need to redo this for staging PW as it will be dramatically different to PW1 when staging injector4StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector5StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); injector6StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel3InjDegrees, currentStatus.injAngle); @@ -646,7 +647,7 @@ void loop(void) if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW4, timePerDegree).quot; //Need to redo this for staging PW as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW4); //Need to redo this for staging PW as it will be dramatically different to PW1 when staging injector4StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector5StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); injector6StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel3InjDegrees, currentStatus.injAngle); @@ -688,7 +689,7 @@ void loop(void) if( (configPage10.stagingEnabled == true) && (BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE) == true) ) { - PWdivTimerPerDegree = div(currentStatus.PW5, timePerDegree).quot; //Need to redo this for PW3 as it will be dramatically different to PW1 when staging + PWdivTimerPerDegree = timeToAngleDegPerMicroSec(currentStatus.PW5); //Need to redo this for PW3 as it will be dramatically different to PW1 when staging injector5StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel1InjDegrees, currentStatus.injAngle); injector6StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel2InjDegrees, currentStatus.injAngle); injector7StartAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, channel3InjDegrees, currentStatus.injAngle); @@ -725,7 +726,7 @@ void loop(void) } currentStatus.dwell = correctionsDwell(currentStatus.dwell); - int dwellAngle = timeToAngle(currentStatus.dwell, CRANKMATH_METHOD_INTERVAL_REV); //Convert the dwell time to dwell angle based on the current engine speed + int dwellAngle = timeToAngleDegPerMicroSec(currentStatus.dwell); //Convert the dwell time to dwell angle based on the current engine speed calculateIgnitionAngles(dwellAngle); @@ -1061,8 +1062,8 @@ void loop(void) //ONLY ONE OF THE BELOW SHOULD BE USED (PROBABLY THE FIRST): //********* - if(ignition1EndAngle > crankAngle) { uSToEnd = fastDegreesToUS( (ignition1EndAngle - crankAngle) ); } - else { uSToEnd = fastDegreesToUS( (360 + ignition1EndAngle - crankAngle) ); } + if(ignition1EndAngle > crankAngle) { uSToEnd = angleToTimeMicroSecPerDegree( (ignition1EndAngle - crankAngle) ); } + else { uSToEnd = angleToTimeMicroSecPerDegree( (360 + ignition1EndAngle - crankAngle) ); } //********* //uSToEnd = ((ignition1EndAngle - crankAngle) * (toothLastToothTime - toothLastMinusOneToothTime)) / triggerToothAngle; //********* @@ -1222,18 +1223,18 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen) //iCorrections = divu100((corrections << bitShift)); - unsigned long intermediate = ((uint32_t)REQ_FUEL * (uint32_t)iVE) >> 7; //Need to use an intermediate value to avoid overflowing the long - if ( configPage2.multiplyMAP > 0 ) { intermediate = (intermediate * (unsigned long)iMAP) >> 7; } + uint32_t intermediate = ((uint32_t)REQ_FUEL * (uint32_t)iVE) >> 7; //Need to use an intermediate value to avoid overflowing the long + if ( configPage2.multiplyMAP > 0 ) { intermediate = (intermediate * (uint32_t)iMAP) >> 7; } if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) { //EGO type must be set to wideband and the AFR warmup time must've elapsed for this to be used - intermediate = (intermediate * (unsigned long)iAFR) >> 7; + intermediate = (intermediate * (uint32_t)iAFR) >> 7; } if ( (configPage2.incorporateAFR == true) && (configPage2.includeAFR == false) ) { - intermediate = (intermediate * (unsigned long)iAFR) >> 7; + intermediate = (intermediate * (uint32_t)iAFR) >> 7; } - intermediate = (intermediate * (unsigned long)iCorrections) >> bitShift; + intermediate = (intermediate * (uint32_t)iCorrections) >> bitShift; if (intermediate != 0) { //If intermediate is not 0, we need to add the opening time (0 typically indicates that one of the full fuel cuts is active) @@ -1244,7 +1245,7 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen) //AE Adds % of req_fuel if ( configPage2.aeApplyMode == AE_MODE_ADDER ) { - intermediate += ( ((unsigned long)REQ_FUEL) * (currentStatus.AEamount - 100) ) / 100; + intermediate += div100(((uint32_t)REQ_FUEL) * (currentStatus.AEamount - 100U)); } } @@ -1277,7 +1278,7 @@ byte getVE1(void) else if (configPage2.fuelAlgorithm == LOAD_SOURCE_IMAPEMAP) { //IMAP / EMAP - currentStatus.fuelLoad = (currentStatus.MAP * 100) / currentStatus.EMAP; + currentStatus.fuelLoad = ((int16_t)currentStatus.MAP * 100U) / currentStatus.EMAP; } else { currentStatus.fuelLoad = currentStatus.MAP; } //Fallback position tempVE = get3DTableValue(&fuelTable, currentStatus.fuelLoad, currentStatus.RPM); //Perform lookup into fuel map for RPM vs MAP value @@ -1307,7 +1308,7 @@ byte getAdvance1(void) else if (configPage2.fuelAlgorithm == LOAD_SOURCE_IMAPEMAP) { //IMAP / EMAP - currentStatus.ignLoad = (currentStatus.MAP * 100) / currentStatus.EMAP; + currentStatus.ignLoad = ((int16_t)currentStatus.MAP * 100U) / currentStatus.EMAP; } tempAdvance = get3DTableValue(&ignitionTable, currentStatus.ignLoad, currentStatus.RPM) - OFFSET_IGNITION; //As above, but for ignition advance tempAdvance = correctionsIgn(tempAdvance); @@ -1438,21 +1439,21 @@ void calculateStaging(uint32_t pwLimit) { //Scale the 'full' pulsewidth by each of the injector capacities currentStatus.PW1 -= inj_opentime_uS; //Subtract the opening time from PW1 as it needs to be multiplied out again by the pri/sec req_fuel values below. It is added on again after that calculation. - uint32_t tempPW1 = (((unsigned long)currentStatus.PW1 * staged_req_fuel_mult_pri) / 100); + uint32_t tempPW1 = div100((uint32_t)currentStatus.PW1 * staged_req_fuel_mult_pri); if(configPage10.stagingMode == STAGING_MODE_TABLE) { - uint32_t tempPW3 = (((unsigned long)currentStatus.PW1 * staged_req_fuel_mult_sec) / 100); //This is ONLY needed in in table mode. Auto mode only calculates the difference. + uint32_t tempPW3 = div100((uint32_t)currentStatus.PW1 * staged_req_fuel_mult_sec); //This is ONLY needed in in table mode. Auto mode only calculates the difference. - byte stagingSplit = get3DTableValue(&stagingTable, currentStatus.fuelLoad, currentStatus.RPM); - currentStatus.PW1 = ((100 - stagingSplit) * tempPW1) / 100; + uint8_t stagingSplit = get3DTableValue(&stagingTable, currentStatus.fuelLoad, currentStatus.RPM); + currentStatus.PW1 = div100((100U - stagingSplit) * tempPW1); currentStatus.PW1 += inj_opentime_uS; //PW2 is used temporarily to hold the secondary injector pulsewidth. It will be assigned to the correct channel below if(stagingSplit > 0) { BIT_SET(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE); //Set the staging active flag - currentStatus.PW2 = (stagingSplit * tempPW3) / 100; + currentStatus.PW2 = div100(stagingSplit * tempPW3); currentStatus.PW2 += inj_opentime_uS; } else @@ -1471,7 +1472,7 @@ void calculateStaging(uint32_t pwLimit) BIT_SET(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE); //Set the staging active flag uint32_t extraPW = tempPW1 - pwLimit + inj_opentime_uS; //The open time must be added here AND below because tempPW1 does not include an open time. The addition of it here takes into account the fact that pwLlimit does not contain an allowance for an open time. currentStatus.PW1 = pwLimit; - currentStatus.PW2 = ((extraPW * staged_req_fuel_mult_sec) / staged_req_fuel_mult_pri); //Convert the 'left over' fuel amount from primary injector scaling to secondary + currentStatus.PW2 = udiv_32_16(extraPW * staged_req_fuel_mult_sec, staged_req_fuel_mult_pri); //Convert the 'left over' fuel amount from primary injector scaling to secondary currentStatus.PW2 += inj_opentime_uS; } else diff --git a/speeduino/src/libdivide/libdivide.h b/speeduino/src/libdivide/libdivide.h index 1f245b82..98e32b68 100644 --- a/speeduino/src/libdivide/libdivide.h +++ b/speeduino/src/libdivide/libdivide.h @@ -243,8 +243,12 @@ static LIBDIVIDE_INLINE uint16_t libdivide_u16_do_raw( uint16_t numer, uint16_t magic, uint8_t more); static LIBDIVIDE_INLINE uint16_t libdivide_u16_do( uint16_t numer, const struct libdivide_u16_t* denom); +static LIBDIVIDE_INLINE uint32_t libdivide_s32_do_raw( + int32_t numer, int32_t magic, uint8_t more); static LIBDIVIDE_INLINE int32_t libdivide_s32_do( int32_t numer, const struct libdivide_s32_t *denom); +static LIBDIVIDE_INLINE uint32_t libdivide_u32_do_raw( + uint32_t numer, uint32_t magic, uint8_t more); static LIBDIVIDE_INLINE uint32_t libdivide_u32_do( uint32_t numer, const struct libdivide_u32_t *denom); static LIBDIVIDE_INLINE int64_t libdivide_s64_do( @@ -912,12 +916,11 @@ struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) { return ret; } -uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) { - uint8_t more = denom->more; - if (!denom->magic) { +uint32_t libdivide_u32_do_raw(uint32_t numer, uint32_t magic, uint8_t more) { + if (!magic) { return numer >> more; } else { - uint32_t q = libdivide_mullhi_u32(denom->magic, numer); + uint32_t q = libdivide_mullhi_u32(magic, numer); if (more & LIBDIVIDE_ADD_MARKER) { uint32_t t = ((numer - q) >> 1) + q; return t >> (more & LIBDIVIDE_32_SHIFT_MASK); @@ -929,6 +932,10 @@ uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) { } } +uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) { + return libdivide_u32_do_raw(numer, denom->magic, denom->more); +} + uint32_t libdivide_u32_branchfree_do( uint32_t numer, const struct libdivide_u32_branchfree_t *denom) { uint32_t q = libdivide_mullhi_u32(denom->magic, numer); @@ -1428,11 +1435,10 @@ struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) { return result; } -int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { - uint8_t more = denom->more; +uint32_t libdivide_s32_do_raw(int32_t numer, int32_t magic, uint8_t more) { uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; - if (!denom->magic) { + if (!magic) { uint32_t sign = (int8_t)more >> 7; uint32_t mask = ((uint32_t)1 << shift) - 1; uint32_t uq = numer + ((numer >> 31) & mask); @@ -1441,7 +1447,7 @@ int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { q = (q ^ sign) - sign; return q; } else { - uint32_t uq = (uint32_t)libdivide_mullhi_s32(denom->magic, numer); + uint32_t uq = (uint32_t)libdivide_mullhi_s32(magic, numer); if (more & LIBDIVIDE_ADD_MARKER) { // must be arithmetic shift and then sign extend int32_t sign = (int8_t)more >> 7; @@ -1456,6 +1462,10 @@ int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { } } +int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { + return libdivide_s32_do_raw(numer, denom->magic, denom->more); +} + int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom) { uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; diff --git a/speeduino/storage.h b/speeduino/storage.h index 888e5590..c1f84280 100644 --- a/speeduino/storage.h +++ b/speeduino/storage.h @@ -196,6 +196,6 @@ extern uint32_t deferEEPROMWritesUntil; #define EEPROM_CALIBRATION_IAT_OLD 3071 #define EEPROM_CALIBRATION_CLT_OLD 3583 -#define EEPROM_DEFER_DELAY 1000000UL //1.0 second pause after large comms before writing to EEPROM +#define EEPROM_DEFER_DELAY MICROS_PER_SEC //1.0 second pause after large comms before writing to EEPROM #endif // STORAGE_H \ No newline at end of file diff --git a/speeduino/table3d_interpolate.cpp b/speeduino/table3d_interpolate.cpp index b6ba4074..ec9a2760 100644 --- a/speeduino/table3d_interpolate.cpp +++ b/speeduino/table3d_interpolate.cpp @@ -1,4 +1,5 @@ #include "table3d_interpolate.h" +#include "maths.h" // ============================= Axis Bin Searching ========================= @@ -140,7 +141,7 @@ static inline QU1X8_t compute_bin_position(table3d_axis_t value, const table3d_d // But since we are computing the ratio (0 to 1), p is guaranteed to be // less than binWidth and thus the division below will result in a value // <=1. So we can reduce the data type from 24.8 (uint32_t) to 1.8 (uint16_t) - return p / (uint32_t)binWidth; + return udiv_32_16(p, (uint16_t)binWidth); } diff --git a/test/test_math/main.cpp b/test/test_math/main.cpp index abca8f36..6adeb2dd 100644 --- a/test/test_math/main.cpp +++ b/test/test_math/main.cpp @@ -1,8 +1,9 @@ #include #include - #include "tests_crankmaths.h" -#include "tests_maths.h" + +extern void testPercent(void); +extern void testDivision(void); #define UNITY_EXCLUDE_DETAILS @@ -17,7 +18,8 @@ void setup() UNITY_BEGIN(); // IMPORTANT LINE! testCrankMaths(); - testMaths(); + testPercent(); + testDivision(); UNITY_END(); // stop unit testing } diff --git a/test/test_math/test_division.cpp b/test/test_math/test_division.cpp new file mode 100644 index 00000000..5b78975d --- /dev/null +++ b/test/test_math/test_division.cpp @@ -0,0 +1,222 @@ +#include +#include "test_fp_support.h" +#include "maths.h" +#include "crankMaths.h" +#include "..\timer.hpp" + + +template +static void test_div100(T testValue) { + assert_rounded_div(testValue, 100, div100(testValue)); +} + +template +static void test_div100_Seed(T seedValue) { + test_div100((T)seedValue-(T)100); + test_div100((T)seedValue-(T)51); + test_div100((T)seedValue-(T)50); + test_div100((T)seedValue); + test_div100((T)seedValue+(T)50); + test_div100((T)seedValue+(T)51); + test_div100((T)seedValue+(T)100); +} + +void test_maths_div100_U16(void) +{ + test_div100_Seed(100U); + test_div100_Seed(10000U); + + test_div100(0); + test_div100(99); + test_div100(UINT16_MAX-100); + + // We expect this to fail - the rounding doesn't do integer promotion + TEST_ASSERT_EQUAL_UINT16(0, div100((uint16_t)UINT16_MAX)); +} + +void test_maths_div100_U32(void) +{ + test_div100(0); + test_div100_Seed(100U); + test_div100_Seed(10000U); + test_div100_Seed(100000000UL); +} + +void test_maths_div100_S16(void) +{ + //Check both the signed and unsigned results + test_div100_Seed(100U); + test_div100_Seed(10000U); + + test_div100(0); + test_div100(UINT16_MAX-100); + + test_div100_Seed(-100); + test_div100_Seed(-10000); + + test_div100(INT16_MIN+100); + + // We expect this to fail - the rounding doesn't do integer promotion + TEST_ASSERT_EQUAL_UINT16(327U, div100((int16_t)INT16_MIN)); +} + +void test_maths_div100_S32(void) +{ + //Check both the signed and unsigned results +#if defined(__arm__) + test_div100_Seed(100U); + test_div100_Seed(10000U); + test_div100_Seed(100000000UL); + + test_div100_Seed(-100); + test_div100_Seed(-10000); + test_div100_Seed(-100000000); +#else + test_div100_Seed(100U); + test_div100_Seed(10000U); + test_div100_Seed(100000000UL); + + test_div100_Seed(-100); + test_div100_Seed(-10000); + test_div100_Seed(-100000000); +#endif +} + +static void test_div360(uint32_t testValue) { + assert_rounded_div(testValue, 360, div360(testValue)); +} +void test_maths_div360(void) +{ + test_div360(10000000UL-360UL); + test_div360((10000000UL-180UL)-1UL); + test_div360(10000000UL-180UL); + test_div360((10000000UL-180UL)+1UL); + test_div360(10000000UL); + test_div360((10000000UL+180UL)-1UL); + test_div360(10000000UL+180UL); + test_div360((10000000UL+180UL)+1UL); + test_div360(10000000UL+360UL); + + test_div360((360UL*MICROS_PER_DEG_1_RPM)/MAX_RPM); // Min revolution time + test_div360((360UL*MICROS_PER_DEG_1_RPM)/MIN_RPM); // Max revolution time +} + +void assert_udiv_32_16(uint32_t dividend, uint16_t divisor) { + TEST_ASSERT_EQUAL_UINT16(dividend/(uint32_t)divisor, udiv_32_16(dividend, divisor)); +} + +void test_maths_udiv_32_16(void) +{ + // Divide by zero + TEST_ASSERT_EQUAL_UINT16(UINT16_MAX, udiv_32_16(0, 0)); + + // Result doesn't fit into 16-bits + TEST_ASSERT_EQUAL_UINT16(UINT16_MAX, udiv_32_16(UINT32_MAX, UINT16_MAX)); + + assert_udiv_32_16(1, 1); + assert_udiv_32_16(UINT16_MAX+1, UINT16_MAX); + assert_udiv_32_16(UINT16_MAX-1, UINT16_MAX); + assert_udiv_32_16(MICROS_PER_MIN, 60000); // 1000 RPM + assert_udiv_32_16(MICROS_PER_MIN, 54005); // 1111 RPM + assert_udiv_32_16(MICROS_PER_MIN, 7590); // 7905 RPM + assert_udiv_32_16(MICROS_PER_MIN, 7715); // 7777 RPM + assert_udiv_32_16(MICROS_PER_MIN, 3333); // 18000 RPM +} + + +void assert_udiv_32_16_closest(uint32_t dividend, uint16_t divisor) { + assert_rounded_div(dividend, divisor, udiv_32_16_closest(dividend, divisor)); +} + +void test_maths_udiv_32_16_closest(void) +{ + // Divide by zero + TEST_ASSERT_EQUAL_UINT16(UINT16_MAX, udiv_32_16_closest(0, 0)); + + // Result doesn't fit into 16-bits + TEST_ASSERT_EQUAL_UINT16(UINT16_MAX, udiv_32_16_closest(UINT32_MAX-(UINT16_MAX/2)-1, UINT16_MAX)); + + assert_udiv_32_16(1, 1); + assert_udiv_32_16(2, 3); + assert_udiv_32_16(UINT16_MAX+1, UINT16_MAX); + assert_udiv_32_16(UINT16_MAX-1, UINT16_MAX); + assert_udiv_32_16(MICROS_PER_MIN, 60000); // 1000 RPM + assert_udiv_32_16(MICROS_PER_MIN, 54005); // 1111 RPM + assert_udiv_32_16(MICROS_PER_MIN, 7590); // 7905 RPM + assert_udiv_32_16(MICROS_PER_MIN, 7715); // 7777 RPM + assert_udiv_32_16(MICROS_PER_MIN, 3333); // 18000 RPM +} + +static uint32_t indexToDividend(int16_t index) { + return (uint32_t)index + (UINT16_MAX*index); +} +void test_maths_udiv_32_16_perf(void) +{ + uint16_t iters = 32; + uint16_t start_index = UINT16_MAX/3; + uint16_t end_index = UINT16_MAX/3*2; + uint16_t step = 111; + + auto nativeTest = [] (uint16_t index, uint32_t &checkSum) { checkSum += (uint32_t)indexToDividend(index) / (uint32_t)index; }; + auto optimizedTest = [] (uint16_t index, uint32_t &checkSum) { checkSum += udiv_32_16(indexToDividend(index), index); }; + TEST_MESSAGE("udiv_32_16"); + auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); + + // The checksums will be different due to rounding. This is only + // here to force the compiler to run the loops above + TEST_ASSERT_INT32_WITHIN(UINT32_MAX/2, comparison.timeA.result, comparison.timeB.result); + + TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); +} + +void test_maths_div100_s16_perf(void) +{ + constexpr int16_t iters = 1; + constexpr int16_t start_index = -10000; + constexpr int16_t end_index = -1; + constexpr int16_t step = 11; + + auto nativeTest = [] (int16_t index, int32_t &checkSum) { checkSum += (int16_t)index / (int16_t)100; }; + auto optimizedTest = [] (int16_t index, int32_t &checkSum) { checkSum += div100(index); }; + TEST_MESSAGE("div100_s16"); + auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); + + // The checksums will be different due to rounding. This is only + // here to force the compiler to run the loops above + TEST_ASSERT_INT32_WITHIN(UINT32_MAX/2, comparison.timeA.result, comparison.timeB.result); + + TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); +} + + +void test_maths_div100_s32_perf(void) +{ + constexpr int32_t iters = 1; + constexpr int32_t start_index = -1439190; + constexpr int32_t end_index = -1; + constexpr int32_t step = 3715; + + auto nativeTest = [] (int32_t index, int32_t &checkSum) { checkSum += (int32_t)index / (int32_t)100; }; + auto optimizedTest = [] (int32_t index, int32_t &checkSum) { checkSum += div100(index); }; + TEST_MESSAGE("div100_s32"); + auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); + + // The checksums will be different due to rounding. This is only + // here to force the compiler to run the loops above + TEST_ASSERT_INT32_WITHIN(UINT32_MAX/2, comparison.timeA.result, comparison.timeB.result); + + TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); +} + +void testDivision(void) { + RUN_TEST(test_maths_div100_U16); + RUN_TEST(test_maths_div100_U32); + RUN_TEST(test_maths_div100_S16); + RUN_TEST(test_maths_div100_S32); + RUN_TEST(test_maths_udiv_32_16); + RUN_TEST(test_maths_udiv_32_16_closest); + RUN_TEST(test_maths_udiv_32_16_perf); + RUN_TEST(test_maths_div360); + RUN_TEST(test_maths_div100_s16_perf); + RUN_TEST(test_maths_div100_s32_perf); +} \ No newline at end of file diff --git a/test/test_math/test_fp_support.cpp b/test/test_math/test_fp_support.cpp new file mode 100644 index 00000000..80e09071 --- /dev/null +++ b/test/test_math/test_fp_support.cpp @@ -0,0 +1,14 @@ +#include "test_fp_support.h" + +float64_t floatDivision(int32_t a, int32_t b) { + return fp64_div(fp64_int32_to_float64(a), fp64_int32_to_float64(b)); +} + +void assert_rounded_div(int32_t a, int32_t b, int32_t actual) { + float64_t fExpected = floatDivision(a, b); + int32_t expected = fp64_lround(fExpected); + + char msg[64]; + sprintf(msg, "a: %" PRIi32 ", b: %" PRIi32 " fExpected: %s", a, b, fp64_to_string(fExpected, 17, 15)); + TEST_ASSERT_EQUAL_MESSAGE(expected, actual, msg); +} \ No newline at end of file diff --git a/test/test_math/test_fp_support.h b/test/test_math/test_fp_support.h new file mode 100644 index 00000000..d6881b3a --- /dev/null +++ b/test/test_math/test_fp_support.h @@ -0,0 +1,8 @@ +#pragma once +#include +#include +#include + +float64_t floatDivision(int32_t a, int32_t b); + +void assert_rounded_div(int32_t a, int32_t b, int32_t actual); \ No newline at end of file diff --git a/test/test_math/tests_crankmaths.cpp b/test/test_math/tests_crankmaths.cpp index 5d491aea..ac87ee31 100644 --- a/test/test_math/tests_crankmaths.cpp +++ b/test/test_math/tests_crankmaths.cpp @@ -1,8 +1,10 @@ -#include "globals.h" +#include +// #include "globals.h" #include "crankMaths.h" -#include "unity.h" #include "decoders.h" +extern void SetRevolutionTime(uint32_t revTime); + struct crankmaths_rev_testdata { uint16_t rpm; unsigned long revolutionTime; @@ -10,6 +12,12 @@ struct crankmaths_rev_testdata { unsigned long expected; } *crankmaths_rev_testdata_current; +void test_crankmaths_angletotime_revolution_execute() { + crankmaths_rev_testdata *testdata = crankmaths_rev_testdata_current; + SetRevolutionTime(testdata->revolutionTime); + TEST_ASSERT_INT32_WITHIN(1, testdata->expected, angleToTimeMicroSecPerDegree(testdata->angle)); +} + struct crankmaths_tooth_testdata { uint16_t rpm; uint16_t triggerToothAngle; @@ -18,17 +26,11 @@ struct crankmaths_tooth_testdata { unsigned long expected; } *crankmaths_tooth_testdata_current; -void test_crankmaths_angletotime_revolution_execute() { - crankmaths_rev_testdata *testdata = crankmaths_rev_testdata_current; - revolutionTime = testdata->revolutionTime; - TEST_ASSERT_EQUAL(testdata->expected, angleToTime(testdata->angle, CRANKMATH_METHOD_INTERVAL_REV)); -} - void test_crankmaths_angletotime_tooth_execute() { crankmaths_tooth_testdata *testdata = crankmaths_tooth_testdata_current; triggerToothAngle = testdata->triggerToothAngle; toothLastToothTime = toothLastMinusOneToothTime + testdata->toothTime; - TEST_ASSERT_EQUAL(testdata->expected, angleToTime(testdata->angle, CRANKMATH_METHOD_INTERVAL_TOOTH)); + TEST_ASSERT_EQUAL(testdata->expected, angleToTimeIntervalTooth(testdata->angle)); } void testCrankMaths() @@ -41,7 +43,7 @@ void testCrankMaths() { .rpm = 50, .revolutionTime = 1200000, .angle = 25, .expected = 83333 }, // 83333,3333 { .rpm = 50, .revolutionTime = 1200000, .angle = 720, .expected = 2400000 }, { .rpm = 2500, .revolutionTime = 24000, .angle = 0, .expected = 0 }, - { .rpm = 2500, .revolutionTime = 24000, .angle = 25, .expected = 1666 }, // 1666,6666 + { .rpm = 2500, .revolutionTime = 24000, .angle = 25, .expected = 1667 }, // 1666,6666 { .rpm = 2500, .revolutionTime = 24000, .angle = 720, .expected = 48000 }, { .rpm = 20000, .revolutionTime = 3000, .angle = 0, .expected = 0 }, { .rpm = 20000, .revolutionTime = 3000, .angle = 25, .expected = 208 }, // 208,3333 diff --git a/test/test_math/tests_maths.cpp b/test/test_math/tests_maths.cpp deleted file mode 100644 index db7c885d..00000000 --- a/test/test_math/tests_maths.cpp +++ /dev/null @@ -1,151 +0,0 @@ -//#include -#include // memcpy -#include -#include -#include "tests_maths.h" -#include "maths.h" - - -void testMaths() -{ - RUN_TEST(test_maths_percent_U8); - RUN_TEST(test_maths_percent_U16); - RUN_TEST(test_maths_percent_U32); - RUN_TEST(test_maths_halfpercent_U8); - RUN_TEST(test_maths_halfpercent_U16); - RUN_TEST(test_maths_halfpercent_U32); - RUN_TEST(test_maths_div100_U8); - RUN_TEST(test_maths_div100_U16); - RUN_TEST(test_maths_div100_U32); - RUN_TEST(test_maths_div100_S8); - RUN_TEST(test_maths_div100_S16); - RUN_TEST(test_maths_div100_S32); - -} - -void test_maths_percent_U8(void) -{ - uint8_t percentOf = 200; - TEST_ASSERT_EQUAL(100, percentage(50, percentOf)); - TEST_ASSERT_EQUAL(150, percentage(75, percentOf)); - TEST_ASSERT_EQUAL(0, percentage(0, percentOf)); - TEST_ASSERT_EQUAL(200, percentage(100, percentOf)); - TEST_ASSERT_EQUAL(250, percentage(125, percentOf)); -} - -void test_maths_percent_U16(void) -{ - uint16_t percentOf = 20000; - TEST_ASSERT_EQUAL(10000, percentage(50, percentOf)); - TEST_ASSERT_EQUAL(15000, percentage(75, percentOf)); - TEST_ASSERT_EQUAL(0, percentage(0, percentOf)); - TEST_ASSERT_EQUAL(20000, percentage(100, percentOf)); - TEST_ASSERT_EQUAL(25000, percentage(125, percentOf)); -} - -void test_maths_percent_U32(void) -{ - uint32_t percentOf = 20000000UL; - TEST_ASSERT_EQUAL(10000000UL, percentage(50, percentOf)); - TEST_ASSERT_EQUAL(15000000UL, percentage(75, percentOf)); - TEST_ASSERT_EQUAL(0, percentage(0, percentOf)); - TEST_ASSERT_EQUAL(20000000UL, percentage(100, percentOf)); - TEST_ASSERT_EQUAL(25000000UL, percentage(125, percentOf)); -} - -void test_maths_halfpercent_U8(void) -{ - uint8_t percentOf = 200; - TEST_ASSERT_EQUAL(50, halfPercentage(50, percentOf)); - TEST_ASSERT_EQUAL(75, halfPercentage(75, percentOf)); - TEST_ASSERT_EQUAL(0, halfPercentage(0, percentOf)); - TEST_ASSERT_EQUAL(100, halfPercentage(100, percentOf)); - TEST_ASSERT_EQUAL(125, halfPercentage(125, percentOf)); -} - -void test_maths_halfpercent_U16(void) -{ - uint16_t percentOf = 20000; - TEST_ASSERT_EQUAL(5000, halfPercentage(50, percentOf)); - TEST_ASSERT_EQUAL(7500, halfPercentage(75, percentOf)); - TEST_ASSERT_EQUAL(0, halfPercentage(0, percentOf)); - TEST_ASSERT_EQUAL(10000, halfPercentage(100, percentOf)); - TEST_ASSERT_EQUAL(12500, halfPercentage(125, percentOf)); -} - -void test_maths_halfpercent_U32(void) -{ - uint32_t percentOf = 20000000UL; - TEST_ASSERT_EQUAL(5000000UL, halfPercentage(50, percentOf)); - TEST_ASSERT_EQUAL(7500000UL, halfPercentage(75, percentOf)); - TEST_ASSERT_EQUAL(0, halfPercentage(0, percentOf)); - TEST_ASSERT_EQUAL(10000000UL, halfPercentage(100, percentOf)); - TEST_ASSERT_EQUAL(12500000UL, halfPercentage(125, percentOf)); -} - -void test_maths_div100_U8(void) -{ - TEST_ASSERT_EQUAL_UINT8(1, div100((uint8_t)100U)); - TEST_ASSERT_EQUAL_UINT8(2, div100((uint8_t)200U)); - TEST_ASSERT_EQUAL_UINT8(0, div100((uint8_t)0U)); - TEST_ASSERT_EQUAL_UINT8(0, div100((uint8_t)50U)); - TEST_ASSERT_EQUAL_UINT8(2, div100((uint8_t)250U)); -} - -void test_maths_div100_U16(void) -{ - TEST_ASSERT_EQUAL_UINT16(100, div100((uint16_t)10000U)); - TEST_ASSERT_EQUAL_UINT16(400, div100((uint16_t)40000U)); -} - -void test_maths_div100_U32(void) -{ - TEST_ASSERT_EQUAL_UINT32(1000000UL, div100(100000000UL)); - TEST_ASSERT_EQUAL_UINT32(2000000UL, div100(200000000UL)); -} - -void test_maths_div100_S8(void) -{ - //Check both the signed and unsigned results - TEST_ASSERT_EQUAL_INT8(1, div100((int8_t)100)); - TEST_ASSERT_EQUAL_INT8(0, div100((int8_t)0)); - TEST_ASSERT_EQUAL_INT8(0, div100((int8_t)50)); - - TEST_ASSERT_EQUAL_INT8(-1, div100((int8_t)-100)); - TEST_ASSERT_EQUAL_INT8(0, div100((int8_t)-50)); - TEST_ASSERT_EQUAL_INT8(-1, div100((int8_t)-120)); -} - -void test_maths_div100_S16(void) -{ - //Check both the signed and unsigned results - TEST_ASSERT_EQUAL_INT16(100, div100((int16_t)10000)); - TEST_ASSERT_EQUAL_INT16(0, div100((int16_t)0)); - TEST_ASSERT_EQUAL_INT16(0, div100((int16_t)50)); - - TEST_ASSERT_EQUAL_INT16(-100, div100((int16_t)-10000)); - TEST_ASSERT_EQUAL_INT16(0, div100((int16_t)-50)); - TEST_ASSERT_EQUAL_INT16(-1, div100((int16_t)-120)); -} - -void test_maths_div100_S32(void) -{ - //Check both the signed and unsigned results -#if defined(__arm__) - TEST_ASSERT_EQUAL_INT32(1000000L, div100((int)100000000L)); - TEST_ASSERT_EQUAL_INT32(0, div100((int)0)); - TEST_ASSERT_EQUAL_INT32(0, div100((int)50)); - - TEST_ASSERT_EQUAL_INT32(-1000000L, div100((int)-100000000L)); - TEST_ASSERT_EQUAL_INT32(0, div100((int)-50)); - TEST_ASSERT_EQUAL_INT32(-1, div100((int)-120)); -#else - TEST_ASSERT_EQUAL_INT32(1000000L, div100((int32_t)100000000L)); - TEST_ASSERT_EQUAL_INT32(0, div100((int32_t)0)); - TEST_ASSERT_EQUAL_INT32(0, div100((int32_t)50)); - - TEST_ASSERT_EQUAL_INT32(-1000000L, div100((int32_t)-100000000L)); - TEST_ASSERT_EQUAL_INT32(0, div100((int32_t)-50)); - TEST_ASSERT_EQUAL_INT32(-1, div100((int32_t)-120)); -#endif -} diff --git a/test/test_math/tests_maths.h b/test/test_math/tests_maths.h deleted file mode 100644 index 76240fe3..00000000 --- a/test/test_math/tests_maths.h +++ /dev/null @@ -1,18 +0,0 @@ -extern void testMaths(); -void test_maths_percent_U8(void); -void test_maths_percent_U16(void); -void test_maths_percent_U32(void); -void test_maths_halfpercent_U8(void); -void test_maths_halfpercent_U16(void); -void test_maths_halfpercent_U32(void); -void test_maths_div100_U8(void); -void test_maths_div100_U16(void); -void test_maths_div100_U32(void); -void test_maths_div100_S8(void); -void test_maths_div100_S16(void); -void test_maths_div100_S32(void); -void test_maths_div10_U8(void); -void test_maths_div10_U16(void); -void test_maths_div10_U32(void); - - diff --git a/test/test_math/tests_percent.cpp b/test/test_math/tests_percent.cpp new file mode 100644 index 00000000..45c3ee45 --- /dev/null +++ b/test/test_math/tests_percent.cpp @@ -0,0 +1,112 @@ +#include +#include "test_fp_support.h" +#include "maths.h" +#include "..\timer.hpp" + +static void test_percent(uint8_t percent, uint16_t value) { + assert_rounded_div((uint32_t)percent*value, 100, percentage(percent, value)); +} + +void test_maths_percent_U8(void) +{ + uint8_t percentOf = 77; + test_percent(0, percentOf); + test_percent(33, percentOf); + test_percent(50, percentOf); + test_percent(66, percentOf); + test_percent(75, percentOf); + test_percent(100, percentOf); + test_percent(125, percentOf); +} + +void test_maths_percent_U16(void) +{ + uint16_t percentOf = 33333; + test_percent(0, percentOf); + test_percent(33, percentOf); + test_percent(50, percentOf); + test_percent(66, percentOf); + test_percent(75, percentOf); + test_percent(100, percentOf); + test_percent(125, percentOf); +} + + +static void test_halfPercentage(uint8_t percent, uint16_t value) { + assert_rounded_div((int32_t)percent*value, 200, halfPercentage(percent, value)); +} + +void test_maths_halfpercent_U8(void) +{ + uint8_t percentOf = 111; + test_halfPercentage(0, percentOf); + test_halfPercentage(33, percentOf); + test_halfPercentage(50, percentOf); + test_halfPercentage(66, percentOf); + test_halfPercentage(75, percentOf); + test_halfPercentage(100, percentOf); + test_halfPercentage(125, percentOf); +} + +void test_maths_halfpercent_U16(void) +{ + uint16_t percentOf = 57357; + test_halfPercentage(0, percentOf); + test_halfPercentage(33, percentOf); + test_halfPercentage(50, percentOf); + test_halfPercentage(66, percentOf); + test_halfPercentage(75, percentOf); + test_halfPercentage(100, percentOf); + test_halfPercentage(125, percentOf); +} + +void test_maths_halfPercentage_perf(void) +{ + constexpr int16_t iters = 4; + constexpr uint8_t start_index = 3; + constexpr uint8_t end_index = 99; + constexpr uint8_t step = 3; + constexpr uint16_t percentOf = 57357; + + auto nativeTest = [] (uint8_t index, uint32_t &checkSum) { checkSum += ((uint32_t)percentOf * index) / 200U; }; + auto optimizedTest = [] (uint8_t index, uint32_t &checkSum) { checkSum += halfPercentage(index, percentOf); }; + TEST_MESSAGE("halfPercentage "); + auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); + + // The checksums will be different due to rounding. This is only + // here to force the compiler to run the loops above + TEST_ASSERT_INT32_WITHIN(UINT32_MAX/2, comparison.timeA.result, comparison.timeB.result); + + TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); +} + + +void test_maths_percentage_perf(void) +{ + constexpr uint16_t iters = 4; + constexpr uint8_t start_index = 3; + constexpr uint8_t end_index = 99; + constexpr uint8_t step = 3; + constexpr uint16_t percentOf = 57357; + + auto nativeTest = [] (uint8_t index, uint32_t &checkSum) { checkSum += ((uint32_t)percentOf * index) / 100U; }; + auto optimizedTest = [] (uint8_t index, uint32_t &checkSum) { checkSum += percentage(index, percentOf); }; + TEST_MESSAGE("Percentage "); + auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); + + // The checksums will be different due to rounding. This is only + // here to force the compiler to run the loops above + TEST_ASSERT_INT32_WITHIN(UINT32_MAX/2, comparison.timeA.result, comparison.timeB.result); + + TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); +} + +void testPercent() +{ + RUN_TEST(test_maths_percent_U8); + RUN_TEST(test_maths_percent_U16); + RUN_TEST(test_maths_halfpercent_U8); + RUN_TEST(test_maths_halfpercent_U16); + RUN_TEST(test_maths_halfPercentage_perf); + RUN_TEST(test_maths_percentage_perf); +} diff --git a/test/test_schedule_calcs/test_ign_calcs.cpp b/test/test_schedule_calcs/test_ign_calcs.cpp index 7ba3bebe..46a0cda0 100644 --- a/test/test_schedule_calcs/test_ign_calcs.cpp +++ b/test/test_schedule_calcs/test_ign_calcs.cpp @@ -3,23 +3,21 @@ #include "test_calcs_common.h" #include "schedule_calcs.h" #include "crankMaths.h" +#include "Decoders.h" #define _countof(x) (sizeof(x) / sizeof (x[0])) -extern volatile uint16_t degreesPeruSx2048; +extern void SetRevolutionTime(uint32_t revTime); constexpr uint16_t DWELL_TIME_MS = 4; uint16_t dwellAngle; + void setEngineSpeed(uint16_t rpm, int16_t max_crank) { - timePerDegreex16 = ldiv( 2666656L, rpm).quot; //The use of a x16 value gives accuracy down to 0.1 of a degree and can provide noticeably better timing results on low resolution triggers - timePerDegree = timePerDegreex16 / 16; - degreesPeruSx2048 = 2048 / timePerDegree; - degreesPeruSx32768 = 524288L / timePerDegreex16; - revolutionTime = (60L*1000000L) / rpm; + SetRevolutionTime(UDIV_ROUND_CLOSEST(60UL*1000000UL, rpm, uint32_t)); CRANK_ANGLE_MAX_IGN = max_crank; CRANK_ANGLE_MAX_INJ = max_crank; - dwellAngle = timeToAngle(DWELL_TIME_MS*1000UL, CRANKMATH_METHOD_INTERVAL_REV); + dwellAngle = timeToAngleDegPerMicroSec(DWELL_TIME_MS*1000UL); } struct ign_test_parameters @@ -49,11 +47,11 @@ void test_calc_ign_timeout(const ign_test_parameters &test_params) sprintf_P(msg, PSTR("PENDING advanceAngle: %" PRIi8 ", channelAngle: %" PRIu16 ", crankAngle: %" PRIu16 ", endAngle: %" PRIi16), test_params.advanceAngle, test_params.channelAngle, test_params.crankAngle, endAngle); schedule.Status = PENDING; - TEST_ASSERT_EQUAL_MESSAGE(test_params.pending, calculateIgnitionTimeout(schedule, startAngle, test_params.channelAngle, test_params.crankAngle), msg); + TEST_ASSERT_INT32_WITHIN_MESSAGE(1, test_params.pending, calculateIgnitionTimeout(schedule, startAngle, test_params.channelAngle, test_params.crankAngle), msg); sprintf_P(msg, PSTR("RUNNING advanceAngle: %" PRIi8 ", channelAngle: %" PRIu16 ", crankAngle: %" PRIu16 ", endAngle: %" PRIi16), test_params.advanceAngle, test_params.channelAngle, test_params.crankAngle, endAngle); schedule.Status = RUNNING; - TEST_ASSERT_EQUAL_MESSAGE(test_params.running, calculateIgnitionTimeout(schedule, startAngle, test_params.channelAngle, test_params.crankAngle), msg); + TEST_ASSERT_INT32_WITHIN_MESSAGE(1, test_params.running, calculateIgnitionTimeout(schedule, startAngle, test_params.channelAngle, test_params.crankAngle), msg); } void test_calc_ign_timeout(const ign_test_parameters *pStart, const ign_test_parameters *pEnd) @@ -70,24 +68,30 @@ void test_calc_ign_timeout(const ign_test_parameters *pStart, const ign_test_par void test_calc_ign_timeout_360() { setEngineSpeed(4000, 360); + + TEST_ASSERT_EQUAL(15000, revolutionTime); + TEST_ASSERT_EQUAL(786, degreesPerMicro); + TEST_ASSERT_EQUAL(10667, microsPerDegree); + TEST_ASSERT_EQUAL(96, dwellAngle); + // Expected test values were generated using floating point calculations (in Excel) static const ign_test_parameters test_data[] PROGMEM = { // ChannelAngle (deg), Advance, Crank, Expected Pending, Expected Running - { 0, -40, 0, 12666, 12666, 304, 40 }, - { 0, -40, 45, 10791, 10791, 304, 40 }, - { 0, -40, 90, 8916, 8916, 304, 40 }, - { 0, -40, 135, 7041, 7041, 304, 40 }, - { 0, -40, 180, 5166, 5166, 304, 40 }, + { 0, -40, 0, 12667, 12667, 304, 40 }, + { 0, -40, 45, 10792, 10792, 304, 40 }, + { 0, -40, 90, 8917, 8917, 304, 40 }, + { 0, -40, 135, 7042, 7042, 304, 40 }, + { 0, -40, 180, 5167, 5167, 304, 40 }, { 0, -40, 215, 3708, 3708, 304, 40 }, - { 0, -40, 270, 1416, 1416, 304, 40 }, - { 0, -40, 315, 0, 14541, 304, 40 }, - { 0, -40, 360, 0, 12666, 304, 40 }, + { 0, -40, 270, 1417, 1417, 304, 40 }, + { 0, -40, 315, 0, 14542, 304, 40 }, + { 0, -40, 360, 0, 12667, 304, 40 }, { 0, 0, 0, 11000, 11000, 264, 360 }, { 0, 0, 45, 9125, 9125, 264, 360 }, { 0, 0, 90, 7250, 7250, 264, 360 }, { 0, 0, 135, 5375, 5375, 264, 360 }, { 0, 0, 180, 3500, 3500, 264, 360 }, - { 0, 0, 215, 2041, 2041, 264, 360 }, + { 0, 0, 215, 2042, 2042, 264, 360 }, { 0, 0, 270, 0, 14750, 264, 360 }, { 0, 0, 315, 0, 12875, 264, 360 }, { 0, 0, 360, 0, 11000, 264, 360 }, @@ -100,21 +104,21 @@ void test_calc_ign_timeout_360() { 0, 40, 270, 0, 13083, 224, 320 }, { 0, 40, 315, 0, 11208, 224, 320 }, { 0, 40, 360, 0, 9333, 224, 320 }, - { 72, -40, 0, 666, 666, 16, 112 }, - { 72, -40, 45, 0, 13791, 16, 112 }, - { 72, -40, 90, 11916, 11916, 16, 112 }, - { 72, -40, 135, 10041, 10041, 16, 112 }, - { 72, -40, 180, 8166, 8166, 16, 112 }, + { 72, -40, 0, 667, 667, 16, 112 }, + { 72, -40, 45, 0, 13792, 16, 112 }, + { 72, -40, 90, 11917, 11917, 16, 112 }, + { 72, -40, 135, 10042, 10042, 16, 112 }, + { 72, -40, 180, 8167, 8167, 16, 112 }, { 72, -40, 215, 6708, 6708, 16, 112 }, - { 72, -40, 270, 4416, 4416, 16, 112 }, - { 72, -40, 315, 2541, 2541, 16, 112 }, - { 72, -40, 360, 666, 666, 16, 112 }, + { 72, -40, 270, 4417, 4417, 16, 112 }, + { 72, -40, 315, 2542, 2542, 16, 112 }, + { 72, -40, 360, 667, 667, 16, 112 }, { 72, 0, 0, 0, 14000, 336, 72 }, { 72, 0, 45, 0, 12125, 336, 72 }, { 72, 0, 90, 10250, 10250, 336, 72 }, { 72, 0, 135, 8375, 8375, 336, 72 }, { 72, 0, 180, 6500, 6500, 336, 72 }, - { 72, 0, 215, 5041, 5041, 336, 72 }, + { 72, 0, 215, 5042, 5042, 336, 72 }, { 72, 0, 270, 2750, 2750, 336, 72 }, { 72, 0, 315, 875, 875, 336, 72 }, { 72, 0, 360, 0, 14000, 336, 72 }, @@ -127,21 +131,21 @@ void test_calc_ign_timeout_360() { 72, 40, 270, 1083, 1083, 296, 32 }, { 72, 40, 315, 0, 14208, 296, 32 }, { 72, 40, 360, 0, 12333, 296, 32 }, - { 90, -40, 0, 1416, 1416, 34, 130 }, - { 90, -40, 45, 0, 14541, 34, 130 }, - { 90, -40, 90, 12666, 12666, 34, 130 }, - { 90, -40, 135, 10791, 10791, 34, 130 }, - { 90, -40, 180, 8916, 8916, 34, 130 }, + { 90, -40, 0, 1417, 1417, 34, 130 }, + { 90, -40, 45, 0, 14542, 34, 130 }, + { 90, -40, 90, 12667, 12667, 34, 130 }, + { 90, -40, 135, 10792, 10792, 34, 130 }, + { 90, -40, 180, 8917, 8917, 34, 130 }, { 90, -40, 215, 7458, 7458, 34, 130 }, - { 90, -40, 270, 5166, 5166, 34, 130 }, - { 90, -40, 315, 3291, 3291, 34, 130 }, - { 90, -40, 360, 1416, 1416, 34, 130 }, + { 90, -40, 270, 5167, 5167, 34, 130 }, + { 90, -40, 315, 3292, 3292, 34, 130 }, + { 90, -40, 360, 1417, 1417, 34, 130 }, { 90, 0, 0, 0, 14750, 354, 90 }, { 90, 0, 45, 0, 12875, 354, 90 }, { 90, 0, 90, 11000, 11000, 354, 90 }, { 90, 0, 135, 9125, 9125, 354, 90 }, { 90, 0, 180, 7250, 7250, 354, 90 }, - { 90, 0, 215, 5791, 5791, 354, 90 }, + { 90, 0, 215, 5792, 5792, 354, 90 }, { 90, 0, 270, 3500, 3500, 354, 90 }, { 90, 0, 315, 1625, 1625, 354, 90 }, { 90, 0, 360, 0, 14750, 354, 90 }, @@ -154,21 +158,21 @@ void test_calc_ign_timeout_360() { 90, 40, 270, 1833, 1833, 314, 50 }, { 90, 40, 315, 0, 14958, 314, 50 }, { 90, 40, 360, 0, 13083, 314, 50 }, - { 144, -40, 0, 3666, 3666, 88, 184 }, - { 144, -40, 45, 1791, 1791, 88, 184 }, - { 144, -40, 90, 0, 14916, 88, 184 }, - { 144, -40, 135, 0, 13041, 88, 184 }, - { 144, -40, 180, 11166, 11166, 88, 184 }, + { 144, -40, 0, 3667, 3667, 88, 184 }, + { 144, -40, 45, 1792, 1792, 88, 184 }, + { 144, -40, 90, 0, 14917, 88, 184 }, + { 144, -40, 135, 0, 13042, 88, 184 }, + { 144, -40, 180, 11167, 11167, 88, 184 }, { 144, -40, 215, 9708, 9708, 88, 184 }, - { 144, -40, 270, 7416, 7416, 88, 184 }, - { 144, -40, 315, 5541, 5541, 88, 184 }, - { 144, -40, 360, 3666, 3666, 88, 184 }, + { 144, -40, 270, 7417, 7417, 88, 184 }, + { 144, -40, 315, 5542, 5542, 88, 184 }, + { 144, -40, 360, 3667, 3667, 88, 184 }, { 144, 0, 0, 2000, 2000, 48, 144 }, { 144, 0, 45, 125, 125, 48, 144 }, { 144, 0, 90, 0, 13250, 48, 144 }, { 144, 0, 135, 0, 11375, 48, 144 }, { 144, 0, 180, 9500, 9500, 48, 144 }, - { 144, 0, 215, 8041, 8041, 48, 144 }, + { 144, 0, 215, 8042, 8042, 48, 144 }, { 144, 0, 270, 5750, 5750, 48, 144 }, { 144, 0, 315, 3875, 3875, 48, 144 }, { 144, 0, 360, 2000, 2000, 48, 144 }, @@ -181,21 +185,21 @@ void test_calc_ign_timeout_360() { 144, 40, 270, 4083, 4083, 8, 104 }, { 144, 40, 315, 2208, 2208, 8, 104 }, { 144, 40, 360, 333, 333, 8, 104 }, - { 180, -40, 0, 5166, 5166, 124, 220 }, - { 180, -40, 45, 3291, 3291, 124, 220 }, - { 180, -40, 90, 1416, 1416, 124, 220 }, - { 180, -40, 135, 0, 14541, 124, 220 }, - { 180, -40, 180, 12666, 12666, 124, 220 }, + { 180, -40, 0, 5167, 5167, 124, 220 }, + { 180, -40, 45, 3292, 3292, 124, 220 }, + { 180, -40, 90, 1417, 1417, 124, 220 }, + { 180, -40, 135, 0, 14542, 124, 220 }, + { 180, -40, 180, 12667, 12667, 124, 220 }, { 180, -40, 215, 11208, 11208, 124, 220 }, - { 180, -40, 270, 8916, 8916, 124, 220 }, - { 180, -40, 315, 7041, 7041, 124, 220 }, - { 180, -40, 360, 5166, 5166, 124, 220 }, + { 180, -40, 270, 8917, 8917, 124, 220 }, + { 180, -40, 315, 7042, 7042, 124, 220 }, + { 180, -40, 360, 5167, 5167, 124, 220 }, { 180, 0, 0, 3500, 3500, 84, 180 }, { 180, 0, 45, 1625, 1625, 84, 180 }, { 180, 0, 90, 0, 14750, 84, 180 }, { 180, 0, 135, 0, 12875, 84, 180 }, { 180, 0, 180, 11000, 11000, 84, 180 }, - { 180, 0, 215, 9541, 9541, 84, 180 }, + { 180, 0, 215, 9542, 9542, 84, 180 }, { 180, 0, 270, 7250, 7250, 84, 180 }, { 180, 0, 315, 5375, 5375, 84, 180 }, { 180, 0, 360, 3500, 3500, 84, 180 }, @@ -208,21 +212,21 @@ void test_calc_ign_timeout_360() { 180, 40, 270, 5583, 5583, 44, 140 }, { 180, 40, 315, 3708, 3708, 44, 140 }, { 180, 40, 360, 1833, 1833, 44, 140 }, - { 270, -40, 0, 8916, 8916, 214, 310 }, - { 270, -40, 45, 7041, 7041, 214, 310 }, - { 270, -40, 90, 5166, 5166, 214, 310 }, - { 270, -40, 135, 3291, 3291, 214, 310 }, - { 270, -40, 180, 1416, 1416, 214, 310 }, + { 270, -40, 0, 8917, 8917, 214, 310 }, + { 270, -40, 45, 7042, 7042, 214, 310 }, + { 270, -40, 90, 5167, 5167, 214, 310 }, + { 270, -40, 135, 3292, 3292, 214, 310 }, + { 270, -40, 180, 1417, 1417, 214, 310 }, { 270, -40, 215, 0, 14958, 214, 310 }, - { 270, -40, 270, 12666, 12666, 214, 310 }, - { 270, -40, 315, 10791, 10791, 214, 310 }, - { 270, -40, 360, 8916, 8916, 214, 310 }, + { 270, -40, 270, 12667, 12667, 214, 310 }, + { 270, -40, 315, 10792, 10792, 214, 310 }, + { 270, -40, 360, 8917, 8917, 214, 310 }, { 270, 0, 0, 7250, 7250, 174, 270 }, { 270, 0, 45, 5375, 5375, 174, 270 }, { 270, 0, 90, 3500, 3500, 174, 270 }, { 270, 0, 135, 1625, 1625, 174, 270 }, { 270, 0, 180, 0, 14750, 174, 270 }, - { 270, 0, 215, 0, 13291, 174, 270 }, + { 270, 0, 215, 0, 13292, 174, 270 }, { 270, 0, 270, 11000, 11000, 174, 270 }, { 270, 0, 315, 9125, 9125, 174, 270 }, { 270, 0, 360, 7250, 7250, 174, 270 }, @@ -235,21 +239,21 @@ void test_calc_ign_timeout_360() { 270, 40, 270, 9333, 9333, 134, 230 }, { 270, 40, 315, 7458, 7458, 134, 230 }, { 270, 40, 360, 5583, 5583, 134, 230 }, - { 360, -40, 0, 12666, 12666, 304, 40 }, - { 360, -40, 45, 10791, 10791, 304, 40 }, - { 360, -40, 90, 8916, 8916, 304, 40 }, - { 360, -40, 135, 7041, 7041, 304, 40 }, - { 360, -40, 180, 5166, 5166, 304, 40 }, + { 360, -40, 0, 12667, 12667, 304, 40 }, + { 360, -40, 45, 10792, 10792, 304, 40 }, + { 360, -40, 90, 8917, 8917, 304, 40 }, + { 360, -40, 135, 7042, 7042, 304, 40 }, + { 360, -40, 180, 5167, 5167, 304, 40 }, { 360, -40, 215, 3708, 3708, 304, 40 }, - { 360, -40, 270, 1416, 1416, 304, 40 }, - { 360, -40, 315, 0, 14541, 304, 40 }, - { 360, -40, 360, 12666, 12666, 304, 40 }, + { 360, -40, 270, 1417, 1417, 304, 40 }, + { 360, -40, 315, 0, 14542, 304, 40 }, + { 360, -40, 360, 12667, 12667, 304, 40 }, { 360, 0, 0, 11000, 11000, 264, 360 }, { 360, 0, 45, 9125, 9125, 264, 360 }, { 360, 0, 90, 7250, 7250, 264, 360 }, { 360, 0, 135, 5375, 5375, 264, 360 }, { 360, 0, 180, 3500, 3500, 264, 360 }, - { 360, 0, 215, 2041, 2041, 264, 360 }, + { 360, 0, 215, 2042, 2042, 264, 360 }, { 360, 0, 270, 0, 14750, 264, 360 }, { 360, 0, 315, 0, 12875, 264, 360 }, { 360, 0, 360, 11000, 11000, 264, 360 }, @@ -273,23 +277,24 @@ void test_calc_ign_timeout_720() { setEngineSpeed(4000, 720); + // Expected test values were generated using floating point calculations (in Excel) static const ign_test_parameters test_data[] PROGMEM = { // ChannelAngle (deg), Advance, Crank, Expected Pending, Expected Running - { 0, -40, 0, 27666, 27666, 664, 40 }, - { 0, -40, 45, 25791, 25791, 664, 40 }, - { 0, -40, 90, 23916, 23916, 664, 40 }, - { 0, -40, 135, 22041, 22041, 664, 40 }, - { 0, -40, 180, 20166, 20166, 664, 40 }, + { 0, -40, 0, 27667, 27667, 664, 40 }, + { 0, -40, 45, 25792, 25792, 664, 40 }, + { 0, -40, 90, 23917, 23917, 664, 40 }, + { 0, -40, 135, 22042, 22042, 664, 40 }, + { 0, -40, 180, 20167, 20167, 664, 40 }, { 0, -40, 215, 18708, 18708, 664, 40 }, - { 0, -40, 270, 16416, 16416, 664, 40 }, - { 0, -40, 315, 14541, 14541, 664, 40 }, - { 0, -40, 360, 12666, 12666, 664, 40 }, + { 0, -40, 270, 16417, 16417, 664, 40 }, + { 0, -40, 315, 14542, 14542, 664, 40 }, + { 0, -40, 360, 12667, 12667, 664, 40 }, { 0, 0, 0, 26000, 26000, 624, 720 }, { 0, 0, 45, 24125, 24125, 624, 720 }, { 0, 0, 90, 22250, 22250, 624, 720 }, { 0, 0, 135, 20375, 20375, 624, 720 }, { 0, 0, 180, 18500, 18500, 624, 720 }, - { 0, 0, 215, 17041, 17041, 624, 720 }, + { 0, 0, 215, 17042, 17042, 624, 720 }, { 0, 0, 270, 14750, 14750, 624, 720 }, { 0, 0, 315, 12875, 12875, 624, 720 }, { 0, 0, 360, 11000, 11000, 624, 720 }, @@ -302,21 +307,21 @@ void test_calc_ign_timeout_720() { 0, 40, 270, 13083, 13083, 584, 680 }, { 0, 40, 315, 11208, 11208, 584, 680 }, { 0, 40, 360, 9333, 9333, 584, 680 }, - { 72, -40, 0, 666, 666, 16, 112 }, - { 72, -40, 45, 0, 28791, 16, 112 }, - { 72, -40, 90, 26916, 26916, 16, 112 }, - { 72, -40, 135, 25041, 25041, 16, 112 }, - { 72, -40, 180, 23166, 23166, 16, 112 }, + { 72, -40, 0, 667, 667, 16, 112 }, + { 72, -40, 45, 0, 28792, 16, 112 }, + { 72, -40, 90, 26917, 26917, 16, 112 }, + { 72, -40, 135, 25042, 25042, 16, 112 }, + { 72, -40, 180, 23167, 23167, 16, 112 }, { 72, -40, 215, 21708, 21708, 16, 112 }, - { 72, -40, 270, 19416, 19416, 16, 112 }, - { 72, -40, 315, 17541, 17541, 16, 112 }, - { 72, -40, 360, 15666, 15666, 16, 112 }, + { 72, -40, 270, 19417, 19417, 16, 112 }, + { 72, -40, 315, 17542, 17542, 16, 112 }, + { 72, -40, 360, 15667, 15667, 16, 112 }, { 72, 0, 0, 0, 29000, 696, 72 }, { 72, 0, 45, 0, 27125, 696, 72 }, { 72, 0, 90, 25250, 25250, 696, 72 }, { 72, 0, 135, 23375, 23375, 696, 72 }, { 72, 0, 180, 21500, 21500, 696, 72 }, - { 72, 0, 215, 20041, 20041, 696, 72 }, + { 72, 0, 215, 20042, 20042, 696, 72 }, { 72, 0, 270, 17750, 17750, 696, 72 }, { 72, 0, 315, 15875, 15875, 696, 72 }, { 72, 0, 360, 14000, 14000, 696, 72 }, @@ -329,21 +334,21 @@ void test_calc_ign_timeout_720() { 72, 40, 270, 16083, 16083, 656, 32 }, { 72, 40, 315, 14208, 14208, 656, 32 }, { 72, 40, 360, 12333, 12333, 656, 32 }, - { 90, -40, 0, 1416, 1416, 34, 130 }, - { 90, -40, 45, 0, 29541, 34, 130 }, - { 90, -40, 90, 27666, 27666, 34, 130 }, - { 90, -40, 135, 25791, 25791, 34, 130 }, - { 90, -40, 180, 23916, 23916, 34, 130 }, + { 90, -40, 0, 1417, 1417, 34, 130 }, + { 90, -40, 45, 0, 29542, 34, 130 }, + { 90, -40, 90, 27667, 27667, 34, 130 }, + { 90, -40, 135, 25792, 25792, 34, 130 }, + { 90, -40, 180, 23917, 23917, 34, 130 }, { 90, -40, 215, 22458, 22458, 34, 130 }, - { 90, -40, 270, 20166, 20166, 34, 130 }, - { 90, -40, 315, 18291, 18291, 34, 130 }, - { 90, -40, 360, 16416, 16416, 34, 130 }, + { 90, -40, 270, 20167, 20167, 34, 130 }, + { 90, -40, 315, 18292, 18292, 34, 130 }, + { 90, -40, 360, 16417, 16417, 34, 130 }, { 90, 0, 0, 0, 29750, 714, 90 }, { 90, 0, 45, 0, 27875, 714, 90 }, { 90, 0, 90, 26000, 26000, 714, 90 }, { 90, 0, 135, 24125, 24125, 714, 90 }, { 90, 0, 180, 22250, 22250, 714, 90 }, - { 90, 0, 215, 20791, 20791, 714, 90 }, + { 90, 0, 215, 20792, 20792, 714, 90 }, { 90, 0, 270, 18500, 18500, 714, 90 }, { 90, 0, 315, 16625, 16625, 714, 90 }, { 90, 0, 360, 14750, 14750, 714, 90 }, @@ -356,21 +361,21 @@ void test_calc_ign_timeout_720() { 90, 40, 270, 16833, 16833, 674, 50 }, { 90, 40, 315, 14958, 14958, 674, 50 }, { 90, 40, 360, 13083, 13083, 674, 50 }, - { 144, -40, 0, 3666, 3666, 88, 184 }, - { 144, -40, 45, 1791, 1791, 88, 184 }, - { 144, -40, 90, 0, 29916, 88, 184 }, - { 144, -40, 135, 0, 28041, 88, 184 }, - { 144, -40, 180, 26166, 26166, 88, 184 }, + { 144, -40, 0, 3667, 3667, 88, 184 }, + { 144, -40, 45, 1792, 1792, 88, 184 }, + { 144, -40, 90, 0, 29917, 88, 184 }, + { 144, -40, 135, 0, 28042, 88, 184 }, + { 144, -40, 180, 26167, 26167, 88, 184 }, { 144, -40, 215, 24708, 24708, 88, 184 }, - { 144, -40, 270, 22416, 22416, 88, 184 }, - { 144, -40, 315, 20541, 20541, 88, 184 }, - { 144, -40, 360, 18666, 18666, 88, 184 }, + { 144, -40, 270, 22417, 22417, 88, 184 }, + { 144, -40, 315, 20542, 20542, 88, 184 }, + { 144, -40, 360, 18667, 18667, 88, 184 }, { 144, 0, 0, 2000, 2000, 48, 144 }, { 144, 0, 45, 125, 125, 48, 144 }, { 144, 0, 90, 0, 28250, 48, 144 }, { 144, 0, 135, 0, 26375, 48, 144 }, { 144, 0, 180, 24500, 24500, 48, 144 }, - { 144, 0, 215, 23041, 23041, 48, 144 }, + { 144, 0, 215, 23042, 23042, 48, 144 }, { 144, 0, 270, 20750, 20750, 48, 144 }, { 144, 0, 315, 18875, 18875, 48, 144 }, { 144, 0, 360, 17000, 17000, 48, 144 }, @@ -383,21 +388,21 @@ void test_calc_ign_timeout_720() { 144, 40, 270, 19083, 19083, 8, 104 }, { 144, 40, 315, 17208, 17208, 8, 104 }, { 144, 40, 360, 15333, 15333, 8, 104 }, - { 180, -40, 0, 5166, 5166, 124, 220 }, - { 180, -40, 45, 3291, 3291, 124, 220 }, - { 180, -40, 90, 1416, 1416, 124, 220 }, - { 180, -40, 135, 0, 29541, 124, 220 }, - { 180, -40, 180, 27666, 27666, 124, 220 }, + { 180, -40, 0, 5167, 5167, 124, 220 }, + { 180, -40, 45, 3292, 3292, 124, 220 }, + { 180, -40, 90, 1417, 1417, 124, 220 }, + { 180, -40, 135, 0, 29542, 124, 220 }, + { 180, -40, 180, 27667, 27667, 124, 220 }, { 180, -40, 215, 26208, 26208, 124, 220 }, - { 180, -40, 270, 23916, 23916, 124, 220 }, - { 180, -40, 315, 22041, 22041, 124, 220 }, - { 180, -40, 360, 20166, 20166, 124, 220 }, + { 180, -40, 270, 23917, 23917, 124, 220 }, + { 180, -40, 315, 22042, 22042, 124, 220 }, + { 180, -40, 360, 20167, 20167, 124, 220 }, { 180, 0, 0, 3500, 3500, 84, 180 }, { 180, 0, 45, 1625, 1625, 84, 180 }, { 180, 0, 90, 0, 29750, 84, 180 }, { 180, 0, 135, 0, 27875, 84, 180 }, { 180, 0, 180, 26000, 26000, 84, 180 }, - { 180, 0, 215, 24541, 24541, 84, 180 }, + { 180, 0, 215, 24542, 24542, 84, 180 }, { 180, 0, 270, 22250, 22250, 84, 180 }, { 180, 0, 315, 20375, 20375, 84, 180 }, { 180, 0, 360, 18500, 18500, 84, 180 }, @@ -410,21 +415,21 @@ void test_calc_ign_timeout_720() { 180, 40, 270, 20583, 20583, 44, 140 }, { 180, 40, 315, 18708, 18708, 44, 140 }, { 180, 40, 360, 16833, 16833, 44, 140 }, - { 270, -40, 0, 8916, 8916, 214, 310 }, - { 270, -40, 45, 7041, 7041, 214, 310 }, - { 270, -40, 90, 5166, 5166, 214, 310 }, - { 270, -40, 135, 3291, 3291, 214, 310 }, - { 270, -40, 180, 1416, 1416, 214, 310 }, + { 270, -40, 0, 8917, 8917, 214, 310 }, + { 270, -40, 45, 7042, 7042, 214, 310 }, + { 270, -40, 90, 5167, 5167, 214, 310 }, + { 270, -40, 135, 3292, 3292, 214, 310 }, + { 270, -40, 180, 1417, 1417, 214, 310 }, { 270, -40, 215, 0, 29958, 214, 310 }, - { 270, -40, 270, 27666, 27666, 214, 310 }, - { 270, -40, 315, 25791, 25791, 214, 310 }, - { 270, -40, 360, 23916, 23916, 214, 310 }, + { 270, -40, 270, 27667, 27667, 214, 310 }, + { 270, -40, 315, 25792, 25792, 214, 310 }, + { 270, -40, 360, 23917, 23917, 214, 310 }, { 270, 0, 0, 7250, 7250, 174, 270 }, { 270, 0, 45, 5375, 5375, 174, 270 }, { 270, 0, 90, 3500, 3500, 174, 270 }, { 270, 0, 135, 1625, 1625, 174, 270 }, { 270, 0, 180, 0, 29750, 174, 270 }, - { 270, 0, 215, 0, 28291, 174, 270 }, + { 270, 0, 215, 0, 28292, 174, 270 }, { 270, 0, 270, 26000, 26000, 174, 270 }, { 270, 0, 315, 24125, 24125, 174, 270 }, { 270, 0, 360, 22250, 22250, 174, 270 }, @@ -437,21 +442,21 @@ void test_calc_ign_timeout_720() { 270, 40, 270, 24333, 24333, 134, 230 }, { 270, 40, 315, 22458, 22458, 134, 230 }, { 270, 40, 360, 20583, 20583, 134, 230 }, - { 360, -40, 0, 12666, 12666, 304, 400 }, - { 360, -40, 45, 10791, 10791, 304, 400 }, - { 360, -40, 90, 8916, 8916, 304, 400 }, - { 360, -40, 135, 7041, 7041, 304, 400 }, - { 360, -40, 180, 5166, 5166, 304, 400 }, + { 360, -40, 0, 12667, 12667, 304, 400 }, + { 360, -40, 45, 10792, 10792, 304, 400 }, + { 360, -40, 90, 8917, 8917, 304, 400 }, + { 360, -40, 135, 7042, 7042, 304, 400 }, + { 360, -40, 180, 5167, 5167, 304, 400 }, { 360, -40, 215, 3708, 3708, 304, 400 }, - { 360, -40, 270, 1416, 1416, 304, 400 }, - { 360, -40, 315, 0, 29541, 304, 400 }, - { 360, -40, 360, 27666, 27666, 304, 400 }, + { 360, -40, 270, 1417, 1417, 304, 400 }, + { 360, -40, 315, 0, 29542, 304, 400 }, + { 360, -40, 360, 27667, 27667, 304, 400 }, { 360, 0, 0, 11000, 11000, 264, 360 }, { 360, 0, 45, 9125, 9125, 264, 360 }, { 360, 0, 90, 7250, 7250, 264, 360 }, { 360, 0, 135, 5375, 5375, 264, 360 }, { 360, 0, 180, 3500, 3500, 264, 360 }, - { 360, 0, 215, 2041, 2041, 264, 360 }, + { 360, 0, 215, 2042, 2042, 264, 360 }, { 360, 0, 270, 0, 29750, 264, 360 }, { 360, 0, 315, 0, 27875, 264, 360 }, { 360, 0, 360, 26000, 26000, 264, 360 }, @@ -464,21 +469,21 @@ void test_calc_ign_timeout_720() { 360, 40, 270, 0, 28083, 224, 320 }, { 360, 40, 315, 0, 26208, 224, 320 }, { 360, 40, 360, 24333, 24333, 224, 320 }, - { 432, -40, 0, 15666, 15666, 376, 472 }, - { 432, -40, 45, 13791, 13791, 376, 472 }, - { 432, -40, 90, 11916, 11916, 376, 472 }, - { 432, -40, 135, 10041, 10041, 376, 472 }, - { 432, -40, 180, 8166, 8166, 376, 472 }, + { 432, -40, 0, 15667, 15667, 376, 472 }, + { 432, -40, 45, 13792, 13792, 376, 472 }, + { 432, -40, 90, 11917, 11917, 376, 472 }, + { 432, -40, 135, 10042, 10042, 376, 472 }, + { 432, -40, 180, 8167, 8167, 376, 472 }, { 432, -40, 215, 6708, 6708, 376, 472 }, - { 432, -40, 270, 4416, 4416, 376, 472 }, - { 432, -40, 315, 2541, 2541, 376, 472 }, - { 432, -40, 360, 666, 666, 376, 472 }, + { 432, -40, 270, 4417, 4417, 376, 472 }, + { 432, -40, 315, 2542, 2542, 376, 472 }, + { 432, -40, 360, 667, 667, 376, 472 }, { 432, 0, 0, 14000, 14000, 336, 432 }, { 432, 0, 45, 12125, 12125, 336, 432 }, { 432, 0, 90, 10250, 10250, 336, 432 }, { 432, 0, 135, 8375, 8375, 336, 432 }, { 432, 0, 180, 6500, 6500, 336, 432 }, - { 432, 0, 215, 5041, 5041, 336, 432 }, + { 432, 0, 215, 5042, 5042, 336, 432 }, { 432, 0, 270, 2750, 2750, 336, 432 }, { 432, 0, 315, 875, 875, 336, 432 }, { 432, 0, 360, 0, 29000, 336, 432 }, @@ -491,21 +496,21 @@ void test_calc_ign_timeout_720() { 432, 40, 270, 1083, 1083, 296, 392 }, { 432, 40, 315, 0, 29208, 296, 392 }, { 432, 40, 360, 0, 27333, 296, 392 }, - { 576, -40, 0, 21666, 21666, 520, 616 }, - { 576, -40, 45, 19791, 19791, 520, 616 }, - { 576, -40, 90, 17916, 17916, 520, 616 }, - { 576, -40, 135, 16041, 16041, 520, 616 }, - { 576, -40, 180, 14166, 14166, 520, 616 }, + { 576, -40, 0, 21667, 21667, 520, 616 }, + { 576, -40, 45, 19792, 19792, 520, 616 }, + { 576, -40, 90, 17917, 17917, 520, 616 }, + { 576, -40, 135, 16042, 16042, 520, 616 }, + { 576, -40, 180, 14167, 14167, 520, 616 }, { 576, -40, 215, 12708, 12708, 520, 616 }, - { 576, -40, 270, 10416, 10416, 520, 616 }, - { 576, -40, 315, 8541, 8541, 520, 616 }, - { 576, -40, 360, 6666, 6666, 520, 616 }, + { 576, -40, 270, 10417, 10417, 520, 616 }, + { 576, -40, 315, 8542, 8542, 520, 616 }, + { 576, -40, 360, 6667, 6667, 520, 616 }, { 576, 0, 0, 20000, 20000, 480, 576 }, { 576, 0, 45, 18125, 18125, 480, 576 }, { 576, 0, 90, 16250, 16250, 480, 576 }, { 576, 0, 135, 14375, 14375, 480, 576 }, { 576, 0, 180, 12500, 12500, 480, 576 }, - { 576, 0, 215, 11041, 11041, 480, 576 }, + { 576, 0, 215, 11042, 11042, 480, 576 }, { 576, 0, 270, 8750, 8750, 480, 576 }, { 576, 0, 315, 6875, 6875, 480, 576 }, { 576, 0, 360, 5000, 5000, 480, 576 }, @@ -518,21 +523,21 @@ void test_calc_ign_timeout_720() { 576, 40, 270, 7083, 7083, 440, 536 }, { 576, 40, 315, 5208, 5208, 440, 536 }, { 576, 40, 360, 3333, 3333, 440, 536 }, - { 600, -40, 0, 22666, 22666, 544, 640 }, - { 600, -40, 45, 20791, 20791, 544, 640 }, - { 600, -40, 90, 18916, 18916, 544, 640 }, - { 600, -40, 135, 17041, 17041, 544, 640 }, - { 600, -40, 180, 15166, 15166, 544, 640 }, + { 600, -40, 0, 22667, 22667, 544, 640 }, + { 600, -40, 45, 20792, 20792, 544, 640 }, + { 600, -40, 90, 18917, 18917, 544, 640 }, + { 600, -40, 135, 17042, 17042, 544, 640 }, + { 600, -40, 180, 15167, 15167, 544, 640 }, { 600, -40, 215, 13708, 13708, 544, 640 }, - { 600, -40, 270, 11416, 11416, 544, 640 }, - { 600, -40, 315, 9541, 9541, 544, 640 }, - { 600, -40, 360, 7666, 7666, 544, 640 }, + { 600, -40, 270, 11417, 11417, 544, 640 }, + { 600, -40, 315, 9542, 9542, 544, 640 }, + { 600, -40, 360, 7667, 7667, 544, 640 }, { 600, 0, 0, 21000, 21000, 504, 600 }, { 600, 0, 45, 19125, 19125, 504, 600 }, { 600, 0, 90, 17250, 17250, 504, 600 }, { 600, 0, 135, 15375, 15375, 504, 600 }, { 600, 0, 180, 13500, 13500, 504, 600 }, - { 600, 0, 215, 12041, 12041, 504, 600 }, + { 600, 0, 215, 12042, 12042, 504, 600 }, { 600, 0, 270, 9750, 9750, 504, 600 }, { 600, 0, 315, 7875, 7875, 504, 600 }, { 600, 0, 360, 6000, 6000, 504, 600 }, diff --git a/test/test_schedule_calcs/test_inj_calcs.cpp b/test/test_schedule_calcs/test_inj_calcs.cpp index f32f7386..2326eb7b 100644 --- a/test/test_schedule_calcs/test_inj_calcs.cpp +++ b/test/test_schedule_calcs/test_inj_calcs.cpp @@ -28,19 +28,19 @@ static void test_calc_inj_timeout(const inj_test_parameters ¶meters) { static constexpr uint16_t injAngle = 355; char msg[150]; - uint16_t PWdivTimerPerDegree = div(parameters.pw, timePerDegree).quot; + uint16_t PWdivTimerPerDegree = timeToAngleDegPerMicroSec(parameters.pw); FuelSchedule schedule(FUEL2_COUNTER, FUEL2_COMPARE, nullInjCallback, nullInjCallback); schedule.Status = PENDING; uint16_t startAngle = calculateInjectorStartAngle(PWdivTimerPerDegree, parameters.channelAngle, injAngle); sprintf_P(msg, PSTR("PENDING channelAngle: %" PRIu16 ", pw: %" PRIu16 ", crankAngle: %" PRIu16 ", startAngle: %" PRIu16), parameters.channelAngle, parameters.pw, parameters.crankAngle, startAngle); - TEST_ASSERT_EQUAL_MESSAGE(parameters.pending, calculateInjectorTimeout(schedule, parameters.channelAngle, startAngle, parameters.crankAngle), msg); + TEST_ASSERT_INT32_WITHIN_MESSAGE(1, parameters.pending, calculateInjectorTimeout(schedule, parameters.channelAngle, startAngle, parameters.crankAngle), msg); schedule.Status = RUNNING; startAngle = calculateInjectorStartAngle( PWdivTimerPerDegree, parameters.channelAngle, injAngle); sprintf_P(msg, PSTR("RUNNING channelAngle: %" PRIu16 ", pw: %" PRIu16 ", crankAngle: %" PRIu16 ", startAngle: %" PRIu16), parameters.channelAngle, parameters.pw, parameters.crankAngle, startAngle); - TEST_ASSERT_EQUAL_MESSAGE(parameters.running, calculateInjectorTimeout(schedule, parameters.channelAngle, startAngle, parameters.crankAngle), msg); + TEST_ASSERT_INT32_WITHIN_MESSAGE(1, parameters.running, calculateInjectorTimeout(schedule, parameters.channelAngle, startAngle, parameters.crankAngle), msg); } @@ -59,89 +59,89 @@ static void test_calc_inj_timeout_360() { setEngineSpeed(4000, 360); + // Expected test values were generated using floating point calculations (in Excel) static const inj_test_parameters test_data[] PROGMEM = { - // ChannelAngle (deg), PW (uS), Crank (deg), Expected Pending (uS), Expected Running (uS) - { 0, 3000, 0, 11562, 11562 }, - { 0, 3000, 45, 9717, 9717 }, - { 0, 3000, 90, 7872, 7872 }, - { 0, 3000, 135, 6027, 6027 }, - { 0, 3000, 180, 4182, 4182 }, - { 0, 3000, 215, 2747, 2747 }, - { 0, 3000, 270, 492, 492 }, - { 0, 3000, 315, 0, 13407 }, - { 0, 3000, 360, 0, 11562 }, - { 72, 3000, 0, 0, 14514 }, - { 72, 3000, 45, 0, 12669 }, - { 72, 3000, 90, 10824, 10824 }, - { 72, 3000, 135, 8979, 8979 }, - { 72, 3000, 180, 7134, 7134 }, - { 72, 3000, 215, 5699, 5699 }, - { 72, 3000, 270, 3444, 3444 }, - { 72, 3000, 315, 1599, 1599 }, - { 72, 3000, 360, 0, 14514 }, - { 80, 3000, 0, 82, 82 }, - { 80, 3000, 45, 0, 12997 }, - { 80, 3000, 90, 11152, 11152 }, - { 80, 3000, 135, 9307, 9307 }, - { 80, 3000, 180, 7462, 7462 }, - { 80, 3000, 215, 6027, 6027 }, - { 80, 3000, 270, 3772, 3772 }, - { 80, 3000, 315, 1927, 1927 }, - { 80, 3000, 360, 82, 82 }, - { 90, 3000, 0, 492, 492 }, - { 90, 3000, 45, 0, 13407 }, - { 90, 3000, 90, 11562, 11562 }, - { 90, 3000, 135, 9717, 9717 }, - { 90, 3000, 180, 7872, 7872 }, - { 90, 3000, 215, 6437, 6437 }, - { 90, 3000, 270, 4182, 4182 }, - { 90, 3000, 315, 2337, 2337 }, - { 90, 3000, 360, 492, 492 }, - { 144, 3000, 0, 2706, 2706 }, - { 144, 3000, 45, 861, 861 }, - { 144, 3000, 90, 0, 13776 }, - { 144, 3000, 135, 0, 11931 }, - { 144, 3000, 180, 10086, 10086 }, - { 144, 3000, 215, 8651, 8651 }, - { 144, 3000, 270, 6396, 6396 }, - { 144, 3000, 315, 4551, 4551 }, - { 144, 3000, 360, 2706, 2706 }, - { 180, 3000, 0, 4182, 4182 }, - { 180, 3000, 45, 2337, 2337 }, - { 180, 3000, 90, 492, 492 }, - { 180, 3000, 135, 0, 13407 }, - { 180, 3000, 180, 11562, 11562 }, - { 180, 3000, 215, 10127, 10127 }, - { 180, 3000, 270, 7872, 7872 }, - { 180, 3000, 315, 6027, 6027 }, - { 180, 3000, 360, 4182, 4182 }, - { 240, 3000, 0, 6642, 6642 }, - { 240, 3000, 45, 4797, 4797 }, - { 240, 3000, 90, 2952, 2952 }, - { 240, 3000, 135, 1107, 1107 }, - { 240, 3000, 180, 0, 14022 }, - { 240, 3000, 215, 0, 12587 }, - { 240, 3000, 270, 10332, 10332 }, - { 240, 3000, 315, 8487, 8487 }, - { 240, 3000, 360, 6642, 6642 }, - { 270, 3000, 0, 7872, 7872 }, - { 270, 3000, 45, 6027, 6027 }, - { 270, 3000, 90, 4182, 4182 }, - { 270, 3000, 135, 2337, 2337 }, - { 270, 3000, 180, 492, 492 }, - { 270, 3000, 215, 0, 13817 }, - { 270, 3000, 270, 11562, 11562 }, - { 270, 3000, 315, 9717, 9717 }, - { 270, 3000, 360, 7872, 7872 }, - { 360, 3000, 0, 11562, 11562 }, - { 360, 3000, 45, 9717, 9717 }, - { 360, 3000, 90, 7872, 7872 }, - { 360, 3000, 135, 6027, 6027 }, - { 360, 3000, 180, 4182, 4182 }, - { 360, 3000, 215, 2747, 2747 }, - { 360, 3000, 270, 492, 492 }, - { 360, 3000, 315, 0, 13407 }, - { 360, 3000, 360, 11562, 11562 }, + { 0, 3000, 0, 11792, 11792 }, + { 0, 3000, 45, 9917, 9917 }, + { 0, 3000, 90, 8042, 8042 }, + { 0, 3000, 135, 6167, 6167 }, + { 0, 3000, 180, 4292, 4292 }, + { 0, 3000, 215, 2833, 2833 }, + { 0, 3000, 270, 542, 542 }, + { 0, 3000, 315, 0, 13667 }, + { 0, 3000, 360, 0, 11792 }, + { 72, 3000, 0, 0, 14792 }, + { 72, 3000, 45, 0, 12917 }, + { 72, 3000, 90, 11042, 11042 }, + { 72, 3000, 135, 9167, 9167 }, + { 72, 3000, 180, 7292, 7292 }, + { 72, 3000, 215, 5833, 5833 }, + { 72, 3000, 270, 3542, 3542 }, + { 72, 3000, 315, 1667, 1667 }, + { 72, 3000, 360, 0, 14792 }, + { 80, 3000, 0, 125, 125 }, + { 80, 3000, 45, 0, 13250 }, + { 80, 3000, 90, 11375, 11375 }, + { 80, 3000, 135, 9500, 9500 }, + { 80, 3000, 180, 7625, 7625 }, + { 80, 3000, 215, 6167, 6167 }, + { 80, 3000, 270, 3875, 3875 }, + { 80, 3000, 315, 2000, 2000 }, + { 80, 3000, 360, 125, 125 }, + { 90, 3000, 0, 542, 542 }, + { 90, 3000, 45, 0, 13667 }, + { 90, 3000, 90, 11792, 11792 }, + { 90, 3000, 135, 9917, 9917 }, + { 90, 3000, 180, 8042, 8042 }, + { 90, 3000, 215, 6583, 6583 }, + { 90, 3000, 270, 4292, 4292 }, + { 90, 3000, 315, 2417, 2417 }, + { 90, 3000, 360, 542, 542 }, + { 144, 3000, 0, 2792, 2792 }, + { 144, 3000, 45, 917, 917 }, + { 144, 3000, 90, 0, 14042 }, + { 144, 3000, 135, 0, 12167 }, + { 144, 3000, 180, 10292, 10292 }, + { 144, 3000, 215, 8833, 8833 }, + { 144, 3000, 270, 6542, 6542 }, + { 144, 3000, 315, 4667, 4667 }, + { 144, 3000, 360, 2792, 2792 }, + { 180, 3000, 0, 4292, 4292 }, + { 180, 3000, 45, 2417, 2417 }, + { 180, 3000, 90, 542, 542 }, + { 180, 3000, 135, 0, 13667 }, + { 180, 3000, 180, 11792, 11792 }, + { 180, 3000, 215, 10333, 10333 }, + { 180, 3000, 270, 8042, 8042 }, + { 180, 3000, 315, 6167, 6167 }, + { 180, 3000, 360, 4292, 4292 }, + { 240, 3000, 0, 6792, 6792 }, + { 240, 3000, 45, 4917, 4917 }, + { 240, 3000, 90, 3042, 3042 }, + { 240, 3000, 135, 1167, 1167 }, + { 240, 3000, 180, 0, 14292 }, + { 240, 3000, 215, 0, 12833 }, + { 240, 3000, 270, 10542, 10542 }, + { 240, 3000, 315, 8667, 8667 }, + { 240, 3000, 360, 6792, 6792 }, + { 270, 3000, 0, 8042, 8042 }, + { 270, 3000, 45, 6167, 6167 }, + { 270, 3000, 90, 4292, 4292 }, + { 270, 3000, 135, 2417, 2417 }, + { 270, 3000, 180, 542, 542 }, + { 270, 3000, 215, 0, 14083 }, + { 270, 3000, 270, 11792, 11792 }, + { 270, 3000, 315, 9917, 9917 }, + { 270, 3000, 360, 8042, 8042 }, + { 360, 3000, 0, 11792, 11792 }, + { 360, 3000, 45, 9917, 9917 }, + { 360, 3000, 90, 8042, 8042 }, + { 360, 3000, 135, 6167, 6167 }, + { 360, 3000, 180, 4292, 4292 }, + { 360, 3000, 215, 2833, 2833 }, + { 360, 3000, 270, 542, 542 }, + { 360, 3000, 315, 0, 13667 }, + { 360, 3000, 360, 11792, 11792 }, }; test_calc_inj_timeout(&test_data[0], &test_data[0]+_countof(test_data)); @@ -151,124 +151,127 @@ static void test_calc_inj_timeout_720() { setEngineSpeed(4000, 720); + // Expected test values were generated using floating point calculations (in Excel) static const inj_test_parameters test_data[] PROGMEM = { // ChannelAngle (deg), PW (uS), Crank (deg), Expected Pending (uS), Expected Running (uS) - { 0, 3000, 90, 7872, 7872 }, - { 0, 3000, 135, 6027, 6027 }, - { 0, 3000, 180, 4182, 4182 }, - { 0, 3000, 215, 2747, 2747 }, - { 0, 3000, 270, 492, 492 }, - { 0, 3000, 315, 0, 28167 }, - { 0, 3000, 360, 0, 26322 }, - { 72, 3000, 0, 0, 14514 }, - { 72, 3000, 45, 0, 12669 }, - { 72, 3000, 90, 10824, 10824 }, - { 72, 3000, 135, 8979, 8979 }, - { 72, 3000, 180, 7134, 7134 }, - { 72, 3000, 215, 5699, 5699 }, - { 72, 3000, 270, 3444, 3444 }, - { 72, 3000, 315, 1599, 1599 }, - { 72, 3000, 360, 0, 29274 }, - { 80, 3000, 0, 0, 14842 }, - { 80, 3000, 45, 0, 12997 }, - { 80, 3000, 90, 11152, 11152 }, - { 80, 3000, 135, 9307, 9307 }, - { 80, 3000, 180, 7462, 7462 }, - { 80, 3000, 215, 6027, 6027 }, - { 80, 3000, 270, 3772, 3772 }, - { 80, 3000, 315, 1927, 1927 }, - { 80, 3000, 360, 82, 82 }, - { 90, 3000, 0, 0, 15252 }, - { 90, 3000, 45, 0, 13407 }, - { 90, 3000, 90, 11562, 11562 }, - { 90, 3000, 135, 9717, 9717 }, - { 90, 3000, 180, 7872, 7872 }, - { 90, 3000, 215, 6437, 6437 }, - { 90, 3000, 270, 4182, 4182 }, - { 90, 3000, 315, 2337, 2337 }, - { 90, 3000, 360, 492, 492 }, - { 144, 3000, 0, 0, 17466 }, - { 144, 3000, 45, 0, 15621 }, - { 144, 3000, 90, 0, 13776 }, - { 144, 3000, 135, 0, 11931 }, - { 144, 3000, 180, 10086, 10086 }, - { 144, 3000, 215, 8651, 8651 }, - { 144, 3000, 270, 6396, 6396 }, - { 144, 3000, 315, 4551, 4551 }, - { 144, 3000, 360, 2706, 2706 }, - { 180, 3000, 0, 0, 18942 }, - { 180, 3000, 45, 0, 17097 }, - { 180, 3000, 90, 0, 15252 }, - { 180, 3000, 135, 0, 13407 }, - { 180, 3000, 180, 11562, 11562 }, - { 180, 3000, 215, 10127, 10127 }, - { 180, 3000, 270, 7872, 7872 }, - { 180, 3000, 315, 6027, 6027 }, - { 180, 3000, 360, 4182, 4182 }, - { 240, 3000, 0, 0, 21402 }, - { 240, 3000, 45, 0, 19557 }, - { 240, 3000, 90, 0, 17712 }, - { 240, 3000, 135, 0, 15867 }, - { 240, 3000, 180, 0, 14022 }, - { 240, 3000, 215, 0, 12587 }, - { 240, 3000, 270, 10332, 10332 }, - { 240, 3000, 315, 8487, 8487 }, - { 240, 3000, 360, 6642, 6642 }, - { 270, 3000, 0, 0, 22632 }, - { 270, 3000, 45, 0, 20787 }, - { 270, 3000, 90, 0, 18942 }, - { 270, 3000, 135, 0, 17097 }, - { 270, 3000, 180, 0, 15252 }, - { 270, 3000, 215, 0, 13817 }, - { 270, 3000, 270, 11562, 11562 }, - { 270, 3000, 315, 9717, 9717 }, - { 270, 3000, 360, 7872, 7872 }, - { 360, 3000, 0, 0, 26322 }, - { 360, 3000, 45, 0, 24477 }, - { 360, 3000, 90, 0, 22632 }, - { 360, 3000, 135, 0, 20787 }, - { 360, 3000, 180, 0, 18942 }, - { 360, 3000, 215, 0, 17507 }, - { 360, 3000, 270, 0, 15252 }, - { 360, 3000, 315, 0, 13407 }, - { 360, 3000, 360, 11562, 11562 }, - { 480, 3000, 0, 1722, 1722 }, - { 480, 3000, 45, 0, 29397 }, - { 480, 3000, 90, 0, 27552 }, - { 480, 3000, 135, 0, 25707 }, - { 480, 3000, 180, 0, 23862 }, - { 480, 3000, 215, 0, 22427 }, - { 480, 3000, 270, 0, 20172 }, - { 480, 3000, 315, 0, 18327 }, - { 480, 3000, 360, 0, 16482 }, - { 540, 3000, 0, 4182, 4182 }, - { 540, 3000, 45, 2337, 2337 }, - { 540, 3000, 90, 492, 492 }, - { 540, 3000, 135, 0, 28167 }, - { 540, 3000, 180, 0, 26322 }, - { 540, 3000, 215, 0, 24887 }, - { 540, 3000, 270, 0, 22632 }, - { 540, 3000, 315, 0, 20787 }, - { 540, 3000, 360, 0, 18942 }, - { 600, 3000, 0, 6642, 6642 }, - { 600, 3000, 45, 4797, 4797 }, - { 600, 3000, 90, 2952, 2952 }, - { 600, 3000, 135, 1107, 1107 }, - { 600, 3000, 180, 0, 28782 }, - { 600, 3000, 215, 0, 27347 }, - { 600, 3000, 270, 0, 25092 }, - { 600, 3000, 315, 0, 23247 }, - { 600, 3000, 360, 0, 21402 }, - { 630, 3000, 0, 7872, 7872 }, - { 630, 3000, 45, 6027, 6027 }, - { 630, 3000, 90, 4182, 4182 }, - { 630, 3000, 135, 2337, 2337 }, - { 630, 3000, 180, 492, 492 }, - { 630, 3000, 215, 0, 28577 }, - { 630, 3000, 270, 0, 26322 }, - { 630, 3000, 315, 0, 24477 }, - { 630, 3000, 360, 0, 22632 }, - }; + { 0, 3000, 0, 11792, 11792 }, + { 0, 3000, 45, 9917, 9917 }, + { 0, 3000, 90, 8042, 8042 }, + { 0, 3000, 135, 6167, 6167 }, + { 0, 3000, 180, 4292, 4292 }, + { 0, 3000, 215, 2833, 2833 }, + { 0, 3000, 270, 542, 542 }, + { 0, 3000, 315, 0, 28667 }, + { 0, 3000, 360, 0, 26792 }, + { 72, 3000, 0, 0, 14792 }, + { 72, 3000, 45, 0, 12917 }, + { 72, 3000, 90, 11042, 11042 }, + { 72, 3000, 135, 9167, 9167 }, + { 72, 3000, 180, 7292, 7292 }, + { 72, 3000, 215, 5833, 5833 }, + { 72, 3000, 270, 3542, 3542 }, + { 72, 3000, 315, 1667, 1667 }, + { 72, 3000, 360, 0, 29792 }, + { 80, 3000, 0, 0, 15125 }, + { 80, 3000, 45, 0, 13250 }, + { 80, 3000, 90, 11375, 11375 }, + { 80, 3000, 135, 9500, 9500 }, + { 80, 3000, 180, 7625, 7625 }, + { 80, 3000, 215, 6167, 6167 }, + { 80, 3000, 270, 3875, 3875 }, + { 80, 3000, 315, 2000, 2000 }, + { 80, 3000, 360, 125, 125 }, + { 90, 3000, 0, 0, 15542 }, + { 90, 3000, 45, 0, 13667 }, + { 90, 3000, 90, 11792, 11792 }, + { 90, 3000, 135, 9917, 9917 }, + { 90, 3000, 180, 8042, 8042 }, + { 90, 3000, 215, 6583, 6583 }, + { 90, 3000, 270, 4292, 4292 }, + { 90, 3000, 315, 2417, 2417 }, + { 90, 3000, 360, 542, 542 }, + { 144, 3000, 0, 0, 17792 }, + { 144, 3000, 45, 0, 15917 }, + { 144, 3000, 90, 0, 14042 }, + { 144, 3000, 135, 0, 12167 }, + { 144, 3000, 180, 10292, 10292 }, + { 144, 3000, 215, 8833, 8833 }, + { 144, 3000, 270, 6542, 6542 }, + { 144, 3000, 315, 4667, 4667 }, + { 144, 3000, 360, 2792, 2792 }, + { 180, 3000, 0, 0, 19292 }, + { 180, 3000, 45, 0, 17417 }, + { 180, 3000, 90, 0, 15542 }, + { 180, 3000, 135, 0, 13667 }, + { 180, 3000, 180, 11792, 11792 }, + { 180, 3000, 215, 10333, 10333 }, + { 180, 3000, 270, 8042, 8042 }, + { 180, 3000, 315, 6167, 6167 }, + { 180, 3000, 360, 4292, 4292 }, + { 240, 3000, 0, 0, 21792 }, + { 240, 3000, 45, 0, 19917 }, + { 240, 3000, 90, 0, 18042 }, + { 240, 3000, 135, 0, 16167 }, + { 240, 3000, 180, 0, 14292 }, + { 240, 3000, 215, 0, 12833 }, + { 240, 3000, 270, 10542, 10542 }, + { 240, 3000, 315, 8667, 8667 }, + { 240, 3000, 360, 6792, 6792 }, + { 270, 3000, 0, 0, 23042 }, + { 270, 3000, 45, 0, 21167 }, + { 270, 3000, 90, 0, 19292 }, + { 270, 3000, 135, 0, 17417 }, + { 270, 3000, 180, 0, 15542 }, + { 270, 3000, 215, 0, 14083 }, + { 270, 3000, 270, 11792, 11792 }, + { 270, 3000, 315, 9917, 9917 }, + { 270, 3000, 360, 8042, 8042 }, + { 360, 3000, 0, 0, 26792 }, + { 360, 3000, 45, 0, 24917 }, + { 360, 3000, 90, 0, 23042 }, + { 360, 3000, 135, 0, 21167 }, + { 360, 3000, 180, 0, 19292 }, + { 360, 3000, 215, 0, 17833 }, + { 360, 3000, 270, 0, 15542 }, + { 360, 3000, 315, 0, 13667 }, + { 360, 3000, 360, 11792, 11792 }, + { 480, 3000, 0, 1792, 1792 }, + { 480, 3000, 45, 0, 29917 }, + { 480, 3000, 90, 0, 28042 }, + { 480, 3000, 135, 0, 26167 }, + { 480, 3000, 180, 0, 24292 }, + { 480, 3000, 215, 0, 22833 }, + { 480, 3000, 270, 0, 20542 }, + { 480, 3000, 315, 0, 18667 }, + { 480, 3000, 360, 0, 16792 }, + { 540, 3000, 0, 4292, 4292 }, + { 540, 3000, 45, 2417, 2417 }, + { 540, 3000, 90, 542, 542 }, + { 540, 3000, 135, 0, 28667 }, + { 540, 3000, 180, 0, 26792 }, + { 540, 3000, 215, 0, 25333 }, + { 540, 3000, 270, 0, 23042 }, + { 540, 3000, 315, 0, 21167 }, + { 540, 3000, 360, 0, 19292 }, + { 600, 3000, 0, 6792, 6792 }, + { 600, 3000, 45, 4917, 4917 }, + { 600, 3000, 90, 3042, 3042 }, + { 600, 3000, 135, 1167, 1167 }, + { 600, 3000, 180, 0, 29292 }, + { 600, 3000, 215, 0, 27833 }, + { 600, 3000, 270, 0, 25542 }, + { 600, 3000, 315, 0, 23667 }, + { 600, 3000, 360, 0, 21792 }, + { 630, 3000, 0, 8042, 8042 }, + { 630, 3000, 45, 6167, 6167 }, + { 630, 3000, 90, 4292, 4292 }, + { 630, 3000, 135, 2417, 2417 }, + { 630, 3000, 180, 542, 542 }, + { 630, 3000, 215, 0, 29083 }, + { 630, 3000, 270, 0, 26792 }, + { 630, 3000, 315, 0, 24917 }, + { 630, 3000, 360, 0, 23042 }, + }; test_calc_inj_timeout(&test_data[0], &test_data[0]+_countof(test_data)); } diff --git a/test/timer.hpp b/test/timer.hpp new file mode 100644 index 00000000..0f7fd46c --- /dev/null +++ b/test/timer.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include + +#if defined(__AVR__) +#include +#else +#include +#endif + +#if !defined(MILLIS_PER_SEC) +#define MILLIS_PER_SEC 1000ULL +#endif +#if !defined(MICROS_PER_SEC) +#define MICROS_PER_SEC (MILLIS_PER_SEC*1000) +#endif +#if !defined(NANOS_PER_SEC) +#define NANOS_PER_SEC (MICROS_PER_SEC*1000) +#endif + +class timer { +private: +#if defined(__AVR__) + uint32_t start_time; + uint32_t end_time; +#else + struct timeval start_time; + struct timeval end_time; +#endif + +public: + + timer() { + } + + void start() { +#if defined(__AVR__) + start_time = micros(); +#else + gettimeofday(&start_time, NULL); +#endif + } + + void stop() { +#if defined(__AVR__) + end_time = micros(); +#else + gettimeofday(&end_time, NULL); +#endif + } + + uint32_t duration_micros() { +#if defined(__AVR__) + return end_time-start_time; +#else + return (uint32_t)(((end_time.tv_sec - start_time.tv_sec) * MICROS_PER_SEC) + (end_time.tv_usec - start_time.tv_usec)); +#endif + } +}; + +template +void measure_executiontime(uint16_t iterations, TLoop from, TLoop to, TLoop step, timer &measure, TParam param, void (*pTestFun)(TLoop, TParam)) { + measure.start(); + for (uint16_t loop=0; loop +struct execution_time { + TParam result; + uint32_t durationMicros; +}; +template +struct comparative_execution_times { + execution_time timeA; + execution_time timeB; +}; +template +comparative_execution_times compare_executiontime(uint16_t iterations, TLoop from, TLoop to, TLoop step, void (*pTestFunA)(TLoop, TParam&), void (*pTestFunB)(TLoop, TParam&)) { + + timer timerA; + TParam paramA = 0; + measure_executiontime(iterations, from, to, step, timerA, paramA, pTestFunA); + + timer timerB; + TParam paramB = 0; + measure_executiontime(iterations, from, to, step, timerB, paramB, pTestFunB); + + char buffer[128]; + sprintf(buffer, "Timing: %" PRIu32 ", %" PRIu32, timerA.duration_micros(), timerB.duration_micros()); + TEST_MESSAGE(buffer); + + return comparative_execution_times { + .timeA = execution_time { .result = paramA, .durationMicros = timerA.duration_micros()}, + .timeB = execution_time { .result = paramB, .durationMicros = timerB.duration_micros()} + }; +} \ No newline at end of file