rusEFI
The most advanced open source ECU
trigger_central.cpp
Go to the documentation of this file.
1 /*
2  * @file trigger_central.cpp
3  * Here we have a bunch of higher-level methods which are not directly related to actual signal decoding
4  *
5  * @date Feb 23, 2014
6  * @author Andrey Belomutskiy, (c) 2012-2020
7  */
8 
9 #include "pch.h"
10 
11 #include "trigger_central.h"
12 #include "trigger_decoder.h"
13 #include "main_trigger_callback.h"
14 #include "listener_array.h"
15 #include "hip9011.h"
16 #include "logic_analyzer.h"
17 
18 #include "local_version_holder.h"
19 #include "trigger_simulator.h"
20 #include "trigger_emulator_algo.h"
21 
22 #include "map_averaging.h"
23 #include "main_trigger_callback.h"
24 #include "status_loop.h"
25 #include "engine_sniffer.h"
27 
28 #if EFI_TUNER_STUDIO
29 #include "tunerstudio.h"
30 #endif /* EFI_TUNER_STUDIO */
31 
32 #if EFI_ENGINE_SNIFFER
34 #endif /* EFI_ENGINE_SNIFFER */
35 
37 #define DEBUG_PIN_DELAY US2NT(60)
38 
39 #define TRIGGER_WAVEFORM(x) getTriggerCentral()->triggerShape.x
40 
41 #if EFI_SHAFT_POSITION_INPUT
42 
44  vvtEventRiseCounter(),
45  vvtEventFallCounter(),
46  vvtPosition(),
47  triggerState("TRG")
48 {
49  memset(&hwEventCounters, 0, sizeof(hwEventCounters));
52 }
53 
55  memset(lastSignalTimes, 0xff, sizeof(lastSignalTimes)); // = -1
56  memset(accumSignalPeriods, 0, sizeof(accumSignalPeriods));
58 }
59 
60 int TriggerCentral::getHwEventCounter(int index) const {
61  return hwEventCounters[index];
62 }
63 
64 
65 angle_t TriggerCentral::getVVTPosition(uint8_t bankIndex, uint8_t camIndex) {
66  if (bankIndex >= BANKS_COUNT || camIndex >= CAMS_PER_BANK) {
67  return NAN;
68  }
69  return vvtPosition[bankIndex][camIndex];
70 }
71 
72 /**
73  * @return angle since trigger synchronization point, NOT angle since TDC.
74  */
75 expected<float> TriggerCentral::getCurrentEnginePhase(efitick_t nowNt) const {
77 
78  if (cisnan(oneDegreeUs)) {
79  return unexpected;
80  }
81 
82  float elapsed;
83  float toothPhase;
84 
85  {
86  // under lock to avoid mismatched tooth phase and time
87  chibios_rt::CriticalSectionLocker csl;
88 
89  elapsed = m_lastToothTimer.getElapsedUs(nowNt);
90  toothPhase = m_lastToothPhaseFromSyncPoint;
91  }
92 
93  return toothPhase + elapsed / oneDegreeUs;
94 }
95 
96 /**
97  * todo: why is this method NOT reciprocal to getRpmMultiplier?!
98  */
99 int getCrankDivider(operation_mode_e operationMode) {
100  switch (operationMode) {
102  return 2;
104  return SYMMETRICAL_CRANK_SENSOR_DIVIDER;
106  return SYMMETRICAL_THREE_TIMES_CRANK_SENSOR_DIVIDER;
108  return SYMMETRICAL_SIX_TIMES_CRANK_SENSOR_DIVIDER;
110  return SYMMETRICAL_TWELVE_TIMES_CRANK_SENSOR_DIVIDER;
111  case OM_NONE:
113  case TWO_STROKE:
114  // That's easy - trigger cycle matches engine cycle
115  return 1;
116  /* let's NOT handle default in order to benefit from -Werror=switch */
117  }
118  /**
119  wow even while we explicitly handle all enumerations in the switch above we still need a return statement due to
120  https://stackoverflow.com/questions/34112483/gcc-how-best-to-handle-warning-about-unreachable-end-of-function-after-switch
121  */
122  criticalError("unreachable getCrankDivider");
123  return 1;
124 }
125 
126 static bool vvtWithRealDecoder(vvt_mode_e vvtMode) {
127  return vvtMode != VVT_INACTIVE
128  && vvtMode != VVT_TOYOTA_3_TOOTH /* VVT_2JZ is an unusual 3/0 missed tooth symmetrical wheel */
129  && vvtMode != VVT_HONDA_K_INTAKE
130  && vvtMode != VVT_MAP_V_TWIN
131  && vvtMode != VVT_SINGLE_TOOTH;
132 }
133 
135  angle_t engineCycle = getEngineCycle(getEngineRotationState()->getOperationMode());
136 
137  angle_t totalShift = triggerState.syncEnginePhase(divider, remainder, engineCycle);
138  if (totalShift != 0) {
139  // Reset instant RPM, since the engine phase has now changed, invalidating the tooth history buffer
140  // maybe TODO: could/should we rotate the buffer around to re-align it instead? Is that worth it?
142  }
143  return totalShift;
144 }
145 
146 static void turnOffAllDebugFields(void *arg) {
147  (void)arg;
148 #if EFI_PROD_CODE
149  for (int index = 0;index<TRIGGER_INPUT_PIN_COUNT;index++) {
151  writePad("trigger debug", engineConfiguration->triggerInputDebugPins[index], 0);
152  }
153  }
154  for (int index = 0;index<CAM_INPUTS_COUNT;index++) {
156  writePad("cam debug", engineConfiguration->camInputsDebug[index], 0);
157  }
158  }
159 #endif /* EFI_PROD_CODE */
160 }
161 
162 static angle_t adjustCrankPhase(int camIndex) {
163  float maxSyncThreshold = engineConfiguration->maxCamPhaseResolveRpm;
164  if (maxSyncThreshold != 0 && Sensor::getOrZero(SensorType::Rpm) > maxSyncThreshold) {
165  // The user has elected to stop trying to resolve crank phase after some RPM.
166  // Maybe their cam sensor only works at low RPM or something.
167  // Anyway, don't try to change crank phase at all, and return that we made no change.
168  return 0;
169  }
170 
172 
173  auto crankDivider = getCrankDivider(operationMode);
174  if (crankDivider == 1) {
175  // Crank divider of 1 means there's no ambiguity, so don't try to resolve it
176  return 0;
177  }
178 
180 
181  vvt_mode_e vvtMode = engineConfiguration->vvtMode[camIndex];
182  switch (vvtMode) {
183  case VVT_MAP_V_TWIN:
184  case VVT_MITSUBISHI_4G63:
185  case VVT_MITSUBISHI_4G9x:
186  return tc->syncEnginePhaseAndReport(crankDivider, 1);
187  case VVT_SINGLE_TOOTH:
188  case VVT_NISSAN_VQ:
189  case VVT_BOSCH_QUICK_START:
190  case VVT_MIATA_NB:
191  case VVT_TOYOTA_3_TOOTH:
192  case VVT_TOYOTA_4_1:
193  case VVT_FORD_COYOTE:
194  case VVT_FORD_ST170:
195  case VVT_BARRA_3_PLUS_1:
196  case VVT_NISSAN_MR:
197  case VVT_MAZDA_SKYACTIV:
198  case VVT_MITSUBISHI_4G69:
199  case VVT_MITSUBISHI_3A92:
200  case VVT_MITSUBISHI_6G72:
201  case VVT_MITSUBISHI_6G75:
202  case VVT_HONDA_K_EXHAUST:
203  case VVT_HONDA_CBR_600:
204  return tc->syncEnginePhaseAndReport(crankDivider, 0);
205  case VVT_HONDA_K_INTAKE:
206  // with 4 evenly spaced tooth we cannot use this wheel for engine sync
207  criticalError("Honda K Intake is not suitable for engine sync");
208  [[fallthrough]];
209  case VVT_INACTIVE:
210  // do nothing
211  return 0;
212  }
213  return 0;
214 }
215 
216 /**
217  * See also wrapAngle
218  */
219 static angle_t wrapVvt(angle_t vvtPosition, int period) {
220  // Wrap VVT position in to the range [-360, 360)
221  while (vvtPosition < -period / 2) {
222  vvtPosition += period;
223  }
224  while (vvtPosition >= period / 2) {
225  vvtPosition -= period;
226  }
227  return vvtPosition;
228 }
229 
230 static void logVvtFront(bool useOnlyRise, bool isImportantFront, TriggerValue front, efitick_t nowNt, int index) {
231  if (isImportantFront && isBrainPinValid(engineConfiguration->camInputsDebug[index])) {
232 #if EFI_PROD_CODE
233  writePad("cam debug", engineConfiguration->camInputsDebug[index], 1);
234 #endif /* EFI_PROD_CODE */
236  }
237 
239  // If we care about both edges OR displayLogicLevel is set, log every front exactly as it is
241 
242 #if EFI_TOOTH_LOGGER
244 #endif /* EFI_TOOTH_LOGGER */
245  } else {
246  if (isImportantFront) {
247  // On the important edge, log a rise+fall pair, and nothing on the real falling edge
250 
251 #if EFI_TOOTH_LOGGER
254 #endif /* EFI_TOOTH_LOGGER */
255  }
256  }
257 }
258 
259 void hwHandleVvtCamSignal(bool isRising, efitick_t timestamp, int index) {
260  hwHandleVvtCamSignal(isRising ? TriggerValue::RISE : TriggerValue::FALL, timestamp, index);
261 }
262 
263 // 'invertCamVVTSignal' is already accounted by the time this method is invoked
264 void hwHandleVvtCamSignal(TriggerValue front, efitick_t nowNt, int index) {
267  // sensor noise + self-stim = loss of trigger sync
268  return;
269  }
270  handleVvtCamSignal(front, nowNt, index);
271 }
272 
273 void handleVvtCamSignal(TriggerValue front, efitick_t nowNt, int index) {
275  if (index == 0) {
277  } else if (index == 1) {
279  } else if (index == 2) {
281  } else if (index == 3) {
283  }
284 
285  int bankIndex = BANK_BY_INDEX(index);
286  int camIndex = CAM_BY_INDEX(index);
287  if (front == TriggerValue::RISE) {
288  tc->vvtEventRiseCounter[index]++;
289  } else {
290  tc->vvtEventFallCounter[index]++;
291  }
292  if (engineConfiguration->vvtMode[camIndex] == VVT_INACTIVE) {
293  warning(ObdCode::CUSTOM_VVT_MODE_NOT_SELECTED, "VVT: event on %d but no mode", camIndex);
294  }
295 
296 #ifdef VR_HW_CHECK_MODE
297  // some boards do not have hardware VR input LEDs which makes such boards harder to validate
298  // from experience we know that assembly mistakes happen and quality control is required
301 
302  for (int i = 0 ; i < 100 ; i++) {
303  // turning pin ON and busy-waiting a bit
304  palWritePad(criticalErrorLedPort, criticalErrorLedPin, 1);
305  }
306 
307  palWritePad(criticalErrorLedPort, criticalErrorLedPin, 0);
308 #endif // VR_HW_CHECK_MODE
309 
310  const auto& vvtShape = tc->vvtShape[camIndex];
311 
312  bool isVvtWithRealDecoder = vvtWithRealDecoder(engineConfiguration->vvtMode[camIndex]);
313 
314  // Non real decoders only use the rising edge
315  bool vvtUseOnlyRise = !isVvtWithRealDecoder || vvtShape.useOnlyRisingEdges;
316  bool isImportantFront = !vvtUseOnlyRise || (front == TriggerValue::RISE);
317 
318  logVvtFront(vvtUseOnlyRise, isImportantFront, front, nowNt, index);
319 
320  if (!isImportantFront) {
321  // This edge is unimportant, ignore it.
322  return;
323  }
324 
325  // If the main trigger is not synchronized, don't decode VVT yet
326  if (!tc->triggerState.getShaftSynchronized()) {
327  return;
328  }
329 
330  TriggerDecoderBase& vvtDecoder = tc->vvtState[bankIndex][camIndex];
331 
332  if (isVvtWithRealDecoder) {
333  vvtDecoder.decodeTriggerEvent(
334  "vvt",
335  vvtShape,
336  nullptr,
337  tc->vvtTriggerConfiguration[camIndex],
339  // yes we log data from all VVT channels into same fields for now
341  tc->triggerState.vvtToothDurations0 = (uint32_t)NT2US(vvtDecoder.toothDurations[0]);
343  }
344 
345  // here we count all cams together
346  tc->vvtCamCounter++;
347 
348  auto currentPhase = tc->getCurrentEnginePhase(nowNt);
349  if (!currentPhase) {
350  // If we couldn't resolve engine speed (yet primary trigger is sync'd), this
351  // probably means that we have partial crank sync, but not RPM information yet
352  return;
353  }
354 
355  angle_t angleFromPrimarySyncPoint = currentPhase.Value;
356  // convert trigger cycle angle into engine cycle angle
357  angle_t currentPosition = angleFromPrimarySyncPoint - tdcPosition();
358  // https://github.com/rusefi/rusefi/issues/1713 currentPosition could be negative that's expected
359 
360 #if EFI_UNIT_TEST
361  tc->currentVVTEventPosition[bankIndex][camIndex] = currentPosition;
362 #endif // EFI_UNIT_TEST
363 
364  tc->triggerState.vvtCurrentPosition = currentPosition;
365 
366  if (isVvtWithRealDecoder && vvtDecoder.currentCycle.current_index != 0) {
367  // this is not sync tooth - exiting
368  return;
369  }
370 
371  auto vvtPosition = engineConfiguration->vvtOffsets[bankIndex * CAMS_PER_BANK + camIndex] - currentPosition;
372  tc->triggerState.vvtToothPosition[index] = vvtPosition;
373 
374  switch(engineConfiguration->vvtMode[camIndex]) {
375  case VVT_TOYOTA_3_TOOTH:
376  {
379  // we do not know if we are in sync or out of sync, so we have to be looking for both possibilities
380  if ((currentPosition < from || currentPosition > to) &&
381  (currentPosition < from + 360 || currentPosition > to + 360)) {
382  // outside of the expected range
383  return;
384  }
385  }
386  break;
387  default:
388  // else, do nothing
389  break;
390  }
391 
392  // this could be just an 'if' but let's have it expandable for future use :)
393  switch(engineConfiguration->vvtMode[camIndex]) {
394  case VVT_HONDA_K_INTAKE:
395  // honda K has four tooth in VVT intake trigger, so we just wrap each of those to 720 / 4
396  vvtPosition = wrapVvt(vvtPosition, 180);
397  break;
398  default:
399  // else, do nothing
400  break;
401  }
402 
403  // Only do engine sync using one cam, other cams just provide VVT position.
404  if (index == engineConfiguration->engineSyncCam) {
405  angle_t crankOffset = adjustCrankPhase(camIndex);
406  // vvtPosition was calculated against wrong crank zero position. Now that we have adjusted crank position we
407  // shall adjust vvt position as well
408  vvtPosition -= crankOffset;
409  vvtPosition = wrapVvt(vvtPosition, FOUR_STROKE_CYCLE_DURATION);
410 
411  if (absF(angleFromPrimarySyncPoint) < 7) {
412  /**
413  * we prefer not to have VVT sync right at trigger sync so that we do not have phase detection error if things happen a bit in
414  * wrong order due to belt flex or else
415  * https://github.com/rusefi/rusefi/issues/3269
416  */
417  warning(ObdCode::CUSTOM_VVT_SYNC_POSITION, "VVT sync position too close to trigger sync");
418  }
419  } else {
420  // Not using this cam for engine sync, just wrap the value in to the reasonable range
421  vvtPosition = wrapVvt(vvtPosition, FOUR_STROKE_CYCLE_DURATION);
422  }
423 
424  // Only record VVT position if we have full engine sync - may be bogus before that point
426  tc->vvtPosition[bankIndex][camIndex] = vvtPosition;
427  } else {
428  tc->vvtPosition[bankIndex][camIndex] = 0;
429  }
430 }
431 
435 uint32_t triggerMaxDuration = 0;
436 
437 /**
438  * This function is called by all "hardware" trigger inputs:
439  * - Hardware triggers
440  * - Trigger replay from CSV (unit tests)
441  */
442 void hwHandleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp) {
445 #ifdef VR_HW_CHECK_MODE
446  // some boards do not have hardware VR input LEDs which makes such boards harder to validate
447  // from experience we know that assembly mistakes happen and quality control is required
450 
451 #if HW_CHECK_ALWAYS_STIMULATE
453 #endif // HW_CHECK_ALWAYS_STIMULATE
454 
455 
456  for (int i = 0 ; i < 100 ; i++) {
457  // turning pin ON and busy-waiting a bit
458  palWritePad(criticalErrorLedPort, criticalErrorLedPin, 1);
459  }
460 
461  palWritePad(criticalErrorLedPort, criticalErrorLedPin, 0);
462 #endif // VR_HW_CHECK_MODE
463 
465  // sensor noise + self-stim = loss of trigger sync
466  return;
467  }
468 
469  handleShaftSignal(signalIndex, isRising, timestamp);
470 }
471 
472 // Handle all shaft signals - hardware or emulated both
473 void handleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp) {
474  bool isPrimary = signalIndex == 0;
475  if (!isPrimary && !TRIGGER_WAVEFORM(needSecondTriggerInput)) {
476  return;
477  }
478 
479  trigger_event_e signal;
480  // todo: add support for 3rd channel
481  if (isRising) {
482  signal = isPrimary ?
485  } else {
486  signal = isPrimary ?
489  }
490  if (isPrimary) {
492  } else {
494  }
495 
496  // Don't accept trigger input in case of some problems
497  if (!getLimpManager()->allowTriggerInput()) {
498  return;
499  }
500 
501 #if EFI_TOOTH_LOGGER
502  // Log to the Tunerstudio tooth logger
503  // We want to do this before anything else as we
504  // actually want to capture any noise/jitter that may be occurring
505 
507 
508  if (!logLogicState) {
509  // we log physical state even if displayLogicLevelsInEngineSniffer if both fronts are used by decoder
510  LogTriggerTooth(signal, timestamp);
511  }
512 
513 #endif /* EFI_TOOTH_LOGGER */
514 
515  // for effective noise filtering, we need both signal edges,
516  // so we pass them to handleShaftSignal() and defer this test
518  if (!isUsefulSignal(signal, getTriggerCentral()->triggerShape)) {
519  /**
520  * no need to process VR falls further
521  */
522  return;
523  }
524  }
525 
527 #if EFI_PROD_CODE
528  writePad("trigger debug", engineConfiguration->triggerInputDebugPins[signalIndex], 1);
529 #endif /* EFI_PROD_CODE */
530  getExecutorInterface()->scheduleByTimestampNt("dbg_off", &debugToggleScheduling, timestamp + DEBUG_PIN_DELAY, &turnOffAllDebugFields);
531  }
532 
533 #if EFI_TOOTH_LOGGER
534  if (logLogicState) {
535  // first log rising normally
536  LogTriggerTooth(signal, timestamp);
537  // in 'logLogicState' mode we log opposite front right after logical rising away
538  if (signal == SHAFT_PRIMARY_RISING) {
540  } else {
542  }
543  }
544 #endif /* EFI_TOOTH_LOGGER */
545 
546  uint32_t triggerHandlerEntryTime = getTimeNowLowerNt();
550 
551  getTriggerCentral()->handleShaftSignal(signal, timestamp);
552 
554  triggerDuration = getTimeNowLowerNt() - triggerHandlerEntryTime;
556 }
557 
559  memset(hwEventCounters, 0, sizeof(hwEventCounters));
560 }
561 
562 static const bool isUpEvent[4] = { false, true, false, true };
563 static const int wheelIndeces[4] = { 0, 0, 1, 1};
564 
565 static void reportEventToWaveChart(trigger_event_e ckpSignalType, int triggerEventIndex, bool addOppositeEvent) {
566  if (!getTriggerCentral()->isEngineSnifferEnabled) { // this is here just as a shortcut so that we avoid engine sniffer as soon as possible
567  return; // engineSnifferRpmThreshold is accounted for inside getTriggerCentral()->isEngineSnifferEnabled
568  }
569 
570  int wheelIndex = wheelIndeces[(int )ckpSignalType];
571 
572  bool isUp = isUpEvent[(int) ckpSignalType];
573 
574  addEngineSnifferCrankEvent(wheelIndex, triggerEventIndex, isUp ? FrontDirection::UP : FrontDirection::DOWN);
575  if (addOppositeEvent) {
576  // let's add the opposite event right away
577  addEngineSnifferCrankEvent(wheelIndex, triggerEventIndex, isUp ? FrontDirection::DOWN : FrontDirection::UP);
578  }
579 }
580 
581 /**
582  * This is used to filter noise spikes (interference) in trigger signal. See
583  * The basic idea is to use not just edges, but the average amount of time the signal stays in '0' or '1'.
584  * So we update 'accumulated periods' to track where the signal is.
585  * And then compare between the current period and previous, with some tolerance (allowing for the wheel speed change).
586  * @return true if the signal is passed through.
587  */
588 bool TriggerNoiseFilter::noiseFilter(efitick_t nowNt,
589  TriggerDecoderBase * triggerState,
590  trigger_event_e signal) {
591  // todo: find a better place for these defs
594  // we process all trigger channels independently
595  TriggerWheel ti = triggerIdx[signal];
596  // falling is opposite to rising, and vise versa
597  trigger_event_e os = opposite[signal];
598 
599  // todo: currently only primary channel is filtered, because there are some weird trigger types on other channels
600  if (ti != TriggerWheel::T_PRIMARY)
601  return true;
602 
603  // update period accumulator: for rising signal, we update '0' accumulator, and for falling - '1'
604  if (lastSignalTimes[signal] != -1)
605  accumSignalPeriods[signal] += nowNt - lastSignalTimes[signal];
606  // save current time for this trigger channel
607  lastSignalTimes[signal] = nowNt;
608 
609  // now we want to compare current accumulated period to the stored one
610  efitick_t currentPeriod = accumSignalPeriods[signal];
611  // the trick is to compare between different
612  efitick_t allowedPeriod = accumSignalPrevPeriods[os];
613 
614  // but first check if we're expecting a gap
615  bool isGapExpected = TRIGGER_WAVEFORM(isSynchronizationNeeded) && triggerState->getShaftSynchronized() &&
616  (triggerState->currentCycle.eventCount[(int)ti] + 1) == TRIGGER_WAVEFORM(getExpectedEventCount(ti));
617 
618  if (isGapExpected) {
619  // usually we need to extend the period for gaps, based on the trigger info
620  allowedPeriod *= TRIGGER_WAVEFORM(syncRatioAvg);
621  }
622 
623  // also we need some margin for rapidly changing trigger-wheel speed,
624  // that's why we expect the period to be no less than 2/3 of the previous period (this is just an empirical 'magic' coef.)
625  efitick_t minAllowedPeriod = 2 * allowedPeriod / 3;
626  // but no longer than 5/4 of the previous 'normal' period
627  efitick_t maxAllowedPeriod = 5 * allowedPeriod / 4;
628 
629  // above all, check if the signal comes not too early
630  if (currentPeriod >= minAllowedPeriod) {
631  // now we store this period as a reference for the next time,
632  // BUT we store only 'normal' periods, and ignore too long periods (i.e. gaps)
633  if (!isGapExpected && (maxAllowedPeriod == 0 || currentPeriod <= maxAllowedPeriod)) {
634  accumSignalPrevPeriods[signal] = currentPeriod;
635  }
636  // reset accumulator
637  accumSignalPeriods[signal] = 0;
638  return true;
639  }
640  // all premature or extra-long events are ignored - treated as interference
641  return false;
642 }
643 
644 void TriggerCentral::decodeMapCam(efitick_t timestamp, float currentPhase) {
645  isDecodingMapCam = engineConfiguration->vvtMode[0] == VVT_MAP_V_TWIN &&
647  if (isDecodingMapCam) {
648  // we are trying to figure out which 360 half of the total 720 degree cycle is which, so we compare those in 360 degree sense.
649  auto toothAngle360 = currentPhase;
650  while (toothAngle360 >= 360) {
651  toothAngle360 -= 360;
652  }
653 
654  if (mapCamPrevToothAngle < engineConfiguration->mapCamDetectionAnglePosition && toothAngle360 > engineConfiguration->mapCamDetectionAnglePosition) {
655  // we are somewhere close to 'mapCamDetectionAnglePosition'
656 
657  // warning: hack hack hack
659 
660  // Compute diff against the last time we were here
661  float diff = map - mapCamPrevCycleValue;
662  mapCamPrevCycleValue = map;
663 
664  if (diff > 0) {
665  mapVvt_map_peak++;
667  mapVvt_MAP_AT_CYCLE_COUNT = revolutionCounter - prevChangeAtCycle;
668  prevChangeAtCycle = revolutionCounter;
669 
670  hwHandleVvtCamSignal(TriggerValue::RISE, timestamp, /*index*/0);
671  hwHandleVvtCamSignal(TriggerValue::FALL, timestamp, /*index*/0);
672 #if EFI_UNIT_TEST
673  // hack? feature? existing unit test relies on VVT phase available right away
674  // but current implementation which is based on periodicFastCallback would only make result available on NEXT tooth
676 #endif // EFI_UNIT_TEST
677  }
678 
680  mapVvt_MAP_AT_DIFF = diff;
681  }
682 
683  mapCamPrevToothAngle = toothAngle360;
684  }
685 }
686 
687 bool TriggerCentral::isToothExpectedNow(efitick_t timestamp) {
688  // Check that the expected next phase (from the last tooth) is close to the actual current phase:
689  // basically, check that the tooth width is correct
690  auto estimatedCurrentPhase = getCurrentEnginePhase(timestamp);
691  auto lastToothPhase = m_lastToothPhaseFromSyncPoint;
692 
693  if (expectedNextPhase && estimatedCurrentPhase) {
694  float angleError = expectedNextPhase.Value - estimatedCurrentPhase.Value;
695 
696  // Wrap around correctly at the end of the cycle
697  float cycle = getEngineState()->engineCycle;
698  if (angleError < -cycle / 2) {
699  angleError += cycle;
700  }
701 
702  triggerToothAngleError = angleError;
703 
704  // Only perform checks if engine is spinning quickly
705  // All kinds of garbage happens while cranking
706  if (Sensor::getOrZero(SensorType::Rpm) > 1000) {
707  // Now compute how close we are to the last tooth decoded
708  float angleSinceLastTooth = estimatedCurrentPhase.Value - lastToothPhase;
709  if (angleSinceLastTooth < 0.5f) {
710  // This tooth came impossibly early, ignore it
711  // This rejects things like doubled edges, for example:
712  // |-| |----------------
713  // | | |
714  // ____________| |_|
715  // 1 2
716  // #1 will be decoded
717  // #2 will be ignored
718  // We're not sure which edge was the "real" one, but they were close enough
719  // together that it doesn't really matter.
720  warning(ObdCode::CUSTOM_PRIMARY_DOUBLED_EDGE, "doubled trigger edge after %.2f deg at #%d", angleSinceLastTooth, triggerState.currentCycle.current_index);
721 
722  return false;
723  }
724 
725  // Absolute error from last tooth
726  float absError = absF(angleError);
727  float isRpmEnough = Sensor::getOrZero(SensorType::Rpm) > 1000;
728  // TODO: configurable threshold
729  if (isRpmEnough && absError > 10 && absError < 180) {
730  // This tooth came at a very unexpected time, ignore it
732 
733  // TODO: this causes issues with some real engine logs, should it?
734  // return false;
735  }
736  }
737  } else {
739  }
740 
741  // We aren't ready to reject unexpected teeth, so accept this tooth
742  return true;
743 }
744 
745 BOARD_WEAK bool boardAllowTriggerActions() { return true; }
746 
748  int currentToothIndex = p_currentToothIndex;
749  // TODO: is this logic to compute next trigger tooth angle correct?
750  angle_t nextPhase = 0;
751 
752  int loopAllowance = 2 * engineCycleEventCount + 1000;
753  do {
754  // I don't love this.
755  currentToothIndex = (currentToothIndex + 1) % engineCycleEventCount;
756  nextPhase = getTriggerCentral()->triggerFormDetails.eventAngles[currentToothIndex] - tdcPosition();
757  wrapAngle(nextPhase, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555);
758  } while (nextPhase == currentEngineDecodedPhase && --loopAllowance > 0);
759  if (nextPhase != 0 && loopAllowance == 0) {
760  firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_ZERO, "handleShaftSignal unexpected loop end %d %d %f", p_currentToothIndex, engineCycleEventCount, nextPhase);
761  }
762  return nextPhase;
763 }
764 
765 /**
766  * This method is NOT invoked for VR falls.
767  */
768 void TriggerCentral::handleShaftSignal(trigger_event_e signal, efitick_t timestamp) {
770  // trigger is broken, we cannot do anything here
771  warning(ObdCode::CUSTOM_ERR_UNEXPECTED_SHAFT_EVENT, "Shaft event while trigger is mis-configured");
772  // magic value to indicate a problem
773  hwEventCounters[0] = 155;
774  return;
775  }
776 
777  // This code gathers some statistics on signals and compares accumulated periods to filter interference
779  if (!noiseFilter.noiseFilter(timestamp, &triggerState, signal)) {
780  return;
781  }
782  if (!isUsefulSignal(signal, triggerShape)) {
783  return;
784  }
785  }
786 
787  if (!isToothExpectedNow(timestamp)) {
789  return;
790  }
791 
793 
794 #if EFI_HD_ACR
795  bool firstEventInAWhile = m_lastEventTimer.hasElapsedSec(1);
796  if (firstEventInAWhile) {
797  // let's open that valve on first sign of movement
798  engine->module<HarleyAcr>()->updateAcr();
799  }
800 #endif // EFI_HD_ACR
801 
802  if (boardAllowTriggerActions()) {
803  m_lastEventTimer.reset(timestamp);
804  }
805 
806  int eventIndex = (int) signal;
807  efiAssertVoid(ObdCode::CUSTOM_TRIGGER_EVENT_TYPE, eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type");
809 
810  // Decode the trigger!
811  auto decodeResult = triggerState.decodeTriggerEvent(
812  "trigger",
813  triggerShape,
814  engine,
816  signal, timestamp);
817 
818  // Don't propagate state if we don't know where we are
819  if (decodeResult) {
821 
822  /**
823  * If we only have a crank position sensor with four stroke, here we are extending crank revolutions with a 360 degree
824  * cycle into a four stroke, 720 degrees cycle.
825  */
826  int crankDivider = getCrankDivider(triggerShape.getWheelOperationMode());
827  int crankInternalIndex = triggerState.getCrankSynchronizationCounter() % crankDivider;
828  int triggerIndexForListeners = decodeResult.Value.CurrentIndex + (crankInternalIndex * triggerShape.getSize());
829 
830  reportEventToWaveChart(signal, triggerIndexForListeners, triggerShape.useOnlyRisingEdges);
831 
832  // Look up this tooth's angle from the sync point. If this tooth is the sync point, we'll get 0 here.
833  auto currentPhaseFromSyncPoint = getTriggerCentral()->triggerFormDetails.eventAngles[triggerIndexForListeners];
834 
835  // Adjust so currentPhase is in engine-space angle, not trigger-space angle
836  currentEngineDecodedPhase = wrapAngleMethod(currentPhaseFromSyncPoint - tdcPosition(), "currentEnginePhase", ObdCode::CUSTOM_ERR_6555);
837 
838  // Record precise time and phase of the engine. This is used for VVT decode, and to check that the
839  // trigger pattern selected matches reality (ie, we check the next tooth is where we think it should be)
840  {
841  // under lock to avoid mismatched tooth phase and time
842  chibios_rt::CriticalSectionLocker csl;
843 
844  m_lastToothTimer.reset(timestamp);
845  m_lastToothPhaseFromSyncPoint = currentPhaseFromSyncPoint;
846  }
847 
848 #if TRIGGER_EXTREME_LOGGING
849  efiPrintf("trigger %d %d %d", triggerIndexForListeners, getRevolutionCounter(), time2print(getTimeNowUs()));
850 #endif /* TRIGGER_EXTREME_LOGGING */
851 
852  // Update engine RPM
853  rpmShaftPositionCallback(signal, triggerIndexForListeners, timestamp);
854 
855  // Schedule the TDC mark
856  tdcMarkCallback(triggerIndexForListeners, timestamp);
857 
858 #if !EFI_UNIT_TEST
859 #if EFI_MAP_AVERAGING
860  mapAveragingTriggerCallback(triggerIndexForListeners, timestamp);
861 #endif /* EFI_MAP_AVERAGING */
862 #endif /* EFI_UNIT_TEST */
863 
864 #if EFI_LOGIC_ANALYZER
865  waTriggerEventListener(signal, triggerIndexForListeners, timestamp);
866 #endif
867 
868  angle_t nextPhase = findNextTriggerToothAngle(triggerIndexForListeners);
869 
870  float expectNextPhase = nextPhase + tdcPosition();
871  wrapAngle(expectNextPhase, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555);
872  expectedNextPhase = expectNextPhase;
873 
874 #if EFI_CDM_INTEGRATION
875  if (trgEventIndex == 0 && isBrainPinValid(engineConfiguration->cdmInputPin)) {
877  engine->knockLogic(cdmKnockValue);
878  }
879 #endif /* EFI_CDM_INTEGRATION */
880 
881  if (engine->rpmCalculator.getCachedRpm() > 0 && triggerIndexForListeners == 0) {
883  }
884 
885  // Handle ignition and injection
886  mainTriggerCallback(triggerIndexForListeners, timestamp, currentEngineDecodedPhase, nextPhase);
887 
888  // Decode the MAP based "cam" sensor
890  } else {
891  // We don't have sync, but report to the wave chart anyway as index 0.
893 
894  expectedNextPhase = unexpected;
895  }
896 }
897 
898 static void triggerShapeInfo() {
899 #if EFI_PROD_CODE || EFI_SIMULATOR
901  TriggerFormDetails *triggerFormDetails = &getTriggerCentral()->triggerFormDetails;
902  efiPrintf("syncEdge=%s", getSyncEdge(TRIGGER_WAVEFORM(syncEdge)));
903  efiPrintf("gap from %.2f to %.2f", TRIGGER_WAVEFORM(synchronizationRatioFrom[0]), TRIGGER_WAVEFORM(synchronizationRatioTo[0]));
904 
905  for (size_t i = 0; i < shape->getSize(); i++) {
906  efiPrintf("event %d %.2f", i, triggerFormDetails->eventAngles[i]);
907  }
908 #endif
909 }
910 
911 #if EFI_PROD_CODE
912 extern PwmConfig triggerEmulatorSignals[NUM_EMULATOR_CHANNELS];
913 #endif /* #if EFI_PROD_CODE */
914 
915 void triggerInfo(void) {
916 #if EFI_PROD_CODE || EFI_SIMULATOR
917 
919  TriggerWaveform *ts = &tc->triggerShape;
920 
921 
922 #if (HAL_TRIGGER_USE_PAL == TRUE) && (PAL_USE_CALLBACKS == TRUE)
923  efiPrintf("trigger PAL mode %d", tc->hwTriggerInputEnabled);
924 #else
925 
926 #endif /* HAL_TRIGGER_USE_PAL */
927 
928  efiPrintf("Template %s (%d) trigger %s (%d) syncEdge=%s tdcOffset=%.2f",
931  getSyncEdge(TRIGGER_WAVEFORM(syncEdge)), TRIGGER_WAVEFORM(tdcPosition));
932 
934  efiPrintf("total %d/skipped %d", engineConfiguration->trigger.customTotalToothCount,
936  }
937 
938 
939  efiPrintf("trigger#1 event counters up=%d/down=%d", tc->getHwEventCounter(0),
940  tc->getHwEventCounter(1));
941 
942  if (ts->needSecondTriggerInput) {
943  efiPrintf("trigger#2 event counters up=%d/down=%d", tc->getHwEventCounter(2),
944  tc->getHwEventCounter(3));
945  }
946  efiPrintf("expected cycle events %d/%d",
947  TRIGGER_WAVEFORM(getExpectedEventCount(TriggerWheel::T_PRIMARY)),
948  TRIGGER_WAVEFORM(getExpectedEventCount(TriggerWheel::T_SECONDARY)));
949 
950  efiPrintf("trigger type=%d/need2ndChannel=%s", engineConfiguration->trigger.type,
951  boolToString(TRIGGER_WAVEFORM(needSecondTriggerInput)));
952 
953 
954  efiPrintf("synchronizationNeeded=%s/isError=%s/total errors=%d ord_err=%d/total revolutions=%d/self=%s",
961 
962  if (TRIGGER_WAVEFORM(isSynchronizationNeeded)) {
963  efiPrintf("gap from %.2f to %.2f", TRIGGER_WAVEFORM(synchronizationRatioFrom[0]), TRIGGER_WAVEFORM(synchronizationRatioTo[0]));
964  }
965 
966 #endif /* EFI_PROD_CODE || EFI_SIMULATOR */
967 
968 #if EFI_PROD_CODE
969 
970  efiPrintf("primary trigger input: %s", hwPortname(engineConfiguration->triggerInputPins[0]));
971  efiPrintf("primary trigger simulator: %s %s freq=%d",
975 
976  if (ts->needSecondTriggerInput) {
977  efiPrintf("secondary trigger input: %s", hwPortname(engineConfiguration->triggerInputPins[1]));
978 #if EFI_EMULATE_POSITION_SENSORS
979  efiPrintf("secondary trigger simulator: %s %s phase=%d",
982 #endif /* EFI_EMULATE_POSITION_SENSORS */
983  }
984 
985 
986  for (int camInputIndex = 0; camInputIndex<CAM_INPUTS_COUNT;camInputIndex++) {
987  if (isBrainPinValid(engineConfiguration->camInputs[camInputIndex])) {
988  int camLogicalIndex = camInputIndex % CAMS_PER_BANK;
989  efiPrintf("VVT input: %s mode %s", hwPortname(engineConfiguration->camInputs[camInputIndex]),
990  getVvt_mode_e(engineConfiguration->vvtMode[camLogicalIndex]));
991  efiPrintf("VVT %d event counters: %d/%d",
992  camInputIndex,
993  tc->vvtEventRiseCounter[camInputIndex], tc->vvtEventFallCounter[camInputIndex]);
994  }
995  }
996 
997  efiPrintf("primary logic input: %s", hwPortname(engineConfiguration->logicAnalyzerPins[0]));
998  efiPrintf("secondary logic input: %s", hwPortname(engineConfiguration->logicAnalyzerPins[1]));
999 
1000 
1001  efiPrintf("totalTriggerHandlerMaxTime=%d", triggerMaxDuration);
1002 
1003 #endif /* EFI_PROD_CODE */
1004 
1005 #if EFI_ENGINE_SNIFFER
1006  efiPrintf("engine sniffer current size=%d", waveChart.getSize());
1007 #endif /* EFI_ENGINE_SNIFFER */
1008 
1009 }
1010 
1012 #if !EFI_UNIT_TEST
1014  triggerInfo();
1015 #endif
1016 }
1017 
1019  bool changed = false;
1020  // todo: how do we static_assert here?
1021  criticalAssertVoid(efi::size(engineConfiguration->camInputs) == efi::size(engineConfiguration->vvtOffsets), "sizes");
1022 
1023  for (size_t camIndex = 0; camIndex < efi::size(engineConfiguration->camInputs); camIndex++) {
1024  changed |= isConfigurationChanged(camInputs[camIndex]);
1025  changed |= isConfigurationChanged(vvtOffsets[camIndex]);
1026  }
1027 
1028  for (size_t i = 0; i < efi::size(engineConfiguration->triggerGapOverrideFrom); i++) {
1029  changed |= isConfigurationChanged(triggerGapOverrideFrom[i]);
1030  changed |= isConfigurationChanged(triggerGapOverrideTo[i]);
1031  }
1032 
1033  for (size_t i = 0; i < efi::size(engineConfiguration->triggerInputPins); i++) {
1034  changed |= isConfigurationChanged(triggerInputPins[i]);
1036  if (engineConfiguration->vvtMode[0] == VVT_MAP_V_TWIN && isBrainPinValid(pin)) {
1037  criticalError("Please no physical sensors in CAM by MAP mode index=%d %s", i, hwPortname(pin));
1038  }
1039  }
1040 
1041  for (size_t i = 0; i < efi::size(engineConfiguration->vvtMode); i++) {
1042  changed |= isConfigurationChanged(vvtMode[i]);
1043  }
1044 
1045  changed |= isConfigurationChanged(trigger.type);
1046  changed |= isConfigurationChanged(skippedWheelOnCam);
1047  changed |= isConfigurationChanged(twoStroke);
1048  changed |= isConfigurationChanged(globalTriggerAngleOffset);
1049  changed |= isConfigurationChanged(trigger.customTotalToothCount);
1050  changed |= isConfigurationChanged(trigger.customSkippedToothCount);
1051  changed |= isConfigurationChanged(overrideTriggerGaps);
1052  changed |= isConfigurationChanged(gapTrackingLengthOverride);
1053  changed |= isConfigurationChanged(overrideVvtTriggerGaps);
1054  changed |= isConfigurationChanged(gapVvtTrackingLengthOverride);
1055 
1056  if (changed) {
1057  #if EFI_ENGINE_CONTROL
1060  #endif
1061  }
1062 #if EFI_DEFAILED_LOGGING
1063  efiPrintf("isTriggerConfigChanged=%d", triggerConfigChanged);
1064 #endif /* EFI_DEFAILED_LOGGING */
1065 
1066  // we do not want to miss two updates in a row
1068 }
1069 
1072  shape.initializeSyncPoint(initState, p_config);
1073 }
1074 
1076  // micro-optimized 'crankSynchronizationCounter % 256'
1077  int camVvtValidationIndex = triggerState.getCrankSynchronizationCounter() & 0xFF;
1078  if (camVvtValidationIndex == 0) {
1079  vvtCamCounter = 0;
1080  } else if (camVvtValidationIndex == 0xFE && vvtCamCounter < 60) {
1081  // magic logic: we expect at least 60 CAM/VVT events for each 256 trigger cycles, otherwise throw a code
1082  warning(ObdCode::OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, "No Camshaft Position Sensor signals");
1083  }
1084 }
1085 /**
1086  * Calculate 'shape.triggerShapeSynchPointIndex' value using 'TriggerDecoderBase *state'
1087  */
1089  const PrimaryTriggerConfiguration &primaryTriggerConfiguration,
1090  TriggerWaveform& shape,
1092 
1093 #if EFI_PROD_CODE
1094  efiAssertVoid(ObdCode::CUSTOM_TRIGGER_STACK, hasLotsOfRemainingStack(), "calc s");
1095 #endif
1096 
1097  shape.initializeSyncPoint(initState, primaryTriggerConfiguration);
1098 
1099  if (shape.getSize() >= PWM_PHASE_MAX_COUNT) {
1100  // todo: by the time we are here we had already modified a lot of RAM out of bounds!
1101  firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_WAVEFORM_TOO_LONG, "Trigger length above maximum: %d", shape.getSize());
1102  shape.setShapeDefinitionError(true);
1103  return;
1104  }
1105 
1106  if (shape.getSize() == 0) {
1107  firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_ZERO, "triggerShape size is zero");
1108  }
1109 }
1110 
1112 
1114  // Re-read config in case it's changed
1116  for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) {
1117  vvtTriggerConfiguration[camIndex].update();
1118  }
1119 
1121 
1122  /**
1123  * this is only useful while troubleshooting a new trigger shape in the field
1124  * in very VERY rare circumstances
1125  */
1127  int gapIndex = 0;
1128 
1130 
1131  // copy however many the user wants
1132  for (; gapIndex < engineConfiguration->gapTrackingLengthOverride; gapIndex++) {
1133  float gapOverrideFrom = engineConfiguration->triggerGapOverrideFrom[gapIndex];
1134  float gapOverrideTo = engineConfiguration->triggerGapOverrideTo[gapIndex];
1135  TRIGGER_WAVEFORM(setTriggerSynchronizationGap3(/*gapIndex*/gapIndex, gapOverrideFrom, gapOverrideTo));
1136  }
1137 
1138  // fill the remainder with the default gaps
1139  for (; gapIndex < GAP_TRACKING_LENGTH; gapIndex++) {
1140  triggerShape.synchronizationRatioFrom[gapIndex] = NAN;
1141  triggerShape.synchronizationRatioTo[gapIndex] = NAN;
1142  }
1143  }
1144 
1146  int length = triggerShape.getLength();
1147  engineCycleEventCount = length;
1148 
1149  efiAssertVoid(ObdCode::CUSTOM_SHAPE_LEN_ZERO, length > 0, "shapeLength=0");
1150 
1151  triggerErrorDetection.clear();
1152 
1153  /**
1154  * 'initState' instance of TriggerDecoderBase is used only to initialize 'this' TriggerWaveform instance
1155  * #192 BUG real hardware trigger events could be coming even while we are initializing trigger
1156  */
1158  triggerShape,
1159  initState);
1160  }
1161 
1163  int gapIndex = 0;
1164 
1165  TriggerWaveform *shape = &vvtShape[0];
1166 
1167  for (; gapIndex < engineConfiguration->gapVvtTrackingLengthOverride; gapIndex++) {
1168  float gapOverrideFrom = engineConfiguration->triggerVVTGapOverrideFrom[gapIndex];
1169  float gapOverrideTo = engineConfiguration->triggerVVTGapOverrideTo[gapIndex];
1170  shape->synchronizationRatioFrom[gapIndex] = gapOverrideFrom;
1171  shape->synchronizationRatioTo[gapIndex] = gapOverrideTo;
1172  }
1173  // fill the remainder with the default gaps
1174  for (; gapIndex < VVT_TRACKING_LENGTH; gapIndex++) {
1175  shape->synchronizationRatioFrom[gapIndex] = NAN;
1176  shape->synchronizationRatioTo[gapIndex] = NAN;
1177  }
1178  }
1179 
1180  for (int camIndex = 0; camIndex < CAMS_PER_BANK; camIndex++) {
1181  // todo: should 'vvtWithRealDecoder' be used here?
1182  if (engineConfiguration->vvtMode[camIndex] != VVT_INACTIVE) {
1183  initVvtShape(
1184  vvtShape[camIndex],
1185  vvtTriggerConfiguration[camIndex],
1186  initState
1187  );
1188  }
1189  }
1190 
1191  // This is not the right place for this, but further refactoring has to happen before it can get moved.
1193 
1194 }
1195 
1196 /**
1197  * @returns true if configuration just changed, and if that change has affected trigger
1198  */
1200  // we want to make sure that configuration has changed AND that change has changed trigger specifically
1202  triggerConfigChangedOnLastConfigurationChange = false; // whoever has called the method is supposed to react to changes
1203  return result;
1204 }
1205 
1206 #if EFI_UNIT_TEST
1209 }
1210 #endif // EFI_UNIT_TEST
1211 
1214  criticalError("First trigger channel not configured while second one is.");
1215  }
1216 
1218  criticalError("First bank cam input is required if second bank specified");
1219  }
1220 }
1221 
1223 
1224 #if EFI_ENGINE_SNIFFER
1226 #endif /* EFI_ENGINE_SNIFFER */
1227 
1228 #if EFI_PROD_CODE || EFI_SIMULATOR
1229  addConsoleAction(CMD_TRIGGERINFO, triggerInfo);
1230  addConsoleAction("trigger_shape_info", triggerShapeInfo);
1232 #endif // EFI_PROD_CODE || EFI_SIMULATOR
1233 
1234 }
1235 
1236 /**
1237  * @return TRUE is something is wrong with trigger decoding
1238  */
1240  return triggerErrorDetection.sum(6) > 4;
1241 }
1242 
1243 #endif // EFI_SHAFT_POSITION_INPUT
const char * getPin_output_mode_e(pin_output_mode_e value)
const char * getVvt_mode_e(vvt_mode_e value)
const char * getTrigger_type_e(trigger_type_e value)
const char * getEngine_type_e(engine_type_e value)
const char * getSyncEdge(SyncEdge value)
beuint32_t period
int getCurrentCdmValue(int currentRevolution)
TriggerCentral triggerCentral
Definition: engine.h:286
int getGlobalConfigurationVersion(void) const
Definition: engine.cpp:300
RpmCalculator rpmCalculator
Definition: engine.h:273
constexpr auto & module()
Definition: engine.h:177
TunerStudioOutputChannels outputChannels
Definition: engine.h:99
TpsAccelEnrichment tpsAccelEnrichment
Definition: engine.h:283
void updateTriggerWaveform()
Definition: engine.cpp:118
virtual operation_mode_e getOperationMode() const =0
angle_t engineCycle
Definition: engine_state.h:27
void onFastCallback() override
bool isOld(int globalVersion)
void setNeedsDisambiguation(bool needsDisambiguation)
void resetState() override
angle_t syncEnginePhase(int divider, int remainder, angle_t engineCycle)
bool hasSynchronizedPhase() const
Multi-channel software PWM output configuration.
pwm_config_safe_state_s safe
floatus_t oneDegreeUs
float getCachedRpm() const
static float getOrZero(SensorType type)
Definition: sensor.h:92
VvtTriggerDecoder vvtState[BANKS_COUNT][CAMS_PER_BANK]
InstantRpmCalculator instantRpm
PrimaryTriggerDecoder triggerState
float m_lastToothPhaseFromSyncPoint
angle_t findNextTriggerToothAngle(int nextToothIndex)
angle_t getVVTPosition(uint8_t bankIndex, uint8_t camIndex)
TriggerWaveform vvtShape[CAMS_PER_BANK]
LocalVersionHolder triggerVersion
int getHwEventCounter(int index) const
int vvtEventFallCounter[CAM_INPUTS_COUNT]
bool isSpinningJustForWatchdog
expected< float > expectedNextPhase
TriggerWaveform triggerShape
bool isToothExpectedNow(efitick_t timestamp)
angle_t vvtPosition[BANKS_COUNT][CAMS_PER_BANK]
TriggerFormDetails triggerFormDetails
void handleShaftSignal(trigger_event_e signal, efitick_t timestamp)
float mapCamPrevCycleValue
TriggerNoiseFilter noiseFilter
bool checkIfTriggerConfigChanged()
cyclic_buffer< int > triggerErrorDetection
VvtTriggerConfiguration vvtTriggerConfiguration[CAMS_PER_BANK]
void decodeMapCam(efitick_t nowNt, float currentPhase)
int vvtEventRiseCounter[CAM_INPUTS_COUNT]
expected< float > getCurrentEnginePhase(efitick_t nowNt) const
bool directSelfStimulation
bool hwTriggerInputEnabled
bool triggerConfigChangedOnLastConfigurationChange
PrimaryTriggerConfiguration primaryTriggerConfiguration
angle_t currentVVTEventPosition[BANKS_COUNT][CAMS_PER_BANK]
uint32_t engineCycleEventCount
angle_t syncEnginePhaseAndReport(int divider, int remainder)
trigger_config_s TriggerType
int getCrankSynchronizationCounter() const
uint32_t orderingErrorCounter
expected< TriggerDecodeResult > decodeTriggerEvent(const char *msg, const TriggerWaveform &triggerShape, TriggerStateListener *triggerStateListener, const TriggerConfiguration &triggerConfiguration, const trigger_event_e signal, const efitick_t nowNt)
Trigger decoding happens here VR falls are filtered out and some VR noise detection happens prior to ...
current_cycle_state_s currentCycle
uint32_t toothDurations[GAP_TRACKING_LENGTH+1]
uint32_t totalTriggerErrorCounter
angle_t eventAngles[2 *PWM_PHASE_MAX_COUNT]
efitick_t accumSignalPrevPeriods[HW_EVENT_TYPES]
bool noiseFilter(efitick_t nowNt, TriggerDecoderBase *triggerState, trigger_event_e signal)
efitick_t accumSignalPeriods[HW_EVENT_TYPES]
efitick_t lastSignalTimes[HW_EVENT_TYPES]
Trigger shape has all the fields needed to describe and decode trigger signal.
void setShapeDefinitionError(bool value)
bool needsDisambiguation() const
void initializeSyncPoint(TriggerDecoderBase &state, const TriggerConfiguration &triggerConfiguration)
float synchronizationRatioFrom[GAP_TRACKING_LENGTH]
void initializeTriggerWaveform(operation_mode_e triggerOperationMode, const trigger_config_s &triggerType)
size_t getLength() const
float synchronizationRatioTo[GAP_TRACKING_LENGTH]
operation_mode_e getWheelOperationMode() const
size_t getSize() const
rusEfi console sniffer data buffer
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
Gpio
@ Unassigned
const char * boolToString(bool value)
Definition: efilib.cpp:18
efitimeus_t getTimeNowUs()
Definition: efitime.cpp:26
int time2print(int64_t time)
Definition: efitime.h:22
EngineRotationState * getEngineRotationState()
Definition: engine.cpp:572
LimpManager * getLimpManager()
Definition: engine.cpp:595
ExecutorInterface * getExecutorInterface()
Definition: engine.cpp:584
EngineState * getEngineState()
Definition: engine.cpp:576
TriggerCentral * getTriggerCentral()
Definition: engine.cpp:589
Engine * engine
void addEngineSnifferVvtEvent(int vvtIndex, FrontDirection frontDirection)
void addEngineSnifferCrankEvent(int wheelIndex, int triggerEventIndex, FrontDirection frontDirection)
void initWaveChart(WaveChart *chart)
rusEfi console wave sniffer
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
ioportmask_t criticalErrorLedPin
Definition: efi_gpio.cpp:809
ioportid_t criticalErrorLedPort
Definition: efi_gpio.cpp:808
uint32_t ioportmask_t
Digital I/O port sized unsigned type.
Definition: hal_pal_lld.h:78
GPIO_TypeDef * ioportid_t
Port Identifier.
Definition: hal_pal_lld.h:102
HIP9011/TPIC8101 driver.
void writePad(const char *msg, brain_pin_e pin, int bit)
Definition: io_pins.cpp:115
void waTriggerEventListener(trigger_event_e ckpSignalType, uint32_t index, efitick_t edgeTimestamp)
void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_t currentPhase, angle_t nextPhase)
Main logic header.
void mapAveragingTriggerCallback(uint32_t index, efitick_t edgeTimestamp)
uint32_t getTimeNowLowerNt()
@ CUSTOM_SHAPE_LEN_ZERO
@ CUSTOM_ERR_TRIGGER_ZERO
@ CUSTOM_PRIMARY_BAD_TOOTH_TIMING
@ CUSTOM_ERR_TRIGGER_WAVEFORM_TOO_LONG
@ CUSTOM_VVT_SYNC_POSITION
@ CUSTOM_TRIGGER_STACK
@ CUSTOM_TRIGGER_EVENT_TYPE
@ CUSTOM_VVT_MODE_NOT_SELECTED
@ CUSTOM_ERR_UNEXPECTED_SHAFT_EVENT
@ CUSTOM_ERR_6555
@ OBD_Camshaft_Position_Sensor_Circuit_Range_Performance
@ CUSTOM_PRIMARY_DOUBLED_EDGE
@ HandleShaftSignal
@ ShaftPositionListeners
engine_configuration_s * engineConfiguration
const char * hwPortname(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)
void tdcMarkCallback(uint32_t trgEventIndex, efitick_t nowNt)
operation_mode_e lookupOperationMode()
void rpmShaftPositionCallback(trigger_event_e ckpSignalType, uint32_t trgEventIndex, efitick_t nowNt)
Shaft position callback used by RPM calculation logic.
vvt_mode_e
Definition: rusefi_enums.h:120
operation_mode_e
Definition: rusefi_enums.h:247
@ FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR
Definition: rusefi_enums.h:268
@ FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR
Definition: rusefi_enums.h:278
@ FOUR_STROKE_THREE_TIMES_CRANK_SENSOR
Definition: rusefi_enums.h:273
@ FOUR_STROKE_CRANK_SENSOR
Definition: rusefi_enums.h:254
@ OM_NONE
Definition: rusefi_enums.h:248
@ FOUR_STROKE_CAM_SENSOR
Definition: rusefi_enums.h:258
@ TWO_STROKE
Definition: rusefi_enums.h:262
@ FOUR_STROKE_SIX_TIMES_CRANK_SENSOR
Definition: rusefi_enums.h:283
TriggerWheel
Definition: rusefi_enums.h:47
float floatus_t
Definition: rusefi_types.h:68
float angle_t
Definition: rusefi_types.h:58
trigger_event_e
@ SHAFT_SECONDARY_RISING
@ SHAFT_SECONDARY_FALLING
@ SHAFT_PRIMARY_FALLING
@ SHAFT_PRIMARY_RISING
TriggerValue
brain_pin_e pin
Definition: stm32_adc.cpp:15
virtual void scheduleByTimestampNt(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s action)=0
size_t eventCount[PWM_PHASE_MAX_WAVE_PER_PWM]
brain_input_pin_e logicAnalyzerPins[LOGIC_ANALYZER_CHANNEL_COUNT]
brain_input_pin_e triggerInputPins[TRIGGER_INPUT_PIN_COUNT]
pin_output_mode_e triggerSimulatorPinModes[TRIGGER_SIMULATOR_PIN_COUNT]
scaled_channel< uint16_t, 30, 1 > instantMAPValue
uint32_t hwEventCounters[HW_EVENT_TYPES]
void LogTriggerTooth(trigger_event_e tooth, efitick_t timestamp)
composite packet size
PwmConfig triggerEmulatorSignals[NUM_EMULATOR_CHANNELS]
static bool vvtWithRealDecoder(vvt_mode_e vvtMode)
void hwHandleVvtCamSignal(bool isRising, efitick_t timestamp, int index)
static void triggerShapeInfo()
uint32_t triggerDuration
static void reportEventToWaveChart(trigger_event_e ckpSignalType, int triggerEventIndex, bool addOppositeEvent)
int getCrankDivider(operation_mode_e operationMode)
static const bool isUpEvent[4]
static scheduling_s debugToggleScheduling
TriggerDecoderBase initState("init")
void validateTriggerInputs()
void hwHandleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp)
static void logVvtFront(bool useOnlyRise, bool isImportantFront, TriggerValue front, efitick_t nowNt, int index)
void initTriggerCentral()
static const int wheelIndeces[4]
static angle_t wrapVvt(angle_t vvtPosition, int period)
int maxTriggerReentrant
static void turnOffAllDebugFields(void *arg)
int triggerReentrant
static void initVvtShape(TriggerWaveform &shape, const TriggerConfiguration &p_config, TriggerDecoderBase &initState)
uint32_t triggerMaxDuration
static angle_t adjustCrankPhase(int camIndex)
BOARD_WEAK bool boardAllowTriggerActions()
void handleVvtCamSignal(TriggerValue front, efitick_t nowNt, int index)
void triggerInfo(void)
void onConfigurationChangeTriggerCallback()
void handleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp)
static void calculateTriggerSynchPoint(const PrimaryTriggerConfiguration &primaryTriggerConfiguration, TriggerWaveform &shape, TriggerDecoderBase &initState)
static void resetRunningTriggerCounters()
WaveChart waveChart
static TriggerWheel eventIndex[4]
void disableTriggerStimulator()
bool isUsefulSignal(trigger_event_e signal, const TriggerWaveform &shape)
angle_t wrapAngleMethod(angle_t param, const char *msg="", ObdCode code=ObdCode::OBD_PCM_Processor_Fault)
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
angle_t getEngineCycle(operation_mode_e operationMode)