/** * @file trigger_input_comp.cpp * @brief Position sensor hardware layer, Using hardware comparator * * @date Apr 13, 2019 * @author Andrey Belomutskiy, (c) 2012-2020 * @author andreika */ #include "global.h" #if (EFI_SHAFT_POSITION_INPUT && HAL_USE_COMP) || defined(__DOXYGEN__) #include "hal_comp.h" #include "trigger_input.h" #include "digital_input_icu.h" extern bool hasFirmwareErrorFlag; EXTERN_ENGINE ; static Logging *logger; static volatile int centeredDacValue = 127; static volatile int toothCnt = 0; static volatile int dacHysteresisMin = 1; // = 5V * 1/256 (8-bit DAC) = ~20mV static volatile int dacHysteresisMax = 15; // = ~300mV static volatile int dacHysteresisDelta = dacHysteresisMin; static volatile int hystUpdatePeriodNumEvents = 116; // every ~1 turn of 60-2 wheel static volatile efitick_t prevNt = 0; // VR-sensor saturation stuff static volatile float curVrFreqNt = 0, saturatedVrFreqNt = 0; // We want to interpolate between min and max depending on the signal level (adaptive hysteresis). // But we don't want to measure the signal amplitude directly, so we estimate it by measuring the signal frequency: // for VR sensors, the amplitude is inversely proportional to the tooth's 'time-width'. // We find it by dividing the total time by the teeth count, and use the reciprocal value as signal frequency! static void setHysteresis(COMPDriver *comp, int sign) { // update the hysteresis threshold, but not for every tooth #ifdef EFI_TRIGGER_COMP_ADAPTIVE_HYSTERESIS if (toothCnt++ > hystUpdatePeriodNumEvents) { efitick_t nowNt = getTimeNowNt(); curVrFreqNt = (float)toothCnt / (float)(nowNt - prevNt); dacHysteresisDelta = (int)efiRound(interpolateClamped(0.0f, dacHysteresisMin, saturatedVrFreqNt, dacHysteresisMax, curVrFreqNt), 1.0f); toothCnt = 0; prevNt = nowNt; #ifdef TRIGGER_COMP_EXTREME_LOGGING scheduleMsg(logger, "* f=%f d=%d", curVrFreqNt * 1000.0f, dacHysteresisDelta); #endif /* TRIGGER_COMP_EXTREME_LOGGING */ } #endif /* EFI_TRIGGER_COMP_ADAPTIVE_HYSTERESIS */ comp_lld_set_dac_value(comp, centeredDacValue + dacHysteresisDelta * sign); } static void comp_shaft_callback(COMPDriver *comp) { efitick_t stamp = getTimeNowNt(); uint32_t status = comp_lld_get_status(comp); int isPrimary = (comp == EFI_COMP_PRIMARY_DEVICE); if (!isPrimary && !TRIGGER_WAVEFORM(needSecondTriggerInput)) { return; } trigger_event_e signal; if (status & COMP_IRQ_RISING) { signal = isPrimary ? (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_FALLING : SHAFT_PRIMARY_RISING) : (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_FALLING : SHAFT_SECONDARY_RISING); hwHandleShaftSignal(signal, stamp); // shift the threshold down a little bit to avoid false-triggering (threshold hysteresis) setHysteresis(comp, -1); } if (status & COMP_IRQ_FALLING) { signal = isPrimary ? (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_RISING : SHAFT_PRIMARY_FALLING) : (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_RISING : SHAFT_SECONDARY_FALLING); hwHandleShaftSignal(signal, stamp); // shift the threshold up a little bit to avoid false-triggering (threshold hysteresis) setHysteresis(comp, 1); } } // todo: add cam support? #if 0 static void comp_cam_callback(COMPDriver *comp) { efitick_t stamp = getTimeNowNt(); if (isRising) { hwHandleVvtCamSignal(TV_RISE, stamp); } else { hwHandleVvtCamSignal(TV_FALL, stamp); } } #endif static COMPConfig comp_shaft_cfg = { COMP_OUTPUT_NORMAL, COMP_IRQ_BOTH, comp_shaft_callback, 0 }; static bool isCompEnabled = false; void turnOnTriggerInputPins(Logging *sharedLogger) { logger = sharedLogger; compInit(); compStart(EFI_COMP_PRIMARY_DEVICE, &comp_shaft_cfg); applyNewTriggerInputPins(); } static int getDacValue(uint8_t voltage DECLARE_ENGINE_PARAMETER_SUFFIX) { constexpr float maxDacValue = 255.0f; // 8-bit DAC return (int)efiRound(maxDacValue * (float)voltage * VOLTAGE_1_BYTE_PACKING_DIV / CONFIG(adcVcc), 1.0f); } void startTriggerInputPins(void) { //efiAssertVoid(CUSTOM_ERR_, !isCompEnabled, "isCompEnabled"); if (isCompEnabled) { scheduleMsg(logger, "startTIPins(): already enabled!"); return; } centeredDacValue = getDacValue(CONFIG(triggerCompCenterVolt) PASS_ENGINE_PARAMETER_SUFFIX); // usually 2.5V resistor divider dacHysteresisMin = getDacValue(CONFIG(triggerCompHystMin) PASS_ENGINE_PARAMETER_SUFFIX); // usually ~20mV dacHysteresisMax = getDacValue(CONFIG(triggerCompHystMax) PASS_ENGINE_PARAMETER_SUFFIX); // usually ~300mV dacHysteresisDelta = dacHysteresisMin; // 20 rpm (60_2) = 1000*60/((2*60)*20) = 25 ms for 1 tooth event float satRpm = CONFIG(triggerCompSensorSatRpm) * RPM_1_BYTE_PACKING_MULT; hystUpdatePeriodNumEvents = ENGINE(triggerCentral.triggerShape).getSize(); // = 116 for "60-2" trigger wheel float saturatedToothDurationUs = 60.0f * US_PER_SECOND_F / satRpm / hystUpdatePeriodNumEvents; saturatedVrFreqNt = 1.0f / US2NT(saturatedToothDurationUs); scheduleMsg(logger, "startTIPins(): cDac=%d hystMin=%d hystMax=%d satRpm=%.0f satFreq*1k=%f period=%d", centeredDacValue, dacHysteresisMin, dacHysteresisMax, satRpm, saturatedVrFreqNt * 1000.0f, hystUpdatePeriodNumEvents); #ifdef EFI_TRIGGER_COMP_ADAPTIVE_HYSTERESIS scheduleMsg(logger, "startTIPins(): ADAPTIVE_HYSTERESIS enabled!"); #endif /* EFI_TRIGGER_COMP_ADAPTIVE_HYSTERESIS */ int channel = EFI_COMP_TRIGGER_CHANNEL; // todo: use getInputCaptureChannel(hwPin); // todo: set pin mode to default (analog/comparator) //palSetPadMode(comp_channel_port[channel], comp_channel_pad[channel], PAL_MODE_INPUT_ANALOG); // no generic hal support for extended COMP configuration, so we use hal_lld layer... osalSysLock(); comp_lld_set_dac_value(EFI_COMP_PRIMARY_DEVICE, centeredDacValue); comp_lld_channel_enable(EFI_COMP_PRIMARY_DEVICE, channel); osalSysUnlock(); compEnable(EFI_COMP_PRIMARY_DEVICE); isCompEnabled = true; } void stopTriggerInputPins(void) { if (!isCompEnabled) { scheduleMsg(logger, "stopTIPins(): already disabled!"); return; } scheduleMsg(logger, "stopTIPins();"); compDisable(EFI_COMP_PRIMARY_DEVICE); isCompEnabled = false; #if 0 for (int i = 0; i < TRIGGER_SUPPORTED_CHANNELS; i++) { if (isConfigurationChanged(bc.triggerInputPins[i])) { turnOffTriggerInputPin(activeConfiguration.bc.triggerInputPins[i]); } } if (isConfigurationChanged(camInput)) { turnOffTriggerInputPin(activeConfiguration.camInput); } #endif } #endif /* EFI_SHAFT_POSITION_INPUT && HAL_USE_COMP */