diff options
Diffstat (limited to 'src/Heating/TemperatureSensor.cpp')
-rw-r--r-- | src/Heating/TemperatureSensor.cpp | 449 |
1 files changed, 0 insertions, 449 deletions
diff --git a/src/Heating/TemperatureSensor.cpp b/src/Heating/TemperatureSensor.cpp deleted file mode 100644 index 74a9c399..00000000 --- a/src/Heating/TemperatureSensor.cpp +++ /dev/null @@ -1,449 +0,0 @@ -#include "TemperatureSensor.h" -#include "Platform.h" -#include "RepRap.h" - -// MAX31855 thermocouple chip -// -// The MAX31855 continuously samples a Type K thermocouple. When the MAX31855 -// is selected via its chip select (CS) pin, it unconditionally writes a 32 bit -// sequence onto the bus. This sequence is designed such that we need only -// interpret the high 16 bits in order to ascertain the temperature reading and -// whether there a fault condition exists. The low 16 bits provide the -// MAX31855's cold junction temperature (and thus the board temperature) as -// well as finer detail on any existing fault condition. -// -// The temperature read from the chip is a signed, two's-complement integer. -// As it is a 14 bit value (in units of one-quarter degrees Celsius), we -// convert it to a proper signed 16 bit value by adding two high bits. The -// high bits added should both be zero if the value is positive (highest bit -// of the 14 bit value is zero), or both be one if the value is negative -// (highest bit of the 14 bit value is one). -// -// Note Bene: there's a Arduino Due sketch floating about the internet which -// gets this wrong and for negative temperatures generates incorrect values. -// E.g, -2047C for what should be -1C; -1798C for what should be -250C. The -// values of -1C and -250C are shown as examples in Table 4 of the datasheet -// for the MAX21855.) The incorrect Arduino Due sketch appears in, and may be -// from, the book Arduino Sketches: Tools and Techniques for Programming -// Wizardry, James A. Langbridge, January 12, 2015, John Wiley & Sons. - -// Bits -- Interpretation -// ----- ----------------------------------------------------------------- -// 31:18 -- 14 bit, signed thermocouple temperature data. Units of 0.25 C -// 17 -- Reserved -// 16 -- Fault indicator (1 if fault detected; 0 otherwise) -// 15:04 -- 12 bit, signed cold junction temperature data. Units of 0.0625 C -// 03 -- Reserved -// 02 -- SCV fault; reads 1 if the thermocouple is shorted to Vcc -// 01 -- SCG fault; reads 1 if the thermocouple is shorted to ground -// 00 -- OC fault; reads 1 if the thermocouple is not connected (open) - -// For purposes of setting bit transfer widths and timings, we need to use a -// Peripheral Channel Select (PCS). Use channel #3 as it is unlikely to be -// used by anything else as the Arduino Due leaves pin 78 unconnected. -// -// No warranty given or implied, use at your own risk. -// dan.newman@mtbaldy.us -// GPL v3 - -const uint32_t MAX31855_Frequency = 4000000; // maximum for MAX31855 is 5MHz -const uint32_t MAX31865_Frequency = 4000000; // maximum for MAX31865 is also 5MHz -const uint32_t MCP3204_Frequency = 1000000; // maximum for MCP3204 is 1MHz @ 2.7V, will be slightly higher at 3.3V - -// SPI modes: -// If the inactive state of SCL is LOW (CPOL = 0) (in the case of the MAX31865, this is sampled on the falling edge of CS): -// The MAX31855 sets up the first data bit after the falling edge of CLK, and changes the data on each falling clock edge. -// So the SAM needs to sample data on the rising clock edge. This requires NCPHA = 1. -// The MAX31865 changes data after the rising edge of CLK, and samples input data on the falling edge. -// This requires NCPHA = 0. -// The MCP3204 samples input data on the rising edge and changes the output data on the rising edge. - -const uint8_t MAX31855_SpiMode = SPI_MODE_0; -const uint8_t MAX31865_SpiMode = SPI_MODE_1; -const uint8_t MCP3204_SpiMode = SPI_MODE_0; - -// Define the minimum interval between readings. The MAX31865 needs 62.5ms in 50Hz filter mode. -const uint32_t MinimumReadInterval = 100; // minimum interval between reads, in milliseconds - -// Table of temperature vs. MAX31865 result for PT100 thermistor, from the MAX31865 datasheet -struct TempTableEntry -{ - int16_t temperature; - uint16_t adcReading; -}; - -static const TempTableEntry tempTable[] = -{ - {-30, 7227}, - {-20, 7550}, - {-10, 7871}, - {0, 8192}, - {10, 8512}, - {20, 8830}, - {30, 9148}, - {40, 9465}, - {50, 9781}, - {60, 10096}, - {70, 10410}, - {80, 10723}, - {90, 11035}, - {100, 11346}, - {110, 11657}, - {120, 11966}, - {130, 12274}, - {140, 12582}, - {150, 12888}, - {160, 13194}, - {170, 13498}, - {180, 13802}, - {190, 14104}, - {200, 14406}, - {225, 15156}, - {250, 15901}, - {275, 16639}, - {300, 17371}, - {325, 18098}, - {350, 18818}, - {375, 19533}, - {400, 20242}, - {425, 20945}, - {450, 21642}, - {475, 22333}, - {500, 23018}, - {525, 23697}, - {550, 24370} -}; - -const size_t NumTempTableEntries = sizeof(tempTable)/sizeof(tempTable[0]); - -// Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus. -void TemperatureSensor::InitThermocouple(uint8_t cs) -{ - device.csPin = cs; - device.spiMode = MAX31855_SpiMode; - device.clockFrequency = MAX31855_Frequency; - sspi_master_init(&device, 8); - - lastReadingTime = millis(); - lastResult = TemperatureError::success; - lastTemperature = 0.0; -} - -// Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus. -void TemperatureSensor::InitRtd(uint8_t cs) -{ - device.csPin = cs; - device.spiMode = MAX31865_SpiMode; - device.clockFrequency = MAX31865_Frequency; - sspi_master_init(&device, 8); - - TemperatureError rslt; - for (unsigned int i = 0; i < 3; ++i) // try 3 times - { - rslt = TryInitRtd(); - if (rslt == TemperatureError::success) - { - break; - } - delay(MinimumReadInterval); - } - - lastReadingTime = millis(); - lastResult = rslt; - lastTemperature = 0.0; - - if (rslt != TemperatureError::success) - { - reprap.GetPlatform().MessageF(GENERIC_MESSAGE, "Error: failed to initialise RTD: %s\n", TemperatureErrorString(rslt)); - } -} - -// Try to initialise the RTD -TemperatureError TemperatureSensor::TryInitRtd() const -{ - // Note that to get the MAX31865 to do continuous conversions, we need to set the bias bit as well as the continuous-conversion bit - static const uint8_t modeData[2] = { 0x80, 0xC3 }; // write register 0, bias on, auto conversion, clear errors, 50Hz - uint32_t rawVal; - TemperatureError sts = DoSpiTransaction(modeData, 2, rawVal); - - if (sts == TemperatureError::success) - { - static const uint8_t readData[2] = { 0x00, 0x00 }; // read register 0 - sts = DoSpiTransaction(readData, 2, rawVal); - } - - //debugPrintf("Status %d data %04x\n", (int)sts, rawVal); - return (sts == TemperatureError::success && (uint8_t)rawVal != 0xC1) - ? TemperatureError::badResponse - : sts; -} - -// Initialise the linear ADC -void TemperatureSensor::InitLinearAdc(uint8_t cs) -{ - device.csPin = cs; - device.spiMode = MCP3204_SpiMode; - device.clockFrequency = MCP3204_Frequency; - sspi_master_init(&device, 8); - - for (unsigned int i = 0; i < 3; ++i) // try 3 times - { - TryGetLinearAdcTemperature(); - if (lastResult == TemperatureError::success) - { - break; - } - delay(MinimumReadInterval); - } - - lastReadingTime = millis(); - - if (lastResult != TemperatureError::success) - { - reprap.GetPlatform().MessageF(GENERIC_MESSAGE, "Error: failed to initialise daughter board ADC: %s\n", TemperatureErrorString(lastResult)); - } - -} - -TemperatureError TemperatureSensor::GetThermocoupleTemperature(float& t) -{ - if (inInterrupt() || millis() - lastReadingTime < MinimumReadInterval) - { - t = lastTemperature; - } - else - { - uint32_t rawVal; - TemperatureError sts = DoSpiTransaction(nullptr, 4, rawVal); - if (sts != TemperatureError::success) - { - lastResult = sts; - } - else - { - lastReadingTime = millis(); - - if ((rawVal & 0x00020008) != 0) - { - // These two bits should always read 0. Likely the entire read was 0xFF 0xFF which is not uncommon when first powering up - lastResult = TemperatureError::ioError; - } - else if ((rawVal & 0x00010007) != 0) // check the fault bits - { - // Check for three more types of bad reads as we set the response code: - // 1. A read in which the fault indicator bit (16) is set but the fault reason bits (0:2) are all clear; - // 2. A read in which the fault indicator bit (16) is clear, but one or more of the fault reason bits (0:2) are set; and, - // 3. A read in which more than one of the fault reason bits (0:1) are set. - if ((rawVal & 0x00010000) == 0) - { - // One or more fault reason bits are set but the fault indicator bit is clear - lastResult = TemperatureError::ioError; - } - else - { - // At this point we are assured that bit 16 (fault indicator) is set and that at least one of the fault reason bits (0:2) are set. - // We now need to ensure that only one fault reason bit is set. - uint8_t nbits = 0; - if (rawVal & 0x01) - { - // Open Circuit - ++nbits; - lastResult = TemperatureError::openCircuit; - } - if (rawVal & 0x02) - { - // Short to ground; - ++nbits; - lastResult = TemperatureError::shortToGround; - } - if (rawVal && 0x04) - { - // Short to Vcc - ++nbits; - lastResult = TemperatureError::shortToVcc; - } - - if (nbits != 1) - { - // Fault indicator was set but a fault reason was not set (nbits == 0) or too many fault reason bits were set (nbits > 1). - // Assume that a communication error with the MAX31855 has occurred. - lastResult = TemperatureError::ioError; - } - } - } - else - { - rawVal >>= 18; // shift the 14-bit temperature data to the bottom of the word - rawVal |= (0 - (rawVal & 0x2000)); // sign-extend the sign bit - - // And convert to from units of 1/4C to 1C - t = lastTemperature = (float)(0.25 * (float)(int32_t)rawVal); - lastResult = TemperatureError::success; - } - } - } - return lastResult; -} - -TemperatureError TemperatureSensor::GetRtdTemperature(float& t) -{ - if (inInterrupt() || millis() - lastReadingTime < MinimumReadInterval) - { - t = lastTemperature; - } - else - { - static const uint8_t dataOut[4] = {0, 55, 55, 55}; // read registers 0 (control), 1 (MSB) and 2 (LSB) - uint32_t rawVal; - TemperatureError sts = DoSpiTransaction(dataOut, 4, rawVal); - - if (sts != TemperatureError::success) - { - lastResult = sts; - } - else - { - lastReadingTime = millis(); - if (((rawVal & 0x00C10000) != 0xC10000) -#if 0 - // We no longer check the error status bit, because it seems to be impossible to clear it once it has been set. - // Perhaps we would need to exit continuous reading mode to do so, and then re-enable it afterwards. But this would - // take to long. -#else - || (rawVal & 1) != 0 -#endif - ) - { - // Either the continuous conversion bit has got cleared, or the fault bit has been set - TryInitRtd(); - lastResult = TemperatureError::hardwareError; - } - else - { - uint16_t adcVal = (rawVal >> 1) & 0x7FFF; - - // Formally-verified binary search routine, adapted from one of the eCv examples - size_t low = 0u, high = NumTempTableEntries; - while (high > low) - keep(low <= high; high <= NumTempTableEntries) - keep(low == 0u || tempTable[low - 1u].adcReading < adcVal) - keep(high == NumTempTableEntries || adcVal <= tempTable[high].adcReading) - decrease(high - low) - { - size_t mid = (high - low)/2u + low; // get the mid point, avoiding arithmetic overflow - if (adcVal <= tempTable[mid].adcReading) - { - high = mid; - } - else - { - low = mid + 1u; - } - } - assert(low <= NumTempTableEntries); - assert(low == 0 || tempTable[low - 1] < adcVal); - assert(low == NumTempTableEntries || adcVal <= tempTable[low]); - - if (low == 0) // if off the bottom of the table - { - lastResult = TemperatureError::shortCircuit; - } - else if (low >= NumTempTableEntries) // if off the top of the table - { - lastResult = TemperatureError::openCircuit; - } - else - { - const float interpolationFraction = (float)(adcVal - tempTable[low - 1].adcReading)/(float)(tempTable[low].adcReading - tempTable[low - 1].adcReading); - t = lastTemperature = ((float)(tempTable[low].temperature - tempTable[low - 1].temperature) * interpolationFraction) - + (float)tempTable[low - 1].temperature; - //debugPrintf("raw %u low %u interp %f temp %f\n", adcVal, low, interpolationFraction, *t); - lastResult = TemperatureError::success; - } - } - } - } - return lastResult; -} - -TemperatureError TemperatureSensor::GetLinearAdcTemperature(float& t) -{ - if (!inInterrupt() && millis() - lastReadingTime >= MinimumReadInterval) - { - TryGetLinearAdcTemperature(); - } - - t = lastTemperature; - return lastResult; -} - -// Try to get a temperature reading from the linear ADC by doing an SPI transaction -void TemperatureSensor::TryGetLinearAdcTemperature() -{ - // The MCP3204 waits for a high input input bit before it does anything. Call this clock 1. - // The next input bit it high for single-ended operation, low for differential. This is clock 2. - // The next 3 input bits are the channel selection bits. These are clocks 3..5. - // Clock 6 produces a null bit on its trailing edge, which is read by the processor on clock 7. - // Clocks 7..18 produce data bits B11..B0 on their trailing edges, which are read by the MCU on the leading edges of clocks 8-19. - // If we supply further clocks, then clocks 18..29 are the same data but LSB first, omitting bit 0. - // Clocks 30 onwards will be zeros. - // So we need to use at least 19 clocks. We round this up to 24 clocks, and we check that the extra 5 bits we receive are the 5 least significant data bits in reverse order. - - static const uint8_t adcData[] = { 0xC0, 0x00, 0x00 }; // start bit, single ended, channel 0 - uint32_t rawVal; - lastResult = DoSpiTransaction(adcData, 3, rawVal); - //debugPrintf("ADC data %u\n", rawVal); - - if (lastResult == TemperatureError::success) - { - const uint32_t adcVal1 = (rawVal >> 5) & ((1 << 13) - 1); - const uint32_t adcVal2 = ((rawVal & 1) << 5) | ((rawVal & 2) << 3) | ((rawVal & 4) << 1) | ((rawVal & 8) >> 1) | ((rawVal & 16) >> 3) | ((rawVal & 32) >> 5); - if (adcVal1 >= 4096 || adcVal2 != (adcVal1 & ((1 << 6) - 1))) - { - lastResult = TemperatureError::badResponse; - } - else - { - lastTemperature = MinLinearAdcTemp + (LinearAdcDegCPerCount * (float)adcVal1); - } - } -} - -// Send and receive 1 to 4 bytes of data and return the result as a single 32-bit word -TemperatureError TemperatureSensor::DoSpiTransaction(const uint8_t dataOut[], size_t nbytes, uint32_t& rslt) const -{ - if (!sspi_acquire()) - { - return TemperatureError::busBusy; - } - - sspi_master_setup_device(&device); - delayMicroseconds(1); - sspi_select_device(&device); - delayMicroseconds(1); - - uint8_t rawBytes[4]; - spi_status_t sts = sspi_transceive_packet(dataOut, rawBytes, nbytes); - - delayMicroseconds(1); - sspi_deselect_device(&device); - delayMicroseconds(1); - - sspi_release(); - - if (sts != SPI_OK) - { - return TemperatureError::timeout; - } - - rslt = rawBytes[0]; - for (size_t i = 1; i < nbytes; ++i) - { - rslt <<= 8; - rslt |= rawBytes[i]; - } - - return TemperatureError::success; -} - -// End |