rusEFI
The most advanced open source ECU
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 
32 #include "main_trigger_callback.h"
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"
39 #include "local_version_holder.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 
52  event->update();
53 }
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 
78 void 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  engine->module<TripOdometer>()->consumeFuel(actualInjectedMass, nowNt);
111  }
112 #endif // EFI_VEHICLE_SPEED
113 
114  const floatms_t injectionDurationStage1 = engine->module<InjectorModelPrimary>()->getInjectionDuration(injectionMassStage1);
115  const floatms_t injectionDurationStage2 = injectionMassStage2 > 0 ? engine->module<InjectorModelSecondary>()->getInjectionDuration(injectionMassStage2) : 0;
116 
117 #if EFI_PRINTF_FUEL_DETAILS
118  if (printFuelDebug) {
119  printf("fuel injectionDuration=%.2fms adjusted=%.2fms\n",
120  getEngineState()->injectionDuration,
121  injectionDurationStage1);
122  }
123 #endif /*EFI_PRINTF_FUEL_DETAILS */
124 
125  if (this->cylinderNumber == 0) {
126  engine->outputChannels.actualLastInjection = injectionDurationStage1;
127  engine->outputChannels.actualLastInjectionStage2 = injectionDurationStage2;
128  }
129 
130  if (cisnan(injectionDurationStage1) || cisnan(injectionDurationStage2)) {
131  warning(ObdCode::CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse");
132  return;
133  }
134  if (injectionDurationStage1 < 0) {
135  warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDurationStage1);
136  return;
137  }
138 
139  // If somebody commanded an impossibly short injection, do nothing.
140  // Durations under 50us-ish aren't safe for the scheduler
141  // as their order may be swapped, resulting in a stuck open injector
142  // see https://github.com/rusefi/rusefi/pull/596 for more details
143  if (injectionDurationStage1 < 0.050f)
144  {
145  return;
146  }
147 
148  floatus_t durationUsStage1 = MS2US(injectionDurationStage1);
149  floatus_t durationUsStage2 = MS2US(injectionDurationStage2);
150 
151  // Only bother with the second stage if it's long enough to be relevant
152  bool hasStage2Injection = durationUsStage2 > 50;
153 
154 #if EFI_PRINTF_FUEL_DETAILS
155  if (printFuelDebug) {
156  InjectorOutputPin *output = outputs[0];
157  printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->getName(), (int)durationUsStage1,
159  }
160 #endif /*EFI_PRINTF_FUEL_DETAILS */
161 
162  action_s startAction, endActionStage1, endActionStage2;
163  // We use different callbacks based on whether we're running sequential mode or not - everything else is the same
164  if (isSimultaneous) {
165  startAction = startSimultaneousInjection;
166  endActionStage1 = { &endSimultaneousInjection, this };
167  } else {
168  uintptr_t startActionPtr = reinterpret_cast<uintptr_t>(this);
169 
170  if (hasStage2Injection) {
171  // Set the low bit in the arg if there's a secondary injection to start too
172  startActionPtr |= 1;
173  }
174 
175  // sequential or batch
176  startAction = { &turnInjectionPinHigh, startActionPtr };
177  endActionStage1 = { &turnInjectionPinLow, this };
178  endActionStage2 = { &turnInjectionPinLowStage2, this };
179  }
180 
181  // Correctly wrap injection start angle
182  float angleFromNow = eventAngle - currentPhase;
183  if (angleFromNow < 0) {
184  angleFromNow += getEngineState()->engineCycle;
185  }
186 
187  // Schedule opening (stage 1 + stage 2 open together)
188  efitick_t startTime = scheduleByAngle(nullptr, nowNt, angleFromNow, startAction);
189 
190  // Schedule closing stage 1
191  efitick_t turnOffTimeStage1 = startTime + US2NT((int)durationUsStage1);
192  getExecutorInterface()->scheduleByTimestampNt("inj", nullptr, turnOffTimeStage1, endActionStage1);
193 
194  // Schedule closing stage 2 (if applicable)
195  if (hasStage2Injection && endActionStage2) {
196  efitick_t turnOffTimeStage2 = startTime + US2NT((int)durationUsStage2);
197  getExecutorInterface()->scheduleByTimestampNt("inj stage 2", nullptr, turnOffTimeStage2, endActionStage2);
198  }
199 
200 #if EFI_DEFAILED_LOGGING
201  printf("scheduling injection angle=%.2f/delay=%d injectionDuration=%d %d\r\n", angleFromNow, (int)NT2US(startTime - nowNt), (int)durationUsStage1, (int)durationUsStage2);
202 #endif
203 #if EFI_DEFAILED_LOGGING
204  efiPrintf("handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
205  injEventIndex,
206  injectionDurationStage1,
207  getRevolutionCounter());
208  efiPrintf("handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
209  getRevolutionCounter());
210 #endif /* EFI_DEFAILED_LOGGING */
211 }
212 
213 static void handleFuel(efitick_t nowNt, float currentPhase, float nextPhase) {
215 
216  efiAssertVoid(ObdCode::CUSTOM_STACK_6627, hasLotsOfRemainingStack(), "lowstck#3");
217 
218  LimpState limitedFuelState = getLimpManager()->allowInjection();
219 
220  // todo: eliminate state copy logic by giving limpManager it's owm limp_manager.txt and leveraging LiveData
221  engine->outputChannels.fuelCutReason = (int8_t)limitedFuelState.reason;
222  bool limitedFuel = !limitedFuelState.value;
223  if (limitedFuel) {
224  return;
225  }
226 
227  // This is called in the fast callback already, but since we may have just achieved engine sync (and RPM)
228  // for the first time, force update the schedule so that we can inject immediately if necessary
230  if (!fs->isReady) {
231  fs->addFuelEvents();
232  }
233 
234 #if FUEL_MATH_EXTREME_LOGGING
235  if (printFuelDebug) {
236  efiPrintf("handleFuel [%.1f, %.1f) %d", currentPhase, nextPhase, getRevolutionCounter());
237  }
238 #endif /* FUEL_MATH_EXTREME_LOGGING */
239 
240  fs->onTriggerTooth(nowNt, currentPhase, nextPhase);
241 }
242 
243 /**
244  * This is the main trigger event handler.
245  * Both injection and ignition are controlled from this method.
246  */
247 void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_t currentPhase, angle_t nextPhase) {
249 
250 #if ! HW_CHECK_MODE
251  if (hasFirmwareError()) {
252  /**
253  * In case on a major error we should not process any more events.
254  */
255  return;
256  }
257 #endif // HW_CHECK_MODE
258 
259  int rpm = engine->rpmCalculator.getCachedRpm();
260  if (rpm == 0) {
261  // this happens while we just start cranking
262 
263  // todo: check for 'trigger->is_synchnonized?'
264  return;
265  }
266  if (rpm == NOISY_RPM) {
268  return;
269  }
270 
271 
272  if (trgEventIndex == 0) {
273 
274  if (getTriggerCentral()->checkIfTriggerConfigChanged()) {
275  getIgnitionEvents()->isReady = false; // we need to rebuild complete ignition schedule
276  getFuelSchedule()->isReady = false;
277  // moved 'triggerIndexByAngle' into trigger initialization (why was it invoked from here if it's only about trigger shape & optimization?)
278  // see updateTriggerWaveform() -> prepareOutputSignals()
279 
280  // we need this to apply new 'triggerIndexByAngle' values
282  }
283  }
284 
285  /**
286  * For fuel we schedule start of injection based on trigger angle, and then inject for
287  * specified duration of time
288  */
289  handleFuel(edgeTimestamp, currentPhase, nextPhase);
290 
291  engine->module<TriggerScheduler>()->scheduleEventsUntilNextTriggerTooth(
292  rpm, edgeTimestamp, currentPhase, nextPhase);
293 
294  /**
295  * For spark we schedule both start of coil charge and actual spark based on trigger angle
296  */
297  onTriggerEventSparkLogic(rpm, edgeTimestamp, currentPhase, nextPhase);
298 }
299 
300 #endif /* EFI_ENGINE_CONTROL */
Non-volatile backup-RAM registers support.
void periodicFastCallback()
Definition: engine.cpp:557
RpmCalculator rpmCalculator
Definition: engine.h:262
constexpr auto & module()
Definition: engine.h:174
TunerStudioOutputChannels outputChannels
Definition: engine.h:96
virtual bool isCranking() const =0
angle_t engineCycle
Definition: engine_state.h:27
float injectionMass[MAX_CYLINDER_COUNT]
Definition: engine_state.h:35
float injectionStage2Fraction
Definition: engine_state.h:37
void onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase)
void addFuelEvents()
InjectorOutputPin * outputsStage2[MAX_WIRES_COUNT]
Definition: fuel_schedule.h:51
WallFuel wallFuel
Definition: fuel_schedule.h:46
void onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase)
InjectorOutputPin * outputs[MAX_WIRES_COUNT]
Definition: fuel_schedule.h:50
float injectionStartAngle
Definition: fuel_schedule.h:52
uint8_t cylinderNumber
Definition: fuel_schedule.h:44
void close(efitick_t nowNt)
LimpState allowInjection() const
const char * getName() const
Definition: efi_gpio.cpp:399
float getCachedRpm() const
static float getOrZero(SensorType type)
Definition: sensor.h:92
float adjust(float desiredMassGrams)
Definition: wall_fuel.cpp:14
bool isPhaseInRange(float test, float current, float next)
Definition: efilib.cpp:195
efitick_t getTimeNowNt()
Definition: efitime.cpp:19
EngineRotationState * getEngineRotationState()
Definition: engine.cpp:572
FuelSchedule * getFuelSchedule()
Definition: engine.cpp:599
LimpManager * getLimpManager()
Definition: engine.cpp:595
IgnitionEventList * getIgnitionEvents()
Definition: engine.cpp:603
ExecutorInterface * getExecutorInterface()
Definition: engine.cpp:584
EngineState * getEngineState()
Definition: engine.cpp:576
TriggerCentral * getTriggerCentral()
Definition: engine.cpp:589
Engine * engine
floatms_t getCrankshaftRevolutionTimeMs(int rpm)
Definition: engine_math.cpp:40
bool warning(ObdCode code, const char *fmt,...)
int getNumberOfInjections(injection_mode_e mode)
Definition: fuel_math.cpp:239
void turnInjectionPinHigh(uintptr_t arg)
void startSimultaneousInjection(void *)
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.
@ OBD_Crankshaft_Position_Sensor_A_Circuit_Malfunction
@ CUSTOM_OBD_NEG_INJECTION
@ CUSTOM_STACK_6627
@ CUSTOM_OBD_NAN_INJECTION
@ MainTriggerCallback
@ HandleFuel
engine_configuration_s * engineConfiguration
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s action)
float floatms_t
Definition: rusefi_types.h:70
float floatus_t
Definition: rusefi_types.h:71
float angle_t
Definition: rusefi_types.h:61
void onTriggerEventSparkLogic(int rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
virtual void scheduleByTimestampNt(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s action)=0
const bool value
Definition: limp_manager.h:76
const ClearReason reason
Definition: limp_manager.h:77
scaled_channel< uint16_t, 300, 1 > actualLastInjectionStage2
scaled_channel< uint16_t, 300, 1 > actualLastInjection
composite packet size
printf("\n")