rusEFI
The most advanced open source ECU
efi_gpio.cpp
Go to the documentation of this file.
1 /**
2  * @file efi_gpio.cpp
3  * @brief EFI-related GPIO code
4  *
5  * @date Sep 26, 2014
6  * @author Andrey Belomutskiy, (c) 2012-2020
7  */
8 
9 #include "pch.h"
10 #include "bench_test.h"
11 #include "engine_sniffer.h"
12 
13 #include "drivers/gpio/gpio_ext.h"
14 
15 #if HW_HELLEN
16 #include "hellen_meta.h"
17 #endif // HW_HELLEN
18 
19 #if EFI_ELECTRONIC_THROTTLE_BODY
20 #include "electronic_throttle.h"
21 #endif /* EFI_ELECTRONIC_THROTTLE_BODY */
22 
23 // todo: clean this mess, this should become 'static'/private
25 
26 static const char* const sparkNames[] = { "Coil 1", "Coil 2", "Coil 3", "Coil 4", "Coil 5", "Coil 6", "Coil 7", "Coil 8",
27  "Coil 9", "Coil 10", "Coil 11", "Coil 12"};
28 
29 static const char* const trailNames[] = { "Trail 1", "Trail 2", "Trail 3", "Trail 4", "Trail 5", "Trail 6", "Trail 7", "Trail 8",
30  "Trail 9", "Trail 10", "Trail 11", "Trail 12"};
31 
32 static const char* const trailShortNames[] = { "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "rA", "rB", "rD" };
33 
34 const char *vvtNames[] = {
35  PROTOCOL_VVT1_NAME,
36  PROTOCOL_VVT2_NAME,
37  PROTOCOL_VVT3_NAME,
38  PROTOCOL_VVT4_NAME};
39 
40 const char *laNames[] = {
41  PROTOCOL_WA_CHANNEL_1,
42  PROTOCOL_WA_CHANNEL_2,
43  PROTOCOL_WA_CHANNEL_3,
44  PROTOCOL_WA_CHANNEL_4};
45 
46 // these short names are part of engine sniffer protocol
47 static const char* const sparkShortNames[] = { PROTOCOL_COIL1_SHORT_NAME, "c2", "c3", "c4", "c5", "c6", "c7", "c8",
48  "c9", "cA", "cB", "cD"};
49 
50 static const char* const injectorNames[] = { "Injector 1", "Injector 2", "Injector 3", "Injector 4", "Injector 5", "Injector 6",
51  "Injector 7", "Injector 8", "Injector 9", "Injector 10", "Injector 11", "Injector 12"};
52 
53 static const char* const injectorShortNames[] = { PROTOCOL_INJ1_SHORT_NAME, "i2", "i3", "i4", "i5", "i6", "i7", "i8",
54  "i9", "iA", "iB", "iC"};
55 
56 static const char* const injectorStage2Names[] = { "Injector Second Stage 1", "Injector Second Stage 2", "Injector Second Stage 3", "Injector Second Stage 4", "Injector Second Stage 5", "Injector Second Stage 6",
57  "Injector Second Stage 7", "Injector Second Stage 8", "Injector Second Stage 9", "Injector Second Stage 10", "Injector Second Stage 11", "Injector Second Stage 12"};
58 
59 static const char* const injectorStage2ShortNames[] = { PROTOCOL_INJ1_STAGE2_SHORT_NAME, "j2", "j3", "j4", "j5", "j6", "j7", "j8",
60  "j9", "jA", "jB", "jC"};
61 
62 static const char* const auxValveShortNames[] = { "a1", "a2"};
63 
65 
66 RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *p_name, size_t pinOffset,
67  size_t pinModeOffset) : RegisteredOutputPin(p_name, pinOffset, pinModeOffset) {
68 }
69 
70 RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *p_name, size_t pinOffset) :
71  RegisteredOutputPin(p_name, pinOffset) {
72 }
73 
74 RegisteredOutputPin::RegisteredOutputPin(const char *p_registrationName, size_t pinOffset,
75  size_t pinModeOffset)
76  : next(registeredOutputHead)
77  , registrationName(p_registrationName)
78  , m_pinOffset(static_cast<uint16_t>(pinOffset))
79  , m_hasPinMode(true)
80  , m_pinModeOffset(static_cast<uint16_t>(pinModeOffset))
81 {
82  // adding into head of the list is so easy and since we do not care about order that's what we shall do
83  registeredOutputHead = this;
84 }
85 
86 RegisteredOutputPin::RegisteredOutputPin(const char *p_registrationName, size_t pinOffset)
87  : next(registeredOutputHead)
88  , registrationName(p_registrationName)
89  , m_pinOffset(static_cast<uint16_t>(pinOffset))
90  , m_hasPinMode(false)
91  , m_pinModeOffset(-1)
92 {
93  // adding into head of the list is so easy and since we do not care about order that's what we shall do
94  registeredOutputHead = this;
95 }
96 
98 #if EFI_PROD_CODE
99  brain_pin_e curPin = *(brain_pin_e *) ((void *) (&((char*)&activeConfiguration)[m_pinOffset]));
100  brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
101  bool pinChanged = curPin != newPin;
102 
103  if (!m_hasPinMode) {
104  return pinChanged;
105  }
106 
107  pin_output_mode_e curMode = *(pin_output_mode_e *) ((void *) (&((char*)&activeConfiguration)[m_pinModeOffset]));
108  pin_output_mode_e newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
109  return pinChanged || curMode != newMode;
110 #else
111  return true;
112 #endif // EFI_PROD_CODE
113 }
114 
116  brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
117 
118  pin_output_mode_e newMode;
119  if (m_hasPinMode) {
120  newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
121  } else {
122  newMode = OM_DEFAULT;
123  }
124 
126  this->initPin(registrationName, newPin, newMode);
127  }
128 }
129 
133  }
134 }
135 
136 #define CONFIG_OFFSET(x) (offsetof(engine_configuration_s, x))
137 // todo: pin and pinMode should be combined into a composite entity
138 // todo: one of the impediments is code generator hints handling (we need custom hints and those are not handled nice for fields of structs?)
139 #define CONFIG_PIN_OFFSETS(x) CONFIG_OFFSET(x##Pin), CONFIG_OFFSET(x##PinMode)
140 
141 // offset of X within engineConfiguration, plus offset of Y within X
142 // decltype(engine_configuration_s::x) resolves the typename of the struct X inside engineConfiguration
143 #define CONFIG_OFFSET2(x, y) (offsetof(engine_configuration_s, x) + offsetof(decltype(engine_configuration_s::x), y))
144 #define CONFIG_PIN_OFFSETS2(x, y) CONFIG_OFFSET2(x, y##Pin), CONFIG_OFFSET2(x, y##PinMode)
145 
147  mainRelay("Main Relay", CONFIG_PIN_OFFSETS(mainRelay)),
148  hpfpValve("HPFP Valve", CONFIG_PIN_OFFSETS(hpfpValve)),
149  starterControl("Starter Relay", CONFIG_PIN_OFFSETS(starterControl)),
150  starterRelayDisable("Starter Disable Relay", CONFIG_PIN_OFFSETS(starterRelayDisable)),
151  fanRelay("Fan Relay", CONFIG_PIN_OFFSETS(fan)),
152  fanRelay2("Fan Relay 2", CONFIG_PIN_OFFSETS(fan2)),
153  acRelay("A/C Relay", CONFIG_PIN_OFFSETS(acRelay)),
154  fuelPumpRelay("Fuel pump Relay", CONFIG_PIN_OFFSETS(fuelPump)),
155 #if EFI_HD_ACR
156  harleyAcr("Harley ACR", CONFIG_OFFSET(acrPin)),
157  harleyAcr2("Harley ACR 2", CONFIG_OFFSET(acrPin2)),
158 #endif // EFI_HD_ACR
159  boostPin("Boost", CONFIG_PIN_OFFSETS(boostControl)),
160  idleSolenoidPin("Idle Valve", CONFIG_OFFSET2(idle, solenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
161  secondIdleSolenoidPin("Idle Valve#2", CONFIG_OFFSET(secondSolenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
162  alternatorPin("Alternator control", CONFIG_PIN_OFFSETS(alternatorControl)),
163  checkEnginePin("checkEnginePin", CONFIG_PIN_OFFSETS(malfunctionIndicator)),
164  tachOut("tachOut", CONFIG_PIN_OFFSETS(tachOutput)),
165  triggerDecoderErrorPin("led: trigger debug", CONFIG_PIN_OFFSETS(triggerError)),
166  speedoOut("speedoOut", CONFIG_OFFSET(speedometerOutputPin))
167 {
168  hpfpValve.setName(PROTOCOL_HPFP_NAME);
169 #if EFI_HD_ACR
170  harleyAcr.setName(PROTOCOL_ACR_NAME);
171 #endif // EFI_HD_ACR
172 
173  static_assert(efi::size(sparkNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
174  static_assert(efi::size(trailNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
175  static_assert(efi::size(injectorNames) >= MAX_CYLINDER_COUNT, "Too many injection pins");
176  for (int i = 0; i < MAX_CYLINDER_COUNT;i++) {
177  enginePins.coils[i].coilIndex = i;
180 
183 
187 
191  }
192 
193  static_assert(efi::size(auxValveShortNames) >= AUX_DIGITAL_VALVE_COUNT, "Too many aux valve pins");
194  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT;i++) {
196  }
197 }
198 
199 /**
200  * Sets the value of the pin. On this layer the value is assigned as is, without any conversion.
201  */
202 
203 #define unregisterOutputIfPinChanged(output, pin) { \
204  if (isConfigurationChanged(pin)) { \
205  (output).deInit(); \
206  } \
207 }
208 
209 #define unregisterOutputIfPinOrModeChanged(output, pin, mode) { \
210  if (isPinOrModeChanged(pin, mode)) { \
211  (output).deInit(); \
212  } \
213 }
214 
216  bool result = false;
217  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
218  result |= coils[i].stop();
219  result |= injectors[i].stop();
220  result |= injectorsStage2[i].stop();
221  result |= trailingCoils[i].stop();
222  }
223  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
224  result |= auxValve[i].stop();
225  }
226  return result;
227 }
228 
232  stopAuxValves();
233 
234 #if EFI_ELECTRONIC_THROTTLE_BODY
236 #endif /* EFI_ELECTRONIC_THROTTLE_BODY */
237 
238  // todo: add pinMode
239  unregisterOutputIfPinChanged(sdCsPin, sdCardCsPin);
240  unregisterOutputIfPinChanged(accelerometerCs, accelerometerCsPin);
241 
243  while (pin != nullptr) {
244  pin->unregister();
245  pin = pin->next;
246  }
247 }
248 
251  while (pin != nullptr) {
252  efiPrintf("%s %d", pin->getRegistrationName(), pin->currentLogicValue);
253  pin = pin->next;
254  }
255 }
256 
258 #if EFI_ENGINE_CONTROL
261  startAuxValves();
262 #endif /* EFI_ENGINE_CONTROL */
263 
265  while (pin != nullptr) {
266  pin->init();
267  pin = pin->next;
268  }
269 }
270 
272  for (int i = 0; i < MAX_CYLINDER_COUNT;i++) {
273  injectors[i].reset();
274  coils[i].reset();
275  trailingCoils[i].reset();
276  }
277 }
278 
280  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
281  unregisterOutputIfPinOrModeChanged(enginePins.coils[i], ignitionPins[i], ignitionPinMode);
282  unregisterOutputIfPinOrModeChanged(enginePins.trailingCoils[i], trailingCoilPins[i], ignitionPinMode);
283  }
284 }
285 
287  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
288  unregisterOutputIfPinOrModeChanged(enginePins.injectors[i], injectionPins[i], injectionPinMode);
289  unregisterOutputIfPinOrModeChanged(enginePins.injectorsStage2[i], injectionPinsStage2[i], injectionPinMode);
290  }
291 }
292 
294  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
295  NamedOutputPin *output = &enginePins.auxValve[i];
296  // todo: do we need auxValveMode and reuse code?
297  if (isConfigurationChanged(auxValves[i])) {
298  (output)->deInit();
299  }
300  }
301 }
302 
304 #if EFI_PROD_CODE
305  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
306  NamedOutputPin *output = &enginePins.auxValve[i];
307  // todo: do we need auxValveMode and reuse code?
308  if (isConfigurationChanged(auxValves[i])) {
309  output->initPin(output->getName(), engineConfiguration->auxValves[i]);
310  }
311  }
312 #endif /* EFI_PROD_CODE */
313 }
314 
316 #if EFI_PROD_CODE
317  for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
318  NamedOutputPin *trailingOutput = &enginePins.trailingCoils[i];
319  if (isPinOrModeChanged(trailingCoilPins[i], ignitionPinMode)) {
321  }
322 
323  NamedOutputPin *output = &enginePins.coils[i];
324  if (isPinOrModeChanged(ignitionPins[i], ignitionPinMode)) {
326  }
327  }
328 #endif /* EFI_PROD_CODE */
329 }
330 
332 #if EFI_PROD_CODE
333  // todo: should we move this code closer to the injection logic?
334  for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
335  NamedOutputPin *output = &enginePins.injectors[i];
336  if (isPinOrModeChanged(injectionPins[i], injectionPinMode)) {
337  output->initPin(output->getName(), engineConfiguration->injectionPins[i],
339  }
340 
341  output = &enginePins.injectorsStage2[i];
342  if (isPinOrModeChanged(injectionPinsStage2[i], injectionPinMode)) {
345  }
346  }
347 #endif /* EFI_PROD_CODE */
348 }
349 
351  switch(index) {
352 #if EFI_VVT_PID
353  case BENCH_VVT0_VALVE:
354  return getVvtOutputPin(0);
355  case BENCH_VVT1_VALVE:
356  return getVvtOutputPin(1);
357  case BENCH_VVT2_VALVE:
358  return getVvtOutputPin(2);
359  case BENCH_VVT3_VALVE:
360  return getVvtOutputPin(3);
361 #endif // EFI_VVT_PID
362  case BENCH_MAIN_RELAY:
363  return &mainRelay;
364  case BENCH_HPFP_VALVE:
365  return &hpfpValve;
366  case BENCH_FUEL_PUMP:
367  return &fuelPumpRelay;
369  return &starterControl;
371  return &checkEnginePin;
373  return &acRelay;
374  case BENCH_FAN_RELAY:
375  return &fanRelay;
376 #if EFI_HD_ACR
377  case HD_ACR:
378  return &harleyAcr;
379  case HD_ACR2:
380  return &harleyAcr2;
381 #endif
382  case BENCH_IDLE_VALVE:
383  return &idleSolenoidPin;
384  case BENCH_FAN_RELAY_2:
385  return &fanRelay;
386  default:
387  criticalError("Unexpected bench pin %d", index);
388  }
389  return nullptr;
390 }
391 
393 }
394 
395 NamedOutputPin::NamedOutputPin(const char *p_name) : OutputPin() {
396  name = p_name;
397 }
398 
399 const char *NamedOutputPin::getName() const {
400  return name;
401 }
402 
403 void NamedOutputPin::setName(const char* p_name) {
404  name = p_name;
405 }
406 
407 const char *NamedOutputPin::getShortName() const {
408  return shortName == nullptr ? name : shortName;
409 }
410 
411 #if EFI_UNIT_TEST
412 extern bool verboseMode;
413 #endif // EFI_UNIT_TEST
414 
416  setHigh(nullptr);
417 }
418 
419 void NamedOutputPin::setHigh(const char *msg) {
420 #if EFI_UNIT_TEST
421  if (verboseMode) {
422  efiPrintf("pin %s goes high", name);
423  }
424 #endif // EFI_UNIT_TEST
425 #if EFI_DEFAILED_LOGGING
426 // signal->hi_time = hTimeNow();
427 #endif /* EFI_DEFAILED_LOGGING */
428 
429  // turn the output level ACTIVE
430  setValue(msg, true);
431 
432 #if EFI_ENGINE_SNIFFER
434 #endif /* EFI_ENGINE_SNIFFER */
435 }
436 
438  setLow(nullptr);
439 }
440 
441 void NamedOutputPin::setLow(const char *msg) {
442 #if EFI_UNIT_TEST
443  if (verboseMode) {
444  efiPrintf("pin %s goes low", name);
445  }
446 #endif // EFI_UNIT_TEST
447 
448  // turn off the output
449  setValue(msg, false);
450 
451 #if EFI_ENGINE_SNIFFER
453 #endif /* EFI_ENGINE_SNIFFER */
454 }
455 
457 #if EFI_GPIO_HARDWARE
458  if (isInitialized() && getLogicValue()) {
459  setValue("stop", false);
460  efiPrintf("turning off %s", name);
461  return true;
462  }
463 #endif /* EFI_GPIO_HARDWARE */
464  return false;
465 }
466 
468  // If this injector was open, close it and reset state
469  if (overlappingCounter != 0) {
470  overlappingCounter = 0;
471  setValue("reset", 0);
472  }
473 
474  // todo: this could be refactored by calling some super-reset method
475  currentLogicValue = 0;
476 }
477 
479  reset();
480 }
481 
484  // this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
485  switch (coilIndex) {
486  case 0:
488  break;
489  case 1:
491  break;
492  case 2:
494  break;
495  case 3:
497  break;
498  case 4:
500  break;
501  case 5:
503  break;
504  }
505 }
506 
509  // this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
510  switch (coilIndex) {
511  case 0:
513  break;
514  case 1:
516  break;
517  case 2:
519  break;
520  case 3:
522  break;
523  case 4:
525  break;
526  case 5:
528  break;
529  }
530 }
531 
533  signalFallSparkId = 0;
534 }
535 
537 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
538 #if (BOARD_EXT_GPIOCHIPS > 0)
539  if (ext)
540  return true;
541 #endif /* (BOARD_EXT_GPIOCHIPS > 0) */
542  return m_port != NULL;
543 #else /* EFI_GPIO_HARDWARE */
544  return true;
545 #endif /* EFI_GPIO_HARDWARE */
546 }
547 
549  setValue("toggle", !getLogicValue());
550 }
551 
552 bool OutputPin::getAndSet(int logicValue) {
553  bool oldValue = getLogicValue();
554  setValue(logicValue);
555  return oldValue;
556 }
557 
558 // This function is only used on real hardware
559 #if EFI_PROD_CODE
560 void OutputPin::setOnchipValue(int electricalValue) {
562  // todo: make 'setOnchipValue' or 'reportsetOnchipValueError' virtual and override for NamedOutputPin?
563  warning(ObdCode::CUSTOM_ERR_6586, "attempting to change unassigned pin");
564  return;
565  }
566  palWritePad(m_port, m_pin, electricalValue);
567 }
568 #endif // EFI_PROD_CODE
569 
570 void OutputPin::setValue(int logicValue, bool isForce) {
571  setValue(nullptr, logicValue, isForce);
572 }
573 
574 #if EFI_SIMULATOR
577  pinToggleCounter = 0;
578 }
579 #endif // EFI_SIMULATOR
580 
581 extern bool qcDirectPinControlMode;
582 
583 void OutputPin::setValue(const char *msg, int logicValue, bool isForce) {
584  UNUSED(msg);
585  if ((qcDirectPinControlMode || getOutputOnTheBenchTest() == this) && !isForce) {
586  return;
587  }
588 
589 #if ENABLE_PERF_TRACE
590 // todo: https://github.com/rusefi/rusefi/issues/1638
591 // ScopePerf perf(PE::OutputPinSetValue);
592 #endif // ENABLE_PERF_TRACE
593 
594 #if EFI_UNIT_TEST
595  if (currentLogicValue != logicValue) {
597  }
598 #endif // EFI_UNIT_TEST
599 
600 #if EFI_SIMULATOR
601  if (currentLogicValue != logicValue) {
602  if (pinToggleCounter > 0) {
604  durationsInStateMs[1] = pinToggleTimer.getElapsedUs() / 1000;
605  }
607  pinToggleTimer.reset();
608  }
609 #endif // EFI_SIMULATOR
610 
611 #if EFI_UNIT_TEST
612  if (verboseMode) {
613  efiPrintf("pin goes %d", logicValue);
614  }
615 #endif // EFI_UNIT_TEST
616 
617  // Always store the current logical value of the pin (so it can be
618  // used internally even if not connected to a real hardware pin)
619  currentLogicValue = logicValue;
620 
621  // Nothing else to do if not configured
622  if (!isBrainPinValid(brainPin)) {
623  return;
624  }
625 
626  efiAssertVoid(ObdCode::CUSTOM_ERR_6622, mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
627  int electricalValue = getElectricalValue(logicValue, mode);
628 
629 #if EFI_PROD_CODE
630  #if (BOARD_EXT_GPIOCHIPS > 0)
631  if (!this->ext) {
632  setOnchipValue(electricalValue);
633  } else {
634  /* external pin */
635  gpiochips_writePad(this->brainPin, logicValue);
636  /* TODO: check return value */
637  }
638  #else
639  setOnchipValue(electricalValue);
640  #endif
641 #else /* EFI_PROD_CODE */
642  setMockState(brainPin, electricalValue);
643 #endif /* EFI_PROD_CODE */
644 }
645 
647  // Compare against 1 since it could also be INITIAL_PIN_STATE (which means logical 0, but we haven't initialized the pin yet)
648  return currentLogicValue == 1;
649 }
650 
652  assertOMode(mode);
653  this->mode = outputMode;
654  setValue(false, /*force*/true); // initial state
655 }
656 
658 #if EFI_PROD_CODE
659 #if BOARD_EXT_GPIOCHIPS > 0
661  return gpiochips_getDiag(brainPin);
662  }
663 #endif
664 #endif /* EFI_PROD_CODE */
665  // TODO: add hook to board code for custom diagnostic, like it is done on S105
666  return PIN_UNKNOWN;
667 }
668 
670 #if EFI_GPIO_HARDWARE
671 
672 #if HAL_USE_SPI
674 #endif /* HAL_USE_SPI */
675 
676 #if EFI_SHAFT_POSITION_INPUT
677  // todo: migrate remaining OutputPin to RegisteredOutputPin in order to get consistent dynamic pin init/deinit
679 #endif // EFI_SHAFT_POSITION_INPUT
680 
682 
683 #endif /* EFI_GPIO_HARDWARE */
684 }
685 
686 void OutputPin::initPin(const char *p_msg, brain_pin_e p_brainPin) {
687  initPin(p_msg, p_brainPin, OM_DEFAULT);
688 }
689 
690 void OutputPin::initPin(const char *msg, brain_pin_e p_brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError) {
691 #if EFI_UNIT_TEST
692  pinToggleCounter = 0;
693 #endif
694 
695  if (!isBrainPinValid(p_brainPin)) {
696  return;
697  }
698 
699  // Enter a critical section so that other threads can't change the pin state out from underneath us
700  chibios_rt::CriticalSectionLocker csl;
701 
702  if (!forceInitWithFatalError && hasFirmwareError()) {
703  // Don't allow initializing more pins if we have a fatal error.
704  // Pins should have just been reset, so we shouldn't try to init more.
705  return;
706  }
707 
708  // Check that this OutputPin isn't already assigned to another pin (reinit is allowed to change mode)
709  // To avoid this error, call deInit() first
710  if (isBrainPinValid(brainPin) && brainPin != p_brainPin) {
711  firmwareError(ObdCode::CUSTOM_OBD_PIN_CONFLICT, "outputPin [%s] already assigned, cannot reassign without unregister first", msg);
712  return;
713  }
714 
715  if (outputMode > OM_OPENDRAIN_INVERTED) {
716  firmwareError(ObdCode::CUSTOM_INVALID_MODE_SETTING, "%s invalid pin_output_mode_e %d %s",
717  msg,
718  outputMode,
719  hwPortname(p_brainPin)
720  );
721  return;
722  }
723 
724 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
725  iomode_t l_mode = (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) ?
726  PAL_MODE_OUTPUT_PUSHPULL : PAL_MODE_OUTPUT_OPENDRAIN;
727 
728  #if (BOARD_EXT_GPIOCHIPS > 0)
729  this->ext = false;
730  #endif
731  if (brain_pin_is_onchip(p_brainPin)) {
732  m_port = getHwPort(msg, p_brainPin);
733  m_pin = getHwPin(msg, p_brainPin);
734 
735  // Validate port
736  if (m_port == GPIO_NULL) {
737  criticalError("OutputPin::initPin got invalid port for pin idx %d", static_cast<int>(p_brainPin));
738  return;
739  }
740  }
741  #if (BOARD_EXT_GPIOCHIPS > 0)
742  else {
743  this->ext = true;
744  }
745  #endif
746 #endif // briefly leave the include guard because we need to set default state in tests
747 
748  brainPin = p_brainPin;
749 
750  // The order of the next two calls may look strange, which is a good observation.
751  // We call them in this order so that the pin is set to a known state BEFORE
752  // it's enabled. Enabling the pin then setting it could result in a (brief)
753  // mystery state being driven on the pin (potentially dangerous).
754  setDefaultPinState(outputMode);
755 
756 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
757  efiSetPadMode(msg, brainPin, l_mode);
759  // todo: handle OM_OPENDRAIN and OM_OPENDRAIN_INVERTED as well
760  if (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) {
761 #ifndef DISABLE_PIN_STATE_VALIDATION
762  int actualValue = palReadPad(m_port, m_pin);
763  // we had enough drama with pin configuration in board.h and else that we shall self-check
764 
765  const int logicalValue =
766  (outputMode == OM_INVERTED)
767  ? !actualValue
768  : actualValue;
769 
770  // if the pin was set to logical 1, then set an error and disable the pin so that things don't catch fire
771  if (logicalValue) {
772  criticalError("HARDWARE VALIDATION FAILED %s: unexpected startup pin state %s actual value=%d logical value=%d mode=%s", msg, hwPortname(brainPin), actualValue, logicalValue, getPin_output_mode_e(outputMode));
774  }
775 #endif
776  }
777  }
778 #endif /* EFI_GPIO_HARDWARE */
779 }
780 
782  // Unregister under lock - we don't want other threads mucking with the pin while we're trying to turn it off
783  chibios_rt::CriticalSectionLocker csl;
784 
785  // nothing to do if not registered in the first place
786  if (!isBrainPinValid(brainPin)) {
787  return;
788  }
789 
790 #if (BOARD_EXT_GPIOCHIPS > 0)
791  ext = false;
792 #endif // (BOARD_EXT_GPIOCHIPS > 0)
793 
794  efiPrintf("unregistering %s", hwPortname(brainPin));
795 
796 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
798 #endif /* EFI_GPIO_HARDWARE */
799 
800  // Clear the pin so that it won't get set any more
802 }
803 
804 #if EFI_GPIO_HARDWARE
805 
806 // questionable trick: we avoid using 'getHwPort' and 'getHwPin' in case of errors in order to increase the changes of turning the LED
807 // by reducing stack requirement
811 
812 #if EFI_PROD_CODE
813 static void initErrorLed(Gpio led) {
814  enginePins.errorLedPin.initPin("led: CRITICAL status", led, (LED_PIN_MODE));
815  criticalErrorLedPort = getHwPort("CRITICAL", led);
816  criticalErrorLedPin = getHwPin("CRITICAL", led);
817  criticalErrorLedState = (LED_PIN_MODE == OM_INVERTED) ? 0 : 1;
818 }
819 #endif /* EFI_PROD_CODE */
820 
822 #if EFI_PROD_CODE
823  initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
824 
825  addConsoleAction("gpio_pins", EnginePins::debug);
826 #endif /* EFI_PROD_CODE */
827 }
828 
829 /**
830  * This method is part of fatal error handling.
831  * The whole method is pretty naive, but that's at least something.
832  */
833 void turnAllPinsOff(void) {
834  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
835  enginePins.injectors[i].setValue(false);
836  enginePins.coils[i].setValue(false);
838  }
841  enginePins.checkEnginePin.setValue(true); // yes this one can go ON
842 }
843 #endif /* EFI_GPIO_HARDWARE */
844 
static SimplePwm alternatorControl("alt")
const char * getPin_output_mode_e(pin_output_mode_e value)
const OutputPin * getOutputOnTheBenchTest()
Definition: bench_test.cpp:32
Utility methods related to bench testing.
void efiSetPadMode(const char *msg, brain_pin_e brainPin, iomode_t mode)
TunerStudioOutputChannels outputChannels
Definition: engine.h:99
RegisteredOutputPin harleyAcr2
Definition: efi_gpio.h:94
RegisteredNamedOutputPin harleyAcr
Definition: efi_gpio.h:93
OutputPin accelerometerCs
Definition: efi_gpio.h:123
static void debug()
Definition: efi_gpio.cpp:249
void startPins()
Definition: efi_gpio.cpp:257
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:126
RegisteredOutputPin mainRelay
Definition: efi_gpio.h:76
NamedOutputPin auxValve[AUX_DIGITAL_VALVE_COUNT]
Definition: efi_gpio.h:129
void startAuxValves()
Definition: efi_gpio.cpp:303
void startIgnitionPins()
Definition: efi_gpio.cpp:315
OutputPin o2heater
Definition: efi_gpio.h:97
RegisteredOutputPin fanRelay
Definition: efi_gpio.h:86
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:128
OutputPin errorLedPin
Definition: efi_gpio.h:103
RegisteredOutputPin starterControl
Definition: efi_gpio.h:82
OutputPin debugTriggerSync
Definition: efi_gpio.h:108
void startInjectionPins()
Definition: efi_gpio.cpp:331
void stopInjectionPins()
Definition: efi_gpio.cpp:286
void reset()
Definition: efi_gpio.cpp:271
InjectorOutputPin injectors[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:125
void stopAuxValves()
Definition: efi_gpio.cpp:293
bool stopPins()
Definition: efi_gpio.cpp:215
RegisteredOutputPin fuelPumpRelay
Definition: efi_gpio.h:91
void unregisterPins()
Definition: efi_gpio.cpp:229
RegisteredOutputPin acRelay
Definition: efi_gpio.h:90
OutputPin * getOutputPinForBenchMode(bench_mode_e idx)
Definition: efi_gpio.cpp:350
IgnitionOutputPin coils[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:127
OutputPin sdCsPin
Definition: efi_gpio.h:122
RegisteredOutputPin checkEnginePin
Definition: efi_gpio.h:116
RegisteredOutputPin idleSolenoidPin
Definition: efi_gpio.h:110
RegisteredNamedOutputPin hpfpValve
Definition: efi_gpio.h:80
void stopIgnitionPins()
Definition: efi_gpio.cpp:279
void setHigh() override
Definition: efi_gpio.cpp:482
void setLow() override
Definition: efi_gpio.cpp:507
int signalFallSparkId
Definition: efi_gpio.h:35
int8_t coilIndex
Definition: efi_gpio.h:36
const char * getName() const
Definition: efi_gpio.cpp:399
virtual void setLow()
Definition: efi_gpio.cpp:437
void setName(const char *)
Definition: efi_gpio.cpp:403
const char * shortName
Definition: efi_output.h:129
const char * name
Definition: efi_output.h:133
virtual void setHigh()
Definition: efi_gpio.cpp:415
const char * getShortName() const
Definition: efi_gpio.cpp:407
Single output pin reference and state.
Definition: efi_output.h:50
Timer pinToggleTimer
Definition: efi_output.h:81
brain_pin_diag_e getDiag() const
Definition: efi_gpio.cpp:657
uint32_t durationsInStateMs[2]
Definition: efi_output.h:82
bool ext
Definition: efi_output.h:91
void resetToggleStats()
Definition: efi_gpio.cpp:575
void deInit()
Definition: efi_gpio.cpp:781
void initPin(const char *msg, brain_pin_e brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError=false)
Definition: efi_gpio.cpp:690
bool getLogicValue() const
Definition: efi_gpio.cpp:646
pin_output_mode_e mode
Definition: efi_output.h:105
void setOnchipValue(int electricalValue)
Definition: efi_gpio.cpp:560
bool getAndSet(int logicValue)
Definition: efi_gpio.cpp:552
void setValue(const char *msg, int logicValue, bool isForce=false)
Definition: efi_gpio.cpp:583
void toggle()
Definition: efi_gpio.cpp:548
int8_t currentLogicValue
Definition: efi_output.h:94
ioportid_t m_port
Definition: efi_output.h:72
bool isInitialized() const
Definition: efi_gpio.cpp:536
int pinToggleCounter
Definition: efi_output.h:77
uint8_t m_pin
Definition: efi_output.h:73
brain_pin_e brainPin
Definition: efi_output.h:87
void setDefaultPinState(pin_output_mode_e mode)
Definition: efi_gpio.cpp:651
RegisteredNamedOutputPin(const char *name, size_t pinOffset, size_t pinModeOffset)
Definition: efi_gpio.cpp:66
const char *const registrationName
Definition: efi_gpio.h:53
RegisteredOutputPin(const char *registrationName, size_t pinOffset, size_t pinModeOffset)
Definition: efi_gpio.cpp:74
const uint16_t m_pinModeOffset
Definition: efi_gpio.h:56
const bool m_hasPinMode
Definition: efi_gpio.h:55
bool isPinConfigurationChanged()
Definition: efi_gpio.cpp:97
const uint16_t m_pinOffset
Definition: efi_gpio.h:54
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
Gpio
@ Unassigned
@ Invalid
int gpiochips_writePad(brain_pin_e pin, int value)
Set value to gpio of gpiochip.
Definition: core.cpp:335
brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin)
Get diagnostic for given gpio.
Definition: core.cpp:373
ioportmask_t criticalErrorLedPin
Definition: efi_gpio.cpp:809
bool verboseMode
const char * laNames[]
Definition: efi_gpio.cpp:40
static const char *const auxValveShortNames[]
Definition: efi_gpio.cpp:62
static const char *const trailNames[]
Definition: efi_gpio.cpp:29
static const char *const injectorNames[]
Definition: efi_gpio.cpp:50
static RegisteredOutputPin * registeredOutputHead
Definition: efi_gpio.cpp:64
void initPrimaryPins()
Definition: efi_gpio.cpp:821
ioportid_t criticalErrorLedPort
Definition: efi_gpio.cpp:808
bool qcDirectPinControlMode
static const char *const sparkShortNames[]
Definition: efi_gpio.cpp:47
uint8_t criticalErrorLedState
Definition: efi_gpio.cpp:810
static const char *const injectorStage2Names[]
Definition: efi_gpio.cpp:56
EnginePins enginePins
Definition: efi_gpio.cpp:24
static const char *const sparkNames[]
Definition: efi_gpio.cpp:26
static const char *const injectorShortNames[]
Definition: efi_gpio.cpp:53
static const char *const injectorStage2ShortNames[]
Definition: efi_gpio.cpp:59
const char * vvtNames[]
Definition: efi_gpio.cpp:34
void turnAllPinsOff(void)
Definition: efi_gpio.cpp:833
static void initErrorLed(Gpio led)
Definition: efi_gpio.cpp:813
void initMiscOutputPins()
Definition: efi_gpio.cpp:669
static const char *const trailShortNames[]
Definition: efi_gpio.cpp:32
ioportid_t getHwPort(const char *msg, brain_pin_e brainPin)
ioportmask_t getHwPin(const char *msg, brain_pin_e brainPin)
void unregisterEtbPins()
engine_configuration_s & activeConfiguration
Engine * engine
void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection)
rusEfi console wave sniffer
bench_mode_e
Definition: engine_types.h:492
@ BENCH_AC_COMPRESSOR_RELAY
Definition: engine_types.h:499
@ BENCH_FAN_RELAY
Definition: engine_types.h:497
@ BENCH_VVT0_VALVE
Definition: engine_types.h:509
@ BENCH_MAIN_RELAY
Definition: engine_types.h:493
@ HD_ACR2
Definition: engine_types.h:522
@ BENCH_IDLE_VALVE
Definition: engine_types.h:501
@ HD_ACR
Definition: engine_types.h:521
@ BENCH_CHECK_ENGINE_LIGHT
Definition: engine_types.h:500
@ BENCH_VVT3_VALVE
Definition: engine_types.h:512
@ BENCH_VVT1_VALVE
Definition: engine_types.h:510
@ BENCH_FAN_RELAY_2
Definition: engine_types.h:498
@ BENCH_HPFP_VALVE
Definition: engine_types.h:502
@ BENCH_FUEL_PUMP
Definition: engine_types.h:494
@ BENCH_STARTER_ENABLE_RELAY
Definition: engine_types.h:495
@ BENCH_VVT2_VALVE
Definition: engine_types.h:511
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
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
uint32_t iomode_t
Digital I/O modes.
Definition: hal_pal_lld.h:83
void efiSetPadUnused(brain_pin_e brainPin)
Definition: io_pins.cpp:20
void setMockState(brain_pin_e pin, bool state)
Definition: io_pins.cpp:129
UNUSED(samplingTimeSeconds)
@ CUSTOM_INVALID_MODE_SETTING
@ CUSTOM_OBD_PIN_CONFLICT
@ CUSTOM_ERR_6622
@ CUSTOM_ERR_6586
engine_configuration_s * engineConfiguration
const char * hwPortname(brain_pin_e brainPin)
bool brain_pin_is_onchip(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)
brain_pin_diag_e
Definition: rusefi_enums.h:43
pin_output_mode_e
Definition: rusefi_enums.h:236
starterRelayDisable("starterRelayDisable", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 389, 1.0, -10000.0, 10000.0, "")
brain_pin_e pin
Definition: stm32_adc.cpp:15
composite packet size
OutputPin * getVvtOutputPin(int index)
Definition: vvt.cpp:145