rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
trigger_input_adc.cpp
Go to the documentation of this file.
1/**
2 * @file trigger_input_adc.cpp
3 * @brief Position sensor hardware layer, Using ADC and software comparator
4 *
5 * @date Jan 27, 2020
6 * @author andreika <prometheus.pcb@gmail.com>
7 * @author Andrey Belomutskiy, (c) 2012-2020
8 */
9
10#include "pch.h"
11#include "trigger_input_adc.h"
12
13
15
16#define DELTA_THRESHOLD_CNT_LOW (GPT_FREQ_FAST / GPT_PERIOD_FAST / 32) // ~1/32 second?
17#define DELTA_THRESHOLD_CNT_HIGH (GPT_FREQ_FAST / GPT_PERIOD_FAST / 4) // ~1/4 second?
18
19#if HAL_USE_ADC || EFI_UNIT_TEST
20#define triggerVoltsToAdcDivided(volts) (voltsToAdc(volts) / trigAdcState.triggerInputDividerCoefficient)
21#endif // HAL_USE_ADC || EFI_UNIT_TEST
22
23// hardware-dependent part
24#if (EFI_SHAFT_POSITION_INPUT && HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || defined(__DOXYGEN__)
25
26#include "digital_input_exti.h"
27
28#ifndef TRIGGER_ADC_DEBUG_LED
29#define TRIGGER_ADC_DEBUG_LED FALSE
30#endif
31//#define DEBUG_OUTPUT_IGN1 TRUE
32//#define TRIGGER_ADC_DUMP_BUF TRUE
33
34#ifdef TRIGGER_ADC_DEBUG_LED
35#define TRIGGER_ADC_DEBUG_LED1_PORT GPIOH
36#define TRIGGER_ADC_DEBUG_LED1_PIN 9
37
38#ifdef TRIGGER_ADC_DUMP_BUF
39static const int dumpBufNum = 100;
41static int dumpBufCnt = 0;
42#endif /* TRIGGER_ADC_DUMP_BUF */
43
44void toggleLed(int led, int mode) {
45#if 1
46 static uint8_t st[5] = { 0 };
47 if ((st[led] == 0 && mode == 0) || mode == 1) {
48 palClearPad(TRIGGER_ADC_DEBUG_LED1_PORT, TRIGGER_ADC_DEBUG_LED1_PIN);
49#ifdef DEBUG_OUTPUT_IGN1
50 palClearPad(GPIOI, 8);
51#endif
52 }
53 else if ((st[led] != 0 && mode == 0) || mode == -1) {
54 palSetPad(TRIGGER_ADC_DEBUG_LED1_PORT, TRIGGER_ADC_DEBUG_LED1_PIN);
55#ifdef DEBUG_OUTPUT_IGN1
56 palSetPad(GPIOI, 8);
57#endif
58 }
59 st[led] = (st[led] + 1) % 2/*10*/; //!!!!!!!!!!!
60#endif
61}
62#endif /* TRIGGER_ADC_DEBUG_LED */
63
64// used for fast pin mode switching between ADC and EXTINT
67
68#ifndef PAL_MODE_EXTINT
69#define PAL_MODE_EXTINT PAL_MODE_INPUT
70#endif /* PAL_MODE_EXTINT */
71
73 trigAdcState.curAdcMode = adcMode;
75
76 palSetPadMode(triggerInputPort, triggerInputPin,
77 (adcMode == TRIGGER_ADC_ADC) ? PAL_MODE_INPUT_ANALOG : PAL_MODE_EXTINT);
78}
79
80static void shaft_callback(void *arg, efitick_t stamp) {
81 // do the time sensitive things as early as possible!
82 ioline_t pal_line = (ioline_t)arg;
83 bool rise = (palReadLine(pal_line) == PAL_HIGH);
84
85 trigAdcState.digitalCallback(stamp, true, rise);
86}
87
88static void cam_callback(void *, efitick_t stamp) {
89 // TODO: implement...
90}
91
93 efitick_t stamp = getTimeNowNt();
94 trigAdcState.analogCallback(stamp, value);
95}
96
97#ifdef TRIGGER_ADC_DUMP_BUF
98static void printDumpBuf(void) {
99 efiPrintf("------");
100 for (int i = 0; i < dumpBufNum; i++) {
101 int pos = (dumpBufCnt - i - 1 + dumpBufNum) % dumpBufNum;
103 efiPrintf("[%d] %d", i, v);
104 }
105}
106#endif /* TRIGGER_ADC_DUMP_BUF */
107
108
109int adcTriggerTurnOnInputPin(const char *msg, int index, bool isTriggerShaft) {
110 brain_pin_e brainPin = isTriggerShaft ?
112
114
115 triggerInputPort = getHwPort("trg", brainPin);
116 triggerInputPin = getHwPin("trg", brainPin);
117
118 ioline_t pal_line = PAL_LINE(triggerInputPort, triggerInputPin);
119 efiPrintf("turnOnTriggerInputPin %s l=%ld", hwPortname(brainPin), pal_line);
120
121 if (efiExtiEnablePin(msg, brainPin, PAL_EVENT_MODE_BOTH_EDGES,
122 isTriggerShaft ? shaft_callback : cam_callback, (void *)pal_line) < 0) {
123 return -1;
124 }
125
126 // ADC mode is default, because we don't know if the wheel is already spinning
128
129#ifdef TRIGGER_ADC_DEBUG_LED
130 palSetPadMode(TRIGGER_ADC_DEBUG_LED1_PORT, TRIGGER_ADC_DEBUG_LED1_PIN, PAL_MODE_OUTPUT_PUSHPULL);
131#ifdef DEBUG_OUTPUT_IGN1
132 palSetPadMode(GPIOI, 8, PAL_MODE_OUTPUT_PUSHPULL);
133#endif
134#endif /* TRIGGER_ADC_DEBUG_LED */
135
136#ifdef TRIGGER_ADC_DUMP_BUF
137 addConsoleAction("trigger_adc_dump", printDumpBuf);
138#endif /* TRIGGER_ADC_DUMP_BUF */
139
140 return 0;
141}
142
144 efiExtiDisablePin(brainPin);
145}
146
149
151 // todo: add other trigger or cam channels?
153 if (!isBrainPinValid(brainPin))
154 return EFI_ADC_NONE;
155 return getAdcChannel(brainPin);
156}
157
164
165void onTriggerChanged(efitick_t stamp, bool isPrimary, bool isRising) {
166#ifdef TRIGGER_ADC_DEBUG_LED
167 toggleLed(0, 0);
168#endif /* TRIGGER_ADC_DEBUG_LED */
169
170#if 1
171 // todo: support for 3rd trigger input channel
172 // todo: start using real event time from HW event, not just software timer?
173
174 // call the main trigger handler
175 hwHandleShaftSignal(isPrimary ? 0 : 1, isRising, stamp);
176#endif // 1
177}
178
179#endif // EFI_SHAFT_POSITION_INPUT && HAL_TRIGGER_USE_ADC && HAL_USE_ADC
180
181
183#if ! EFI_SIMULATOR
184
185 // todo: move some of these to config
186
187#if HAL_USE_ADC || EFI_UNIT_TEST
188 // 4.7k||5.1k + 4.7k
189 triggerInputDividerCoefficient = 1.52f; // = analogInputDividerCoefficient
190
191 // we need to make at least minNumAdcMeasurementsPerTooth for 1 tooth (i.e. between two consequent events)
192 const int minNumAdcMeasurementsPerTooth = 10; // for 60-2 wheel: 1/(10*2*60/10000/60) = 500 RPM
193 minDeltaTimeForStableAdcDetectionNt = US2NT(US_PER_SECOND_LL * minNumAdcMeasurementsPerTooth * GPT_PERIOD_FAST / GPT_FREQ_FAST);
194 // we assume that the transition occurs somewhere in the middle of the measurement period, so we take the half of it
195 stampCorrectionForAdc = US2NT(US_PER_SECOND_LL * GPT_PERIOD_FAST / GPT_FREQ_FAST / 2);
196
199
200 // used to filter out low signals
201 minDeltaThresholdWeakSignal = triggerVoltsToAdcDivided(0.05f); // 50mV
202 // we need to shift the default threshold even for strong signals because of the possible loss of the first tooth (after the sync)
203 minDeltaThresholdStrongSignal = triggerVoltsToAdcDivided(0.04f); // 5mV
204
205 const triggerAdcSample_t adcDeltaThreshold = triggerVoltsToAdcDivided(0.25f);
206 adcDefaultThreshold = triggerVoltsToAdcDivided(2.5f); // this corresponds to VREF1 on Hellen boards
207 adcMinThreshold = adcDefaultThreshold - adcDeltaThreshold;
208 adcMaxThreshold = adcDefaultThreshold + adcDeltaThreshold;
209
210 // these thresholds allow to switch from ADC mode to EXTI mode, indicating the clamping of the signal
211 // they should exceed the MCU schmitt trigger thresholds (usually 0.3*Vdd and 0.7*Vdd)
212 switchingThresholdLow = triggerVoltsToAdcDivided(1.0f); // = 0.2*Vdd (<0.3*Vdd)
213 switchingThresholdHigh = triggerVoltsToAdcDivided(4.0f); // = 0.8*Vdd (>0.7*Vdd)
214#endif // HAL_USE_ADC || EFI_UNIT_TEST
215
216 modeSwitchCnt = 0;
217
218 reset();
219#endif // ! EFI_SIMULATOR
220}
221
223 switchingCnt = 0;
225#if HAL_USE_ADC || EFI_UNIT_TEST
226 // when the strong signal becomes weak, we want to ignore the increased noise
227 // so we create a dead-zone between the pos. and neg. thresholds
230
232
233 isSignalWeak = true;
234 integralSum = 0;
238#endif // HAL_USE_ADC || EFI_UNIT_TEST
239
240 prevValue = 0; // not set
241 prevStamp = 0;
242}
243
244void TriggerAdcDetector::digitalCallback(efitick_t stamp, bool isPrimary, bool rise) {
245#if !EFI_SIMULATOR && EFI_SHAFT_POSITION_INPUT
247 return;
248 }
249
250 UNUSED(isPrimary);
251
252 onTriggerChanged(stamp, isPrimary, rise);
253
254#if (HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || EFI_UNIT_TEST
256 switchingCnt++;
257 } else {
258 switchingCnt = 0;
260 }
261
263 switchingCnt = 0;
264 // we need at least 3 wide teeth to be certain!
265 // we don't want to confuse them with a sync.gap
266 if (switchingTeethCnt++ > 3) {
268 prevValue = rise ? 1: -1;
270 }
271 }
272#endif // (HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || EFI_UNIT_TEST
273
274 prevStamp = stamp;
275#endif // !EFI_SIMULATOR && EFI_SHAFT_POSITION_INPUT
276}
277
279#if ! EFI_SIMULATOR && ((HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || EFI_UNIT_TEST)
281 return;
282 }
283
284#ifdef TRIGGER_ADC_DUMP_BUF
285 dumpBuf[dumpBufCnt] = value;
287#endif /* TRIGGER_ADC_DUMP_BUF */
288
289 // <1V or >4V?
290 if (value >= switchingThresholdHigh || value <= switchingThresholdLow) {
291 switchingCnt++;
292 } else {
293 //switchingCnt = 0;
294 switchingCnt = maxI(switchingCnt - 1, 0);
295 }
296
297 int delta = value - adcThreshold;
298 int aDelta = absI(delta);
299 if (isSignalWeak) {
300 // todo: detect if the sensor is disconnected (where the signal is always near 'ADC_MAX_VALUE')
301
302 // filter out low signals (noise)
303 if (delta >= minDeltaThresholdWeakSignal) {
305 }
306 if (delta <= -minDeltaThresholdWeakSignal) {
308 }
309 } else {
310 // we just had a strong signal, let's reset the counter
311 if (delta >= minDeltaThresholdWeakSignal) {
312 minDeltaThresholdCntPos = DELTA_THRESHOLD_CNT_HIGH;
313 }
314 if (delta <= -minDeltaThresholdWeakSignal) {
315 minDeltaThresholdCntNeg = DELTA_THRESHOLD_CNT_HIGH;
316 }
319 // we haven't seen the strong signal (pos or neg) for too long, maybe it's lost or too weak?
321 // reset to the weak signal mode
322 reset();
323 return;
324 }
325 }
326
327 // the threshold should always correspond to the averaged signal.
328 integralSum += delta;
329 // we need some limits for the integral sum
330 // we use a simple I-regulator to move the threshold
332 // limit the threshold for safety
334
335 // now to the transition part... First, we need a cooldown to pre-filter the transition noise
336 if (transitionCooldownCnt-- < 0)
338
339 // we need at least 2 different measurements to detect a transition
340 if (prevValue == 0) {
341 // we can take the measurement only from outside the dead-zone
342 if (aDelta > minDeltaThresholdWeakSignal) {
343 prevValue = (delta > 0) ? 1 : -1;
344 } else {
345 return;
346 }
347 }
348
349 if (isSignalWeak) {
350 if (minDeltaThresholdCntPos >= DELTA_THRESHOLD_CNT_LOW && minDeltaThresholdCntNeg >= DELTA_THRESHOLD_CNT_LOW) {
351 // ok, now we have a legit strong signal, let's restore the threshold
352 isSignalWeak = false;
353 integralSum = 0;
355 } else {
356 // we cannot trust the weak signal!
357 return;
358 }
359 }
360
361 if (transitionCooldownCnt <= 0) {
362 // detect the edge
363 int transition = 0;
364 if (delta > zeroThreshold && prevValue == -1) {
365 // a rising transition found!
366 transition = 1;
367 }
368 else if (delta <= -zeroThreshold && prevValue == 1) {
369 // a falling transition found!
370 transition = -1;
371 }
372 else {
373 return; // both are positive/negative/zero: not interested!
374 }
375
376 onTriggerChanged(stamp - stampCorrectionForAdc, true, transition == 1);
377 // let's skip some nearest possible measurements:
378 // the transition cannot be SO fast, but the jitter can!
380
381 // it should not accumulate too much
382 integralSum = 0;
383#if 0
384 // update triggerAdcITerm
385 efitimeus_t deltaTimeUs = NT2US(stamp - prevStamp);
386 if (deltaTimeUs > 200) { // 200 us = ~2500 RPM (we don't need this correction for large RPM)
387 triggerAdcITerm = 1.0f / (triggerAdcITermCoef * deltaTimeUs);
389 }
390#endif // 0
391
392 prevValue = transition;
393 }
394
395#ifdef EFI_SHAFT_POSITION_INPUT
397 switchingCnt = 0;
398 // we need at least 3 high-signal teeth to be certain!
399 if (switchingTeethCnt++ > 3) {
401
403
404 // we don't want to loose the signal on return
405 minDeltaThresholdCntPos = DELTA_THRESHOLD_CNT_HIGH;
406 minDeltaThresholdCntNeg = DELTA_THRESHOLD_CNT_HIGH;
407 // we want to reset the thresholds on return
410 // reset integrator
412 integralSum = 0;
414 return;
415 }
416 } else {
417 // we don't see "big teeth" anymore
419 }
420#endif // EFI_SHAFT_POSITION_INPUT
421
422 prevStamp = stamp;
423#else
424 UNUSED(stamp); UNUSED(value);
425#endif // ! EFI_SIMULATOR && ((HAL_TRIGGER_USE_ADC && HAL_USE_ADC) || EFI_UNIT_TEST)
426}
427
429#if HAL_USE_ADC || EFI_UNIT_TEST
430 isSignalWeak = isWeak;
431 if (!isSignalWeak) {
432 minDeltaThresholdCntPos = minDeltaThresholdCntNeg = DELTA_THRESHOLD_CNT_LOW;
433 } else {
435 }
436#endif // HAL_USE_ADC || EFI_UNIT_TEST
437}
438
442
445}
446
void addFastAdcChannel(const char *, adc_channel_e hwChannel)
bool isAdcChannelValid(adc_channel_e hwChannel)
Definition adc_inputs.h:23
uint16_t channel
Definition adc_inputs.h:104
triggerAdcSample_t switchingThresholdHigh
triggerAdcSample_t switchingThresholdLow
triggerAdcSample_t adcMaxThreshold
triggerAdcSample_t adcMinThreshold
void analogCallback(efitick_t stamp, triggerAdcSample_t value)
triggerAdcMode_t curAdcMode
void setWeakSignal(bool isWeak)
efidur_t minDeltaTimeForStableAdcDetectionNt
void digitalCallback(efitick_t stamp, bool isPrimary, bool rise)
triggerAdcSample_t adcDefaultThreshold
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
adc_channel_e getAdcChannel(brain_pin_e pin)
void efiExtiDisablePin(brain_pin_e brainPin)
int efiExtiEnablePin(const char *msg, brain_pin_e brainPin, uint32_t mode, ExtiCallback cb, void *cb_data)
ioportid_t getHwPort(const char *msg, brain_pin_e brainPin)
ioportmask_t getHwPin(const char *msg, brain_pin_e brainPin)
efitick_t getTimeNowNt()
Definition efitime.cpp:19
static constexpr engine_configuration_s * engineConfiguration
uint32_t ioportmask_t
Digital I/O port sized unsigned type.
Definition hal_pal_lld.h:78
GPIO_TypeDef * ioportid_t
Port Identifier.
uint32_t ioline_t
Type of an I/O line.
Definition hal_pal_lld.h:88
UNUSED(samplingTimeSeconds)
const char * hwPortname(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)
brain_input_pin_e triggerInputPins[TRIGGER_INPUT_PIN_COUNT]
void hwHandleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp)
void setTriggerAdcMode(triggerAdcMode_t adcMode)
triggerAdcMode_t
@ TRIGGER_ADC_ADC
@ TRIGGER_ADC_EXTI
void onTriggerChanged(efitick_t stamp, bool isPrimary, bool isRising)
adcsample_t triggerAdcSample_t
void setTriggerAdcMode(triggerAdcMode_t adcMode)
int adcTriggerTurnOnInputPin(const char *msg, int index, bool isTriggerShaft)
void adcTriggerTurnOffInputPin(brain_pin_e brainPin)
void triggerAdcCallback(triggerAdcSample_t value)
static void shaft_callback(void *arg, efitick_t stamp)
TriggerAdcDetector trigAdcState
static void printDumpBuf(void)
void addAdcChannelForTrigger(void)
triggerAdcMode_t getTriggerAdcMode(void)
void onTriggerChanged(efitick_t stamp, bool isPrimary, bool isRising)
static ioportmask_t triggerInputPin
float getTriggerAdcThreshold(void)
adc_channel_e getAdcChannelForTrigger(void)
static const int dumpBufNum
void toggleLed(int led, int mode)
static void cam_callback(void *, efitick_t stamp)
static triggerAdcSample_t dumpBuf[dumpBufNum]
void adcTriggerTurnOnInputPins()
static ioportid_t triggerInputPort
int getTriggerAdcModeCnt(void)
static int dumpBufCnt
Position sensor hardware layer, Using ADC and software comparator.