From 96a14fd64970c23d4f9e4010585e99e4b255917a Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Mon, 8 Mar 2021 11:50:50 -0800 Subject: [PATCH] ADC cleanup, enable oversampling on H7 (#2437) * cleanup v2 adc * simplify * oversample on h7 * output 16b result * port-ify ADC_MAX_VALUE * simplify include * guard for cypress * make sim/tests happy * 16x oversample + comment * this check is uesless Co-authored-by: Matthew Kennedy --- firmware/hw_layer/adc/adc_inputs.cpp | 16 ++++------ firmware/hw_layer/adc/adc_inputs.h | 13 -------- firmware/hw_layer/algo/adc_math.h | 6 +++- .../hw_layer/ports/cypress/port_mpu_util.h | 2 ++ .../hw_layer/ports/kinetis/port_mpu_util.h | 2 ++ .../hw_layer/ports/stm32/stm32_adc_v2.cpp | 14 ++++----- .../hw_layer/ports/stm32/stm32_adc_v4.cpp | 30 ++++++++----------- .../ports/stm32/stm32f4/device_mpu_util.h | 1 + .../ports/stm32/stm32f7/device_mpu_util.h | 2 ++ .../ports/stm32/stm32h7/device_mpu_util.h | 2 ++ 10 files changed, 38 insertions(+), 50 deletions(-) diff --git a/firmware/hw_layer/adc/adc_inputs.cpp b/firmware/hw_layer/adc/adc_inputs.cpp index b9da15549f..75ded2ab24 100644 --- a/firmware/hw_layer/adc/adc_inputs.cpp +++ b/firmware/hw_layer/adc/adc_inputs.cpp @@ -38,7 +38,12 @@ #include "perf_trace.h" #include "thread_priority.h" -static adcsample_t slowAdcSamples[ADC_MAX_CHANNELS_COUNT]; +/* Depth of the conversion buffer, channels are sampled X times each.*/ +#ifndef ADC_BUF_DEPTH_FAST +#define ADC_BUF_DEPTH_FAST 4 +#endif + +static NO_CACHE adcsample_t slowAdcSamples[ADC_MAX_CHANNELS_COUNT]; static NO_CACHE adcsample_t fastAdcSampleBuf[ADC_BUF_DEPTH_FAST * ADC_MAX_CHANNELS_COUNT]; static adc_channel_mode_e adcHwChannelEnabled[HW_MAX_ADC_INDEX]; @@ -217,11 +222,6 @@ int getInternalAdcValue(const char *msg, adc_channel_e hwChannel) { } #endif // EFI_USE_FAST_ADC - if (adcHwChannelEnabled[hwChannel] != ADC_SLOW) { - // todo: make this not happen during hardware continuous integration - warning(CUSTOM_OBD_WRONG_ADC_MODE, "ADC is off [%s] index=%d", msg, hwChannel); - } - return slowAdcSamples[hwChannel - 1]; } @@ -517,10 +517,6 @@ static SlowAdcController slowAdcController; void initAdcInputs() { scheduleMsg(&logger, "initAdcInputs()"); - if (ADC_BUF_DEPTH_FAST > MAX_ADC_GRP_BUF_DEPTH) - firmwareError(CUSTOM_ERR_ADC_DEPTH_FAST, "ADC_BUF_DEPTH_FAST too high"); - if (ADC_BUF_DEPTH_SLOW > MAX_ADC_GRP_BUF_DEPTH) - firmwareError(CUSTOM_ERR_ADC_DEPTH_SLOW, "ADC_BUF_DEPTH_SLOW too high"); configureInputs(); diff --git a/firmware/hw_layer/adc/adc_inputs.h b/firmware/hw_layer/adc/adc_inputs.h index ec0e171e9a..860dd7f197 100644 --- a/firmware/hw_layer/adc/adc_inputs.h +++ b/firmware/hw_layer/adc/adc_inputs.h @@ -56,19 +56,6 @@ float getMCUInternalTemperature(void); void addChannel(const char *name, adc_channel_e setting, adc_channel_mode_e mode); void removeChannel(const char *name, adc_channel_e setting); -/* Depth of the conversion buffer, channels are sampled X times each.*/ -#ifndef ADC_BUF_DEPTH_SLOW -#define ADC_BUF_DEPTH_SLOW 8 -#endif /* ADC_BUF_DEPTH_SLOW */ - -#ifndef ADC_BUF_DEPTH_FAST -#define ADC_BUF_DEPTH_FAST 4 -#endif /* ADC_BUF_DEPTH_FAST */ - -// todo: preprocessor way of doing 'max'? -// max(ADC_BUF_DEPTH_SLOW, ADC_BUF_DEPTH_FAST) -#define MAX_ADC_GRP_BUF_DEPTH 8 - #define getAdcValue(msg, hwChannel) getInternalAdcValue(msg, hwChannel) #define adcToVoltsDivided(adc) (adcToVolts(adc) * engineConfiguration->analogInputDividerCoefficient) diff --git a/firmware/hw_layer/algo/adc_math.h b/firmware/hw_layer/algo/adc_math.h index 3e1bd72a95..27ea354324 100644 --- a/firmware/hw_layer/algo/adc_math.h +++ b/firmware/hw_layer/algo/adc_math.h @@ -10,8 +10,12 @@ #pragma once -#include "engine_configuration.h" +#if EFI_PROD_CODE +#include "port_mpu_util.h" +#include "rusefi_hw_enums.h" +#else // not EFI_PROD_CODE #define ADC_MAX_VALUE 4095 +#endif #define adcToVolts(adc) ((engineConfiguration->adcVcc) / ADC_MAX_VALUE * (adc)) diff --git a/firmware/hw_layer/ports/cypress/port_mpu_util.h b/firmware/hw_layer/ports/cypress/port_mpu_util.h index 51eb8fe30e..be8f891b7f 100644 --- a/firmware/hw_layer/ports/cypress/port_mpu_util.h +++ b/firmware/hw_layer/ports/cypress/port_mpu_util.h @@ -46,3 +46,5 @@ typedef enum { // TODO #define SPI_CR1_24BIT_MODE 0 #define SPI_CR2_24BIT_MODE 0 + +#define ADC_MAX_VALUE 4095 diff --git a/firmware/hw_layer/ports/kinetis/port_mpu_util.h b/firmware/hw_layer/ports/kinetis/port_mpu_util.h index cc62dc3ed9..5b83db8d7e 100644 --- a/firmware/hw_layer/ports/kinetis/port_mpu_util.h +++ b/firmware/hw_layer/ports/kinetis/port_mpu_util.h @@ -34,3 +34,5 @@ typedef enum { // TODO #define SPI_CR1_24BIT_MODE 0 #define SPI_CR2_24BIT_MODE 0 + +#define ADC_MAX_VALUE 4095 diff --git a/firmware/hw_layer/ports/stm32/stm32_adc_v2.cpp b/firmware/hw_layer/ports/stm32/stm32_adc_v2.cpp index e08e43bfbb..4c8d39fba9 100644 --- a/firmware/hw_layer/ports/stm32/stm32_adc_v2.cpp +++ b/firmware/hw_layer/ports/stm32/stm32_adc_v2.cpp @@ -14,9 +14,7 @@ EXTERN_CONFIG; /* Depth of the conversion buffer, channels are sampled X times each.*/ -#ifndef ADC_BUF_DEPTH_SLOW -#define ADC_BUF_DEPTH_SLOW 8 -#endif /* ADC_BUF_DEPTH_SLOW */ +#define SLOW_ADC_OVERSAMPLE 8 void portInitAdc() { // Init slow ADC @@ -150,13 +148,13 @@ static constexpr ADCConversionGroup convGroupSlow = { .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 }; -static NO_CACHE adcsample_t slowSampleBuffer[ADC_BUF_DEPTH_SLOW * slowChannelCount]; +static NO_CACHE adcsample_t slowSampleBuffer[SLOW_ADC_OVERSAMPLE * slowChannelCount]; bool readSlowAnalogInputs(adcsample_t* convertedSamples) { - msg_t result = adcConvert(&ADCD1, &convGroupSlow, slowSampleBuffer, ADC_BUF_DEPTH_SLOW); + msg_t result = adcConvert(&ADCD1, &convGroupSlow, slowSampleBuffer, SLOW_ADC_OVERSAMPLE); // If something went wrong - try again later - if (result == MSG_RESET || result == MSG_TIMEOUT) { + if (result != MSG_OK) { return false; } @@ -164,12 +162,12 @@ bool readSlowAnalogInputs(adcsample_t* convertedSamples) { for (int i = 0; i < slowChannelCount; i++) { uint32_t sum = 0; size_t index = i; - for (size_t j = 0; j < ADC_BUF_DEPTH_SLOW; j++) { + for (size_t j = 0; j < SLOW_ADC_OVERSAMPLE; j++) { sum += slowSampleBuffer[index]; index += slowChannelCount; } - adcsample_t value = static_cast(sum / ADC_BUF_DEPTH_SLOW); + adcsample_t value = static_cast(sum / SLOW_ADC_OVERSAMPLE); convertedSamples[i] = value; } diff --git a/firmware/hw_layer/ports/stm32/stm32_adc_v4.cpp b/firmware/hw_layer/ports/stm32/stm32_adc_v4.cpp index 7636824aa4..003798e13c 100644 --- a/firmware/hw_layer/ports/stm32/stm32_adc_v4.cpp +++ b/firmware/hw_layer/ports/stm32/stm32_adc_v4.cpp @@ -24,8 +24,11 @@ float getMcuTemperature() { return 0; } -// TODO: use a define instead of magic number -#define ADC_SAMPLING_SLOW (7) +// ADC Clock is 25MHz +// 32.5 sampling + 8.5 conversion = 41 cycles per sample total +// 16 channels * 16x oversample = 256 samples per batch +// (41 * 256) / 25MHz -> 419 microseconds to sample all channels +#define ADC_SAMPLING_SLOW ADC_SMPR_SMP_32P5 // Sample the 16 channels that line up with the STM32F4/F7 constexpr size_t slowChannelCount = 16; @@ -38,7 +41,9 @@ static constexpr ADCConversionGroup convGroupSlow = { .end_cb = nullptr, .error_cb = nullptr, .cfgr = 0, - .cfgr2 = 0, // no oversampling (yet) + .cfgr2 = 16 << ADC_CFGR2_OVSR_Pos | // Oversample by 16x + 4 << ADC_CFGR2_OVSS_Pos | // shift the result right 4 bits to make a 16 bit result + ADC_CFGR2_ROVSE, // Enable oversampling .ccr = 0, .pcsel = 0xFFFFFFFF, // enable analog switches on all channels // Thresholds aren't used @@ -88,21 +93,10 @@ static constexpr ADCConversionGroup convGroupSlow = { }, }; -static NO_CACHE adcsample_t slowSampleBuffer[slowChannelCount]; - bool readSlowAnalogInputs(adcsample_t* convertedSamples) { - msg_t result = adcConvert(&ADCD1, &convGroupSlow, slowSampleBuffer, 1); + // Oversampling and right-shift happen in hardware, so we can sample directly to the output buffer + msg_t result = adcConvert(&ADCD1, &convGroupSlow, convertedSamples, 1); - // If something went wrong - try again later - if (result != MSG_OK) { - return false; - } - - // V4 ADC can oversample in hardware, so no need to oversample in software - for (int i = 0; i < slowChannelCount; i++) { - // Convert from 16b result to 12b result - convertedSamples[i] = slowSampleBuffer[i] >> 4; - } - - return true; + // Return true if OK + return result == MSG_OK; } diff --git a/firmware/hw_layer/ports/stm32/stm32f4/device_mpu_util.h b/firmware/hw_layer/ports/stm32/stm32f4/device_mpu_util.h index fe61adb2fa..4c016c5ca1 100644 --- a/firmware/hw_layer/ports/stm32/stm32f4/device_mpu_util.h +++ b/firmware/hw_layer/ports/stm32/stm32f4/device_mpu_util.h @@ -21,3 +21,4 @@ #define SPI_CR1_24BIT_MODE 0 #define SPI_CR2_24BIT_MODE 0 +#define ADC_MAX_VALUE 4095 diff --git a/firmware/hw_layer/ports/stm32/stm32f7/device_mpu_util.h b/firmware/hw_layer/ports/stm32/stm32f7/device_mpu_util.h index b1108a1d3b..f729dee187 100644 --- a/firmware/hw_layer/ports/stm32/stm32f7/device_mpu_util.h +++ b/firmware/hw_layer/ports/stm32/stm32f7/device_mpu_util.h @@ -20,3 +20,5 @@ /* 3 x 8-bit transfer */ #define SPI_CR1_24BIT_MODE 0 #define SPI_CR2_24BIT_MODE SPI_CR2_DS_2 | SPI_CR2_DS_1 | SPI_CR2_DS_0 + +#define ADC_MAX_VALUE 4095 diff --git a/firmware/hw_layer/ports/stm32/stm32h7/device_mpu_util.h b/firmware/hw_layer/ports/stm32/stm32h7/device_mpu_util.h index 8b13f13bf9..93a4f8cf24 100644 --- a/firmware/hw_layer/ports/stm32/stm32h7/device_mpu_util.h +++ b/firmware/hw_layer/ports/stm32/stm32h7/device_mpu_util.h @@ -12,3 +12,5 @@ #define MCU_SERIAL_NUMBER_LOCATION (uint8_t*)(0x1FF1E800) // todo SPI! #2284 + +#define ADC_MAX_VALUE 65535