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

          Line data    Source code
       1             : /*
       2             :  * This file is free software: you can redistribute it and/or modify it
       3             :  * under the terms of the GNU General Public License as published by the
       4             :  * Free Software Foundation, either version 3 of the License, or
       5             :  * (at your option) any later version.
       6             :  *
       7             :  * This file is distributed in the hope that it will be useful, but
       8             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      10             :  * See the GNU General Public License for more details.
      11             :  *
      12             :  * You should have received a copy of the GNU General Public License along
      13             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : #include "AP_Compass_BMM350.h"
      16             : 
      17             : #if AP_COMPASS_BMM350_ENABLED
      18             : 
      19             : #include <AP_HAL/AP_HAL.h>
      20             : 
      21             : #include <utility>
      22             : 
      23             : #include <AP_HAL/utility/sparse-endian.h>
      24             : #include <AP_Math/AP_Math.h>
      25             : 
      26             : #define BMM350_REG_CHIP_ID              0x00
      27             : #define BMM350_REG_PMU_CMD_AGGR_SET     0x04
      28             : #define BMM350_REG_PMU_CMD_AXIS_EN      0x05
      29             : #define BMM350_REG_PMU_CMD              0x06
      30             : #define BMM350_REG_PMU_CMD_STATUS_0     0x07
      31             : #define BMM350_REG_INT_CTRL             0x2E
      32             : #define BMM350_REG_MAG_X_XLSB           0x31
      33             : #define BMM350_REG_OTP_CMD              0x50
      34             : #define BMM350_REG_OTP_DATA_MSB         0x52
      35             : #define BMM350_REG_OTP_DATA_LSB         0x53
      36             : #define BMM350_REG_OTP_STATUS           0x55
      37             : #define BMM350_REG_CMD                  0x7E
      38             : 
      39             : // OTP(one-time programmable memory)
      40             : #define BMM350_OTP_CMD_DIR_READ         (0x01<<5U)
      41             : #define BMM350_OTP_CMD_PWR_OFF_OTP      (0x04<<5U)
      42             : 
      43             : #define BMM350_OTP_STATUS_ERROR_MASK    0xE0
      44             : #define BMM350_OTP_STATUS_CMD_DONE      0x01
      45             : 
      46             : #define BMM350_TEMP_OFF_SENS            0x0D
      47             : #define BMM350_MAG_OFFSET_X             0x0E
      48             : #define BMM350_MAG_OFFSET_Y             0x0F
      49             : #define BMM350_MAG_OFFSET_Z             0x10
      50             : #define BMM350_MAG_SENS_X               0x10
      51             : #define BMM350_MAG_SENS_Y               0x11
      52             : #define BMM350_MAG_SENS_Z               0x11
      53             : #define BMM350_MAG_TCO_X                0x12
      54             : #define BMM350_MAG_TCO_Y                0x13
      55             : #define BMM350_MAG_TCO_Z                0x14
      56             : #define BMM350_MAG_TCS_X                0x12
      57             : #define BMM350_MAG_TCS_Y                0x13
      58             : #define BMM350_MAG_TCS_Z                0x14
      59             : #define BMM350_MAG_DUT_T_0              0x18
      60             : #define BMM350_CROSS_X_Y                0x15
      61             : #define BMM350_CROSS_Y_X                0x15
      62             : #define BMM350_CROSS_Z_X                0x16
      63             : #define BMM350_CROSS_Z_Y                0x16
      64             : #define BMM350_SENS_CORR_Y              0.01f
      65             : #define BMM350_TCS_CORR_Z               0.0001f
      66             : 
      67             : #define BMM350_CMD_SOFTRESET            0xB6
      68             : #define BMM350_INT_MODE_PULSED          (0<<0U)
      69             : #define BMM350_INT_POL_ACTIVE_HIGH      (1<<1U)
      70             : #define BMM350_INT_OD_PUSHPULL          (1<<2U)
      71             : #define BMM350_INT_OUTPUT_DISABLE       (0<<3U)
      72             : #define BMM350_INT_DRDY_EN              (1<<7U)
      73             : 
      74             : // Averaging performance
      75             : #define BMM350_AVERAGING_4              (0x02 << 4U)
      76             : #define BMM350_AVERAGING_8              (0x03 << 4U)
      77             : 
      78             : // Output data rate
      79             : #define BMM350_ODR_100HZ                0x04
      80             : #define BMM350_ODR_50HZ                 0x05
      81             : 
      82             : // Power modes
      83             : #define BMM350_PMU_CMD_SUSPEND_MODE     0x00
      84             : #define BMM350_PMU_CMD_NORMAL_MODE      0x01
      85             : #define BMM350_PMU_CMD_UPD_OAE          0x02
      86             : #define BMM350_PMU_CMD_FGR              0x05
      87             : #define BMM350_PMU_CMD_BR               0x07
      88             : 
      89             : // OTP data length
      90             : #define BMM350_OTP_DATA_LENGTH          32U
      91             : 
      92             : // Chip ID of BMM350
      93             : #define BMM350_CHIP_ID                  0x33
      94             : 
      95             : #define BMM350_XY_SENSITIVE             14.55f
      96             : #define BMM350_Z_SENSITIVE              9.0f
      97             : #define BMM350_TEMP_SENSITIVE           0.00204f
      98             : #define BMM350_XY_INA_GAIN              19.46f
      99             : #define BMM350_Z_INA_GAIN               31.0f
     100             : #define BMM350_ADC_GAIN                 (1.0f / 1.5f)
     101             : #define BMM350_LUT_GAIN                 0.714607238769531f
     102             : #define BMM350_POWER                    ((float)(1000000.0 / 1048576.0))
     103             : 
     104             : #define BMM350_XY_SCALE                 (BMM350_POWER / (BMM350_XY_SENSITIVE * BMM350_XY_INA_GAIN * BMM350_ADC_GAIN * BMM350_LUT_GAIN))
     105             : #define BMM350_Z_SCALE                  (BMM350_POWER / (BMM350_Z_SENSITIVE * BMM350_Z_INA_GAIN * BMM350_ADC_GAIN * BMM350_LUT_GAIN))
     106             : #define BMM350_TEMP_SCALE               (1.0f / (BMM350_TEMP_SENSITIVE * BMM350_ADC_GAIN * BMM350_LUT_GAIN * 1048576))
     107             : 
     108             : extern const AP_HAL::HAL &hal;
     109             : 
     110           0 : AP_Compass_Backend *AP_Compass_BMM350::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev,
     111             :                                               bool force_external,
     112             :                                               enum Rotation rotation)
     113             : {
     114           0 :     if (!dev) {
     115           0 :         return nullptr;
     116             :     }
     117           0 :     AP_Compass_BMM350 *sensor = NEW_NOTHROW AP_Compass_BMM350(std::move(dev), force_external, rotation);
     118           0 :     if (!sensor || !sensor->init()) {
     119           0 :         delete sensor;
     120           0 :         return nullptr;
     121             :     }
     122             : 
     123           0 :     return sensor;
     124             : }
     125             : 
     126           0 : AP_Compass_BMM350::AP_Compass_BMM350(AP_HAL::OwnPtr<AP_HAL::Device> dev,
     127             :                                        bool force_external,
     128           0 :                                        enum Rotation rotation)
     129           0 :     : _dev(std::move(dev))
     130           0 :     , _force_external(force_external)
     131           0 :     , _rotation(rotation)
     132             : {
     133           0 : }
     134             : 
     135             : /**
     136             :  * @brief Read out OTP(one-time programmable memory) data of sensor which is the compensation coefficients
     137             :  * @see https://github.com/boschsensortec/BMM350-SensorAPI
     138             :  */
     139           0 : bool AP_Compass_BMM350::read_otp_data()
     140             : {
     141             :     uint16_t otp_data[BMM350_OTP_DATA_LENGTH];
     142             : 
     143           0 :     for (uint8_t index = 0; index < BMM350_OTP_DATA_LENGTH; index++) {
     144             :         // Set OTP address
     145           0 :         if (!_dev->write_register(BMM350_REG_OTP_CMD, (BMM350_OTP_CMD_DIR_READ | index))) {
     146           0 :             return false;
     147             :         }
     148             : 
     149             :         // Wait for OTP status be ready
     150           0 :         int8_t tries = 3;    // Try polling 3 times
     151           0 :         while (tries--)
     152             :         {
     153             :             uint8_t status;
     154           0 :             hal.scheduler->delay_microseconds(300);
     155             :             // Read OTP status
     156           0 :             if (!read_bytes(BMM350_REG_OTP_STATUS, &status, 1) || ((status & BMM350_OTP_STATUS_ERROR_MASK) != 0)) {
     157           0 :                 return false;
     158             :             }
     159           0 :             if (status & BMM350_OTP_STATUS_CMD_DONE) {
     160           0 :                 break;
     161             :             }
     162             :         }
     163             : 
     164           0 :         if (tries == -1) {
     165           0 :             return false;
     166             :         }
     167             : 
     168             :         // Read OTP data
     169             :         be16_t reg_data;
     170           0 :         if (!read_bytes(BMM350_REG_OTP_DATA_MSB, (uint8_t *)&reg_data, 2)) {
     171           0 :             return false;
     172             :         }
     173             : 
     174           0 :         otp_data[index] = be16toh(reg_data);
     175             :     }
     176             : 
     177             :     // Update magnetometer offset and sensitivity data.
     178             :     // 12-bit unsigned integer to be left-aligned in a 16-bit integer
     179           0 :     _mag_comp.offset_coef.x = float(int16_t((otp_data[BMM350_MAG_OFFSET_X] & 0x0FFF) << 4) >> 4);
     180           0 :     _mag_comp.offset_coef.y = float(int16_t((((otp_data[BMM350_MAG_OFFSET_X] & 0xF000) >> 4) +
     181           0 :                                               (otp_data[BMM350_MAG_OFFSET_Y] & 0x00FF)) << 4) >> 4);
     182           0 :     _mag_comp.offset_coef.z = float(int16_t(((otp_data[BMM350_MAG_OFFSET_Y] & 0x0F00) +
     183           0 :                                              (otp_data[BMM350_MAG_OFFSET_Z] & 0x00FF)) << 4) >> 4);
     184           0 :     _mag_comp.offset_coef.temp = float(int8_t(otp_data[BMM350_TEMP_OFF_SENS])) * (1.0f / 5.0f);
     185             : 
     186           0 :     _mag_comp.sensit_coef.x = float(int8_t((otp_data[BMM350_MAG_SENS_X] & 0xFF00) >> 8)) * (1.0f / 256.0f);
     187           0 :     _mag_comp.sensit_coef.y = float(int8_t(otp_data[BMM350_MAG_SENS_Y])) * (1.0f / 256.0f) + BMM350_SENS_CORR_Y;
     188           0 :     _mag_comp.sensit_coef.z = float(int8_t((otp_data[BMM350_MAG_SENS_Z] & 0xFF00) >> 8)) * (1.0f / 256.0f);
     189           0 :     _mag_comp.sensit_coef.temp = float(int8_t((otp_data[BMM350_TEMP_OFF_SENS] & 0xFF00) >> 8)) * (1.0f / 512.0f);
     190             : 
     191           0 :     _mag_comp.tco.x = float(int8_t(otp_data[BMM350_MAG_TCO_X])) * (1.0f / 32.0f);
     192           0 :     _mag_comp.tco.y = float(int8_t(otp_data[BMM350_MAG_TCO_Y])) * (1.0f / 32.0f);
     193           0 :     _mag_comp.tco.z = float(int8_t(otp_data[BMM350_MAG_TCO_Z])) * (1.0f / 32.0f);
     194             : 
     195           0 :     _mag_comp.tcs.x = float(int8_t((otp_data[BMM350_MAG_TCS_X] & 0xFF00) >> 8)) * (1.0f / 16384.0f);
     196           0 :     _mag_comp.tcs.y = float(int8_t((otp_data[BMM350_MAG_TCS_Y] & 0xFF00) >> 8)) * (1.0f / 16384.0f);
     197           0 :     _mag_comp.tcs.z = float(int8_t((otp_data[BMM350_MAG_TCS_Z] & 0xFF00) >> 8)) * (1.0f / 16384.0f) - BMM350_TCS_CORR_Z;
     198             : 
     199           0 :     _mag_comp.t0_reading = float(int16_t(otp_data[BMM350_MAG_DUT_T_0])) * (1.0f / 512.0f) + 23.0f;
     200             : 
     201           0 :     _mag_comp.cross_axis.cross_x_y = float(int8_t(otp_data[BMM350_CROSS_X_Y])) * (1.0f / 800.0f);
     202           0 :     _mag_comp.cross_axis.cross_y_x = float(int8_t((otp_data[BMM350_CROSS_Y_X] & 0xFF00) >> 8)) * (1.0f / 800.0f);
     203           0 :     _mag_comp.cross_axis.cross_z_x = float(int8_t(otp_data[BMM350_CROSS_Z_X])) * (1.0f / 800.0f);
     204           0 :     _mag_comp.cross_axis.cross_z_y = float(int8_t((otp_data[BMM350_CROSS_Z_Y] & 0xFF00) >> 8)) * (1.0f / 800.0f);
     205             : 
     206           0 :     return true;
     207             : }
     208             : 
     209             : /**
     210             :  * @brief Wait PMU_CMD register operation completed. Need to specify which command just sent
     211             :  */
     212           0 : bool AP_Compass_BMM350::wait_pmu_cmd_ready(const uint8_t cmd, const uint32_t timeout)
     213             : {
     214           0 :     const uint32_t start_tick = AP_HAL::millis();
     215             : 
     216             :     do {
     217           0 :         hal.scheduler->delay(1);
     218             :         uint8_t status;
     219           0 :         if (!read_bytes(BMM350_REG_PMU_CMD_STATUS_0, &status, 1)) {
     220           0 :             return false;
     221             :         }
     222           0 :         if (((status & 0x01) == 0x00) && (((status & 0xE0) >> 5) == cmd)) {
     223           0 :             return true;
     224             :         }
     225           0 :     } while ((AP_HAL::millis() - start_tick) < timeout);
     226             : 
     227           0 :     return false;
     228             : }
     229             : 
     230             : /**
     231             :  * @brief Reset bit of magnetic register and wait for change to normal mode
     232             :  */
     233           0 : bool AP_Compass_BMM350::mag_reset_and_wait()
     234             : {
     235             :     uint8_t data;
     236             : 
     237             :     // Get PMU command status 0 data
     238           0 :     if (!read_bytes(BMM350_REG_PMU_CMD_STATUS_0, &data, 1)) {
     239           0 :         return false;
     240             :     }
     241             : 
     242             :     // Check whether the power mode is Normal
     243           0 :     if (data & 0x08) {
     244             :         // Set PMU command to suspend mode
     245           0 :         if (!_dev->write_register(BMM350_REG_PMU_CMD, BMM350_PMU_CMD_SUSPEND_MODE)) {
     246           0 :             return false;
     247             :         }
     248           0 :         wait_pmu_cmd_ready(BMM350_PMU_CMD_SUSPEND_MODE, 6);
     249             :     }
     250             : 
     251             :     // Set BR(bit reset) to PMU_CMD register
     252             :     // In offical example, it wait for 14ms. But it may not be enough, so we wait an extra 5ms
     253           0 :     if (!_dev->write_register(BMM350_REG_PMU_CMD, BMM350_PMU_CMD_BR) ||
     254           0 :         !wait_pmu_cmd_ready(BMM350_PMU_CMD_BR, 19)) {
     255           0 :         return false;
     256             :     }
     257             : 
     258             :     // Set FGR(flux-guide reset) to PMU_CMD register
     259             :     // 18ms got from offical example, we wait an extra 5ms
     260           0 :     if (!_dev->write_register(BMM350_REG_PMU_CMD, BMM350_PMU_CMD_FGR) ||
     261           0 :         !wait_pmu_cmd_ready(BMM350_PMU_CMD_FGR, 23)) {
     262           0 :         return false;
     263             :     }
     264             : 
     265             :     // Switch to normal mode
     266           0 :     if (!set_power_mode(POWER_MODE_NORMAL)) {
     267           0 :         return false;
     268             :     }
     269             : 
     270           0 :     return true;
     271             : }
     272             : 
     273             : /**
     274             :  * @brief Switch sensor power mode
     275             :  */
     276           0 : bool AP_Compass_BMM350::set_power_mode(const enum power_mode mode)
     277             : {
     278             :     uint8_t pmu_cmd;
     279             : 
     280             :     // Get PMU register data as current mode
     281           0 :     if (!read_bytes(BMM350_REG_PMU_CMD, &pmu_cmd, 1)) {
     282           0 :         return false;
     283             :     }
     284             : 
     285           0 :     if (pmu_cmd == BMM350_PMU_CMD_NORMAL_MODE || pmu_cmd == BMM350_PMU_CMD_UPD_OAE) {
     286             :         // Set PMU command to suspend mode
     287           0 :         if (!_dev->write_register(BMM350_REG_PMU_CMD, POWER_MODE_SUSPEND)) {
     288           0 :             return false;
     289             :         }
     290             :         // Wait for sensor switch to suspend mode
     291             :         // Wait for maximum 6ms, it got from the official example, not explained in datasheet
     292           0 :         wait_pmu_cmd_ready(POWER_MODE_SUSPEND, 6);
     293             :     }
     294             : 
     295             :     // Set PMU command to target mode
     296           0 :     if (!_dev->write_register(BMM350_REG_PMU_CMD, mode)) {
     297           0 :         return false;
     298             :     }
     299             : 
     300             :     // Wait for mode change. When switch from suspend mode to normal mode, we wait for at most 38ms.
     301             :     // It got from official example too
     302           0 :     wait_pmu_cmd_ready(mode, 38);
     303             : 
     304           0 :     return true;
     305             : }
     306             : 
     307             : /**
     308             :  * @brief Read bytes from sensor
     309             :  */
     310           0 : bool AP_Compass_BMM350::read_bytes(const uint8_t reg, uint8_t *out, const uint16_t read_len)
     311             : {
     312           0 :     uint8_t data[read_len + 2];
     313             : 
     314           0 :     if (!_dev->read_registers(reg, data, read_len + 2)) {
     315           0 :         return false;
     316             :     }
     317             : 
     318           0 :     memcpy(out, &data[2], read_len);
     319             : 
     320           0 :     return true;
     321           0 : }
     322             : 
     323           0 : bool AP_Compass_BMM350::init()
     324             : {
     325           0 :     _dev->get_semaphore()->take_blocking();
     326             : 
     327             :     // 10 retries for init
     328           0 :     _dev->set_retries(10);
     329             : 
     330             :     // Use checked registers to cope with bus errors
     331           0 :     _dev->setup_checked_registers(4);
     332             : 
     333           0 :     int8_t boot_retries = 5;
     334           0 :     while (boot_retries--) {
     335             :         // Soft reset
     336           0 :         if (!_dev->write_register(BMM350_REG_CMD, BMM350_CMD_SOFTRESET)) {
     337           0 :             continue;
     338             :         }
     339           0 :         hal.scheduler->delay(24);   // Wait 24ms for soft reset complete, it got from offical example
     340             : 
     341             :         // Read and verify chip ID
     342             :         uint8_t chip_id;
     343           0 :         if (!read_bytes(BMM350_REG_CHIP_ID, &chip_id, 1)) {
     344           0 :             continue;
     345             :         }
     346           0 :         if (chip_id == BMM350_CHIP_ID) {
     347           0 :             break;
     348             :         }
     349             :     }
     350             :     
     351           0 :     if (boot_retries == -1) {
     352           0 :         goto err;
     353             :     }
     354             : 
     355             :     // Read out OTP data
     356           0 :     if (!read_otp_data()) {
     357           0 :         goto err;
     358             :     }
     359             : 
     360             :     // Power off OTP
     361           0 :     if (!_dev->write_register(BMM350_REG_OTP_CMD, BMM350_OTP_CMD_PWR_OFF_OTP)) {
     362           0 :         goto err;
     363             :     }
     364             : 
     365             :     // Magnetic reset
     366           0 :     if (!mag_reset_and_wait()) {
     367           0 :         goto err;
     368             :     }
     369             : 
     370             :     // Configure interrupt settings and enable DRDY
     371             :     // Set INT mode as PULSED | active_high polarity | PUSH_PULL | unmap | DRDY interrupt
     372           0 :     if (!_dev->write_register(BMM350_REG_INT_CTRL, (BMM350_INT_MODE_PULSED |
     373             :                                                     BMM350_INT_POL_ACTIVE_HIGH |
     374             :                                                     BMM350_INT_OD_PUSHPULL |
     375             :                                                     BMM350_INT_OUTPUT_DISABLE |
     376             :                                                     BMM350_INT_DRDY_EN))) {
     377           0 :         goto err;
     378             :     }
     379             : 
     380             :     // Setup ODR and performance. 100Hz ODR and 4 average for lownoise
     381           0 :     if (!_dev->write_register(BMM350_REG_PMU_CMD_AGGR_SET, (BMM350_AVERAGING_4 | BMM350_ODR_100HZ))) {
     382           0 :         goto err;
     383             :     }
     384             : 
     385             :     // Update ODR and avg parameter
     386           0 :     if (!_dev->write_register(BMM350_REG_PMU_CMD, BMM350_PMU_CMD_UPD_OAE)) {
     387           0 :         goto err;
     388             :     }
     389             :     // Wait at most 20ms for update ODR and avg paramter
     390           0 :     wait_pmu_cmd_ready(BMM350_PMU_CMD_UPD_OAE, 20);
     391             : 
     392             :     // Enable measurement of 3 axis
     393           0 :     if (!_dev->write_register(BMM350_REG_PMU_CMD_AXIS_EN, 0x07)) {
     394           0 :         goto err;
     395             :     }
     396             : 
     397             :     // Switch power mode to normal mode
     398           0 :     if (!set_power_mode(POWER_MODE_NORMAL)) {
     399           0 :         goto err;
     400             :     }
     401             : 
     402             :     // Lower retries for run
     403           0 :     _dev->set_retries(3);
     404             : 
     405           0 :     _dev->get_semaphore()->give();
     406             : 
     407             :     /* Register the compass instance in the frontend */
     408           0 :     _dev->set_device_type(DEVTYPE_BMM350);
     409           0 :     if (!register_compass(_dev->get_bus_id())) {
     410           0 :         return false;
     411             :     }
     412             : 
     413             :     // printf("BMM350: Found at address 0x%x as compass %u\n", _dev->get_bus_address(), _compass_instance);
     414             : 
     415           0 :     set_rotation(_rotation);
     416             : 
     417           0 :     if (_force_external) {
     418           0 :         set_external(true);
     419             :     }
     420             :     
     421             :     // Call timer() at 100Hz
     422           0 :     _dev->register_periodic_callback(1000000U/100U, FUNCTOR_BIND_MEMBER(&AP_Compass_BMM350::timer, void));
     423             : 
     424           0 :     return true;
     425             : 
     426           0 : err:
     427           0 :     _dev->get_semaphore()->give();
     428           0 :     return false;
     429             : }
     430             : 
     431           0 : void AP_Compass_BMM350::timer()
     432             : {
     433             :     struct PACKED {
     434             :         uint8_t magx[3];
     435             :         uint8_t magy[3];
     436             :         uint8_t magz[3];
     437             :         uint8_t temp[3];
     438             :     } data;
     439             : 
     440             :     // Read out measurement data
     441           0 :     if (!read_bytes(BMM350_REG_MAG_X_XLSB, (uint8_t *)&data, sizeof(data))) {
     442           0 :         return;
     443             :     }
     444             : 
     445             :     // Converts 24-bit raw data to signed integer value
     446           0 :     const int32_t magx_raw = int32_t(((uint32_t)data.magx[0] << 8) + ((uint32_t)data.magx[1] << 16) + ((uint32_t)data.magx[2] << 24)) >> 8;
     447           0 :     const int32_t magy_raw = int32_t(((uint32_t)data.magy[0] << 8) + ((uint32_t)data.magy[1] << 16) + ((uint32_t)data.magy[2] << 24)) >> 8;
     448           0 :     const int32_t magz_raw = int32_t(((uint32_t)data.magz[0] << 8) + ((uint32_t)data.magz[1] << 16) + ((uint32_t)data.magz[2] << 24)) >> 8;
     449           0 :     const int32_t temp_raw = int32_t(((uint32_t)data.temp[0] << 8) + ((uint32_t)data.temp[1] << 16) + ((uint32_t)data.temp[2] << 24)) >> 8;
     450             :     
     451             :     // Convert mag lsb to uT and temp lsb to degC
     452           0 :     float magx = (float)magx_raw * BMM350_XY_SCALE;
     453           0 :     float magy = (float)magy_raw * BMM350_XY_SCALE;
     454           0 :     float magz = (float)magz_raw * BMM350_Z_SCALE;
     455           0 :     float temp = ((float)temp_raw * BMM350_TEMP_SCALE) - 25.49f;
     456             : 
     457             :     // Apply compensation
     458           0 :     temp = ((1 + _mag_comp.sensit_coef.temp) * temp) + _mag_comp.offset_coef.temp;
     459             :     // Compensate raw magnetic data
     460           0 :     magx = ((1 + _mag_comp.sensit_coef.x) * magx) + _mag_comp.offset_coef.x + (_mag_comp.tco.x * (temp - _mag_comp.t0_reading));
     461           0 :     magx /= 1 + _mag_comp.tcs.x * (temp - _mag_comp.t0_reading);
     462             : 
     463           0 :     magy = ((1 + _mag_comp.sensit_coef.y) * magy) + _mag_comp.offset_coef.y + (_mag_comp.tco.y * (temp - _mag_comp.t0_reading));
     464           0 :     magy /= 1 + _mag_comp.tcs.y * (temp - _mag_comp.t0_reading);
     465             : 
     466           0 :     magz = ((1 + _mag_comp.sensit_coef.z) * magz) + _mag_comp.offset_coef.z + (_mag_comp.tco.z * (temp - _mag_comp.t0_reading));
     467           0 :     magz /= 1 + _mag_comp.tcs.z * (temp - _mag_comp.t0_reading);
     468             : 
     469           0 :     const float cr_ax_comp_x = (magx - _mag_comp.cross_axis.cross_x_y * magy) / (1 - _mag_comp.cross_axis.cross_y_x * _mag_comp.cross_axis.cross_x_y);
     470           0 :     const float cr_ax_comp_y = (magy - _mag_comp.cross_axis.cross_y_x * magx) / (1 - _mag_comp.cross_axis.cross_y_x * _mag_comp.cross_axis.cross_x_y);
     471           0 :     const float cr_ax_comp_z = (magz + (magx * (_mag_comp.cross_axis.cross_y_x * _mag_comp.cross_axis.cross_z_y - _mag_comp.cross_axis.cross_z_x) - 
     472           0 :                                         magy * (_mag_comp.cross_axis.cross_z_y - _mag_comp.cross_axis.cross_x_y * _mag_comp.cross_axis.cross_z_x)) /
     473           0 :                                        (1 - _mag_comp.cross_axis.cross_y_x * _mag_comp.cross_axis.cross_x_y));
     474             : 
     475             :     // Store in field vector and convert uT to milligauss
     476           0 :     Vector3f field { cr_ax_comp_x * 10.0f, cr_ax_comp_y * 10.0f, cr_ax_comp_z * 10.0f };
     477           0 :     accumulate_sample(field);
     478             : }
     479             : 
     480             : #endif  // AP_COMPASS_BMM350_ENABLED

Generated by: LCOV version 1.14