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

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : /*
      17             :   backend driver for airspeed from a I2C MS4525D0 sensor
      18             :  */
      19             : #include "AP_Airspeed_MS4525.h"
      20             : 
      21             : #if AP_AIRSPEED_MS4525_ENABLED
      22             : 
      23             : #include <AP_Common/AP_Common.h>
      24             : #include <AP_HAL/AP_HAL.h>
      25             : #include <AP_HAL/I2CDevice.h>
      26             : #include <AP_Math/AP_Math.h>
      27             : #include <GCS_MAVLink/GCS.h>
      28             : #include <stdio.h>
      29             : #include <utility>
      30             : 
      31             : extern const AP_HAL::HAL &hal;
      32             : 
      33             : #define MS4525D0_I2C_ADDR1 0x28
      34             : #define MS4525D0_I2C_ADDR2 0x36
      35             : #define MS4525D0_I2C_ADDR3 0x46
      36             : 
      37             : // probe for a sensor
      38           0 : bool AP_Airspeed_MS4525::probe(uint8_t bus, uint8_t address)
      39             : {
      40           0 :     _dev = hal.i2c_mgr->get_device_ptr(bus, address);
      41           0 :     if (!_dev) {
      42           0 :         return false;
      43             :     }
      44           0 :     WITH_SEMAPHORE(_dev->get_semaphore());
      45             : 
      46             :     // lots of retries during probe
      47           0 :     _dev->set_retries(10);
      48             :     
      49           0 :     _measure();
      50           0 :     hal.scheduler->delay(10);
      51           0 :     _collect();
      52             : 
      53           0 :     return _last_sample_time_ms != 0;
      54           0 : }
      55             : 
      56             : // probe and initialise the sensor
      57           0 : bool AP_Airspeed_MS4525::init()
      58             : {
      59             :     static const uint8_t addresses[] = { MS4525D0_I2C_ADDR1, MS4525D0_I2C_ADDR2, MS4525D0_I2C_ADDR3 };
      60           0 :     if (bus_is_configured()) {
      61             :         // the user has configured a specific bus
      62           0 :         for (uint8_t addr : addresses) {
      63           0 :             if (probe(get_bus(), addr)) {
      64           0 :                 goto found_sensor;
      65             :             }
      66             :         }
      67             :     } else {
      68             :         // if bus is not configured then fall back to the old
      69             :         // behaviour of probing all buses, external first
      70           0 :         FOREACH_I2C_EXTERNAL(bus) {
      71           0 :             for (uint8_t addr : addresses) {
      72           0 :                 if (probe(bus, addr)) {
      73           0 :                     goto found_sensor;
      74             :                 }
      75             :             }
      76             :         }
      77           0 :         FOREACH_I2C_INTERNAL(bus) {
      78           0 :             for (uint8_t addr : addresses) {
      79           0 :                 if (probe(bus, addr)) {
      80           0 :                     goto found_sensor;
      81             :                 }
      82             :             }
      83             :         }
      84             :     }
      85             : 
      86           0 :     GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "MS4525[%u]: no sensor found", get_instance());
      87           0 :     return false;
      88             : 
      89           0 : found_sensor:
      90           0 :     _dev->set_device_type(uint8_t(DevType::MS4525));
      91           0 :     set_bus_id(_dev->get_bus_id());
      92             : 
      93           0 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "MS4525[%u]: Found bus %u addr 0x%02x", get_instance(), _dev->bus_num(), _dev->get_bus_address());
      94             : 
      95             :     // drop to 2 retries for runtime
      96           0 :     _dev->set_retries(2);
      97             :     
      98           0 :     _dev->register_periodic_callback(20000,
      99             :                                      FUNCTOR_BIND_MEMBER(&AP_Airspeed_MS4525::_timer, void));
     100           0 :     return true;
     101             : }
     102             : 
     103             : // start a measurement
     104           0 : void AP_Airspeed_MS4525::_measure()
     105             : {
     106           0 :     _measurement_started_ms = 0;
     107           0 :     uint8_t cmd = 0;
     108           0 :     if (_dev->transfer(&cmd, 1, nullptr, 0)) {
     109           0 :         _measurement_started_ms = AP_HAL::millis();
     110             :     }
     111           0 : }
     112             : 
     113             : /*
     114             :   this equation is an inversion of the equation in the
     115             :   pressure transfer function figure on page 4 of the datasheet
     116             :   
     117             :   We negate the result so that positive differential pressures
     118             :   are generated when the bottom port is used as the static
     119             :   port on the pitot and top port is used as the dynamic port
     120             : */
     121           0 : float AP_Airspeed_MS4525::_get_pressure(int16_t dp_raw) const
     122             : {
     123           0 :     const float P_max = get_psi_range();
     124           0 :     const float P_min = - P_max;
     125           0 :     const float PSI_to_Pa = 6894.757f;
     126             : 
     127           0 :     float diff_press_PSI  = -((dp_raw - 0.1f*16383) * (P_max-P_min)/(0.8f*16383) + P_min);
     128           0 :     float press  = diff_press_PSI * PSI_to_Pa;
     129           0 :     return press;
     130             : }
     131             : 
     132             : /*
     133             :   convert raw temperature to temperature in degrees C
     134             :  */
     135           0 : float AP_Airspeed_MS4525::_get_temperature(int16_t dT_raw) const
     136             : {
     137           0 :     float temp  = ((200.0f * dT_raw) / 2047) - 50;
     138           0 :     return temp;
     139             : }
     140             : 
     141             : // read the values from the sensor
     142           0 : void AP_Airspeed_MS4525::_collect()
     143             : {
     144             :     uint8_t data[4];
     145             :     uint8_t data2[4];
     146             : 
     147           0 :     _measurement_started_ms = 0;
     148             : 
     149           0 :     if (!_dev->transfer(nullptr, 0, data, sizeof(data))) {
     150           0 :         return;
     151             :     }
     152             :     // reread the data, so we can attempt to detect bad inputs
     153           0 :     if (!_dev->transfer(nullptr, 0, data2, sizeof(data2))) {
     154           0 :         return;
     155             :     }
     156             : 
     157           0 :     uint8_t status = (data[0] & 0xC0) >> 6;
     158             :     // only check the status on the first read, the second read is expected to be stale
     159           0 :     if (status == 2 || status == 3) {
     160           0 :         return;
     161             :     }
     162             : 
     163             :     int16_t dp_raw, dT_raw;
     164           0 :     dp_raw = (data[0] << 8) + data[1];
     165           0 :     dp_raw = 0x3FFF & dp_raw;
     166           0 :     dT_raw = (data[2] << 8) + data[3];
     167           0 :     dT_raw = (0xFFE0 & dT_raw) >> 5;
     168             : 
     169             :     int16_t dp_raw2, dT_raw2;
     170           0 :     dp_raw2 = (data2[0] << 8) + data2[1];
     171           0 :     dp_raw2 = 0x3FFF & dp_raw2;
     172           0 :     dT_raw2 = (data2[2] << 8) + data2[3];
     173           0 :     dT_raw2 = (0xFFE0 & dT_raw2) >> 5;
     174             : 
     175             :     // reject any values that are the absolute minimum or maximums these
     176             :     // can happen due to gnd lifts or communication errors on the bus
     177           0 :     if (dp_raw  == 0x3FFF || dp_raw  == 0 || dT_raw  == 0x7FF || dT_raw == 0 ||
     178           0 :         dp_raw2 == 0x3FFF || dp_raw2 == 0 || dT_raw2 == 0x7FF || dT_raw2 == 0) {
     179           0 :         return;
     180             :     }
     181             : 
     182             :     // reject any double reads where the value has shifted in the upper more than
     183             :     // 0xFF
     184           0 :     if (abs(dp_raw - dp_raw2) > 0xFF || abs(dT_raw - dT_raw2) > 0xFF) {
     185           0 :         return;
     186             :     }
     187             : 
     188           0 :     float press  = _get_pressure(dp_raw);
     189           0 :     float press2 = _get_pressure(dp_raw2);
     190           0 :     float temp  = _get_temperature(dT_raw);
     191           0 :     float temp2 = _get_temperature(dT_raw2);
     192             :     
     193           0 :     if (!disable_voltage_correction()) {
     194           0 :         _voltage_correction(press, temp);
     195           0 :         _voltage_correction(press2, temp2);
     196             :     }
     197             : 
     198           0 :     WITH_SEMAPHORE(sem);
     199             : 
     200           0 :     _press_sum += press + press2;
     201           0 :     _temp_sum += temp + temp2;
     202           0 :     _press_count += 2;
     203           0 :     _temp_count += 2;
     204             : 
     205           0 :     _last_sample_time_ms = AP_HAL::millis();
     206           0 : }
     207             : 
     208             : /**
     209             :    correct for 5V rail voltage if the system_power ORB topic is
     210             :    available
     211             : 
     212             :    See http://uav.tridgell.net/MS4525/MS4525-offset.png for a graph of
     213             :    offset versus voltage for 3 sensors
     214             :  */
     215           0 : void AP_Airspeed_MS4525::_voltage_correction(float &diff_press_pa, float &temperature)
     216             : {
     217           0 :         const float slope = 65.0f;
     218           0 :         const float temp_slope = 0.887f;
     219             : 
     220             :         /*
     221             :           apply a piecewise linear correction within range given by above graph
     222             :          */
     223           0 :         float voltage_diff = hal.analogin->board_voltage() - 5.0f;
     224             : 
     225           0 :     voltage_diff = constrain_float(voltage_diff, -0.7f, 0.5f);
     226             : 
     227           0 :         diff_press_pa -= voltage_diff * slope;
     228           0 :         temperature -= voltage_diff * temp_slope;
     229           0 : }
     230             : 
     231             : // 50Hz timer
     232           0 : void AP_Airspeed_MS4525::_timer()
     233             : {
     234           0 :     if (_measurement_started_ms == 0) {
     235           0 :         _measure();
     236           0 :         return;
     237             :     }
     238           0 :     if ((AP_HAL::millis() - _measurement_started_ms) > 10) {
     239           0 :         _collect();
     240             :         // start a new measurement
     241           0 :         _measure();
     242             :     }
     243             : }
     244             : 
     245             : // return the current differential_pressure in Pascal
     246           0 : bool AP_Airspeed_MS4525::get_differential_pressure(float &pressure)
     247             : {
     248           0 :     WITH_SEMAPHORE(sem);
     249             : 
     250           0 :     if ((AP_HAL::millis() - _last_sample_time_ms) > 100) {
     251           0 :         return false;
     252             :     }
     253             : 
     254           0 :     if (_press_count > 0) {
     255           0 :         _pressure = _press_sum / _press_count;
     256           0 :         _press_count = 0;
     257           0 :         _press_sum = 0;
     258             :     }
     259             : 
     260           0 :     pressure = _pressure;
     261           0 :     return true;
     262           0 : }
     263             : 
     264             : // return the current temperature in degrees C, if available
     265           0 : bool AP_Airspeed_MS4525::get_temperature(float &temperature)
     266             : {
     267           0 :     WITH_SEMAPHORE(sem);
     268             : 
     269           0 :     if ((AP_HAL::millis() - _last_sample_time_ms) > 100) {
     270           0 :         return false;
     271             :     }
     272             : 
     273           0 :     if (_temp_count > 0) {
     274           0 :         _temperature = _temp_sum / _temp_count;
     275           0 :         _temp_count = 0;
     276           0 :         _temp_sum = 0;
     277             :     }
     278             : 
     279           0 :     temperature = _temperature;
     280           0 :     return true;
     281           0 : }
     282             : 
     283             : #endif  // AP_AIRSPEED_MS4525_ENABLED

Generated by: LCOV version 1.14