| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * @file idle_thread_io.cpp | |||
| 3 | * | |||
| 4 | * | |||
| 5 | * enable verbose_idle | |||
| 6 | * disable verbose_idle | |||
| 7 | * | |||
| 8 | * This file is part of rusEfi - see http://rusefi.com | |||
| 9 | * | |||
| 10 | * rusEfi is free software; you can redistribute it and/or modify it under the terms of | |||
| 11 | * the GNU General Public License as published by the Free Software Foundation; either | |||
| 12 | * version 3 of the License, or (at your option) any later version. | |||
| 13 | * | |||
| 14 | * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without | |||
| 15 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 16 | * GNU General Public License for more details. | |||
| 17 | * | |||
| 18 | * You should have received a copy of the GNU General Public License along with this program. | |||
| 19 | * If not, see <http://www.gnu.org/licenses/>. | |||
| 20 | * | |||
| 21 | * @date Oct 17, 2021 | |||
| 22 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 23 | */ | |||
| 24 | ||||
| 25 | #include "pch.h" | |||
| 26 | ||||
| 27 | #if ! EFI_UNIT_TEST | |||
| 28 | #include "dc_motors.h" | |||
| 29 | #include "idle_hardware.h" | |||
| 30 | ||||
| 31 | void setManualIdleValvePosition(int positionPercent) { | |||
| 32 | if (positionPercent < 1 || positionPercent > 99) | |||
| 33 | return; | |||
| 34 | efiPrintf("setting idle valve position %d", positionPercent); | |||
| 35 | // todo: this is not great that we have to write into configuration here | |||
| 36 | setTable(config->cltIdleCorrTable, positionPercent); | |||
| 37 | } | |||
| 38 | ||||
| 39 | #endif /* EFI_UNIT_TEST */ | |||
| 40 | ||||
| 41 | #if EFI_PROD_CODE | |||
| 42 | static void startInputPinIfValid(const char *msg, brain_pin_e pin, pin_input_mode_e mode) { | |||
| 43 | efiSetPadMode(msg, pin, getInputMode(mode)); | |||
| 44 | } | |||
| 45 | #endif // EFI_PROD_CODE | |||
| 46 | ||||
| 47 | ✗ | percent_t getIdlePosition() { | ||
| 48 | #if EFI_IDLE_CONTROL | |||
| 49 | ✗ | return engine->module<IdleController>().unmock().currentIdlePosition; | ||
| 50 | #else | |||
| 51 | return 0; | |||
| 52 | #endif | |||
| 53 | } | |||
| 54 | ||||
| 55 | 805 | void startSwitchPins() { | ||
| 56 | #if EFI_PROD_CODE | |||
| 57 | // this is neutral/no gear switch input. on Miata it's wired both to clutch pedal and neutral in gearbox | |||
| 58 | // this switch is not used yet | |||
| 59 | startInputPinIfValid("clutch down switch", engineConfiguration->clutchDownPin, engineConfiguration->clutchDownPinMode); | |||
| 60 | ||||
| 61 | startInputPinIfValid("clutch up switch", engineConfiguration->clutchUpPin, engineConfiguration->clutchUpPinMode); | |||
| 62 | ||||
| 63 | startInputPinIfValid("brake pedal switch", engineConfiguration->brakePedalPin, engineConfiguration->brakePedalPinMode); | |||
| 64 | startInputPinIfValid("Launch Button", engineConfiguration->launchActivatePin, engineConfiguration->launchActivatePinMode); | |||
| 65 | startInputPinIfValid("Antilag Button", engineConfiguration->ALSActivatePin, engineConfiguration->ALSActivatePinMode); | |||
| 66 | startInputPinIfValid("Ignition Switch", engineConfiguration->ignitionKeyDigitalPin, engineConfiguration->ignitionKeyDigitalPinMode); | |||
| 67 | startInputPinIfValid("Torque Reduction Button", engineConfiguration->torqueReductionTriggerPin, engineConfiguration->torqueReductionTriggerPinMode); | |||
| 68 | startInputPinIfValid("Nitrous Button", engineConfiguration->nitrousControlTriggerPin, engineConfiguration->nitrousControlTriggerPinMode); | |||
| 69 | #endif /* EFI_PROD_CODE */ | |||
| 70 | 805 | } | ||
| 71 | ||||
| 72 | 221 | void stopSwitchPins() { | ||
| 73 | 221 | brain_pin_markUnused(activeConfiguration.clutchUpPin); | ||
| 74 | 221 | brain_pin_markUnused(activeConfiguration.clutchDownPin); | ||
| 75 | 221 | brain_pin_markUnused(activeConfiguration.brakePedalPin); | ||
| 76 | 221 | brain_pin_markUnused(activeConfiguration.launchActivatePin); | ||
| 77 | 221 | brain_pin_markUnused(activeConfiguration.ALSActivatePin); | ||
| 78 | 221 | brain_pin_markUnused(activeConfiguration.ignitionKeyDigitalPin); | ||
| 79 | 221 | } | ||
| 80 | ||||
| 81 | #if ! EFI_UNIT_TEST | |||
| 82 | ||||
| 83 | #if EFI_IDLE_CONTROL | |||
| 84 | static void applyPidSettings() { | |||
| 85 | engine->module<IdleController>().unmock().getIdlePid()->updateFactors(engineConfiguration->idleRpmPid.pFactor, engineConfiguration->idleRpmPid.iFactor, engineConfiguration->idleRpmPid.dFactor); | |||
| 86 | } | |||
| 87 | #endif // EFI_IDLE_CONTROL | |||
| 88 | ||||
| 89 | void setTargetIdleRpm(int value) { | |||
| 90 | setTargetRpmCurve(value); | |||
| 91 | efiPrintf("target idle RPM %d", value); | |||
| 92 | } | |||
| 93 | ||||
| 94 | /** | |||
| 95 | * Idle test would activate the solenoid for three seconds | |||
| 96 | */ | |||
| 97 | void startIdleBench(void) { | |||
| 98 | engine->timeToStopIdleTest = getTimeNowUs() + MS2US(3000); // 3 seconds | |||
| 99 | efiPrintf("idle valve bench test"); | |||
| 100 | } | |||
| 101 | ||||
| 102 | #endif /* EFI_UNIT_TEST */ | |||
| 103 | ||||
| 104 | #if EFI_IDLE_CONTROL | |||
| 105 | ||||
| 106 | 587 | void setDefaultIdleParameters() { | ||
| 107 | 587 | engineConfiguration->idleRpmPid.pFactor = 0.01f; | ||
| 108 | 587 | engineConfiguration->idleRpmPid.iFactor = 0.05f; | ||
| 109 | 587 | engineConfiguration->idleRpmPid.dFactor = 0.0f; | ||
| 110 | ||||
| 111 | 587 | engineConfiguration->idlerpmpid_iTermMin = -20; | ||
| 112 | 587 | engineConfiguration->idlerpmpid_iTermMax = 20; | ||
| 113 | ||||
| 114 | // Good starting point is 10 degrees per 100 rpm, aka 0.1 deg/rpm | |||
| 115 | 587 | engineConfiguration->idleTimingPid.pFactor = 0.1f; | ||
| 116 | 587 | engineConfiguration->idleTimingPid.iFactor = 0; | ||
| 117 | 587 | engineConfiguration->idleTimingPid.dFactor = 0; | ||
| 118 | ||||
| 119 | // Allow +- 10 degrees adjustment | |||
| 120 | 587 | engineConfiguration->idleTimingPid.minValue = -10; | ||
| 121 | 587 | engineConfiguration->idleTimingPid.maxValue = 10; | ||
| 122 | ||||
| 123 | // Idle region is target + 300 RPM | |||
| 124 | 587 | engineConfiguration->idlePidRpmUpperLimit = 300; | ||
| 125 | ||||
| 126 | 587 | engineConfiguration->idlePidRpmDeadZone = 50; | ||
| 127 | 587 | engineConfiguration->idleReturnTargetRampDuration = 3; | ||
| 128 | 587 | } | ||
| 129 | ||||
| 130 | 585 | void startIdleThread() { | ||
| 131 | // Force the idle controller to use 0 offset, as this is handled by the open loop table instead. | |||
| 132 | 585 | engineConfiguration->idleRpmPid.offset = 0; | ||
| 133 | ||||
| 134 | 585 | IdleController *controller = &engine->module<IdleController>().unmock(); | ||
| 135 | ||||
| 136 | 585 | controller->init(); | ||
| 137 | ||||
| 138 | #if ! EFI_UNIT_TEST | |||
| 139 | // todo: we still have to explicitly init all hardware on start in addition to handling configuration change via | |||
| 140 | // 'applyNewHardwareSettings' todo: maybe unify these two use-cases? | |||
| 141 | initIdleHardware(); | |||
| 142 | #endif /* EFI_UNIT_TEST */ | |||
| 143 | ||||
| 144 | 585 | controller->idleState = INIT; | ||
| 145 | 585 | controller->baseIdlePosition = -100.0f; | ||
| 146 | 585 | controller->currentIdlePosition = -100.0f; | ||
| 147 | ||||
| 148 | #if ! EFI_UNIT_TEST | |||
| 149 | // split this whole file into manual controller and auto controller? move these commands into the file | |||
| 150 | // which would be dedicated to just auto-controller? | |||
| 151 | ||||
| 152 | addConsoleAction("idlebench", startIdleBench); | |||
| 153 | applyPidSettings(); | |||
| 154 | #endif /* EFI_UNIT_TEST */ | |||
| 155 | 585 | } | ||
| 156 | ||||
| 157 | #endif /* EFI_IDLE_CONTROL */ | |||
| 158 |