fome-fw/firmware/controllers/system/efi_gpio.cpp

690 lines
22 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
2019-03-29 06:11:13 -07:00
* @file efi_gpio.cpp
2017-04-21 10:36:51 -07:00
* @brief EFI-related GPIO code
2015-07-10 06:01:56 -07:00
*
* @date Sep 26, 2014
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*/
#include "pch.h"
2022-10-29 18:55:05 -07:00
#include "engine_sniffer.h"
2022-09-07 12:56:45 -07:00
#include "drivers/gpio/gpio_ext.h"
2015-07-10 06:01:56 -07:00
#if HW_HELLEN
#include "hellen_meta.h"
#endif // HW_HELLEN
#if EFI_ELECTRONIC_THROTTLE_BODY
#include "electronic_throttle.h"
#endif /* EFI_ELECTRONIC_THROTTLE_BODY */
2017-04-21 13:36:50 -07:00
// todo: clean this mess, this should become 'static'/private
CCM_OPTIONAL EnginePins enginePins;
2017-04-21 13:36:50 -07:00
static const char* const sparkNames[] = { "Coil 1", "Coil 2", "Coil 3", "Coil 4", "Coil 5", "Coil 6", "Coil 7", "Coil 8",
"Coil 9", "Coil 10", "Coil 11", "Coil 12"};
static const char* const trailNames[] = { "Trail 1", "Trail 2", "Trail 3", "Trail 4", "Trail 5", "Trail 6", "Trail 7", "Trail 8",
2021-07-09 07:14:00 -07:00
"Trail 9", "Trail 10", "Trail 11", "Trail 12"};
2021-07-21 17:51:36 -07:00
static const char* const trailShortNames[] = { "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "rA", "rB", "rD" };
const char *vvtNames[] = {
PROTOCOL_VVT1_NAME,
PROTOCOL_VVT2_NAME,
PROTOCOL_VVT3_NAME,
PROTOCOL_VVT4_NAME};
const char *laNames[] = {
PROTOCOL_WA_CHANNEL_1,
PROTOCOL_WA_CHANNEL_2,
PROTOCOL_WA_CHANNEL_3,
PROTOCOL_WA_CHANNEL_4};
// these short names are part of engine sniffer protocol
static const char* const sparkShortNames[] = { PROTOCOL_COIL1_SHORT_NAME, "c2", "c3", "c4", "c5", "c6", "c7", "c8",
2016-09-27 08:01:57 -07:00
"c9", "cA", "cB", "cD"};
static const char* const injectorNames[] = { "Injector 1", "Injector 2", "Injector 3", "Injector 4", "Injector 5", "Injector 6",
"Injector 7", "Injector 8", "Injector 9", "Injector 10", "Injector 11", "Injector 12"};
static const char* const injectorShortNames[] = { PROTOCOL_INJ1_SHORT_NAME, "i2", "i3", "i4", "i5", "i6", "i7", "i8",
2024-01-06 23:10:56 -08:00
"i9", "iA", "iB", "iC"};
2016-09-27 08:01:57 -07:00
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",
"Injector Second Stage 7", "Injector Second Stage 8", "Injector Second Stage 9", "Injector Second Stage 10", "Injector Second Stage 11", "Injector Second Stage 12"};
static const char* const injectorStage2ShortNames[] = { PROTOCOL_INJ1_STAGE2_SHORT_NAME, "j2", "j3", "j4", "j5", "j6", "j7", "j8",
"j9", "jA", "jB", "jC"};
static const char* const auxValveShortNames[] = { "a1", "a2"};
2017-11-26 19:30:37 -08:00
static RegisteredOutputPin * registeredOutputHead = nullptr;
2022-10-23 05:25:47 -07:00
RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *name, size_t pinOffset,
size_t pinModeOffset) : RegisteredOutputPin(name, pinOffset, pinModeOffset) {
}
2022-10-23 05:25:47 -07:00
RegisteredOutputPin::RegisteredOutputPin(const char *registrationName, size_t pinOffset,
size_t pinModeOffset)
: next(registeredOutputHead)
2023-11-01 14:54:57 -07:00
, m_registrationName(registrationName)
2022-10-23 05:25:47 -07:00
, m_pinOffset(static_cast<uint16_t>(pinOffset))
, m_hasPinMode(true)
2022-10-23 05:25:47 -07:00
, m_pinModeOffset(static_cast<uint16_t>(pinModeOffset))
{
// adding into head of the list is so easy and since we do not care about order that's what we shall do
registeredOutputHead = this;
}
RegisteredOutputPin::RegisteredOutputPin(const char *registrationName, size_t pinOffset)
: next(registeredOutputHead)
2023-11-01 14:54:57 -07:00
, m_registrationName(registrationName)
, m_pinOffset(static_cast<uint16_t>(pinOffset))
, m_hasPinMode(false)
, m_pinModeOffset(-1)
{
// adding into head of the list is so easy and since we do not care about order that's what we shall do
registeredOutputHead = this;
}
bool RegisteredOutputPin::isPinConfigurationChanged() {
#if EFI_PROD_CODE
2022-10-23 05:25:47 -07:00
brain_pin_e curPin = *(brain_pin_e *) ((void *) (&((char*)&activeConfiguration)[m_pinOffset]));
brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
bool pinChanged = curPin != newPin;
if (!m_hasPinMode) {
return pinChanged;
}
pin_output_mode_e curMode = *(pin_output_mode_e *) ((void *) (&((char*)&activeConfiguration)[m_pinModeOffset]));
pin_output_mode_e newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
return pinChanged || curMode != newMode;
#else
return true;
#endif // EFI_PROD_CODE
}
void RegisteredOutputPin::init() {
2022-10-23 05:25:47 -07:00
brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
2023-02-25 00:31:12 -08:00
pin_output_mode_e newMode;
if (m_hasPinMode) {
2023-02-25 00:31:12 -08:00
newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
} else {
2023-02-25 00:31:12 -08:00
newMode = OM_DEFAULT;
}
if (isPinConfigurationChanged()) {
2023-11-01 14:54:57 -07:00
initPin(m_registrationName, newPin, newMode);
}
}
void RegisteredOutputPin::unregister() {
if (isPinConfigurationChanged()) {
OutputPin::deInit();
}
}
#define CONFIG_OFFSET(x) (offsetof(engine_configuration_s, x))
// todo: pin and pinMode should be combined into a composite entity
// todo: one of the impediments is code generator hints handling (we need custom hints and those are not handled nice for fields of structs?)
#define CONFIG_PIN_OFFSETS(x) CONFIG_OFFSET(x##Pin), CONFIG_OFFSET(x##PinMode)
// offset of X within engineConfiguration, plus offset of Y within X
// decltype(engine_configuration_s::x) resolves the typename of the struct X inside engineConfiguration
#define CONFIG_OFFSET2(x, y) (offsetof(engine_configuration_s, x) + offsetof(decltype(engine_configuration_s::x), y))
#define CONFIG_PIN_OFFSETS2(x, y) CONFIG_OFFSET2(x, y##Pin), CONFIG_OFFSET2(x, y##PinMode)
EnginePins::EnginePins() :
2020-12-17 14:00:00 -08:00
mainRelay("Main Relay", CONFIG_PIN_OFFSETS(mainRelay)),
2023-11-01 15:35:41 -07:00
hpfpValve("HPFP Valve", CONFIG_PIN_OFFSETS(hpfpValve)),
2020-12-17 14:00:00 -08:00
starterControl("Starter Relay", CONFIG_PIN_OFFSETS(starterControl)),
starterRelayDisable("Starter Disable Relay", CONFIG_PIN_OFFSETS(starterRelayDisable)),
fanRelay("Fan Relay", CONFIG_PIN_OFFSETS(fan)),
fanRelay2("Fan Relay 2", CONFIG_PIN_OFFSETS(fan2)),
2020-12-17 14:00:00 -08:00
acRelay("A/C Relay", CONFIG_PIN_OFFSETS(acRelay)),
fuelPumpRelay("Fuel pump Relay", CONFIG_PIN_OFFSETS(fuelPump)),
harleyAcr("Harley ACR", CONFIG_OFFSET(acrPin)),
2023-03-05 18:19:57 -08:00
harleyAcr2("Harley ACR 2", CONFIG_OFFSET(acrPin2)),
boostPin("Boost", CONFIG_PIN_OFFSETS(boostControl)),
idleSolenoidPin("Idle Valve", CONFIG_OFFSET2(idle, solenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
secondIdleSolenoidPin("Idle Valve#2", CONFIG_OFFSET(secondSolenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
alternatorPin("Alternator control", CONFIG_PIN_OFFSETS(alternatorControl)),
checkEnginePin("checkEnginePin", CONFIG_PIN_OFFSETS(malfunctionIndicator)),
tachOut("tachOut", CONFIG_PIN_OFFSETS(tachOutput)),
speedoOut("speedoOut", CONFIG_OFFSET(speedometerOutputPin))
{
2023-11-01 15:35:41 -07:00
hpfpValve.setName(PROTOCOL_HPFP_NAME);
static_assert(efi::size(sparkNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
2021-07-09 07:14:00 -07:00
static_assert(efi::size(trailNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
static_assert(efi::size(injectorNames) >= MAX_CYLINDER_COUNT, "Too many injection pins");
2024-07-03 00:22:48 -07:00
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
enginePins.coils[i].coilIndex = i;
2023-11-01 14:54:57 -07:00
enginePins.coils[i].setName(sparkNames[i]);
enginePins.coils[i].shortName = sparkShortNames[i];
2019-11-13 05:42:16 -08:00
2023-11-01 14:54:57 -07:00
enginePins.trailingCoils[i].setName(trailNames[i]);
enginePins.trailingCoils[i].shortName = trailShortNames[i];
2021-07-09 07:14:00 -07:00
2016-11-30 15:02:19 -08:00
enginePins.injectors[i].injectorIndex = i;
2023-11-01 14:54:57 -07:00
enginePins.injectors[i].setName(injectorNames[i]);
enginePins.injectors[i].shortName = injectorShortNames[i];
enginePins.injectorsStage2[i].injectorIndex = i;
enginePins.injectorsStage2[i].setName(injectorStage2Names[i]);
enginePins.injectorsStage2[i].shortName = injectorStage2ShortNames[i];
2016-09-27 08:01:57 -07:00
}
2019-11-13 05:42:16 -08:00
static_assert(efi::size(auxValveShortNames) >= AUX_DIGITAL_VALVE_COUNT, "Too many aux valve pins");
2024-07-03 00:22:48 -07:00
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
2023-11-01 14:54:57 -07:00
enginePins.auxValve[i].setName(auxValveShortNames[i]);
2017-11-26 19:30:37 -08:00
}
2016-09-06 21:02:11 -07:00
}
2017-04-21 13:52:02 -07:00
/**
* Sets the value of the pin. On this layer the value is assigned as is, without any conversion.
*/
#define unregisterOutputIfPinChanged(output, pin) { \
if (isConfigurationChanged(pin)) { \
(output).deInit(); \
} \
}
#define unregisterOutputIfPinOrModeChanged(output, pin, mode) { \
if (isPinOrModeChanged(pin, mode)) { \
(output).deInit(); \
} \
}
2016-11-03 20:02:58 -07:00
bool EnginePins::stopPins() {
bool result = false;
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
2016-11-03 20:02:58 -07:00
result |= coils[i].stop();
result |= injectors[i].stop();
result |= injectorsStage2[i].stop();
2021-07-09 07:14:00 -07:00
result |= trailingCoils[i].stop();
2016-11-03 20:02:58 -07:00
}
2017-11-26 19:30:37 -08:00
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
result |= auxValve[i].stop();
}
2016-11-03 20:02:58 -07:00
return result;
}
2021-07-09 14:02:25 -07:00
void EnginePins::unregisterPins() {
stopInjectionPins();
stopIgnitionPins();
stopAuxValves();
#if EFI_ELECTRONIC_THROTTLE_BODY
unregisterEtbPins();
#endif /* EFI_ELECTRONIC_THROTTLE_BODY */
2021-07-09 11:43:58 -07:00
// todo: add pinMode
unregisterOutputIfPinChanged(sdCsPin, sdCardCsPin);
unregisterOutputIfPinChanged(accelerometerCs, LIS302DLCsPin);
2017-06-04 15:29:57 -07:00
RegisteredOutputPin * pin = registeredOutputHead;
while (pin != nullptr) {
pin->unregister();
pin = pin->next;
}
2017-06-04 13:35:13 -07:00
}
void EnginePins::debug() {
RegisteredOutputPin * pin = registeredOutputHead;
while (pin != nullptr) {
2023-11-01 14:54:57 -07:00
efiPrintf("%s %d", pin->getRegistrationName(), pin->m_currentLogicValue);
pin = pin->next;
}
}
2021-07-09 14:02:25 -07:00
void EnginePins::startPins() {
#if EFI_ENGINE_CONTROL
2021-07-09 14:02:25 -07:00
startInjectionPins();
startIgnitionPins();
startAuxValves();
#endif /* EFI_ENGINE_CONTROL */
RegisteredOutputPin * pin = registeredOutputHead;
while (pin != nullptr) {
pin->init();
pin = pin->next;
}
}
2016-11-03 20:02:58 -07:00
void EnginePins::reset() {
2024-07-03 00:22:48 -07:00
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
2016-11-01 06:02:29 -07:00
injectors[i].reset();
coils[i].reset();
2021-07-09 07:14:00 -07:00
trailingCoils[i].reset();
2016-11-01 06:02:29 -07:00
}
}
2021-07-09 14:02:25 -07:00
void EnginePins::stopIgnitionPins() {
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
unregisterOutputIfPinOrModeChanged(enginePins.coils[i], ignitionPins[i], ignitionPinMode);
unregisterOutputIfPinOrModeChanged(enginePins.trailingCoils[i], trailingCoilPins[i], ignitionPinMode);
2017-06-04 15:53:43 -07:00
}
}
2021-07-09 14:02:25 -07:00
void EnginePins::stopInjectionPins() {
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
unregisterOutputIfPinOrModeChanged(enginePins.injectors[i], injectionPins[i], injectionPinMode);
unregisterOutputIfPinOrModeChanged(enginePins.injectorsStage2[i], injectionPinsStage2[i], injectionPinMode);
2017-06-04 15:53:43 -07:00
}
}
2021-07-09 14:02:25 -07:00
void EnginePins::stopAuxValves() {
2021-02-17 05:57:18 -08:00
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
NamedOutputPin *output = &enginePins.auxValve[i];
// todo: do we need auxValveMode and reuse code?
if (isConfigurationChanged(auxValves[i])) {
(output)->deInit();
}
}
}
2021-07-09 14:02:25 -07:00
void EnginePins::startAuxValves() {
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
2017-11-26 19:30:37 -08:00
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
NamedOutputPin *output = &enginePins.auxValve[i];
2021-02-17 05:57:18 -08:00
// todo: do we need auxValveMode and reuse code?
if (isConfigurationChanged(auxValves[i])) {
2023-11-01 14:54:57 -07:00
output->initPin(output->getName(), engineConfiguration->auxValves[i]);
2021-02-17 05:57:18 -08:00
}
2017-11-26 19:30:37 -08:00
}
#endif /* EFI_PROD_CODE */
}
2021-07-09 14:02:25 -07:00
void EnginePins::startIgnitionPins() {
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
2023-03-27 00:58:18 -07:00
for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
2021-07-09 07:14:00 -07:00
NamedOutputPin *trailingOutput = &enginePins.trailingCoils[i];
if (isPinOrModeChanged(trailingCoilPins[i], ignitionPinMode)) {
2023-11-01 14:54:57 -07:00
trailingOutput->initPin(trailingOutput->getName(), engineConfiguration->trailingCoilPins[i], engineConfiguration->ignitionPinMode);
2021-07-09 07:14:00 -07:00
}
2017-06-04 15:53:43 -07:00
NamedOutputPin *output = &enginePins.coils[i];
if (isPinOrModeChanged(ignitionPins[i], ignitionPinMode)) {
2023-11-01 14:54:57 -07:00
output->initPin(output->getName(), engineConfiguration->ignitionPins[i], engineConfiguration->ignitionPinMode);
2017-06-04 15:53:43 -07:00
}
}
#endif /* EFI_PROD_CODE */
}
2021-07-09 14:02:25 -07:00
void EnginePins::startInjectionPins() {
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
2017-06-04 15:53:43 -07:00
// todo: should we move this code closer to the injection logic?
2023-03-27 00:58:18 -07:00
for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
2017-06-04 15:53:43 -07:00
NamedOutputPin *output = &enginePins.injectors[i];
if (isPinOrModeChanged(injectionPins[i], injectionPinMode)) {
2023-11-01 14:54:57 -07:00
output->initPin(output->getName(), engineConfiguration->injectionPins[i],
2023-02-25 00:31:12 -08:00
engineConfiguration->injectionPinMode);
2017-06-04 15:53:43 -07:00
}
output = &enginePins.injectorsStage2[i];
if (isPinOrModeChanged(injectionPinsStage2[i], injectionPinMode)) {
output->initPin(output->getName(), engineConfiguration->injectionPinsStage2[i],
engineConfiguration->injectionPinMode);
}
2017-06-04 15:53:43 -07:00
}
#endif /* EFI_PROD_CODE */
}
2017-04-21 12:14:37 -07:00
NamedOutputPin::NamedOutputPin() : OutputPin() {
}
2023-11-01 14:54:57 -07:00
NamedOutputPin::NamedOutputPin(const char *name)
: m_name(name)
{
2017-04-21 12:14:37 -07:00
}
const char *NamedOutputPin::getName() const {
2023-11-01 14:54:57 -07:00
return m_name;
}
void NamedOutputPin::setName(const char* name) {
m_name = name;
}
const char *NamedOutputPin::getShortName() const {
2023-11-01 14:54:57 -07:00
return shortName ? shortName : m_name;
2017-04-21 12:14:37 -07:00
}
2021-10-01 20:50:32 -07:00
#if EFI_UNIT_TEST
extern bool verboseMode;
#endif // EFI_UNIT_TEST
2017-04-21 16:23:20 -07:00
void NamedOutputPin::setHigh() {
2021-10-01 20:50:32 -07:00
#if EFI_UNIT_TEST
if (verboseMode) {
2023-11-01 15:14:53 -07:00
efiPrintf("pin %s goes high", m_name);
2021-10-01 20:50:32 -07:00
}
#endif // EFI_UNIT_TEST
2019-04-12 19:07:03 -07:00
#if EFI_DEFAILED_LOGGING
2017-04-21 16:23:20 -07:00
// signal->hi_time = hTimeNow();
#endif /* EFI_DEFAILED_LOGGING */
// turn the output level ACTIVE
setValue(true);
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_SNIFFER
addEngineSnifferOutputPinEvent(this, true);
2017-04-21 16:23:20 -07:00
#endif /* EFI_ENGINE_SNIFFER */
}
void NamedOutputPin::setLow() {
2021-10-01 20:50:32 -07:00
#if EFI_UNIT_TEST
if (verboseMode) {
2023-11-01 15:14:53 -07:00
efiPrintf("pin %s goes low", m_name);
2021-10-01 20:50:32 -07:00
}
#endif // EFI_UNIT_TEST
2017-04-21 16:23:20 -07:00
// turn off the output
setValue(false);
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_SNIFFER
2023-05-31 10:24:36 -07:00
addEngineSnifferOutputPinEvent(this, false);
2017-04-21 16:23:20 -07:00
#endif /* EFI_ENGINE_SNIFFER */
}
2016-11-03 20:02:58 -07:00
bool NamedOutputPin::stop() {
2019-04-12 19:07:03 -07:00
#if EFI_GPIO_HARDWARE
2016-11-03 20:02:58 -07:00
if (isInitialized() && getLogicValue()) {
setValue(false);
2023-11-01 14:54:57 -07:00
efiPrintf("turning off %s", m_name);
2016-11-03 20:02:58 -07:00
return true;
}
2017-04-21 13:36:50 -07:00
#endif /* EFI_GPIO_HARDWARE */
2016-11-03 20:02:58 -07:00
return false;
}
2016-09-03 21:03:27 -07:00
void InjectorOutputPin::reset() {
2020-07-16 23:55:41 -07:00
// If this injector was open, close it and reset state
2023-11-01 14:54:57 -07:00
if (m_overlappingCounter != 0) {
m_overlappingCounter = 0;
2020-07-16 23:55:41 -07:00
setValue(0);
}
2016-09-04 22:03:25 -07:00
// todo: this could be refactored by calling some super-reset method
2023-11-01 14:54:57 -07:00
m_currentLogicValue = 0;
2016-09-03 21:03:27 -07:00
}
2016-10-31 19:02:12 -07:00
IgnitionOutputPin::IgnitionOutputPin() {
reset();
}
void IgnitionOutputPin::reset() {
2016-11-01 20:01:54 -07:00
outOfOrder = false;
signalFallSparkId = 0;
2016-10-31 19:02:12 -07:00
}
2023-11-01 14:54:57 -07:00
bool OutputPin::isInitialized() const {
return isBrainPinValid(m_brainPin);
2015-07-10 06:01:56 -07:00
}
2018-01-28 08:27:33 -08:00
void OutputPin::toggle() {
2018-01-28 08:08:37 -08:00
setValue(!getLogicValue());
2020-03-30 22:06:19 -07:00
}
2018-01-28 08:08:37 -08:00
2020-03-30 22:06:19 -07:00
bool OutputPin::getAndSet(int logicValue) {
bool oldValue = getLogicValue();
2020-03-30 22:06:19 -07:00
setValue(logicValue);
return oldValue;
2018-01-28 08:08:37 -08:00
}
2020-03-30 22:06:19 -07:00
2020-12-10 16:18:14 -08:00
// This function is only used on real hardware
#if EFI_PROD_CODE
2020-12-10 16:18:14 -08:00
void OutputPin::setOnchipValue(int electricalValue) {
2023-11-01 14:54:57 -07:00
if (m_brainPin == Gpio::Unassigned || m_brainPin == Gpio::Invalid) {
// todo: make 'setOnchipValue' or 'reportsetOnchipValueError' virtual and override for NamedOutputPin?
warning(ObdCode::CUSTOM_ERR_6586, "attempting to change unassigned pin");
return;
}
2023-11-01 14:54:57 -07:00
palWritePad(m_port, m_pin, electricalValue);
}
2020-12-10 16:18:14 -08:00
#endif // EFI_PROD_CODE
2015-07-10 06:01:56 -07:00
void OutputPin::setValue(int logicValue) {
2020-07-25 16:04:15 -07:00
#if ENABLE_PERF_TRACE
// todo: https://github.com/rusefi/rusefi/issues/1638
// ScopePerf perf(PE::OutputPinSetValue);
2020-07-25 16:04:15 -07:00
#endif // ENABLE_PERF_TRACE
2019-10-13 13:14:08 -07:00
2021-10-01 20:50:32 -07:00
#if EFI_UNIT_TEST
unitTestTurnedOnCounter++;
2021-10-01 20:50:32 -07:00
if (verboseMode) {
efiPrintf("pin goes %d", logicValue);
}
#endif // EFI_UNIT_TEST
// Always store the current logical value of the pin (so it can be
// used internally even if not connected to a real hardware pin)
2023-11-01 14:54:57 -07:00
m_currentLogicValue = logicValue;
// Nothing else to do if not configured
2023-11-01 14:54:57 -07:00
if (!isInitialized()) {
return;
}
2023-11-01 14:54:57 -07:00
efiAssertVoid(ObdCode::CUSTOM_ERR_6622, m_mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
int electricalValue = getElectricalValue(logicValue, m_mode);
#if EFI_PROD_CODE
#if (BOARD_EXT_GPIOCHIPS > 0)
if (!this->ext) {
2020-12-10 16:18:14 -08:00
setOnchipValue(electricalValue);
} else {
/* external pin */
2023-11-01 14:54:57 -07:00
gpiochips_writePad(m_brainPin, logicValue);
/* TODO: check return value */
}
#else
2020-12-10 16:18:14 -08:00
setOnchipValue(electricalValue);
#endif
2017-04-21 13:52:02 -07:00
#else /* EFI_PROD_CODE */
2023-11-01 15:14:53 -07:00
setMockState(m_brainPin, electricalValue);
2017-04-21 13:52:02 -07:00
#endif /* EFI_PROD_CODE */
2015-07-10 06:01:56 -07:00
}
bool OutputPin::getLogicValue() const {
2020-12-10 16:18:14 -08:00
// Compare against 1 since it could also be INITIAL_PIN_STATE (which means logical 0, but we haven't initialized the pin yet)
2023-11-01 14:54:57 -07:00
return m_currentLogicValue == 1;
2015-07-10 06:01:56 -07:00
}
2023-02-25 00:31:12 -08:00
void OutputPin::setDefaultPinState(pin_output_mode_e outputMode) {
2023-11-01 14:54:57 -07:00
assertOMode(outputMode);
m_mode = outputMode;
2015-07-10 06:01:56 -07:00
setValue(false); // initial state
}
brain_pin_diag_e OutputPin::getDiag() const {
#if BOARD_EXT_GPIOCHIPS > 0
2023-11-01 14:54:57 -07:00
return gpiochips_getDiag(m_brainPin);
#else
return PIN_OK;
#endif
}
void initOutputPins() {
2019-04-12 19:07:03 -07:00
#if EFI_GPIO_HARDWARE
2017-04-21 12:14:37 -07:00
2019-04-12 19:07:03 -07:00
#if HAL_USE_SPI
enginePins.sdCsPin.initPin("SD CS", engineConfiguration->sdCardCsPin);
2017-04-21 12:14:37 -07:00
#endif /* HAL_USE_SPI */
#if EFI_SHAFT_POSITION_INPUT
// todo: migrate remaining OutputPin to RegisteredOutputPin in order to get consistent dynamic pin init/deinit
enginePins.debugTriggerSync.initPin("debug: sync", engineConfiguration->debugTriggerSync);
#endif // EFI_SHAFT_POSITION_INPUT
enginePins.o2heater.initPin("O2 heater", engineConfiguration->o2heaterPin);
2017-04-21 12:14:37 -07:00
#endif /* EFI_GPIO_HARDWARE */
}
2017-04-21 15:11:36 -07:00
void OutputPin::initPin(const char *msg, brain_pin_e brainPin) {
2023-02-25 00:31:12 -08:00
initPin(msg, brainPin, OM_DEFAULT);
2017-04-21 14:50:28 -07:00
}
2023-02-25 00:31:12 -08:00
void OutputPin::initPin(const char *msg, brain_pin_e brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError) {
#if EFI_UNIT_TEST
unitTestTurnedOnCounter = 0;
#endif
if (!isBrainPinValid(brainPin)) {
return;
}
// Enter a critical section so that other threads can't change the pin state out from underneath us
chibios_rt::CriticalSectionLocker csl;
if (!forceInitWithFatalError && hasFirmwareError()) {
// Don't allow initializing more pins if we have a fatal error.
// Pins should have just been reset, so we shouldn't try to init more.
return;
}
// Check that this OutputPin isn't already assigned to another pin (reinit is allowed to change mode)
// To avoid this error, call deInit() first
2023-11-01 14:54:57 -07:00
if (isBrainPinValid(m_brainPin) && m_brainPin != brainPin) {
firmwareError(ObdCode::CUSTOM_OBD_PIN_CONFLICT, "outputPin [%s] already assigned, cannot reassign without unregister first", msg);
2017-04-21 14:14:14 -07:00
return;
}
2017-04-21 12:14:37 -07:00
2023-02-25 00:31:12 -08:00
if (outputMode > OM_OPENDRAIN_INVERTED) {
firmwareError(ObdCode::CUSTOM_INVALID_MODE_SETTING, "%s invalid pin_output_mode_e %d %s",
msg,
2023-02-25 00:31:12 -08:00
outputMode,
hwPortname(brainPin)
);
2020-03-05 18:16:45 -08:00
return;
}
#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
#if (BOARD_EXT_GPIOCHIPS > 0)
this->ext = false;
#endif
if (brain_pin_is_onchip(brainPin)) {
ioportid_t port = getHwPort(msg, brainPin);
int pin = getHwPin(msg, brainPin);
// Validate port
if (port == GPIO_NULL) {
firmwareError(ObdCode::OBD_PCM_Processor_Fault, "OutputPin::initPin got invalid port for pin idx %d", static_cast<int>(brainPin));
return;
}
2023-11-01 14:54:57 -07:00
m_port = port;
m_pin = pin;
2017-04-21 14:26:50 -07:00
}
#if (BOARD_EXT_GPIOCHIPS > 0)
else {
this->ext = true;
}
#endif
#endif // briefly leave the include guard because we need to set default state in tests
2023-11-01 14:54:57 -07:00
m_brainPin = brainPin;
2019-07-03 00:18:04 -07:00
// The order of the next two calls may look strange, which is a good observation.
// We call them in this order so that the pin is set to a known state BEFORE
// it's enabled. Enabling the pin then setting it could result in a (brief)
// mystery state being driven on the pin (potentially dangerous).
2017-04-21 15:11:36 -07:00
setDefaultPinState(outputMode);
#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
2023-11-01 14:54:57 -07:00
iomode_t ioMode = (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) ?
PAL_MODE_OUTPUT_PUSHPULL : PAL_MODE_OUTPUT_OPENDRAIN;
efiSetPadMode(msg, m_brainPin, ioMode);
2023-11-03 15:31:13 -07:00
#ifndef DISABLE_PIN_STATE_VALIDATION
2023-11-01 14:54:57 -07:00
if (brain_pin_is_onchip(m_brainPin)) {
int actualValue = palReadPad(m_port, m_pin);
// we had enough drama with pin configuration in board.h and else that we shall self-check
// todo: handle OM_OPENDRAIN and OM_OPENDRAIN_INVERTED as well
2023-02-25 00:31:12 -08:00
if (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) {
const int logicalValue =
2023-02-25 00:31:12 -08:00
(outputMode == OM_INVERTED)
? !actualValue
: actualValue;
// if the pin was set to logical 1, then set an error and disable the pin so that things don't catch fire
if (logicalValue) {
2023-11-01 14:54:57 -07:00
firmwareError(ObdCode::OBD_PCM_Processor_Fault, "HARDWARE VALIDATION FAILED %s: unexpected startup pin state %s actual value=%d logical value=%d mode=%s", msg, hwPortname(m_brainPin), actualValue, logicalValue, getPin_output_mode_e(outputMode));
OutputPin::deInit();
}
}
}
2023-11-03 15:31:13 -07:00
#endif // DISABLE_PIN_STATE_VALIDATION
2017-04-21 14:38:13 -07:00
#endif /* EFI_GPIO_HARDWARE */
}
void OutputPin::deInit() {
// Unregister under lock - we don't want other threads mucking with the pin while we're trying to turn it off
chibios_rt::CriticalSectionLocker csl;
// nothing to do if not registered in the first place
2023-11-01 14:54:57 -07:00
if (!isInitialized()) {
return;
}
#if (BOARD_EXT_GPIOCHIPS > 0)
ext = false;
#endif // (BOARD_EXT_GPIOCHIPS > 0)
2023-11-01 14:54:57 -07:00
efiPrintf("unregistering %s", hwPortname(m_brainPin));
#if EFI_GPIO_HARDWARE && EFI_PROD_CODE
2023-11-01 14:54:57 -07:00
efiSetPadUnused(m_brainPin);
#endif /* EFI_GPIO_HARDWARE */
// Clear the pin so that it won't get set any more
2023-11-01 14:54:57 -07:00
m_brainPin = Gpio::Unassigned;
}
2019-04-12 19:07:03 -07:00
#if EFI_GPIO_HARDWARE
2017-04-21 14:38:13 -07:00
// questionable trick: we avoid using 'getHwPort' and 'getHwPin' in case of errors in order to increase the changes of turning the LED
2019-09-19 18:41:52 -07:00
// by reducing stack requirement
ioportid_t criticalErrorLedPort;
ioportmask_t criticalErrorLedPin;
uint8_t criticalErrorLedState;
2020-09-09 15:18:59 -07:00
2019-09-19 18:41:52 -07:00
#if EFI_PROD_CODE
static void initErrorLed(Gpio led) {
2023-06-04 23:32:14 -07:00
enginePins.errorLedPin.initPin("led: CRITICAL status", led, (LED_PIN_MODE));
criticalErrorLedPort = getHwPort("CRITICAL", led);
criticalErrorLedPin = getHwPin("CRITICAL", led);
2023-06-04 23:32:14 -07:00
criticalErrorLedState = (LED_PIN_MODE == OM_INVERTED) ? 0 : 1;
}
#endif /* EFI_PROD_CODE */
void initPrimaryPins() {
#if EFI_PROD_CODE
initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
addConsoleAction("gpio_pins", EnginePins::debug);
2019-09-19 18:41:52 -07:00
#endif /* EFI_PROD_CODE */
2017-04-21 12:14:37 -07:00
}
2017-04-21 12:28:47 -07:00
/**
* This method is part of fatal error handling.
* The whole method is pretty naive, but that's at least something.
*/
void turnAllPinsOff(void) {
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
2017-04-21 12:28:47 -07:00
enginePins.injectors[i].setValue(false);
enginePins.coils[i].setValue(false);
2021-07-09 07:14:00 -07:00
enginePins.trailingCoils[i].setValue(false);
2017-04-21 12:28:47 -07:00
}
}
2017-04-21 12:14:37 -07:00
#endif /* EFI_GPIO_HARDWARE */