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
557 currentLogicValue = INITIAL_PIN_STATE;
558}
559
561#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
562#if (BOARD_EXT_GPIOCHIPS > 0)
563 if (ext)
564 return true;
565#endif /* (BOARD_EXT_GPIOCHIPS > 0) */
566 return m_port != NULL;
567#else /* EFI_GPIO_HARDWARE */
568 return true;
569#endif /* EFI_GPIO_HARDWARE */
570}
571
573 setValue("toggle", !getLogicValue());
574}
575
576bool OutputPin::getAndSet(int logicValue) {
577 bool oldValue = getLogicValue();
578 setValue(logicValue);
579 return oldValue;
580}
581
582// This function is only used on real hardware
583#if EFI_PROD_CODE
584void OutputPin::setOnchipValue(int electricalValue) {
586 // todo: make 'setOnchipValue' or 'reportsetOnchipValueError' virtual and override for NamedOutputPin?
587 warning(ObdCode::CUSTOM_ERR_6586, "attempting to change unassigned pin");
588 return;
589 }
590 palWritePad(m_port, m_pin, electricalValue);
591}
592#endif // EFI_PROD_CODE
593
594void OutputPin::setValue(int logicValue, bool isForce) {
595 setValue(nullptr, logicValue, isForce);
596}
597
598#if EFI_SIMULATOR
603#endif // EFI_SIMULATOR
604
605void OutputPin::setValue(const char *msg, int logicValue, bool isForce) {
606 UNUSED(msg);
607 if ((isHwQcMode() || getOutputOnTheBenchTest() == this) && !isForce) {
608 return;
609 }
610
611#if ENABLE_PERF_TRACE
612// todo: https://github.com/rusefi/rusefi/issues/1638
613// ScopePerf perf(PE::OutputPinSetValue);
614#endif // ENABLE_PERF_TRACE
615
616#if EFI_UNIT_TEST
617 if (currentLogicValue != logicValue) {
619 }
620#endif // EFI_UNIT_TEST
621
622#if EFI_SIMULATOR
623 if (currentLogicValue != logicValue) {
624 if (pinToggleCounter > 0) {
626 durationsInStateMs[1] = pinToggleTimer.getElapsedUs() / 1000;
627 }
629 pinToggleTimer.reset();
630 }
631#endif // EFI_SIMULATOR
632
633#if EFI_UNIT_TEST
634 if (verboseMode) {
635 efiPrintf("pin goes %d", logicValue);
636 }
637#endif // EFI_UNIT_TEST
638
639 // Always store the current logical value of the pin (so it can be
640 // used internally even if not connected to a real hardware pin)
641 currentLogicValue = logicValue;
642
643 // Nothing else to do if not configured
645 return;
646 }
647
648 efiAssertVoid(ObdCode::CUSTOM_ERR_6622, mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
649 int electricalValue = getElectricalValue(logicValue, mode);
650
651#if EFI_PROD_CODE
652 #if (BOARD_EXT_GPIOCHIPS > 0)
653 if (!this->ext) {
654 setOnchipValue(electricalValue);
655 } else {
656 /* external pin */
657 gpiochips_writePad(this->brainPin, logicValue);
658 /* TODO: check return value */
659 }
660 #else
661 setOnchipValue(electricalValue);
662 #endif
663#else /* EFI_PROD_CODE */
664 setMockState(brainPin, electricalValue);
665#endif /* EFI_PROD_CODE */
666}
667
669 // Compare against 1 since it could also be INITIAL_PIN_STATE (which means logical 0, but we haven't initialized the pin yet)
670 return currentLogicValue == 1;
671}
672
674 assertOMode(mode);
675 this->mode = outputMode;
676 setValue(false, /*force*/true); // initial state
677}
678
680#if EFI_PROD_CODE
681#if BOARD_EXT_GPIOCHIPS > 0
684 }
685#endif
686#endif /* EFI_PROD_CODE */
687 // TODO: add hook to board code for custom diagnostic, like it is done on S105
688 return PIN_UNKNOWN;
689}
690
692#if EFI_GPIO_HARDWARE
693
694#if HAL_USE_SPI
696#endif /* HAL_USE_SPI */
697
698#if EFI_SHAFT_POSITION_INPUT
699 // todo: migrate remaining OutputPin to RegisteredOutputPin in order to get consistent dynamic pin init/deinit
701#endif // EFI_SHAFT_POSITION_INPUT
702
704
705#endif /* EFI_GPIO_HARDWARE */
706}
707
708void OutputPin::initPin(const char *p_msg, brain_pin_e p_brainPin) {
709 initPin(p_msg, p_brainPin, OM_DEFAULT);
710}
711
712void OutputPin::initPin(const char *msg, brain_pin_e p_brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError) {
713#if EFI_UNIT_TEST
715#endif
716
717 if (!isBrainPinValid(p_brainPin)) {
718 return;
719 }
720
721 // Enter a critical section so that other threads can't change the pin state out from underneath us
722 chibios_rt::CriticalSectionLocker csl;
723
724 if (!forceInitWithFatalError && hasFirmwareError()) {
725 // Don't allow initializing more pins if we have a fatal error.
726 // Pins should have just been reset, so we shouldn't try to init more.
727 return;
728 }
729
730 // Check that this OutputPin isn't already assigned to another pin (reinit is allowed to change mode)
731 // To avoid this error, call deInit() first
732 if (isBrainPinValid(brainPin) && brainPin != p_brainPin) {
733 firmwareError(ObdCode::CUSTOM_OBD_PIN_CONFLICT, "outputPin [%s] already assigned, cannot reassign without unregister first", msg);
734 return;
735 }
736
737 if (outputMode > OM_OPENDRAIN_INVERTED) {
738 firmwareError(ObdCode::CUSTOM_INVALID_MODE_SETTING, "%s invalid pin_output_mode_e %d %s",
739 msg,
740 outputMode,
741 hwPortname(p_brainPin)
742 );
743 return;
744 }
745
746#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
747 iomode_t l_mode = (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) ?
748 PAL_MODE_OUTPUT_PUSHPULL : PAL_MODE_OUTPUT_OPENDRAIN;
749
750 #if (BOARD_EXT_GPIOCHIPS > 0)
751 this->ext = false;
752 #endif
753 if (brain_pin_is_onchip(p_brainPin)) {
754 m_port = getHwPort(msg, p_brainPin);
755 m_pin = getHwPin(msg, p_brainPin);
756
757 // Validate port
758 if (!m_port) {
759 criticalError("OutputPin::initPin got invalid port for pin idx %d", static_cast<int>(p_brainPin));
760 return;
761 }
762 }
763 #if (BOARD_EXT_GPIOCHIPS > 0)
764 else {
765 this->ext = true;
766 }
767 #endif
768#endif // briefly leave the include guard because we need to set default state in tests
769
770 brainPin = p_brainPin;
771
772 // The order of the next two calls may look strange, which is a good observation.
773 // We call them in this order so that the pin is set to a known state BEFORE
774 // it's enabled. Enabling the pin then setting it could result in a (brief)
775 // mystery state being driven on the pin (potentially dangerous).
776 setDefaultPinState(outputMode);
777
778#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
779 efiSetPadMode(msg, brainPin, l_mode);
781 // todo: handle OM_OPENDRAIN and OM_OPENDRAIN_INVERTED as well
782 if (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) {
783#ifndef DISABLE_PIN_STATE_VALIDATION
784 int actualValue = palReadPad(m_port, m_pin);
785 // we had enough drama with pin configuration in board.h and else that we shall self-check
786
787 const int logicalValue =
788 (outputMode == OM_INVERTED)
789 ? !actualValue
790 : actualValue;
791
792 // if the pin was set to logical 1, then set an error and disable the pin so that things don't catch fire
793 if (logicalValue) {
794 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));
796 }
797#endif
798 }
799 }
800#endif /* EFI_GPIO_HARDWARE */
801}
802
804 efiPrintf("unregistering %s", hwPortname(brainPin));
805
806 // Unregister under lock - we don't want other threads mucking with the pin while we're trying to turn it off
807 chibios_rt::CriticalSectionLocker csl;
808
809 // nothing to do if not registered in the first place
811 return;
812 }
813
814#if (BOARD_EXT_GPIOCHIPS > 0)
815 ext = false;
816#endif // (BOARD_EXT_GPIOCHIPS > 0)
817
818#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
820#endif /* EFI_GPIO_HARDWARE */
821
822 // Clear the pin so that it won't get set any more
824}
825
826#if EFI_GPIO_HARDWARE
827
828// questionable trick: we avoid using 'getHwPort' and 'getHwPin' in case of errors in order to increase the changes of turning the LED
829// by reducing stack requirement
833
834#if EFI_PROD_CODE
835static void initErrorLed(Gpio led) {
836 enginePins.errorLedPin.initPin("led: CRITICAL status", led, (LED_PIN_MODE));
837 criticalErrorLedPort = getHwPort("CRITICAL", led);
838 criticalErrorLedPin = getHwPin("CRITICAL", led);
839 criticalErrorLedState = (LED_PIN_MODE == OM_INVERTED) ? 0 : 1;
840}
841#endif /* EFI_PROD_CODE */
842
844#if EFI_PROD_CODE
845 initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
846
848#endif /* EFI_PROD_CODE */
849}
850
851/**
852 * This method is part of fatal error handling.
853 * The whole method is pretty naive, but that's at least something.
854 */
856 for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
857 enginePins.injectors[i].setValue(false);
858 enginePins.coils[i].setValue(false);
860 }
863 enginePins.checkEnginePin.setValue(true); // yes this one can go ON
864#if EFI_PROD_CODE && HW_HELLEN
866#endif
867}
868#endif /* EFI_GPIO_HARDWARE */
869
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:113
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:679
uint32_t durationsInStateMs[2]
Definition efi_output.h:81
void resetToggleStats()
Definition efi_gpio.cpp:599
void deInit()
Definition efi_gpio.cpp:803
void initPin(const char *msg, brain_pin_e brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError=false)
Definition efi_gpio.cpp:712
bool getLogicValue() const
Definition efi_gpio.cpp:668
pin_output_mode_e mode
Definition efi_output.h:104
void setOnchipValue(int electricalValue)
Definition efi_gpio.cpp:584
bool getAndSet(int logicValue)
Definition efi_gpio.cpp:576
void setValue(const char *msg, int logicValue, bool isForce=false)
Definition efi_gpio.cpp:605
void toggle()
Definition efi_gpio.cpp:572
int8_t currentLogicValue
Definition efi_output.h:93
ioportid_t m_port
Definition efi_output.h:71
bool isInitialized() const
Definition efi_gpio.cpp:560
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:673
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:831
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:843
ioportid_t criticalErrorLedPort
Definition efi_gpio.cpp:830
static const char *const sparkShortNames[]
Definition efi_gpio.cpp:47
uint8_t criticalErrorLedState
Definition efi_gpio.cpp:832
void turnAllPinsOff()
Definition efi_gpio.cpp:855
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:835
void initMiscOutputPins()
Definition efi_gpio.cpp:691
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:415
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:153