rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
stm32_adc_v2.cpp
Go to the documentation of this file.
1/**
2 * @file stm32_adc_v2.cpp
3 * @brief Port implementation for the STM32 "v2" ADC found on the STM32F4 and STM32F7
4 *
5 * @date February 9, 2021
6 * @author Matthew Kennedy, (c) 2021
7 */
8
9#include "pch.h"
10
11#ifdef EFI_SOFTWARE_KNOCK
12#include "knock_config.h"
13#endif
14
15#if HAL_USE_ADC
16
17/* HW channels count per ADC */
18constexpr size_t adcChannelCount = 16;
19constexpr size_t adcAux1ChannelCount = 2;
20constexpr size_t adcAux2ChannelCount = 1;
21
22/* Depth of the conversion buffer, channels are sampled X times each.*/
23#define SLOW_ADC_OVERSAMPLE 8
24
25#ifndef EFI_INTERNAL_SLOW_ADC_BACKGROUND
26#define EFI_INTERNAL_SLOW_ADC_BACKGROUND FALSE
27#endif
28
29#ifdef ADC_MUX_PIN
30// https://github.com/rusefi/alphax-4chan is the reference board with ADC mux
32#endif // ADC_MUX_PIN
33
34#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
35static void slowAdcEndCB(ADCDriver *adcp);
36#endif
37static void slowAdcErrorCB(ADCDriver *, adcerror_t);
38
39/*
40 * ADC conversion group.
41 */
42static const ADCConversionGroup aux1ConvGroup = {
43 .circular = FALSE,
44 .num_channels = adcAux1ChannelCount,
45#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
46 .end_cb = slowAdcEndCB,
47#else
48 .end_cb = nullptr,
49#endif
50 .error_cb = slowAdcErrorCB,
51 /* HW dependent part below */
52 .cr1 = 0,
53 .cr2 = ADC_CR2_SWSTART,
54 // sample times for channels 10...18
55 .smpr1 =
56 ADC_SMPR1_SMP_VBAT(ADC_SAMPLE_144) | /* input18 - temperature and vbat input on some STM32F7xx */
57 ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_144) | /* input16 - temperature sensor input on STM32F4xx */
58 ADC_SMPR1_SMP_VREF(ADC_SAMPLE_144), /* input17 - Vrefint input */
59 .smpr2 = 0,
60 .htr = 0, .ltr = 0,
61 .sqr1 = 0,
62 .sqr2 = 0,
63 .sqr3 =
64#if defined(STM32F4XX)
65 ADC_SQR3_SQ1_N(16) |
66#endif
67#if defined(STM32F7XX)
68 ADC_SQR3_SQ1_N(18) |
69#endif
70 ADC_SQR3_SQ2_N(17),
71};
72
73static const ADCConversionGroup aux2ConvGroup = {
74 .circular = FALSE,
75 .num_channels = adcAux2ChannelCount,
76#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
77 .end_cb = slowAdcEndCB,
78#else
79 .end_cb = nullptr,
80#endif
81 .error_cb = slowAdcErrorCB,
82 /* HW dependent part below */
83 .cr1 = 0,
84 .cr2 = ADC_CR2_SWSTART,
85 // sample times for channels 10...18
86 .smpr1 =
87 ADC_SMPR1_SMP_VBAT(ADC_SAMPLE_144), /* input18 - vbat input on STM32F4xx and STM32F7xx */
88 .smpr2 = 0,
89 .htr = 0, .ltr = 0,
90 .sqr1 = 0,
91 .sqr2 = 0,
92 .sqr3 =
93 ADC_SQR3_SQ1_N(18),
94};
95// 4x oversample is plenty
96static constexpr int auxSensorOversample = 4;
99
101 uint32_t sum = 0;
102 for (size_t i = 0; i < auxSensorOversample; i++) {
104 }
105
106 float volts = (float)sum / (ADC_MAX_VALUE * auxSensorOversample);
107 volts *= engineConfiguration->adcVcc;
108
109 volts -= 0.760f; // Subtract the reference voltage at 25 deg C
110 float degrees = volts / 0.0025f; // Divide by slope 2.5mV
111
112 degrees += 25.0; // Add the 25 deg C
113
114 return degrees;
115}
116
118 uint32_t sum = 0;
119 for (size_t i = 0; i < auxSensorOversample; i++) {
121 }
122
123 // TODO: apply calibration value from OTP (if exists)
124 // vrefint should be 1.21V
125 // Let's calculate external Vref+
126 // sum / (ADC_MAX_VALUE * auxSensorOversample) * Vref+ = 1.21;
127 float Vref = 1.21f * auxSensorOversample * ADC_MAX_VALUE / sum;
128
129 return Vref;
130}
131
133 uint32_t sum = 0;
134 for (size_t i = 0; i < auxSensorOversample; i++) {
136 }
137
138#if defined(STM32F4XX)
139 // VBAT/2 on STM32F40xx and STM32F41xx devices, VBAT/4 on STM32F42xx and STM32F43xx devices
140 int mult = 2;
141 if (isStm32F42x()) {
142 mult = 4;
143 }
144#endif
145#if defined(STM32F7XX)
146 int mult = 4;
147#endif
148
149 float Vbat = (float)sum * mult / (ADC_MAX_VALUE * auxSensorOversample);
151
152 return Vbat;
153}
154
155// See https://github.com/rusefi/rusefi/issues/976 for discussion on these values
156// ... there is no reason to use a longer sampling time than 56 cycles with the current clock ...
157#ifndef ADC_SAMPLING_SLOW
158#define ADC_SAMPLING_SLOW ADC_SAMPLE_56
159#endif
160// see also ADC_SAMPLING_FAST in adc_inputs.cpp
161// ADC clock is 21MHz on F4 and 27MHz on F7
162// We want 500 Hz refresh rate for 16 (32) channels + MCU temperature
163// 21 MHz / 500 = 42000 clocks for all channels including oversampling
164// We want SLOW_ADC_OVERSAMPLE
165// 42000 / 8 / 16 = 328.125 clocks / channel
166// 42000 / 8 / 32 = 164 clocks / channel
167// This ^ does not include additional MCU temperatur conversions
168
169// Slow ADC has 16 channels we can sample, or 32 if ADC mux mode is enabled.
170static volatile NO_CACHE adcsample_t slowSampleBuffer[SLOW_ADC_OVERSAMPLE * adcChannelCount];
171#ifdef ADC_MUX_PIN
172static volatile NO_CACHE adcsample_t slowSampleBufferMuxed[SLOW_ADC_OVERSAMPLE * adcChannelCount];
173#endif
174
175static void slowAdcErrorCB(ADCDriver *, adcerror_t err) {
177 if (err == ADC_ERR_OVERFLOW) {
179 }
180 // TODO: restart?
181}
182
183// Conversion group for slow channels
184// This simply samples every channel in sequence
185static /* constexpr */ ADCConversionGroup convGroupSlow = {
186 .circular = FALSE,
187 .num_channels = adcChannelCount,
188#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
189 .end_cb = slowAdcEndCB,
190#else
191 .end_cb = nullptr,
192#endif
193 .error_cb = slowAdcErrorCB,
194 /* HW dependent part.*/
195 .cr1 = 0,
196 .cr2 = ADC_CR2_SWSTART,
197 // Configure all channels to ADC_SAMPLING_SLOW sample time
198 .smpr1 =
199 ADC_SMPR1_SMP_AN10(ADC_SAMPLING_SLOW) |
200 ADC_SMPR1_SMP_AN11(ADC_SAMPLING_SLOW) |
201 ADC_SMPR1_SMP_AN12(ADC_SAMPLING_SLOW) |
202 ADC_SMPR1_SMP_AN13(ADC_SAMPLING_SLOW) |
203 ADC_SMPR1_SMP_AN14(ADC_SAMPLING_SLOW) |
204 ADC_SMPR1_SMP_AN15(ADC_SAMPLING_SLOW),
205 .smpr2 =
206 ADC_SMPR2_SMP_AN0(ADC_SAMPLING_SLOW) |
207 ADC_SMPR2_SMP_AN1(ADC_SAMPLING_SLOW) |
208 ADC_SMPR2_SMP_AN2(ADC_SAMPLING_SLOW) |
209 ADC_SMPR2_SMP_AN3(ADC_SAMPLING_SLOW) |
210 ADC_SMPR2_SMP_AN4(ADC_SAMPLING_SLOW) |
211 ADC_SMPR2_SMP_AN5(ADC_SAMPLING_SLOW) |
212 ADC_SMPR2_SMP_AN6(ADC_SAMPLING_SLOW) |
213 ADC_SMPR2_SMP_AN7(ADC_SAMPLING_SLOW) |
214 ADC_SMPR2_SMP_AN8(ADC_SAMPLING_SLOW) |
215 ADC_SMPR2_SMP_AN9(ADC_SAMPLING_SLOW),
216 .htr = 0,
217 .ltr = 0,
218 // Simply sequence every channel in order
219 .sqr1 = ADC_SQR1_SQ13_N(12) | ADC_SQR1_SQ14_N(13) | ADC_SQR1_SQ15_N(14) | ADC_SQR1_SQ16_N(15), // Conversion group sequence 13...16
220 .sqr2 = ADC_SQR2_SQ7_N(6) | ADC_SQR2_SQ8_N(7) | ADC_SQR2_SQ9_N(8) | ADC_SQR2_SQ10_N(9) | ADC_SQR2_SQ11_N(10) | ADC_SQR2_SQ12_N(11), // Conversion group sequence 7...12
221 .sqr3 = ADC_SQR3_SQ1_N(0) | ADC_SQR3_SQ2_N(1) | ADC_SQR3_SQ3_N(2) | ADC_SQR3_SQ4_N(3) | ADC_SQR3_SQ5_N(4) | ADC_SQR3_SQ6_N(5), // Conversion group sequence 1...6
222};
223
224#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
225
226typedef enum {
228#ifdef ADC_MUX_PIN
230#endif
234
236{
237 switch (state) {
238 case convertPrimary:
239 #ifdef ADC_MUX_PIN
240 return convertMuxed;
241 #else
242 return convertAux;
243 #endif
244 break;
245#ifdef ADC_MUX_PIN
246 case convertMuxed:
247 return convertAux;
248 break;
249#endif
250 case convertAux:
251 return convertAux2;
252 case convertAux2:
253 return convertPrimary;
254 break;
255 }
256 return convertPrimary;
257}
258
260
261static void slowAdcEndCB(ADCDriver *adcp) {
262 if (adcIsBufferComplete(adcp)) {
263 chSysLockFromISR();
264 // Switch state to ready to allow starting new conversion from here
265 adcp->state = ADC_READY;
266 // get next state
268 switch (slowAdcState) {
269 case convertPrimary:
270 #ifdef ADC_MUX_PIN
271 muxControl.setValue(0, /*force*/true);
272 #endif
273 adcStartConversionI(adcp, &convGroupSlow, (adcsample_t *)slowSampleBuffer, SLOW_ADC_OVERSAMPLE);
274 break;
275 #ifdef ADC_MUX_PIN
276 case convertMuxed:
277 muxControl.setValue(1, /*force*/true);
278 // convert second half
279 adcStartConversionI(adcp, &convGroupSlow, (adcsample_t *)slowSampleBufferMuxed, SLOW_ADC_OVERSAMPLE);
280 break;
281 #endif
282 case convertAux:
283 adcSTM32DisableVBATE();
284 adcStartConversionI(adcp, &aux1ConvGroup, (adcsample_t *)aux1SensorSamples, auxSensorOversample);
285 break;
286 case convertAux2:
287 adcSTM32EnableVBATE();
288 adcStartConversionI(adcp, &aux2ConvGroup, (adcsample_t *)aux2SensorSamples, auxSensorOversample);
289 break;
290 }
291 chSysUnlockFromISR();
292 }
293}
294#endif
295
296static bool readBatch(adcsample_t* convertedSamples, adcsample_t* b) {
297#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
298 msg_t result = adcConvert(&ADCD1, &convGroupSlow, b, SLOW_ADC_OVERSAMPLE);
299
300 // If something went wrong - try again later
301 if (result != MSG_OK) {
302 return false;
303 }
304
305 // Temperature sensor is only physically wired to ADC1
306 adcConvert(&ADCD1, &auxConvGroup, (adcsample_t *)auxSensorSamples, auxSensorOversample);
307
308 // Switch IN18 input to Vbat
309 adcSTM32EnableVBATE();
311 adcSTM32DisableVBATE();
312#endif
313
314 // Average samples to get some noise filtering and oversampling
315 for (size_t i = 0; i < adcChannelCount; i++) {
316 uint32_t sum = 0;
317 size_t index = i;
318 for (size_t j = 0; j < SLOW_ADC_OVERSAMPLE; j++) {
319 sum += b[index];
320 index += adcChannelCount;
321 }
322
323 adcsample_t value = static_cast<adcsample_t>(sum / SLOW_ADC_OVERSAMPLE);
324 convertedSamples[i] = value;
325 }
326
327 return true;
328}
329
330bool readSlowAnalogInputs(adcsample_t* convertedSamples) {
331 bool result = true;
332
333 result &= readBatch(convertedSamples, (adcsample_t *)slowSampleBuffer);
334
335#ifdef ADC_MUX_PIN
336 #if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
337 muxControl.setValue(1, /*force*/true);
338 #endif
339 // read the second batch, starting where we left off
340 result &= readBatch(&convertedSamples[adcChannelCount], (adcsample_t *)slowSampleBufferMuxed);
341 #if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
342 muxControl.setValue(0, /*force*/true);
343 #endif
344#endif
345
346 return result;
347}
348
349#if EFI_USE_FAST_ADC
350
351#include "adc_device.h"
352#include "adc_onchip.h"
353
354extern AdcDevice fastAdc;
355
356// See: https://github.com/rusefi/rusefi/issues/8445
357// We need to disable Slow ADC access to pins that are handled by fast ADC to avoid additional noise
358static void slowAdcEnableDisableChannel(adc_channel_e hwChannel, bool en)
359{
360 if (!isAdcChannelValid(hwChannel)) {
361 return;
362 }
363
364 /* TODO: following is correct for STM32 ADC1/2.
365 * ADC3 has another input to gpio mapping
366 * and should be handled separately */
367 uint32_t channelAdcIndex = hwChannel - EFI_ADC_0;
368 // Switch disabled channel to internal Vrefint channel
369 adcConversionGroupSetSeqInput(&convGroupSlow, channelAdcIndex, en ? channelAdcIndex : 17);
370}
371
373 if (!isAdcChannelValid(hwChannel)) {
374 return invalidAdcToken;
375 }
376
377 // Do not run slow ADC for fast ADC inputs
378 slowAdcEnableDisableChannel(hwChannel, false);
379
380 return fastAdc.getAdcChannelToken(hwChannel);
381}
382
384 if (token == invalidAdcToken) {
385 return 0;
386 }
387
388 return fastAdc.getAdcValueByToken(token);
389}
390
391#endif // EFI_USE_FAST_ADC
392
393#ifdef EFI_SOFTWARE_KNOCK
394
395static void knockCompletionCallback(ADCDriver* adcp) {
396 if (adcIsBufferComplete(adcp)) {
398 }
399
400 assertInterruptPriority(__func__, EFI_IRQ_ADC_PRIORITY);
401}
402
403static void knockErrorCallback(ADCDriver*, adcerror_t) {
404}
405
406static const uint32_t smpr1 =
407 ADC_SMPR1_SMP_AN10(KNOCK_SAMPLE_TIME) |
408 ADC_SMPR1_SMP_AN11(KNOCK_SAMPLE_TIME) |
409 ADC_SMPR1_SMP_AN12(KNOCK_SAMPLE_TIME) |
410 ADC_SMPR1_SMP_AN13(KNOCK_SAMPLE_TIME) |
411 ADC_SMPR1_SMP_AN14(KNOCK_SAMPLE_TIME) |
412 ADC_SMPR1_SMP_AN15(KNOCK_SAMPLE_TIME);
413
414static const uint32_t smpr2 =
415 ADC_SMPR2_SMP_AN0(KNOCK_SAMPLE_TIME) |
416 ADC_SMPR2_SMP_AN1(KNOCK_SAMPLE_TIME) |
417 ADC_SMPR2_SMP_AN2(KNOCK_SAMPLE_TIME) |
418 ADC_SMPR2_SMP_AN3(KNOCK_SAMPLE_TIME) |
419 ADC_SMPR2_SMP_AN4(KNOCK_SAMPLE_TIME) |
420 ADC_SMPR2_SMP_AN5(KNOCK_SAMPLE_TIME) |
421 ADC_SMPR2_SMP_AN6(KNOCK_SAMPLE_TIME) |
422 ADC_SMPR2_SMP_AN7(KNOCK_SAMPLE_TIME) |
423 ADC_SMPR2_SMP_AN8(KNOCK_SAMPLE_TIME) |
424 ADC_SMPR2_SMP_AN9(KNOCK_SAMPLE_TIME);
425
426static const ADCConversionGroup adcConvGroupCh1 = {
427 .circular = FALSE,
428 .num_channels = 1,
429 .end_cb = &knockCompletionCallback,
430 .error_cb = &knockErrorCallback,
431 .cr1 = 0,
432 .cr2 = ADC_CR2_SWSTART,
433 // sample times for channels 10...18
434 .smpr1 = smpr1,
435 // sample times for channels 0...9
436 .smpr2 = smpr2,
437
438 .htr = 0,
439 .ltr = 0,
440
441 .sqr1 = 0,
442 .sqr2 = 0,
443 .sqr3 = ADC_SQR3_SQ1_N(KNOCK_ADC_CH1)
444};
445
446// Not all boards have a second channel - configure it if it exists
447#if KNOCK_HAS_CH2
448static const ADCConversionGroup adcConvGroupCh2 = {
449 .circular = FALSE,
450 .num_channels = 1,
451 .end_cb = &knockCompletionCallback,
452 .error_cb = &knockErrorCallback,
453 .cr1 = 0,
454 .cr2 = ADC_CR2_SWSTART,
455 // sample times for channels 10...18
456 .smpr1 = smpr1,
457 // sample times for channels 0...9
458 .smpr2 = smpr2,
459
460 .htr = 0,
461 .ltr = 0,
462
463 .sqr1 = 0,
464 .sqr2 = 0,
465 .sqr3 = ADC_SQR3_SQ1_N(KNOCK_ADC_CH2)
466};
467#endif // KNOCK_HAS_CH2
468
469const ADCConversionGroup* getKnockConversionGroup(uint8_t channelIdx) {
470#if KNOCK_HAS_CH2
471 if (channelIdx == 1) {
472 return &adcConvGroupCh2;
473 }
474#else
475 (void)channelIdx;
476#endif // KNOCK_HAS_CH2
477
478 return &adcConvGroupCh1;
479}
480
481#endif // EFI_SOFTWARE_KNOCK
482
484#ifdef ADC_MUX_PIN
485 muxControl.initPin("ADC Mux", ADC_MUX_PIN);
486#endif //ADC_MUX_PIN
487
488 // Init slow ADC
489 adcStart(&ADCD1, NULL);
490
491 // Enable internal temperature reference
492 adcSTM32EnableTSVREFE(); // Internal temperature sensor
493
494#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
495 adcStartConversion(&ADCD1, &convGroupSlow, (adcsample_t *)slowSampleBuffer, SLOW_ADC_OVERSAMPLE);
496#endif
497
498#if EFI_USE_FAST_ADC
499 // Init fast ADC (MAP sensor)
500 adcStart(&ADCD2, NULL);
501#endif
502
503#if defined(STM32F7XX)
504 /* the temperature sensor is internally
505 * connected to the same input channel as VBAT. Only one conversion,
506 * temperature sensor or VBAT, must be selected at a time. */
507 adcSTM32DisableVBATE();
508#endif
509
510 /* Enable this code only when you absolutly sure
511 * that there is no possible errors from ADC */
512#if 0
513 /* All ADC use DMA and DMA calls end_cb from its IRQ
514 * If none of ADC users need error callback - we can disable
515 * shared ADC IRQ and save some CPU ticks */
516 if ((adcgrpcfgSlow.error_cb == NULL) &&
517 (adcgrpcfgFast.error_cb == NULL)
518 /* TODO: Add ADC3? */) {
519 nvicDisableVector(STM32_ADC_NUMBER);
520 }
521#endif
522
523#ifdef EFI_SOFTWARE_KNOCK
524 adcStart(&KNOCK_ADC, nullptr);
525#endif // EFI_SOFTWARE_KNOCK
526}
527
528#endif // HAL_USE_ADC
static constexpr AdcToken invalidAdcToken
Definition adc_inputs.h:110
bool isAdcChannelValid(adc_channel_e hwChannel)
Definition adc_inputs.h:23
uint32_t AdcToken
Definition adc_inputs.h:98
int adcConversionGroupSetSeqInput(ADCConversionGroup *cfg, size_t sqn, size_t input)
Low level ChibiOS ADC helpers header.
static ADCConversionGroup adcgrpcfgFast
bool isStm32F42x(void)
AdcToken getAdcChannelToken(adc_channel_e hwChannel)
adcsample_t getAdcValueByToken(AdcToken token)
Definition adc_device.h:20
TunerStudioOutputChannels outputChannels
Definition engine.h:113
Single output pin reference and state.
Definition efi_output.h:49
void initPin(const char *msg, brain_pin_e brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError=false)
Definition efi_gpio.cpp:711
void setValue(const char *msg, int logicValue, bool isForce=false)
Definition efi_gpio.cpp:604
static EngineAccessor engine
Definition engine.h:421
static constexpr engine_configuration_s * engineConfiguration
adcerror_t
Possible ADC failure causes.
ADCDriver ADCD2
ADC2 driver identifier.
Definition hal_adc_lld.c:49
uint16_t adcsample_t
ADC sample data type.
ADCDriver ADCD1
ADC1 driver identifier.
Definition hal_adc_lld.c:44
@ ADC_ERR_OVERFLOW
float uint8_t channelIdx
static union @47 NO_CACHE
void assertInterruptPriority(const char *func, uint8_t expectedPrio)
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1886, 1.0, -1.0, -1.0, "")
void onKnockSamplingComplete()
const ADCConversionGroup * getKnockConversionGroup(uint8_t channelIdx)
static const uint32_t smpr1
float getMcuVbatVoltage()
bool readSlowAnalogInputs(adcsample_t *convertedSamples)
static ADCConversionGroup convGroupSlow
static volatile NO_CACHE adcsample_t aux2SensorSamples[adcAux2ChannelCount *auxSensorOversample]
static const ADCConversionGroup aux2ConvGroup
constexpr size_t adcAux1ChannelCount
static void slowAdcEndCB(ADCDriver *adcp)
AdcToken enableFastAdcChannel(const char *, adc_channel_e hwChannel)
static constexpr int auxSensorOversample
static void slowAdcErrorCB(ADCDriver *, adcerror_t)
static volatile NO_CACHE adcsample_t aux1SensorSamples[adcAux1ChannelCount *auxSensorOversample]
adcsample_t getFastAdc(AdcToken token)
static bool readBatch(adcsample_t *convertedSamples, adcsample_t *b)
static const ADCConversionGroup aux1ConvGroup
static const ADCConversionGroup adcConvGroupCh2
static void knockCompletionCallback(ADCDriver *adcp)
static void slowAdcEnableDisableChannel(adc_channel_e hwChannel, bool en)
constexpr size_t adcAux2ChannelCount
static const uint32_t smpr2
slowAdcState_t
@ convertPrimary
@ convertAux2
@ convertMuxed
@ convertAux
static slowAdcState_t slowAdcState
static volatile NO_CACHE adcsample_t slowSampleBufferMuxed[SLOW_ADC_OVERSAMPLE *adcChannelCount]
static OutputPin muxControl
static slowAdcState_t slowAdcGetNextState(slowAdcState_t state)
float getMcuVrefVoltage()
constexpr size_t adcChannelCount
static volatile NO_CACHE adcsample_t slowSampleBuffer[SLOW_ADC_OVERSAMPLE *adcChannelCount]
static void knockErrorCallback(ADCDriver *, adcerror_t)
float getMcuTemperature()
void portInitAdc()
AdcDevice fastAdc
static const ADCConversionGroup adcConvGroupCh1