/* * @file vvt.cpp * * @date Jun 26, 2016 * @author Andrey Belomutskiy, (c) 2012-2020 */ #include "pch.h" #include "local_version_holder.h" #include "vvt.h" #define NO_PIN_PERIOD 500 #if defined(HAS_OS_ACCESS) #error "Unexpected OS ACCESS HERE" #endif /* HAS_OS_ACCESS */ using vvt_map_t = Map3D; static vvt_map_t vvtTable1; static vvt_map_t vvtTable2; void VvtController::init(int index, int bankIndex, int camIndex, const ValueProvider3D* targetMap) { this->index = index; m_bank = bankIndex; m_cam = camIndex; // Use the same settings for the Nth cam in every bank (ie, all exhaust cams use the same PID) m_pid.initPidClass(&engineConfiguration->auxPid[camIndex]); m_targetMap = targetMap; } int VvtController::getPeriodMs() { return isBrainPinValid(engineConfiguration->vvtPins[index]) ? GET_PERIOD_LIMITED(&engineConfiguration->auxPid[index]) : NO_PIN_PERIOD; } void VvtController::PeriodicTask() { if (engine->auxParametersVersion.isOld(engine->getGlobalConfigurationVersion())) { m_pid.reset(); } update(); } expected VvtController::observePlant() const { #if EFI_SHAFT_POSITION_INPUT return engine->triggerCentral.getVVTPosition(m_bank, m_cam); #else return unexpected; #endif // EFI_SHAFT_POSITION_INPUT } expected VvtController::getSetpoint() { int rpm = Sensor::getOrZero(SensorType::Rpm); float load = getFuelingLoad(); float target = m_targetMap->getValue(rpm, load); #if EFI_TUNER_STUDIO engine->outputChannels.vvtTargets[index] = target; #endif return target; } expected VvtController::getOpenLoop(angle_t target) { // TODO: could we do VVT open loop? UNUSED(target); return 0; } expected VvtController::getClosedLoop(angle_t target, angle_t observation) { float retVal = m_pid.getOutput(target, observation); if (engineConfiguration->isVerboseAuxPid1) { efiPrintf("aux duty: %.2f/value=%.2f/p=%.2f/i=%.2f/d=%.2f int=%.2f", retVal, observation, m_pid.getP(), m_pid.getI(), m_pid.getD(), m_pid.getIntegration()); } #if EFI_TUNER_STUDIO static constexpr const debug_mode_e debugModeByIndex[4] = {DBG_VVT_1_PID, DBG_VVT_2_PID, DBG_VVT_3_PID, DBG_VVT_4_PID}; if (engineConfiguration->debugMode == debugModeByIndex[index]) { m_pid.postState(&engine->outputChannels); engine->outputChannels.debugIntField3 = (int)(10 * target); } #endif /* EFI_TUNER_STUDIO */ return retVal; } void VvtController::setOutput(expected outputValue) { float rpm = Sensor::getOrZero(SensorType::Rpm); #if EFI_SHAFT_POSITION_INPUT bool enabled = rpm > engineConfiguration->cranking.rpm /* todo: make this configurable? */ && engine->rpmCalculator.getSecondsSinceEngineStart(getTimeNowNt()) > engineConfiguration->vvtActivationDelayMs / MS_PER_SECOND ; if (outputValue && enabled) { m_pwm.setSimplePwmDutyCycle(PERCENT_TO_DUTY(outputValue.Value)); } else { m_pwm.setSimplePwmDutyCycle(0); // we need to avoid accumulating iTerm while engine is not running m_pid.reset(); } #endif // EFI_SHAFT_POSITION_INPUT } #if EFI_AUX_PID static VvtController instances[CAM_INPUTS_COUNT]; static void turnAuxPidOn(int index) { if (!isBrainPinValid(engineConfiguration->vvtPins[index])) { return; } startSimplePwmExt(&instances[index].m_pwm, "Aux PID", &engine->executor, engineConfiguration->vvtPins[index], &instances[index].m_pin, // todo: do we need two separate frequencies? engineConfiguration->vvtOutputFrequency[0], 0.1); } void startVvtControlPins() { for (int i = 0;i vvtTable1, config->vvtTable1LoadBins, config->vvtTable1RpmBins); vvtTable2.init(config->vvtTable2, config->vvtTable2LoadBins, config->vvtTable2RpmBins); for (int i = 0;i < CAM_INPUTS_COUNT;i++) { int camIndex = i % CAMS_PER_BANK; int bankIndex = i / CAMS_PER_BANK; auto targetMap = camIndex == 0 ? &vvtTable1 : &vvtTable2; instances[i].init(i, bankIndex, camIndex, targetMap); } startVvtControlPins(); for (int i = 0;i < CAM_INPUTS_COUNT;i++) { instances[i].Start(); } } #endif