fome-fw/firmware/hw_layer/ports/stm32/stm32_adc_v2.cpp

227 lines
6.3 KiB
C++

/**
* @file stm32_adc_v2.cpp
* @brief Port implementation for the STM32 "v2" ADC found on the STM32F4 and STM32F7
*
* @date February 9, 2021
* @author Matthew Kennedy, (c) 2021
*/
#include "pch.h"
#if HAL_USE_ADC
/* Depth of the conversion buffer, channels are sampled X times each.*/
#define SLOW_ADC_OVERSAMPLE 8
#ifdef ADC_MUX_PIN
static OutputPin muxControl;
#endif // ADC_MUX_PIN
void portInitAdc() {
// Init slow ADC
adcStart(&ADCD1, NULL);
#ifdef ADC_MUX_PIN
muxControl.initPin("ADC Mux", ADC_MUX_PIN);
#endif //ADC_MUX_PIN
#if EFI_USE_FAST_ADC
// Init fast ADC (MAP sensor)
adcStart(&ADCD2, NULL);
#endif
// Enable internal temperature reference
adcSTM32EnableTSVREFE(); // Internal temperature sensor
#if defined(STM32F7XX)
/* the temperature sensor is internally
* connected to the same input channel as VBAT. Only one conversion,
* temperature sensor or VBAT, must be selected at a time. */
adcSTM32DisableVBATE();
#endif
/* Enable this code only when you absolutly sure
* that there is no possible errors from ADC */
#if 0
/* All ADC use DMA and DMA calls end_cb from its IRQ
* If none of ADC users need error callback - we can disable
* shared ADC IRQ and save some CPU ticks */
if ((adcgrpcfgSlow.error_cb == NULL) &&
(adcgrpcfgFast.error_cb == NULL)
/* TODO: Add ADC3? */) {
nvicDisableVector(STM32_ADC_NUMBER);
}
#endif
}
/*
* ADC conversion group.
*/
static const ADCConversionGroup tempSensorConvGroup = {
.circular = FALSE,
.num_channels = 1,
.end_cb = nullptr,
.error_cb = nullptr,
/* HW dependent part below */
.cr1 = 0,
.cr2 = ADC_CR2_SWSTART,
// sample times for channels 10...18
.smpr1 =
ADC_SMPR1_SMP_VBAT(ADC_SAMPLE_144) | /* input18 - temperature and vbat input on some STM32F7xx */
ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_144), /* input16 - temperature sensor input on STM32F4xx */
.smpr2 = 0,
.htr = 0, .ltr = 0,
.sqr1 = 0,
.sqr2 = 0,
#if defined(STM32F4XX)
.sqr3 = ADC_SQR3_SQ1_N(16),
#endif
#if defined(STM32F7XX)
.sqr3 = ADC_SQR3_SQ1_N(18),
#endif
};
// 4x oversample is plenty
static constexpr int oversample = 4;
static adcsample_t samples[oversample];
float getMcuTemperature() {
// Temperature sensor is only physically wired to ADC1
adcConvert(&ADCD1, &tempSensorConvGroup, samples, oversample);
uint32_t sum = 0;
for (size_t i = 0; i < oversample; i++) {
sum += samples[i];
}
float volts = (float)sum / (4096 * oversample);
volts *= engineConfiguration->adcVcc;
volts -= 0.760f; // Subtract the reference voltage at 25 deg C
float degrees = volts / 0.0025f; // Divide by slope 2.5mV
degrees += 25.0; // Add the 25 deg C
if (degrees > 150.0f || degrees < -50.0f) {
/*
* we have a sporadic issue with this check todo https://github.com/rusefi/rusefi/issues/2552
firmwareError(ObdCode::OBD_PCM_Processor_Fault, "Invalid CPU temperature measured %f", degrees);
*/
}
return degrees;
}
// See https://github.com/rusefi/rusefi/issues/976 for discussion on these values
#define ADC_SAMPLING_SLOW ADC_SAMPLE_56
#define ADC_SAMPLING_FAST ADC_SAMPLE_28
// Slow ADC has 16 channels we can sample, or 32 if ADC mux mode is enabled.
constexpr size_t adcChannelCount = 16;
// Conversion group for slow channels
// This simply samples every channel in sequence
static constexpr ADCConversionGroup convGroupSlow = {
.circular = FALSE,
.num_channels = adcChannelCount,
.end_cb = nullptr,
.error_cb = nullptr,
/* HW dependent part.*/
.cr1 = 0,
.cr2 = ADC_CR2_SWSTART,
// Configure all channels to ADC_SAMPLING_SLOW sample time
.smpr1 =
ADC_SMPR1_SMP_AN10(ADC_SAMPLING_SLOW) |
ADC_SMPR1_SMP_AN11(ADC_SAMPLING_SLOW) |
ADC_SMPR1_SMP_AN12(ADC_SAMPLING_SLOW) |
ADC_SMPR1_SMP_AN13(ADC_SAMPLING_SLOW) |
ADC_SMPR1_SMP_AN14(ADC_SAMPLING_SLOW) |
ADC_SMPR1_SMP_AN15(ADC_SAMPLING_SLOW),
.smpr2 =
ADC_SMPR2_SMP_AN0(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN1(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN2(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN3(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN4(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN5(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN6(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN7(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN8(ADC_SAMPLING_SLOW) |
ADC_SMPR2_SMP_AN9(ADC_SAMPLING_SLOW),
.htr = 0,
.ltr = 0,
// Simply sequence every channel in order
.sqr1 = ADC_SQR1_SQ13_N(12) | ADC_SQR1_SQ14_N(13) | ADC_SQR1_SQ15_N(14) | ADC_SQR1_SQ16_N(15) | ADC_SQR1_NUM_CH(16), // Conversion group sequence 13...16 + sequence length
.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
.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[SLOW_ADC_OVERSAMPLE * adcChannelCount];
static bool readBatch(adcsample_t* convertedSamples, size_t start) {
msg_t result = adcConvert(&ADCD1, &convGroupSlow, slowSampleBuffer, SLOW_ADC_OVERSAMPLE);
// If something went wrong - try again later
if (result != MSG_OK) {
return false;
}
// Average samples to get some noise filtering and oversampling
for (size_t i = 0; i < adcChannelCount; i++) {
uint32_t sum = 0;
size_t index = i;
for (size_t j = 0; j < SLOW_ADC_OVERSAMPLE; j++) {
sum += slowSampleBuffer[index];
index += adcChannelCount;
}
adcsample_t value = static_cast<adcsample_t>(sum / SLOW_ADC_OVERSAMPLE);
convertedSamples[start + i] = value;
}
return true;
}
bool readSlowAnalogInputs(adcsample_t* convertedSamples) {
bool result = true;
result &= readBatch(convertedSamples, 0);
#ifdef ADC_MUX_PIN
muxControl.setValue(1);
// read the second batch, starting where we left off
result &= readBatch(convertedSamples, adcChannelCount);
muxControl.setValue(0);
#endif
return result;
}
#if EFI_USE_FAST_ADC
#include "AdcConfiguration.h"
extern AdcDevice fastAdc;
static constexpr FastAdcToken invalidToken = (FastAdcToken)(-1);
FastAdcToken enableFastAdcChannel(const char*, adc_channel_e channel) {
if (!isAdcChannelValid(channel)) {
return invalidToken;
}
return fastAdc.internalAdcIndexByHardwareIndex[static_cast<size_t>(channel)];
}
adcsample_t getFastAdc(FastAdcToken token) {
if (token == invalidToken) {
return 0;
}
return fastAdc.m_samples[token];
}
#endif
#endif // HAL_USE_ADC