Multispark implementation (#1192)

* multispark prototype

* only multispark if not cut

* typo

* do some reasonableish math

* consume cfg

* todo

* bad merge

* move math out of engine2.cpp

* engine state config

* consume generated

* use new

* hand generate

* doc, debugging

* debug channel names

* don't depend on rpmcalc

* safer, fix math

* tests

* default multispark config

* remove todo

Co-authored-by: Matthew Kennedy <makenne@microsoft.com>
This commit is contained in:
Matthew Kennedy 2020-03-25 22:49:36 -07:00 committed by GitHub
parent 608cf45d26
commit a5d4d06868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 194 additions and 18 deletions

View File

@ -163,9 +163,10 @@ angle_t getAdvanceCorrections(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
tsOutputChannels.debugFloatField2 = engine->engineState.cltTimingCorrection;
tsOutputChannels.debugFloatField3 = engine->fsioState.fsioTimingAdjustment;
tsOutputChannels.debugFloatField4 = pidTimingCorrection;
tsOutputChannels.debugIntField1 = engine->engineState.multispark.count;
#endif /* EFI_TUNER_STUDIO */
}
return iatCorrection
+ engine->fsioState.fsioTimingAdjustment
+ engine->engineState.cltTimingCorrection
@ -231,6 +232,43 @@ angle_t getAdvance(int rpm, float engineLoad DECLARE_ENGINE_PARAMETER_SUFFIX) {
#endif
}
size_t getMultiSparkCount(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
// Compute multispark (if enabled)
if (CONFIG(multisparkEnable)
&& rpm <= CONFIG(multisparkMaxRpm)
&& CONFIG(multisparkMaxExtraSparkCount) > 0) {
// For zero RPM, disable multispark. We don't yet know the engine speed, so multispark may not be safe.
if (rpm == 0) {
return 0;
}
floatus_t multiDelay = CONFIG(multisparkSparkDuration);
floatus_t multiDwell = CONFIG(multisparkDwell);
ENGINE(engineState.multispark.delay) = US2NT(multiDelay);
ENGINE(engineState.multispark.dwell) = US2NT(multiDwell);
constexpr float usPerDegreeAt1Rpm = 60e6 / 360;
floatus_t usPerDegree = usPerDegreeAt1Rpm / rpm;
// How long is there for sparks? The user configured an angle, convert to time.
floatus_t additionalSparksUs = usPerDegree * CONFIG(multisparkMaxSparkingAngle);
// How long does one spark take?
floatus_t oneSparkTime = multiDelay + multiDwell;
// How many sparks can we fit in the alloted time?
float sparksFitInTime = additionalSparksUs / oneSparkTime;
// Take the floor (convert to uint8_t) - we want to undershoot, not overshoot
uint32_t floored = sparksFitInTime;
// Allow no more than the maximum number of extra sparks
return minI(floored, CONFIG(multisparkMaxExtraSparkCount));
} else {
return 0;
}
}
void setDefaultIatTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
setLinearCurve(config->ignitionIatCorrLoadBins, /*from*/CLT_CURVE_RANGE_FROM, 110, 1);
#if IGN_LOAD_COUNT == DEFAULT_IGN_LOAD_COUNT

View File

@ -16,3 +16,4 @@ float getTopAdvanceForBore(chamber_style_e style, int octane, double compression
float getInitialAdvance(int rpm, float map, float advanceMax);
void buildTimingMap(float advanceMax DECLARE_CONFIG_PARAMETER_SUFFIX);
angle_t getAdvanceCorrections(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
size_t getMultiSparkCount(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);

View File

@ -197,6 +197,8 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE);
timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER_SUFFIX);
multispark.count = getMultiSparkCount(rpm PASS_ENGINE_PARAMETER_SUFFIX);
if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) {
float tps = getTPS(PASS_ENGINE_PARAMETER_SIGNATURE);
updateTChargeK(rpm, tps PASS_ENGINE_PARAMETER_SUFFIX);

View File

@ -620,6 +620,17 @@ int getTargetRpmForIdleCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return targetRpm + engine->fsioState.fsioIdleTargetRPMAdjustment;
}
void setDefaultMultisparkParameters(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
// 1ms spark + 2ms dwell
engineConfiguration->multisparkSparkDuration = 1000;
engineConfiguration->multisparkDwell = 2000;
// Conservative defaults - probably won't blow up coils
engineConfiguration->multisparkMaxRpm = 1500;
engineConfiguration->multisparkMaxExtraSparkCount = 2;
engineConfiguration->multisparkMaxSparkingAngle = 30;
}
/**
* @brief Global default engine configuration
* This method sets the global engine configuration defaults. These default values are then
@ -861,6 +872,8 @@ static void setDefaultEngineConfiguration(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
engineConfiguration->timingMode = TM_DYNAMIC;
engineConfiguration->fixedModeTiming = 50;
setDefaultMultisparkParameters(PASS_ENGINE_PARAMETER_SIGNATURE);
#if !EFI_UNIT_TEST
engineConfiguration->analogInputDividerCoefficient = 2;
#endif

View File

@ -86,6 +86,11 @@ public:
IgnitionOutputPin *outputs[MAX_OUTPUTS_FOR_IGNITION];
scheduling_s dwellStartTimer;
AngleBasedEvent sparkEvent;
// How many additional sparks should we fire after the first one?
// For single sparks, this should be zero.
uint8_t sparksRemaining = 0;
/**
* Desired timing advance
*/

