diff --git a/firmware/controllers/engine_cycle/map_averaging.cpp b/firmware/controllers/engine_cycle/map_averaging.cpp index 4bbdaba9b9..f5b6d806eb 100644 --- a/firmware/controllers/engine_cycle/map_averaging.cpp +++ b/firmware/controllers/engine_cycle/map_averaging.cpp @@ -158,17 +158,25 @@ static void endAveraging(void*) { #if HAL_USE_ADC if (mapMeasurementsCounter > 0) { v_averagedMapValue = adcToVoltsDivided(mapAdcAccumulator / mapMeasurementsCounter); - // todo: move out of locked context? - averagedMapRunningBuffer[averagedMapBufIdx] = getMapByVoltage(v_averagedMapValue); - // 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]; + + 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()); + + currentPressure = minPressure; } - currentPressure = minPressure; } else { warning(CUSTOM_UNEXPECTED_MAP_VALUE, "No MAP values"); } diff --git a/firmware/controllers/engine_cycle/map_averaging.h b/firmware/controllers/engine_cycle/map_averaging.h index 05c07457ba..0dd7e5ca0f 100644 --- a/firmware/controllers/engine_cycle/map_averaging.h +++ b/firmware/controllers/engine_cycle/map_averaging.h @@ -19,6 +19,9 @@ void refreshMapAveragingPreCalc(DECLARE_ENGINE_PARAMETER_SIGNATURE); void mapAveragingTriggerCallback( uint32_t index, efitick_t edgeTimestamp DECLARE_ENGINE_PARAMETER_SUFFIX); +void onMapAveraged(float mapKpa, efitick_t nowNt); +SensorResult convertMap(float volts); + #if EFI_TUNER_STUDIO void postMapState(TunerStudioOutputChannels *tsOutputChannels); #endif diff --git a/firmware/controllers/sensors/converters/identity_func.cpp b/firmware/controllers/sensors/converters/identity_func.cpp new file mode 100644 index 0000000000..896d73f636 --- /dev/null +++ b/firmware/controllers/sensors/converters/identity_func.cpp @@ -0,0 +1,3 @@ +#include "identity_func.h" + +IdentityFunction identityFunction; diff --git a/firmware/controllers/sensors/converters/identity_func.h b/firmware/controllers/sensors/converters/identity_func.h new file mode 100644 index 0000000000..e6ebc09264 --- /dev/null +++ b/firmware/controllers/sensors/converters/identity_func.h @@ -0,0 +1,13 @@ +#pragma once + +#include "sensor_converter_func.h" + +struct IdentityFunction : public SensorConverter { + SensorResult convert(float raw) const { + return raw; + } + + void showInfo(float testRawValue) const; +}; + +extern IdentityFunction identityFunction; diff --git a/firmware/controllers/sensors/fallback_sensor.h b/firmware/controllers/sensors/fallback_sensor.h new file mode 100644 index 0000000000..bed2f7733a --- /dev/null +++ b/firmware/controllers/sensors/fallback_sensor.h @@ -0,0 +1,29 @@ +#pragma once + +#include "sensor.h" + +class FallbackSensor final : public Sensor { +public: + FallbackSensor(SensorType outputType, SensorType primarySensor, SensorType fallbackSensor) + : Sensor(outputType) + , m_primary(primarySensor) + , m_fallback(fallbackSensor) + { + } + + SensorResult get() const override { + auto primary = Sensor::get(m_primary); + + if (primary) { + return primary; + } + + return Sensor::get(m_fallback); + } + + void showInfo(const char* sensorName) const override; + +private: + const SensorType m_primary; + const SensorType m_fallback; +}; diff --git a/firmware/controllers/sensors/sensor.cpp b/firmware/controllers/sensors/sensor.cpp index cdd2960a14..2a32841988 100644 --- a/firmware/controllers/sensors/sensor.cpp +++ b/firmware/controllers/sensors/sensor.cpp @@ -53,6 +53,9 @@ static const char* const s_sensorNames[] = { "Vehicle speed", "Turbo speed", + + "MAP (fast)", + "MAP (slow)", }; // This struct represents one sensor in the registry. diff --git a/firmware/controllers/sensors/sensor_info_printing.cpp b/firmware/controllers/sensors/sensor_info_printing.cpp index cd50ee28b4..bc1c2b439d 100644 --- a/firmware/controllers/sensors/sensor_info_printing.cpp +++ b/firmware/controllers/sensors/sensor_info_printing.cpp @@ -3,10 +3,12 @@ #include "functional_sensor.h" #include "redundant_sensor.h" #include "redundant_ford_tps.h" +#include "fallback_sensor.h" #include "Lps25Sensor.h" #include "linear_func.h" #include "resistance_func.h" #include "thermistor_func.h" +#include "identity_func.h" void ProxySensor::showInfo(const char* sensorName) const { efiPrintf("Sensor \"%s\" proxied from sensor \"%s\"", sensorName, getSensorName(m_proxiedSensor)); @@ -39,6 +41,10 @@ void RedundantFordTps::showInfo(const char* sensorName) const { efiPrintf("Sensor \"%s\" is Ford-type redundant TPS combining \"%s\" and \"%s\"", sensorName, getSensorName(m_first), getSensorName(m_second)); } +void FallbackSensor::showInfo(const char* sensorName) const { + efiPrintf("Sensor \"%s\" is fallback sensor with primary \"%s\" and fallback \"%s\"", sensorName, getSensorName(m_primary), getSensorName(m_fallback)); +} + void RpmCalculator::showInfo(const char* /*sensorName*/) const { efiPrintf("RPM sensor: stopped: %d spinning up: %d cranking: %d running: %d rpm: %f", isStopped(), @@ -68,3 +74,7 @@ void ThermistorFunc::showInfo(float testInputValue) const { const auto [valid, value] = convert(testInputValue); efiPrintf(" %.1f ohms -> valid: %s. %.1f deg C", testInputValue, boolToString(valid), value); } + +void IdentityFunction::showInfo(float /*testInputValue*/) const { + efiPrintf(" Identity function passes along value."); +} diff --git a/firmware/controllers/sensors/sensor_type.h b/firmware/controllers/sensors/sensor_type.h index 073dd77176..a3676b3008 100644 --- a/firmware/controllers/sensors/sensor_type.h +++ b/firmware/controllers/sensors/sensor_type.h @@ -77,6 +77,11 @@ enum class SensorType : unsigned char { TurbochargerSpeed = 35, + // Fast MAP is synchronous to crank angle - user selectable phase/window + MapFast = 36, + // Slow MAP is asynchronous - not synced to anything, normal analog sampling + MapSlow = 37, + // Leave me at the end! - PlaceholderLast = 36, + PlaceholderLast = 38, }; diff --git a/firmware/controllers/sensors/sensors.mk b/firmware/controllers/sensors/sensors.mk index 357908a16a..91020f5070 100644 --- a/firmware/controllers/sensors/sensors.mk +++ b/firmware/controllers/sensors/sensors.mk @@ -19,4 +19,5 @@ CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cp $(PROJECT_DIR)/controllers/sensors/converters/linear_func.cpp \ $(PROJECT_DIR)/controllers/sensors/converters/resistance_func.cpp \ $(PROJECT_DIR)/controllers/sensors/converters/thermistor_func.cpp \ + $(PROJECT_DIR)/controllers/sensors/converters/identity_func.cpp \ $(PROJECT_DIR)/controllers/sensors/vr_pwm.cpp diff --git a/firmware/init/init.h b/firmware/init/init.h index 1ae47f36da..36a118449a 100644 --- a/firmware/init/init.h +++ b/firmware/init/init.h @@ -42,3 +42,4 @@ void deinitOilPressure(); void deInitFlexSensor(); void deInitVehicleSpeedSensor(); void deinitTurbochargerSpeedSensor(); +void deinitMap(); diff --git a/firmware/init/sensor/init_aux.cpp b/firmware/init/sensor/init_aux.cpp index 6ac333b0f5..bbd31d6ed1 100644 --- a/firmware/init/sensor/init_aux.cpp +++ b/firmware/init/sensor/init_aux.cpp @@ -3,15 +3,9 @@ #include "init.h" #include "adc_subscription.h" #include "functional_sensor.h" +#include "identity_func.h" // These aux sensors just read voltage - so the converter function has nothing to do -struct IdentityFunction : public SensorConverter { - SensorResult convert(float raw) const { - return raw; - } -}; - -static IdentityFunction func; static FunctionalSensor auxSensors[] = { { SensorType::Aux1, MS2NT(50) }, @@ -32,7 +26,7 @@ void initAuxSensors(DECLARE_CONFIG_PARAMETER_SIGNATURE) { } auto& sensor = auxSensors[i]; - sensor.setFunction(func); + sensor.setFunction(identityFunction); sensor.Register(); AdcSubscription::SubscribeSensor(sensor, channel, 10); diff --git a/firmware/init/sensor/init_map.cpp b/firmware/init/sensor/init_map.cpp index 645fc7b377..e8ae5366d6 100644 --- a/firmware/init/sensor/init_map.cpp +++ b/firmware/init/sensor/init_map.cpp @@ -1,21 +1,11 @@ #include "pch.h" +#include "adc_subscription.h" +#include "linear_func.h" +#include "fallback_sensor.h" +#include "functional_sensor.h" #include "function_pointer_sensor.h" - -struct GetMapWrapper { - DECLARE_ENGINE_PTR; - - float getMap() { - return ::getMap(PASS_ENGINE_PARAMETER_SIGNATURE); - } -}; - -static GetMapWrapper mapWrapper; - -static FunctionPointerSensor mapSensor(SensorType::Map, -[]() { - return mapWrapper.getMap(); -}); +#include "identity_func.h" struct GetBaroWrapper { DECLARE_ENGINE_PTR; @@ -32,13 +22,120 @@ static FunctionPointerSensor baroSensor(SensorType::BarometricPressure, return baroWrapper.getBaro(); }); +// This converter is shared between both fast and slow: the only difference is +// how the *voltage* is determined, not how its converted to a pressure. +static LinearFunc mapConverter; +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)); + +// 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); + +// helper struct for the local getMapCfg function +struct MapCfg { + float v1, v2; + float map1, map2; +}; + +static MapCfg getMapCfg(DECLARE_CONFIG_PARAMETER_SIGNATURE) { + auto sensorType = engineConfiguration->map.sensor.type; + switch (sensorType) { + case MT_DENSO183: + return {0, -6.64, 5, 182.78}; + case MT_MPX4250: + return {0, 8, 5, 260}; + case MT_MPX4100: + return {0.3, 20, 4.9, 105}; + case MT_MPX4250A: + return {0.25, 20, 4.875, 250}; + case MT_HONDA3BAR: + return {0.5, 91.422, 3.0, 0}; + case MT_DODGE_NEON_2003: + return {0.4, 15.34, 4.5, 100}; + case MT_SUBY_DENSO: + return {0, 0, 5, 200}; + case MT_GM_3_BAR: + return {0.631, 40, 4.914, 304}; + case MT_GM_2_BAR: + return {0, 8.8, 5, 208}; + case MT_GM_1_BAR: + return {0, 10, 5, 105}; + case MT_TOYOTA_89420_02010: + return {3.7 - 2, 33.32, 3.7, 100}; + case MT_MAZDA_1_BAR: + return {0, 2.5, 5, 117}; + case MT_BOSCH_2_5: + return {0.4 , 20 , 4.65, 250}; + case MT_MPXH6400: + return {1, 90, 3, 250}; + default: + firmwareError(CUSTOM_ERR_MAP_TYPE, "Unknown MAP type: decoder %d", sensorType); + // falls through to custom + return {}; + case MT_CUSTOM: { + auto& mapConfig = engineConfiguration->map.sensor; + return { + engineConfiguration->mapLowValueVoltage, + mapConfig.lowValue, + engineConfiguration->mapHighValueVoltage, + mapConfig.highValue + }; + }} +} + +void configureMapFunction(DECLARE_CONFIG_PARAMETER_SIGNATURE) { + auto cfg = getMapCfg(PASS_CONFIG_PARAMETER_SIGNATURE); + + mapConverter.configure( + cfg.v1, + cfg.map1, + cfg.v2, + cfg.map2, + engineConfiguration->mapErrorDetectionTooLow, + engineConfiguration->mapErrorDetectionTooHigh + ); +} + void initMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) { - INJECT_ENGINE_REFERENCE(&mapWrapper); INJECT_ENGINE_REFERENCE(&baroWrapper); - mapSensor.Register(); + + auto mapChannel = engineConfiguration->map.sensor.hwChannel; + + if (isAdcChannelValid(mapChannel)) { + // Set up the conversion function + configureMapFunction(PASS_CONFIG_PARAMETER_SIGNATURE); + + slowMapSensor.setFunction(mapConverter); + fastMapSensor.setFunction(identityFunction); + + slowMapSensor.Register(); + fastMapSensor.Register(); + mapCombiner.Register(); + + // Configure slow MAP as a normal analog sensor + AdcSubscription::SubscribeSensor(slowMapSensor, mapChannel, 100); + } // Only register if configured if (isAdcChannelValid(engineConfiguration->baroSensor.hwChannel)) { baroSensor.Register(); } } + +void deinitMap() { + AdcSubscription::UnsubscribeSensor(slowMapSensor); +} diff --git a/firmware/init/sensor/init_sensors.cpp b/firmware/init/sensor/init_sensors.cpp index ff9f7fbd3d..e396560100 100644 --- a/firmware/init/sensor/init_sensors.cpp +++ b/firmware/init/sensor/init_sensors.cpp @@ -33,7 +33,6 @@ void deInitIfValid(const char* msg, adc_channel_e channel) { static void initOldAnalogInputs(DECLARE_CONFIG_PARAMETER_SIGNATURE) { initIfValid("AFR", engineConfiguration->afr.hwChannel); - initIfValid("MAP", engineConfiguration->map.sensor.hwChannel); initIfValid("Baro", engineConfiguration->baroSensor.hwChannel); initIfValid("AUXF#1", engineConfiguration->auxFastSensor1_adcChannel); initIfValid("CJ125 UR", engineConfiguration->cj125ur); @@ -42,7 +41,6 @@ static void initOldAnalogInputs(DECLARE_CONFIG_PARAMETER_SIGNATURE) { static void deInitOldAnalogInputs(DECLARE_CONFIG_PARAMETER_SIGNATURE) { deInitIfValid("AFR", activeConfiguration.afr.hwChannel); - deInitIfValid("MAP", activeConfiguration.map.sensor.hwChannel); deInitIfValid("Baro", activeConfiguration.baroSensor.hwChannel); deInitIfValid("AUXF#1", activeConfiguration.auxFastSensor1_adcChannel); deInitIfValid("CJ125 UR", activeConfiguration.cj125ur); @@ -87,6 +85,7 @@ void stopSensors(DECLARE_CONFIG_PARAMETER_SIGNATURE) { deInitFlexSensor(); deInitVehicleSpeedSensor(); deinitTurbochargerSpeedSensor(); + deinitMap(); } void reconfigureSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) { diff --git a/java_console/autotest/src/main/java/com/rusefi/f4discovery/CommonFunctionalTest.java b/java_console/autotest/src/main/java/com/rusefi/f4discovery/CommonFunctionalTest.java index 92c58f6558..2285bfed3f 100644 --- a/java_console/autotest/src/main/java/com/rusefi/f4discovery/CommonFunctionalTest.java +++ b/java_console/autotest/src/main/java/com/rusefi/f4discovery/CommonFunctionalTest.java @@ -206,7 +206,7 @@ public class CommonFunctionalTest extends RusefiTestBase { ecu.setEngineType(ET_DODGE_NEON_2003_CRANK); ecu.sendCommand("set wwaeTau 0"); ecu.sendCommand("set wwaeBeta 0"); - ecu.sendCommand("set mock_map_voltage 1"); + ecu.sendCommand("set_sensor_mock 4 69.12"); // MAP ecu.sendCommand("set_sensor_mock 27 12"); ecu.sendCommand("disable cylinder_cleanup"); EngineChart chart; @@ -322,7 +322,7 @@ public class CommonFunctionalTest extends RusefiTestBase { assertWaveNotNull(msg, chart, EngineChart.SPARK_3); // switching to Speed Density - ecu.sendCommand("set mock_map_voltage 1"); + ecu.sendCommand("set_sensor_mock 4 69.12"); // MAP sendComplexCommand("set algorithm 3"); ecu.changeRpm(2600); ecu.changeRpm(2000); @@ -351,8 +351,8 @@ public class CommonFunctionalTest extends RusefiTestBase { public void testFordAspire() { ecu.setEngineType(ET_FORD_ASPIRE); ecu.sendCommand("disable cylinder_cleanup"); - ecu.sendCommand("set mock_map_voltage 1"); - ecu.sendCommand("set_sensor_mock 27 12"); + ecu.sendCommand("set_sensor_mock 4 69.12"); // MAP + ecu.sendCommand("set_sensor_mock 27 12"); // vbatt String msg; EngineChart chart; // todo: interesting changeRpm(100); diff --git a/unit_tests/tests/sensor/test_sensor_init.cpp b/unit_tests/tests/sensor/test_sensor_init.cpp index c131d1fc9b..01d685e1c8 100644 --- a/unit_tests/tests/sensor/test_sensor_init.cpp +++ b/unit_tests/tests/sensor/test_sensor_init.cpp @@ -226,6 +226,13 @@ TEST(SensorInit, Map) { auto s = Sensor::getSensorOfType(SensorType::Map); ASSERT_NE(nullptr, s); - engine->mockMapValue = 55; - EXPECT_FLOAT_EQ(55.0f, Sensor::get(SensorType::Map).value_or(0)); + Sensor::setMockValue(SensorType::MapFast, 25); + Sensor::setMockValue(SensorType::MapSlow, 75); + + // Should prefer fast MAP + EXPECT_FLOAT_EQ(25, Sensor::get(SensorType::Map).value_or(0)); + + // But when that fails, should return slow MAP + Sensor::resetMockValue(SensorType::MapFast); + EXPECT_FLOAT_EQ(75, Sensor::get(SensorType::Map).value_or(0)); }