rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
main_trigger_callback.cpp
Go to the documentation of this file.
1/**
2 * @file main_trigger_callback.cpp
3 * @brief Main logic is here!
4 *
5 * See http://rusefi.com/docs/html/
6 *
7 * @date Feb 7, 2013
8 * @author Andrey Belomutskiy, (c) 2012-2020
9 *
10 * This file is part of rusEfi - see http://rusefi.com
11 *
12 * rusEfi is free software; you can redistribute it and/or modify it under the terms of
13 * the GNU General Public License as published by the Free Software Foundation; either
14 * version 3 of the License, or (at your option) any later version.
15 *
16 * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
17 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along with this program.
21 * If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "pch.h"
25
26#if EFI_PRINTF_FUEL_DETAILS
27 bool printFuelDebug = false;
28#endif // EFI_PRINTF_FUEL_DETAILS
29
30#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
31
33#include "trigger_central.h"
34#include "spark_logic.h"
35#include "advance_map.h"
36#include "cyclic_buffer.h"
37#include "fuel_math.h"
38#include "cdm_ion_sense.h"
40#include "event_queue.h"
41#include "injector_model.h"
42#include "injection_gpio.h"
43
44#if EFI_LAUNCH_CONTROL
45#include "launch_control.h"
46#endif // EFI_LAUNCH_CONTROL
47
48#include "backup_ram.h"
49
54
56 efitick_t nowNt = getTimeNowNt();
57
58 for (size_t i = 0; i < efi::size(event->outputs); i++) {
59 InjectorOutputPin *output = event->outputs[i];
60 if (output) {
61 output->close(nowNt);
62 }
63 }
64 event->update();
65}
66
68 efitick_t nowNt = getTimeNowNt();
69
70 for (size_t i = 0; i < efi::size(event->outputsStage2); i++) {
71 InjectorOutputPin *output = event->outputsStage2[i];
72 if (output) {
73 output->close(nowNt);
74 }
75 }
76}
77
78void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase) {
79 auto eventAngle = injectionStartAngle;
80
81 // Determine whether our angle is going to happen before (or near) the next tooth
82 if (!isPhaseInRange(eventAngle, currentPhase, nextPhase)) {
83 return;
84 }
85
86 // Select fuel mass from the correct cylinder
87 auto injectionMassGrams = getEngineState()->injectionMass[this->cylinderNumber];
88
89 // Perform wall wetting adjustment on fuel mass, not duration, so that
90 // it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
91 // TODO: is it correct to wall wet on both pulses?
92 injectionMassGrams = wallFuel.adjust(injectionMassGrams);
93
94 // Disable staging in simultaneous mode
95 float stage2Fraction = isSimultaneous ? 0 : getEngineState()->injectionStage2Fraction;
96
97 // Compute fraction of fuel on stage 2, remainder goes on stage 1
98 const float injectionMassStage2 = stage2Fraction * injectionMassGrams;
99 float injectionMassStage1 = injectionMassGrams - injectionMassStage2;
100
101#if EFI_VEHICLE_SPEED
102 {
103 // Log this fuel as consumed
104
105 bool isCranking = getEngineRotationState()->isCranking();
107
108 float actualInjectedMass = numberOfInjections * (injectionMassStage1 + injectionMassStage2);
109
110#ifdef MODULE_ODOMETER
111 engine->module<TripOdometer>()->consumeFuel(actualInjectedMass, nowNt);
112#endif // MODULE_ODOMETER
113
114 }
115#endif // EFI_VEHICLE_SPEED
116
117 const floatms_t injectionDurationStage1 = engine->module<InjectorModelPrimary>()->getInjectionDuration(injectionMassStage1);
118 const floatms_t injectionDurationStage2 = injectionMassStage2 > 0 ? engine->module<InjectorModelSecondary>()->getInjectionDuration(injectionMassStage2) : 0;
119
120#if EFI_PRINTF_FUEL_DETAILS
121 if (printFuelDebug) {
122 printf("fuel injectionDuration=%.2fms adjusted=%.2fms\n",
123 getEngineState()->injectionDuration,
124 injectionDurationStage1);
125 }
126#endif /*EFI_PRINTF_FUEL_DETAILS */
127
128 if (this->cylinderNumber == 0) {
131 } else {
133 }
136 } else {
138 }
139 engine->outputChannels.actualLastInjection = injectionDurationStage1;
140 engine->outputChannels.actualLastInjectionStage2 = injectionDurationStage2;
141 }
142
143 if (std::isnan(injectionDurationStage1) || std::isnan(injectionDurationStage2)) {
144 warning(ObdCode::CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse");
145 return;
146 }
147 if (injectionDurationStage1 < 0) {
148 warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDurationStage1);
149 return;
150 }
151
152 // If somebody commanded an impossibly short injection, do nothing.
153 // Durations under 50us-ish aren't safe for the scheduler
154 // as their order may be swapped, resulting in a stuck open injector
155 // see https://github.com/rusefi/rusefi/pull/596 for more details
156 if (injectionDurationStage1 < 0.050f)
157 {
158 return;
159 }
160
161 floatus_t durationUsStage1 = MS2US(injectionDurationStage1);
162 floatus_t durationUsStage2 = MS2US(injectionDurationStage2);
163
164 // Only bother with the second stage if it's long enough to be relevant
165 bool hasStage2Injection = durationUsStage2 > 50;
166
167#if EFI_PRINTF_FUEL_DETAILS
168 if (printFuelDebug) {
169 InjectorOutputPin *output = outputs[0];
170 printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->getName(), (int)durationUsStage1,
172 }
173#endif /*EFI_PRINTF_FUEL_DETAILS */
174
175 action_s startAction, endActionStage1, endActionStage2;
176 // We use different callbacks based on whether we're running sequential mode or not - everything else is the same
177 if (isSimultaneous) {
178 startAction = action_s::make<startSimultaneousInjection>();
179 endActionStage1 = action_s::make<endSimultaneousInjection>(this);
180 } else {
181 auto const taggedPointer{TaggedPointer<decltype(this)>::make(this, hasStage2Injection)};
182
183 // sequential or batch
184 startAction = action_s::make<turnInjectionPinHigh>( taggedPointer.getRaw() );
185 endActionStage1 = action_s::make<turnInjectionPinLow>( this );
186 endActionStage2 = action_s::make<turnInjectionPinLowStage2>( this );
187 }
188
189 // Correctly wrap injection start angle
190 float angleFromNow = eventAngle - currentPhase;
191 if (angleFromNow < 0) {
192 angleFromNow += getEngineState()->engineCycle;
193 }
194
195 // Schedule opening (stage 1 + stage 2 open together)
196 efitick_t startTime = scheduleByAngle(nullptr, nowNt, angleFromNow, startAction);
197
198 // Schedule closing stage 1
199 efitick_t turnOffTimeStage1 = startTime + US2NT((int)durationUsStage1);
200 getScheduler()->schedule("inj", nullptr, turnOffTimeStage1, endActionStage1);
201
202 // Schedule closing stage 2 (if applicable)
203 if (hasStage2Injection && endActionStage2) {
204 efitick_t turnOffTimeStage2 = startTime + US2NT((int)durationUsStage2);
205 getScheduler()->schedule("inj stage 2", nullptr, turnOffTimeStage2, endActionStage2);
206 }
207
208#if EFI_DETAILED_LOGGING
209 printf("scheduling injection angle=%.2f/delay=%d injectionDuration=%d %d\r\n", angleFromNow, (int)NT2US(startTime - nowNt), (int)durationUsStage1, (int)durationUsStage2);
210#endif
211#if EFI_DETAILED_LOGGING
212 efiPrintf("handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
213 injEventIndex,
214 injectionDurationStage1,
215 getRevolutionCounter());
216 efiPrintf("handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
217 getRevolutionCounter());
218#endif /* EFI_DETAILED_LOGGING */
219}
220
221static void handleFuel(efitick_t nowNt, float currentPhase, float nextPhase) {
223
224 efiAssertVoid(ObdCode::CUSTOM_STACK_6627, hasLotsOfRemainingStack(), "lowstck#3");
225
226 LimpState limitedFuelState = getLimpManager()->allowInjection();
227
228 // todo: eliminate state copy logic by giving limpManager it's owm limp_manager.txt and leveraging LiveData
229 engine->outputChannels.fuelCutReason = (int8_t)limitedFuelState.reason;
230 bool limitedFuel = !limitedFuelState.value;
231 if (limitedFuel) {
232 return;
233 }
234
235 // This is called in the fast callback already, but since we may have just achieved engine sync (and RPM)
236 // for the first time, force update the schedule so that we can inject immediately if necessary
238 if (!fs->isReady) {
239 fs->addFuelEvents();
240 }
241
242#if FUEL_MATH_EXTREME_LOGGING
243 if (printFuelDebug) {
244 efiPrintf("handleFuel [%.1f, %.1f) %d", currentPhase, nextPhase, getRevolutionCounter());
245 }
246#endif /* FUEL_MATH_EXTREME_LOGGING */
247
248 fs->onTriggerTooth(nowNt, currentPhase, nextPhase);
249}
250
251/**
252 * This is the main trigger event handler.
253 * Both injection and ignition are controlled from this method.
254 */
255void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_t currentPhase, angle_t nextPhase) {
257
258 if (hasFirmwareError()) {
259 /**
260 * In case on a major error we should not process any more events.
261 */
262 return;
263 }
264
265 float rpm = engine->rpmCalculator.getCachedRpm();
266 if (rpm == 0) {
267 // this happens while we just start cranking
268
269 // todo: check for 'trigger->is_synchnonized?'
270 return;
271 }
272
273 if (trgEventIndex == 0) {
274
275 if (getTriggerCentral()->checkIfTriggerConfigChanged()) {
276 getIgnitionEvents()->isReady = false; // we need to rebuild complete ignition schedule
277 getFuelSchedule()->isReady = false;
278 // moved 'triggerIndexByAngle' into trigger initialization (why was it invoked from here if it's only about trigger shape & optimization?)
279 // see updateTriggerConfiguration() -> prepareOutputSignals()
280
281 // we need this to apply new 'triggerIndexByAngle' values
283 }
284 }
285
286 engine->engineModules.apply_all([=](auto & m) {
287 m.onEnginePhase(rpm, edgeTimestamp, currentPhase, nextPhase);
288 });
289
290 /**
291 * For fuel we schedule start of injection based on trigger angle, and then inject for
292 * specified duration of time
293 */
294 handleFuel(edgeTimestamp, currentPhase, nextPhase);
295
296 engine->module<TriggerScheduler>()->scheduleEventsUntilNextTriggerTooth(
297 rpm, edgeTimestamp, currentPhase, nextPhase);
298
299 /**
300 * For spark we schedule both start of coil charge and actual spark based on trigger angle
301 */
302 onTriggerEventSparkLogic(rpm, edgeTimestamp, currentPhase, nextPhase);
303}
304
305#endif /* EFI_ENGINE_CONTROL */
Non-volatile backup-RAM registers support.
void periodicFastCallback()
Definition engine.cpp:556
RpmCalculator rpmCalculator
Definition engine.h:306
TunerStudioOutputChannels outputChannels
Definition engine.h:109
constexpr auto & module()
Definition engine.h:200
type_list< Mockable< InjectorModelPrimary >, Mockable< InjectorModelSecondary >,#if EFI_IDLE_CONTROL Mockable< IdleController >,#endif TriggerScheduler,#if EFI_HPFP &&EFI_ENGINE_CONTROL Mockable< HpfpController >,#endif #if EFI_ENGINE_CONTROL Mockable< ThrottleModel >,#endif #if EFI_ALTERNATOR_CONTROL AlternatorController,#endif MainRelayController, Mockable< IgnitionController >, Mockable< AcController >, PrimeController, DfcoController,#if EFI_HD_ACR HarleyAcr,#endif Mockable< WallFuelController >, KnockController, SensorChecker,#if EFI_ENGINE_CONTROL Mockable< LimpManager >,#endif #if EFI_VVT_PID VvtController1, VvtController2, VvtController3, VvtController4,#endif #if EFI_BOOST_CONTROL BoostController,#endif TpsAccelEnrichment,#if EFI_LAUNCH_CONTROL NitrousController,#endif #if EFI_LTFT_CONTROL LongTermFuelTrim,#endif ShortTermFuelTrim,#include "modules_list_generated.h" EngineModule > engineModules
Definition engine.h:194
virtual bool isCranking() const =0
angle_t engineCycle
float injectionStage2Fraction
void onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase)
InjectorOutputPin * outputsStage2[MAX_WIRES_COUNT]
WallFuel wallFuel
void onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase)
InjectorOutputPin * outputs[MAX_WIRES_COUNT]
float injectionStartAngle
uint8_t cylinderNumber
void close(efitick_t nowNt)
LimpState allowInjection() const
const char * getName() const
Definition efi_gpio.cpp:422
float getCachedRpm() const
static float getOrZero(SensorType type)
Definition sensor.h:83
float adjust(float desiredMassGrams)
Definition wall_fuel.cpp:14
bool isPhaseInRange(float test, float current, float next)
Definition efilib.cpp:176
efitick_t getTimeNowNt()
Definition efitime.cpp:19
LimpManager * getLimpManager()
Definition engine.cpp:596
Scheduler * getScheduler()
Definition engine.cpp:585
TriggerCentral * getTriggerCentral()
Definition engine.cpp:590
FuelSchedule * getFuelSchedule()
Definition engine.cpp:600
EngineRotationState * getEngineRotationState()
Definition engine.cpp:573
EngineState * getEngineState()
Definition engine.cpp:577
IgnitionEventList * getIgnitionEvents()
Definition engine.cpp:604
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
floatms_t getCrankshaftRevolutionTimeMs(float rpm)
bool warning(ObdCode code, const char *fmt,...)
int getNumberOfInjections(injection_mode_e mode)
void endSimultaneousInjectionOnlyTogglePins()
bool printFuelDebug
void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_t currentPhase, angle_t nextPhase)
static void handleFuel(efitick_t nowNt, float currentPhase, float nextPhase)
void turnInjectionPinLow(InjectionEvent *event)
void endSimultaneousInjection(InjectionEvent *event)
static void turnInjectionPinLowStage2(InjectionEvent *event)
Main logic header.
@ CUSTOM_OBD_NEG_INJECTION
@ CUSTOM_STACK_6627
@ CUSTOM_OBD_NAN_INJECTION
@ MainTriggerCallback
@ HandleFuel
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s const &action)
float angle_t
void onTriggerEventSparkLogic(float rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
const bool value
const ClearReason reason
virtual void schedule(const char *msg, scheduling_s *scheduling, efitick_t targetTime, action_s const &action)=0
Schedule an action to be executed in the future.
float injectionMass[MAX_CYLINDER_COUNT]
scaled_channel< uint16_t, 300, 1 > actualLastInjectionStage2
scaled_channel< uint16_t, 300, 1 > actualLastInjection
printf("\n")