GCC Code Coverage Report


Directory: ./
File: firmware/hw_layer/adc/adc_inputs.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 0.0% 0 0 19
Functions: 0.0% 0 0 8
Branches: 0.0% 0 0 2
Decisions: 0.0% 0 - 3

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