From 5b1dfc20f33d4b3d60a7f61853fcfae60541d5a6 Mon Sep 17 00:00:00 2001 From: Dario Pennisi Date: Mon, 13 Jun 2022 19:27:46 +0200 Subject: [PATCH] added support for linearization and more precise temperature measurements --- src/utility/THERMOCOUPLE/MAX31855.cpp | 133 ++++++++++++++++++++++++-- src/utility/THERMOCOUPLE/MAX31855.h | 52 ++++++++++ 2 files changed, 175 insertions(+), 10 deletions(-) diff --git a/src/utility/THERMOCOUPLE/MAX31855.cpp b/src/utility/THERMOCOUPLE/MAX31855.cpp index 6053867..7b88827 100644 --- a/src/utility/THERMOCOUPLE/MAX31855.cpp +++ b/src/utility/THERMOCOUPLE/MAX31855.cpp @@ -19,6 +19,25 @@ #include "MAX31855.h" +const double MAX31855Class::Jm210_760[] ; +const double MAX31855Class::J760_1200[] ; +const double MAX31855Class::Km270_0[] ; +const double MAX31855Class::K0_1372[] ; + +const double MAX31855Class::InvJ_neg[] ; +const double MAX31855Class::InvJ0_760[] ; +const double MAX31855Class::InvJ760_1200[] ; + +const double MAX31855Class::InvK_neg[] ; +const double MAX31855Class::InvK0_500[] ; +const double MAX31855Class::InvK500_1372[] ; + +const MAX31855Class::coefftable MAX31855Class::CoeffJ[]; +const MAX31855Class::coefftable MAX31855Class::CoeffK[]; + +const MAX31855Class::coefftable MAX31855Class::InvCoeffJ[]; +const MAX31855Class::coefftable MAX31855Class::InvCoeffK[]; + MAX31855Class::MAX31855Class(int cs, SPIClass& spi) : _cs(cs), _spi(&spi), @@ -72,10 +91,78 @@ uint32_t MAX31855Class::readSensor() return read; } +double MAX31855Class::polynomial(double value, int tableEntries, coefftable const (*table) ) +{ + double output = 0; + double valuePower = 1; + for (int i=0;i0) { + voltage += 0.118597600000E+00 * exp( -0.118343200000E-03 * pow(temp-0.126968600000E+03, 2)); + } + return voltage; +} + +double MAX31855Class::mvtoTemp(int type, double voltage) { + coefftable const (*table); + int tableEntries; + + switch (type) { + case PROBE_J: + table = InvCoeffJ; + tableEntries = sizeof(InvCoeffJ)/sizeof(coefftable); + break; + case PROBE_K: + table = InvCoeffK; + tableEntries = sizeof(InvCoeffJ)/sizeof(coefftable); + break; + } + return polynomial(voltage, tableEntries, table); +} + float MAX31855Class::readTemperature(int type) { uint32_t rawword; - float celsius; + int32_t measuredTempInt; + int32_t measuredColdInt; + double measuredTemp; + double measuredCold; + double measuredVolt; rawword = readSensor(); @@ -83,23 +170,49 @@ float MAX31855Class::readTemperature(int type) if (rawword & 0x7) { return NAN; } - // The temperature is stored in the last 14 word's bits + // The cold junction temperature is stored in the last 14 word's bits + // whereas the ttermocouple temperature (non linearized) is in the topmost 18 bits // sent by the Thermocouple-to-Digital Converter + + // sign extend thermocouple value if (rawword & 0x80000000) { // Negative value, drop the lower 18 bits and explicitly extend sign bits. - rawword = 0xFFFFC000 | ((rawword >> 18) & 0x00003FFFF); + measuredTempInt = 0xFFFC0000 | ((rawword >> 18) & 0x00003FFFF); } else { // Positive value, just drop the lower 18 bits. - rawword >>= 18; + measuredTempInt = rawword>>18; } - // multiply for the LSB value - celsius = rawword * 0.25f; - if (type == PROBE_J) { - // conversion factor from K type to J type - celsius = celsius * 4/5; + // convert it to degrees + measuredTemp = measuredTempInt * 0.25f; + + // sign extend cold junction temperature + measuredColdInt = (rawword>>4)&0xfff; + if (measuredColdInt&0x800) { + // Negative value, sign extend + measuredColdInt |= 0xfffff000; } - return celsius; + + // convert it to degrees + measuredCold = measuredColdInt/16.0f; + + // now the tricky part... since MAX31855K is considering a linear response + // and is trimemd for K thermocouples, we have to convert the reading back + // to mV and then use NIST polynomial approximation to determine temperature + // we know that reading from chip is calculated as: + // temp = chip_temperature + thermocouple_voltage/0.041276f + // + // convert temperature to mV is accomplished converting the chip temperature + // to mV using NIST polynomial and then by adding the measured voltage + // calculated inverting the function above + // this way we calculate the voltage we would have measured if cold junction + // was at 0 degrees celsius + + measuredVolt = coldTempTomv(type, measuredCold)+(measuredTemp-measuredCold) * 0.041276f; + + // finally from the cold junction compensated voltage we calculate the temperature + // using NIST polynomial approximation for the thermocouple type we are using + return mvtoTemp(type,measuredVolt); } float MAX31855Class::readReferenceTemperature(int type) diff --git a/src/utility/THERMOCOUPLE/MAX31855.h b/src/utility/THERMOCOUPLE/MAX31855.h index f5c2b3a..6a05aae 100644 --- a/src/utility/THERMOCOUPLE/MAX31855.h +++ b/src/utility/THERMOCOUPLE/MAX31855.h @@ -43,6 +43,58 @@ class MAX31855Class { int _cs; SPIClass* _spi; SPISettings _spiSettings; + + // NIST coefficient tables + static constexpr double Jm210_760[] = { 0.000000000000E+00, 0.503811878150E-01, 0.304758369300E-04,-0.856810657200E-07, 0.132281952950E-09,-0.170529583370E-12, 0.209480906970E-15,-0.125383953360E-18, 0.156317256970E-22 }; + static constexpr double J760_1200[] = { 0.296456256810E+03,-0.149761277860E+01, 0.317871039240E-02,-0.318476867010E-05, 0.157208190040E-08,-0.306913690560E-12 }; + + static constexpr double Km270_0[] = { 0.000000000000E+00, 0.394501280250E-01, 0.236223735980E-04,-0.328589067840E-06,-0.499048287770E-08,-0.675090591730E-10,-0.574103274280E-12,-0.310888728940E-14,-0.104516093650E-16,-0.198892668780E-19,-0.163226974860E-22 }; + static constexpr double K0_1372[] = { -0.176004136860E-01, 0.389212049750E-01, 0.185587700320E-04,-0.994575928740E-07, 0.318409457190E-09,-0.560728448890E-12, 0.560750590590E-15,-0.320207200030E-18, 0.971511471520E-22,-0.121047212750E-25 }; + + static constexpr double InvJ_neg[] = { 0.0000000E+00, 1.9528268E+01, -1.2286185E+00, -1.0752178E+00, -5.9086933E-01, -1.7256713E-01, -2.8131513E-02,-2.3963370E-03,-8.3823321E-05}; + static constexpr double InvJ0_760[] = { 0.000000E+00, 1.978425E+01, -2.001204E-01, 1.036969E-02, -2.549687E-04, 3.585153E-06, -5.344285E-08, 5.099890E-10 }; + static constexpr double InvJ760_1200[] = { -3.11358187E+03, 3.00543684E+02, -9.94773230E+00, 1.70276630E-01, -1.43033468E-03, 4.73886084E-06 }; + + static constexpr double InvK_neg[] = { 0.0000000E+00, 2.5173462E+01, 1.1662878E+00, 1.0833638E+00, 8.9773540E-01, 3.7342377E-01, 8.6632643E-02, 1.0450598E-02, 5.1920577E-04 }; + static constexpr double InvK0_500[] = { 0.000000E+00, 2.508355E+01, 7.860106E-02, -2.503131E-01, 8.315270E-02, -1.228034E-02, 9.804036E-04, -4.413030E-05, 1.057734E-06, -1.052755E-08 }; + static constexpr double InvK500_1372[] = { -1.318058E+02, 4.830222E+01, -1.646031E+00, 5.464731E-02, -9.650715E-04, 8.802193E-06, -3.110810E-08 }; + + typedef struct { + int size; + double max; + const double *coeffs; + } coefftable; + + static constexpr coefftable CoeffJ []= { + {0,-210, NULL}, + {sizeof(Jm210_760) / sizeof(double), 760.0f, &Jm210_760[0]}, + {sizeof(J760_1200) / sizeof(double), 1200.0f, &J760_1200[0]} + }; + + static constexpr coefftable CoeffK []= { + {0,-270, NULL}, + {sizeof(Jm210_760) / sizeof(double), 0.0f, &Km270_0[0]}, + {sizeof(K0_1372) / sizeof(double), 1200.0f, &K0_1372[0]} + }; + + static constexpr coefftable InvCoeffJ []= { + {0,-0.895, NULL}, + {sizeof(InvJ_neg) / sizeof(double), 0.0f, &InvJ_neg[0]}, + {sizeof(InvJ0_760) / sizeof(double), 42.919f, &InvJ0_760[0]}, + {sizeof(InvJ760_1200) / sizeof(double), 69.533f, &InvJ760_1200[0]} + }; + + static constexpr coefftable InvCoeffK []= { + {0,-5.891, NULL}, + {sizeof(InvK_neg) / sizeof(double), 0.0f, &InvK_neg[0]}, + {sizeof(InvK0_500) / sizeof(double), 20.644f, &InvK0_500[0]}, + {sizeof(InvK500_1372) / sizeof(double), 54.886f, &InvK500_1372[0]}, + }; + + double mvtoTemp(int type, double voltage); + double coldTempTomv(int type, double temp); + double polynomial(double value, int tableEntries, coefftable const (*table) ); + }; extern MAX31855Class THERM;