LCOV - code coverage report
Current view: top level - AP_TemperatureSensor - AP_TemperatureSensor_MAX31865.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 62 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 4 0.0 %

          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 &params) :
      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             : 

Generated by: LCOV version 1.14