diff --git a/firmware/hw_layer/adc/adc_inputs.cpp b/firmware/hw_layer/adc/adc_inputs.cpp index 1683327421..9bd53b87d3 100644 --- a/firmware/hw_layer/adc/adc_inputs.cpp +++ b/firmware/hw_layer/adc/adc_inputs.cpp @@ -66,17 +66,6 @@ AdcDevice::AdcDevice(ADCConversionGroup* hwConfig, adcsample_t *buf, size_t buf_ memset(internalAdcIndexByHardwareIndex, 0xFF, sizeof(internalAdcIndexByHardwareIndex)); } -#if !defined(GPT_FREQ_FAST) || !defined(GPT_PERIOD_FAST) -/** - * 8000 RPM is 133Hz - * If we want to sample MAP once per 5 degrees we need 133Hz * (360 / 5) = 9576Hz of fast ADC - */ -// todo: migrate to continuous ADC mode? probably not - we cannot afford the callback in -// todo: continuous mode. todo: look into our options -#define GPT_FREQ_FAST 100000 /* PWM clock frequency. I wonder what does this setting mean? */ -#define GPT_PERIOD_FAST 10 /* PWM period (in PWM ticks). */ -#endif /* GPT_FREQ_FAST GPT_PERIOD_FAST */ - #endif // EFI_USE_FAST_ADC // is there a reason to have this configurable at runtime? diff --git a/firmware/hw_layer/adc/adc_inputs.h b/firmware/hw_layer/adc/adc_inputs.h index ac684dded4..8339e9cc6f 100644 --- a/firmware/hw_layer/adc/adc_inputs.h +++ b/firmware/hw_layer/adc/adc_inputs.h @@ -62,6 +62,17 @@ void removeChannel(const char *name, adc_channel_e setting); #define adcToVoltsDivided(adc) (adcToVolts(adc) * engineConfiguration->analogInputDividerCoefficient) +#if !defined(GPT_FREQ_FAST) || !defined(GPT_PERIOD_FAST) +/** + * 8000 RPM is 133Hz + * If we want to sample MAP once per 5 degrees we need 133Hz * (360 / 5) = 9576Hz of fast ADC + */ +// todo: migrate to continuous ADC mode? probably not - we cannot afford the callback in +// todo: continuous mode. todo: look into our options +#define GPT_FREQ_FAST 100000 /* PWM clock frequency. I wonder what does this setting mean? */ +#define GPT_PERIOD_FAST 10 /* PWM period (in PWM ticks). */ +#endif /* GPT_FREQ_FAST GPT_PERIOD_FAST */ + // This callback is called by the ADC driver when a new fast ADC sample is ready void onFastAdcComplete(adcsample_t* samples); diff --git a/firmware/hw_layer/digital_input/trigger/trigger_input.h b/firmware/hw_layer/digital_input/trigger/trigger_input.h index 78fe98d7d5..70343a4cd5 100644 --- a/firmware/hw_layer/digital_input/trigger/trigger_input.h +++ b/firmware/hw_layer/digital_input/trigger/trigger_input.h @@ -33,16 +33,20 @@ void stopTriggerInputPins(); void stopTriggerDebugPins(); void startTriggerDebugPins(); -#if HAL_TRIGGER_USE_ADC && HAL_USE_ADC +#if HAL_USE_ADC +typedef adcsample_t triggerAdcSample_t; +#endif /* HAL_USE_ADC */ + // This detector has 2 modes for low-RPM (ADC) and fast-RPM (EXTI) enum triggerAdcMode_t { - TRIGGER_NONE = 0, - TRIGGER_ADC, - TRIGGER_EXTI, + TRIGGER_ADC_NONE = 0, + TRIGGER_ADC_ADC, + TRIGGER_ADC_EXTI, }; adc_channel_e getAdcChannelForTrigger(void); void addAdcChannelForTrigger(void); -void triggerAdcCallback(adcsample_t value); -#endif /* HAL_USE_ADC */ +void triggerAdcCallback(triggerAdcSample_t value); +void setTriggerAdcMode(triggerAdcMode_t adcMode); +void onTriggerChanged(efitick_t stamp, bool isPrimary, bool isRising); diff --git a/firmware/hw_layer/trigger_input_adc.cpp b/firmware/hw_layer/digital_input/trigger/trigger_input_adc.cpp similarity index 54% rename from firmware/hw_layer/trigger_input_adc.cpp rename to firmware/hw_layer/digital_input/trigger/trigger_input_adc.cpp index 346ae136a5..2a61eed52f 100644 --- a/firmware/hw_layer/trigger_input_adc.cpp +++ b/firmware/hw_layer/digital_input/trigger/trigger_input_adc.cpp @@ -8,202 +8,80 @@ */ #include "pch.h" +#include "trigger_input_adc.h" -#if (EFI_SHAFT_POSITION_INPUT && HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || defined(__DOXYGEN__) +#define voltsToAdcDivided(volts) (voltsToAdc(volts) / engineConfiguration->analogInputDividerCoefficient) -#include "trigger_input.h" -#include "digital_input_exti.h" -//!!!!!!!!!!!!!!! -extern "C" void toggleLed(int led, int mode); -#define BOARD_MOD1_PORT GPIOD -#define BOARD_MOD1_PIN 5 - -#if 0 -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; -#endif - -static const adcsample_t adcDefaultThreshold = (ADC_MAX_VALUE / 2); -static const adcsample_t adcMinThreshold = adcDefaultThreshold - 200; -static const adcsample_t adcMaxThreshold = adcDefaultThreshold + 200; - -static float triggerAdcITermCoef = 1600.0f; -static float triggerAdcITermMin = 3.125e-8f; // corresponds to rpm=25 - -static int transitionCooldown = 5; +/*static*/ TriggerAdcDetector trigAdcState; #define DELTA_THRESHOLD_CNT_LOW (GPT_FREQ_FAST / GPT_PERIOD_FAST / 32) // ~1/32 second? #define DELTA_THRESHOLD_CNT_HIGH (GPT_FREQ_FAST / GPT_PERIOD_FAST / 4) // ~1/4 second? -/*static */triggerAdcMode_t curAdcMode = TRIGGER_NONE; -/*static*/ float adcThreshold = adcDefaultThreshold; -static float triggerAdcITerm = triggerAdcITermMin; -// these thresholds allow to switch from ADC mode (low-rpm) to EXTI mode (fast-rpm), indicating the clamping of the signal -static adcsample_t switchingThresholdLow = 0, switchingThresholdHigh = 0; -static efitick_t minDeltaTimeForStableAdcDetectionNt = 0; -static efitick_t stampCorrectionForAdc = 0; -static int switchingCnt = 0, switchingTeethCnt = 0; -static int prevValue = 0; // not set -static efitick_t prevStamp = 0; -// we need to distinguish between weak and strong signals because of different SNR and thresholds. -static bool isSignalWeak = true; -static int zeroThreshold = 0; -// the 'center' of the signal is variable, so we need to adjust the thresholds. -static int minDeltaThresholdWeakSignal = 0, minDeltaThresholdStrongSignal = 0; -// this is the number of measurements while we store the counter before we reset to 'isSignalWeak' -static int minDeltaThresholdCntPos = 0, minDeltaThresholdCntNeg = 0; -static int integralSum = 0; -static int transitionCooldownCnt = 0; +// hardware-dependent part +#if (EFI_SHAFT_POSITION_INPUT && HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || defined(__DOXYGEN__) + +#include "digital_input_exti.h" + +//!!!!!!!!!! +#define TRIGGER_ADC_DEBUG_LED TRUE + +#ifdef TRIGGER_ADC_DEBUG_LED +#define TRIGGER_ADC_DEBUG_LED1_PORT GPIOH +#define TRIGGER_ADC_DEBUG_LED1_PIN 9 +//#define DEBUG_OUTPUT_IGN1 + +void toggleLed(int led, int mode) { +#if 1 + static uint8_t st[5] = { 0 }; + if ((st[led] == 0 && mode == 0) || mode == 1) { + palClearPad(TRIGGER_ADC_DEBUG_LED1_PORT, TRIGGER_ADC_DEBUG_LED1_PIN); +#ifdef DEBUG_OUTPUT_IGN1 + palClearPad(GPIOI, 8); +#endif + } + else if ((st[led] != 0 && mode == 0) || mode == -1) { + palSetPad(TRIGGER_ADC_DEBUG_LED1_PORT, TRIGGER_ADC_DEBUG_LED1_PIN); +#ifdef DEBUG_OUTPUT_IGN1 + palSetPad(GPIOI, 8); +#endif + } + st[led] = (st[led] + 1) % 2/*10*/; //!!!!!!!!!!! +#endif +} +#endif /* TRIGGER_ADC_DEBUG_LED */ // used for fast pin mode switching between ADC and EXTINT static ioportid_t triggerInputPort; static ioportmask_t triggerInputPin; +#ifndef PAL_MODE_EXTINT +#define PAL_MODE_EXTINT PAL_MODE_INPUT +#endif /* PAL_MODE_EXTINT */ -#if 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(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 - efiPrintf("* 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); -} -#endif - -static void setTriggerAdcMode(triggerAdcMode_t adcMode) { +void setTriggerAdcMode(triggerAdcMode_t adcMode) { palSetPadMode(triggerInputPort, triggerInputPin, - (adcMode == TRIGGER_ADC) ? PAL_MODE_INPUT_ANALOG : PAL_MODE_ALTERNATE(PAL_MODE_ALTERNATIVE_EXTINT)); - curAdcMode = adcMode; + (adcMode == TRIGGER_ADC_ADC) ? PAL_MODE_INPUT_ANALOG : PAL_MODE_EXTINT); + trigAdcState.curAdcMode = adcMode; } -static void onTriggerChanged(efitick_t stamp, bool isPrimary, bool isRising) { - //!!!!!!!!! - palWritePad(BOARD_MOD1_PORT, BOARD_MOD1_PIN, isRising ? 1 : 0); - - //toggleLed(2, (curAdcMode == TRIGGER_ADC) ? 0 : -1); - //toggleLed(3, (curAdcMode == TRIGGER_EXTI) ? 0 : -1); - -#if 1 - // todo: support for 3rd trigger input channel - // todo: start using real event time from HW event, not just software timer? - - // call the main trigger handler - hwHandleShaftSignal(isPrimary ? 0 : 1, isRising, stamp); -#endif -} - - -static void shaft_callback(void *arg, efitick_t stamp) { - if (curAdcMode != TRIGGER_EXTI) { - return; - } - +static void shaft_callback(void *arg) { // do the time sensitive things as early as possible! ioline_t pal_line = (ioline_t)arg; bool rise = (palReadLine(pal_line) == PAL_HIGH); - onTriggerChanged(stamp, true, rise); - - if ((stamp - prevStamp) > minDeltaTimeForStableAdcDetectionNt) { - switchingCnt++; - } else { - switchingCnt = 0; - switchingTeethCnt = 0; - } - - if (switchingCnt > 4) { - switchingCnt = 0; - // we need at least 3 wide teeth to be certain! - // we don't want to confuse them with a sync.gap - if (switchingTeethCnt++ > 3) { - switchingTeethCnt = 0; - prevValue = rise ? 1: -1; - setTriggerAdcMode(TRIGGER_ADC); - } - } - - prevStamp = stamp; + trigAdcState.digitalCallback(stamp, true, rise); } -static void cam_callback(void *, efitick_t) { +static void cam_callback(void *) { + // TODO: implement... } -// todo: add cam support? -#if 0 -static void comp_cam_callback(COMPDriver *comp) { +void triggerAdcCallback(triggerAdcSample_t value) { efitick_t stamp = getTimeNowNt(); - - if (isRising) { - hwHandleVvtCamSignal(TV_RISE, stamp); - } else { - hwHandleVvtCamSignal(TV_FALL, stamp); - } -} -#endif - -void turnOnTriggerInputPins() { - applyNewTriggerInputPins(); + trigAdcState.analogCallback(stamp, value); } -#if 0 -static int getDacValue(uint8_t voltage) { - constexpr float maxDacValue = 255.0f; // 8-bit DAC - return (int)efiRound(maxDacValue * (float)voltage * VOLTAGE_1_BYTE_PACKING_DIV / engineConfiguration->adcVcc, 1.0f); -} -#endif - -static void resetTriggerDetector() { - // todo: move some of these to config - - // we need to make at least minNumAdcMeasurementsPerTooth for 1 tooth (i.e. between two consequent events) - const int minNumAdcMeasurementsPerTooth = 20; - minDeltaTimeForStableAdcDetectionNt = US2NT(US_PER_SECOND_LL * minNumAdcMeasurementsPerTooth * GPT_PERIOD_FAST / GPT_FREQ_FAST); - // we assume that the transition occurs somewhere in the middle of the measurement period, so we take the half of it - stampCorrectionForAdc = US2NT(US_PER_SECOND_LL * GPT_PERIOD_FAST / GPT_FREQ_FAST / 2); - // these thresholds allow to switch from ADC mode to EXTI mode, indicating the clamping of the signal - switchingThresholdLow = voltsToAdc(1.0f); - switchingThresholdHigh = voltsToAdc(4.0f); - switchingCnt = 0; - switchingTeethCnt = 0; - // used to filter out low signals - minDeltaThresholdWeakSignal = voltsToAdc(0.05f); // 50mV - // we need to shift the default threshold even for strong signals because of the possible loss of the first tooth (after the sync) - minDeltaThresholdStrongSignal = voltsToAdc(0.04f); // 5mV - // when the strong signal becomes weak, we want to ignore the increased noise - // so we create a dead-zone between the pos. and neg. thresholds - zeroThreshold = minDeltaThresholdWeakSignal / 2; - triggerAdcITerm = triggerAdcITermMin; - adcThreshold = adcDefaultThreshold; - isSignalWeak = true; - integralSum = 0; - transitionCooldownCnt = 0; - prevValue = 0; // not set - prevStamp = 0; - minDeltaThresholdCntPos = 0; - minDeltaThresholdCntNeg = 0; -} static int turnOnTriggerInputPin(const char *msg, int index, bool isTriggerShaft) { brain_pin_e brainPin = isTriggerShaft ? @@ -211,24 +89,8 @@ static int turnOnTriggerInputPin(const char *msg, int index, bool isTriggerShaft if (!isBrainPinValid(brainPin)) return 0; -#if 0 - centeredDacValue = getDacValue(engineConfiguration->triggerCompCenterVolt); // usually 2.5V resistor divider - - dacHysteresisMin = getDacValue(engineConfiguration->triggerCompHystMin); // usually ~20mV - dacHysteresisMax = getDacValue(engineConfiguration->triggerCompHystMax); // usually ~300mV - dacHysteresisDelta = dacHysteresisMin; - - // 20 rpm (60_2) = 1000*60/((2*60)*20) = 25 ms for 1 tooth event - float satRpm = engineConfiguration->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); - - efiPrintf("startTIPins(): cDac=%d hystMin=%d hystMax=%d satRpm=%.0f satFreq*1k=%f period=%d", - centeredDacValue, dacHysteresisMin, dacHysteresisMax, satRpm, saturatedVrFreqNt * 1000.0f, hystUpdatePeriodNumEvents); -#endif - resetTriggerDetector(); + trigAdcState.init(PASS_ENGINE_PARAMETER_SIGNATURE); triggerInputPort = getHwPort("trg", brainPin); triggerInputPin = getHwPin("trg", brainPin); @@ -239,34 +101,23 @@ static int turnOnTriggerInputPin(const char *msg, int index, bool isTriggerShaft efiExtiEnablePin(msg, brainPin, PAL_EVENT_MODE_BOTH_EDGES, isTriggerShaft ? shaft_callback : cam_callback, (void *)pal_line); // ADC mode is default, because we don't know if the wheel is already spinning - setTriggerAdcMode(TRIGGER_ADC); + setTriggerAdcMode(TRIGGER_ADC_ADC); + +#ifdef TRIGGER_ADC_DEBUG_LED + palSetPadMode(TRIGGER_ADC_DEBUG_LED1_PORT, TRIGGER_ADC_DEBUG_LED1_PIN, PAL_MODE_OUTPUT_PUSHPULL); +#ifdef DEBUG_OUTPUT_IGN1 + palSetPadMode(GPIOI, 8, PAL_MODE_OUTPUT_PUSHPULL); +#endif +#endif /* TRIGGER_ADC_DEBUG_LED */ return 0; } -void startTriggerInputPins(void) { - for (int i = 0; i < TRIGGER_SUPPORTED_CHANNELS; i++) { - if (isConfigurationChanged(triggerInputPins[i])) { - const char * msg = (i == 0 ? "trigger#1" : (i == 1 ? "trigger#2" : "trigger#3")); - turnOnTriggerInputPin(msg, i, true); - } - } +void adcTriggerTurnOffInputPin(brain_pin_e brainPin) { + efiExtiDisablePin(brainPin); } - -void stopTriggerInputPins(void) { - efiPrintf("stopTIPins();"); - -#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 +void adcTriggerTurnOnInputPins() { } adc_channel_e getAdcChannelForTrigger(void) { @@ -284,12 +135,100 @@ void addAdcChannelForTrigger(void) { } } -void triggerAdcCallback(adcsample_t value) { - if (curAdcMode != TRIGGER_ADC) { +void onTriggerChanged(efitick_t stamp, bool isPrimary, bool isRising) { +#ifdef TRIGGER_ADC_DEBUG_LED + toggleLed(0, 0); +#endif /* TRIGGER_ADC_DEBUG_LED */ + +#if 1 + // todo: support for 3rd trigger input channel + // todo: start using real event time from HW event, not just software timer? + + // call the main trigger handler + hwHandleShaftSignal(isPrimary ? 0 : 1, isRising, stamp); +#endif +} + +#endif /* EFI_SHAFT_POSITION_INPUT && HAL_TRIGGER_USE_ADC && HAL_USE_ADC */ + + +void TriggerAdcDetector::init() { + // todo: move some of these to config + + // we need to make at least minNumAdcMeasurementsPerTooth for 1 tooth (i.e. between two consequent events) + const int minNumAdcMeasurementsPerTooth = 20; + minDeltaTimeForStableAdcDetectionNt = US2NT(US_PER_SECOND_LL * minNumAdcMeasurementsPerTooth * GPT_PERIOD_FAST / GPT_FREQ_FAST); + // we assume that the transition occurs somewhere in the middle of the measurement period, so we take the half of it + stampCorrectionForAdc = US2NT(US_PER_SECOND_LL * GPT_PERIOD_FAST / GPT_FREQ_FAST / 2); + + // these thresholds allow to switch from ADC mode to EXTI mode, indicating the clamping of the signal + switchingThresholdLow = voltsToAdcDivided(1.0f); + switchingThresholdHigh = voltsToAdcDivided(4.0f); + + // used to filter out low signals + minDeltaThresholdWeakSignal = voltsToAdcDivided(0.05f); // 50mV + // we need to shift the default threshold even for strong signals because of the possible loss of the first tooth (after the sync) + minDeltaThresholdStrongSignal = voltsToAdcDivided(0.04f); // 5mV + + const triggerAdcSample_t adcDeltaThreshold = voltsToAdcDivided(0.25f); + adcDefaultThreshold = voltsToAdcDivided(3.4f); // this corresponds to VREF1 on Hellen boards + adcMinThreshold = adcDefaultThreshold - adcDeltaThreshold; + adcMaxThreshold = adcDefaultThreshold - adcDeltaThreshold; + + reset(); +} + +void TriggerAdcDetector::reset() { + switchingCnt = 0; + switchingTeethCnt = 0; + // when the strong signal becomes weak, we want to ignore the increased noise + // so we create a dead-zone between the pos. and neg. thresholds + zeroThreshold = minDeltaThresholdWeakSignal / 2; + triggerAdcITerm = triggerAdcITermMin; + + adcThreshold = adcDefaultThreshold; + + isSignalWeak = true; + integralSum = 0; + transitionCooldownCnt = 0; + prevValue = 0; // not set + prevStamp = 0; + minDeltaThresholdCntPos = 0; + minDeltaThresholdCntNeg = 0; +} + +void TriggerAdcDetector::digitalCallback(efitick_t stamp, bool isPrimary, bool rise) { + if (curAdcMode != TRIGGER_ADC_EXTI) { return; } - efitick_t stamp = getTimeNowNt(); + onTriggerChanged(stamp, isPrimary, rise); + + if ((stamp - prevStamp) > minDeltaTimeForStableAdcDetectionNt) { + switchingCnt++; + } else { + switchingCnt = 0; + switchingTeethCnt = 0; + } + + if (switchingCnt > 4) { + switchingCnt = 0; + // we need at least 3 wide teeth to be certain! + // we don't want to confuse them with a sync.gap + if (switchingTeethCnt++ > 3) { + switchingTeethCnt = 0; + prevValue = rise ? 1: -1; + setTriggerAdcMode(TRIGGER_ADC_ADC); + } + } + + prevStamp = stamp; +} + +void TriggerAdcDetector::analogCallback(efitick_t stamp, triggerAdcSample_t value) { + if (curAdcMode != TRIGGER_ADC_ADC) { + return; + } // <1V or >4V? if (value >= switchingThresholdHigh || value <= switchingThresholdLow) { @@ -324,7 +263,7 @@ void triggerAdcCallback(adcsample_t value) { // we haven't seen the strong signal (pos or neg) for too long, maybe it's lost or too weak? if (minDeltaThresholdCntPos <= 0 || minDeltaThresholdCntNeg <= 0) { // reset to the weak signal mode - resetTriggerDetector(); + reset(); return; } } @@ -362,17 +301,9 @@ void triggerAdcCallback(adcsample_t value) { transition = -1; } else { - //!!!!!!!!!! - //toggleLed(2, 0); - return; // both are positive/negative/zero: not interested! } - //!!!!!!!!!! - //toggleLed(2, -1); - //!!!!!!!!!! - //toggleLed(3, 0); - if (isSignalWeak) { if (minDeltaThresholdCntPos >= DELTA_THRESHOLD_CNT_LOW && minDeltaThresholdCntNeg >= DELTA_THRESHOLD_CNT_LOW) { // ok, now we have a legit strong signal, let's restore the threshold @@ -407,7 +338,7 @@ void triggerAdcCallback(adcsample_t value) { // we need at least 3 high-signal teeth to be certain! if (switchingTeethCnt++ > 3) { switchingTeethCnt = 0; - setTriggerAdcMode(TRIGGER_EXTI); + setTriggerAdcMode(TRIGGER_ADC_EXTI); // we don't want to loose the signal on return minDeltaThresholdCntPos = DELTA_THRESHOLD_CNT_HIGH; minDeltaThresholdCntNeg = DELTA_THRESHOLD_CNT_HIGH; @@ -422,7 +353,5 @@ void triggerAdcCallback(adcsample_t value) { prevValue = transition; prevStamp = stamp; - } -#endif /* EFI_SHAFT_POSITION_INPUT && HAL_TRIGGER_USE_ADC && HAL_USE_ADC */ diff --git a/firmware/hw_layer/digital_input/trigger/trigger_input_adc.h b/firmware/hw_layer/digital_input/trigger/trigger_input_adc.h new file mode 100644 index 0000000000..9ddade3580 --- /dev/null +++ b/firmware/hw_layer/digital_input/trigger/trigger_input_adc.h @@ -0,0 +1,60 @@ +/** + * @file trigger_input_adc.h + * @brief Position sensor hardware layer, Using ADC and software comparator + * + * @date Jan 27, 2020 + * @author andreika + * @author Andrey Belomutskiy, (c) 2012-2020 + */ + +#pragma once +#include "global.h" +#include "trigger_input.h" +#include "adc_inputs.h" + + +#define DELTA_THRESHOLD_CNT_LOW (GPT_FREQ_FAST / GPT_PERIOD_FAST / 32) // ~1/32 second? +#define DELTA_THRESHOLD_CNT_HIGH (GPT_FREQ_FAST / GPT_PERIOD_FAST / 4) // ~1/4 second? + +class TriggerAdcDetector { +public: + void init(); + void reset(); + + void digitalCallback(efitick_t stamp, bool isPrimary, bool rise); + void analogCallback(efitick_t stamp, triggerAdcSample_t value); + +public: + triggerAdcSample_t adcDefaultThreshold; + triggerAdcSample_t adcMinThreshold; + triggerAdcSample_t adcMaxThreshold; + + float triggerAdcITermCoef = 1600.0f; + float triggerAdcITermMin = 3.125e-8f; // corresponds to rpm=25 + + int transitionCooldown = 5; + + triggerAdcMode_t curAdcMode = TRIGGER_ADC_NONE; + float adcThreshold = adcDefaultThreshold; + float triggerAdcITerm = triggerAdcITermMin; + + // these thresholds allow to switch from ADC mode (low-rpm) to EXTI mode (fast-rpm), indicating the clamping of the signal + triggerAdcSample_t switchingThresholdLow = 0, switchingThresholdHigh = 0; + efitick_t minDeltaTimeForStableAdcDetectionNt = 0; + efitick_t stampCorrectionForAdc = 0; + int switchingCnt = 0, switchingTeethCnt = 0; + int prevValue = 0; // not set + efitick_t prevStamp = 0; + + // we need to distinguish between weak and strong signals because of different SNR and thresholds. + bool isSignalWeak = true; + int zeroThreshold = 0; + + // the 'center' of the signal is variable, so we need to adjust the thresholds. + int minDeltaThresholdWeakSignal = 0, minDeltaThresholdStrongSignal = 0; + + // this is the number of measurements while we store the counter before we reset to 'isSignalWeak' + int minDeltaThresholdCntPos = 0, minDeltaThresholdCntNeg = 0; + int integralSum = 0; + int transitionCooldownCnt = 0; +}; diff --git a/firmware/hw_layer/hw_layer.mk b/firmware/hw_layer/hw_layer.mk index 7d66bd1320..b4e4793264 100644 --- a/firmware/hw_layer/hw_layer.mk +++ b/firmware/hw_layer/hw_layer.mk @@ -20,6 +20,7 @@ HW_LAYER_EMS_CPP = \ $(PROJECT_DIR)/hw_layer/digital_input/trigger/trigger_input.cpp \ $(PROJECT_DIR)/hw_layer/digital_input/trigger/trigger_input_icu.cpp \ $(PROJECT_DIR)/hw_layer/digital_input/trigger/trigger_input_exti.cpp \ + $(PROJECT_DIR)/hw_layer/digital_input/trigger/trigger_input_adc.cpp \ $(PROJECT_DIR)/hw_layer/hardware.cpp \ $(PROJECT_DIR)/hw_layer/smart_gpio.cpp \ $(PROJECT_DIR)/hw_layer/mmc_card.cpp \