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