rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
fuel_schedule.cpp
Go to the documentation of this file.
1/**
2 * @file fuel_schedule.cpp
3 *
4 * Handles injection scheduling
5 */
6
7#include "pch.h"
8
9#if EFI_ENGINE_CONTROL
10
12 auto const nowNt{ getTimeNowNt() };
13
14 auto const taggedPointer{ TaggedPointer<InjectionEvent>::fromRaw(arg) };
15 auto const event{ taggedPointer.getOriginalPointer() };
16 auto const hasStage2Injection{ taggedPointer.getFlag() };
17
18 for (auto const& output: event->outputs) {
19 if (output) {
20 output->open(nowNt);
21 }
22 }
23
24 if (hasStage2Injection) {
25 for (auto const& output: event->outputsStage2) {
26 if (output) {
27 output->open(nowNt);
28 }
29 }
30 }
31}
32
34 for (int cylinderIndex = 0; cylinderIndex < MAX_CYLINDER_COUNT; cylinderIndex++) {
35 elements[cylinderIndex].setIndex(cylinderIndex);
36 }
37}
38
42
44 isReady = false;
45}
46
48 for (auto& inj : enginePins.injectors) {
49 inj.reset();
50 }
51}
52
53// Determines how much to adjust injection opening angle based on the injection's duration and the current phasing mode
54static float getInjectionAngleCorrection(float fuelMs, float oneDegreeUs) {
56 if (mode == InjectionTimingMode::Start) {
57 // Start of injection gets no correction for duration
58 return 0;
59 }
60
61 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(fuelMs), "NaN fuelMs", false);
62
63 angle_t injectionDurationAngle = MS2US(fuelMs) / oneDegreeUs;
64 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(injectionDurationAngle), "NaN injectionDurationAngle", false);
65 assertAngleRange(injectionDurationAngle, "injectionDuration_r", ObdCode::CUSTOM_INJ_DURATION);
66
67 if (mode == InjectionTimingMode::Center) {
68 // Center of injection is half-corrected for duration
69 return injectionDurationAngle * 0.5f;
70 } else {
71 // End of injection gets "full correction" so we advance opening by the full duration
72 return injectionDurationAngle;
73 }
74}
75
76// Returns the start angle of this injector in engine coordinates (0-720 for a 4 stroke),
77// or unexpected if unable to calculate the start angle due to missing information.
79 floatus_t oneDegreeUs = getEngineRotationState()->getOneDegreeUs(); // local copy
80 if (std::isnan(oneDegreeUs)) {
81 // in order to have fuel schedule we need to have current RPM
82 // wonder if this line slows engine startup?
83 return unexpected;
84 }
85
86 float fuelMs = getEngineState()->injectionDuration;
87 if (std::isnan(fuelMs)) {
88 return unexpected;
89 }
90
91 // injection phase may be scheduled by injection end, so we need to step the angle back
92 // for the duration of the injection
93 angle_t injectionDurationAngle = getInjectionAngleCorrection(fuelMs, oneDegreeUs);
94
95 // User configured offset - degrees after TDC combustion
97 if (std::isnan(injectionOffset)) {
98 // injection offset map not ready - we are not ready to schedule fuel events
99 return unexpected;
100 }
101
102 angle_t openingAngle = injectionOffset - injectionDurationAngle;
103 assertAngleRange(openingAngle, "openingAngle_r", ObdCode::CUSTOM_ERR_6554);
104 wrapAngle(openingAngle, "addFuel#1", ObdCode::CUSTOM_ERR_6555);
105 // TODO: should we log per-cylinder injection timing? #76
107
108 // Convert from cylinder-relative to cylinder-1-relative
110
111 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(openingAngle), "findAngle#3", false);
112 assertAngleRange(openingAngle, "findAngle#a33", ObdCode::CUSTOM_ERR_6544);
113
114 wrapAngle(openingAngle, "addFuel#2", ObdCode::CUSTOM_ERR_6555);
115
116#if EFI_UNIT_TEST
117// printf("registerInjectionEvent openingAngle=%.2f inj %d\r\n", openingAngle, cylinderNumber);
118#endif
119
120 return openingAngle;
121}
122
124 auto result = computeInjectionAngle();
125
126 if (result) {
127 // If injector duty cycle is high, lock injection SOI so that we
128 // don't miss injections at or above 100% duty
129 if (getEngineState()->shouldUpdateInjectionTiming) {
130 injectionStartAngle = result.Value;
131 }
132
133 return true;
134 } else {
135 return false;
136 }
137}
138
139/**
140 * @returns false in case of error, true if success
141 */
143 bool updatedAngle = updateInjectionAngle();
144
145 if (!updatedAngle) {
146 return false;
147 }
148
150 engine->outputChannels.currentInjectionMode = static_cast<uint8_t>(mode);
151
152 int injectorIndex;
153 if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
154 // These modes only have one injector
155 injectorIndex = 0;
156 } else if (mode == IM_SEQUENTIAL || mode == IM_BATCH) {
157 // Map order index -> cylinder index (firing order)
158 injectorIndex = getCylinderNumberAtIndex(ownIndex);
159 } else {
160 firmwareError(ObdCode::CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
161 injectorIndex = 0;
162 }
163
164 InjectorOutputPin *secondOutput;
165 InjectorOutputPin* secondOutputStage2;
166
167 if (mode == IM_BATCH) {
168 /**
169 * also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
170 */
171 // Compute the position of this cylinder's twin in the firing order
172 // Each injector gets fired as a primary (the same as sequential), but also
173 // fires the injector 360 degrees later in the firing order.
175 int secondIndex = getCylinderNumberAtIndex(secondOrder);
176 secondOutput = &enginePins.injectors[secondIndex];
177 secondOutputStage2 = &enginePins.injectorsStage2[secondIndex];
178 } else {
179 secondOutput = nullptr;
180 secondOutputStage2 = nullptr;
181 }
182
183 InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
184
185 outputs[0] = output;
186 outputs[1] = secondOutput;
187 isSimultaneous = mode == IM_SIMULTANEOUS;
188 // Stash the cylinder number so we can select the correct fueling bank later
189 cylinderNumber = injectorIndex;
190
191 outputsStage2[0] = &enginePins.injectorsStage2[injectorIndex];
192 outputsStage2[1] = secondOutputStage2;
193
194 if (!isSimultaneous && !output->isInitialized()) {
195 // todo: extract method for this index math
197 }
198
199 return true;
200}
201
203 for (size_t cylinderIndex = 0; cylinderIndex < engineConfiguration->cylindersCount; cylinderIndex++) {
204 bool result = elements[cylinderIndex].update();
205
206 if (!result) {
207 invalidate();
208 return;
209 }
210 }
211
212 // We made it through all cylinders, mark the schedule as ready so it can be used
213 isReady = true;
214}
215
216void FuelSchedule::onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase) {
217 // Wait for schedule to be built - this happens the first time we get RPM
218 if (!isReady) {
219 return;
220 }
221
222 for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
223 elements[i].onTriggerTooth(nowNt, currentPhase, nextPhase);
224 }
225}
226
227#endif // EFI_ENGINE_CONTROL
TunerStudioOutputChannels outputChannels
Definition engine.h:109
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT]
Definition efi_gpio.h:128
InjectorOutputPin injectors[MAX_CYLINDER_COUNT]
Definition efi_gpio.h:127
virtual floatus_t getOneDegreeUs()=0
angle_t injectionOffset
floatms_t injectionDuration
InjectionEvent elements[MAX_CYLINDER_COUNT]
void onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase)
static void resetOverlapping()
InjectorOutputPin * outputsStage2[MAX_WIRES_COUNT]
expected< float > computeInjectionAngle() const
WallFuel & getWallFuel()
void setIndex(uint8_t index)
WallFuel wallFuel
void onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase)
bool updateInjectionAngle()
InjectorOutputPin * outputs[MAX_WIRES_COUNT]
float injectionStartAngle
uint8_t cylinderNumber
const char * getName() const
Definition efi_gpio.cpp:422
bool isInitialized() const
Definition efi_gpio.cpp:559
EnginePins enginePins
Definition efi_gpio.cpp:24
efitick_t getTimeNowNt()
Definition efitime.cpp:19
injection_mode_e getCurrentInjectionMode()
Definition engine.cpp:548
EngineRotationState * getEngineRotationState()
Definition engine.cpp:573
EngineState * getEngineState()
Definition engine.cpp:577
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
angle_t getPerCylinderFiringOrderOffset(uint8_t cylinderIndex, uint8_t cylinderNumber)
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
size_t getCylinderNumberAtIndex(size_t cylinderIndex)
void turnInjectionPinHigh(scheduler_arg_t const arg)
static float getInjectionAngleCorrection(float fuelMs, float oneDegreeUs)
@ CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED
@ CUSTOM_INJ_DURATION
@ CUSTOM_OBD_UNEXPECTED_INJECTION_MODE
@ CUSTOM_ERR_6544
@ CUSTOM_ERR_ASSERT
@ CUSTOM_ERR_6555
@ CUSTOM_ERR_6554
injection_mode_e
float angle_t
uintptr_t scheduler_arg_t
Definition scheduler.h:21
injectionOffset("Fuel: Injection timing SOI", SensorCategory.SENSOR_INPUTS, FieldType.INT16, 56, 1.0, 0.0, 0.0, "deg")
constexpr T * getOriginalPointer() const
Definition scheduler.h:64
static constexpr TaggedPointer fromRaw(scheduler_arg_t raw)
Definition scheduler.h:52
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
TunerStudioOutputChannels * getTunerStudioOutputChannels()
Definition engine.cpp:581