calculate per-cylinder ignition timing (#3652)

* simplify cylinder phasing

* per cylinder timing

* s

* s

* s

* why was there a divide by 2?
This commit is contained in:
Matthew Kennedy 2021-12-06 18:19:37 -08:00 committed by GitHub
parent 17debda6b6
commit 3a95e86112
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 36 additions and 32 deletions

View File

@ -624,7 +624,7 @@ static void updateFuelInfo() {
}
static void updateIgnition(int rpm) {
float timing = engine->engineState.timingAdvance;
float timing = engine->engineState.timingAdvance[0];
// that's weird logic. also seems broken for two stroke?
tsOutputChannels.ignitionAdvance = timing > FOUR_STROKE_CYCLE_DURATION / 2 ? timing - FOUR_STROKE_CYCLE_DURATION : timing;
// 60

View File

@ -165,7 +165,11 @@ void EngineState::periodicFastCallback() {
injectionOffset = getInjectionOffset(rpm, fuelLoad);
float ignitionLoad = getIgnitionLoad();
timingAdvance = getAdvance(rpm, ignitionLoad) * luaAdjustments.ignitionTimingMult + luaAdjustments.ignitionTimingAdd;
float advance = getAdvance(rpm, ignitionLoad) * luaAdjustments.ignitionTimingMult + luaAdjustments.ignitionTimingAdd;
for (size_t i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
timingAdvance[i] = advance;
}
// TODO: calculate me from a table!
trailingSparkAngle = engineConfiguration->trailingSparkAngle;

View File

@ -54,7 +54,7 @@ public:
/**
* timing advance is angle distance before Top Dead Center (TDP), i.e. "10 degree timing advance" means "happens 10 degrees before TDC"
*/
angle_t timingAdvance = 0;
angle_t timingAdvance[MAX_CYLINDER_COUNT] = {0};
// Angle between firing the main (primary) spark and the secondary (trailing) spark
angle_t trailingSparkAngle = 0;

View File

@ -600,7 +600,7 @@ void canDashboardHaltech(CanCycle cycle) {
msg[2] = 0x00;
msg[3] = 0x00;
/* Ignition Angle (Leading) - y = x/10 */
float timing = engine->engineState.timingAdvance;
float timing = engine->engineState.timingAdvance[0];
int16_t ignAngle = ((timing > 360 ? timing - 720 : timing) * 10);
msg[4] = (ignAngle >> 8);
msg[5] = (ignAngle & 0x00ff);

View File

@ -56,7 +56,7 @@ static void populateFrame(Speeds& msg) {
auto rpm = GET_RPM();
msg.rpm = rpm;
auto timing = engine->engineState.timingAdvance;
auto timing = engine->engineState.timingAdvance[0];
msg.timing = timing > 360 ? timing - 720 : timing;
msg.injDuty = getInjectorDutyCycle(rpm);

View File

@ -138,7 +138,7 @@ static void handleGetDataRequest(const CANRxFrame& rx) {
obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::VehicleSpeed));
break;
case PID_TIMING_ADVANCE: {
float timing = engine->engineState.timingAdvance;
float timing = engine->engineState.timingAdvance[0];
timing = (timing > 360.0f) ? (timing - 720.0f) : timing;
obdSendValue(_1_MODE, pid, 1, (timing + 64.0f) * 2.0f); // angle before TDC. (A/2)-64
break;

View File

@ -11,9 +11,9 @@
#include "hip9011.h"
int getCylinderKnockBank(uint8_t cylinderIndex) {
int getCylinderKnockBank(uint8_t cylinderNumber) {
// C/C++ can't index in to bit fields, we have to provide lookup ourselves
switch (cylinderIndex) {
switch (cylinderNumber) {
#if EFI_PROD_CODE
case 0:
return engineConfiguration->knockBankCyl1;
@ -45,13 +45,13 @@ int getCylinderKnockBank(uint8_t cylinderIndex) {
}
}
bool KnockController::onKnockSenseCompleted(uint8_t cylinderIndex, float dbv, efitick_t lastKnockTime) {
bool KnockController::onKnockSenseCompleted(uint8_t cylinderNumber, float dbv, efitick_t lastKnockTime) {
bool isKnock = dbv > engine->engineState.knockThreshold;
#if EFI_TUNER_STUDIO
// Pass through per-cylinder peak detector
float cylPeak = peakDetectors[cylinderIndex].detect(dbv, lastKnockTime);
tsOutputChannels.knock[cylinderIndex] = roundf(cylPeak);
float cylPeak = peakDetectors[cylinderNumber].detect(dbv, lastKnockTime);
tsOutputChannels.knock[cylinderNumber] = roundf(cylPeak);
// Pass through all-cylinders peak detector
tsOutputChannels.knockLevel = allCylinderPeakDetector.detect(dbv, lastKnockTime);
@ -64,7 +64,7 @@ bool KnockController::onKnockSenseCompleted(uint8_t cylinderIndex, float dbv, ef
// TODO: retard timing, then put it back!
if (isKnock) {
auto baseTiming = engine->engineState.timingAdvance;
auto baseTiming = engine->engineState.timingAdvance[cylinderNumber];
// TODO: 20 configurable? Better explanation why 20?
auto distToMinimum = baseTiming - (-20);
@ -107,13 +107,13 @@ void KnockController::periodicFastCallback() {
}
// This callback is to be implemented by the knock sense driver
__attribute__((weak)) void onStartKnockSampling(uint8_t cylinderIndex, float samplingTimeSeconds, uint8_t channelIdx) {
UNUSED(cylinderIndex);
__attribute__((weak)) void onStartKnockSampling(uint8_t cylinderNumber, float samplingTimeSeconds, uint8_t channelIdx) {
UNUSED(cylinderNumber);
UNUSED(samplingTimeSeconds);
UNUSED(channelIdx);
}
static uint8_t cylinderIndexCopy;
static uint8_t cylinderNumberCopy;
// Called when its time to start listening for knock
// Does some math, then hands off to the driver to start any sampling hardware
@ -126,16 +126,16 @@ static void startKnockSampling(Engine* engine) {
float samplingSeconds = engine->rpmCalculator.oneDegreeUs * engineConfiguration->knockSamplingDuration / US_PER_SECOND_F;
// Look up which channel this cylinder uses
auto channel = getCylinderKnockBank(cylinderIndexCopy);
auto channel = getCylinderKnockBank(cylinderNumberCopy);
// Call the driver to begin sampling
onStartKnockSampling(cylinderIndexCopy, samplingSeconds, channel);
onStartKnockSampling(cylinderNumberCopy, samplingSeconds, channel);
}
static scheduling_s startSampling;
void Engine::onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt) {
cylinderIndexCopy = cylinderIndex;
void Engine::onSparkFireKnockSense(uint8_t cylinderNumber, efitick_t nowNt) {
cylinderNumberCopy = cylinderNumber;
#if EFI_HIP_9011 || EFI_SOFTWARE_KNOCK
scheduleByAngle(&startSampling, nowNt,
@ -143,6 +143,6 @@ void Engine::onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt) {
#endif
#if EFI_HIP_9011
hip9011_onFireEvent(cylinderIndex, nowNt);
hip9011_onFireEvent(cylinderNumber, nowNt);
#endif
}

View File

@ -9,12 +9,12 @@
#include "peak_detect.h"
int getCylinderKnockBank(uint8_t cylinderIndex);
int getCylinderKnockBank(uint8_t cylinderNumber);
class KnockController {
public:
// onKnockSenseCompleted is the callback from the knock sense driver to report a sensed knock level
bool onKnockSenseCompleted(uint8_t cylinderIndex, float dbv, efitick_t lastKnockTime);
bool onKnockSenseCompleted(uint8_t cylinderNumber, float dbv, efitick_t lastKnockTime);
void periodicFastCallback();
float getKnockRetard() const;

View File

@ -503,7 +503,7 @@ static void showMainInfo(Engine *engine) {
int rpm = GET_RPM();
float el = getFuelingLoad();
efiPrintf("rpm %d engine_load %.2f", rpm, el);
efiPrintf("fuel %.2fms timing %.2f", engine->injectionDuration, engine->engineState.timingAdvance);
efiPrintf("fuel %.2fms timing %.2f", engine->injectionDuration, engine->engineState.timingAdvance[0]);
#endif /* EFI_PROD_CODE */
}

View File

@ -75,7 +75,7 @@ static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_
const angle_t sparkAngle =
// Negate because timing *before* TDC, and we schedule *after* TDC
- engine->engineState.timingAdvance
- engine->engineState.timingAdvance[event->cylinderNumber]
// Offset by this cylinder's position in the cycle
+ getCylinderAngle(event->cylinderIndex, event->cylinderNumber)
// Pull any extra timing for knock retard
@ -373,7 +373,7 @@ void initializeIgnitionActions() {
IgnitionEventList *list = &engine->ignitionEvents;
angle_t dwellAngle = engine->engineState.dwellAngle;
floatms_t sparkDwell = engine->engineState.sparkDwell;
if (cisnan(engine->engineState.timingAdvance) || cisnan(dwellAngle)) {
if (cisnan(engine->engineState.timingAdvance[0]) || cisnan(dwellAngle)) {
// error should already be reported
// need to invalidate previous ignition schedule
list->isReady = false;

View File

@ -11,7 +11,7 @@
#include "ch.hpp"
static NO_CACHE adcsample_t sampleBuffer[2000];
static int8_t currentCylinderIndex = 0;
static int8_t currentCylinderNumber = 0;
static efitick_t lastKnockSampleTime = 0;
static Biquad knockFilter;
@ -104,7 +104,7 @@ const ADCConversionGroup* getConversionGroup(uint8_t channelIdx) {
return &adcConvGroupCh1;
}
void onStartKnockSampling(uint8_t cylinderIndex, float samplingSeconds, uint8_t channelIdx) {
void onStartKnockSampling(uint8_t cylinderNumber, float samplingSeconds, uint8_t channelIdx) {
if (!engineConfiguration->enableSoftwareKnock) {
return;
}
@ -128,8 +128,8 @@ void onStartKnockSampling(uint8_t cylinderIndex, float samplingSeconds, uint8_t
// Select the appropriate conversion group - it will differ depending on which sensor this cylinder should listen on
auto conversionGroup = getConversionGroup(channelIdx);
// Stash the current cylinder's index so we can store the result appropriately
currentCylinderIndex = cylinderIndex;
// Stash the current cylinder's number so we can store the result appropriately
currentCylinderNumber = cylinderNumber;
adcStartConversionI(&KNOCK_ADC, conversionGroup, sampleBuffer, sampleCount);
lastKnockSampleTime = getTimeNowNt();
@ -201,7 +201,7 @@ void processLastKnockEvent() {
// clamp to reasonable range
db = clampF(-100, db, 100);
engine->knockController.onKnockSenseCompleted(currentCylinderIndex, db, lastKnockTime);
engine->knockController.onKnockSenseCompleted(currentCylinderNumber, db, lastKnockTime);
}
void KnockThread::ThreadTask() {

View File

@ -23,7 +23,7 @@ TEST(ignition, twoCoils) {
ASSERT_EQ(engine->ignitionPin[ID2INDEX(12)], 1);
// let's recalculate with zero timing so that we can focus on relation advance between cylinders
engine->engineState.timingAdvance = 0;
setArrayValues(engine->engineState.timingAdvance, 0.0f);
initializeIgnitionActions();
ASSERT_EQ(engine->ignitionEvents.elements[0].sparkAngle, 0);

View File

@ -345,7 +345,7 @@ TEST(misc, testRpmCalculator) {
eth.engine.periodicFastCallback();
ASSERT_NEAR(engine->engineState.timingAdvance, 707, 0.1f);
ASSERT_NEAR(engine->engineState.timingAdvance[0], 707, 0.1f);
assertEqualsM("fuel #1", 4.5450, engine->injectionDuration);
InjectionEvent *ie0 = &engine->injectionEvents.elements[0];