diff --git a/firmware/controllers/engine_cycle/map_averaging.cpp b/firmware/controllers/engine_cycle/map_averaging.cpp index 6c4a9ff6e7..88d2660088 100644 --- a/firmware/controllers/engine_cycle/map_averaging.cpp +++ b/firmware/controllers/engine_cycle/map_averaging.cpp @@ -41,6 +41,20 @@ */ static NamedOutputPin mapAveragingPin("map"); +/** + * Running MAP accumulator - sum of all measurements within averaging window + */ +static volatile float mapAdcAccumulator = 0; +/** + * Running counter of measurements to consider for averaging + */ +static volatile int mapMeasurementsCounter = 0; + +/** + * v_ for Voltage + */ +static float v_averagedMapValue; + // allow smoothing up to number of cylinders #define MAX_MAP_BUFFER_LENGTH (MAX_CYLINDER_COUNT) // in MAP units, not voltage! @@ -54,12 +68,24 @@ static int averagedMapBufIdx = 0; static scheduling_s startTimers[MAX_CYLINDER_COUNT][2]; static scheduling_s endTimers[MAX_CYLINDER_COUNT][2]; +/** + * that's a performance optimization: let's not bother averaging + * if we are outside of of the window + */ +static bool isAveraging = false; + static void endAveraging(void *arg); static void startAveraging(scheduling_s *endAveragingScheduling) { efiAssertVoid(CUSTOM_ERR_6649, getCurrentRemainingStack() > 128, "lowstck#9"); - getMapAvg().start(); + { + // with locking we will have a consistent state + chibios_rt::CriticalSectionLocker csl; + mapAdcAccumulator = 0; + mapMeasurementsCounter = 0; + isAveraging = true; + } mapAveragingPin.setHigh(); @@ -67,51 +93,6 @@ static void startAveraging(scheduling_s *endAveragingScheduling) { endAveraging); } -void MapAverager::start() { - chibios_rt::CriticalSectionLocker csl; - - m_counter = 0; - m_sum = 0; - m_isAveraging = true; -} - -SensorResult MapAverager::submit(float volts) { - auto result = m_function ? m_function->convert(volts) : unexpected; - - if (m_isAveraging && result) { - chibios_rt::CriticalSectionLocker csl; - - m_counter++; - m_sum += result.Value; - } - - return result; -} - -void MapAverager::stop() { - m_isAveraging = false; - - if (m_counter > 0) { - float averageMap = m_sum / m_counter; - m_lastCounter = m_counter; - - // TODO: this should be per-sensor, not one for all MAP sensors - averagedMapRunningBuffer[averagedMapBufIdx] = averageMap; - // increment circular running buffer index - averagedMapBufIdx = (averagedMapBufIdx + 1) % mapMinBufferLength; - // find min. value (only works for pressure values, not raw voltages!) - float minPressure = averagedMapRunningBuffer[0]; - for (int i = 1; i < mapMinBufferLength; i++) { - if (averagedMapRunningBuffer[i] < minPressure) - minPressure = averagedMapRunningBuffer[i]; - } - - setValidValue(minPressure, getTimeNowNt()); - } else { - warning(CUSTOM_UNEXPECTED_MAP_VALUE, "No MAP values"); - } -} - #if HAL_USE_ADC /** @@ -123,18 +104,23 @@ void mapAveragingAdcCallback(adcsample_t adcValue) { efiAssertVoid(CUSTOM_ERR_6650, getCurrentRemainingStack() > 128, "lowstck#9a"); float instantVoltage = adcToVoltsDivided(adcValue); - - SensorResult mapResult = getMapAvg().submit(instantVoltage); - + SensorResult mapResult = convertMap(instantVoltage); if (!mapResult) { // hopefully this warning is not too much CPU consumption for fast ADC callback warning(CUSTOM_INSTANT_MAP_DECODING, "Invalid MAP at %f", instantVoltage); } - float instantMap = mapResult.value_or(0); #if EFI_TUNER_STUDIO engine->outputChannels.instantMAPValue = instantMap; #endif // EFI_TUNER_STUDIO + + /* Calculates the average values from the ADC samples.*/ + if (isAveraging) { + // with locking we will have a consistent state + chibios_rt::CriticalSectionLocker csl; + mapAdcAccumulator += adcValue; + mapMeasurementsCounter++; + } } #endif @@ -142,9 +128,32 @@ static void endAveraging(void*) { #if ! EFI_UNIT_TEST chibios_rt::CriticalSectionLocker csl; #endif + isAveraging = false; + // with locking we would have a consistent state +#if HAL_USE_ADC + if (mapMeasurementsCounter > 0) { + v_averagedMapValue = adcToVoltsDivided(mapAdcAccumulator / mapMeasurementsCounter); - getMapAvg().stop(); + SensorResult mapValue = convertMap(v_averagedMapValue); + // Skip update if conversion invalid + if (mapValue) { + averagedMapRunningBuffer[averagedMapBufIdx] = mapValue.Value; + // increment circular running buffer index + averagedMapBufIdx = (averagedMapBufIdx + 1) % mapMinBufferLength; + // find min. value (only works for pressure values, not raw voltages!) + float minPressure = averagedMapRunningBuffer[0]; + for (int i = 1; i < mapMinBufferLength; i++) { + if (averagedMapRunningBuffer[i] < minPressure) + minPressure = averagedMapRunningBuffer[i]; + } + + onMapAveraged(minPressure, getTimeNowNt()); + } + } else { + warning(CUSTOM_UNEXPECTED_MAP_VALUE, "No MAP values"); + } +#endif mapAveragingPin.setLow(); } @@ -161,7 +170,9 @@ static void applyMapMinBufferLength() { #if EFI_TUNER_STUDIO void postMapState(TunerStudioOutputChannels *tsOutputChannels) { + tsOutputChannels->debugFloatField1 = v_averagedMapValue; tsOutputChannels->debugFloatField2 = engine->engineState.mapAveragingDuration; + tsOutputChannels->debugIntField1 = mapMeasurementsCounter; } #endif /* EFI_TUNER_STUDIO */ @@ -243,13 +254,13 @@ void mapAveragingTriggerCallback( // only if value is already prepared int structIndex = getRevolutionCounter() % 2; - scheduling_s *startTimer = &startTimers[i][structIndex]; + scheduling_s *starTimer = &startTimers[i][structIndex]; scheduling_s *endTimer = &endTimers[i][structIndex]; // at the moment we schedule based on time prediction based on current RPM and angle // we are loosing precision in case of changing RPM - the further away is the event the worse is precision // todo: schedule this based on closest trigger event, same as ignition works - scheduleByAngle(startTimer, edgeTimestamp, samplingStart, + scheduleByAngle(starTimer, edgeTimestamp, samplingStart, { startAveraging, endTimer }); } #endif diff --git a/firmware/controllers/engine_cycle/map_averaging.h b/firmware/controllers/engine_cycle/map_averaging.h index 9514179d88..fda757e1b0 100644 --- a/firmware/controllers/engine_cycle/map_averaging.h +++ b/firmware/controllers/engine_cycle/map_averaging.h @@ -7,8 +7,6 @@ #pragma once -#include "sensor_converter_func.h" - #if EFI_MAP_AVERAGING #if HAL_USE_ADC @@ -21,40 +19,11 @@ void refreshMapAveragingPreCalc(); void mapAveragingTriggerCallback( uint32_t index, efitick_t edgeTimestamp); +void onMapAveraged(float mapKpa, efitick_t nowNt); +SensorResult convertMap(float volts); + #if EFI_TUNER_STUDIO void postMapState(TunerStudioOutputChannels *tsOutputChannels); #endif -// allow smoothing up to number of cylinders -#define MAX_MAP_BUFFER_LENGTH (MAX_CYLINDER_COUNT) - #endif /* EFI_MAP_AVERAGING */ - -class MapAverager : public StoredValueSensor { -public: - MapAverager(SensorType type, efitick_t timeout) - : StoredValueSensor(type, timeout) - { - } - - void start(); - void stop(); - - SensorResult submit(float sensorVolts); - - void setFunction(SensorConverter& func) { - m_function = &func; - } - - void showInfo(const char* sensorName) const override; - -private: - SensorConverter* m_function = nullptr; - - bool m_isAveraging = false; - size_t m_counter = 0; - size_t m_lastCounter = 0; - float m_sum = 0; -}; - -MapAverager& getMapAvg(); diff --git a/firmware/controllers/sensors/sensor_info_printing.cpp b/firmware/controllers/sensors/sensor_info_printing.cpp index 443e10a36a..44b75f9942 100644 --- a/firmware/controllers/sensors/sensor_info_printing.cpp +++ b/firmware/controllers/sensors/sensor_info_printing.cpp @@ -10,7 +10,6 @@ #include "resistance_func.h" #include "thermistor_func.h" #include "identity_func.h" -#include "map_averaging.h" void ProxySensor::showInfo(const char* sensorName) const { efiPrintf("Sensor \"%s\" proxied from sensor \"%s\"", sensorName, getSensorName(m_proxiedSensor)); @@ -67,11 +66,6 @@ void Lps25Sensor::showInfo(const char* sensorName) const { efiPrintf("%s: LPS25 baro %.2f kPa", sensorName, get().Value); } -void MapAverager::showInfo(const char* sensorName) const { - const auto value = get(); - efiPrintf("Sensor \"%s\" is MAP averager: valid: %s value: %.2f averaged sample count: %d", sensorName, boolToString(value.Valid), value.Value, m_lastCounter); -} - void LinearFunc::showInfo(float testRawValue) const { efiPrintf(" Linear function slope: %.2f offset: %.2f min: %.1f max: %.1f", m_a, m_b, m_minOutput, m_maxOutput); const auto value = convert(testRawValue); diff --git a/firmware/init/sensor/init_map.cpp b/firmware/init/sensor/init_map.cpp index b3c9853c1c..5a9a5a0925 100644 --- a/firmware/init/sensor/init_map.cpp +++ b/firmware/init/sensor/init_map.cpp @@ -4,7 +4,8 @@ #include "linear_func.h" #include "fallback_sensor.h" #include "functional_sensor.h" -#include "map_averaging.h" +#include "function_pointer_sensor.h" +#include "identity_func.h" static LinearFunc baroConverter; static FunctionalSensor baroSensor(SensorType::BarometricPressure, MS2NT(50)); @@ -17,12 +18,17 @@ static FunctionalSensor slowMapSensor(SensorType::MapSlow, MS2NT(50)); // lowest reasonable idle is maybe 600 rpm // one sample per cycle (1 cylinder, or "sample one cyl" mode) gives a period of 100ms // add some margin -> 200ms timeout for fast MAP sampling -MapAverager fastMapSensor(SensorType::MapFast, MS2NT(200)); +static FunctionalSensor fastMapSensor(SensorType::MapFast, MS2NT(200)); -MapAverager& getMapAvg() { - return fastMapSensor; +// This is called from the fast ADC completion callback +void onMapAveraged(float mapKpa, efitick_t nowNt) { + // This sensor uses identity function, so it's kPa in, kPa out + fastMapSensor.postRawValue(mapKpa, nowNt); } +SensorResult convertMap(float volts) { + return mapConverter.convert(volts); +} // Combine MAP sensors: prefer fast sensor, but use slow if fast is unavailable. static FallbackSensor mapCombiner(SensorType::Map, SensorType::MapFast, SensorType::MapSlow); @@ -100,7 +106,7 @@ void initMap() { configureMapFunction(mapConverter, engineConfiguration->map.sensor.type); slowMapSensor.setFunction(mapConverter); - fastMapSensor.setFunction(mapConverter); + fastMapSensor.setFunction(identityFunction); slowMapSensor.Register(); fastMapSensor.Register();