From 6a9b481c6531478eb8989952516ec5171de3886d Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Thu, 27 Oct 2022 10:59:36 -0700 Subject: [PATCH] prep for multiple MAP sensors (#4705) * prep for multiple MAP sensors * move guard * sensor info printing * maybe kinetis memory --- .../engine_cycle/map_averaging.cpp | 117 ++++++++---------- .../controllers/engine_cycle/map_averaging.h | 37 +++++- .../sensors/sensor_info_printing.cpp | 6 + firmware/init/sensor/init_map.cpp | 16 +-- 4 files changed, 98 insertions(+), 78 deletions(-) diff --git a/firmware/controllers/engine_cycle/map_averaging.cpp b/firmware/controllers/engine_cycle/map_averaging.cpp index 88d2660088..6c4a9ff6e7 100644 --- a/firmware/controllers/engine_cycle/map_averaging.cpp +++ b/firmware/controllers/engine_cycle/map_averaging.cpp @@ -41,20 +41,6 @@ */ 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! @@ -68,24 +54,12 @@ 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"); - { - // with locking we will have a consistent state - chibios_rt::CriticalSectionLocker csl; - mapAdcAccumulator = 0; - mapMeasurementsCounter = 0; - isAveraging = true; - } + getMapAvg().start(); mapAveragingPin.setHigh(); @@ -93,6 +67,51 @@ 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 /** @@ -104,23 +123,18 @@ void mapAveragingAdcCallback(adcsample_t adcValue) { efiAssertVoid(CUSTOM_ERR_6650, getCurrentRemainingStack() > 128, "lowstck#9a"); float instantVoltage = adcToVoltsDivided(adcValue); - SensorResult mapResult = convertMap(instantVoltage); + + SensorResult mapResult = getMapAvg().submit(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 @@ -128,32 +142,9 @@ 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); - SensorResult mapValue = convertMap(v_averagedMapValue); + getMapAvg().stop(); - // 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(); } @@ -170,9 +161,7 @@ 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 */ @@ -254,13 +243,13 @@ void mapAveragingTriggerCallback( // only if value is already prepared int structIndex = getRevolutionCounter() % 2; - scheduling_s *starTimer = &startTimers[i][structIndex]; + scheduling_s *startTimer = &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(starTimer, edgeTimestamp, samplingStart, + scheduleByAngle(startTimer, edgeTimestamp, samplingStart, { startAveraging, endTimer }); } #endif diff --git a/firmware/controllers/engine_cycle/map_averaging.h b/firmware/controllers/engine_cycle/map_averaging.h index fda757e1b0..9514179d88 100644 --- a/firmware/controllers/engine_cycle/map_averaging.h +++ b/firmware/controllers/engine_cycle/map_averaging.h @@ -7,6 +7,8 @@ #pragma once +#include "sensor_converter_func.h" + #if EFI_MAP_AVERAGING #if HAL_USE_ADC @@ -19,11 +21,40 @@ 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 44b75f9942..443e10a36a 100644 --- a/firmware/controllers/sensors/sensor_info_printing.cpp +++ b/firmware/controllers/sensors/sensor_info_printing.cpp @@ -10,6 +10,7 @@ #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)); @@ -66,6 +67,11 @@ 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 5a9a5a0925..b3c9853c1c 100644 --- a/firmware/init/sensor/init_map.cpp +++ b/firmware/init/sensor/init_map.cpp @@ -4,8 +4,7 @@ #include "linear_func.h" #include "fallback_sensor.h" #include "functional_sensor.h" -#include "function_pointer_sensor.h" -#include "identity_func.h" +#include "map_averaging.h" static LinearFunc baroConverter; static FunctionalSensor baroSensor(SensorType::BarometricPressure, MS2NT(50)); @@ -18,17 +17,12 @@ 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 -static FunctionalSensor fastMapSensor(SensorType::MapFast, MS2NT(200)); +MapAverager fastMapSensor(SensorType::MapFast, MS2NT(200)); -// 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); +MapAverager& getMapAvg() { + return fastMapSensor; } -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); @@ -106,7 +100,7 @@ void initMap() { configureMapFunction(mapConverter, engineConfiguration->map.sensor.type); slowMapSensor.setFunction(mapConverter); - fastMapSensor.setFunction(identityFunction); + fastMapSensor.setFunction(mapConverter); slowMapSensor.Register(); fastMapSensor.Register();