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 adcAuxChannelCount = 2;
20
21/* Depth of the conversion buffer, channels are sampled X times each.*/
22#define SLOW_ADC_OVERSAMPLE 8
23
24#ifndef EFI_INTERNAL_SLOW_ADC_BACKGROUND
25#define EFI_INTERNAL_SLOW_ADC_BACKGROUND FALSE
26#endif
27
28#ifdef ADC_MUX_PIN
29// https://github.com/rusefi/alphax-4chan is the reference board with ADC mux
31#endif // ADC_MUX_PIN
32
33#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
34static void slowAdcEndCB(ADCDriver *adcp);
35#endif
36static void slowAdcErrorCB(ADCDriver *, adcerror_t);
37
38/*
39 * ADC conversion group.
40 */
41static const ADCConversionGroup auxConvGroup = {
42 .circular = FALSE,
43 .num_channels = adcAuxChannelCount,
44#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
45 .end_cb = slowAdcEndCB,
46#else
47 .end_cb = nullptr,
48#endif
49 .error_cb = slowAdcErrorCB,
50 /* HW dependent part below */
51 .cr1 = 0,
52 .cr2 = ADC_CR2_SWSTART,
53 // sample times for channels 10...18
54 .smpr1 =
55 ADC_SMPR1_SMP_VBAT(ADC_SAMPLE_144) | /* input18 - temperature and vbat input on some STM32F7xx */
56 ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_144) | /* input16 - temperature sensor input on STM32F4xx */
57 ADC_SMPR1_SMP_VREF(ADC_SAMPLE_144), /* input17 - Vrefint input */
58 .smpr2 = 0,
59 .htr = 0, .ltr = 0,
60 .sqr1 = 0,
61 .sqr2 = 0,
62 .sqr3 =
63#if defined(STM32F4XX)
64 ADC_SQR3_SQ1_N(16) |
65#endif
66#if defined(STM32F7XX)
67 ADC_SQR3_SQ1_N(18) |
68#endif
69 ADC_SQR3_SQ2_N(17),
70};
71
72// 4x oversample is plenty
73static constexpr int auxSensorOversample = 4;
75
77#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
78 // Temperature sensor is only physically wired to ADC1
80#endif
81
82 uint32_t sum = 0;
83 for (size_t i = 0; i < auxSensorOversample; i++) {
85 }
86
87 float volts = (float)sum / (ADC_MAX_VALUE * auxSensorOversample);
89
90 volts -= 0.760f; // Subtract the reference voltage at 25 deg C
91 float degrees = volts / 0.0025f; // Divide by slope 2.5mV
92
93 degrees += 25.0; // Add the 25 deg C
94
95 return degrees;
96}
97
99 uint32_t sum = 0;
100 for (size_t i = 0; i < auxSensorOversample; i++) {
101 sum += auxSensorSamples[1 + adcAuxChannelCount * i];
102 }
103
104 // TODO: apply calibration value from OTP (if exists)
105 // vrefint should be 1.21V
106 // Let's calculate external Vref+
107 // sum / (ADC_MAX_VALUE * auxSensorOversample) * Vref+ = 1.21;
108 float Vref = 1.21f * auxSensorOversample * ADC_MAX_VALUE / sum;
109
110 return Vref;
111}
112
113// See https://github.com/rusefi/rusefi/issues/976 for discussion on these values
114// ... there is no reason to use a longer sampling time than 56 cycles with the current clock ...
115#ifndef ADC_SAMPLING_SLOW
116#define ADC_SAMPLING_SLOW ADC_SAMPLE_56
117#endif
118// see also ADC_SAMPLING_FAST in adc_inputs.cpp
119// ADC clock is 21MHz on F4 and 27MHz on F7
120// We want 500 Hz refresh rate for 16 (32) channels + MCU temperature
121// 21 MHz / 500 = 42000 clocks for all channels including oversampling
122// We want SLOW_ADC_OVERSAMPLE
123// 42000 / 8 / 16 = 328.125 clocks / channel
124// 42000 / 8 / 32 = 164 clocks / channel
125// This ^ does not include additional MCU temperatur conversions
126
127// Slow ADC has 16 channels we can sample, or 32 if ADC mux mode is enabled.
128static volatile NO_CACHE adcsample_t slowSampleBuffer[SLOW_ADC_OVERSAMPLE * adcChannelCount];
129#ifdef ADC_MUX_PIN
130static volatile NO_CACHE adcsample_t slowSampleBufferMuxed[SLOW_ADC_OVERSAMPLE * adcChannelCount];
131#endif
132
133static void slowAdcErrorCB(ADCDriver *, adcerror_t err) {
135 if (err == ADC_ERR_OVERFLOW) {
137 }
138 // TODO: restart?
139}
140
141// Conversion group for slow channels
142// This simply samples every channel in sequence
143static /* constexpr */ ADCConversionGroup convGroupSlow = {
144 .circular = FALSE,
145 .num_channels = adcChannelCount,
146#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
147 .end_cb = slowAdcEndCB,
148#else
149 .end_cb = nullptr,
150#endif
151 .error_cb = slowAdcErrorCB,
152 /* HW dependent part.*/
153 .cr1 = 0,
154 .cr2 = ADC_CR2_SWSTART,
155 // Configure all channels to ADC_SAMPLING_SLOW sample time
156 .smpr1 =
157 ADC_SMPR1_SMP_AN10(ADC_SAMPLING_SLOW) |
158 ADC_SMPR1_SMP_AN11(ADC_SAMPLING_SLOW) |
159 ADC_SMPR1_SMP_AN12(ADC_SAMPLING_SLOW) |
160 ADC_SMPR1_SMP_AN13(ADC_SAMPLING_SLOW) |
161 ADC_SMPR1_SMP_AN14(ADC_SAMPLING_SLOW) |
162 ADC_SMPR1_SMP_AN15(ADC_SAMPLING_SLOW),
163 .smpr2 =
164 ADC_SMPR2_SMP_AN0(ADC_SAMPLING_SLOW) |
165 ADC_SMPR2_SMP_AN1(ADC_SAMPLING_SLOW) |
166 ADC_SMPR2_SMP_AN2(ADC_SAMPLING_SLOW) |
167 ADC_SMPR2_SMP_AN3(ADC_SAMPLING_SLOW) |
168 ADC_SMPR2_SMP_AN4(ADC_SAMPLING_SLOW) |
169 ADC_SMPR2_SMP_AN5(ADC_SAMPLING_SLOW) |
170 ADC_SMPR2_SMP_AN6(ADC_SAMPLING_SLOW) |
171 ADC_SMPR2_SMP_AN7(ADC_SAMPLING_SLOW) |
172 ADC_SMPR2_SMP_AN8(ADC_SAMPLING_SLOW) |
173 ADC_SMPR2_SMP_AN9(ADC_SAMPLING_SLOW),
174 .htr = 0,
175 .ltr = 0,
176 // Simply sequence every channel in order
177 .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
178 .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
179 .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
180};
181
182#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
183
184typedef enum {
186#ifdef ADC_MUX_PIN
188#endif
191
193{
194 switch (state) {
195 case convertPrimary:
196 #ifdef ADC_MUX_PIN
197 return convertMuxed;
198 #else
199 return convertAux;
200 #endif
201 break;
202#ifdef ADC_MUX_PIN
203 case convertMuxed:
204 return convertAux;
205 break;
206#endif
207 case convertAux:
208 return convertPrimary;
209 break;
210 }
211 return convertPrimary;
212}
213
215
216static void slowAdcEndCB(ADCDriver *adcp) {
217 if (adcIsBufferComplete(adcp)) {
218 chSysLockFromISR();
219 // Switch state to ready to allow starting new conversion from here
220 adcp->state = ADC_READY;
221 // get next state
223 switch (slowAdcState) {
224 case convertPrimary:
225 #ifdef ADC_MUX_PIN
226 muxControl.setValue(0, /*force*/true);
227 #endif
228 adcStartConversionI(adcp, &convGroupSlow, (adcsample_t *)slowSampleBuffer, SLOW_ADC_OVERSAMPLE);
229 break;
230 #ifdef ADC_MUX_PIN
231 case convertMuxed:
232 muxControl.setValue(1, /*force*/true);
233 // convert second half
234 adcStartConversionI(adcp, &convGroupSlow, (adcsample_t *)slowSampleBufferMuxed, SLOW_ADC_OVERSAMPLE);
235 break;
236 #endif
237 case convertAux:
238 adcStartConversionI(adcp, &auxConvGroup, (adcsample_t *)auxSensorSamples, auxSensorOversample);
239 break;
240 }
241 chSysUnlockFromISR();
242 }
243}
244#endif
245
246static bool readBatch(adcsample_t* convertedSamples, adcsample_t* b) {
247#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
248 msg_t result = adcConvert(&ADCD1, &convGroupSlow, b, SLOW_ADC_OVERSAMPLE);
249
250 // If something went wrong - try again later
251 if (result != MSG_OK) {
252 return false;
253 }
254#endif
255
256 // Average samples to get some noise filtering and oversampling
257 for (size_t i = 0; i < adcChannelCount; i++) {
258 uint32_t sum = 0;
259 size_t index = i;
260 for (size_t j = 0; j < SLOW_ADC_OVERSAMPLE; j++) {
261 sum += b[index];
262 index += adcChannelCount;
263 }
264
265 adcsample_t value = static_cast<adcsample_t>(sum / SLOW_ADC_OVERSAMPLE);
266 convertedSamples[i] = value;
267 }
268
269 return true;
270}
271
272bool readSlowAnalogInputs(adcsample_t* convertedSamples) {
273 bool result = true;
274
275 result &= readBatch(convertedSamples, (adcsample_t *)slowSampleBuffer);
276
277#ifdef ADC_MUX_PIN
278 #if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
279 muxControl.setValue(1, /*force*/true);
280 #endif
281 // read the second batch, starting where we left off
282 result &= readBatch(&convertedSamples[adcChannelCount], (adcsample_t *)slowSampleBufferMuxed);
283 #if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == FALSE)
284 muxControl.setValue(0, /*force*/true);
285 #endif
286#endif
287
288 return result;
289}
290
291#if EFI_USE_FAST_ADC
292
293#include "adc_device.h"
294#include "adc_onchip.h"
295
296extern AdcDevice fastAdc;
297
298// See: https://github.com/rusefi/rusefi/issues/8445
299// We need to disable Slow ADC access to pins that are handled by fast ADC to avoid additional noise
300static void slowAdcEnableDisableChannel(adc_channel_e hwChannel, bool en)
301{
302 if (!isAdcChannelValid(hwChannel)) {
303 return;
304 }
305
306 /* TODO: following is correct for STM32 ADC1/2.
307 * ADC3 has another input to gpio mapping
308 * and should be handled separately */
309 uint32_t channelAdcIndex = hwChannel - EFI_ADC_0;
310 // Switch disabled channel to internal Vrefint channel
311 adcConversionGroupSetSeqInput(&convGroupSlow, channelAdcIndex, en ? channelAdcIndex : 17);
312}
313
315 if (!isAdcChannelValid(hwChannel)) {
316 return invalidAdcToken;
317 }
318
319 // Do not run slow ADC for fast ADC inputs
320 slowAdcEnableDisableChannel(hwChannel, false);
321
322 return fastAdc.getAdcChannelToken(hwChannel);
323}
324
326 if (token == invalidAdcToken) {
327 return 0;
328 }
329
330 return fastAdc.getAdcValueByToken(token);
331}
332
333#endif // EFI_USE_FAST_ADC
334
335#ifdef EFI_SOFTWARE_KNOCK
336
337static void knockCompletionCallback(ADCDriver* adcp) {
338 if (adcIsBufferComplete(adcp)) {
340 }
341}
342
343static void knockErrorCallback(ADCDriver*, adcerror_t) {
344}
345
346static const uint32_t smpr1 =
347 ADC_SMPR1_SMP_AN10(KNOCK_SAMPLE_TIME) |
348 ADC_SMPR1_SMP_AN11(KNOCK_SAMPLE_TIME) |
349 ADC_SMPR1_SMP_AN12(KNOCK_SAMPLE_TIME) |
350 ADC_SMPR1_SMP_AN13(KNOCK_SAMPLE_TIME) |
351 ADC_SMPR1_SMP_AN14(KNOCK_SAMPLE_TIME) |
352 ADC_SMPR1_SMP_AN15(KNOCK_SAMPLE_TIME);
353
354static const uint32_t smpr2 =
355 ADC_SMPR2_SMP_AN0(KNOCK_SAMPLE_TIME) |
356 ADC_SMPR2_SMP_AN1(KNOCK_SAMPLE_TIME) |
357 ADC_SMPR2_SMP_AN2(KNOCK_SAMPLE_TIME) |
358 ADC_SMPR2_SMP_AN3(KNOCK_SAMPLE_TIME) |
359 ADC_SMPR2_SMP_AN4(KNOCK_SAMPLE_TIME) |
360 ADC_SMPR2_SMP_AN5(KNOCK_SAMPLE_TIME) |
361 ADC_SMPR2_SMP_AN6(KNOCK_SAMPLE_TIME) |
362 ADC_SMPR2_SMP_AN7(KNOCK_SAMPLE_TIME) |
363 ADC_SMPR2_SMP_AN8(KNOCK_SAMPLE_TIME) |
364 ADC_SMPR2_SMP_AN9(KNOCK_SAMPLE_TIME);
365
366static const ADCConversionGroup adcConvGroupCh1 = {
367 .circular = FALSE,
368 .num_channels = 1,
369 .end_cb = &knockCompletionCallback,
370 .error_cb = &knockErrorCallback,
371 .cr1 = 0,
372 .cr2 = ADC_CR2_SWSTART,
373 // sample times for channels 10...18
374 .smpr1 = smpr1,
375 // sample times for channels 0...9
376 .smpr2 = smpr2,
377
378 .htr = 0,
379 .ltr = 0,
380
381 .sqr1 = 0,
382 .sqr2 = 0,
383 .sqr3 = ADC_SQR3_SQ1_N(KNOCK_ADC_CH1)
384};
385
386// Not all boards have a second channel - configure it if it exists
387#if KNOCK_HAS_CH2
388static const ADCConversionGroup adcConvGroupCh2 = {
389 .circular = FALSE,
390 .num_channels = 1,
391 .end_cb = &knockCompletionCallback,
392 .error_cb = &knockErrorCallback,
393 .cr1 = 0,
394 .cr2 = ADC_CR2_SWSTART,
395 // sample times for channels 10...18
396 .smpr1 = smpr1,
397 // sample times for channels 0...9
398 .smpr2 = smpr2,
399
400 .htr = 0,
401 .ltr = 0,
402
403 .sqr1 = 0,
404 .sqr2 = 0,
405 .sqr3 = ADC_SQR3_SQ1_N(KNOCK_ADC_CH2)
406};
407#endif // KNOCK_HAS_CH2
408
409const ADCConversionGroup* getKnockConversionGroup(uint8_t channelIdx) {
410#if KNOCK_HAS_CH2
411 if (channelIdx == 1) {
412 return &adcConvGroupCh2;
413 }
414#else
415 (void)channelIdx;
416#endif // KNOCK_HAS_CH2
417
418 return &adcConvGroupCh1;
419}
420
421#endif // EFI_SOFTWARE_KNOCK
422
424#ifdef ADC_MUX_PIN
425 muxControl.initPin("ADC Mux", ADC_MUX_PIN);
426#endif //ADC_MUX_PIN
427
428 // Init slow ADC
429 adcStart(&ADCD1, NULL);
430
431 // Enable internal temperature reference
432 adcSTM32EnableTSVREFE(); // Internal temperature sensor
433
434#if (EFI_INTERNAL_SLOW_ADC_BACKGROUND == TRUE)
435 adcStartConversion(&ADCD1, &convGroupSlow, (adcsample_t *)slowSampleBuffer, SLOW_ADC_OVERSAMPLE);
436#endif
437
438#if EFI_USE_FAST_ADC
439 // Init fast ADC (MAP sensor)
440 adcStart(&ADCD2, NULL);
441#endif
442
443#if defined(STM32F7XX)
444 /* the temperature sensor is internally
445 * connected to the same input channel as VBAT. Only one conversion,
446 * temperature sensor or VBAT, must be selected at a time. */
447 adcSTM32DisableVBATE();
448#endif
449
450 /* Enable this code only when you absolutly sure
451 * that there is no possible errors from ADC */
452#if 0
453 /* All ADC use DMA and DMA calls end_cb from its IRQ
454 * If none of ADC users need error callback - we can disable
455 * shared ADC IRQ and save some CPU ticks */
456 if ((adcgrpcfgSlow.error_cb == NULL) &&
457 (adcgrpcfgFast.error_cb == NULL)
458 /* TODO: Add ADC3? */) {
459 nvicDisableVector(STM32_ADC_NUMBER);
460 }
461#endif
462
463#ifdef EFI_SOFTWARE_KNOCK
464 adcStart(&KNOCK_ADC, nullptr);
465#endif // EFI_SOFTWARE_KNOCK
466}
467
468#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
AdcToken getAdcChannelToken(adc_channel_e hwChannel)
adcsample_t getAdcValueByToken(AdcToken token)
Definition adc_device.h:20
TunerStudioOutputChannels outputChannels
Definition engine.h:109
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:413
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
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1871, 1.0, -1.0, -1.0, "")
void onKnockSamplingComplete()
const ADCConversionGroup * getKnockConversionGroup(uint8_t channelIdx)
static const uint32_t smpr1
bool readSlowAnalogInputs(adcsample_t *convertedSamples)
static ADCConversionGroup convGroupSlow
static void slowAdcEndCB(ADCDriver *adcp)
AdcToken enableFastAdcChannel(const char *, adc_channel_e hwChannel)
static constexpr int auxSensorOversample
static void slowAdcErrorCB(ADCDriver *, adcerror_t)
adcsample_t getFastAdc(AdcToken token)
static const ADCConversionGroup auxConvGroup
static bool readBatch(adcsample_t *convertedSamples, adcsample_t *b)
static const ADCConversionGroup adcConvGroupCh2
static void knockCompletionCallback(ADCDriver *adcp)
static void slowAdcEnableDisableChannel(adc_channel_e hwChannel, bool en)
constexpr size_t adcAuxChannelCount
static const uint32_t smpr2
slowAdcState_t
@ convertPrimary
@ 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)
static volatile NO_CACHE adcsample_t auxSensorSamples[adcAuxChannelCount *auxSensorOversample]
float getMcuTemperature()
void portInitAdc()
AdcDevice fastAdc
static const ADCConversionGroup adcConvGroupCh1