component max31855 "Support for the MAX31855 Thermocouple-to-Digital converter using bitbanged spi";
description """The component requires at least 3 pins to bitbang spi protocol, for example:
\\fB loadrt max31855 personality=1\\fR
\\fB setp hm2_6i25.0.gpio.023.is_output true\\fR
\\fB setp hm2_6i25.0.gpio.024.is_output true\\fR
\\fB net spi.clk.in hm2_6i25.0.gpio.023.out max31855.0.clk.out\\fR
\\fB net spi.cs.in hm2_6i25.0.gpio.024.out max31855.0.cs.out\\fR
\\fB net spi.data0.in hm2_6i25.0.gpio.033.in_not max31855.0.data.0.in\\fR
\\fB addf max31855.0.bitbang-spi servo-thread \\fR
The MAX31855 supports a range of -270C to 1800C, however linearization data
is only available for the -200C to 1350C range, beyond which raw temperature is returned.
Temperature pins are provided for readings in Celsius, Fahrenheit and Kelvin,
temperature values are not updated while a fault condition is present.
The personality parameter is used to indicate the number of sensors.
Multiple sensors share the clk and cs pins, but connect to discrete data input pins.
A maximum of 15 sensors are supported.
""";
pin in bit data.#.in [15 : (personality & 0xf)] "Pin(s) connected to data out.";
pin out bit cs.out "Pin connected to cs, pulled low to shift data, pulled high for data refresh.";
pin out bit clk.out "Pin connected to clk.";
pin out float temp_celsius.# [15 : (personality & 0xf)] """Temperature output values in Celsius.""";
pin out float temp_fahrenheit.# [15 : (personality & 0xf)] """Temperature in Fahrenheit.""";
pin out float temp_kelvin.# [15 : (personality & 0xf)] """Temperature in Kelvin.""";
pin out bit fault.# [15 : (personality & 0xf)] "Fault condition detected.";
pin out unsigned fault_flags.# [15 : (personality & 0xf)] "Fault flags: 0x1 = open sensor, 0x2 short to gnd, 0x3 short to vcc.";
variable unsigned data_frame [15];
variable unsigned state = 1;
function bitbang_spi fp;
license "GPL";
author "Joseph Calderon";
;;
#include "rtapi_math.h"
static float accpoly(float *v, size_t n, float p) {
int i;
float ret = 0;
for (i = 0; i < n; i++) {
ret += v[i] * pow(p, i);
}
return ret;
}
static float to_kelvin(float celsius) {
return celsius + 273.15;
}
static float to_fahrenheit(float celsius) {
return celsius * 1.80 + 32.0;
}
static float read_celsius(int32_t v) {
if (v & 0x7) {
return nan(""); /* fault bit(s) set */
}
/* thermocouple temperature is in bits 31:18 */
if (v & 0x80000000) {
v = 0xffffc000 | ((v >> 18) & 0x0003ffff); /* extend sign bit */
} else {
v >>= 18;
}
return v / 4.0; /* 0.25 degree resolution */
}
static float read_internal(int32_t v) {
if (v & 0x7) {
return nan(""); /* fault bit(s) set */
}
/* internal temperature is in bits 15:4 */
v = 0x0000ffff & v;
if (v & 0x8000) {
v = 0xfffff000 | ((v >> 4) & 0x00000fff); /* extend sign bit */
} else {
v >>= 4;
}
return v / 16.0; /* 0.0625 degree resolution */
}
static float read_celsius_adjusted(int32_t sensor_data) {
float temp_raw = read_celsius(sensor_data);
float temp_internal = read_internal(sensor_data);
float voltage_internal = 0, voltage_thermocouple = 0, temp_corrected = 0;
if (isnan(temp_raw) || isnan(temp_internal))
return nan("");
/* NIST K-Type table (http://srdata.nist.gov/its90/download/type_k.tab) */
float coeff[][11] = {
{-0.0176004134, 0.0389212035, 1.85587705e-05, -9.94575942e-08,
3.18409465e-10, -5.60728439e-13, 5.60750581e-16, -3.20207199e-19,
9.71511487e-23, -1.21047216e-26, 0},
{0, 0.0394501276, 2.36223732e-05, -3.28589067e-07, -4.99048269e-09,
-6.75090608e-11, -5.74103265e-13, -3.10888726e-15, -1.0451609e-17,
-1.9889267e-20, -1.63226981e-23},
{0, 25.1734619, -1.16628778, -1.08336377, -0.897735417, -0.373423755,
-0.0866326466, -0.0104505979, -0.000519205758, 0, 0},
{0, 25.0835495, 0.0786010623, -0.250313103, 0.0831526965, -0.0122803403,
0.000980403624, -4.41302982e-05, 1.05773404e-06, -1.05275504e-08, 0},
{-131.805801, 48.3022194, -1.64603102, 0.0546473116, -0.000965071493,
8.80219341e-06, -3.1108101e-08, 0, 0, 0, 0}};
int coeff_cols = sizeof(coeff[0]) / sizeof(float);
/* determine thermocouple voltage by substracting internal temp and adjusting
* for K-type thermocouple */
voltage_thermocouple = (temp_raw - temp_internal) * 0.041276;
if (temp_internal >= 0) {
float a[] = {0.118597597, -0.000118343203, 126.968597};
/* for positive temps additional exponential coefficients are needed */
voltage_internal += accpoly(coeff[0], coeff_cols, temp_internal);
voltage_internal += a[0] * exp(a[1] * pow((temp_internal - a[2]), 2));
} else if (temp_internal < 0) {
voltage_internal += accpoly(coeff[1], coeff_cols, temp_internal);
}
float voltage_total = voltage_thermocouple + voltage_internal;
/* linearize temperature depending on voltage range */
if (voltage_total < 0) {
/* Temperature is between -200 and 0C. */
temp_corrected += accpoly(coeff[2], coeff_cols, voltage_total);
} else if (voltage_total < 20.644) {
/* Temperature is between 0C and 500C. */
temp_corrected += accpoly(coeff[3], coeff_cols, voltage_total);
} else if (voltage_total < 54.886) {
/* Temperature is between 500C and 1372C. */
temp_corrected += accpoly(coeff[4], coeff_cols, voltage_total);
} else {
/* NIST only has data for K-type thermocouples from -200C to +1372C. */
temp_corrected = temp_raw;
}
return temp_corrected;
}
FUNCTION(bitbang_spi) {
int nbit = (state >> 1) & 0x3f;
int delay = (state >> 7) & 0x3ff;
int cs = state & 0x1;
clk_out ^= 0x1;
if (cs) {
/* data refreshes when cs is pulled high */
delay--;
if (delay <= 0) {
nbit = 32;
cs = 0;
}
} else {
int i, n = (personality > 15) ? 15 : personality;
/* with cs low pull data bits when clock is high */
if (clk_out) {
for (i = 0; i < n; i++) {
data_frame[i] |= (data_in(i) << nbit);
}
nbit--;
}
if (nbit < 0) {
for (i = 0; i < n; i++) {
float f = read_celsius_adjusted((int32_t)data_frame[i]);
fault(i) = (data_frame[i] & 0x7) ? TRUE : FALSE;
fault_flags(i) = data_frame[i] & 0x7;
if (isnan(f)) {
rtapi_print("max31855: sensor %d detected fault %x\n", i, data_frame[i] & 0x7);
} else {
temp_celsius(i) = f;
temp_fahrenheit(i) = to_fahrenheit(f);
temp_kelvin(i) = to_kelvin(f);
}
data_frame[i] = 0;
}
nbit = 0;
cs = 1;
}
delay++;
}
state = (delay << 7) | (nbit << 1) | cs;
cs_out = cs;
}