Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | /* | |||
2 | * @file trigger_central.h | |||
3 | * | |||
4 | * @date Feb 23, 2014 | |||
5 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
6 | */ | |||
7 | ||||
8 | #pragma once | |||
9 | ||||
10 | #include "rusefi_enums.h" | |||
11 | #include "listener_array.h" | |||
12 | #include "trigger_decoder.h" | |||
13 | #include "instant_rpm_calculator.h" | |||
14 | #include "trigger_central_generated.h" | |||
15 | #include <rusefi/timer.h> | |||
16 | #include "pin_repository.h" | |||
17 | #include "local_version_holder.h" | |||
18 | #include "cyclic_buffer.h" | |||
19 | ||||
20 | #define MAP_CAM_BUFFER 64 | |||
21 | ||||
22 | #ifndef RPM_LOW_THRESHOLD | |||
23 | // no idea what is the best value, 25 is as good as any other guess | |||
24 | #define RPM_LOW_THRESHOLD 25 | |||
25 | #endif | |||
26 | ||||
27 | class Engine; | |||
28 | typedef void (*ShaftPositionListener)(trigger_event_e signal, uint32_t index, efitick_t edgeTimestamp); | |||
29 | ||||
30 | #define HAVE_CAM_INPUT() (isBrainPinValid(engineConfiguration->camInputs[0])) | |||
31 | ||||
32 | class TriggerNoiseFilter { | |||
33 | public: | |||
34 | void resetAccumSignalData(); | |||
35 | bool noiseFilter(efitick_t nowNt, | |||
36 | TriggerDecoderBase* triggerState, | |||
37 | trigger_event_e signal); | |||
38 | ||||
39 | efitick_t lastSignalTimes[HW_EVENT_TYPES]; | |||
40 | efitick_t accumSignalPeriods[HW_EVENT_TYPES]; | |||
41 | efitick_t accumSignalPrevPeriods[HW_EVENT_TYPES]; | |||
42 | }; | |||
43 | ||||
44 | /** | |||
45 | * Maybe merge TriggerCentral and TriggerState classes into one class? | |||
46 | * Probably not: we have an instance of TriggerState which is used for trigger initialization, | |||
47 | * also composition probably better than inheritance here | |||
48 | */ | |||
49 | class TriggerCentral final : public trigger_central_s { | |||
50 | public: | |||
51 | TriggerCentral(); | |||
52 | /** | |||
53 | * we have two kinds of sync: | |||
54 | * this method is about detecting of exact engine phase with 720 degree precision usually based on cam wheel decoding | |||
55 | * not to be confused with a totally different trigger _wheel_ sync which could be either crank wheel sync or cam wheel sync | |||
56 | */ | |||
57 | angle_t syncEnginePhaseAndReport(int divider, int remainder); | |||
58 | void handleShaftSignal(trigger_event_e signal, efitick_t timestamp); | |||
59 | int getHwEventCounter(int index) const; | |||
60 | void resetCounters(); | |||
61 | void validateCamVvtCounters(); | |||
62 | void applyShapesConfiguration(); | |||
63 | ||||
64 | angle_t findNextTriggerToothAngle(int nextToothIndex); | |||
65 | ||||
66 | InstantRpmCalculator instantRpm; | |||
67 | ||||
68 | 1490 | void prepareTriggerShape() { | ||
69 | #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
70 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1490 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1490 times.
|
1490 | if (triggerShape.shapeDefinitionError) { |
71 | // Nothing to do here if there's a problem with the trigger shape | |||
72 | ✗ | return; | ||
73 | } | |||
74 | ||||
75 | 1490 | triggerFormDetails.prepareEventAngles(&triggerShape); | ||
76 | #endif | |||
77 | } | |||
78 | ||||
79 | // this is useful at least for real hardware integration testing - maybe a proper solution would be to simply | |||
80 | // GND input pins instead of leaving them floating | |||
81 | bool hwTriggerInputEnabled = true; | |||
82 | ||||
83 | cyclic_buffer<int> triggerErrorDetection; | |||
84 | ||||
85 | /** | |||
86 | * See also triggerSimulatorRpm | |||
87 | */ | |||
88 | bool directSelfStimulation = false; | |||
89 | ||||
90 | PrimaryTriggerConfiguration primaryTriggerConfiguration; | |||
91 | #if CAMS_PER_BANK == 1 | |||
92 | VvtTriggerConfiguration vvtTriggerConfiguration[CAMS_PER_BANK] = {{"VVT1 ", 0}}; | |||
93 | #else | |||
94 | VvtTriggerConfiguration vvtTriggerConfiguration[CAMS_PER_BANK] = {{"VVT1 ", 0}, {"VVT2 ", 1}}; | |||
95 | #endif | |||
96 | ||||
97 | LocalVersionHolder triggerVersion; | |||
98 | ||||
99 | /** | |||
100 | * By the way: | |||
101 | * 'cranking' means engine is not stopped and the rpm are below crankingRpm | |||
102 | * 'running' means RPM are above crankingRpm | |||
103 | * 'spinning' means the engine is not stopped | |||
104 | */ | |||
105 | // todo: combine with other RpmCalculator fields? | |||
106 | /** | |||
107 | * this is set to true each time we register a trigger tooth signal | |||
108 | */ | |||
109 | bool isSpinningJustForWatchdog = false; | |||
110 | ||||
111 | float mapCamPrevCycleValue = 0; | |||
112 | int prevChangeAtCycle = 0; | |||
113 | ||||
114 | /** | |||
115 | * value of 'triggerShape.getLength()' | |||
116 | * pre-calculating this value is a performance optimization | |||
117 | */ | |||
118 | uint32_t engineCycleEventCount = 0; | |||
119 | /** | |||
120 | * true if a recent configuration change has changed any of the trigger settings which | |||
121 | * we have not adjusted for yet | |||
122 | */ | |||
123 | bool triggerConfigChangedOnLastConfigurationChange = false; | |||
124 | ||||
125 | bool checkIfTriggerConfigChanged(); | |||
126 | #if EFI_UNIT_TEST | |||
127 | bool isTriggerConfigChanged(); | |||
128 | #endif // EFI_UNIT_TEST | |||
129 | ||||
130 | bool isTriggerDecoderError(); | |||
131 | ||||
132 | expected<float> getCurrentEnginePhase(efitick_t nowNt) const; | |||
133 | ||||
134 | 5630 | float getSecondsSinceTriggerEvent(efitick_t nowNt) const { | ||
135 | 5630 | return m_lastEventTimer.getElapsedSeconds(nowNt); | ||
136 | } | |||
137 | ||||
138 | 5630 | bool engineMovedRecently(efitick_t nowNt) const { | ||
139 | // todo: this user-defined property is a quick solution, proper fix https://github.com/rusefi/rusefi/issues/6593 is needed | |||
140 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 5630 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 5630 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 5630 times.
|
5630 | if (engineConfiguration->triggerEventsTimeoutMs != 0 && m_lastEventTimer.hasElapsedMs(engineConfiguration->triggerEventsTimeoutMs)) { |
141 | ✗ | return false; | ||
142 | } | |||
143 | ||||
144 | 5630 | constexpr float oneRevolutionLimitInSeconds = 60.0 / RPM_LOW_THRESHOLD; | ||
145 | 5630 | auto maxAverageToothTime = oneRevolutionLimitInSeconds / triggerShape.getSize(); | ||
146 | ||||
147 | // Some triggers may have long gaps (with many teeth), don't count that as stopped! | |||
148 | 5630 | auto maxAllowedGap = maxAverageToothTime * 10; | ||
149 | ||||
150 | // Clamp between 0.1 seconds ("instant" for a human) and worst case of one engine cycle on low tooth count wheel | |||
151 | 5630 | maxAllowedGap = clampF(0.1f, maxAllowedGap, oneRevolutionLimitInSeconds); | ||
152 | ||||
153 |
3/4✓ Branch 1 taken 2857 times.
✓ Branch 2 taken 2773 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2857 times.
|
5630 | return (getSecondsSinceTriggerEvent(nowNt) < maxAllowedGap) || directSelfStimulation; | |
154 | } | |||
155 | ||||
156 | 3295 | bool engineMovedRecently() const { | ||
157 | 3295 | return engineMovedRecently(getTimeNowNt()); | ||
158 | } | |||
159 | ||||
160 | TriggerNoiseFilter noiseFilter; | |||
161 | ||||
162 | angle_t getVVTPosition(uint8_t bankIndex, uint8_t camIndex); | |||
163 | ||||
164 | #if EFI_UNIT_TEST | |||
165 | // latest VVT event position (could be not synchronization event) | |||
166 | angle_t currentVVTEventPosition[BANKS_COUNT][CAMS_PER_BANK]; | |||
167 | #endif // EFI_UNIT_TEST | |||
168 | ||||
169 | // synchronization event position | |||
170 | angle_t vvtPosition[BANKS_COUNT][CAMS_PER_BANK]; | |||
171 | ||||
172 | #if EFI_SHAFT_POSITION_INPUT | |||
173 | PrimaryTriggerDecoder triggerState; | |||
174 | #endif //EFI_SHAFT_POSITION_INPUT | |||
175 | ||||
176 | TriggerWaveform triggerShape; | |||
177 | ||||
178 | VvtTriggerDecoder vvtState[BANKS_COUNT][CAMS_PER_BANK] = { | |||
179 | { | |||
180 | "VVT B1 Int", | |||
181 | #if CAMS_PER_BANK >= 2 | |||
182 | "VVT B1 Exh" | |||
183 | #endif | |||
184 | }, | |||
185 | #if BANKS_COUNT >= 2 | |||
186 | { | |||
187 | "VVT B2 Int", | |||
188 | #if CAMS_PER_BANK >= 2 | |||
189 | "VVT B1 Exh" | |||
190 | #endif | |||
191 | } | |||
192 | #endif | |||
193 | }; | |||
194 | ||||
195 | TriggerWaveform vvtShape[CAMS_PER_BANK]; | |||
196 | ||||
197 | TriggerFormDetails triggerFormDetails; | |||
198 | ||||
199 | // Keep track of the last time we got a valid trigger event | |||
200 | Timer m_lastEventTimer; | |||
201 | ||||
202 | /** | |||
203 | * this is based on engineSnifferRpmThreshold settings and current RPM | |||
204 | */ | |||
205 | bool isEngineSnifferEnabled = false; | |||
206 | ||||
207 | void applyCamGapOverride(); | |||
208 | bool isMapCamSync(efitick_t nowNt, float currentPhase); | |||
209 | private: | |||
210 | void decodeMapCam(int triggerIndexForListeners, efitick_t nowNt, float currentPhase); | |||
211 | void applyTriggerGapOverride(); | |||
212 | ||||
213 | bool isToothExpectedNow(efitick_t timestamp); | |||
214 | ||||
215 | // Time since the last tooth | |||
216 | Timer m_lastToothTimer; | |||
217 | // Phase of the last tooth relative to the sync point | |||
218 | float m_lastToothPhaseFromSyncPoint; | |||
219 | ||||
220 | // At what engine phase do we expect the next tooth to arrive? | |||
221 | // Used for checking whether your trigger pattern is correct. | |||
222 | expected<float> expectedNextPhase = unexpected; | |||
223 | }; | |||
224 | ||||
225 | void triggerInfo(void); | |||
226 | void hwHandleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp); | |||
227 | void handleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp); | |||
228 | void hwHandleVvtCamSignal(TriggerValue front, efitick_t timestamp, int index); | |||
229 | void hwHandleVvtCamSignal(bool isRising, efitick_t timestamp, int index); | |||
230 | void handleVvtCamSignal(TriggerValue front, efitick_t timestamp, int index); | |||
231 | ||||
232 | void validateTriggerInputs(); | |||
233 | ||||
234 | void initTriggerCentral(); | |||
235 | ||||
236 | int isSignalDecoderError(void); | |||
237 | ||||
238 | void onConfigurationChangeTriggerCallback(); | |||
239 | ||||
240 | #define SYMMETRICAL_CRANK_SENSOR_DIVIDER (2 * 2) | |||
241 | #define SYMMETRICAL_THREE_TIMES_CRANK_SENSOR_DIVIDER (3 * 2) | |||
242 | #define SYMMETRICAL_SIX_TIMES_CRANK_SENSOR_DIVIDER (6 * 2) | |||
243 | #define SYMMETRICAL_TWELVE_TIMES_CRANK_SENSOR_DIVIDER (12 * 2) | |||
244 | ||||
245 | TriggerCentral * getTriggerCentral(); | |||
246 | int getCrankDivider(operation_mode_e operationMode); | |||
247 | ||||
248 | 174987 | constexpr bool isTriggerUpEvent(trigger_event_e event) { | ||
249 |
2/3✓ Branch 0 taken 71918 times.
✓ Branch 1 taken 103069 times.
✗ Branch 2 not taken.
|
174987 | switch (event) { | |
250 |
1/1✓ Decision 'true' taken 71918 times.
|
71918 | case SHAFT_PRIMARY_FALLING: | |
251 | case SHAFT_SECONDARY_FALLING: | |||
252 |
1/1✓ Decision 'true' taken 71918 times.
|
71918 | return false; | |
253 |
1/1✓ Decision 'true' taken 103069 times.
|
103069 | case SHAFT_PRIMARY_RISING: | |
254 | case SHAFT_SECONDARY_RISING: | |||
255 |
1/1✓ Decision 'true' taken 103069 times.
|
103069 | return true; | |
256 | } | |||
257 | ||||
258 | ✗ | return false; | ||
259 | } | |||
260 |