diff --git a/firmware/controllers/system/efi_output.cpp b/firmware/controllers/system/efi_output.cpp new file mode 100644 index 0000000000..3a57750cee --- /dev/null +++ b/firmware/controllers/system/efi_output.cpp @@ -0,0 +1,8 @@ +/* + * @file efi_output.cpp + * + */ + +#include "efi_output.h" + + diff --git a/firmware/controllers/system/efi_output.h b/firmware/controllers/system/efi_output.h new file mode 100644 index 0000000000..95f1f44e29 --- /dev/null +++ b/firmware/controllers/system/efi_output.h @@ -0,0 +1,100 @@ +/* + * @file efi_output.h + * + */ + +#include "io_pins.h" +#include "smart_gpio.h" + +#pragma once + +// Used if you want a function to be virtual only for unit testing purposes +#if EFI_UNIT_TEST +#define TEST_VIRTUAL virtual +#else +#define TEST_VIRTUAL +#endif + +/** + * @brief Single output pin reference and state + */ +class OutputPin { +public: + OutputPin(); + /** + * initializes pin & registers it in pin repository + * outputMode being a pointer allow us to change configuration (for example invert logical pin) in configuration and get resuts applied + * away, or at least I hope that's why + */ + void initPin(const char *msg, brain_pin_e brainPin, const pin_output_mode_e *outputMode, bool forceInitWithFatalError = false); + /** + * same as above, with DEFAULT_OUTPUT mode + */ + void initPin(const char *msg, brain_pin_e brainPin); + + /** + * dissociates pin from this output and un-registers it in pin repository + */ + void deInit(); + + bool isInitialized(); + + bool getAndSet(int logicValue); + TEST_VIRTUAL void setValue(int logicValue); + void toggle(); + bool getLogicValue() const; + + brain_pin_diag_e getDiag() const; + +#if EFI_GPIO_HARDWARE + ioportid_t port = 0; + uint8_t pin = 0; +#endif /* EFI_GPIO_HARDWARE */ + +#if EFI_UNIT_TEST + int unitTestTurnedOnCounter = 0; +#endif + + brain_pin_e brainPin = Gpio::Unassigned; + +#if (EFI_GPIO_HARDWARE && (BOARD_EXT_GPIOCHIPS > 0)) + /* used for external pins */ + bool ext = false; +#endif /* EFI_GPIO_HARDWARE */ + + int8_t currentLogicValue = INITIAL_PIN_STATE; + /** + * we track current pin status so that we do not touch the actual hardware if we want to write new pin bit + * which is same as current pin value. This maybe helps in case of status leds, but maybe it's a total over-engineering + */ +private: + // todo: inline this method? + void setDefaultPinState(const pin_output_mode_e *defaultState); + void setOnchipValue(int electricalValue); + + // 4 byte pointer is a bit of a memory waste here + const pin_output_mode_e *modePtr = nullptr; +}; + +/** + * OutputPin which is reported on Engine Sniffer + */ +class NamedOutputPin : public virtual OutputPin { +public: + NamedOutputPin(); + explicit NamedOutputPin(const char *name); + virtual void setHigh(); + virtual void setLow(); + const char *getName() const; + const char *getShortName() const; + /** + * @return true if pin was stopped + */ + bool stop(); + // todo: char pointer is a bit of a memory waste here, we can reduce RAM usage by software-based getName() method + const char *name = nullptr; + /** + * rusEfi Engine Sniffer protocol uses these short names to reduce bytes usage + */ + const char *shortName = nullptr; +}; diff --git a/firmware/controllers/system/injection_gpio.cpp b/firmware/controllers/system/injection_gpio.cpp new file mode 100644 index 0000000000..23e555a391 --- /dev/null +++ b/firmware/controllers/system/injection_gpio.cpp @@ -0,0 +1,74 @@ +/* + * injection_gpio.cpp + */ + +#include "injection_gpio.h" +#include "engine_state.h" +#include "tooth_logger.h" +#include "tunerstudio_outputs.h" + +extern bool printFuelDebug; + +InjectorOutputPin::InjectorOutputPin() : NamedOutputPin() { + overlappingCounter = 1; // Force update in reset + reset(); + injectorIndex = -1; +} + +void InjectorOutputPin::open(efitick_t nowNt) { + // per-output counter for error detection + overlappingCounter++; + // global counter for logging + getEngineState()->fuelInjectionCounter++; + +#if FUEL_MATH_EXTREME_LOGGING + if (printFuelDebug) { + printf("InjectorOutputPin::open %s %d now=%0.1fms\r\n", name, overlappingCounter, (int)getTimeNowUs() / 1000.0); + } +#endif /* FUEL_MATH_EXTREME_LOGGING */ + + if (overlappingCounter > 1) { +// /** +// * #299 +// * this is another kind of overlap which happens in case of a small duty cycle after a large duty cycle +// */ +#if FUEL_MATH_EXTREME_LOGGING + if (printFuelDebug) { + printf("overlapping, no need to touch pin %s %d\r\n", name, (int)getTimeNowUs()); + } +#endif /* FUEL_MATH_EXTREME_LOGGING */ + } else { +#if EFI_TOOTH_LOGGER + LogTriggerInjectorState(nowNt, true); +#endif // EFI_TOOTH_LOGGER + setHigh(); + } +} + +void InjectorOutputPin::close(efitick_t nowNt) { +#if FUEL_MATH_EXTREME_LOGGING + if (printFuelDebug) { + printf("InjectorOutputPin::close %s %d %d\r\n", name, overlappingCounter, (int)getTimeNowUs()); + } +#endif /* FUEL_MATH_EXTREME_LOGGING */ + + overlappingCounter--; + if (overlappingCounter > 0) { +#if FUEL_MATH_EXTREME_LOGGING + if (printFuelDebug) { + printf("was overlapping, no need to touch pin %s %d\r\n", name, (int)getTimeNowUs()); + } +#endif /* FUEL_MATH_EXTREME_LOGGING */ + } else { +#if EFI_TOOTH_LOGGER + LogTriggerInjectorState(nowNt, false); +#endif // EFI_TOOTH_LOGGER + setLow(); + } + + // Don't allow negative overlap count + if (overlappingCounter < 0) { + overlappingCounter = 0; + } +} + diff --git a/firmware/controllers/system/injection_gpio.h b/firmware/controllers/system/injection_gpio.h new file mode 100644 index 0000000000..102112e0f0 --- /dev/null +++ b/firmware/controllers/system/injection_gpio.h @@ -0,0 +1,29 @@ +/* + * @file injection_gpio.h + */ + +#pragma once + +#include "efi_output.h" + +void startSimultaneousInjection(void* = nullptr); +void endSimultaneousInjectionOnlyTogglePins(); + +class InjectorOutputPin final : public NamedOutputPin { +public: + InjectorOutputPin(); + void reset(); + + void open(efitick_t nowNt); + void close(efitick_t nowNt); + void setHigh() override; + void setLow() override; + + int8_t getOverlappingCounter() const { return overlappingCounter; } + + // todo: re-implement this injectorIndex via address manipulation to reduce memory usage? + int8_t injectorIndex; + +private: + int8_t overlappingCounter; +};