Skip to content

Commit

Permalink
revolutionTime should be owned by crank math module.
Browse files Browse the repository at this point in the history
  • Loading branch information
adbancroft committed Dec 9, 2024
1 parent ec73f6c commit 01baea9
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 179 deletions.
2 changes: 1 addition & 1 deletion speeduino/comms_CAN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ void DashMessage(uint16_t DashMessageID)
case CAN_HALTECH_DATA3:
temp_Advance = currentStatus.advance * 10U; //Note: Signed value
//Convert PW into duty cycle
temp_DutyCycle = (fuelSchedule1.pw * 100UL * currentStatus.nSquirts) / revolutionTime;
temp_DutyCycle = (fuelSchedule1.pw * 100UL * currentStatus.nSquirts) / getRevolutionTime(currentStatus);
if (configPage2.strokes == FOUR_STROKE) { temp_DutyCycle = temp_DutyCycle / 2U; }

outMsg.len = 8;
Expand Down
7 changes: 4 additions & 3 deletions speeduino/corrections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ There are 2 top level functions that call more detailed corrections for Fuel and
#include "maths.h"
#include "sensors.h"
#include "src/PID_v1/PID_v1.h"
#include "crankMaths.h"

long PID_O2, PID_output, PID_AFRTarget;
/** Instance of the PID object in case that algorithm is used (Always instantiated).
Expand Down Expand Up @@ -1096,11 +1097,11 @@ uint16_t correctionsDwell(uint16_t dwell)
pulsesPerRevolution = (configPage2.nCylinders >> 1);
dwellPerRevolution = dwellPerRevolution * pulsesPerRevolution;
}
if(dwellPerRevolution > revolutionTime)
if(dwellPerRevolution > getRevolutionTime(currentStatus))
{
//Possibly need some method of reducing spark duration here as well, but this is a start
uint16_t adjustedSparkDur = udiv_32_16(sparkDur_uS * revolutionTime, dwellPerRevolution);
tempDwell = udiv_32_16(revolutionTime, (uint16_t)pulsesPerRevolution) - adjustedSparkDur;
uint16_t adjustedSparkDur = udiv_32_16(sparkDur_uS * getRevolutionTime(currentStatus), dwellPerRevolution);
tempDwell = udiv_32_16(getRevolutionTime(currentStatus), (uint16_t)pulsesPerRevolution) - adjustedSparkDur;
}

return tempDwell;
Expand Down
33 changes: 23 additions & 10 deletions speeduino/crankMaths.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#include "globals.h"
#include "crankMaths.h"
#include "bit_shifts.h"

Expand All @@ -13,23 +12,37 @@ int rpmDelta;
typedef uint32_t UQ24X8_t;
static constexpr uint8_t UQ24X8_Shift = 8U;

/** @brief uS per degree at current RPM in UQ24.8 fixed point */
/** @brief µS per degree at current RPM in UQ24.8 fixed point
*
* Ranges between
* * 1040649 (4065.039 µS/°) at MIN_RPM/MIN_REVOLUTION_TIME
* * 2370 (9.258333 µS/°) at MAX_RPM/MAX_REVOLUTION_TIME
*/
static UQ24X8_t microsPerDegree;
static constexpr uint8_t microsPerDegree_Shift = UQ24X8_Shift;

typedef uint16_t UQ1X15_t;
static constexpr uint8_t UQ1X15_Shift = 15U;

/** @brief Degrees per uS in UQ1.15 fixed point.
/** @brief Degrees per µS in UQ1.15 fixed point.
*
* Ranges from 8 (0.000246) at MIN_RPM to 3542 (0.108) at MAX_RPM
* Ranges between
* * 8 (0.000246 °/µS) at MIN_RPM/MIN_REVOLUTION_TIME
* * 3539 (0.108011 °/µS) at MAX_RPM/MAX_REVOLUTION_TIME
*/
static UQ1X15_t degreesPerMicro;
static constexpr uint8_t degreesPerMicro_Shift = UQ1X15_Shift;

void setAngleConverterRevolutionTime(uint32_t revolutionTime) {
microsPerDegree = div360(lshift<microsPerDegree_Shift>(revolutionTime));
degreesPerMicro = (uint16_t)UDIV_ROUND_CLOSEST(lshift<degreesPerMicro_Shift>(UINT32_C(360)), revolutionTime, uint32_t);
bool setRevolutionTime(uint32_t revTime, statuses &current) {
bool hasChanged = (revTime!=current.revolutionTime) && (revTime>=MIN_REVOLUTION_TIME) && (revTime<MAX_REVOLUTION_TIME);

if (hasChanged) {
current.revolutionTime = revTime;
microsPerDegree = div360(lshift<microsPerDegree_Shift>(revTime));
degreesPerMicro = (uint16_t)UDIV_ROUND_CLOSEST(lshift<degreesPerMicro_Shift>(UINT32_C(360)), revTime, uint32_t);
}

return hasChanged;
}

uint32_t angleToTimeMicroSecPerDegree(uint16_t angle) {
Expand All @@ -38,8 +51,8 @@ uint32_t angleToTimeMicroSecPerDegree(uint16_t angle) {
}

uint16_t timeToAngleDegPerMicroSec(uint32_t time) {
uint32_t degFixed = time * (uint32_t)degreesPerMicro;
return rshift_round<degreesPerMicro_Shift>(degFixed);
uint32_t degFixed = time * (uint32_t)degreesPerMicro;
return rshift_round<degreesPerMicro_Shift>(degFixed);
}

#if SECOND_DERIV_ENABLED!=0
Expand Down Expand Up @@ -83,4 +96,4 @@ 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
}
}
#endif
#endif
109 changes: 79 additions & 30 deletions speeduino/crankMaths.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,45 @@
#ifndef CRANKMATHS_H
#define CRANKMATHS_H

