rusefi/firmware/hw_layer/adc/adc_inputs_onchip.cpp

268 lines
8.1 KiB
C++

/**
* @file adc_inputs_onchip.cpp
* @brief Low level ADC code
*
* rusEfi uses two ADC devices on the same 16 pins at the moment. Two ADC devices are used in order to distinguish between
* fast and slow devices. The idea is that but only having few channels in 'fast' mode we can sample those faster?
*
* Slow ADC group is used for IAT, CLT, AFR, VBATT etc - this one is currently sampled at 500Hz
*
* Fast ADC group is used for MAP, MAF HIP - this one is currently sampled at 10KHz
* We need frequent MAP for map_averaging.cpp
*
* 10KHz equals one measurement every 3.6 degrees at 6000 RPM
*
* @date Jan 14, 2013
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include "pch.h"
#if HAL_USE_ADC
#include "adc_subscription.h"
#include "AdcDevice.h"
#include "mpu_util.h"
#include "periodic_thread_controller.h"
#include "protected_gpio.h"
// Board voltage, with divider coefficient accounted for
float getVoltageDivided(const char *msg, adc_channel_e hwChannel) {
return getVoltage(msg, hwChannel) * getAnalogInputDividerCoefficient(hwChannel);
}
float PUBLIC_API_WEAK boardAdjustVoltage(float voltage, adc_channel_e hwChannel) {
// a hack useful when we do not trust voltage just after board EN was turned on. is this just hiding electrical design flaws?
return voltage;
}
// voltage in MCU universe, from zero to VDD
float getVoltage(const char *msg, adc_channel_e hwChannel) {
float voltage = adcToVolts(getAdcValue(msg, hwChannel));
return boardAdjustVoltage(voltage, hwChannel);
}
#if EFI_USE_FAST_ADC
// is there a reason to have this configurable at runtime?
#ifndef ADC_FAST_DEVICE
#define ADC_FAST_DEVICE ADCD2
#endif
// Depth of the conversion buffer, channels are sampled X times each
#ifndef ADC_BUF_DEPTH_FAST
#define ADC_BUF_DEPTH_FAST 4
#endif
// See https://github.com/rusefi/rusefi/issues/976 for discussion on this value
#ifndef ADC_SAMPLING_FAST
#define ADC_SAMPLING_FAST ADC_SAMPLE_28
#endif
AdcDevice::AdcDevice(ADCDriver *p_adcp, ADCConversionGroup* p_hwConfig, volatile adcsample_t *p_buf, size_t p_depth) {
adcp = p_adcp;
depth = p_depth;
hwConfig = p_hwConfig;
samples = p_buf;
hwConfig->sqr1 = 0;
hwConfig->sqr2 = 0;
hwConfig->sqr3 = 0;
#if ADC_MAX_CHANNELS_COUNT > 16
hwConfig->sqr4 = 0;
hwConfig->sqr5 = 0;
#endif /* ADC_MAX_CHANNELS_COUNT */
memset(internalAdcIndexByHardwareIndex, 0xFF, sizeof(internalAdcIndexByHardwareIndex));
}
static void fastAdcDoneCB(ADCDriver *adcp);
static void fastAdcErrorCB(ADCDriver *, adcerror_t err);
static ADCConversionGroup adcgrpcfgFast = {
.circular = FALSE,
.num_channels = 0,
.end_cb = fastAdcDoneCB,
.error_cb = fastAdcErrorCB,
/* HW dependent part.*/
.cr1 = 0,
.cr2 = ADC_CR2_SWSTART,
/**
* here we configure all possible channels for fast mode. Some channels would not actually
* be used hopefully that's fine to configure all possible channels.
*
*/
// sample times for channels 10...18
.smpr1 =
ADC_SMPR1_SMP_AN10(ADC_SAMPLING_FAST) |
ADC_SMPR1_SMP_AN11(ADC_SAMPLING_FAST) |
ADC_SMPR1_SMP_AN12(ADC_SAMPLING_FAST) |
ADC_SMPR1_SMP_AN13(ADC_SAMPLING_FAST) |
ADC_SMPR1_SMP_AN14(ADC_SAMPLING_FAST) |
ADC_SMPR1_SMP_AN15(ADC_SAMPLING_FAST),
// In this field must be specified the sample times for channels 0...9
.smpr2 =
ADC_SMPR2_SMP_AN0(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN1(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN2(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN3(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN4(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN5(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN6(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN7(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN8(ADC_SAMPLING_FAST) |
ADC_SMPR2_SMP_AN9(ADC_SAMPLING_FAST),
.htr = 0,
.ltr = 0,
.sqr1 = 0, // Conversion group sequence 13...16 + sequence length
.sqr2 = 0, // Conversion group sequence 7...12
.sqr3 = 0, // Conversion group sequence 1...6
#if ADC_MAX_CHANNELS_COUNT > 16
.sqr4 = 0, // Conversion group sequence 19...24
.sqr5 = 0 // Conversion group sequence 25...30
#endif /* ADC_MAX_CHANNELS_COUNT */
};
static volatile NO_CACHE adcsample_t fastAdcSampleBuf[ADC_BUF_DEPTH_FAST * ADC_MAX_CHANNELS_COUNT];
AdcDevice fastAdc(&ADC_FAST_DEVICE, &adcgrpcfgFast, fastAdcSampleBuf, ADC_BUF_DEPTH_FAST);
static void fastAdcDoneCB(ADCDriver *adcp) {
// State may not be complete if we get a callback for "half done"
if (adcp->state == ADC_COMPLETE) {
fastAdc.conversionCount++;
onFastAdcComplete(adcp->samples);
}
}
static volatile adcerror_t fastAdcLastError;
static void fastAdcErrorCB(ADCDriver *, adcerror_t err) {
fastAdcLastError = err;
engine->outputChannels.fastAdcErrorCallbackCount++;
}
static void fastAdcTrigger(GPTDriver*) {
#if EFI_INTERNAL_ADC
/*
* Starts an asynchronous ADC conversion operation, the conversion
* will be executed in parallel to the current PWM cycle and will
* terminate before the next PWM cycle.
*/
fastAdc.startConversionI();
#endif /* EFI_INTERNAL_ADC */
}
static GPTConfig fast_adc_config = {
.frequency = GPT_FREQ_FAST,
.callback = fastAdcTrigger,
.cr2 = 0,
.dier = 0,
};
int AdcDevice::size() const {
return channelCount;
}
void AdcDevice::init(void) {
hwConfig->num_channels = size();
/* driver does this internally */
//hwConfig->sqr1 += ADC_SQR1_NUM_CH(size());
gptStart(EFI_INTERNAL_FAST_ADC_GPT, &fast_adc_config);
gptStartContinuous(EFI_INTERNAL_FAST_ADC_GPT, GPT_PERIOD_FAST);
}
int AdcDevice::enableChannel(adc_channel_e hwChannel) {
if ((channelCount + 1) >= ADC_MAX_CHANNELS_COUNT) {
criticalError("Too many ADC channels configured");
return -1;
}
int logicChannel = channelCount++;
/* TODO: following is correct for STM32 ADC1/2.
* ADC3 has another input to gpio mapping
* and should be handled separately */
size_t channelAdcIndex = hwChannel - EFI_ADC_0;
internalAdcIndexByHardwareIndex[hwChannel] = logicChannel;
if (logicChannel < 6) {
hwConfig->sqr3 |= channelAdcIndex << (5 * logicChannel);
} else if (logicChannel < 12) {
hwConfig->sqr2 |= channelAdcIndex << (5 * (logicChannel - 6));
} else if (logicChannel < 18) {
hwConfig->sqr1 |= channelAdcIndex << (5 * (logicChannel - 12));
}
#if ADC_MAX_CHANNELS_COUNT > 16
else if (logicChannel < 24) {
hwConfig->sqr4 |= channelAdcIndex << (5 * (logicChannel - 18));
}
else if (logicChannel < 30) {
hwConfig->sqr5 |= channelAdcIndex << (5 * (logicChannel - 24));
}
#endif /* ADC_MAX_CHANNELS_COUNT */
return channelAdcIndex;
}
void AdcDevice::startConversionI()
{
chSysLockFromISR();
if ((ADC_FAST_DEVICE.state != ADC_READY) &&
(ADC_FAST_DEVICE.state != ADC_COMPLETE) &&
(ADC_FAST_DEVICE.state != ADC_ERROR)) {
engine->outputChannels.fastAdcErrorsCount++;
// todo: when? why? criticalError("ADC fast not ready?");
// see notes at https://github.com/rusefi/rusefi/issues/6399
} else {
/* drop volatile type qualifier - this is safe */
adcStartConversionI(adcp, hwConfig, (adcsample_t *)samples, depth);
}
chSysUnlockFromISR();
}
adcsample_t AdcDevice::getAvgAdcValue(adc_channel_e hwChannel) {
uint32_t result = 0;
int numChannels = size();
int index = fastAdc.internalAdcIndexByHardwareIndex[hwChannel];
if (index == 0xff) {
criticalError("Fast ADC attempt to read unconfigured input %d.", hwChannel);
return 0;
}
for (size_t i = 0; i < depth; i++) {
adcsample_t sample = samples[index];
// if (sample > 0x1FFF) {
// // 12bit ADC expected right now, make this configurable one day
// criticalError("fast ADC unexpected sample %d", sample);
// } else
if (sample > ADC_MAX_VALUE) {
criticalError("ADC unexpected sample %d at %ld uptime.",
sample,
(uint32_t)getTimeNowS());
}
result += sample;
index += numChannels;
}
// this truncation is guaranteed to not be lossy - the average can't be larger than adcsample_t
return static_cast<adcsample_t>(result / depth);
}
adc_channel_e AdcDevice::getAdcChannelByInternalIndex(int hwChannel) const {
for (size_t idx = EFI_ADC_0; idx < EFI_ADC_TOTAL_CHANNELS; idx++) {
if (internalAdcIndexByHardwareIndex[idx] == hwChannel) {
return (adc_channel_e)idx;
}
}
return EFI_ADC_NONE;
}
AdcToken AdcDevice::getAdcChannelToken(adc_channel_e hwChannel) {
return fastAdc.internalAdcIndexByHardwareIndex[hwChannel];
}
#endif // EFI_USE_FAST_ADC
#endif