rusefi/firmware/controllers/engine_cycle/knock_controller.cpp

175 lines
4.8 KiB
C++

/*
* @file knock_logic.c
*
* @date Apr 04, 2021
* @author Andrey Gusakov
*/
#include "pch.h"
#include "knock_logic.h"
#include "hip9011.h"
void KnockController::onConfigurationChange(engine_configuration_s const * previousConfig) {
KnockControllerBase::onConfigurationChange(previousConfig);
m_maxRetardTable.initTable(config->maxKnockRetardTable, config->maxKnockRetardLoadBins, config->maxKnockRetardRpmBins);
}
int getCylinderKnockBank(uint8_t cylinderNumber) {
// C/C++ can't index in to bit fields, we have to provide lookup ourselves
switch (cylinderNumber) {
#if EFI_PROD_CODE
case 0:
return engineConfiguration->knockBankCyl1;
case 1:
return engineConfiguration->knockBankCyl2;
case 2:
return engineConfiguration->knockBankCyl3;
case 3:
return engineConfiguration->knockBankCyl4;
case 4:
return engineConfiguration->knockBankCyl5;
case 5:
return engineConfiguration->knockBankCyl6;
case 6:
return engineConfiguration->knockBankCyl7;
case 7:
return engineConfiguration->knockBankCyl8;
case 8:
return engineConfiguration->knockBankCyl9;
case 9:
return engineConfiguration->knockBankCyl10;
case 10:
return engineConfiguration->knockBankCyl11;
case 11:
return engineConfiguration->knockBankCyl12;
#endif
default:
return 0;
}
}
bool KnockControllerBase::onKnockSenseCompleted(uint8_t cylinderNumber, float dbv, efitick_t lastKnockTime) {
bool isKnock = dbv > m_knockThreshold;
// Per-cylinder peak detector
float cylPeak = peakDetectors[cylinderNumber].detect(dbv, lastKnockTime);
m_knockCyl[cylinderNumber] = roundf(cylPeak);
// All-cylinders peak detector
m_knockLevel = allCylinderPeakDetector.detect(dbv, lastKnockTime);
if (isKnock) {
m_knockCount++;
auto baseTiming = engine->engineState.timingAdvance[cylinderNumber];
// TODO: 20 configurable? Better explanation why 20?
auto distToMinimum = baseTiming - (-20);
// percent -> ratio = divide by 100
auto retardFraction = engineConfiguration->knockRetardAggression * 0.01f;
auto retardAmount = distToMinimum * retardFraction;
{
// Adjust knock retard under lock
chibios_rt::CriticalSectionLocker csl;
auto newRetard = m_knockRetard + retardAmount;
m_knockRetard = clampF(0, newRetard, m_maximumRetard);
}
}
return isKnock;
}
float KnockControllerBase::getKnockRetard() const {
return m_knockRetard;
}
uint32_t KnockControllerBase::getKnockCount() const {
return m_knockCount;
}
void KnockControllerBase::onFastCallback() {
m_knockThreshold = getKnockThreshold();
m_maximumRetard = getMaximumRetard();
constexpr auto callbackPeriodSeconds = FAST_CALLBACK_PERIOD_MS / 1000.0f;
auto applyAmount = engineConfiguration->knockRetardReapplyRate * callbackPeriodSeconds;
{
// Adjust knock retard under lock
chibios_rt::CriticalSectionLocker csl;
// Reduce knock retard at the requested rate
float newRetard = m_knockRetard - applyAmount;
// don't allow retard to go negative
if (newRetard < 0) {
m_knockRetard = 0;
} else {
m_knockRetard = newRetard;
}
}
}
float KnockController::getKnockThreshold() const {
return interpolate2d(
Sensor::getOrZero(SensorType::Rpm),
engineConfiguration->knockNoiseRpmBins,
engineConfiguration->knockBaseNoise
);
}
float KnockController::getMaximumRetard() const {
return m_maxRetardTable.getValue(Sensor::getOrZero(SensorType::Rpm), getIgnitionLoad());
}
// This callback is to be implemented by the knock sense driver
__attribute__((weak)) void onStartKnockSampling(uint8_t cylinderNumber, float samplingTimeSeconds, uint8_t channelIdx) {
UNUSED(cylinderNumber);
UNUSED(samplingTimeSeconds);
UNUSED(channelIdx);
}
#if EFI_HIP_9011 || EFI_SOFTWARE_KNOCK
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
static void startKnockSampling(Engine* p_engine) {
// todo: why do we pass engine as parameter? is that for unit tests?
if (!p_engine->rpmCalculator.isRunning()) {
return;
}
// Convert sampling angle to time
float samplingSeconds = engine->rpmCalculator.oneDegreeUs * engineConfiguration->knockSamplingDuration / US_PER_SECOND_F;
// Look up which channel this cylinder uses
auto channel = getCylinderKnockBank(cylinderNumberCopy);
// Call the driver to begin sampling
onStartKnockSampling(cylinderNumberCopy, samplingSeconds, channel);
}
#endif // EFI_HIP_9011 || EFI_SOFTWARE_KNOCK
void Engine::onSparkFireKnockSense(uint8_t cylinderNumber, efitick_t nowNt) {
#if EFI_HIP_9011 || EFI_SOFTWARE_KNOCK
cylinderNumberCopy = cylinderNumber;
scheduleByAngle(nullptr, nowNt,
/*angle*/engineConfiguration->knockDetectionWindowStart, { startKnockSampling, engine });
#else
UNUSED(cylinderNumber);
UNUSED(nowNt);
#endif
#if EFI_HIP_9011
hip9011_onFireEvent(cylinderNumber, nowNt);
#endif
}