View File

@ -171,7 +171,26 @@ if (engineConfiguration->debugMode == DBG_DWELL_METRIC) {
// we are here if engine has just stopped
return;
}
prepareCylinderIgnitionSchedule(dwellAngleDuration, sparkDwell, event PASS_ENGINE_PARAMETER_SUFFIX);
// If there are more sparks to fire, schedule them
if (event->sparksRemaining > 0)
{
event->sparksRemaining--;
efitick_t nowNt = getTimeNowNt();
efitick_t nextDwellStart = nowNt + engine->engineState.multispark.delay;
efitick_t nextFiring = nextDwellStart + engine->engineState.multispark.dwell;
// We can schedule both of these right away, since we're going for "asap" not "particular angle"
engine->executor.scheduleByTimestampNt(&event->dwellStartTimer, nextDwellStart, { &turnSparkPinHigh, event });
engine->executor.scheduleByTimestampNt(&event->sparkEvent.scheduling, nextFiring, { fireSparkAndPrepareNextSchedule, event });
}
else
{
// If all events have been scheduled, prepare for next time.
prepareCylinderIgnitionSchedule(dwellAngleDuration, sparkDwell, event PASS_ENGINE_PARAMETER_SUFFIX);
}
}
static void startDwellByTurningSparkPinHigh(IgnitionEvent *event, IgnitionOutputPin *output) {
@ -321,7 +340,13 @@ static ALWAYS_INLINE void handleSparkEvent(bool limitedSpark, uint32_t trgEventI
* the coil.
*/
engine->executor.scheduleByTimestampNt(&event->dwellStartTimer, edgeTimestamp + US2NT(chargeDelayUs), { &turnSparkPinHigh, event });
event->sparksRemaining = ENGINE(engineState.multispark.count);
} else {
// don't fire multispark if spark is cut completely!
event->sparksRemaining = 0;
}
/**
* Spark event is often happening during a later trigger event timeframe
*/
@ -329,7 +354,6 @@ static ALWAYS_INLINE void handleSparkEvent(bool limitedSpark, uint32_t trgEventI
efiAssertVoid(CUSTOM_ERR_6591, !cisnan(sparkAngle), "findAngle#4");
assertAngleRange(sparkAngle, "findAngle#a5", CUSTOM_ERR_6549);
bool scheduled = scheduleOrQueue(&event->sparkEvent, trgEventIndex, edgeTimestamp, sparkAngle, { fireSparkAndPrepareNextSchedule, event } PASS_ENGINE_PARAMETER_SUFFIX);
if (scheduled) {

View File

@ -248,6 +248,13 @@ struct running_fuel_s {
typedef struct running_fuel_s running_fuel_s;
struct multispark_state_s
{
efitick_t delay;
efitick_t dwell;
uint8_t count;
};
// start of engine_state2_s
struct engine_state2_s {
/**
@ -423,6 +430,9 @@ struct engine_state2_s {
* offset 160
*/
angle_t cltTimingCorrection = (angle_t)0;
// TODO: generate me
multispark_state_s multispark;
/** total size 164*/
};

View File

@ -105,6 +105,14 @@ percent_t etbFeedForward;
floatms_t sparkDwell;ignition dwell duration in ms\nSee also dwellAngle
angle_t dwellAngle;ignition dwell duration as crankshaft angle\nNAN if engine is stopped\nSee also sparkDwell
angle_t cltTimingCorrection
struct multispark_state_s
efitick_t delay
efitick_t dwell
uint8_t count
end_struct
multispark_state_s multispark
! engine_state2_s
end_struct

View File

@ -348,21 +348,21 @@ fileVersion = { @@TS_FILE_VERSION@@ }
enableLogDebugChannels = bits, U08, [0:0], "Yes", "No"
enableLogErrorList = bits, U08, [0:0], "Yes", "No"
; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
; Alternator TPS Acceleration Warmup-Pid Idle Engine Load Acc Trigger Counters ETB PID TLE8888 Boost
debugFieldF1List = bits, U08, [0:7], "Controller Output", "From TPS", "", "Controller Output", "", "Channel 1 Rise Counter", "", "", "", "", "", "", "", "", "", "", "", "ETB Controller Output", "", "", "df1", "df1", "22df1", "", "", "", "", "", "", "", "", "", "", "", "", "", "Open Loop Duty", ""
debugFieldF2List = bits, U08, [0:7], "I-Term", "To TPS", "", "I-Term", "", "Channel 2 Rise Counter", "", "", "", "", "", "", "", "", "", "", "", "ETB I-Term", "", "", "df2", "df2", "22df2", "", "", "", "", "", "", "", "", "", "", "", "", "", "Closed Loop Duty",""
debugFieldF3List = bits, U08, [0:7], "Previous Error", "Current TPS<>TPS", "", "", "", "Channel 3 Rise Counter", "", "", "", "", "", "", "", "", "", "", "", "ETB Previous Error", "", "", "df3", "df3", "22df3", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF4List = bits, U08, [0:7], "I Gain", "Extra Fuel", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB df4", "", "", "df4", "df4", "22df4", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF5List = bits, U08, [0:7], "D Gain", "df5", "df5", "df5", "df5", "df5", "df5", "", "", "", "", "", "", "", "", "", "", "ETB df5", "df5", "df5", "df5", "df5", "22df5", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF6List = bits, U08, [0:7], "D Term", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB df6", "", "", "df6", "df6", "22df6", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF7List = bits, U08, [0:7], "Max-Value", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB df7", "", "", "df7", "df7", "22df7", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
; Alternator TPS Acceleration Warmup-Pid Idle Engine Load Acc Trigger Counters Ignition Timing ETB PID TLE8888 Boost
debugFieldF1List = bits, U08, [0:7], "Controller Output", "From TPS", "", "Controller Output", "", "Channel 1 Rise Counter", "", "", "", "", "Ign IAT Corr", "", "", "", "", "", "", "ETB Controller Output", "", "", "df1", "df1", "22df1", "", "", "", "", "", "", "", "", "", "", "", "", "", "Open Loop Duty", ""
debugFieldF2List = bits, U08, [0:7], "I-Term", "To TPS", "", "I-Term", "", "Channel 2 Rise Counter", "", "", "", "", "Ign CLT Corr", "", "", "", "", "", "", "ETB I-Term", "", "", "df2", "df2", "22df2", "", "", "", "", "", "", "", "", "", "", "", "", "", "Closed Loop Duty",""
debugFieldF3List = bits, U08, [0:7], "Previous Error", "Current TPS<>TPS", "", "", "", "Channel 3 Rise Counter", "", "", "", "", "Ign FSIO Adj", "", "", "", "", "", "", "ETB Previous Error", "", "", "df3", "df3", "22df3", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF4List = bits, U08, [0:7], "I Gain", "Extra Fuel", "", "", "", "", "", "", "", "", "Ign PID Adj", "", "", "", "", "", "", "ETB df4", "", "", "df4", "df4", "22df4", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF5List = bits, U08, [0:7], "D Gain", "df5", "df5", "df5", "df5", "df5", "df5", "", "", "", "", "", "", "", "", "", "", "ETB df5", "df5", "df5", "df5", "df5", "22df5", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF6List = bits, U08, [0:7], "D Term", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB df6", "", "", "df6", "df6", "22df6", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldF7List = bits, U08, [0:7], "Max-Value", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB df7", "", "", "df7", "df7", "22df7", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
debugFieldI1List = bits, U08, [0:7], "P-Gain", "", "", "", "", "Channel 2 Fall Counter", "", "", "", "", "", "", "", "", "", "", "", "ETB P-Gain", "", "", "di1", "di1", "22", "", "", "", "", "", "", "", "", "SPI Counter", "", "", "", "", "", ""
debugFieldI2List = bits, U08, [0:7], "Offset", "", "", "", "", "Channel 3 Fall Counter", "", "", "", "", "", "", "", "", "", "", "", "ETB di2", "", "", "di2", "di2", "22", "", "", "", "", "", "", "", "", "Latest Transmit","", "", "", "", "", ""
debugFieldI3List = bits, U08, [0:7], "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB di3", "", "", "di3", "di3", "22", "", "", "", "", "", "", "", "", "Latest Received","", "", "", "", "", ""
debugFieldI4List = bits, U08, [0:7], "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB di4", "", "", "di4", "di4", "22", "", "", "", "", "", "", "", "", "Init Count", "", "", "", "", "", ""
debugFieldI5List = bits, U08, [0:7], "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "di5", "di5", "ETB di5", "di5", "di5", "di5", "di5", "22di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5"
debugFieldI1List = bits, U08, [0:7], "P-Gain", "", "", "", "", "Channel 2 Fall Counter", "", "", "", "", "Multispark Count", "", "", "", "", "", "", "ETB P-Gain", "", "", "di1", "di1", "22", "", "", "", "", "", "", "", "", "SPI Counter", "", "", "", "", "", ""
debugFieldI2List = bits, U08, [0:7], "Offset", "", "", "", "", "Channel 3 Fall Counter", "", "", "", "", "", "", "", "", "", "", "", "ETB di2", "", "", "di2", "di2", "22", "", "", "", "", "", "", "", "", "Latest Transmit","", "", "", "", "", ""
debugFieldI3List = bits, U08, [0:7], "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB di3", "", "", "di3", "di3", "22", "", "", "", "", "", "", "", "", "Latest Received","", "", "", "", "", ""
debugFieldI4List = bits, U08, [0:7], "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ETB di4", "", "", "di4", "di4", "22", "", "", "", "", "", "", "", "", "Init Count", "", "", "", "", "", ""
debugFieldI5List = bits, U08, [0:7], "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "di5", "di5", "ETB di5", "di5", "di5", "di5", "di5", "22di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5", "di5"
[ConstantsExtensions]
defaultValue = wueAfrTargetOffset, -1.5 -1.4 -1.15 -0.95 -0.775 -0.65 -0.5625 -0.5 -0.4375 -0.375 -0.3125 -0.25 -0.1875 -0.125 -0.0625 0

View File

@ -0,0 +1,74 @@
/*
* @file test_multispark.cpp
*
* @date Mar 15, 2020
* @author Matthew Kennedy, (c) 2020
*/
#include "engine_test_helper.h"
#include "advance_map.h"
TEST(Multispark, DefaultConfiguration) {
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
EXPECT_EQ(0, getMultiSparkCount(0 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(100 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(200 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(500 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(1000 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(2000 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(5000 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(50000 PASS_ENGINE_PARAMETER_SUFFIX));
}
static void multisparkCfg(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
// Turn it on!
CONFIG(multisparkEnable) = true;
// Fire up to 45 degrees worth of sparks...
CONFIG(multisparkMaxSparkingAngle) = 45;
// ...but limit to 10 additional sparks
CONFIG(multisparkMaxExtraSparkCount) = 10;
// 3ms period (spark + dwell)
CONFIG(multisparkDwell) = 2000;
CONFIG(multisparkSparkDuration) = 1000;
}
TEST(Multispark, EnabledNoMaxRpm) {
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
multisparkCfg(PASS_ENGINE_PARAMETER_SIGNATURE);
// Practically no RPM limit
CONFIG(multisparkMaxRpm) = 65000;
EXPECT_EQ(0, getMultiSparkCount(0 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(10, getMultiSparkCount(150 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(10, getMultiSparkCount(250 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(4, getMultiSparkCount(550 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(3, getMultiSparkCount(800 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(2, getMultiSparkCount(900 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(1, getMultiSparkCount(1500 PASS_ENGINE_PARAMETER_SUFFIX));
// 2500 is the threshold where we should get zero
EXPECT_EQ(1, getMultiSparkCount(2499 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(2501 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(5000 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(50000 PASS_ENGINE_PARAMETER_SUFFIX));
}
TEST(Multispark, RpmLimit) {
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
multisparkCfg(PASS_ENGINE_PARAMETER_SIGNATURE);
// Disable at 800 rpm
CONFIG(multisparkMaxRpm) = 800;
EXPECT_EQ(3, getMultiSparkCount(795 PASS_ENGINE_PARAMETER_SUFFIX));
EXPECT_EQ(0, getMultiSparkCount(805 PASS_ENGINE_PARAMETER_SUFFIX));
}

View File

@ -34,6 +34,7 @@ TESTS_SRC_CPP = \
tests/test_pid.cpp \
tests/test_accel_enrichment.cpp \
tests/test_gpiochip.cpp \
tests/test_multispark.cpp \
tests/sensor/basic_sensor.cpp \
tests/sensor/func_sensor.cpp \
tests/sensor/function_pointer_sensor.cpp \