speeduino/speeduino/crankMaths.h

94 lines
2.7 KiB
C
Raw Normal View History

2018-07-19 06:26:31 -07:00
#ifndef CRANKMATHS_H
#define CRANKMATHS_H
Save 600+ bytes RAM (step 3 of 9) - remove per-channel schedule calculation functions, use generic equivalents instead (#1018) * calculateInjector1Timeout proxies calculateInjectorNTimeout Unit test still pass. * Remove calculateInjector1Timeout, Replace with call to calculateInjectorTimeout All unit tests still pass. * Remove unecassary unit test * calculateIgnition1Timeout proxies calculateIgnitionNTimeout Unit test all pass * Remove calculateIgnition1Timeout, Replace with call to calculateIgnitionTimeout All unit tests still pass. * Remove unecassary unit test * calculateIgnitionAngle\d proxies calculateIgnitionAngle Unit tests all pass * Remove calculateIgnitionAngle\d Just call calculateIgnitionAngle() directly * Route rotary ignition angle calcs through generic calculateIgnitionTrailingRotary function. * Remove calculateIgnitionAngle\d Just call calculateIgnitionTrailingRotary() directly * calculateInjectorStartAngle: inject all parameters Do not rely on global state Easier unit testing. * calculateIgnitionAngle: inject all parameters Do not rely on global state Easier unit testing. * Pull all ignition global vars into schedule_calcs * Conditional compile for schedule calc vars Saves memory * Optimize: break out angleToTimeIntervalRev from angleToTime. The new function will always be inlined. * Optimize - simpler code path when no channel offset (zero) Some optimization of arithmetic operations. * Fix unit tests * Fix bug on injector angle calculations Need while loops to correctly bring intermediate values into range --------- Co-authored-by: Josh Stewart <josh@noisymime.org>
2023-06-25 19:13:53 -07:00
#include "maths.h"
#include "globals.h"
Save 600+ bytes RAM (step 3 of 9) - remove per-channel schedule calculation functions, use generic equivalents instead (#1018) * calculateInjector1Timeout proxies calculateInjectorNTimeout Unit test still pass. * Remove calculateInjector1Timeout, Replace with call to calculateInjectorTimeout All unit tests still pass. * Remove unecassary unit test * calculateIgnition1Timeout proxies calculateIgnitionNTimeout Unit test all pass * Remove calculateIgnition1Timeout, Replace with call to calculateIgnitionTimeout All unit tests still pass. * Remove unecassary unit test * calculateIgnitionAngle\d proxies calculateIgnitionAngle Unit tests all pass * Remove calculateIgnitionAngle\d Just call calculateIgnitionAngle() directly * Route rotary ignition angle calcs through generic calculateIgnitionTrailingRotary function. * Remove calculateIgnitionAngle\d Just call calculateIgnitionTrailingRotary() directly * calculateInjectorStartAngle: inject all parameters Do not rely on global state Easier unit testing. * calculateIgnitionAngle: inject all parameters Do not rely on global state Easier unit testing. * Pull all ignition global vars into schedule_calcs * Conditional compile for schedule calc vars Saves memory * Optimize: break out angleToTimeIntervalRev from angleToTime. The new function will always be inlined. * Optimize - simpler code path when no channel offset (zero) Some optimization of arithmetic operations. * Fix unit tests * Fix bug on injector angle calculations Need while loops to correctly bring intermediate values into range --------- Co-authored-by: Josh Stewart <josh@noisymime.org>
2023-06-25 19:13:53 -07:00
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
2023-11-05 14:10:08 -08:00
/**
* @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)
{
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
2023-11-05 14:10:08 -08:00
return nudge(0, CRANK_ANGLE_MAX_IGN, angle, CRANK_ANGLE_MAX_IGN);
}
/**
* @brief Makes one pass at nudging the angle to within [0,CRANK_ANGLE_MAX_INJ]
*
* @param angle A crank angle in degrees
* @return int16_t
*/
static inline int16_t injectorLimits(int16_t angle)
{
int16_t tempAngle = angle;
if(tempAngle < 0) { tempAngle = tempAngle + CRANK_ANGLE_MAX_INJ; }
while(tempAngle > CRANK_ANGLE_MAX_INJ ) { tempAngle -= CRANK_ANGLE_MAX_INJ; }
return tempAngle;
}
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
2023-11-05 14:10:08 -08:00
/** @brief At 1 RPM, each degree of angular rotation takes this many microseconds */
#define MICROS_PER_DEG_1_RPM INT32_C(166667)
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
2023-11-05 14:10:08 -08:00
/** @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)
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
2023-11-05 14:10:08 -08:00
/** @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)
2018-09-03 22:13:14 -07:00
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
2023-11-05 14:10:08 -08:00
/**
* @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);
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
2023-11-05 14:10:08 -08:00
/** @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);
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
2023-11-05 14:10:08 -08:00
/** @brief Converts based on the time interval between the 2 most recently detected decoder teeth
*
* Inverse of angleToTimeIntervalTooth
*/
uint16_t timeToAngleIntervalTooth(uint32_t time);
///@}
2018-07-19 06:26:31 -07:00
#endif