/**
* @file
*
* @brief Crank revolution based mathemtical functions.
*
*/

#include "maths.h"
#include "globals.h"

/** @brief At 1 RPM, each degree of angular rotation takes this many microseconds */
static constexpr uint32_t MICROS_PER_DEG_1_RPM = UDIV_ROUND_CLOSEST(MICROS_PER_MIN, 360UL, uint32_t);

/** @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
**/
static constexpr uint16_t MAX_RPM = 18000U;

/** @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
* time --> angle conversion factor (degreesPerMicro)
*/
static constexpr uint16_t MIN_RPM = (uint16_t)UDIV_ROUND_UP(MICROS_PER_DEG_1_RPM, (uint32_t)UINT16_MAX/16UL, uint32_t);

/**
* @brief Minumum time in µS that one crank revolution can take.
*
* @note: many calculations are done over 2 revolutions (cycles), in which case this would be doubled
*/
static constexpr uint16_t MIN_REVOLUTION_TIME = MICROS_PER_MIN/MAX_RPM;

/**
* @brief Maximum time in µS that one crank revolution can take.
*
* @note: many calculations are done over 2 revolutions (cycles), in which case this would be doubled
*/
static constexpr uint32_t MAX_REVOLUTION_TIME = MICROS_PER_MIN/MIN_RPM;

/**
* @brief Makes one pass at nudging the angle to within [0,CRANK_ANGLE_MAX_IGN]
Expand All @@ -20,59 +57,71 @@ static inline int16_t ignitionLimits(int16_t angle) {
* @param angle A crank angle in degrees
* @return int16_t
*/
static inline int16_t injectorLimits(int16_t angle)
static inline uint16_t injectorLimits(uint16_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;
while(angle > (uint16_t)CRANK_ANGLE_MAX_INJ ) { angle -= (uint16_t)CRANK_ANGLE_MAX_INJ; }
return angle;
}

/** @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.
/**
* @brief Set the revolution time, from which the degree<-->angle conversions are derived
*
* 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
* If the return value is true, then the calculated RPM (@ref rpmFromRevolutionTime) and
* degree<-->angle conversions (@ref angleToTimeMicroSecPerDegree, @ref timeToAngleDegPerMicroSec) will change.
*
* 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)
* @param revTime The crank revolution time in µS. *Must be between MIN_REVOLUTION_TIME and MAX_REVOLUTION_TIME*
* @return true If the revolution time has changed
* @return false If the new time is the same as the current revolution time.
*/
bool setRevolutionTime(uint32_t revTime, statuses &current);

/**
* @brief Set the revolution time, from which some of the degree<-->angle conversions are derived
* @brief Get the latest revolution time
*
* @param revolutionTime The crank revolution time.
* @return uint32_t The revolution time, limited to [MIN_REVOLUTION_TIME, MAX_REVOLUTION_TIME)
*/
void setAngleConverterRevolutionTime(uint32_t revolutionTime);
static inline uint32_t getRevolutionTime(const statuses &current) {
return current.revolutionTime;
}


/**
* @name Converts angular degrees to the time interval that amount of rotation
* will take at current RPM.
* @brief Converts angular degrees to the time interval that amount of rotation
* will take at the current crank revolution time (@ref setRevolutionTime).
*
* Based on angle of [0,720] and min/max RPM, result ranges from
* 9 (MAX_RPM, 1 deg) to 2926828 (MIN_RPM, 720 deg)
* 9 (MAX_RPM, 1 deg) to 2926828 (MIN_RPM, 720 deg). I.e. 24 bits
*
* @param angle Angle in degrees
* @return Time interval in uS
* @return Time interval in µS
*/
uint32_t angleToTimeMicroSecPerDegree(uint16_t angle);

/**
* @name Converts a time interval in microsecods to the equivalent degrees of angular (crank)
* rotation at current RPM.
* @brief Converts a time interval in µS to the equivalent degrees of angular (crank)
* rotation at the current crank revolution time (@ref setRevolutionTime).
*
* Inverse of angleToTimeMicroSecPerDegree
*
* @param time Time interval in uS
* @param time Time interval in µS
* @return Angle in degrees
*/
uint16_t timeToAngleDegPerMicroSec(uint32_t time);

/** @brief Calculate RPM based on the current crank revolution time (@ref setRevolutionTime). */

/**
* @brief Calculate RPM based on the revolution time
*
* @param revTime Time for one 360° rotation in µS. Typically the result of a call to getRevolutionTime()
* @return uint16_t
*/
static inline uint16_t rpmFromRevolutionTime(uint32_t revTime) {
if (revTime<=(uint32_t)UINT16_MAX) {
return udiv_32_16_closest(MICROS_PER_MIN, revTime);
} else {
return UDIV_ROUND_CLOSEST(MICROS_PER_MIN, revTime, uint32_t);
}
}

#endif
Loading

0 comments on commit 01baea9

Please sign in to comment.