Line |
Branch |
Decision |
Exec |
Source |
1 |
|
|
|
/** |
2 |
|
|
|
* @file adc_inputs.cpp |
3 |
|
|
|
* @brief Low level ADC code |
4 |
|
|
|
* |
5 |
|
|
|
* @date Jan 14, 2013 |
6 |
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020 |
7 |
|
|
|
*/ |
8 |
|
|
|
|
9 |
|
|
|
#include "pch.h" |
10 |
|
|
|
|
11 |
|
|
✗ |
float PUBLIC_API_WEAK getAnalogInputDividerCoefficient(adc_channel_e) { |
12 |
|
|
✗ |
return engineConfiguration->analogInputDividerCoefficient; |
13 |
|
|
|
} |
14 |
|
|
|
|
15 |
|
|
✗ |
float PUBLIC_API_WEAK boardAdjustVoltage(float voltage, adc_channel_e /* hwChannel */) { |
16 |
|
|
|
// a hack useful when we do not trust voltage just after board EN was turned on. is this just hiding electrical design flaws? |
17 |
|
|
✗ |
return voltage; |
18 |
|
|
|
} |
19 |
|
|
|
|
20 |
|
|
|
/* overall analog health state |
21 |
|
|
|
* return negative in case of any problems |
22 |
|
|
|
* return 0 if everything is ok or no diagnostic is available */ |
23 |
|
|
✗ |
ObdCode PUBLIC_API_WEAK boardGetAnalogDiagnostic() { |
24 |
|
|
✗ |
return ObdCode::None; |
25 |
|
|
|
} |
26 |
|
|
|
|
27 |
|
|
|
/* simple implementation if board does not provide advanced diagnostic */ |
28 |
|
|
✗ |
int PUBLIC_API_WEAK boardGetAnalogInputDiagnostic(adc_channel_e channel, float) { |
29 |
|
|
|
#if EFI_PROD_CODE |
30 |
|
|
|
/* for on-chip ADC inputs we check common analog health */ |
31 |
|
|
|
if (isAdcChannelOnChip(channel)) { |
32 |
|
|
|
return (boardGetAnalogDiagnostic() == ObdCode::None) ? 0 : -1; |
33 |
|
|
|
} |
34 |
|
|
|
#endif // EFI_PROD_CODE |
35 |
|
|
|
|
36 |
|
|
|
/* input is outside chip/ECU */ |
37 |
|
|
✗ |
return 0; |
38 |
|
|
|
} |
39 |
|
|
|
|
40 |
|
|
✗ |
static ObdCode analogGetVrefDiagnostic() |
41 |
|
|
|
{ |
42 |
|
|
|
#if HAL_USE_ADC |
43 |
|
|
|
float vref = getMCUVref(); |
44 |
|
|
|
|
45 |
|
|
|
// TODO: +/-10% is way too big? |
46 |
|
|
|
if (vref > engineConfiguration->adcVcc * 1.1) { |
47 |
|
|
|
return ObdCode::OBD_Sensor_Refence_Voltate_A_High; |
48 |
|
|
|
} |
49 |
|
|
|
|
50 |
|
|
|
if (vref < engineConfiguration->adcVcc * 0.9) { |
51 |
|
|
|
return ObdCode::OBD_Sensor_Refence_Voltate_A_Low; |
52 |
|
|
|
} |
53 |
|
|
|
#endif |
54 |
|
|
|
|
55 |
|
|
✗ |
return ObdCode::None; |
56 |
|
|
|
} |
57 |
|
|
|
|
58 |
|
|
|
/* Get analog part diagnostic */ |
59 |
|
|
✗ |
ObdCode analogGetDiagnostic() |
60 |
|
|
|
{ |
61 |
|
|
|
/* TODO: debounce? */ |
62 |
|
|
✗ |
auto code = analogGetVrefDiagnostic(); |
63 |
|
|
✗ |
if (code != ObdCode::None) { |
64 |
|
|
✗ |
return code; |
65 |
|
|
|
} |
66 |
|
|
|
|
67 |
|
|
✗ |
return boardGetAnalogDiagnostic(); |
68 |
|
|
|
} |
69 |
|
|
|
|
70 |
|
|
|
#if HAL_USE_ADC |
71 |
|
|
|
|
72 |
|
|
|
#include "adc_device.h" |
73 |
|
|
|
#include "adc_subscription.h" |
74 |
|
|
|
#include "mpu_util.h" |
75 |
|
|
|
#include "protected_gpio.h" |
76 |
|
|
|
|
77 |
|
|
|
// voltage in MCU universe, from zero to Vref |
78 |
|
|
|
expected<float> adcGetRawVoltage(const char *msg, adc_channel_e hwChannel) { |
79 |
|
|
|
float rawVoltage = adcRawValueToRawVoltage(adcGetRawValue(msg, hwChannel)); |
80 |
|
|
|
int inputStatus = boardGetAnalogInputDiagnostic(hwChannel, rawVoltage); |
81 |
|
|
|
|
82 |
|
|
|
if (inputStatus == 0) { |
83 |
|
|
|
return expected(rawVoltage); |
84 |
|
|
|
} |
85 |
|
|
|
|
86 |
|
|
|
/* TODO: convert inputStatus to unexpected? */ |
87 |
|
|
|
return unexpected; |
88 |
|
|
|
} |
89 |
|
|
|
|
90 |
|
|
|
// voltage in ECU universe, with all input dividers and OpAmps gains taken into account, voltage at ECU connector pin |
91 |
|
|
|
expected<float> adcGetScaledVoltage(const char *msg, adc_channel_e hwChannel) { |
92 |
|
|
|
auto rawVoltage = adcGetRawVoltage(msg, hwChannel); |
93 |
|
|
|
|
94 |
|
|
|
if (rawVoltage) { |
95 |
|
|
|
// TODO: merge getAnalogInputDividerCoefficient() and boardAdjustVoltage() into single board hook? |
96 |
|
|
|
float voltage = rawVoltage.value_or(0) * getAnalogInputDividerCoefficient(hwChannel); |
97 |
|
|
|
return expected(boardAdjustVoltage(voltage, hwChannel)); |
98 |
|
|
|
} |
99 |
|
|
|
|
100 |
|
|
|
return expected(rawVoltage); |
101 |
|
|
|
} |
102 |
|
|
|
|
103 |
|
|
|
extern AdcDevice fastAdc; |
104 |
|
|
|
|
105 |
|
|
|
static AdcChannelMode adcHwChannelMode[EFI_ADC_TOTAL_CHANNELS]; |
106 |
|
|
|
|
107 |
|
|
|
// todo: move this flag to Engine god object |
108 |
|
|
|
static int adcDebugReporting = false; |
109 |
|
|
|
|
110 |
|
|
|
AdcChannelMode getAdcMode(adc_channel_e hwChannel) { |
111 |
|
|
|
return adcHwChannelMode[hwChannel]; |
112 |
|
|
|
} |
113 |
|
|
|
|
114 |
|
|
|
extern adcsample_t adcOnchipSlowGetAvgRaw(adc_channel_e hwChannel); |
115 |
|
|
|
|
116 |
|
|
|
int getInternalAdcValue(const char *msg, adc_channel_e hwChannel) { |
117 |
|
|
|
if (!isAdcChannelValid(hwChannel)) { |
118 |
|
|
|
warning(ObdCode::CUSTOM_OBD_ANALOG_INPUT_NOT_CONFIGURED, "ADC: %s input is not configured", msg); |
119 |
|
|
|
return -1; |
120 |
|
|
|
} |
121 |
|
|
|
|
122 |
|
|
|
#if EFI_USE_FAST_ADC |
123 |
|
|
|
if (adcHwChannelMode[hwChannel] == AdcChannelMode::Fast) { |
124 |
|
|
|
return fastAdc.getAvgAdcValue(hwChannel); |
125 |
|
|
|
} |
126 |
|
|
|
#endif // EFI_USE_FAST_ADC |
127 |
|
|
|
|
128 |
|
|
|
return adcOnchipSlowGetAvgRaw(hwChannel); |
129 |
|
|
|
} |
130 |
|
|
|
|
131 |
|
|
|
static void printAdcValue(int channel) { |
132 |
|
|
|
/* Do this check before conversion to adc_channel_e that is uint8_t based */ |
133 |
|
|
|
if ((channel < EFI_ADC_NONE) || (channel >= EFI_ADC_TOTAL_CHANNELS)) { |
134 |
|
|
|
efiPrintf("Invalid ADC channel %d", channel); |
135 |
|
|
|
return; |
136 |
|
|
|
} |
137 |
|
|
|
int adcValue = adcGetRawValue("print", (adc_channel_e)channel); |
138 |
|
|
|
float voltsInput = adcRawValueToScaledVoltage(adcValue, (adc_channel_e)channel); |
139 |
|
|
|
efiPrintf("adc %d input %.3fV", channel, voltsInput); |
140 |
|
|
|
} |
141 |
|
|
|
|
142 |
|
|
|
void adcPrintChannelReport(const char *prefix, int internalIndex, adc_channel_e hwChannel) |
143 |
|
|
|
{ |
144 |
|
|
|
if (isAdcChannelValid(hwChannel)) { |
145 |
|
|
|
ioportid_t port = getAdcChannelPort("print", hwChannel); |
146 |
|
|
|
int pin = getAdcChannelPin(hwChannel); |
147 |
|
|
|
int adcValue = adcGetRawValue("print", hwChannel); |
148 |
|
|
|
auto volts = adcGetRawVoltage("print", hwChannel); |
149 |
|
|
|
auto voltsInput = adcGetScaledVoltage("print", hwChannel); |
150 |
|
|
|
/* Human index starts from 1 */ |
151 |
|
|
|
efiPrintf(" %s ch[%2d] @ %s%d ADC%d 12bit=%4d %.3fV input %.3fV %s", |
152 |
|
|
|
prefix, internalIndex, portname(port), pin, |
153 |
|
|
|
/* TODO: */ hwChannel - EFI_ADC_0 + 1, |
154 |
|
|
|
adcValue, volts.value_or(0), voltsInput.value_or(0), volts ? "valid" : "INVALID"); |
155 |
|
|
|
} |
156 |
|
|
|
} |
157 |
|
|
|
|
158 |
|
|
|
extern void adcOnchipSlowShowReport(); |
159 |
|
|
|
|
160 |
|
|
|
void printFullAdcReport(void) { |
161 |
|
|
|
#if EFI_USE_FAST_ADC |
162 |
|
|
|
efiPrintf("fast %u samples", engine->outputChannels.fastAdcConversionCount); |
163 |
|
|
|
|
164 |
|
|
|
for (int internalIndex = 0; internalIndex < fastAdc.size(); internalIndex++) { |
165 |
|
|
|
adc_channel_e hwChannel = fastAdc.getAdcChannelByInternalIndex(internalIndex); |
166 |
|
|
|
|
167 |
|
|
|
adcPrintChannelReport("F", internalIndex, hwChannel); |
168 |
|
|
|
} |
169 |
|
|
|
#endif // EFI_USE_FAST_ADC |
170 |
|
|
|
|
171 |
|
|
|
adcOnchipSlowShowReport(); |
172 |
|
|
|
} |
173 |
|
|
|
|
174 |
|
|
|
static void setAdcDebugReporting(int value) { |
175 |
|
|
|
adcDebugReporting = value; |
176 |
|
|
|
efiPrintf("adcDebug=%d", adcDebugReporting); |
177 |
|
|
|
} |
178 |
|
|
|
|
179 |
|
|
|
extern void adcOnchipSlowUpdate(efitick_t nowNt); |
180 |
|
|
|
|
181 |
|
|
|
void adcInputsUpdateSubscribers(efitick_t nowNt) { |
182 |
|
|
|
adcOnchipSlowUpdate(nowNt); |
183 |
|
|
|
|
184 |
|
|
|
{ |
185 |
|
|
|
ScopePerf perf(PE::AdcProcessSlow); |
186 |
|
|
|
|
187 |
|
|
|
AdcSubscription::UpdateSubscribers(nowNt); |
188 |
|
|
|
|
189 |
|
|
|
protectedGpio_check(nowNt); |
190 |
|
|
|
} |
191 |
|
|
|
} |
192 |
|
|
|
|
193 |
|
|
|
void addFastAdcChannel(const char*, adc_channel_e hwChannel) { |
194 |
|
|
|
if (!isAdcChannelValid(hwChannel)) { |
195 |
|
|
|
return; |
196 |
|
|
|
} |
197 |
|
|
|
|
198 |
|
|
|
#if EFI_USE_FAST_ADC |
199 |
|
|
|
fastAdc.enableChannel(hwChannel); |
200 |
|
|
|
#endif |
201 |
|
|
|
|
202 |
|
|
|
adcHwChannelMode[hwChannel] = AdcChannelMode::Fast; |
203 |
|
|
|
// Nothing to do for slow channels, input is mapped to analog in init_sensors.cpp |
204 |
|
|
|
} |
205 |
|
|
|
|
206 |
|
|
|
void removeChannel(const char*, adc_channel_e hwChannel) { |
207 |
|
|
|
if (!isAdcChannelValid(hwChannel)) { |
208 |
|
|
|
return; |
209 |
|
|
|
} |
210 |
|
|
|
#if EFI_USE_FAST_ADC |
211 |
|
|
|
if (adcHwChannelMode[hwChannel] == AdcChannelMode::Fast) { |
212 |
|
|
|
/* TODO: */ |
213 |
|
|
|
//fastAdc.disableChannel(hwChannel); |
214 |
|
|
|
} |
215 |
|
|
|
#endif |
216 |
|
|
|
|
217 |
|
|
|
adcHwChannelMode[hwChannel] = AdcChannelMode::Off; |
218 |
|
|
|
} |
219 |
|
|
|
|
220 |
|
|
|
// Weak link a stub so that every board doesn't have to implement this function |
221 |
|
|
|
__attribute__((weak)) void setAdcChannelOverrides() { } |
222 |
|
|
|
|
223 |
|
|
|
static void configureInputs() { |
224 |
|
|
|
memset(adcHwChannelMode, (int)AdcChannelMode::Off, sizeof(adcHwChannelMode)); |
225 |
|
|
|
|
226 |
|
|
|
/** |
227 |
|
|
|
* order of analog channels here is totally random and has no meaning |
228 |
|
|
|
* we also have some weird implementation with internal indices - that all has no meaning, it's just a random implementation |
229 |
|
|
|
* which does not mean anything. |
230 |
|
|
|
*/ |
231 |
|
|
|
|
232 |
|
|
|
addFastAdcChannel("MAP", engineConfiguration->map.sensor.hwChannel); |
233 |
|
|
|
|
234 |
|
|
|
// not currently used addFastAdcChannel("Vref", engineConfiguration->vRefAdcChannel, ADC_SLOW); |
235 |
|
|
|
|
236 |
|
|
|
addFastAdcChannel("AUXF#1", engineConfiguration->auxFastSensor1_adcChannel); |
237 |
|
|
|
|
238 |
|
|
|
setAdcChannelOverrides(); |
239 |
|
|
|
} |
240 |
|
|
|
|
241 |
|
|
|
void initAdcInputs() { |
242 |
|
|
|
efiPrintf("initAdcInputs()"); |
243 |
|
|
|
|
244 |
|
|
|
configureInputs(); |
245 |
|
|
|
|
246 |
|
|
|
// migrate to 'enable adcdebug' |
247 |
|
|
|
addConsoleActionI("adcdebug", &setAdcDebugReporting); |
248 |
|
|
|
|
249 |
|
|
|
#if EFI_INTERNAL_ADC |
250 |
|
|
|
// This will start HW for all used ADCs |
251 |
|
|
|
portInitAdc(); |
252 |
|
|
|
|
253 |
|
|
|
#if EFI_USE_FAST_ADC |
254 |
|
|
|
// After this point fastAdc is not allowed to add channels |
255 |
|
|
|
fastAdc.init(); |
256 |
|
|
|
#endif // EFI_USE_FAST_ADC |
257 |
|
|
|
|
258 |
|
|
|
addConsoleActionI("adc", (VoidInt) printAdcValue); |
259 |
|
|
|
#else // ! EFI_INTERNAL_ADC |
260 |
|
|
|
efiPrintf("ADC disabled"); |
261 |
|
|
|
#endif // EFI_INTERNAL_ADC |
262 |
|
|
|
|
263 |
|
|
|
// Workaround to pre-feed all sensors with some data... |
264 |
|
|
|
chThdSleepMilliseconds(1); |
265 |
|
|
|
adcInputsUpdateSubscribers(getTimeNowNt()); |
266 |
|
|
|
} |
267 |
|
|
|
|
268 |
|
|
|
void printFullAdcReportIfNeeded(void) { |
269 |
|
|
|
if (!adcDebugReporting) |
270 |
|
|
|
return; |
271 |
|
|
|
printFullAdcReport(); |
272 |
|
|
|
} |
273 |
|
|
|
|
274 |
|
|
|
#else /* not HAL_USE_ADC */ |
275 |
|
|
|
|
276 |
|
|
|
// voltage in MCU universe, from zero to VDD |
277 |
|
|
✗ |
__attribute__((weak)) expected<float> adcGetRawVoltage(const char*, adc_channel_e) { |
278 |
|
|
✗ |
return expected(0.0f); |
279 |
|
|
|
} |
280 |
|
|
|
|
281 |
|
|
|
// voltage in ECU universe, with all input dividers and OpAmps gains taken into account, voltage at ECU connector pin |
282 |
|
|
✗ |
__attribute__((weak)) expected<float> adcGetScaledVoltage(const char*, adc_channel_e) { |
283 |
|
|
✗ |
return expected(0.0f); |
284 |
|
|
|
} |
285 |
|
|
|
|
286 |
|
|
|
#endif |
287 |
|
|
|
|