Line data Source code
1 : /*
2 : * Copyright (C) 2023 Kraus Hamdani Aerospace Inc. All rights reserved.
3 : *
4 : * This file is free software: you can redistribute it and/or modify it
5 : * under the terms of the GNU General Public License as published by the
6 : * Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This file is distributed in the hope that it will be useful, but
10 : * WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 : * See the GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License along
15 : * with this program. If not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Code by Tom Pittenger
18 : */
19 : /*
20 : Implements SPI driver for MAX31865 digital RTD Temperature converter
21 : */
22 :
23 : #include "AP_TemperatureSensor_MAX31865.h"
24 :
25 : #if AP_TEMPERATURE_SENSOR_MAX31865_ENABLED
26 : #include <stdio.h>
27 : #include <AP_Math/AP_Math.h>
28 : #include <AP_HAL/utility/sparse-endian.h>
29 :
30 : extern const AP_HAL::HAL &hal;
31 :
32 : #define MAX31865_REG_WRITE_ADDR_OFFSET 0x80
33 :
34 :
35 : #define MAX31865_REG_CONFIG_READ 0x00
36 : #define MAX31865_REG_CONFIG_WRITE (MAX31865_REG_CONFIG_READ | MAX31865_REG_WRITE_ADDR_OFFSET)
37 : #define MAX31865_REG_DATA_MSB 0x01
38 : #define MAX31865_REG_DATA_LSB 0x02
39 : #define MAX31865_REG_HIGH_FAULT_THRESH_MSB 0x03
40 : #define MAX31865_REG_HIGH_FAULT_THRESH_LSB 0x04
41 : #define MAX31865_REG_LOW_FAULT_THRESH_MSB 0x05
42 : #define MAX31865_REG_LOW_FAULT_THRESH_LSB 0x06
43 : #define MAX31865_REG_FAULT_STATUS_READ 0x07
44 : #define MAX31865_REG_FAULT_STATUS_WRITE (MAX31865_REG_FAULT_STATUS_READ | MAX31865_REG_WRITE_ADDR_OFFSET)
45 :
46 : #define MAX31865_DEBUGGING 0
47 :
48 : #if MAX31865_DEBUGGING
49 : #ifdef HAL_BUILD_AP_PERIPH
50 : extern "C" {
51 : void can_printf(const char *fmt, ...);
52 : }
53 : //# define Debug(fmt, args ...) do { can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
54 : # define Debug(fmt, args ...) do { can_printf(fmt, ## args); } while(0)
55 : #else
56 : # define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
57 : #endif // HAL_BUILD_AP_PERIPH
58 : #else
59 : # define Debug(fmt, args ...)
60 : #endif // MAX31865_DEBUGGING
61 :
62 :
63 : const AP_Param::GroupInfo AP_TemperatureSensor_MAX31865::var_info[] = {
64 :
65 : // @Param: RTD_NOM
66 : // @DisplayName: Nominal RTD resistance
67 : // @Description: Nominal RTD resistance used to calculate temperature, typically 100 or 1000 ohms.
68 : // @User: Standard
69 : AP_GROUPINFO("RTD_NOM", 8, AP_TemperatureSensor_MAX31865, nominal_resistance, 100),
70 :
71 : // @Param: RTD_REF
72 : // @DisplayName: RTD reference resistance
73 : // @Description: Reference resistance used to calculate temperature, in ohms
74 : // @User: Standard
75 : AP_GROUPINFO("RTD_REF", 9, AP_TemperatureSensor_MAX31865, reference_resistance, 400),
76 :
77 : // CHECK/UPDATE INDEX TABLE IN AP_TemperatureSensor_Backend.cpp WHEN CHANGING OR ADDING PARAMETERS
78 :
79 : AP_GROUPEND
80 :
81 : };
82 :
83 0 : AP_TemperatureSensor_MAX31865::AP_TemperatureSensor_MAX31865(AP_TemperatureSensor &front,
84 : AP_TemperatureSensor::TemperatureSensor_State &state,
85 0 : AP_TemperatureSensor_Params ¶ms) :
86 0 : AP_TemperatureSensor_Backend(front, state, params)
87 : {
88 0 : AP_Param::setup_object_defaults(this, var_info);
89 0 : _state.var_info = var_info;
90 0 : }
91 :
92 0 : void AP_TemperatureSensor_MAX31865::init()
93 : {
94 0 : if (_dev) {
95 0 : return;
96 : }
97 :
98 : // also look for "max31865_n" where n is the addr param.
99 0 : char name[12] = "max31865";
100 0 : if (_params.bus_address > 0 && _params.bus_address <= 9) {
101 0 : name[8] = '_';
102 0 : name[9] = '0' + _params.bus_address; // convert bus_address to ascii
103 0 : name[10] = 0;
104 : }
105 :
106 0 : _dev = hal.spi->get_device_ptr(name);
107 0 : if (!_dev) {
108 0 : return;
109 : }
110 :
111 : // Set 3 wire config bit
112 0 : if (_params.type == AP_TemperatureSensor_Params::Type::MAX31865_3_wire) {
113 0 : config_register |= 0b00010000;
114 : }
115 :
116 0 : WITH_SEMAPHORE(_dev->get_semaphore());
117 :
118 0 : _dev->set_speed(AP_HAL::Device::SPEED_LOW);
119 :
120 0 : _dev->write_register(MAX31865_REG_CONFIG_WRITE, config_register);
121 :
122 0 : _dev->set_speed(AP_HAL::Device::SPEED_HIGH);
123 :
124 : /* Request 5Hz update */
125 0 : _dev->register_periodic_callback(200 * AP_USEC_PER_MSEC,
126 : FUNCTOR_BIND_MEMBER(&AP_TemperatureSensor_MAX31865::thread_tick, void));
127 0 : }
128 :
129 : // Convert raw value in to temperature in deg celsius
130 : // https://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
131 0 : float AP_TemperatureSensor_MAX31865::calculate_temperature(const uint16_t raw) const
132 : {
133 0 : const float measured_resistance = (raw / 32768.0) * reference_resistance;
134 :
135 : // direct mathmatical method, valid for positive temperature
136 0 : const float A = 3.9083e-3;
137 0 : const float B = -5.775e-7;
138 :
139 0 : const float Z1 = -A;
140 0 : const float Z2 = A*A - (4.0 * B);
141 0 : const float Z3 = (4.0 * B) / nominal_resistance;
142 0 : const float Z4 = 2.0 * B;
143 :
144 0 : float temp = (Z1 + sqrtf(Z2 + (Z3 * measured_resistance))) / Z4;
145 :
146 0 : if (is_positive(temp)) {
147 0 : return temp;
148 : }
149 :
150 : // Polynomial for sub zero temperatures
151 : // normalize to 100 ohm
152 0 : const float norm_resistance = (measured_resistance / nominal_resistance) * 100.0;
153 :
154 0 : float rpoly = norm_resistance;
155 0 : temp = -242.02;
156 0 : temp += 2.2228 * rpoly;
157 0 : rpoly *= norm_resistance;
158 0 : temp += 2.5859e-3 * rpoly;
159 0 : rpoly *= norm_resistance;
160 0 : temp -= 4.8260e-6 * rpoly;
161 0 : rpoly *= norm_resistance;
162 0 : temp -= 2.8183e-8 * rpoly;
163 0 : rpoly *= norm_resistance;
164 0 : temp += 1.5243e-10 * rpoly;
165 :
166 0 : return temp;
167 : }
168 :
169 0 : void AP_TemperatureSensor_MAX31865::thread_tick()
170 : {
171 0 : if (!is_positive(nominal_resistance) || !is_positive(reference_resistance)) {
172 : // calculate_temperature function will fall over with bad reference values
173 0 : return;
174 : }
175 :
176 : uint16_t raw_data;
177 0 : if (!_dev->read_registers(MAX31865_REG_DATA_MSB, (uint8_t *)&raw_data, sizeof(raw_data))) {
178 0 : return;
179 : }
180 :
181 : // 16bit byte swap
182 0 : const uint16_t data = htobe16(raw_data);
183 :
184 : // fault is LSB bit, temperature data is upper 15 bits
185 0 : const bool is_fault = (data & 0x0001) != 0;
186 0 : const uint16_t data_temperature = data >> 1;
187 :
188 0 : if (is_fault) {
189 : #if MAX31865_DEBUGGING
190 : uint8_t data_fault = 0;
191 : if (_dev->read_registers(MAX31865_REG_FAULT_STATUS_READ, (uint8_t *)&data_fault, 1)) {
192 : Debug("%d MAX31865 fault: %u -> 0x%x", _state.instance, data, data_fault);
193 : } else {
194 : Debug("%d MAX31865 unkown fault: %u", data, _state.instance);
195 : }
196 : #endif
197 : // clear the fault
198 0 : _dev->write_register(MAX31865_REG_CONFIG_WRITE, config_register);
199 0 : return;
200 : }
201 :
202 0 : const float temperature = calculate_temperature(data_temperature);
203 :
204 : #if MAX31865_DEBUGGING
205 : Debug("%d MAX31865 %u -> %.2f C", _state.instance, data_temperature, temperature);
206 : #endif
207 :
208 0 : set_temperature(temperature);
209 : }
210 :
211 : #endif // AP_TEMPERATURE_SENSOR_MAX31865_ENABLED
212 :
|