blob: d88bd656b5f1d63ec0358245d37c58dffd554536 [file] [log] [blame]
/*
* Copyright (c) 2022 HAW Hamburg FTZ-DIWIP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "max31865.h"
static int max31865_spi_write(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
{
const struct max31865_config *cfg = dev->config;
const struct spi_buf bufs[] = {{
.buf = &reg,
.len = 1,
},
{.buf = data, .len = len}};
const struct spi_buf_set tx = {.buffers = bufs, .count = 2};
return spi_write_dt(&cfg->spi, &tx);
}
static int max31865_spi_read(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
{
const struct max31865_config *cfg = dev->config;
reg &= 0x7F;
const struct spi_buf tx_buf = {.buf = &reg, .len = 1};
const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1};
struct spi_buf rx_buf[] = {{
.buf = &reg,
.len = 1,
},
{.buf = data, .len = len}};
const struct spi_buf_set rx = {.buffers = rx_buf, .count = 2};
return spi_transceive_dt(&cfg->spi, &tx, &rx);
}
/**
* @brief Set device configuration register
*
* @param device device instance
* @return 0 if successful, or negative error code from SPI API
*/
static int configure_device(const struct device *dev)
{
struct max31865_data *data = dev->data;
uint8_t cmd[] = {data->config_control_bits};
int err = max31865_spi_write(dev, WR(REG_CONFIG), cmd, 1);
if (err < 0) {
LOG_ERR("Error write SPI%d\n", err);
}
return err;
}
/**
* @brief Set device fail threshold registers
*
* @param device device instance
* @return 0 if successful, or negative error code from SPI API
*/
static int set_threshold_values(const struct device *dev)
{
const struct max31865_config *config = dev->config;
uint8_t cmd[] = {
(config->high_threshold >> 7) & 0x00ff, (config->high_threshold << 1) & 0x00ff,
(config->low_threshold >> 7) & 0x00ff, (config->low_threshold << 1) & 0x00ff};
int err = max31865_spi_write(dev, WR(REG_HIGH_FAULT_THR_MSB), cmd, 4);
if (err < 0) {
LOG_ERR("Error write SPI%d\n", err);
}
return err;
}
#ifdef CONFIG_NEWLIB_LIBC
/**
* Apply the Callendar-Van Dusen equation to convert the RTD resistance
* to temperature:
* Tr = (-A + SQRT(delta) ) / 2*B
* delta = A^2 - 4B*(1-Rt/Ro)
* For under zero, taken from
* https://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
* @param resistance measured resistance
* @param resistance_0 constant resistance at 0oC
* @return calculated temperature
*/
static double calculate_temperature(double resistance, double resistance_0)
{
double temperature;
double delta = (RTD_A * RTD_A) - 4 * RTD_B * (1.0 - resistance / resistance_0);
temperature = (-RTD_A + sqrt(delta)) / (2 * RTD_B);
if (temperature > 0.0) {
return temperature;
}
resistance /= resistance_0;
resistance *= 100.0;
temperature = A[0] + A[1] * resistance + A[2] * pow(resistance, 2) +
A[3] * pow(resistance, 3) + A[4] * pow(resistance, 4) +
A[5] * pow(resistance, 5);
return temperature;
}
#else
/**
* Apply a very good linear approximation of the Callendar-Van Dusen equation to convert the RTD
* resistance to temperature:
* @param resistance measured resistance
* @param resistance_0 constant resistance at 0oC
* @return calculated temperature
*/
static double calculate_temperature(double resistance, double resistance_0)
{
double temperature;
temperature = (resistance - resistance_0) / (resistance_0 * RTD_A);
return temperature;
}
#endif
/**
* @brief Enable/Disable Vbias for MAX31865
*
* @param device device instance
* @param enable true, turn on vbias, false, turn off vbias
* @return 0 if successful, or negative error code from SPI API
*/
static int max31865_set_vbias(const struct device *dev, bool enable)
{
struct max31865_data *data = dev->data;
WRITE_BIT(data->config_control_bits, 7, enable);
return configure_device(dev);
}
static char *max31865_error_to_string(uint8_t fault_register)
{
switch (fault_register) {
case 0:
return "No error";
case MAX31865_FAULT_VOLTAGE:
return "Over/under voltage fault";
case MAX31865_FAULT_RTDIN_FORCE:
return "RTDIN- < 0.85*VBIAS (FORCE- open)";
case MAX31865_FAULT_REFIN_FORCE:
return "REFIN- < 0.85*VBIAS (FORCE- open)";
case MAX31865_FAULT_REFIN:
return "REFIN- > 0.85*VBIAS";
case MAX31865_FAULT_LOW_THRESHOLD:
return "RTD below low threshold";
case MAX31865_FAULT_HIGH_THRESHOLD:
return "RTD above high threshold";
}
return "";
}
static int max31865_fault_register(const struct device *dev)
{
uint8_t fault_register;
max31865_spi_read(dev, (REG_FAULT_STATUS), &fault_register, 1);
struct max31865_data *data = dev->data;
/*Clear fault register */
WRITE_BIT(data->config_control_bits, 1, 1);
configure_device(dev);
LOG_ERR("Fault Register: 0x%02x, %s", fault_register,
max31865_error_to_string(fault_register));
WRITE_BIT(data->config_control_bits, 1, 0);
return 0;
}
/**
* @brief Get temperature value in oC for device
*
* @param device device instance
* @param temperature measured temperature
* @return 0 if successful, or negative error code
*/
static int max31865_get_temperature(const struct device *dev)
{
max31865_set_vbias(dev, true);
union read_reg_u {
uint8_t u8[2];
uint16_t u16;
} read_reg;
read_reg.u16 = 0;
/* Waiting Time for Temerature Conversion (Page 3 of the datasheet)*/
k_sleep(K_MSEC(66));
/* Read resistance measured value */
int err = max31865_spi_read(dev, (REG_RTD_MSB), read_reg.u8, 2);
max31865_set_vbias(dev, false);
if (err < 0) {
LOG_ERR("SPI read %d\n", err);
return -EIO;
}
read_reg.u16 = sys_be16_to_cpu(read_reg.u16);
LOG_DBG("RAW: %02X %02X , %04X", read_reg.u8[0], read_reg.u8[1], read_reg.u16);
if (TESTBIT(read_reg.u16, 0)) {
max31865_fault_register(dev);
return -EIO;
}
const struct max31865_config *config = dev->config;
struct max31865_data *data = dev->data;
read_reg.u16 = read_reg.u16 >> 1;
double resistance = (double)read_reg.u16;
resistance /= 32768;
resistance *= config->resistance_reference;
data->temperature = calculate_temperature(resistance, config->resistance_at_zero);
return 0;
}
static int max31865_init(const struct device *dev)
{
const struct max31865_config *config = dev->config;
if (!spi_is_ready_dt(&config->spi)) {
return -ENODEV;
}
struct max31865_data *data = dev->data;
/* Set the confgiuration register */
data->config_control_bits = 0;
WRITE_BIT(data->config_control_bits, 6, config->conversion_mode);
WRITE_BIT(data->config_control_bits, 5, config->one_shot);
WRITE_BIT(data->config_control_bits, 4, config->three_wire);
data->config_control_bits |= config->fault_cycle & 0b00001100;
WRITE_BIT(data->config_control_bits, 0, config->filter_50hz);
configure_device(dev);
set_threshold_values(dev);
max31865_set_vbias(dev, false);
return 0;
}
static int max31865_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
LOG_ERR("Invalid channel provided");
return -ENOTSUP;
}
return max31865_get_temperature(dev);
}
static int max31865_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct max31865_data *data = dev->data;
switch (chan) {
case SENSOR_CHAN_AMBIENT_TEMP:
return sensor_value_from_double(val, data->temperature);
default:
return -EINVAL;
}
}
static const struct sensor_driver_api max31865_api_funcs = {
.sample_fetch = max31865_sample_fetch,
.channel_get = max31865_channel_get,
};
#define MAX31865_DEFINE(inst) \
\
static struct max31865_data max31865_data_##inst; \
\
static const struct max31865_config max31865_config_##inst = { \
.spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
.resistance_at_zero = DT_INST_PROP(inst, resistance_at_zero), \
.resistance_reference = DT_INST_PROP(inst, resistance_reference), \
.conversion_mode = false, \
.one_shot = true, \
.three_wire = DT_INST_PROP(inst, maxim_3_wire), \
.fault_cycle = MAX31865_FAULT_DETECTION_NONE, \
.filter_50hz = DT_INST_PROP(inst, filter_50hz), \
.low_threshold = DT_INST_PROP(inst, low_threshold), \
.high_threshold = DT_INST_PROP(inst, high_threshold), \
}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, max31865_init, NULL, &max31865_data_##inst, \
&max31865_config_##inst, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&max31865_api_funcs);
/* Create the struct device for every status "okay" node in the devicetree. */
DT_INST_FOREACH_STATUS_OKAY(MAX31865_DEFINE)