Merge upstream changes from Marlin 2.1.1
This commit is contained in:
@@ -40,31 +40,27 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
// Useful for RTD debugging.
|
||||
//#define MAX31865_DEBUG
|
||||
//#define MAX31865_DEBUG_SPI
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_MAX31865 && !USE_ADAFRUIT_MAX31865
|
||||
|
||||
//#include <SoftwareSPI.h> // TODO: switch to SPIclass/SoftSPI
|
||||
#include "MAX31865.h"
|
||||
|
||||
#ifndef MAX31865_MIN_SAMPLING_TIME_MSEC
|
||||
#define MAX31865_MIN_SAMPLING_TIME_MSEC 0
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_MAX31865)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
// The maximum speed the MAX31865 can do is 5 MHz
|
||||
SPISettings MAX31865::spiConfig = SPISettings(
|
||||
#if defined(TARGET_LPC1768)
|
||||
SPI_QUARTER_SPEED
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
SPI_CLOCK_DIV4
|
||||
#else
|
||||
500000
|
||||
#endif
|
||||
, MSBFIRST
|
||||
, SPI_MODE_1 // CPOL0 CPHA1
|
||||
TERN(TARGET_LPC1768, SPI_QUARTER_SPEED, TERN(ARDUINO_ARCH_STM32, SPI_CLOCK_DIV4, 500000)),
|
||||
MSBFIRST,
|
||||
SPI_MODE1 // CPOL0 CPHA1
|
||||
);
|
||||
|
||||
#ifndef LARGE_PINMAP
|
||||
#if DISABLED(LARGE_PINMAP)
|
||||
|
||||
/**
|
||||
* Create the interface object using software (bitbang) SPI for PIN values
|
||||
@@ -76,10 +72,10 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param spi_clk the SPI clock pin to use
|
||||
*/
|
||||
MAX31865::MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) {
|
||||
_cs = spi_cs;
|
||||
_mosi = spi_mosi;
|
||||
_miso = spi_miso;
|
||||
_sclk = spi_clk;
|
||||
cselPin = spi_cs;
|
||||
mosiPin = spi_mosi;
|
||||
misoPin = spi_miso;
|
||||
sclkPin = spi_clk;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,11 +85,11 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param spi_cs the SPI CS pin to use along with the default SPI device
|
||||
*/
|
||||
MAX31865::MAX31865(int8_t spi_cs) {
|
||||
_cs = spi_cs;
|
||||
_sclk = _miso = _mosi = -1;
|
||||
cselPin = spi_cs;
|
||||
sclkPin = misoPin = mosiPin = -1;
|
||||
}
|
||||
|
||||
#else
|
||||
#else // LARGE_PINMAP
|
||||
|
||||
/**
|
||||
* Create the interface object using software (bitbang) SPI for PIN values
|
||||
@@ -106,13 +102,11 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param spi_clk the SPI clock pin to use
|
||||
* @param pin_mapping set to 1 for positive pin values
|
||||
*/
|
||||
MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi,
|
||||
uint32_t spi_miso, uint32_t spi_clk,
|
||||
uint8_t pin_mapping) {
|
||||
_cs = spi_cs;
|
||||
_mosi = spi_mosi;
|
||||
_miso = spi_miso;
|
||||
_sclk = spi_clk;
|
||||
MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso, uint32_t spi_clk, uint8_t pin_mapping) {
|
||||
cselPin = spi_cs;
|
||||
mosiPin = spi_mosi;
|
||||
misoPin = spi_miso;
|
||||
sclkPin = spi_clk;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,156 +118,247 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param pin_mapping set to 1 for positive pin values
|
||||
*/
|
||||
MAX31865::MAX31865(uint32_t spi_cs, uint8_t pin_mapping) {
|
||||
_cs = spi_cs;
|
||||
_sclk = _miso = _mosi = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
|
||||
cselPin = spi_cs;
|
||||
sclkPin = misoPin = mosiPin = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
|
||||
}
|
||||
|
||||
#endif // LARGE_PINMAP
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Instance & Class methods
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the SPI interface and set the number of RTD wires used
|
||||
*
|
||||
* @param wires The number of wires in enum format. Can be MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
|
||||
* @param zero The resistance of the RTD at 0 degC, in ohms.
|
||||
* @param ref The resistance of the reference resistor, in ohms.
|
||||
* @param wires The number of wires as an enum: MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
|
||||
* @param zero_res The resistance of the RTD at 0°C, in ohms.
|
||||
* @param ref_res The resistance of the reference resistor, in ohms.
|
||||
* @param wire_res The resistance of the wire connecting the sensor to the RTD, in ohms.
|
||||
*/
|
||||
void MAX31865::begin(max31865_numwires_t wires, float zero, float ref) {
|
||||
Rzero = zero;
|
||||
Rref = ref;
|
||||
void MAX31865::begin(max31865_numwires_t wires, const_float_t zero_res, const_float_t ref_res, const_float_t wire_res) {
|
||||
resNormalizer = 100.0f / zero_res; // reciprocal of resistance, scaled by 100
|
||||
refRes = ref_res;
|
||||
wireRes = wire_res;
|
||||
|
||||
OUT_WRITE(_cs, HIGH);
|
||||
pinMode(cselPin, OUTPUT);
|
||||
digitalWrite(cselPin, HIGH);
|
||||
|
||||
if (_sclk != TERN(LARGE_PINMAP, -1UL, -1)) {
|
||||
// Define pin modes for Software SPI
|
||||
#ifdef MAX31865_DEBUG
|
||||
SERIAL_ECHOLN("Initializing MAX31865 Software SPI");
|
||||
#endif
|
||||
|
||||
OUT_WRITE(_sclk, LOW);
|
||||
SET_OUTPUT(_mosi);
|
||||
SET_INPUT(_miso);
|
||||
}
|
||||
if (sclkPin != TERN(LARGE_PINMAP, -1UL, 255))
|
||||
softSpiInit(); // Define pin modes for Software SPI
|
||||
else {
|
||||
// Start and configure hardware SPI
|
||||
#ifdef MAX31865_DEBUG
|
||||
SERIAL_ECHOLN("Initializing MAX31865 Hardware SPI");
|
||||
#endif
|
||||
|
||||
SPI.begin();
|
||||
DEBUG_ECHOLNPGM("Init MAX31865 Hardware SPI");
|
||||
SPI.begin(); // Start and configure hardware SPI
|
||||
}
|
||||
|
||||
setWires(wires);
|
||||
enableBias(false);
|
||||
autoConvert(false);
|
||||
clearFault();
|
||||
initFixedFlags(wires);
|
||||
|
||||
#ifdef MAX31865_DEBUG_SPI
|
||||
#ifndef LARGE_PINMAP
|
||||
SERIAL_ECHOLNPGM(
|
||||
"Regular begin call with _cs: ", _cs,
|
||||
" _miso: ", _miso,
|
||||
" _sclk: ", _sclk,
|
||||
" _mosi: ", _mosi
|
||||
);
|
||||
#else
|
||||
SERIAL_ECHOLNPGM(
|
||||
"LARGE_PINMAP begin call with _cs: ", _cs,
|
||||
" _miso: ", _miso,
|
||||
" _sclk: ", _sclk,
|
||||
" _mosi: ", _mosi
|
||||
);
|
||||
#endif // LARGE_PINMAP
|
||||
DEBUG_ECHOLNPGM("MAX31865 Regs: CFG ", readRegister8(MAX31865_CONFIG_REG),
|
||||
"|RTD ", readRegister16(MAX31865_RTDMSB_REG),
|
||||
"|HTHRS ", readRegister16(MAX31865_HFAULTMSB_REG),
|
||||
"|LTHRS ", readRegister16(MAX31865_LFAULTMSB_REG),
|
||||
"|FLT ", readRegister8(MAX31865_FAULTSTAT_REG));
|
||||
|
||||
SERIAL_ECHOLNPGM("config: ", readRegister8(MAX31856_CONFIG_REG));
|
||||
SERIAL_EOL();
|
||||
#endif // MAX31865_DEBUG_SPI
|
||||
// fault detection cycle seems to initialize the sensor better
|
||||
runAutoFaultDetectionCycle(); // also initializes flags
|
||||
|
||||
if (lastFault)
|
||||
SERIAL_ECHOLNPGM("MAX31865 init fault ", lastFault);
|
||||
|
||||
writeRegister16(MAX31865_HFAULTMSB_REG, 0xFFFF);
|
||||
writeRegister16(MAX31865_LFAULTMSB_REG, 0);
|
||||
|
||||
#if ENABLED(MAX31865_USE_AUTO_MODE) // make a proper first read to initialize _lastRead
|
||||
|
||||
uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
rtd = fixFault(rtd);
|
||||
#endif
|
||||
|
||||
if (rtd & 1) {
|
||||
lastRead = 0xFFFF; // some invalid value
|
||||
lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
|
||||
clearFault(); // also clears the bias voltage flag, so no further action is required
|
||||
|
||||
DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
|
||||
}
|
||||
else {
|
||||
DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
|
||||
lastRead = rtd;
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = millis());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
enableBias();
|
||||
DELAY_US(2000); // according to the datasheet, 10.5τ+1msec (see below)
|
||||
oneShot();
|
||||
DELAY_US(63000);
|
||||
uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
rtd = fixFault(rtd);
|
||||
#endif
|
||||
|
||||
if (rtd & 1) {
|
||||
lastRead = 0xFFFF; // some invalid value
|
||||
lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
|
||||
clearFault(); // also clears the bias voltage flag, so no further action is required
|
||||
|
||||
DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
|
||||
}
|
||||
else {
|
||||
DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
|
||||
|
||||
resetFlags();
|
||||
|
||||
lastRead = rtd;
|
||||
nextEvent = SETUP_BIAS_VOLTAGE;
|
||||
millis_t now = millis();
|
||||
nextEventStamp = now + MAX31865_MIN_SAMPLING_TIME_MSEC;
|
||||
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = now);
|
||||
}
|
||||
|
||||
#endif // MAX31865_USE_AUTO_MODE
|
||||
|
||||
DEBUG_ECHOLNPGM(
|
||||
TERN(LARGE_PINMAP, "LARGE_PINMAP", "Regular")
|
||||
" begin call with cselPin: ", cselPin,
|
||||
" misoPin: ", misoPin,
|
||||
" sclkPin: ", sclkPin,
|
||||
" mosiPin: ", mosiPin,
|
||||
" config: ", readRegister8(MAX31865_CONFIG_REG)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the raw 8-bit FAULTSTAT register
|
||||
* Return and clear the last fault value
|
||||
*
|
||||
* @return The raw unsigned 8-bit FAULT status register
|
||||
* @return The raw unsigned 8-bit FAULT status register or spike fault
|
||||
*/
|
||||
uint8_t MAX31865::readFault() {
|
||||
return readRegister8(MAX31856_FAULTSTAT_REG);
|
||||
uint8_t r = lastFault;
|
||||
lastFault = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all faults in FAULTSTAT.
|
||||
* Clear last fault
|
||||
*/
|
||||
void MAX31865::clearFault() {
|
||||
setConfig(MAX31856_CONFIG_FAULTSTAT, 1);
|
||||
setConfig(MAX31865_CONFIG_FAULTSTAT, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we want to have continuous conversions (50/60 Hz)
|
||||
*
|
||||
* @param b If true, auto conversion is enabled
|
||||
* Reset flags
|
||||
*/
|
||||
void MAX31865::autoConvert(bool b) {
|
||||
setConfig(MAX31856_CONFIG_MODEAUTO, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we want filter out 50Hz noise or 60Hz noise
|
||||
*
|
||||
* @param b If true, 50Hz noise is filtered, else 60Hz(default)
|
||||
*/
|
||||
void MAX31865::enable50HzFilter(bool b) {
|
||||
setConfig(MAX31856_CONFIG_FILT50HZ, b);
|
||||
void MAX31865::resetFlags() {
|
||||
writeRegister8(MAX31865_CONFIG_REG, stdFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the bias voltage on the RTD sensor
|
||||
*
|
||||
* @param b If true bias is enabled, else disabled
|
||||
*/
|
||||
void MAX31865::enableBias(bool b) {
|
||||
setConfig(MAX31856_CONFIG_BIAS, b);
|
||||
|
||||
// From the datasheet:
|
||||
// Note that if VBIAS is off (to reduce supply current between conversions), any filter
|
||||
// capacitors at the RTDIN inputs need to charge before an accurate conversion can be
|
||||
// performed. Therefore, enable VBIAS and wait at least 10.5 time constants of the input
|
||||
// RC network plus an additional 1ms before initiating the conversion.
|
||||
if (b)
|
||||
DELAY_US(11500); //11.5ms
|
||||
void MAX31865::enableBias() {
|
||||
setConfig(MAX31865_CONFIG_BIAS, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a one-shot temperature reading.
|
||||
*/
|
||||
void MAX31865::oneShot() {
|
||||
setConfig(MAX31856_CONFIG_1SHOT, 1);
|
||||
setConfig(MAX31865_CONFIG_1SHOT | MAX31865_CONFIG_BIAS, 1);
|
||||
}
|
||||
|
||||
// From the datasheet:
|
||||
// Note that a single conversion requires approximately 52ms in 60Hz filter
|
||||
// mode or 62.5ms in 50Hz filter mode to complete. 1-Shot is a self-clearing bit.
|
||||
// TODO: switch this out depending on the filter mode.
|
||||
DELAY_US(65000); // 65ms
|
||||
void MAX31865::runAutoFaultDetectionCycle() {
|
||||
writeRegister8(MAX31865_CONFIG_REG, (stdFlags & 0x11) | 0x84 ); // cfg reg = 100X010Xb
|
||||
DELAY_US(600);
|
||||
for (int i = 0; i < 10 && (readRegister8(MAX31865_CONFIG_REG) & 0xC) > 0; i++) DELAY_US(100); // Fault det completes when bits 2 and 3 are zero (or after 10 tries)
|
||||
readFault();
|
||||
clearFault();
|
||||
}
|
||||
|
||||
/**
|
||||
* How many wires we have in our RTD setup, can be MAX31865_2WIRE,
|
||||
* MAX31865_3WIRE, or MAX31865_4WIRE
|
||||
* Set a value in the configuration register.
|
||||
*
|
||||
* @param config 8-bit value for the config item
|
||||
* @param enable whether to enable or disable the value
|
||||
*/
|
||||
void MAX31865::setConfig(uint8_t config, bool enable) {
|
||||
uint8_t t = stdFlags;
|
||||
if (enable)
|
||||
t |= config;
|
||||
else
|
||||
t &= ~config;
|
||||
writeRegister8(MAX31865_CONFIG_REG, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize standard flags with flags that will not change during operation (Hz, polling mode and no. of wires)
|
||||
*
|
||||
* @param wires The number of wires in enum format
|
||||
*/
|
||||
void MAX31865::setWires(max31865_numwires_t wires) {
|
||||
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
|
||||
void MAX31865::initFixedFlags(max31865_numwires_t wires) {
|
||||
|
||||
// set config-defined flags (same for all sensors)
|
||||
stdFlags = TERN(MAX31865_50HZ_FILTER, MAX31865_CONFIG_FILT50HZ, MAX31865_CONFIG_FILT60HZ) |
|
||||
TERN(MAX31865_USE_AUTO_MODE, MAX31865_CONFIG_MODEAUTO | MAX31865_CONFIG_BIAS, MAX31865_CONFIG_MODEOFF);
|
||||
|
||||
if (wires == MAX31865_3WIRE)
|
||||
t |= MAX31856_CONFIG_3WIRE;
|
||||
else // 2 or 4 wire
|
||||
t &= ~MAX31856_CONFIG_3WIRE;
|
||||
writeRegister8(MAX31856_CONFIG_REG, t);
|
||||
stdFlags |= MAX31865_CONFIG_3WIRE; // 3 wire
|
||||
else
|
||||
stdFlags &= ~MAX31865_CONFIG_3WIRE; // 2 or 4 wire
|
||||
}
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
|
||||
inline uint16_t MAX31865::fixFault(uint16_t rtd) {
|
||||
if (!ignore_faults || !(rtd & 1))
|
||||
return rtd;
|
||||
|
||||
ignore_faults--;
|
||||
clearFault();
|
||||
|
||||
DEBUG_ECHOLNPGM("MAX31865 ignoring fault ", (MAX31865_IGNORE_INITIAL_FAULTY_READS) - ignore_faults);
|
||||
|
||||
return rtd & ~1; // 0xFFFE
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline uint16_t MAX31865::readRawImmediate() {
|
||||
uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
|
||||
DEBUG_ECHOLNPGM("MAX31865 RTD MSB:", (rtd >> 8), " LSB:", (rtd & 0x00FF));
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
rtd = fixFault(rtd);
|
||||
#endif
|
||||
|
||||
if (rtd & 1) {
|
||||
lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
|
||||
lastRead |= 1;
|
||||
clearFault(); // also clears the bias voltage flag, so no further action is required
|
||||
DEBUG_ECHOLNPGM("MAX31865 read fault: ", lastFault);
|
||||
}
|
||||
else {
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, const millis_t ms = millis());
|
||||
if (TERN0(MAX31865_USE_READ_ERROR_DETECTION, ABS((int)(lastRead - rtd)) > 500 && PENDING(ms, lastReadStamp + 1000))) {
|
||||
// If 2 readings within 1s differ too much (~20°C) it's a read error.
|
||||
lastFault = 0x01;
|
||||
lastRead |= 1;
|
||||
DEBUG_ECHOLNPGM("MAX31865 read error: ", rtd);
|
||||
}
|
||||
else {
|
||||
lastRead = rtd;
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = ms);
|
||||
}
|
||||
}
|
||||
|
||||
return rtd;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,33 +368,59 @@ void MAX31865::setWires(max31865_numwires_t wires) {
|
||||
* @return The raw unsigned 16-bit register value with ERROR bit attached, NOT temperature!
|
||||
*/
|
||||
uint16_t MAX31865::readRaw() {
|
||||
clearFault();
|
||||
enableBias(true);
|
||||
|
||||
oneShot();
|
||||
uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG);
|
||||
#if ENABLED(MAX31865_USE_AUTO_MODE)
|
||||
|
||||
readRawImmediate();
|
||||
|
||||
#else
|
||||
|
||||
const millis_t ms = millis();
|
||||
|
||||
if (PENDING(ms, nextEventStamp)) {
|
||||
DEBUG_ECHOLNPGM("MAX31865 waiting for event ", nextEvent);
|
||||
return lastRead;
|
||||
}
|
||||
|
||||
switch (nextEvent) {
|
||||
case SETUP_BIAS_VOLTAGE:
|
||||
enableBias();
|
||||
nextEventStamp = ms + 2; // wait at least 10.5*τ (τ = 100nF*430Ω max for PT100 / 10nF*4.3ΚΩ for PT1000 = 43μsec) + 1msec
|
||||
nextEvent = SETUP_1_SHOT_MODE;
|
||||
DEBUG_ECHOLNPGM("MAX31865 bias voltage enabled");
|
||||
break;
|
||||
|
||||
case SETUP_1_SHOT_MODE:
|
||||
oneShot();
|
||||
nextEventStamp = ms + TERN(MAX31865_50HZ_FILTER, 63, 52); // wait at least 52msec for 60Hz (63msec for 50Hz) before reading RTD register
|
||||
nextEvent = READ_RTD_REG;
|
||||
DEBUG_ECHOLNPGM("MAX31865 1 shot mode enabled");
|
||||
break;
|
||||
|
||||
case READ_RTD_REG:
|
||||
|
||||
if (!(readRawImmediate() & 1)) // if clearFault() was not invoked, need to clear the bias voltage and 1-shot flags
|
||||
resetFlags();
|
||||
|
||||
nextEvent = SETUP_BIAS_VOLTAGE;
|
||||
nextEventStamp = ms + (MAX31865_MIN_SAMPLING_TIME_MSEC); // next step should not occur within less than MAX31865_MIN_SAMPLING_TIME_MSEC from the last one
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef MAX31865_DEBUG
|
||||
SERIAL_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
|
||||
#endif
|
||||
|
||||
// Disable the bias to lower power dissipation between reads.
|
||||
// If the ref resistor heats up, the temperature reading will be skewed.
|
||||
enableBias(false);
|
||||
|
||||
return rtd;
|
||||
return lastRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and return the resistance value of the connected RTD.
|
||||
*
|
||||
* @param refResistor The value of the matching reference resistor, usually 430 or 4300
|
||||
* @return The raw RTD resistance value, NOT temperature!
|
||||
*/
|
||||
float MAX31865::readResistance() {
|
||||
// Strip the error bit (D0) and convert to a float ratio.
|
||||
// less precise method: (readRaw() * Rref) >> 16
|
||||
return (((readRaw() >> 1) / 32768.0f) * Rref);
|
||||
// less precise method: (readRaw() * refRes) >> 16
|
||||
return ((readRaw() * RECIPROCAL(65536.0f)) * refRes - wireRes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,66 +437,77 @@ float MAX31865::temperature() {
|
||||
*
|
||||
* @return Temperature in C
|
||||
*/
|
||||
float MAX31865::temperature(uint16_t adcVal) {
|
||||
return temperature(((adcVal) / 32768.0f) * Rref);
|
||||
float MAX31865::temperature(const uint16_t adc_val) {
|
||||
return temperature(((adc_val) * RECIPROCAL(32768.0f)) * refRes - wireRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the temperature in C from the RTD resistance.
|
||||
* Uses the technique outlined in this PDF:
|
||||
* http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
|
||||
*
|
||||
* @param Rrtd the resistance value in ohms
|
||||
* @return the temperature in degC
|
||||
* @param rtd_res the resistance value in ohms
|
||||
* @return the temperature in °C
|
||||
*/
|
||||
float MAX31865::temperature(float Rrtd) {
|
||||
float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * Rrtd))) / RTD_Z4;
|
||||
float MAX31865::temperature(float rtd_res) {
|
||||
|
||||
rtd_res *= resNormalizer; // normalize to 100 ohm
|
||||
|
||||
// Constants for calculating temperature from the measured RTD resistance.
|
||||
// http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
|
||||
constexpr float RTD_Z1 = -0.0039083,
|
||||
RTD_Z2 = +1.758480889e-5,
|
||||
RTD_Z3 = -2.31e-8,
|
||||
RTD_Z4 = -1.155e-6;
|
||||
|
||||
// Callender-Van Dusen equation
|
||||
float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * rtd_res))) * RECIPROCAL(RTD_Z4);
|
||||
|
||||
// From the PDF...
|
||||
//
|
||||
// The previous equation is valid only for temperatures of 0°C and above.
|
||||
// The equation for RRTD(t) that defines negative temperature behavior is a
|
||||
// fourth-order polynomial (after expanding the third term) and is quite
|
||||
// impractical to solve for a single expression of temperature as a function
|
||||
// of resistance.
|
||||
// of resistance. So here we use a Linear Approximation instead.
|
||||
//
|
||||
if (temp < 0) {
|
||||
Rrtd = (Rrtd / Rzero) * 100; // normalize to 100 ohm
|
||||
float rpoly = Rrtd;
|
||||
#ifndef MAX31865_APPROX
|
||||
#define MAX31865_APPROX 5
|
||||
#endif
|
||||
|
||||
temp = -242.02 + (2.2228 * rpoly);
|
||||
rpoly *= Rrtd; // square
|
||||
temp += 2.5859e-3 * rpoly;
|
||||
rpoly *= Rrtd; // ^3
|
||||
temp -= 4.8260e-6 * rpoly;
|
||||
rpoly *= Rrtd; // ^4
|
||||
temp -= 2.8183e-8 * rpoly;
|
||||
rpoly *= Rrtd; // ^5
|
||||
temp += 1.5243e-10 * rpoly;
|
||||
constexpr float RTD_C[] = {
|
||||
#if MAX31865_APPROX == 5
|
||||
-242.02, +2.2228, +2.5859e-3, -4.8260e-6, -2.8183e-8, +1.5243e-10
|
||||
#elif MAX31865_APPROX == 4
|
||||
-241.96, +2.2163, +2.8541e-3, -9.9121e-6, -1.7152e-8
|
||||
#elif MAX31865_APPROX == 3
|
||||
-242.09, +2.2276, +2.5178e-3, -5.8620e-6
|
||||
#else
|
||||
-242.97, +2.2838, +1.4727e-3
|
||||
#endif
|
||||
};
|
||||
|
||||
float rpoly = rtd_res;
|
||||
temp = RTD_C[0];
|
||||
temp += rpoly * RTD_C[1];
|
||||
rpoly *= rtd_res; temp += rpoly * RTD_C[2];
|
||||
if (MAX31865_APPROX >= 3) { rpoly *= rtd_res; temp += rpoly * RTD_C[3]; }
|
||||
if (MAX31865_APPROX >= 4) { rpoly *= rtd_res; temp += rpoly * RTD_C[4]; }
|
||||
if (MAX31865_APPROX >= 5) { rpoly *= rtd_res; temp += rpoly * RTD_C[5]; }
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
//
|
||||
// private:
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Set a value in the configuration register.
|
||||
*
|
||||
* @param config 8-bit value for the config item
|
||||
* @param enable whether to enable or disable the value
|
||||
* MAX31865 SPI Timing constants
|
||||
* See MAX31865 datasheet (https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf)
|
||||
* All timings in nsec, minimum values.
|
||||
*/
|
||||
void MAX31865::setConfig(uint8_t config, bool enable) {
|
||||
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
|
||||
if (enable)
|
||||
t |= config;
|
||||
else
|
||||
t &= ~config; // disable
|
||||
writeRegister8(MAX31856_CONFIG_REG, t);
|
||||
}
|
||||
|
||||
#define MAX31865_SPI_TIMING_TCC 400 // CS to SCLK setup
|
||||
#define MAX31865_SPI_TIMING_TDC 35 // Data to SCLK setup
|
||||
#define MAX31865_SPI_TIMING_TCL 100 // SCK half period
|
||||
#define MAX31865_SPI_TIMING_TCCH 100 // SCK to CS hold
|
||||
#define MAX31865_SPI_TIMING_TCWH 400 // CS inactive time (min)
|
||||
|
||||
/**
|
||||
* Read a single byte from the specified register address.
|
||||
@@ -396,7 +518,6 @@ void MAX31865::setConfig(uint8_t config, bool enable) {
|
||||
uint8_t MAX31865::readRegister8(uint8_t addr) {
|
||||
uint8_t ret = 0;
|
||||
readRegisterN(addr, &ret, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -407,14 +528,9 @@ uint8_t MAX31865::readRegister8(uint8_t addr) {
|
||||
* @return both register contents as a single 16-bit int
|
||||
*/
|
||||
uint16_t MAX31865::readRegister16(uint8_t addr) {
|
||||
uint8_t buffer[2] = {0, 0};
|
||||
uint8_t buffer[2] = { 0 };
|
||||
readRegisterN(addr, buffer, 2);
|
||||
|
||||
uint16_t ret = buffer[0];
|
||||
ret <<= 8;
|
||||
ret |= buffer[1];
|
||||
|
||||
return ret;
|
||||
return uint16_t(buffer[0]) << 8 | buffer[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,27 +541,26 @@ uint16_t MAX31865::readRegister16(uint8_t addr) {
|
||||
* @param n the number of bytes to read
|
||||
*/
|
||||
void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
|
||||
addr &= 0x7F; // make sure top bit is not set
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
SPI.beginTransaction(spiConfig);
|
||||
else
|
||||
WRITE(_sclk, LOW);
|
||||
|
||||
WRITE(_cs, LOW);
|
||||
spixfer(addr);
|
||||
addr &= 0x7F; // make sure top bit is not set
|
||||
|
||||
spiBeginTransaction();
|
||||
spiTransfer(addr);
|
||||
|
||||
while (n--) {
|
||||
buffer[0] = spixfer(0xFF);
|
||||
#ifdef MAX31865_DEBUG_SPI
|
||||
SERIAL_ECHOLNPGM("buffer read ", n, " data: ", buffer[0]);
|
||||
#endif
|
||||
buffer[0] = spiTransfer(0xFF);
|
||||
buffer++;
|
||||
}
|
||||
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
SPI.endTransaction();
|
||||
spiEndTransaction();
|
||||
}
|
||||
|
||||
WRITE(_cs, HIGH);
|
||||
void MAX31865::writeRegister16(uint8_t addr, uint16_t data) {
|
||||
spiBeginTransaction();
|
||||
spiTransfer(addr | 0x80); // make sure top bit is set
|
||||
spiTransfer(data >> 8);
|
||||
spiTransfer(data & 0xFF);
|
||||
spiEndTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,20 +570,33 @@ void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
|
||||
* @param data the data to write
|
||||
*/
|
||||
void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
spiBeginTransaction();
|
||||
spiTransfer(addr | 0x80); // make sure top bit is set
|
||||
spiTransfer(data);
|
||||
spiEndTransaction();
|
||||
}
|
||||
|
||||
void MAX31865::spiBeginTransaction() {
|
||||
digitalWrite(sclkPin, LOW); // ensure CPOL0
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCWH); // ensure minimum time of CS inactivity after previous operation
|
||||
digitalWrite(cselPin, LOW);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCC);
|
||||
|
||||
if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
|
||||
SPI.beginTransaction(spiConfig);
|
||||
else
|
||||
WRITE(_sclk, LOW);
|
||||
digitalWrite(sclkPin, HIGH);
|
||||
}
|
||||
|
||||
WRITE(_cs, LOW);
|
||||
|
||||
spixfer(addr | 0x80); // make sure top bit is set
|
||||
spixfer(data);
|
||||
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
void MAX31865::spiEndTransaction() {
|
||||
if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
|
||||
SPI.endTransaction();
|
||||
else
|
||||
digitalWrite(sclkPin, LOW);
|
||||
|
||||
WRITE(_cs, HIGH);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCCH);
|
||||
|
||||
digitalWrite(cselPin, HIGH);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,21 +608,31 @@ void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
|
||||
* @param x an 8-bit chunk of data to write
|
||||
* @return the 8-bit response
|
||||
*/
|
||||
uint8_t MAX31865::spixfer(uint8_t x) {
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
uint8_t MAX31865::spiTransfer(uint8_t x) {
|
||||
if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
|
||||
return SPI.transfer(x);
|
||||
|
||||
uint8_t reply = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
digitalWrite(mosiPin, x & _BV(i));
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TDC);
|
||||
digitalWrite(sclkPin, LOW);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCL - MAX31865_SPI_TIMING_TDC);
|
||||
reply <<= 1;
|
||||
WRITE(_sclk, HIGH);
|
||||
WRITE(_mosi, x & (1 << i));
|
||||
WRITE(_sclk, LOW);
|
||||
if (READ(_miso))
|
||||
reply |= 1;
|
||||
if (digitalRead(misoPin)) reply |= 1;
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TDC);
|
||||
digitalWrite(sclkPin, HIGH);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCL - MAX31865_SPI_TIMING_TDC);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void MAX31865::softSpiInit() {
|
||||
DEBUG_ECHOLNPGM("Initializing MAX31865 Software SPI");
|
||||
pinMode(sclkPin, OUTPUT);
|
||||
digitalWrite(sclkPin, LOW);
|
||||
pinMode(mosiPin, OUTPUT);
|
||||
pinMode(misoPin, INPUT);
|
||||
}
|
||||
|
||||
#endif // HAS_MAX31865 && !USE_ADAFRUIT_MAX31865
|
||||
|
Reference in New Issue
Block a user