diff --git a/src/shared/sensors/MAX31855/MAX31855.h b/src/shared/sensors/MAX31855/MAX31855.h index 2c5fb8067b12eec4bf015539d34927ffd6fa8a45..62be42ab10eabb7c66a507c9d67449cbc92eec01 100644 --- a/src/shared/sensors/MAX31855/MAX31855.h +++ b/src/shared/sensors/MAX31855/MAX31855.h @@ -29,6 +29,17 @@ namespace Boardcore { +/** + * @brief MAX31855 thermocouple sensor driver. + * + * The MAX31855 performs cold-junction compensation and digitizes the signal + * from a K, J, N, T, S, R, or E type thermocouple depending on the selected + * product variant. The data is output in a signed 14-bit, SPI-compatible, + * read-only format. This converter resolves temperatures to 0.25°C, allows + * readings as high as +1800°C and as low as -270°C, and exhibits thermocouple + * accuracy of ±2°C for temperatures ranging from -200°C to +700°C for K-type + * thermocouples. + */ class MAX31855 : public Sensor<TemperatureData> { public: diff --git a/src/shared/sensors/MAX31856/MAX31856.cpp b/src/shared/sensors/MAX31856/MAX31856.cpp index 325e1ac8aaad44ce19d95db2c63db613fc5e8d80..ad1d5475980f62eea2eb31e4df99e49778081b53 100644 --- a/src/shared/sensors/MAX31856/MAX31856.cpp +++ b/src/shared/sensors/MAX31856/MAX31856.cpp @@ -49,6 +49,9 @@ bool MAX31856::init() // Set thermocouple type setThermocoupleType(type); + // Reset the cold junction offset + setColdJunctionOffset(0); + // Enable continuous conversion mode spi.writeRegister(CR0, CR0_CMODE); @@ -81,46 +84,44 @@ void MAX31856::setThermocoupleType(ThermocoupleType type) spi.writeRegister(CR1, static_cast<uint8_t>(type)); } -TemperatureData MAX31856::sampleImpl() +void MAX31856::setColdJunctionOffset(float offset) { - int32_t sample; + SPITransaction spi{slave}; + spi.writeRegister(CJTO, + static_cast<int8_t>(offset * 1 / CJ_TEMP_LSB_VALUE)); +} - { - SPITransaction spi{slave}; - sample = spi.readRegister24(LTCBH); - } +MAX31856Data MAX31856::sampleImpl() +{ + SPITransaction spi{slave}; + int16_t cjRaw = spi.readRegister16(CJTH); + int32_t tcRaw = spi.readRegister24(LTCBH); - TemperatureData result{}; + MAX31856Data result; result.temperatureTimestamp = TimestampTimer::getTimestamp(); - // Sign extension - sample = sample << 8; - sample = sample >> (8 + 5); - - // Convert the integer and decimal part separately - result.temperature = static_cast<float>(sample) * 0.007812; + // The register has: + // - A leading sign bit (which is actually two's complement) + // - The other 18 bits + // - 5 unused trailing bits - return result; -} - -TemperatureData MAX31856::readInternalTemperature() -{ - uint16_t sample[2]; - - { - SPITransaction spi{slave}; - spi.read16(sample, sizeof(sample)); - } + // Since the 24 bit registers value is stored into a 32 bit variable, first + // we make a left shift to move the sign bit in the 31st bit, and then a + // right shift to remove the unused bits. + // This automatically extends the sign + tcRaw = tcRaw << 8; + tcRaw = tcRaw >> (8 + 5); - TemperatureData result{}; - result.temperatureTimestamp = TimestampTimer::getTimestamp(); + // Here we just make a right shift as the sign bit is already in the 15th + // bit. This will also perform the sign extension like in sampleImpl() + cjRaw = cjRaw >> 4; - // Extract data bits - sample[1] = sample[1] >> 4; + // Convert the raw value into temperature + result.temperature = static_cast<float>(tcRaw) * TC_TEMP_LSB_VALUE; - // Convert the integer and decimal part separately - result.temperature = static_cast<float>(sample[1] >> 4); - result.temperature += static_cast<float>(sample[1] & 0xF) * 0.0625; + // Convert the raw value into temperature + result.coldJunctionTemperature = + static_cast<float>(cjRaw) * CJ_TEMP_LSB_VALUE; return result; } diff --git a/src/shared/sensors/MAX31856/MAX31856.h b/src/shared/sensors/MAX31856/MAX31856.h index 97fbd817e8eb3e13766eaea09c847c644a836c4a..7e04e481a5b820ccc2fb37597419cea2b341394a 100644 --- a/src/shared/sensors/MAX31856/MAX31856.h +++ b/src/shared/sensors/MAX31856/MAX31856.h @@ -26,10 +26,25 @@ #include <drivers/spi/SPIDriver.h> #include <sensors/Sensor.h> +#include "MAX31856Data.h" + namespace Boardcore { -class MAX31856 : public Sensor<TemperatureData> +/** + * @brief MAX31855 thermocouple sensor driver. + * + * The MAX31856 performs cold-junction compensation and digitizes the signal + * from any type of thermocouple. This converter resolves temperatures to + * 0.0078125°C, allows readings as high as +1800°C and as low as -210°C + * (depending on thermocouple type), and exhibits thermocouple voltage + * measurement accuracy of ±0.15%. The thermocouple inputs are protected against + * over voltage conditions up to ±45V. A lookup table (LUT) stores linearity + * correction data for several types of thermocouples (K, J, N, R, S, T, E, and + * B). Line frequency filtering of 50Hz and 60Hz is included, as is thermocouple + * fault detection + */ +class MAX31856 : public Sensor<MAX31856Data> { public: enum class ThermocoupleType : uint8_t @@ -63,13 +78,10 @@ public: void setThermocoupleType(ThermocoupleType type); - /** - * @brief Read the device internal temperature (cold junction). - */ - TemperatureData readInternalTemperature(); + void setColdJunctionOffset(float offset); private: - TemperatureData sampleImpl() override; + MAX31856Data sampleImpl() override; SPISlave slave; ThermocoupleType type; @@ -99,6 +111,9 @@ private: static constexpr uint8_t CR0_CMODE = 0x1 << 7; static constexpr uint8_t CR0_OCFAULT_0 = 0x1 << 4; static constexpr uint8_t SR_OPEN = 0x1 << 0; + + static constexpr float TC_TEMP_LSB_VALUE = 0.007812; // [°C] + static constexpr float CJ_TEMP_LSB_VALUE = 0.0625; // [°C] }; } // namespace Boardcore diff --git a/src/shared/sensors/MAX31856/MAX31856Data.h b/src/shared/sensors/MAX31856/MAX31856Data.h new file mode 100644 index 0000000000000000000000000000000000000000..ebd5742eb02c1a6d117ec4e061d08187b4141a0f --- /dev/null +++ b/src/shared/sensors/MAX31856/MAX31856Data.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <utils/Debug.h> + +#include <cstdio> +#include <map> + +#include "sensors/SensorData.h" + +namespace Boardcore +{ + +struct MAX31856Data : public TemperatureData +{ + float coldJunctionTemperature; + + MAX31856Data() : MAX31856Data{0, 0.0, 0.0} {} + + MAX31856Data(uint64_t temperatureTimestamp, float temperature) + : MAX31856Data(temperatureTimestamp, temperature, 0) + { + } + + MAX31856Data(uint64_t temperatureTimestamp, float temperature, + float coldJunctionTemperature) + : TemperatureData{temperatureTimestamp, temperature}, + coldJunctionTemperature(coldJunctionTemperature) + { + } + + static std::string header() + { + return "temperatureTimestamp,temperature,coldJunctionTemperature\n"; + } + + void print(std::ostream& os) const + { + os << temperatureTimestamp << "," << temperature << "," + << coldJunctionTemperature << "\n"; + } +}; + +} // namespace Boardcore \ No newline at end of file diff --git a/src/tests/sensors/test-max31856.cpp b/src/tests/sensors/test-max31856.cpp index 8cf44008d1929c29f87cdfbb14ca5f131fc5fb58..6d4a3edd655e93480c365ce5f258f72d7be9fa82 100644 --- a/src/tests/sensors/test-max31856.cpp +++ b/src/tests/sensors/test-max31856.cpp @@ -60,13 +60,23 @@ int main() printf("The thermocouple is connected\n"); } + // Wait for one sample to be made + Thread::sleep(100); + + // Set cold junction offset + // This should set the cold junction temperature to 25° + sensor.sample(); + auto sample = sensor.getLastSample(); + sensor.setColdJunctionOffset(sample.coldJunctionTemperature - 25); + printf("Offset: %f\n", sample.coldJunctionTemperature - 25); + while (true) { sensor.sample(); - TemperatureData sample = sensor.getLastSample(); + sample = sensor.getLastSample(); - printf("\r\e[0K[%.2f] %.2f", sample.temperatureTimestamp / 1e6, - sample.temperature); + printf("[%.2f] %f %f\n", sample.temperatureTimestamp / 1e6, + sample.temperature, sample.coldJunctionTemperature); Thread::sleep(100); }