LCOV - code coverage report
Current view: top level - AP_Baro - AP_Baro_SPL06.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 140 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 8 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             : #include "AP_Baro_SPL06.h"
      16             : 
      17             : #if AP_BARO_SPL06_ENABLED
      18             : 
      19             : #include <strings.h>
      20             : #include <AP_Math/definitions.h>
      21             : 
      22             : extern const AP_HAL::HAL &hal;
      23             : 
      24             : #define SPL06_CHIP_ID                          0x10
      25             : #define SPA06_CHIP_ID                          0x11
      26             : 
      27             : #define SPL06_REG_PRESSURE_B2                  0x00    // Pressure MSB Register
      28             : #define SPL06_REG_PRESSURE_B1                  0x01    // Pressure middle byte Register
      29             : #define SPL06_REG_PRESSURE_B0                  0x02    // Pressure LSB Register
      30             : #define SPL06_REG_PRESSURE_START               SPL06_REG_PRESSURE_B2
      31             : #define SPL06_PRESSURE_LEN                     3       // 24 bits, 3 bytes
      32             : #define SPL06_REG_TEMPERATURE_B2               0x03    // Temperature MSB Register
      33             : #define SPL06_REG_TEMPERATURE_B1               0x04    // Temperature middle byte Register
      34             : #define SPL06_REG_TEMPERATURE_B0               0x05    // Temperature LSB Register
      35             : #define SPL06_REG_TEMPERATURE_START            SPL06_REG_TEMPERATURE_B2
      36             : #define SPL06_TEMPERATURE_LEN                  3       // 24 bits, 3 bytes
      37             : #define SPL06_REG_PRESSURE_CFG                 0x06    // Pressure config
      38             : #define SPL06_REG_TEMPERATURE_CFG              0x07    // Temperature config
      39             : #define SPL06_REG_MODE_AND_STATUS              0x08    // Mode and status
      40             : #define SPL06_REG_INT_AND_FIFO_CFG             0x09    // Interrupt and FIFO config
      41             : #define SPL06_REG_INT_STATUS                   0x0A    // Interrupt and FIFO config
      42             : #define SPL06_REG_FIFO_STATUS                  0x0B    // Interrupt and FIFO config
      43             : #define SPL06_REG_RST                          0x0C    // Softreset Register
      44             : #define SPL06_REG_CHIP_ID                      0x0D    // Chip ID Register
      45             : #define SPL06_REG_CALIB_COEFFS_START           0x10
      46             : #define SPL06_REG_CALIB_COEFFS_END             0x21
      47             : #define SPA06_REG_CALIB_COEFFS_END             0x24
      48             : 
      49             : // number of calibration coefficient bytes for each device
      50             : #define SPL06_NUM_CALIB_COEFFS                 (SPL06_REG_CALIB_COEFFS_END - SPL06_REG_CALIB_COEFFS_START + 1)
      51             : #define SPA06_NUM_CALIB_COEFFS                 (SPA06_REG_CALIB_COEFFS_END - SPL06_REG_CALIB_COEFFS_START + 1)
      52             : 
      53             : // MAX_NUM_CALIB_COEFFS is the largest of the per-device counts:
      54             : #define MAX_NUM_CALIB_COEFFS SPA06_NUM_CALIB_COEFFS
      55             : 
      56             : // PRESSURE_CFG_REG
      57             : #define SPL06_PRES_RATE_32HZ                               (0x05 << 4)
      58             : 
      59             : // TEMPERATURE_CFG_REG
      60             : #define SPL06_TEMP_USE_EXT_SENSOR              (1<<7)
      61             : #define SPL06_TEMP_RATE_32HZ                               (0x05 << 4)
      62             : 
      63             : // MODE_AND_STATUS_REG
      64             : #define SPL06_MEAS_PRESSURE                    (1<<0)  // measure pressure
      65             : #define SPL06_MEAS_TEMPERATURE                 (1<<1)  // measure temperature
      66             : #define SPL06_MEAS_CON_PRE_TEM                             0x07
      67             : 
      68             : #define SPL06_MEAS_CFG_CONTINUOUS              (1<<2)
      69             : #define SPL06_MEAS_CFG_PRESSURE_RDY            (1<<4)
      70             : #define SPL06_MEAS_CFG_TEMPERATURE_RDY         (1<<5)
      71             : #define SPL06_MEAS_CFG_SENSOR_RDY              (1<<6)
      72             : #define SPL06_MEAS_CFG_COEFFS_RDY              (1<<7)
      73             : 
      74             : // INT_AND_FIFO_CFG_REG
      75             : #define SPL06_PRESSURE_RESULT_BIT_SHIFT        (1<<2)  // necessary for pressure oversampling > 8
      76             : #define SPL06_TEMPERATURE_RESULT_BIT_SHIFT     (1<<3)  // necessary for temperature oversampling > 8
      77             : 
      78             : // Don't set oversampling higher than 8 or the measurement time will be higher than 20ms (timer period)
      79             : #define SPL06_PRESSURE_OVERSAMPLING            8
      80             : #define SPL06_TEMPERATURE_OVERSAMPLING         8
      81             : 
      82             : #define SPL06_OVERSAMPLING_TO_REG_VALUE(n)     (ffs(n)-1)
      83             : 
      84             : #define SPL06_BACKGROUND_SAMPLE_RATE    32
      85             : 
      86             : // enable Background Mode for continuous measurement
      87             : #ifndef AP_BARO_SPL06_BACKGROUND_ENABLE
      88             : #define AP_BARO_SPL06_BACKGROUND_ENABLE 1
      89             : #endif
      90             : 
      91           0 : AP_Baro_SPL06::AP_Baro_SPL06(AP_Baro &baro, AP_HAL::Device &dev)
      92             :     : AP_Baro_Backend(baro)
      93           0 :     , _dev(&dev)
      94             : {
      95           0 : }
      96             : 
      97           0 : AP_Baro_Backend *AP_Baro_SPL06::probe(AP_Baro &baro, AP_HAL::Device &dev)
      98             : {
      99           0 :     if (dev.bus_type() == AP_HAL::Device::BUS_TYPE_SPI) {
     100           0 :         dev.set_read_flag(0x80);
     101             :     }
     102             : 
     103           0 :     AP_Baro_SPL06 *sensor = NEW_NOTHROW AP_Baro_SPL06(baro, dev);
     104           0 :     if (!sensor || !sensor->_init()) {
     105           0 :         delete sensor;
     106           0 :         return nullptr;
     107             :     }
     108           0 :     return sensor;
     109             : }
     110             : 
     111           0 : bool AP_Baro_SPL06::_init()
     112             : {
     113           0 :     if (!_dev) {
     114           0 :         return false;
     115             :     }
     116           0 :     WITH_SEMAPHORE(_dev->get_semaphore());
     117             : 
     118           0 :     _dev->set_speed(AP_HAL::Device::SPEED_HIGH);
     119             : 
     120             :     uint8_t whoami;
     121             : 
     122             : // Sometimes SPL06 has init problems, that's due to failure of reading using SPI for the first time. The SPL06 is a dual
     123             : // protocol sensor(I2C and SPI), sometimes it takes one SPI operation to convert it to SPI mode after it starts up.
     124             : 
     125           0 :     for (uint8_t i=0; i<5; i++) {
     126           0 :         if (_dev->read_registers(SPL06_REG_CHIP_ID, &whoami, 1)) {
     127           0 :                 switch(whoami) {
     128           0 :                         case SPL06_CHIP_ID:
     129           0 :                                 type = Type::SPL06;
     130           0 :                                 break;
     131           0 :                         case SPA06_CHIP_ID:
     132           0 :                                 type = Type::SPA06;
     133           0 :                                 break;
     134           0 :                         default:
     135           0 :                                 type = Type::UNKNOWN;
     136           0 :                                 break;
     137             :                         }
     138             :         }
     139             : 
     140           0 :         if (type != Type::UNKNOWN)
     141           0 :                         break;
     142             :     }
     143             :     
     144           0 :     if (type == Type::UNKNOWN) {
     145           0 :         return false;
     146             :     }
     147             : 
     148             :     // number of calibration coefficient bytes to read for this device
     149           0 :     uint8_t calib_coeffs_len = SPL06_NUM_CALIB_COEFFS;
     150           0 :         switch(type) {
     151           0 :         case Type::SPL06:
     152           0 :                 calib_coeffs_len = SPL06_NUM_CALIB_COEFFS;
     153           0 :                 break;
     154           0 :         case Type::SPA06:
     155           0 :                 calib_coeffs_len = SPA06_NUM_CALIB_COEFFS;
     156           0 :                 break;
     157           0 :         default:
     158           0 :                 break;
     159             :         }
     160             : 
     161           0 :     bool ready = false;
     162           0 :     for (uint8_t i=0; i<5; i++) {
     163           0 :         uint8_t status = 0;
     164           0 :         if (_dev->read_registers(SPL06_REG_MODE_AND_STATUS, &status, 1)) {
     165           0 :             if ((status & 1<<7U) && (status & 1<<6U)) {
     166           0 :                 ready = true;
     167           0 :                 break;
     168             :             }
     169             :         }
     170           0 :         hal.scheduler->delay_microseconds(100);
     171             :     }
     172             : 
     173           0 :     if (!ready) {
     174           0 :         return false;
     175             :     }
     176             : 
     177             :     // fixed-size buffer holding the largest device's coefficients; a
     178             :     // compile-time constant rather than a VLA sized by calib_coeffs_len
     179             :     uint8_t buf[MAX_NUM_CALIB_COEFFS];
     180             : 
     181             :     // fail safe if a device's length exceeds the buffer, e.g. a new device
     182             :     // type was added without growing MAX_NUM_CALIB_COEFFS above
     183           0 :     if (calib_coeffs_len > sizeof(buf)) {
     184           0 :         return false;
     185             :     }
     186             : 
     187             : #define READ_LENGTH 9
     188             : 
     189             :     // only read this device's coefficients (calib_coeffs_len <= buffer size)
     190           0 :     for (uint8_t i = 0; i < calib_coeffs_len; ) {
     191           0 :         ssize_t chunk = MIN(READ_LENGTH, calib_coeffs_len - i);
     192           0 :         if (!_dev->read_registers(SPL06_REG_CALIB_COEFFS_START + i, buf + i, chunk)) {
     193           0 :             return false;
     194             :         }
     195           0 :         i += chunk;
     196             :     }
     197             : 
     198             :     // 0x11 c0 [3:0] + 0x10 c0 [11:4]
     199           0 :     _c0 = get_twos_complement(((uint32_t)buf[0] << 4) | (((uint32_t)buf[1] >> 4) & 0x0F), 12);
     200             :     // 0x11 c1 [11:8] + 0x12 c1 [7:0]
     201           0 :     _c1 = get_twos_complement((((uint32_t)buf[1] & 0x0F) << 8) | (uint32_t)buf[2], 12);
     202             :     // 0x13 c00 [19:12] + 0x14 c00 [11:4] + 0x15 c00 [3:0]
     203           0 :     _c00 = get_twos_complement(((uint32_t)buf[3] << 12) | ((uint32_t)buf[4] << 4) | (((uint32_t)buf[5] >> 4) & 0x0F), 20);
     204             :     // 0x15 c10 [19:16] + 0x16 c10 [15:8] + 0x17 c10 [7:0]
     205           0 :     _c10 = get_twos_complement((((uint32_t)buf[5] & 0x0F) << 16) | ((uint32_t)buf[6] << 8) | (uint32_t)buf[7], 20);
     206             :     // 0x18 c01 [15:8] + 0x19 c01 [7:0]
     207           0 :     _c01 = get_twos_complement(((uint32_t)buf[8] << 8) | (uint32_t)buf[9], 16);
     208             :     // 0x1A c11 [15:8] + 0x1B c11 [7:0]
     209           0 :     _c11 = get_twos_complement(((uint32_t)buf[10] << 8) | (uint32_t)buf[11], 16);
     210             :     // 0x1C c20 [15:8] + 0x1D c20 [7:0]
     211           0 :     _c20 = get_twos_complement(((uint32_t)buf[12] << 8) | (uint32_t)buf[13], 16);
     212             :     // 0x1E c21 [15:8] + 0x1F c21 [7:0]
     213           0 :     _c21 = get_twos_complement(((uint32_t)buf[14] << 8) | (uint32_t)buf[15], 16);
     214             :     // 0x20 c30 [15:8] + 0x21 c30 [7:0]
     215           0 :     _c30 = get_twos_complement(((uint32_t)buf[16] << 8) | (uint32_t)buf[17], 16);
     216             : 
     217           0 :     if(type == Type::SPA06) {
     218             :         // 0x23 c31 [3:0] + 0x22 c31 [11:4]
     219           0 :         _c31 = get_twos_complement(((uint32_t)buf[18] << 4) | (((uint32_t)buf[19] >> 4) & 0x0F), 12);
     220             :         // 0x23 c40 [11:8] + 0x24 c40 [7:0]
     221           0 :         _c40 = get_twos_complement((((uint32_t)buf[19] & 0x0F) << 8) | (uint32_t)buf[20], 12);
     222             :         }
     223             : 
     224           0 :     const uint8_t tmp_sensor = (type == Type::SPA06 ? 0 : SPL06_TEMP_USE_EXT_SENSOR);
     225             : #if AP_BARO_SPL06_BACKGROUND_ENABLE
     226             :     // setup temperature and pressure measurements
     227           0 :     _dev->setup_checked_registers(4, 20);
     228             : 
     229             :     //set rate and oversampling
     230           0 :         _dev->write_register(SPL06_REG_TEMPERATURE_CFG, tmp_sensor | SPL06_TEMP_RATE_32HZ | SPL06_OVERSAMPLING_TO_REG_VALUE(SPL06_TEMPERATURE_OVERSAMPLING), true);
     231           0 :         _dev->write_register(SPL06_REG_PRESSURE_CFG, SPL06_PRES_RATE_32HZ | SPL06_OVERSAMPLING_TO_REG_VALUE(SPL06_PRESSURE_OVERSAMPLING), true);
     232             : 
     233             :         //enable background mode
     234           0 :         _dev->write_register(SPL06_REG_MODE_AND_STATUS, SPL06_MEAS_CON_PRE_TEM, true);
     235             : #else
     236             :     // setup temperature and pressure measurements
     237             :     _dev->setup_checked_registers(3, 20);
     238             : 
     239             :     _dev->write_register(SPL06_REG_TEMPERATURE_CFG, tmp_sensor | SPL06_OVERSAMPLING_TO_REG_VALUE(SPL06_TEMPERATURE_OVERSAMPLING), true);
     240             :     _dev->write_register(SPL06_REG_PRESSURE_CFG, SPL06_OVERSAMPLING_TO_REG_VALUE(SPL06_PRESSURE_OVERSAMPLING), true);
     241             : #endif //AP_BARO_SPL06_BACKGROUND_ENABLE
     242             : 
     243           0 :     uint8_t int_and_fifo_reg_value = 0;
     244             :     if (SPL06_TEMPERATURE_OVERSAMPLING > 8) {
     245             :         int_and_fifo_reg_value |= SPL06_TEMPERATURE_RESULT_BIT_SHIFT;
     246             :     }
     247             :     if (SPL06_PRESSURE_OVERSAMPLING > 8) {
     248             :         int_and_fifo_reg_value |= SPL06_PRESSURE_RESULT_BIT_SHIFT;
     249             :     }
     250           0 :     _dev->write_register(SPL06_REG_INT_AND_FIFO_CFG, int_and_fifo_reg_value, true);
     251             : 
     252           0 :     _instance = _frontend.register_sensor();
     253             : 
     254           0 :     if(type == Type::SPA06) {
     255           0 :             _dev->set_device_type(DEVTYPE_BARO_SPA06);
     256             :     } else {
     257           0 :             _dev->set_device_type(DEVTYPE_BARO_SPL06);
     258             :     }
     259             : 
     260           0 :     set_bus_id(_instance, _dev->get_bus_id());
     261             :     
     262             :     // request 50Hz update
     263           0 :     _timer_counter = -1;
     264             : #if AP_BARO_SPL06_BACKGROUND_ENABLE
     265           0 :     _dev->register_periodic_callback(1000000/SPL06_BACKGROUND_SAMPLE_RATE, FUNCTOR_BIND_MEMBER(&AP_Baro_SPL06::_timer, void));
     266             : #else
     267             :     _dev->register_periodic_callback(20 * AP_USEC_PER_MSEC, FUNCTOR_BIND_MEMBER(&AP_Baro_SPL06::_timer, void));
     268             : #endif //AP_BARO_SPL06_BACKGROUND_ENABLE
     269             : 
     270           0 :     return true;
     271           0 : }
     272             : 
     273           0 : int32_t AP_Baro_SPL06::raw_value_scale_factor(uint8_t oversampling)
     274             : {
     275             :     // From the datasheet page 13
     276           0 :     switch(oversampling)
     277             :     {
     278           0 :         case 1: return 524288;
     279           0 :         case 2: return 1572864;
     280           0 :         case 4: return 3670016;
     281           0 :         case 8: return 7864320;
     282           0 :         case 16: return 253952;
     283           0 :         case 32: return 516096;
     284           0 :         case 64: return 1040384;
     285           0 :         case 128: return 2088960;
     286           0 :         default: return -1; // invalid
     287             :     }
     288             : }
     289             : 
     290             : // accumulate a new sensor reading
     291           0 : void AP_Baro_SPL06::_timer(void)
     292             : {
     293             :     uint8_t buf[3];
     294             : 
     295             : #if AP_BARO_SPL06_BACKGROUND_ENABLE
     296           0 :     _dev->read_registers(SPL06_REG_TEMPERATURE_START, buf, sizeof(buf));
     297           0 :         _update_temperature((int32_t)((buf[0] & 0x80 ? 0xFF000000 : 0) | ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2]));
     298             : 
     299           0 :         _dev->read_registers(SPL06_REG_PRESSURE_START, buf, sizeof(buf));
     300           0 :         _update_pressure((int32_t)((buf[0] & 0x80 ? 0xFF000000 : 0) | ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2]));
     301             : #else
     302             :     //command mode
     303             :         if ((_timer_counter == -1) || (_timer_counter == 49)) {
     304             :         // First call and every second start a temperature measurement (50Hz call)
     305             :         _dev->write_register(SPL06_REG_MODE_AND_STATUS, SPL06_MEAS_TEMPERATURE, false);
     306             :         _timer_counter = 0; // Next cycle we are reading the temperature
     307             :     } else if (_timer_counter == 0) {
     308             :         // A temperature measurement had been started during the previous call
     309             :         _dev->read_registers(SPL06_REG_TEMPERATURE_START, buf, sizeof(buf));
     310             :         _update_temperature((int32_t)((buf[0] & 0x80 ? 0xFF000000 : 0) | ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2]));
     311             :         _dev->write_register(SPL06_REG_MODE_AND_STATUS, SPL06_MEAS_PRESSURE, false);
     312             :         _timer_counter += 1;
     313             :     } else {
     314             :         // The rest of the time read the latest pressure and start a new measurement
     315             :         _dev->read_registers(SPL06_REG_PRESSURE_START, buf, sizeof(buf));
     316             :         _update_pressure((int32_t)((buf[0] & 0x80 ? 0xFF000000 : 0) | ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2]));
     317             :         _dev->write_register(SPL06_REG_MODE_AND_STATUS, SPL06_MEAS_PRESSURE, false);
     318             :         _timer_counter += 1;
     319             :     }
     320             : #endif //AP_BARO_SPL06_BACKGROUND_ENABLE
     321             : 
     322           0 :     _dev->check_next_register();
     323           0 : }
     324             : 
     325             : // transfer data to the frontend
     326           0 : void AP_Baro_SPL06::update(void)
     327             : {
     328           0 :     WITH_SEMAPHORE(_sem);
     329             : 
     330           0 :     if (_pressure_count == 0) {
     331           0 :         return;
     332             :     }
     333             : 
     334           0 :     _copy_to_frontend(_instance, _pressure_sum/_pressure_count, _temperature);
     335             : 
     336           0 :     _pressure_sum = 0;
     337           0 :     _pressure_count = 0;
     338           0 : }
     339             : 
     340             : // calculate temperature
     341           0 : void AP_Baro_SPL06::_update_temperature(int32_t temp_raw)
     342             : {
     343           0 :     _temp_raw = (float)temp_raw / raw_value_scale_factor(SPL06_TEMPERATURE_OVERSAMPLING);
     344           0 :     const float temp_comp = (float)_c0 / 2 + _temp_raw * _c1;
     345             : 
     346           0 :     WITH_SEMAPHORE(_sem);
     347             : 
     348           0 :     _temperature = temp_comp;
     349           0 : }
     350             : 
     351             : // calculate pressure
     352           0 : void AP_Baro_SPL06::_update_pressure(int32_t press_raw)
     353             : {
     354           0 :     const float press_raw_sc = (float)press_raw / raw_value_scale_factor(SPL06_PRESSURE_OVERSAMPLING);
     355           0 :     float pressure_cal = 0;
     356           0 :     float press_temp_comp = 0;
     357             : 
     358           0 :     switch(type) {
     359           0 :         case Type::SPL06:
     360           0 :                 pressure_cal = (float)_c00 + press_raw_sc * ((float)_c10 + press_raw_sc * ((float)_c20 + press_raw_sc * _c30));
     361           0 :                 press_temp_comp = _temp_raw * ((float)_c01 + press_raw_sc * ((float)_c11 + press_raw_sc * _c21));
     362           0 :                 break;
     363           0 :         case Type::SPA06:
     364           0 :                 pressure_cal = (float)_c00 + press_raw_sc * ((float)_c10 + press_raw_sc * ((float)_c20 + press_raw_sc * ((float)_c30 + press_raw_sc * _c40)));
     365           0 :                 press_temp_comp = _temp_raw * ((float)_c01 + press_raw_sc * ((float)_c11 + press_raw_sc * ((float)_c21) + press_raw_sc * _c31));
     366           0 :                 break;
     367           0 :         default:
     368           0 :                 break;
     369             :         }
     370             : 
     371           0 :     const float press_comp = pressure_cal + press_temp_comp;
     372             : 
     373           0 :     if (!pressure_ok(press_comp)) {
     374           0 :         return;
     375             :     }
     376             : 
     377           0 :     WITH_SEMAPHORE(_sem);
     378             : 
     379           0 :     _pressure_sum += press_comp;
     380           0 :     _pressure_count++;
     381             : }
     382             : 
     383             : #endif  // AP_BARO_SPL06_ENABLED

Generated by: LCOV version 1.14