
Eliminated module SamNonDuePin because its functions are now provided in CoreDuet Use updated emac module in CoreDuet
245 lines
7.7 KiB
C++
245 lines
7.7 KiB
C++
#include "MAX31855.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
|