2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @file thermistors.cpp
|
|
|
|
*
|
|
|
|
* @date Feb 17, 2013
|
2018-01-20 17:55:31 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2018
|
2015-07-10 06:01:56 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* http://en.wikipedia.org/wiki/Thermistor
|
|
|
|
* http://en.wikipedia.org/wiki/Steinhart%E2%80%93Hart_equation
|
|
|
|
*/
|
|
|
|
|
2018-09-16 19:26:57 -07:00
|
|
|
#include "global.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "thermistors.h"
|
2017-11-24 16:16:00 -08:00
|
|
|
#include "analog_input.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "engine_configuration.h"
|
|
|
|
#include "engine_math.h"
|
|
|
|
|
2016-12-21 15:01:56 -08:00
|
|
|
#define _5_VOLTS 5.0
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
// Celsius
|
2016-12-16 16:02:14 -08:00
|
|
|
#define NO_IAT_SENSOR_TEMPERATURE 32.0f
|
2015-07-10 06:01:56 -07:00
|
|
|
#define LIMPING_MODE_IAT_TEMPERATURE 30.0f
|
|
|
|
#define LIMPING_MODE_CLT_TEMPERATURE 70.0f
|
2016-12-16 16:02:14 -08:00
|
|
|
#define NO_CLT_SENSOR_TEMPERATURE 72.0f
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
EXTERN_ENGINE
|
|
|
|
;
|
|
|
|
|
2017-01-05 17:04:02 -08:00
|
|
|
static Logging *logger = NULL;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* http://en.wikipedia.org/wiki/Voltage_divider
|
|
|
|
*/
|
|
|
|
float getR1InVoltageDividor(float Vout, float Vin, float r2) {
|
|
|
|
return r2 * Vin / Vout - r2;
|
|
|
|
}
|
|
|
|
|
|
|
|
float getR2InVoltageDividor(float Vout, float Vin, float r1) {
|
|
|
|
if (Vout == 0) {
|
|
|
|
return NAN;
|
|
|
|
}
|
|
|
|
return r1 / (Vin / Vout - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
float getVoutInVoltageDividor(float Vin, float r1, float r2) {
|
|
|
|
return r2 * Vin / (r1 + r2);
|
|
|
|
}
|
|
|
|
|
2016-12-27 08:01:26 -08:00
|
|
|
float ThermistorMath::getKelvinTemperatureByResistance(float resistance) {
|
2015-07-10 06:01:56 -07:00
|
|
|
if (resistance <= 0) {
|
|
|
|
//warning("Invalid resistance in getKelvinTemperature=", resistance);
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
float logR = logf(resistance);
|
2016-12-27 08:01:26 -08:00
|
|
|
return 1 / (s_h_a + s_h_b * logR + s_h_c * logR * logR * logR);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
float convertCelsiustoF(float tempC) {
|
|
|
|
return tempC * 9 / 5 + 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
float convertFtoCelsius(float tempF) {
|
|
|
|
return (tempF - 32) / 9 * 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
float convertKelvinToFahrenheit(float kelvin) {
|
|
|
|
float tempC = convertKelvinToCelcius(kelvin);
|
|
|
|
return convertCelsiustoF(tempC);
|
|
|
|
}
|
|
|
|
|
2016-12-22 11:02:38 -08:00
|
|
|
float getResistance(ThermistorConf *config, float voltage) {
|
2018-07-25 20:30:00 -07:00
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, config != NULL, "thermistor config is null", NAN);
|
2015-07-10 06:01:56 -07:00
|
|
|
thermistor_conf_s *tc = &config->config;
|
|
|
|
|
|
|
|
float resistance = getR2InVoltageDividor(voltage, _5_VOLTS, tc->bias_resistor);
|
|
|
|
return resistance;
|
|
|
|
}
|
|
|
|
|
2017-06-12 15:48:55 -07:00
|
|
|
float getTemperatureC(ThermistorConf *config, ThermistorMath *tm, bool useLinear) {
|
2016-12-23 11:01:45 -08:00
|
|
|
tm->setConfig(&config->config); // implementation checks if configuration has changed or not
|
|
|
|
|
2016-12-22 11:02:38 -08:00
|
|
|
float voltage = getVoltageDivided("term", config->adcChannel);
|
2017-06-12 15:48:55 -07:00
|
|
|
if (useLinear) {
|
|
|
|
// todo: fix this horrible code!
|
|
|
|
// should work as a short term fix.
|
|
|
|
// todo: move 'useLinearXXXSensor' into termistor configuration record
|
|
|
|
// yes, we use 'resistance' setting for 'voltage' here
|
2018-06-12 02:45:11 -07:00
|
|
|
return interpolateMsg("temp", config->config.resistance_1, config->config.tempC_1,
|
2017-06-12 15:48:55 -07:00
|
|
|
config->config.resistance_2, config->config.tempC_2,
|
|
|
|
voltage);
|
|
|
|
|
|
|
|
}
|
2016-12-22 11:02:38 -08:00
|
|
|
float resistance = getResistance(config, voltage);
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-12-27 08:01:26 -08:00
|
|
|
float kelvinTemperature = tm->getKelvinTemperatureByResistance(resistance);
|
2015-07-10 06:01:56 -07:00
|
|
|
return convertKelvinToCelcius(kelvinTemperature);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isValidCoolantTemperature(float temperature) {
|
|
|
|
// I hope magic constants are appropriate here
|
|
|
|
return !cisnan(temperature) && temperature > -50 && temperature < 250;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isValidIntakeAirTemperature(float temperature) {
|
|
|
|
// I hope magic constants are appropriate here
|
|
|
|
return !cisnan(temperature) && temperature > -50 && temperature < 100;
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:33:22 -07:00
|
|
|
bool hasCltSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2016-12-17 08:01:40 -08:00
|
|
|
return engineConfiguration->clt.adcChannel != EFI_ADC_NONE;
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @return coolant temperature, in Celsius
|
|
|
|
*/
|
2017-05-15 20:33:22 -07:00
|
|
|
float getCoolantTemperature(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
|
|
|
if (!hasCltSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
|
2017-05-11 05:32:08 -07:00
|
|
|
engine->isCltBroken = false;
|
2016-12-16 18:02:54 -08:00
|
|
|
return NO_CLT_SENSOR_TEMPERATURE;
|
|
|
|
}
|
2017-06-12 15:48:55 -07:00
|
|
|
float temperature = getTemperatureC(&engineConfiguration->clt, &engine->engineState.cltCurve,
|
|
|
|
engineConfiguration->useLinearCltSensor);
|
2015-07-10 06:01:56 -07:00
|
|
|
if (!isValidCoolantTemperature(temperature)) {
|
2018-07-25 20:30:00 -07:00
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, engineConfiguration!=NULL, "NULL engineConfiguration", NAN);
|
2018-01-23 09:05:14 -08:00
|
|
|
warning(OBD_Engine_Coolant_Temperature_Circuit_Malfunction, "unrealistic CLT %.2f", temperature);
|
2017-05-11 05:32:08 -07:00
|
|
|
engine->isCltBroken = true;
|
2015-07-10 06:01:56 -07:00
|
|
|
return LIMPING_MODE_CLT_TEMPERATURE;
|
|
|
|
}
|
2017-05-11 05:32:08 -07:00
|
|
|
engine->isCltBroken = false;
|
2015-07-10 06:01:56 -07:00
|
|
|
return temperature;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setThermistorConfiguration(ThermistorConf * thermistor, float tempC1, float r1, float tempC2, float r2, float tempC3,
|
|
|
|
float r3) {
|
|
|
|
thermistor_conf_s *tc = &thermistor->config;
|
|
|
|
tc->tempC_1 = tempC1;
|
|
|
|
tc->resistance_1 = r1;
|
|
|
|
|
|
|
|
tc->tempC_2 = tempC2;
|
|
|
|
tc->resistance_2 = r2;
|
|
|
|
|
|
|
|
tc->tempC_3 = tempC3;
|
|
|
|
tc->resistance_3 = r3;
|
|
|
|
}
|
|
|
|
|
2016-12-22 11:02:38 -08:00
|
|
|
void ThermistorMath::prepareThermistorCurve(thermistor_conf_s *tc) {
|
2015-07-10 06:01:56 -07:00
|
|
|
float T1 = tc->tempC_1 + KELV;
|
|
|
|
float T2 = tc->tempC_2 + KELV;
|
|
|
|
float T3 = tc->tempC_3 + KELV;
|
2016-10-31 17:02:09 -07:00
|
|
|
#if EXTREME_TERM_LOGGING || defined(__DOXYGEN__)
|
2017-04-07 13:35:43 -07:00
|
|
|
scheduleMsg(logger, "T1=%.5f/T2=%.5f/T3=%.5f", T1, T2, T3);
|
2016-10-31 17:02:09 -07:00
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
float L1 = logf(tc->resistance_1);
|
|
|
|
if (L1 == tc->resistance_1) {
|
2017-04-01 22:46:27 -07:00
|
|
|
/**
|
|
|
|
* See https://github.com/rusefi/rusefi/issues/375
|
|
|
|
* See https://sourceforge.net/p/rusefi/tickets/149/
|
|
|
|
*/
|
2018-01-23 09:05:14 -08:00
|
|
|
firmwareError(CUSTOM_ERR_NATURAL_LOGARITHM_ERROR, "Natural logarithm logf() is broken: %.2f", tc->resistance_1);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
float L2 = logf(tc->resistance_2);
|
|
|
|
float L3 = logf(tc->resistance_3);
|
2016-10-31 17:02:09 -07:00
|
|
|
#if EXTREME_TERM_LOGGING || defined(__DOXYGEN__)
|
2017-04-07 13:35:43 -07:00
|
|
|
scheduleMsg(logger, "R1=%.5f/R2=%.5f/R3=%.5f", tc->resistance_1, tc->resistance_2,
|
2015-07-10 06:01:56 -07:00
|
|
|
tc->resistance_3);
|
2017-04-07 13:35:43 -07:00
|
|
|
scheduleMsg(logger, "L1=%.5f/L2=%.5f/L3=%.5f", L1, L2, L3);
|
2016-10-31 17:02:09 -07:00
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
float Y1 = 1 / T1;
|
|
|
|
float Y2 = 1 / T2;
|
|
|
|
float Y3 = 1 / T3;
|
|
|
|
|
|
|
|
|
|
|
|
float U2 = (Y2 - Y1) / (L2 - L1);
|
|
|
|
float U3 = (Y3 - Y1) / (L3 - L1);
|
|
|
|
|
|
|
|
|
2016-12-22 11:02:38 -08:00
|
|
|
s_h_c = (U3 - U2) / (L3 - L2) * pow(L1 + L2 + L3, -1);
|
|
|
|
s_h_b = U2 - s_h_c * (L1 * L1 + L1 * L2 + L2 * L2);
|
|
|
|
s_h_a = Y1 - (s_h_b + L1 * L1 * s_h_c) * L1;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-10-31 17:02:09 -07:00
|
|
|
#if EXTREME_TERM_LOGGING || defined(__DOXYGEN__)
|
2017-04-07 13:35:43 -07:00
|
|
|
scheduleMsg(logger, "Y1=%.5f/Y2=%.5f/Y3=%.5f", Y1, Y2, Y3);
|
|
|
|
scheduleMsg(logger, "U2=%.5f/U3=%.5f", U2, U3);
|
|
|
|
scheduleMsg(logger, "s_h_c=%.5f/s_h_b=%.5f/s_h_a=%.5f", curve->s_h_c, curve->s_h_b,
|
2015-07-10 06:01:56 -07:00
|
|
|
curve->s_h_a);
|
2016-10-31 17:02:09 -07:00
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-05-15 20:33:22 -07:00
|
|
|
bool hasIatSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2016-12-17 08:01:40 -08:00
|
|
|
return engineConfiguration->iat.adcChannel != EFI_ADC_NONE;
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @return Celsius value
|
|
|
|
*/
|
2017-05-15 20:33:22 -07:00
|
|
|
float getIntakeAirTemperature(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
|
|
|
if (!hasIatSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
|
2016-12-16 16:02:14 -08:00
|
|
|
return NO_IAT_SENSOR_TEMPERATURE;
|
|
|
|
}
|
2017-06-12 15:48:55 -07:00
|
|
|
float temperature = getTemperatureC(&engineConfiguration->iat, &engine->engineState.iatCurve,
|
|
|
|
engineConfiguration->useLinearIatSensor);
|
2015-07-10 06:01:56 -07:00
|
|
|
if (!isValidIntakeAirTemperature(temperature)) {
|
2018-07-25 20:30:00 -07:00
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, engineConfiguration!=NULL, "NULL engineConfiguration", NAN);
|
2017-03-03 20:30:41 -08:00
|
|
|
#if EFI_PROD_CODE || EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2018-01-23 09:05:14 -08:00
|
|
|
warning(OBD_Intake_Air_Temperature_Circuit_Malfunction, "unrealistic IAT %.2f", temperature);
|
2017-03-03 20:26:50 -08:00
|
|
|
#endif /* EFI_PROD_CODE */
|
2015-07-10 06:01:56 -07:00
|
|
|
return LIMPING_MODE_IAT_TEMPERATURE;
|
|
|
|
}
|
|
|
|
return temperature;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setDodgeSensor(ThermistorConf *thermistorConf) {
|
|
|
|
setThermistorConfiguration(thermistorConf, -40, 336660, 30, 7550, 120, 390);
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: better method name?
|
|
|
|
void setCommonNTCSensor(ThermistorConf *thermistorConf) {
|
|
|
|
/**
|
|
|
|
* 18K Ohm @ -20C
|
|
|
|
* 2.1K Ohm @ 24C
|
|
|
|
* 294 Ohm @ 80C
|
|
|
|
* http://www.rexbo.eu/hella/coolant-temperature-sensor-6pt009107121?c=100334&at=3130
|
|
|
|
*/
|
|
|
|
setThermistorConfiguration(thermistorConf, -20, 18000, 23.8889, 2100, 120.0, 100.0);
|
|
|
|
}
|
|
|
|
|
2017-01-05 17:04:02 -08:00
|
|
|
#if EFI_PROD_CODE || defined(__DOXYGEN__)
|
2015-07-10 06:01:56 -07:00
|
|
|
static void testCltByR(float resistance) {
|
2017-01-05 17:04:02 -08:00
|
|
|
if (logger == NULL) {
|
|
|
|
firmwareError(CUSTOM_ERR_THERM, "thermstr not initialized");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-28 16:36:12 -07:00
|
|
|
// we expect periodicSlowCallback to already update configuration in the curve helper class see setConfig
|
2016-12-27 08:01:26 -08:00
|
|
|
float kTemp = engine->engineState.cltCurve.getKelvinTemperatureByResistance(resistance);
|
2018-01-23 09:05:14 -08:00
|
|
|
scheduleMsg(logger, "for R=%.2f we have %.2f", resistance, (kTemp - KELV));
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-05-15 20:33:22 -07:00
|
|
|
void initThermistors(Logging *sharedLogger DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2015-07-10 06:01:56 -07:00
|
|
|
logger = sharedLogger;
|
2018-07-25 20:03:04 -07:00
|
|
|
efiAssertVoid(CUSTOM_ERR_6578, engine!=NULL, "e NULL initThermistors");
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-12-21 15:01:56 -08:00
|
|
|
#if EFI_PROD_CODE || defined(__DOXYGEN__)
|
2015-07-10 06:01:56 -07:00
|
|
|
addConsoleActionF("test_clt_by_r", testCltByR);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
ThermistorMath::ThermistorMath() {
|
|
|
|
memset(¤tConfig, 0, sizeof(currentConfig));
|
2016-12-22 11:02:38 -08:00
|
|
|
s_h_a = s_h_b = s_h_c = 0;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2016-12-21 15:01:56 -08:00
|
|
|
void ThermistorMath::setConfig(thermistor_conf_s *config) {
|
2016-01-11 14:01:33 -08:00
|
|
|
bool isSameConfig = memcmp(config, ¤tConfig, sizeof(currentConfig)) == 0;
|
2015-07-10 06:01:56 -07:00
|
|
|
if (isSameConfig) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(¤tConfig, config, sizeof(currentConfig));
|
2016-12-22 11:02:38 -08:00
|
|
|
prepareThermistorCurve(config);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|