diff --git a/firmware/hw_layer/trigger_input_icu.cpp b/firmware/hw_layer/trigger_input_icu.cpp new file mode 100644 index 0000000000..b1a00ec102 --- /dev/null +++ b/firmware/hw_layer/trigger_input_icu.cpp @@ -0,0 +1,287 @@ +/** + * @file trigger_input_icu.cpp + * @brief Position sensor hardware layer (ICU and PAL drivers) + * + * todo: code reuse with digital_input_hw.cpp was never finished + * todo: at the moment due to half-done code reuse we already depend on EFI_ICU_INPUTS but still have custom code + * todo: VVT implementation is a nasty copy-paste :( + * + * see digital_input_hw.cp + * + * @date Dec 30, 2012 + * @author Andrey Belomutskiy, (c) 2012-2019 + */ + +#include "global.h" + +volatile int icuWidthCallbackCounter = 0; +volatile int icuWidthPeriodCounter = 0; +bool hwTriggerInputEnabled = true; // this is useful at least for real hardware integration testing + +#if EFI_SHAFT_POSITION_INPUT && (HAL_TRIGGER_USE_PAL == TRUE || HAL_USE_ICU == TRUE) && (HAL_USE_COMP == FALSE) + +#include "trigger_input.h" +#include "digital_input_hw.h" +#include "digital_input_exti.h" +#include "pin_repository.h" +#include "trigger_structure.h" +#include "trigger_central.h" +#include "engine_configuration.h" + +extern bool hasFirmwareErrorFlag; + +EXTERN_ENGINE +; +static Logging *logger; + +#if EFI_PROD_CODE +/* PAL based implementation */ +#if (HAL_TRIGGER_USE_PAL == TRUE) && (PAL_USE_CALLBACKS == TRUE) + +/* static variables for PAL implementation */ +static ioline_t primary_line; + +static void shaft_callback(void *arg) { + ioline_t pal_line = (ioline_t)arg; + // todo: support for 3rd trigger input channel + // todo: start using real event time from HW event, not just software timer? + if (hasFirmwareErrorFlag) + return; + + bool isPrimary = pal_line == primary_line; + if (!isPrimary && !TRIGGER_SHAPE(needSecondTriggerInput)) { + return; + } + + bool rise = (palReadLine(pal_line) == PAL_HIGH); + trigger_event_e signal; + // todo: add support for 3rd channel + if (rise) { + signal = isPrimary ? + (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_FALLING : SHAFT_PRIMARY_RISING) : + (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_FALLING : SHAFT_SECONDARY_RISING); + } else { + signal = isPrimary ? + (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_RISING : SHAFT_PRIMARY_FALLING) : + (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_RISING : SHAFT_SECONDARY_FALLING); + } + + hwHandleShaftSignal(signal); +} + +static void cam_callback(void *arg) { + ioline_t pal_line = (ioline_t)arg; + + bool rise = (palReadLine(pal_line) == PAL_HIGH); + + if (rise) { + hwHandleVvtCamSignal(TV_RISE); + } else { + hwHandleVvtCamSignal(TV_FALL); + } +} + +static int turnOnTriggerInputPin(const char *msg, brain_pin_e brainPin, bool is_shaft) { + scheduleMsg(logger, "turnOnTriggerInputPin(PAL) %s %s", msg, hwPortname(brainPin)); + + /* TODO: + * * do not set to both edges if we need only one + * * simplify callback in case of one edge */ + ioline_t pal_line = PAL_LINE(getHwPort("trg", brainPin), getHwPin("trg", brainPin)); + return efiExtiEnablePin(msg, brainPin, PAL_EVENT_MODE_BOTH_EDGES, is_shaft ? shaft_callback : cam_callback, (void *)pal_line); +} + +static void turnOffTriggerInputPin(brain_pin_e brainPin) { + efiExtiDisablePin(brainPin); +} + +static void setPrimaryChannel(brain_pin_e brainPin) { + primary_line = PAL_LINE(getHwPort("trg", brainPin), getHwPin("trg", brainPin)); +} + +/* ICU based implementation */ +#elif (HAL_USE_ICU) + +/* static vars for ICU implementation */ +static ICUDriver *primaryCrankDriver; + +static void cam_icu_width_callback(ICUDriver *icup) { + (void)icup; + hwHandleVvtCamSignal(TV_RISE); +} + +static void cam_icu_period_callback(ICUDriver *icup) { + (void)icup; + hwHandleVvtCamSignal(TV_FALL); +} + +/** + * that's hardware timer input capture IRQ entry point + * 'width' events happens before the 'period' event + */ +static void shaft_icu_width_callback(ICUDriver *icup) { + if (!hwTriggerInputEnabled) { + return; + } + icuWidthCallbackCounter++; +// todo: support for 3rd trigger input channel +// todo: start using real event time from HW event, not just software timer? + if (hasFirmwareErrorFlag) + return; + int isPrimary = icup == primaryCrankDriver; + if (!isPrimary && !TRIGGER_SHAPE(needSecondTriggerInput)) { + return; + } + // icucnt_t last_width = icuGetWidth(icup); so far we are fine with system time + // todo: add support for 3rd channel + trigger_event_e signal = isPrimary ? (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_FALLING : SHAFT_PRIMARY_RISING) : (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_FALLING : SHAFT_SECONDARY_RISING); + + hwHandleShaftSignal(signal); +} + +static void shaft_icu_period_callback(ICUDriver *icup) { + if (!hwTriggerInputEnabled) { + return; + } + icuWidthPeriodCounter++; + if (hasFirmwareErrorFlag) + return; + int isPrimary = icup == primaryCrankDriver; + if (!isPrimary && !TRIGGER_SHAPE(needSecondTriggerInput)) { + return; + } + + // todo: add support for 3rd channel + // icucnt_t last_period = icuGetPeriod(icup); so far we are fine with system time + trigger_event_e signal = + isPrimary ? (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_RISING : SHAFT_PRIMARY_FALLING) : (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_RISING : SHAFT_SECONDARY_FALLING); + hwHandleShaftSignal(signal); +} + +/** + * the main purpose of this configuration structure is to specify the input interrupt callbacks + */ +static ICUConfig shaft_icucfg = { ICU_INPUT_ACTIVE_LOW, + 100000, /* 100kHz ICU clock frequency. */ + shaft_icu_width_callback, + shaft_icu_period_callback, + NULL, + ICU_CHANNEL_1, + 0}; + +/** + * this is about VTTi and stuff kind of cam sensor + */ +static ICUConfig cam_icucfg = { ICU_INPUT_ACTIVE_LOW, + 100000, /* 100kHz ICU clock frequency. */ + cam_icu_width_callback, + cam_icu_period_callback, + NULL, + ICU_CHANNEL_1, + 0}; + +static int turnOnTriggerInputPin(const char *msg, brain_pin_e brainPin, bool is_shaft) { + ICUConfig *icucfg; + + if (brainPin == GPIO_UNASSIGNED) { + return -1; + } + + if (is_shaft) { + icucfg = &shaft_icucfg; + } else { + icucfg = &cam_icucfg; + } + + // configure pin + turnOnCapturePin(msg, brainPin); + icucfg->channel = getInputCaptureChannel(brainPin); + + ICUDriver *driver = getInputCaptureDriver(msg, brainPin); + scheduleMsg(logger, "turnOnTriggerInputPin %s", hwPortname(brainPin)); + // todo: reuse 'setWaveReaderMode' method here? + if (driver != NULL) { + // todo: once http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=1757 is fixed +// bool needWidthCallback = !CONFIG(useOnlyRisingEdgeForTrigger) || TRIGGER_SHAPE(useRiseEdge); +// shaft_icucfg.width_cb = needWidthCallback ? shaft_icu_width_callback : NULL; + +// bool needPeriodCallback = !CONFIG(useOnlyRisingEdgeForTrigger) || !TRIGGER_SHAPE(useRiseEdge); +// shaft_icucfg.period_cb = needPeriodCallback ? shaft_icu_period_callback : NULL; + + efiIcuStart(msg, driver, icucfg); + if (driver->state == ICU_READY) { + efiAssert(CUSTOM_ERR_ASSERT, driver != NULL, "ti: driver is NULL", -1); + efiAssert(CUSTOM_ERR_ASSERT, driver->state == ICU_READY, "ti: driver not ready", -1); + icuStartCapture(driver); // this would change state from READY to WAITING + icuEnableNotifications(driver); + } else { + // we would be here for example if same pin is used for multiple input capture purposes + firmwareError(CUSTOM_ERR_ICU_STATE, "ICU unexpected state [%s]", hwPortname(brainPin)); + } + } + return 0; +} + +static void turnOffTriggerInputPin(brain_pin_e brainPin) { + ICUDriver *driver = getInputCaptureDriver("trigger_off", brainPin); + if (driver != NULL) { + icuDisableNotifications(driver); + icuStopCapture(driver); + icuStop(driver); + scheduleMsg(logger, "turnOffTriggerInputPin %s", hwPortname(brainPin)); + turnOffCapturePin(brainPin); + } +} + +static void setPrimaryChannel(brain_pin_e brainPin) { + primaryCrankDriver = getInputCaptureDriver("primary", brainPin); +} + +#endif /* HAL_USE_ICU */ +#endif /* EFI_PROD_CODE */ + +/*==========================================================================*/ +/* Exported functions. */ +/*==========================================================================*/ + +void turnOnTriggerInputPins(Logging *sharedLogger) { + logger = sharedLogger; + + applyNewTriggerInputPins(); +} + +void stopTriggerInputPins(void) { +#if EFI_PROD_CODE + for (int i = 0; i < TRIGGER_SUPPORTED_CHANNELS; i++) { + if (isConfigurationChanged(bc.triggerInputPins[i])) { + turnOffTriggerInputPin(activeConfiguration.bc.triggerInputPins[i]); + } + } + for (int i = 0; i < CAM_INPUTS_COUNT; i++) { + if (isConfigurationChanged(camInputs[i])) { + turnOffTriggerInputPin(activeConfiguration.camInputs[i]); + } + } +#endif /* EFI_PROD_CODE */ +} + +void startTriggerInputPins(void) { +#if EFI_PROD_CODE + for (int i = 0; i < TRIGGER_SUPPORTED_CHANNELS; i++) { + if (isConfigurationChanged(bc.triggerInputPins[i])) { + const char * msg = (i == 0 ? "trigger#1" : (i == 1 ? "trigger#2" : "trigger#3")); + turnOnTriggerInputPin(msg, CONFIGB(triggerInputPins)[i], true); + } + } + + for (int i = 0; i < CAM_INPUTS_COUNT; i++) { + if (isConfigurationChanged(camInputs[i])) { + turnOnTriggerInputPin("cam", engineConfiguration->camInputs[i], false); + } + } + + setPrimaryChannel(CONFIGB(triggerInputPins)[0]); +#endif /* EFI_PROD_CODE */ +} + +#endif /* (EFI_SHAFT_POSITION_INPUT && (HAL_TRIGGER_USE_PAL == TRUE || HAL_USE_ICU == TRUE) && (HAL_USE_COMP == FALSE)) */