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  outOfOrder = false;
534  signalFallSparkId = 0;
535 }
536 
538 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
539 #if (BOARD_EXT_GPIOCHIPS > 0)
540  if (ext)
541  return true;
542 #endif /* (BOARD_EXT_GPIOCHIPS > 0) */
543  return m_port != NULL;
544 #else /* EFI_GPIO_HARDWARE */
545  return true;
546 #endif /* EFI_GPIO_HARDWARE */
547 }
548 
550  setValue("toggle", !getLogicValue());
551 }
552 
553 bool OutputPin::getAndSet(int logicValue) {
554  bool oldValue = getLogicValue();
555  setValue(logicValue);
556  return oldValue;
557 }
558 
559 // This function is only used on real hardware
560 #if EFI_PROD_CODE
561 void OutputPin::setOnchipValue(int electricalValue) {
563  // todo: make 'setOnchipValue' or 'reportsetOnchipValueError' virtual and override for NamedOutputPin?
564  warning(ObdCode::CUSTOM_ERR_6586, "attempting to change unassigned pin");
565  return;
566  }
567  palWritePad(m_port, m_pin, electricalValue);
568 }
569 #endif // EFI_PROD_CODE
570 
571 void OutputPin::setValue(int logicValue, bool isForce) {
572  setValue(nullptr, logicValue, isForce);
573 }
574 
575 #if EFI_SIMULATOR
578  pinToggleCounter = 0;
579 }
580 #endif // EFI_SIMULATOR
581 
582 extern bool qcDirectPinControlMode;
583 
584 void OutputPin::setValue(const char *msg, int logicValue, bool isForce) {
585  UNUSED(msg);
586  if ((qcDirectPinControlMode || getOutputOnTheBenchTest() == this) && !isForce) {
587  return;
588  }
589 
590 #if ENABLE_PERF_TRACE
591 // todo: https://github.com/rusefi/rusefi/issues/1638
592 // ScopePerf perf(PE::OutputPinSetValue);
593 #endif // ENABLE_PERF_TRACE
594 
595 #if EFI_UNIT_TEST
596  if (currentLogicValue != logicValue) {
598  }
599 #endif // EFI_UNIT_TEST
600 
601 #if EFI_SIMULATOR
602  if (currentLogicValue != logicValue) {
603  if (pinToggleCounter > 0) {
605  durationsInStateMs[1] = pinToggleTimer.getElapsedUs() / 1000;
606  }
608  pinToggleTimer.reset();
609  }
610 #endif // EFI_SIMULATOR
611 
612 #if EFI_UNIT_TEST
613  if (verboseMode) {
614  efiPrintf("pin goes %d", logicValue);
615  }
616 #endif // EFI_UNIT_TEST
617 
618  // Always store the current logical value of the pin (so it can be
619  // used internally even if not connected to a real hardware pin)
620  currentLogicValue = logicValue;
621 
622  // Nothing else to do if not configured
623  if (!isBrainPinValid(brainPin)) {
624  return;
625  }
626 
627  efiAssertVoid(ObdCode::CUSTOM_ERR_6622, mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
628  int electricalValue = getElectricalValue(logicValue, mode);
629 
630 #if EFI_PROD_CODE
631  #if (BOARD_EXT_GPIOCHIPS > 0)
632  if (!this->ext) {
633  setOnchipValue(electricalValue);
634  } else {
635  /* external pin */
636  gpiochips_writePad(this->brainPin, logicValue);
637  /* TODO: check return value */
638  }
639  #else
640  setOnchipValue(electricalValue);
641  #endif
642 #else /* EFI_PROD_CODE */
643  setMockState(brainPin, electricalValue);
644 #endif /* EFI_PROD_CODE */
645 }
646 
648  // Compare against 1 since it could also be INITIAL_PIN_STATE (which means logical 0, but we haven't initialized the pin yet)
649  return currentLogicValue == 1;
650 }
651 
653  assertOMode(mode);
654  this->mode = outputMode;
655  setValue(false, /*force*/true); // initial state
656 }
657 
659 #if EFI_PROD_CODE
660 #if BOARD_EXT_GPIOCHIPS > 0
662  return gpiochips_getDiag(brainPin);
663  }
664 #endif
665 #endif /* EFI_PROD_CODE */
666  // TODO: add hook to board code for custom diagnostic, like it is done on S105
667  return PIN_UNKNOWN;
668 }
669 
671 #if EFI_GPIO_HARDWARE
672 
673 #if HAL_USE_SPI
675 #endif /* HAL_USE_SPI */
676 
677 #if EFI_SHAFT_POSITION_INPUT
678  // todo: migrate remaining OutputPin to RegisteredOutputPin in order to get consistent dynamic pin init/deinit
680 #endif // EFI_SHAFT_POSITION_INPUT
681 
683 
684 #endif /* EFI_GPIO_HARDWARE */
685 }
686 
687 void OutputPin::initPin(const char *p_msg, brain_pin_e p_brainPin) {
688  initPin(p_msg, p_brainPin, OM_DEFAULT);
689 }
690 
691 void OutputPin::initPin(const char *msg, brain_pin_e p_brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError) {
692 #if EFI_UNIT_TEST
693  pinToggleCounter = 0;
694 #endif
695 
696  if (!isBrainPinValid(p_brainPin)) {
697  return;
698  }
699 
700  // Enter a critical section so that other threads can't change the pin state out from underneath us
701  chibios_rt::CriticalSectionLocker csl;
702 
703  if (!forceInitWithFatalError && hasFirmwareError()) {
704  // Don't allow initializing more pins if we have a fatal error.
705  // Pins should have just been reset, so we shouldn't try to init more.
706  return;
707  }
708 
709  // Check that this OutputPin isn't already assigned to another pin (reinit is allowed to change mode)
710  // To avoid this error, call deInit() first
711  if (isBrainPinValid(brainPin) && brainPin != p_brainPin) {
712  firmwareError(ObdCode::CUSTOM_OBD_PIN_CONFLICT, "outputPin [%s] already assigned, cannot reassign without unregister first", msg);
713  return;
714  }
715 
716  if (outputMode > OM_OPENDRAIN_INVERTED) {
717  firmwareError(ObdCode::CUSTOM_INVALID_MODE_SETTING, "%s invalid pin_output_mode_e %d %s",
718  msg,
719  outputMode,
720  hwPortname(p_brainPin)
721  );
722  return;
723  }
724 
725 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
726  iomode_t l_mode = (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) ?
727  PAL_MODE_OUTPUT_PUSHPULL : PAL_MODE_OUTPUT_OPENDRAIN;
728 
729  #if (BOARD_EXT_GPIOCHIPS > 0)
730  this->ext = false;
731  #endif
732  if (brain_pin_is_onchip(p_brainPin)) {
733  m_port = getHwPort(msg, p_brainPin);
734  m_pin = getHwPin(msg, p_brainPin);
735 
736  // Validate port
737  if (m_port == GPIO_NULL) {
738  criticalError("OutputPin::initPin got invalid port for pin idx %d", static_cast<int>(p_brainPin));
739  return;
740  }
741  }
742  #if (BOARD_EXT_GPIOCHIPS > 0)
743  else {
744  this->ext = true;
745  }
746  #endif
747 #endif // briefly leave the include guard because we need to set default state in tests
748 
749  brainPin = p_brainPin;
750 
751  // The order of the next two calls may look strange, which is a good observation.
752  // We call them in this order so that the pin is set to a known state BEFORE
753  // it's enabled. Enabling the pin then setting it could result in a (brief)
754  // mystery state being driven on the pin (potentially dangerous).
755  setDefaultPinState(outputMode);
756 
757 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
758  efiSetPadMode(msg, brainPin, l_mode);
760  // todo: handle OM_OPENDRAIN and OM_OPENDRAIN_INVERTED as well
761  if (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) {
762 #ifndef DISABLE_PIN_STATE_VALIDATION
763  int actualValue = palReadPad(m_port, m_pin);
764  // we had enough drama with pin configuration in board.h and else that we shall self-check
765 
766  const int logicalValue =
767  (outputMode == OM_INVERTED)
768  ? !actualValue
769  : actualValue;
770 
771  // if the pin was set to logical 1, then set an error and disable the pin so that things don't catch fire
772  if (logicalValue) {
773  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));
775  }
776 #endif
777  }
778  }
779 #endif /* EFI_GPIO_HARDWARE */
780 }
781 
783  // Unregister under lock - we don't want other threads mucking with the pin while we're trying to turn it off
784  chibios_rt::CriticalSectionLocker csl;
785 
786  // nothing to do if not registered in the first place
787  if (!isBrainPinValid(brainPin)) {
788  return;
789  }
790 
791 #if (BOARD_EXT_GPIOCHIPS > 0)
792  ext = false;
793 #endif // (BOARD_EXT_GPIOCHIPS > 0)
794 
795  efiPrintf("unregistering %s", hwPortname(brainPin));
796 
797 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
799 #endif /* EFI_GPIO_HARDWARE */
800 
801  // Clear the pin so that it won't get set any more
803 }
804 
805 #if EFI_GPIO_HARDWARE
806 
807 // questionable trick: we avoid using 'getHwPort' and 'getHwPin' in case of errors in order to increase the changes of turning the LED
808 // by reducing stack requirement
812 
813 #if EFI_PROD_CODE
814 static void initErrorLed(Gpio led) {
815  enginePins.errorLedPin.initPin("led: CRITICAL status", led, (LED_PIN_MODE));
816  criticalErrorLedPort = getHwPort("CRITICAL", led);
817  criticalErrorLedPin = getHwPin("CRITICAL", led);
818  criticalErrorLedState = (LED_PIN_MODE == OM_INVERTED) ? 0 : 1;
819 }
820 #endif /* EFI_PROD_CODE */
821 
823 #if EFI_PROD_CODE
824  initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
825 
826  addConsoleAction("gpio_pins", EnginePins::debug);
827 #endif /* EFI_PROD_CODE */
828 }
829 
830 /**
831  * This method is part of fatal error handling.
832  * The whole method is pretty naive, but that's at least something.
833  */
834 void turnAllPinsOff(void) {
835  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
836  enginePins.injectors[i].setValue(false);
837  enginePins.coils[i].setValue(false);
839  }
842  enginePins.checkEnginePin.setValue(true); // yes this one can go ON
843 }
844 #endif /* EFI_GPIO_HARDWARE */
845 
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:96
RegisteredOutputPin harleyAcr2
Definition: efi_gpio.h:92
RegisteredNamedOutputPin harleyAcr
Definition: efi_gpio.h:91
OutputPin accelerometerCs
Definition: efi_gpio.h:121
static void debug()
Definition: efi_gpio.cpp:249
void startPins()
Definition: efi_gpio.cpp:257
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:124
RegisteredOutputPin mainRelay
Definition: efi_gpio.h:74
NamedOutputPin auxValve[AUX_DIGITAL_VALVE_COUNT]
Definition: efi_gpio.h:127
void startAuxValves()
Definition: efi_gpio.cpp:303
void startIgnitionPins()
Definition: efi_gpio.cpp:315
OutputPin o2heater
Definition: efi_gpio.h:95
RegisteredOutputPin fanRelay
Definition: efi_gpio.h:84
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:126
OutputPin errorLedPin
Definition: efi_gpio.h:101
RegisteredOutputPin starterControl
Definition: efi_gpio.h:80
OutputPin debugTriggerSync
Definition: efi_gpio.h:106
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:123
void stopAuxValves()
Definition: efi_gpio.cpp:293
bool stopPins()
Definition: efi_gpio.cpp:215
RegisteredOutputPin fuelPumpRelay
Definition: efi_gpio.h:89
void unregisterPins()
Definition: efi_gpio.cpp:229
RegisteredOutputPin acRelay
Definition: efi_gpio.h:88
OutputPin * getOutputPinForBenchMode(bench_mode_e idx)
Definition: efi_gpio.cpp:350
IgnitionOutputPin coils[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:125
OutputPin sdCsPin
Definition: efi_gpio.h:120
RegisteredOutputPin checkEnginePin
Definition: efi_gpio.h:114
RegisteredOutputPin idleSolenoidPin
Definition: efi_gpio.h:108
RegisteredNamedOutputPin hpfpValve
Definition: efi_gpio.h:78
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:32
int8_t coilIndex
Definition: efi_gpio.h:34
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:658
uint32_t durationsInStateMs[2]
Definition: efi_output.h:82
bool ext
Definition: efi_output.h:91
void resetToggleStats()
Definition: efi_gpio.cpp:576
void deInit()
Definition: efi_gpio.cpp:782
void initPin(const char *msg, brain_pin_e brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError=false)
Definition: efi_gpio.cpp:691
bool getLogicValue() const
Definition: efi_gpio.cpp:647
pin_output_mode_e mode
Definition: efi_output.h:105
void setOnchipValue(int electricalValue)
Definition: efi_gpio.cpp:561
bool getAndSet(int logicValue)
Definition: efi_gpio.cpp:553
void setValue(const char *msg, int logicValue, bool isForce=false)
Definition: efi_gpio.cpp:584
void toggle()
Definition: efi_gpio.cpp:549
int8_t currentLogicValue
Definition: efi_output.h:94
ioportid_t m_port
Definition: efi_output.h:72
bool isInitialized() const
Definition: efi_gpio.cpp:537
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:652
RegisteredNamedOutputPin(const char *name, size_t pinOffset, size_t pinModeOffset)
Definition: efi_gpio.cpp:66
const char *const registrationName
Definition: efi_gpio.h:51
RegisteredOutputPin(const char *registrationName, size_t pinOffset, size_t pinModeOffset)
Definition: efi_gpio.cpp:74
const uint16_t m_pinModeOffset
Definition: efi_gpio.h:54
RegisteredOutputPin *const next
Definition: efi_gpio.h:46
const bool m_hasPinMode
Definition: efi_gpio.h:53
bool isPinConfigurationChanged()
Definition: efi_gpio.cpp:97
const char * getRegistrationName() const
Definition: efi_gpio.h:47
const uint16_t m_pinOffset
Definition: efi_gpio.h:52
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:275
brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin)
Get diagnostic for given gpio.
Definition: core.cpp:313
ioportmask_t criticalErrorLedPin
Definition: efi_gpio.cpp:810
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:822
ioportid_t criticalErrorLedPort
Definition: efi_gpio.cpp:809
bool qcDirectPinControlMode
static const char *const sparkShortNames[]
Definition: efi_gpio.cpp:47
uint8_t criticalErrorLedState
Definition: efi_gpio.cpp:811
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:834
static void initErrorLed(Gpio led)
Definition: efi_gpio.cpp:814
void initMiscOutputPins()
Definition: efi_gpio.cpp:670
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:490
@ BENCH_AC_COMPRESSOR_RELAY
Definition: engine_types.h:497
@ BENCH_FAN_RELAY
Definition: engine_types.h:495
@ BENCH_VVT0_VALVE
Definition: engine_types.h:507
@ BENCH_MAIN_RELAY
Definition: engine_types.h:491
@ HD_ACR2
Definition: engine_types.h:520
@ BENCH_IDLE_VALVE
Definition: engine_types.h:499
@ HD_ACR
Definition: engine_types.h:519
@ BENCH_CHECK_ENGINE_LIGHT
Definition: engine_types.h:498
@ BENCH_VVT3_VALVE
Definition: engine_types.h:510
@ BENCH_VVT1_VALVE
Definition: engine_types.h:508
@ BENCH_FAN_RELAY_2
Definition: engine_types.h:496
@ BENCH_HPFP_VALVE
Definition: engine_types.h:500
@ BENCH_FUEL_PUMP
Definition: engine_types.h:492
@ BENCH_STARTER_ENABLE_RELAY
Definition: engine_types.h:493
@ BENCH_VVT2_VALVE
Definition: engine_types.h:509
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)
static const char * msg
@ 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:220
starterRelayDisable("starterRelayDisable", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 373, 1.0, -10000.0, 10000.0, "")
composite packet size
OutputPin * getVvtOutputPin(int index)
Definition: vvt.cpp:145