This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
reprapfirmware-dc42/Libraries/MAX31855/MAX31855.cpp
David Crocker f5c4bdc770 Version 1.09r
Added timeout to output buffers destined for USB
Fixed bugs in thermocouple code
Reallocated thermocouple pin numbers
Made Roland mill and inkjet support conditional and normally disabled
Fixed issue with thermocouple code messing up the timekeeping system,
which was suspected of causing the network to be unreliable
Added support for M577 (thanks chrishamm)
2016-01-16 23:10:36 +00:00

246 lines
7.8 KiB
C++

#include "MAX31855.h"
#include "SamNonDuePin.h"
#include "spi_master.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
#define PERIPHERAL_CHANNEL_ID 3
#define PERIPHERAL_CHANNEL_CS_PIN 78 // NPCS3
// 5.0 MHz Max clock frequency when clocking data out of a MAX31855
#define MAX31855_MAX_FREQ 5000000u
// Delay before send should be at least 100 ns
#define MAX31855_DLYBS (1u + (F_CPU / 10000000u))
// Delay between consecutive transfers should be 0
#define MAX31855_DLYBCT 0
MAX31855::MAX31855(uint8_t cs, bool deferInit) : initialized(false)
{
if (!deferInit)
{
Init(cs);
}
}
// Perform the actual hardware initialization for attaching and using this
// device on the SPI hardware bus.
//
// A deferred initialization model is permitted so that the
// firmware can declare static instances of these devices at compiled time
// BUT not actually have them configure themselves unless gcode commands
// are issued which explicitly request the devices. Otherwise, pins intended
// for other purposes may be implicitly usurped.
void MAX31855::Init(uint8_t cs)
{
if (!initialized)
{
// Let the SPI library configure our actual CS as an output and
// pull it HIGH. We do not bother the configure the NPCS3 peripheral
device.cs = cs; // Chip select
device.id = PERIPHERAL_CHANNEL_ID; // Peripheral channel
device.bits = SPI_CSR_BITS_16_BIT; // 16 bit data transfers
spi_master_init(SPI0, device.cs, -1);
initialized = true;
}
}
// Use our own read routine; the ASF SPI read routines only do 8 bit data transfers
status_code MAX31855::readRaw(uint16_t *raw) const
{
for (uint8_t i = 0; i < 2; i++)
{
uint32_t timeout = SPI_TIMEOUT;
while (!spi_is_tx_ready(SPI0))
{
if (!timeout--)
{
return ERR_TMO;
}
}
SPI0->SPI_TDR = (i != 1) ? 0x00000000 : 0x00000000 | SPI_TDR_LASTXFER;
timeout = SPI_TIMEOUT;
while (!spi_is_rx_ready(SPI0))
{
if (!timeout--)
{
return ERR_TMO;
}
}
*raw++ = SPI0->SPI_RDR;
}
return STATUS_OK;
}
MAX31855_error MAX31855::getTemperature(float *t) const
{
// Assume properly initialized
// Ensure that the configuration is as needed; another SPI0 consumer
// may have changed the bus speed and/or timing delays.
spi_master_setup_device(SPI0, &device, 0, MAX31855_MAX_FREQ, 0);
spi_set_transfer_delay(SPI0, device.id, MAX31855_DLYBS, MAX31855_DLYBCT);
// Select the device; enable CS (set it LOW)
spi_select_device(SPI0, &device);
// Read in 32 bits
uint16_t raw[2];
if (readRaw(raw) != STATUS_OK)
{
return MAX31855_ERR_TMO;
}
// Deselect the device; disable CS (set it HIGH)
spi_deselect_device(SPI0, &device);
if ((raw[0] & 0x02) || (raw[1] & 0x08))
{
// These two bits should always read 0
// Likely the entire read was 0xFF 0xFF which is not uncommon when first powering up
return MAX31855_ERR_IO;
}
// The MAX31855 response is designed such that we can look at
// just the high 16 bits and know the temperature and whether
// an error occurred. The low 16 bits contain the cold junction
// temperature and finer detail on the nature of any error.
// We also want to check for three more types of bad reads:
//
// 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.
//
// We will perform those three sanity checks as we set the error response code.
if ((raw[0] & 0x01) || (raw[1] & 0x07))
{
if (!(raw[0] & 0x01))
{
// One or more fault reason bits are set but the fault indicator bit is clear?
return MAX31855_ERR_IO;
}
// 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;
MAX31855_error err;
if (raw[1] & 0x01)
{
// Open Circuit
++nbits;
err = MAX31855_ERR_OC;
}
if (raw[1] & 0x02)
{
// Short to ground;
++nbits;
err = MAX31855_ERR_SCG;
}
if (raw[1] && 0x04)
{
// Short to Vcc
++nbits;
err = MAX31855_ERR_SCV;
}
if (nbits == 1)
{
// Looks like a legitimate error response: bit 16 was set and only one of bits 0:2 was set
return err;
}
// 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.
return MAX31855_ERR_IO;
}
// Note, the reading is negative if raw[0] & 0x8000 is nonzero
// Shift the data
raw[0] >>= 2;
int16_t temp = (int16_t)(raw[0] & 0x3fff);
// Handle negative temperatures
if (raw[0] & 0x2000)
{
temp |= 0xc000;
}
// And convert to from units of 1/4C to 1C
*t = (float)(0.25 * temp);
// Success!
return MAX31855_OK;
}
const char* MAX31855::errorStr(MAX31855_error err) const
{
switch (err)
{
default : return "unknown MAX31855 error";
case MAX31855_OK : return "successful temperature read";
case MAX31855_ERR_SCV : return "thermocouple is shorted to +Vcc";
case MAX31855_ERR_SCG : return "thermocouple is shorted to ground";
case MAX31855_ERR_OC : return "thermocouple is broken (open)";
case MAX31855_ERR_TMO : return "error communicating with MAX31855; read timed out";
case MAX31855_ERR_IO : return "error communicating with MAX31855; disconnected?";
}
}
// End