mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-15 01:23:49 +01:00
drivers/max31865: implement the driver
Implement the driver for the MAX31865 RTD-to-digital converter.
This commit is contained in:
parent
803d60c3bc
commit
c3a14457f3
274
drivers/include/max31865.h
Normal file
274
drivers/include/max31865.h
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2025 David Picard
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @defgroup drivers_max31865 MAX31865 RTD-to-Digital converter driver
|
||||
* @ingroup drivers_sensors
|
||||
* @brief Driver for the SPI RTD-to-Digital converter MAX31865.
|
||||
*
|
||||
* \section sec_max31865_ovrvw Overview
|
||||
|
||||
* The MAX31865 is a resistance-to-digital
|
||||
* converter optimized for platinum resistance temperature
|
||||
* detectors (RTDs). An external resistor sets the sensitivity
|
||||
* for the RTD being used and a precision delta-sigma ADC
|
||||
* converts the ratio of the RTD resistance to the reference
|
||||
* resistance on the board, to a 15-bit word.
|
||||
*
|
||||
* - Compatible with 2-, 3-, and 4-Wire Sensor Connections
|
||||
* - 15-Bit ADC resolution; resolution 0.03125°C
|
||||
* - Total accuracy over all operating conditions: 0.5°C (0.05% FS) max
|
||||
* - 21 ms max conversion time
|
||||
*
|
||||
* The RTD register and the threshold registers represent the ratio
|
||||
* of the RTD resistance to the reference resistance.
|
||||
*
|
||||
* @note See the
|
||||
* <a href="https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31865.pdf">
|
||||
* datasheet</a> for more information.
|
||||
*
|
||||
* \section sec_max31865_lut Lookup table
|
||||
*
|
||||
* In order to convert the ADC code to temperature with a decent precision
|
||||
* and low CPU usage by using fixed point computation, this driver uses a lookup table.
|
||||
* The values of the lookup table depend on the type of RTD, the reference
|
||||
* resistance on the board and the temperature range.
|
||||
* A default lookup table for a Pt100 sensor, a reference resistance of 330Ω
|
||||
* and a temperature range of -200..+650°C is provided.
|
||||
* With this lookup table, the standard deviation of the computation
|
||||
* error over the -200..+650°C range is 0.011°C and the maximum error
|
||||
* is 0.039°C.
|
||||
*
|
||||
* If the default lookup doesn't fit the application, the user can generate
|
||||
* a custom one with the Python script `drivers/max31865/dist/genlut.py`.
|
||||
* Type `genlut.py -h` for help.
|
||||
* Move the generated header file to the application project directory.
|
||||
* In the application code, include `max31865_lut.h`.
|
||||
* The header files must be included in the following order:
|
||||
*
|
||||
* @code
|
||||
* #include "max31865.h"
|
||||
* #include "max31865_lut.h"
|
||||
* #include "max31865_params.h"
|
||||
* @endcode
|
||||
*
|
||||
* The lookup table in the application directory will override the default one.
|
||||
*
|
||||
* \section sec_max31865_usage Usage
|
||||
*
|
||||
* The default configuration is set in \ref max31865_params.h as a
|
||||
* \ref max31865_params_t structure.
|
||||
* Most of them can be changed using the preprocessor definitions either
|
||||
* in the board definition in `board.h` or in the Makefile of the application
|
||||
* using the `CFLAGS` variable.
|
||||
* In particular, let the SPI device and chip select pin fit the board.
|
||||
*
|
||||
* @code
|
||||
* define MAX31865_PARAM_SPI (SPI_DEV(0))
|
||||
* define MAX31865_PARAM_CS_PIN (GPIO_PIN(0, 5))
|
||||
* @endcode
|
||||
*
|
||||
* The easiest way to read the temperature is to call #max31865_read().
|
||||
* In critical applications, where a sensor failure may cause damage to the
|
||||
* system, calling #max31865_read_raw(),
|
||||
* #max31865_raw_to_data() and #max31865_detect_fault() independently
|
||||
* provides a better control on fault detection.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author David Picard
|
||||
*/
|
||||
|
||||
/* Add header includes here */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "periph/spi.h"
|
||||
|
||||
/**
|
||||
* @brief Lookup table column indexes
|
||||
*
|
||||
* The lookup table is a 2D array.
|
||||
* This enum defines the indexes of the columns of the table.
|
||||
* The conversion from ADC code to temperature is done with a linear interpolation
|
||||
* between two lines of the table.
|
||||
* The coefficients at line n are valid for the temperature range between line n
|
||||
* and line n+1.
|
||||
*
|
||||
* T = a<sub>0</sub> + a<sub>1</sub> × code
|
||||
*/
|
||||
typedef enum {
|
||||
MAX31865_LUTCOL_CODE = 0, /**< ADC code column index */
|
||||
MAX31865_LUTCOL_TEMP = 1, /**< Temperature column index (µ°C) */
|
||||
MAX31865_LUTCOL_A0 = 2, /**< a<sub>0</sub> coefficient column index */
|
||||
MAX31865_LUTCOL_A1 = 3, /**< a<sub>1</sub> coefficient column index */
|
||||
MAX31865_LUTCOL_NUMOF /**< Number of columns in the lookup table */
|
||||
} max31865_lutcols_t;
|
||||
|
||||
/**
|
||||
* @brief Device initialization parameters
|
||||
*
|
||||
* - Use the bits defined in \ref drivers_max31865_constants_regcfg to build the
|
||||
* configuration byte #max31865_params_t.cfg_byte.
|
||||
* - The high and low thresholds must be set in units of 0.01 °C, e.g. `12000` for 120 °C.
|
||||
* - #max31865_params_t.lut_numlines is defined in the header file generated
|
||||
* by the Python script `drivers/max31865/dist/genlut.py`.
|
||||
*/
|
||||
typedef struct {
|
||||
spi_t spi; /**< SPI device */
|
||||
spi_cs_t cs_pin; /**< Chip select pin */
|
||||
uint8_t cfg_byte; /**< Initial value of the configuration register */
|
||||
int32_t temp_low_threshold; /**< Low threshold temperature (c°C) */
|
||||
int32_t temp_high_threshold; /**< High threshold temperature (c°C) */
|
||||
const int32_t (*lut)[][MAX31865_LUTCOL_NUMOF]; /**< Lookup table */
|
||||
const int lut_numlines; /**< Number of lines in the lookup table */
|
||||
} max31865_params_t;
|
||||
|
||||
/**
|
||||
* @brief Device descriptor for the driver
|
||||
*/
|
||||
typedef struct {
|
||||
const max31865_params_t *params; /**< device configuration */
|
||||
} max31865_t;
|
||||
|
||||
/**
|
||||
* @brief Fault status of the MAX31865
|
||||
*/
|
||||
typedef enum {
|
||||
MAX31865_FAULT_NO_FAULT = 0, /**< No fault */
|
||||
MAX31865_FAULT_RTD_HIGH = 1, /**< The RTD value is too high */
|
||||
MAX31865_FAULT_RTD_LOW = 2, /**< The RTD value is too small */
|
||||
MAX31865_FAULT_CIRCUIT = 3, /**< Open or shorted circuit */
|
||||
MAX31865_FAULT_VOLTAGE = 4 /**< Overvoltage or undervoltage on the FORCE or RTD pin */
|
||||
} max31865_fault_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the given device
|
||||
*
|
||||
* @param[in,out] dev Device descriptor of the driver
|
||||
* @param[in] params Initialization parameters
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -ENXIO invalid SPI device
|
||||
* @retval -EINVAL invalid SPI CS pin/line
|
||||
*/
|
||||
int max31865_init(max31865_t *dev, const max31865_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Clear the fault flag
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[out] config If not NULL, set to the value of the config register
|
||||
*
|
||||
* Call this function if after #max31865_read() or #max31865_detect_fault() if
|
||||
* either of them reports a fault.
|
||||
*/
|
||||
void max31865_clear_fault(const max31865_t *dev, uint8_t *config);
|
||||
|
||||
/**
|
||||
* @brief Read data from the MAX31865. This is a shortcut to read raw data
|
||||
* and parse it to the data structure.
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[out] rtd_temperature_cdegc Temperature in centi-degrees Celsius (0.01°C)
|
||||
*
|
||||
* @pre \a dev and \a data must not be NULL
|
||||
*
|
||||
* This function does a minimal error check.
|
||||
* To get more details on the fault, call
|
||||
* #max31865_detect_fault().
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EIO if an error was detected by the MAX31865
|
||||
*/
|
||||
int max31865_read(const max31865_t *dev, int32_t *rtd_temperature_cdegc);
|
||||
|
||||
/**
|
||||
* @brief Read raw data from the MAX31865
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[out] raw_data Value of the RTD registers
|
||||
*
|
||||
* \a raw_data is a left-justified 15-bit word, representing the ratio
|
||||
* of the RTD resistance to the reference resistance.
|
||||
* Bit 0 is a status bit.
|
||||
*
|
||||
* @retval 0 on success.
|
||||
* @retval -EIO if an error was detected by the MAX31865.
|
||||
* In this case, the temperature is not valid.
|
||||
* Call #max31865_detect_fault() for more details.
|
||||
*/
|
||||
int max31865_read_raw(const max31865_t *dev, uint16_t *raw_data);
|
||||
|
||||
/**
|
||||
* @brief Convert the raw data from the MAX31865 temperature
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[in] raw_data Raw data from the MAX31865
|
||||
* @param[out] rtd_temperature_cdegc Temperature in centi-degrees Celsius (0.01°C)
|
||||
*
|
||||
* @pre @p data must not be NULL
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EINVAL on error
|
||||
*/
|
||||
int max31865_raw_to_data(const max31865_t *dev, uint16_t raw_data, int32_t *rtd_temperature_cdegc);
|
||||
|
||||
/**
|
||||
* @brief Run an automatic fault-detection cycle
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[out] flt_code Fault code
|
||||
*
|
||||
* Run a full fault-detection cycle, in automatic mode.
|
||||
* The automatic mode implies that the input filter of the ADC has
|
||||
* a time constant smaller than 100µs.
|
||||
* This is the case if the capacitor across the RTD has the
|
||||
* value which is recommended in the datasheet.
|
||||
*
|
||||
* The execution time of this function is at least 100µs, until the
|
||||
* fault-detection cycle completes.
|
||||
* It polls the configuration register to check the completion of the cycle.
|
||||
*
|
||||
* @pre V<sub>BIAS</sub> must be on for at least 5 time constants.
|
||||
*
|
||||
* @retval 0 on success.
|
||||
* @retval -EIO on error.
|
||||
*/
|
||||
int max31865_detect_fault(const max31865_t *dev, max31865_fault_t *flt_code);
|
||||
|
||||
/**
|
||||
* @brief Switch V<sub>BIAS</sub> on or off
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[in] enable Set to \c true to switch on, or to \c false to switch off V<sub>BIAS</sub>.
|
||||
*
|
||||
* The bias current of the RTD can be switched off to save power.
|
||||
* After switching it on, wait for at least 10 time constants -- 10 × 100µs,
|
||||
* if the capacitance across the RTD is as recommended in the datasheet --
|
||||
* before measuring a valid temperature.
|
||||
*/
|
||||
void max31865_switch_vbias(const max31865_t *dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Start a one-shot conversion
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
*
|
||||
* The user must wait at least 52ms in 60Hz or 62.5ms in 50Hz filter mode
|
||||
* for the conversion to complete, before reading the conversion result.
|
||||
*/
|
||||
void max31865_oneshot(const max31865_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
1
drivers/max31865/Makefile
Normal file
1
drivers/max31865/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTMAKE)/driver_with_saul.mk
|
||||
4
drivers/max31865/Makefile.dep
Normal file
4
drivers/max31865/Makefile.dep
Normal file
@ -0,0 +1,4 @@
|
||||
FEATURES_REQUIRED += periph_spi
|
||||
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_usec
|
||||
2
drivers/max31865/Makefile.include
Normal file
2
drivers/max31865/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
||||
USEMODULE_INCLUDES_max31865 := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_max31865)
|
||||
220
drivers/max31865/include/max31865_internal.h
Normal file
220
drivers/max31865/include/max31865_internal.h
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2025 David Picard
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @defgroup drivers_max31865_constants MAX31865 constants
|
||||
* @ingroup drivers_max31865
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Internal addresses, registers and constants
|
||||
*
|
||||
* @author David Picard
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/** Maximum number of bytes that can be read/written from/to the device */
|
||||
#define MAX31865_DATA_SIZE (8)
|
||||
|
||||
/** Reference resistance (ohm) */
|
||||
#define MAX31865_REF_RESISTANCE_DEFAULT (220)
|
||||
|
||||
/** Default value of the high threshold register */
|
||||
#define MAX31865_HTHRES_DEFAULT (0xFFFF)
|
||||
|
||||
/** Default value of the low threshold register */
|
||||
#define MAX31865_LTHRES_DEFAULT (0x0000)
|
||||
|
||||
/**
|
||||
* @defgroup drivers_max31865_reg_addresses Register addresses
|
||||
* @ingroup drivers_max31865_constants
|
||||
* @{
|
||||
*
|
||||
* Register | Read address | Write address
|
||||
* --- | --- | ---
|
||||
* Configuration | 0x00 | 0x80
|
||||
* RTD MSB | 0x01 | N/A
|
||||
* RTD LSB | 0x02 | N/A
|
||||
* RTD high threshold MSB | 0x03 | 0x83
|
||||
* RTD high threshold LSB | 0x04 | 0x84
|
||||
* RTD low threshold MSB | 0x05 | 0x85
|
||||
* RTD low threshold LSB | 0x06 | 0x86
|
||||
* Fault | 0x07 | N/A
|
||||
*/
|
||||
|
||||
#define MAX31865_ADDR_CFG_R (0x00) /**< Configuration register, read address */
|
||||
#define MAX31865_ADDR_CFG_W (0x80) /**< Configuration register, write address */
|
||||
#define MAX31865_ADDR_RTD_MSB (0x01) /**< RTD MSB, read-only */
|
||||
#define MAX31865_ADDR_RTD_LSB (0x02) /**< RTD LSB, read-only */
|
||||
#define MAX31865_ADDR_RTD_HTHRES_MSB_R (0x03) /**< RTD high threshold MSB, read address */
|
||||
#define MAX31865_ADDR_RTD_HTHRES_MSB_W (0x83) /**< RTD high threshold MSB, write address */
|
||||
#define MAX31865_ADDR_RTD_HTHRES_LSB_R (0x04) /**< RTD high threshold LSB, read address */
|
||||
#define MAX31865_ADDR_RTD_HTHRES_LSB_W (0x84) /**< RTD high threshold LSB, write address */
|
||||
#define MAX31865_ADDR_RTD_LTHRES_MSB_R (0x05) /**< RTD low threshold MSB, read address */
|
||||
#define MAX31865_ADDR_RTD_LTHRES_MSB_W (0x85) /**< RTD low threshold MSB, write address */
|
||||
#define MAX31865_ADDR_RTD_LTHRES_LSB_R (0x06) /**< RTD low threshold LSB, read address */
|
||||
#define MAX31865_ADDR_RTD_LTHRES_LSB_W (0x86) /**< RTD low threshold LSB, write address */
|
||||
#define MAX31865_ADDR_FAULT (0x07) /**< Fault status register, read-only */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup drivers_max31865_constants_regcfg Configuration register bits
|
||||
* @ingroup drivers_max31865_constants
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enable Vbias
|
||||
*/
|
||||
#define MAX31865_CFG_VBIAS_ON (0b10000000)
|
||||
|
||||
/**
|
||||
* @brief Enable automatic conversion mode
|
||||
*
|
||||
* The conversion rate is the same as the selected filter mode,
|
||||
* \em i.e. either 50 or 60Hz.
|
||||
*/
|
||||
#define MAX31865_CFG_CONV_AUTO (0b01000000)
|
||||
|
||||
/**
|
||||
* @brief Trigger a single conversion
|
||||
*
|
||||
* The automatic conversion mode must be off.
|
||||
* Single conversion time is 52ms in 60Hz or 62.5ms in 50Hz filter mode.
|
||||
*/
|
||||
#define MAX31865_CFG_1SHOT (0b00100000)
|
||||
|
||||
/**
|
||||
* @brief 3-wire resistor connection
|
||||
*
|
||||
* Clear the bit for 2- or 4-wire connections.
|
||||
*/
|
||||
#define MAX31865_CFG_3WIRE (0b00010000)
|
||||
|
||||
/**
|
||||
* @brief Clear fault condition
|
||||
*/
|
||||
#define MAX31865_CFG_CLEAR_FAULT (0b00000010)
|
||||
|
||||
/**
|
||||
* @brief Filter out 50Hz if set, or 60Hz otherwise
|
||||
*
|
||||
* Clear the bit for 60Hz filter mode.
|
||||
*/
|
||||
#define MAX31865_CFG_FILTER_50HZ (0b00000001)
|
||||
|
||||
/**
|
||||
* @brief Fault detection bit mask
|
||||
*
|
||||
* To check the status of the fault detection cycle,
|
||||
* mask the bits of the configuration register and
|
||||
* compare the value with #MAX31865_CFG_FLTDET_IDLE,
|
||||
* #MAX31865_CFG_FLTDET_AUTO_START, #MAX31865_CFG_FLTDET_MANU_START
|
||||
* or #MAX31865_CFG_FLTDET_MANU_START.
|
||||
*/
|
||||
#define MAX31865_CFG_FLTDET_MASK (0b00001100)
|
||||
|
||||
/**
|
||||
* @brief No fault detection is running
|
||||
*/
|
||||
#define MAX31865_CFG_FLTDET_IDLE (0b00000000)
|
||||
|
||||
/**
|
||||
* @brief Start a fault detection with an automatic delay (about 550µs)
|
||||
*/
|
||||
#define MAX31865_CFG_FLTDET_AUTO_START (0b00000100)
|
||||
|
||||
/**
|
||||
* @brief Start a fault detection with a manually controlled delay
|
||||
*/
|
||||
#define MAX31865_CFG_FLTDET_MANU_START (0b00001000)
|
||||
|
||||
/**
|
||||
* @brief Terminate fault detection with a manually controlled delay
|
||||
*/
|
||||
#define MAX31865_CFG_FLTDET_MANU_STOP (0b00001100)
|
||||
|
||||
/** @} */ /* end of group drivers_max31865_constants_regcfg */
|
||||
|
||||
/**
|
||||
* @defgroup drivers_max31865_constants_regflt Fault register bits
|
||||
* In normal conditions, all bits are cleared.
|
||||
* Bits 0 and 1 are not used.
|
||||
*
|
||||
* During measurement, the reference resistor and the RTD are connected in
|
||||
* series.
|
||||
* During a fault detection cycle initiated by the master, the IC can open an
|
||||
* internal switch on the FORCE- pin.
|
||||
* - When the FORCE- switch is closed, current should flow through the reference
|
||||
* resistor and V<sub>REFIN-</sub> is expected to be less than 0.85 × V<sub>BIAS</sub>.
|
||||
* - When the FORCE- switch is open, no current should flow through the reference
|
||||
* resistor nor the RTD and both V<sub>REFIN-</sub> and V<sub>RTDIN-</sub> are
|
||||
* expected to be greater than 0.85 × V<sub>BIAS</sub>.
|
||||
*
|
||||
* The table below sums up the fault conditions when the FORCE- switch is open.
|
||||
* The two left-most column reflect the values of the bits masked by
|
||||
* #MAX31865_FLT_REF_FO and #MAX31865_FLT_RTD_FO.
|
||||
*
|
||||
* V<sub>REFIN-</sub> | V<sub>RTDIN-</sub> | Fault
|
||||
* --- | --- | ---
|
||||
* 0 | 0 | no error
|
||||
* 0 | 1 | RTD disconnected from the RTD- pin
|
||||
* 1 | 0 | RTD- is shorted to VCC; very unlikely
|
||||
* 1 | 1 | RTD disconnected from the RTD+ pin or shorted
|
||||
*
|
||||
* In the last case, it can be asserted that the RTD terminals are shorted together if the bit
|
||||
* masked by #MAX31865_FLT_THRESLOW is set.
|
||||
*
|
||||
* @ingroup drivers_max31865_constants
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Fault: RTD is greater than the high threshold
|
||||
*/
|
||||
#define MAX31865_FLT_THRESHIGH (0b10000000)
|
||||
|
||||
/**
|
||||
* @brief Fault: RTD is less than the low threshold
|
||||
*/
|
||||
#define MAX31865_FLT_THRESLOW (0b01000000)
|
||||
|
||||
/**
|
||||
* @brief Fault: V<sub>REFIN-</sub> > 0.85 × V<sub>BIAS</sub> when FORCE- is closed
|
||||
*/
|
||||
#define MAX31865_FLT_REF_FC (0b00100000)
|
||||
|
||||
/**
|
||||
* @brief Fault: V<sub>REFIN-</sub> < 0.85 × V<sub>BIAS</sub> when FORCE- is open
|
||||
*/
|
||||
#define MAX31865_FLT_REF_FO (0b00010000)
|
||||
|
||||
/**
|
||||
* @brief Fault: V<sub>RTDIN-</sub> < 0.85 × × V<sub>BIAS</sub> when FORCE- is open
|
||||
*/
|
||||
#define MAX31865_FLT_RTD_FO (0b00001000)
|
||||
|
||||
/**
|
||||
* @brief Fault: overvoltage or undervoltage condition
|
||||
*
|
||||
* Overvoltage or undervoltage condition on at least one of the
|
||||
* FORCE or RTD pins.
|
||||
*/
|
||||
#define MAX31865_FLT_VOLTAGE (0b00000100)
|
||||
|
||||
/** @} */ /* end of group drivers_max31865_constants_regflt */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
140
drivers/max31865/include/max31865_lut.h
Normal file
140
drivers/max31865/include/max31865_lut.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2025 David Picard
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @ingroup drivers_max31865
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Default lookup table
|
||||
*
|
||||
* @author David Picard
|
||||
*/
|
||||
|
||||
#include "container.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef MAX31865_CUSTOM_LUT_PROVIDED
|
||||
|
||||
/** Number of lines in #max31865_lut */
|
||||
#define MAX31865_LUT_NUMLINES (ARRAY_SIZE(max31865_lut))
|
||||
/**
|
||||
* @ingroup drivers_max31865
|
||||
* @brief Default lookup table for temperature conversion
|
||||
*
|
||||
* The columns of this table should be indexed by #max31865_lutcols_t.
|
||||
*
|
||||
* This lookup table was generated by @p genlut.py, provided with
|
||||
* the max31865 driver.
|
||||
*
|
||||
* Lookup table parameters:
|
||||
* - RTD @ 0°C = 100Ω
|
||||
* - RREF = 330Ω
|
||||
* - Tmin = -200°C
|
||||
* - Tmax = 650°C
|
||||
*/
|
||||
static const int32_t max31865_lut[][4] =
|
||||
{ { 0x0E5E, -200000000, -242967290, 11682 },
|
||||
{ 0x11B6, -190000000, -243466981, 11792 },
|
||||
{ 0x1506, -180000000, -244071429, 11905 },
|
||||
{ 0x184E, -170000000, -244425837, 11962 },
|
||||
{ 0x1B92, -160000000, -245036145, 12048 },
|
||||
{ 0x1ED0, -150000000, -245496368, 12107 },
|
||||
{ 0x220A, -140000000, -246268293, 12195 },
|
||||
{ 0x253E, -130000000, -247125307, 12285 },
|
||||
{ 0x286C, -120000000, -247438424, 12315 },
|
||||
{ 0x2B98, -110000000, -248461538, 12407 },
|
||||
{ 0x2EBE, -100000000, -248830846, 12438 },
|
||||
{ 0x31E2, -90000000, -249625000, 12500 },
|
||||
{ 0x3502, -80000000, -250477387, 12563 },
|
||||
{ 0x381E, -70000000, -251388889, 12626 },
|
||||
{ 0x3B36, -60000000, -251873418, 12658 },
|
||||
{ 0x3E4C, -50000000, -252385787, 12690 },
|
||||
{ 0x4160, -40000000, -253469388, 12755 },
|
||||
{ 0x4470, -30000000, -254040921, 12788 },
|
||||
{ 0x477E, -20000000, -254641026, 12821 },
|
||||
{ 0x4A8A, -10000000, -255269923, 12853 },
|
||||
{ 0x4D94, 0, -256589147, 12920 },
|
||||
{ 0x509A, 10000000, -256589147, 12920 },
|
||||
{ 0x53A0, 20000000, -258025974, 12987 },
|
||||
{ 0x56A2, 30000000, -258776042, 13021 },
|
||||
{ 0x59A2, 40000000, -259556136, 13055 },
|
||||
{ 0x5CA0, 50000000, -260366492, 13089 },
|
||||
{ 0x5F9C, 60000000, -262052632, 13158 },
|
||||
{ 0x6294, 70000000, -262052632, 13158 },
|
||||
{ 0x658C, 80000000, -263862434, 13228 },
|
||||
{ 0x6880, 90000000, -264801061, 13263 },
|
||||
{ 0x6B72, 100000000, -265771277, 13298 },
|
||||
{ 0x6E62, 110000000, -266773333, 13333 },
|
||||
{ 0x7150, 120000000, -267807487, 13369 },
|
||||
{ 0x743C, 130000000, -269946237, 13441 },
|
||||
{ 0x7724, 140000000, -269946237, 13441 },
|
||||
{ 0x7A0C, 150000000, -272216216, 13514 },
|
||||
{ 0x7CF0, 160000000, -273387534, 13550 },
|
||||
{ 0x7FD2, 170000000, -274592391, 13587 },
|
||||
{ 0x82B2, 180000000, -275831063, 13624 },
|
||||
{ 0x8590, 190000000, -277103825, 13661 },
|
||||
{ 0x886C, 200000000, -278410959, 13699 },
|
||||
{ 0x8B46, 210000000, -281101928, 13774 },
|
||||
{ 0x8E1C, 220000000, -282486188, 13812 },
|
||||
{ 0x90F0, 230000000, -283905817, 13850 },
|
||||
{ 0x93C2, 240000000, -285361111, 13889 },
|
||||
{ 0x9692, 250000000, -286852368, 13928 },
|
||||
{ 0x9960, 260000000, -288379888, 13966 },
|
||||
{ 0x9C2C, 270000000, -291516854, 14045 },
|
||||
{ 0x9EF4, 280000000, -291516854, 14045 },
|
||||
{ 0xA1BC, 290000000, -294802260, 14124 },
|
||||
{ 0xA480, 300000000, -296487252, 14164 },
|
||||
{ 0xA742, 310000000, -298210227, 14205 },
|
||||
{ 0xAA02, 320000000, -299971510, 14245 },
|
||||
{ 0xACC0, 330000000, -301771429, 14286 },
|
||||
{ 0xAF7C, 340000000, -305459770, 14368 },
|
||||
{ 0xB234, 350000000, -307348703, 14409 },
|
||||
{ 0xB4EA, 360000000, -307348703, 14409 },
|
||||
{ 0xB7A0, 370000000, -311275362, 14493 },
|
||||
{ 0xBA52, 380000000, -313284884, 14535 },
|
||||
{ 0xBD02, 390000000, -317397661, 14620 },
|
||||
{ 0xBFAE, 400000000, -317397661, 14620 },
|
||||
{ 0xC25A, 410000000, -319530792, 14663 },
|
||||
{ 0xC504, 420000000, -323893805, 14749 },
|
||||
{ 0xC7AA, 430000000, -326124260, 14793 },
|
||||
{ 0xCA4E, 440000000, -328397626, 14837 },
|
||||
{ 0xCCF0, 450000000, -330714286, 14881 },
|
||||
{ 0xCF90, 460000000, -333074627, 14925 },
|
||||
{ 0xD22E, 470000000, -337897898, 15015 },
|
||||
{ 0xD4C8, 480000000, -337897898, 15015 },
|
||||
{ 0xD762, 490000000, -342900302, 15106 },
|
||||
{ 0xD9F8, 500000000, -345454545, 15152 },
|
||||
{ 0xDC8C, 510000000, -348054711, 15198 },
|
||||
{ 0xDF1E, 520000000, -350701220, 15244 },
|
||||
{ 0xE1AE, 530000000, -353394495, 15291 },
|
||||
{ 0xE43C, 540000000, -356134969, 15337 },
|
||||
{ 0xE6C8, 550000000, -361728395, 15432 },
|
||||
{ 0xE950, 560000000, -364582043, 15480 },
|
||||
{ 0xEBD6, 570000000, -367484472, 15528 },
|
||||
{ 0xEE5A, 580000000, -370436137, 15576 },
|
||||
{ 0xF0DC, 590000000, -373437500, 15625 },
|
||||
{ 0xF35C, 600000000, -376489028, 15674 },
|
||||
{ 0xF5DA, 610000000, -382712934, 15773 },
|
||||
{ 0xF854, 620000000, -382712934, 15773 },
|
||||
{ 0xFACE, 630000000, -389142857, 15873 },
|
||||
{ 0xFD44, 640000000, -392420382, 15924 },
|
||||
{ 0xFFB8, 650000000, -395750799, 15974 } };
|
||||
|
||||
#endif /* MAX31865_CUSTOM_LUT_PROVIDED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
103
drivers/max31865/include/max31865_params.h
Normal file
103
drivers/max31865/include/max31865_params.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2025 David Picard
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @ingroup drivers_max31865
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief Default configuration for the MAX31865 driver
|
||||
*
|
||||
* @author David Picard
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
|
||||
#include "max31865.h"
|
||||
#include "max31865_internal.h"
|
||||
#include "max31865_lut.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Default configuration for the MAX31865 driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Default SPI bus for the MAX31865 driver
|
||||
*/
|
||||
#ifndef MAX31865_PARAM_SPI
|
||||
# define MAX31865_PARAM_SPI (SPI_DEV(0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default CS pin for the MAX31865 driver
|
||||
*/
|
||||
#ifndef MAX31865_PARAM_CS_PIN
|
||||
# define MAX31865_PARAM_CS_PIN (GPIO_PIN(0, 5))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration byte definition
|
||||
*/
|
||||
#ifndef MAX31865_PARAM_CFG_BYTE
|
||||
# define MAX31865_PARAM_CFG_BYTE MAX31865_CFG_VBIAS_ON | \
|
||||
MAX31865_CFG_CONV_AUTO | \
|
||||
MAX31865_CFG_3WIRE | \
|
||||
MAX31865_CFG_FILTER_50HZ
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Low temperature threshold
|
||||
*/
|
||||
#ifndef MAX31865_PARAM_TEMP_THRES_LOW
|
||||
# define MAX31865_PARAM_TEMP_THRES_LOW -19900
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief High temperature threshold
|
||||
*/
|
||||
#ifndef MAX31865_PARAM_TEMP_THRES_HIGH
|
||||
# define MAX31865_PARAM_TEMP_THRES_HIGH 64900
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default parameters for the MAX31865 driver
|
||||
*/
|
||||
#ifndef MAX31865_PARAMS
|
||||
# define MAX31865_PARAMS { \
|
||||
.spi = MAX31865_PARAM_SPI, \
|
||||
.cs_pin = MAX31865_PARAM_CS_PIN, \
|
||||
.cfg_byte = MAX31865_PARAM_CFG_BYTE, \
|
||||
.temp_low_threshold = MAX31865_PARAM_TEMP_THRES_LOW, \
|
||||
.temp_high_threshold = MAX31865_PARAM_TEMP_THRES_HIGH, \
|
||||
.lut = &max31865_lut, \
|
||||
.lut_numlines = MAX31865_LUT_NUMLINES, \
|
||||
}
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* @brief Configuration structs for the MAX31865 driver
|
||||
*/
|
||||
static const max31865_params_t max31865_params[] =
|
||||
{
|
||||
MAX31865_PARAMS
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
347
drivers/max31865/max31865.c
Normal file
347
drivers/max31865/max31865.c
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (C) 2025 David Picard
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_max31865
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Device driver implementation for the drivers_sensors
|
||||
*
|
||||
* @author David Picard
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "max31865.h"
|
||||
#include "max31865_internal.h"
|
||||
#include "max31865_params.h"
|
||||
|
||||
/*
|
||||
* Single data byte read transfer:
|
||||
*
|
||||
* CS |\_____________________/|
|
||||
* SCK | 8 periods | 8 periods |
|
||||
* MOSI | addr byte | idle |
|
||||
* MISO | idle | data byte |
|
||||
*/
|
||||
|
||||
/* ****************************************************************************
|
||||
* PRIVATE FUNCTIONS
|
||||
* ****************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Convert temperature to raw data
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[in] temp Temperature in Celsius centi-degrees (0.01°C)
|
||||
* @param[out] raw_data Code used to set the RTD low and RTD high registers
|
||||
* @return 0 on success.
|
||||
* @return -EINVAL if temperature is out of LUT range.
|
||||
*/
|
||||
static int _temp_to_raw(const max31865_t *dev, int32_t temp, uint16_t *raw_data)
|
||||
{
|
||||
int32_t temp_uc = temp * 10000; // c°C --> µ°C
|
||||
|
||||
assert(raw_data);
|
||||
assert(dev->params->lut_numlines);
|
||||
if ((temp_uc < (*dev->params->lut)[0][MAX31865_LUTCOL_TEMP])
|
||||
|| (temp_uc > (*dev->params->lut)[dev->params->lut_numlines - 1][MAX31865_LUTCOL_TEMP])) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (i = 0 ; i < dev->params->lut_numlines ; i++) {
|
||||
if (temp_uc < (*dev->params->lut)[i][MAX31865_LUTCOL_TEMP]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i--;
|
||||
*raw_data = (temp_uc - (*dev->params->lut)[i][MAX31865_LUTCOL_A0]) /
|
||||
(*dev->params->lut)[i][MAX31865_LUTCOL_A1];
|
||||
|
||||
LOG_DEBUG("%s() >> i = %d, T = %"PRIi32" c°C, ADC code = 0x%04u\n",
|
||||
__FUNCTION__, i, temp, *raw_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* PUBLIC FUNCTIONS
|
||||
* ****************************************************************************/
|
||||
|
||||
int max31865_init(max31865_t *dev, const max31865_params_t *params)
|
||||
{
|
||||
assert(dev);
|
||||
assert(params);
|
||||
|
||||
uint8_t data_tx[2] = { 0 }; /* data sent by MCU to device on the MOSI line */
|
||||
uint16_t th_code; /* high/low threshold ADC code */
|
||||
|
||||
dev->params = params;
|
||||
|
||||
int ret = spi_init_cs(dev->params->spi, dev->params->cs_pin);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("Failed to initialize MAX31865\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The MAX31865 supports SPI modes 1 and 3 (datasheet p16, section "Serial interface") */
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_1, SPI_CLK_5MHZ);
|
||||
|
||||
/* write the configuration register: */
|
||||
spi_transfer_reg(dev->params->spi, dev->params->cs_pin, MAX31865_ADDR_CFG_W, params->cfg_byte);
|
||||
|
||||
/* write the high threshold register; LSB's address is MSB's address + 1 */
|
||||
if (_temp_to_raw(dev, dev->params->temp_high_threshold, &th_code) == 0) {
|
||||
data_tx[0] = th_code >> 8; /* MSB */
|
||||
data_tx[1] = th_code & 0x00FF; /* LSB */
|
||||
spi_transfer_regs(dev->params->spi, dev->params->cs_pin, MAX31865_ADDR_RTD_HTHRES_MSB_W,
|
||||
data_tx, NULL, 2);
|
||||
}
|
||||
|
||||
/* write the low threshold register; LSB's address is MSB's address + 1 */
|
||||
if (_temp_to_raw(dev, dev->params->temp_low_threshold, &th_code) == 0) {
|
||||
data_tx[0] = th_code >> 8; /* MSB */
|
||||
data_tx[1] = th_code & 0x00FF; /* LSB */
|
||||
spi_transfer_regs(dev->params->spi, dev->params->cs_pin, MAX31865_ADDR_RTD_LTHRES_MSB_W,
|
||||
data_tx, NULL, 2);
|
||||
}
|
||||
|
||||
spi_release(dev->params->spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void max31865_clear_fault(const max31865_t *dev, uint8_t *config)
|
||||
{
|
||||
assert(dev);
|
||||
|
||||
uint8_t cfg_byte = 0;
|
||||
uint8_t clr_byte = 0;
|
||||
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_1, SPI_CLK_5MHZ);
|
||||
cfg_byte = spi_transfer_reg(dev->params->spi, dev->params->cs_pin, MAX31865_ADDR_CFG_R, 0);
|
||||
if (config) {
|
||||
*config = cfg_byte;
|
||||
}
|
||||
clr_byte = cfg_byte | MAX31865_CFG_CLEAR_FAULT;
|
||||
spi_transfer_reg(dev->params->spi, dev->params->cs_pin, MAX31865_ADDR_CFG_W, clr_byte);
|
||||
spi_release(dev->params->spi);
|
||||
}
|
||||
|
||||
int max31865_read_raw(const max31865_t *dev, uint16_t *raw_data)
|
||||
{
|
||||
assert(dev);
|
||||
assert(raw_data);
|
||||
|
||||
uint8_t data_tx[3] = { 0 }; /* data sent by MCU to device on the MOSI line */
|
||||
uint8_t data_rx[3] = { 0 }; /* data received by MCU from device on the MISO line */
|
||||
|
||||
data_tx[0] = MAX31865_ADDR_RTD_MSB;
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_1, SPI_CLK_5MHZ);
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, data_rx,
|
||||
sizeof(data_tx));
|
||||
spi_release(dev->params->spi);
|
||||
|
||||
*raw_data = byteorder_bebuftohs(data_rx + 1);
|
||||
|
||||
/* test error bit b0: */
|
||||
if (*raw_data & 0x0001) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int max31865_raw_to_data(const max31865_t *dev, uint16_t raw_data, int32_t *rtd_temperature_cdegc)
|
||||
{
|
||||
assert(dev->params->lut);
|
||||
assert(rtd_temperature_cdegc);
|
||||
|
||||
if (raw_data < (*dev->params->lut)[0][MAX31865_LUTCOL_CODE]) {
|
||||
LOG_ERROR("%s() >> ERROR: raw_data too small 0x%04X < 0x%04X\n", __FUNCTION__, raw_data,
|
||||
(uint16_t)(*dev->params->lut)[0][MAX31865_LUTCOL_CODE]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (raw_data > (*dev->params->lut)[dev->params->lut_numlines - 1][MAX31865_LUTCOL_CODE]) {
|
||||
LOG_ERROR("%s() >> ERROR: raw_data too big 0x%04X > 0x%04X\n", __FUNCTION__, raw_data,
|
||||
(uint16_t)(*dev->params->lut)[dev->params->lut_numlines -
|
||||
1][MAX31865_LUTCOL_CODE]);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* walk the LUT to find the appropriate coefficients for linear interpolation: */
|
||||
int i = 0;
|
||||
for (i = 0 ; i < dev->params->lut_numlines ; i++) {
|
||||
if (raw_data < (*dev->params->lut)[i][MAX31865_LUTCOL_CODE]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i--;
|
||||
LOG_DEBUG("%s() >> i = %d\n", __FUNCTION__, i);
|
||||
LOG_DEBUG("%s() >> a0 = %"PRIi32", a1 = %"PRIi32", raw_data = 0x%04X\n", __FUNCTION__,
|
||||
(*dev->params->lut)[i][MAX31865_LUTCOL_A0],
|
||||
(*dev->params->lut)[i][MAX31865_LUTCOL_A1],
|
||||
(uint16_t)(*dev->params->lut)[i][MAX31865_LUTCOL_CODE]);
|
||||
/* calculate T in µ°C by linear interpolation with the coefficients from the LUT: */
|
||||
int32_t temp_uc = (*dev->params->lut)[i][MAX31865_LUTCOL_A0]
|
||||
+ (*dev->params->lut)[i][MAX31865_LUTCOL_A1] * raw_data;
|
||||
/* convert µ°C to c°C: */
|
||||
int32_t temp_cc = temp_uc / 10000;
|
||||
LOG_DEBUG("%s() >> T (°µC) = %"PRIi32"\n", __FUNCTION__, temp_uc);
|
||||
LOG_DEBUG("%s() >> T (°C) = %"PRIi32".%02d\n", __FUNCTION__,
|
||||
temp_cc / 100, (int)labs(temp_cc) % 100);
|
||||
|
||||
/* convert to centi degC */
|
||||
*rtd_temperature_cdegc = temp_uc / 10000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int max31865_read(const max31865_t *dev, int32_t *rtd_temperature_cdegc)
|
||||
{
|
||||
assert(dev);
|
||||
assert(rtd_temperature_cdegc);
|
||||
|
||||
uint16_t raw_data;
|
||||
if (max31865_read_raw(dev, &raw_data) == -EIO) {
|
||||
return -EIO;
|
||||
}
|
||||
else {
|
||||
return max31865_raw_to_data(dev, raw_data, rtd_temperature_cdegc);
|
||||
}
|
||||
}
|
||||
|
||||
int max31865_detect_fault(const max31865_t *dev, max31865_fault_t *flt_code)
|
||||
{
|
||||
assert(dev);
|
||||
assert(flt_code);
|
||||
|
||||
uint8_t data_tx[2] = { 0 }; /* data sent by MCU to device on the MOSI line */
|
||||
uint8_t data_rx[2] = { 0 }; /* data received by MCU from device on the MISO line */
|
||||
uint8_t cfg; /* value of the config register */
|
||||
uint8_t fault; /* value of the fault register */
|
||||
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_1, SPI_CLK_5MHZ);
|
||||
|
||||
/* read current config: */
|
||||
data_tx[0] = MAX31865_ADDR_CFG_R;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, data_rx,
|
||||
sizeof(data_tx));
|
||||
|
||||
/* start automatic fault detection: */
|
||||
cfg = data_rx[1] | MAX31865_CFG_FLTDET_AUTO_START;
|
||||
data_tx[0] = MAX31865_ADDR_CFG_W;
|
||||
data_tx[1] = cfg;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, NULL,
|
||||
sizeof(data_tx));
|
||||
|
||||
/* wait for completion and check actual completion; datasheet states 100µs: */
|
||||
ztimer_sleep(ZTIMER_USEC, 200);
|
||||
data_tx[0] = MAX31865_ADDR_CFG_R;
|
||||
data_tx[1] = 0;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, data_rx,
|
||||
sizeof(data_tx));
|
||||
cfg = data_rx[1];
|
||||
if ((cfg & MAX31865_CFG_FLTDET_MASK) != MAX31865_CFG_FLTDET_IDLE) {
|
||||
spi_release(dev->params->spi);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* read fault code: */
|
||||
data_tx[0] = MAX31865_ADDR_FAULT;
|
||||
data_tx[1] = 0;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, data_rx,
|
||||
sizeof(data_tx));
|
||||
fault = data_rx[1];
|
||||
|
||||
spi_release(dev->params->spi);
|
||||
|
||||
if (fault & (MAX31865_FLT_REF_FC | MAX31865_FLT_REF_FO | MAX31865_FLT_RTD_FO)) {
|
||||
*flt_code = MAX31865_FAULT_CIRCUIT;
|
||||
}
|
||||
else if (fault & MAX31865_FLT_THRESHIGH) {
|
||||
*flt_code = MAX31865_FAULT_RTD_HIGH;
|
||||
}
|
||||
else if (fault & MAX31865_FLT_THRESLOW) {
|
||||
*flt_code = MAX31865_FAULT_RTD_LOW;
|
||||
}
|
||||
else if (fault & MAX31865_FLT_VOLTAGE) {
|
||||
*flt_code = MAX31865_FAULT_VOLTAGE;
|
||||
}
|
||||
else {
|
||||
*flt_code = MAX31865_FAULT_NO_FAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void max31865_switch_vbias(const max31865_t *dev, bool enable)
|
||||
{
|
||||
assert(dev);
|
||||
|
||||
uint8_t data_tx[2] = { 0 }; /* data sent by MCU to device on the MOSI line */
|
||||
uint8_t data_rx[2] = { 0 }; /* data received by MCU from device on the MISO line */
|
||||
uint8_t cfg; /* value of the config register */
|
||||
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_1, SPI_CLK_5MHZ);
|
||||
|
||||
/* read current config: */
|
||||
data_tx[0] = MAX31865_ADDR_CFG_R;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, data_rx,
|
||||
sizeof(data_tx));
|
||||
cfg = data_rx[1];
|
||||
|
||||
/* switch Vbias on or off: */
|
||||
if (enable) {
|
||||
cfg = cfg | MAX31865_CFG_VBIAS_ON; /* switch on */
|
||||
}
|
||||
else {
|
||||
cfg = cfg & ~MAX31865_CFG_VBIAS_ON; /* switch off */
|
||||
}
|
||||
data_tx[0] = MAX31865_ADDR_CFG_W;
|
||||
data_tx[1] = cfg;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, NULL,
|
||||
sizeof(data_tx));
|
||||
|
||||
spi_release(dev->params->spi);
|
||||
}
|
||||
|
||||
void max31865_oneshot(const max31865_t *dev)
|
||||
{
|
||||
assert(dev);
|
||||
|
||||
uint8_t data_tx[2] = { 0 }; /* data sent by MCU to device on the MOSI line */
|
||||
uint8_t data_rx[2] = { 0 }; /* data received by MCU from device on the MISO line */
|
||||
uint8_t cfg; /* value of the config register */
|
||||
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_1, SPI_CLK_5MHZ);
|
||||
|
||||
/* read current config: */
|
||||
data_tx[0] = MAX31865_ADDR_CFG_R;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, data_rx,
|
||||
sizeof(data_tx));
|
||||
cfg = data_rx[1];
|
||||
|
||||
/* switch Vbias on or off: */
|
||||
cfg = cfg | MAX31865_CFG_1SHOT;
|
||||
data_tx[0] = MAX31865_ADDR_CFG_W;
|
||||
data_tx[1] = cfg;
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data_tx, NULL,
|
||||
sizeof(data_tx));
|
||||
|
||||
spi_release(dev->params->spi);
|
||||
}
|
||||
19
tests/drivers/max31865/README.md
Normal file
19
tests/drivers/max31865/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# MAX31865 driver test
|
||||
|
||||
## About
|
||||
|
||||
This is a test application for the
|
||||
[MAX31865](https://www.analog.com/en/products/max31865.html)
|
||||
driver, a RTD-to-digital converter, tailored for Pt100 and Pt1000
|
||||
sensing elements.
|
||||
|
||||
## Usage
|
||||
|
||||
This test application will initialize a MAX31865 device to output
|
||||
the temperature every second.
|
||||
|
||||
The driver uses fixed-point arithmetic for all conversions.
|
||||
To do so, it needs a lookup table (LUT).
|
||||
A default one is provided for a Pt100 and a 330Ω reference resistor.
|
||||
The user can generate one for different resistor values with the
|
||||
`dist/genlut.py` script.
|
||||
Loading…
x
Reference in New Issue
Block a user