Line data Source code
1 : /* 2 : * Copyright (C) 2018 Lucas De Marchi. 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 2 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 : #include "AP_Compass_IST8308.h" 18 : 19 : #if AP_COMPASS_IST8308_ENABLED 20 : 21 : #include <stdio.h> 22 : #include <utility> 23 : 24 : #include <AP_HAL/AP_HAL.h> 25 : #include <AP_HAL/utility/sparse-endian.h> 26 : #include <AP_Math/AP_Math.h> 27 : 28 : #define WAI_REG 0x0 29 : #define DEVICE_ID 0x08 30 : 31 : #define STAT1_REG 0x10 32 : #define STAT1_VAL_DRDY 0x1 33 : #define STAT1_VAL_DOR 0x2 34 : 35 : #define DATAX_L_REG 0x11 36 : #define DATAX_H_REG 0x12 37 : #define DATAY_L_REG 0x13 38 : #define DATAY_H_REG 0x14 39 : #define DATAZ_L_REG 0x15 40 : #define DATAZ_H_REG 0x16 41 : 42 : #define CNTL1_REG 0x30 43 : 44 : #define CNTL2_REG 0x31 45 : #define CNTL2_VAL_STANDBY_MODE 0x0 46 : #define CNTL2_VAL_SINGLE_MODE 0x1 47 : #define CNTL2_VAL_CONT_ODR10_MODE 0x2 48 : #define CNTL2_VAL_CONT_ODR20_MODE 0x4 49 : #define CNTL2_VAL_CONT_ODR50_MODE 0x6 50 : #define CNTL2_VAL_CONT_ODR100_MODE 0x8 51 : #define CNTL2_VAL_CONT_ODR200_MODE 0xA 52 : #define CNTL2_VAL_CONT_ODR8_MODE 0xB 53 : #define CNTL2_VAL_CONT_ODR1_MODE 0xC 54 : #define CNTL2_VAL_CONT_ODR0P5_MODE 0xD 55 : #define CNTL2_VAL_SINGLE_TEST_MODE 0x10 56 : 57 : #define CNTL3_REG 0x32 58 : #define CNTL3_VAL_SRST 1 59 : #define CNTL3_VAL_DRDY_POLARITY_HIGH (1 << 2) 60 : #define CNTL3_VAL_DRDY_EN (1 << 3) 61 : 62 : #define CNTL4_REG 0x34 63 : #define CNTL4_VAL_DYNAMIC_RANGE_500 0 64 : #define CNTL4_VAL_DYNAMIC_RANGE_200 0x1 65 : 66 : #define OSRCNTL_REG 0x41 67 : #define OSRCNTL_VAL_XZ_1 (0) 68 : #define OSRCNTL_VAL_XZ_2 (1) 69 : #define OSRCNTL_VAL_XZ_4 (2) 70 : #define OSRCNTL_VAL_XZ_8 (3) 71 : #define OSRCNTL_VAL_XZ_16 (4) 72 : #define OSRCNTL_VAL_XZ_32 (4) 73 : #define OSRCNTL_VAL_Y_1 (0 << 3) 74 : #define OSRCNTL_VAL_Y_2 (1 << 3) 75 : #define OSRCNTL_VAL_Y_4 (2 << 3) 76 : #define OSRCNTL_VAL_Y_8 (3 << 3) 77 : #define OSRCNTL_VAL_Y_16 (4 << 3) 78 : #define OSRCNTL_VAL_Y_32 (5 << 3) 79 : 80 : #define SAMPLING_PERIOD_USEC (10 * AP_USEC_PER_MSEC) 81 : 82 : extern const AP_HAL::HAL &hal; 83 : 84 0 : AP_Compass_Backend *AP_Compass_IST8308::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev, 85 : bool force_external, 86 : enum Rotation rotation) 87 : { 88 0 : if (!dev) { 89 0 : return nullptr; 90 : } 91 : 92 0 : AP_Compass_IST8308 *sensor = NEW_NOTHROW AP_Compass_IST8308(std::move(dev), force_external, rotation); 93 0 : if (!sensor || !sensor->init()) { 94 0 : delete sensor; 95 0 : return nullptr; 96 : } 97 : 98 0 : return sensor; 99 : } 100 : 101 0 : AP_Compass_IST8308::AP_Compass_IST8308(AP_HAL::OwnPtr<AP_HAL::Device> dev, 102 : bool force_external, 103 0 : enum Rotation rotation) 104 0 : : _dev(std::move(dev)) 105 0 : , _rotation(rotation) 106 0 : , _force_external(force_external) 107 : { 108 0 : } 109 : 110 0 : bool AP_Compass_IST8308::init() 111 : { 112 0 : uint8_t reset_count = 0; 113 : 114 0 : _dev->get_semaphore()->take_blocking(); 115 : 116 : // high retries for init 117 0 : _dev->set_retries(10); 118 : 119 : uint8_t whoami; 120 0 : if (!_dev->read_registers(WAI_REG, &whoami, 1) || 121 0 : whoami != DEVICE_ID) { 122 : // not an IST8308 123 0 : goto fail; 124 : } 125 : 126 0 : for (; reset_count < 5; reset_count++) { 127 0 : if (!_dev->write_register(CNTL3_REG, CNTL3_VAL_SRST)) { 128 0 : hal.scheduler->delay(10); 129 0 : continue; 130 : } 131 : 132 0 : hal.scheduler->delay(20); 133 : 134 0 : uint8_t cntl3 = 0xFF; 135 0 : if (_dev->read_registers(CNTL3_REG, &cntl3, 1) && 136 0 : (cntl3 & 0x01) == 0) { 137 0 : break; 138 : } 139 : } 140 : 141 0 : if (reset_count == 5) { 142 0 : printf("IST8308: failed to reset device\n"); 143 0 : goto fail; 144 : } 145 : 146 : // DRDY enabled 147 : // Dynamic Range=±500 uT, Sensitivity=6.6 LSB/uT 148 : // OSR 16 (max 100Hz) 149 : // Start continuous mode at 100Hz 150 0 : if (!_dev->write_register(CNTL3_REG, CNTL3_VAL_DRDY_EN) || 151 0 : !_dev->write_register(CNTL4_REG, CNTL4_VAL_DYNAMIC_RANGE_500) || 152 0 : !_dev->write_register(OSRCNTL_REG, OSRCNTL_VAL_Y_16 | OSRCNTL_VAL_XZ_16) || 153 0 : !_dev->write_register(CNTL2_REG, CNTL2_VAL_CONT_ODR100_MODE)) { 154 0 : printf("IST8308: found device but could not set it up\n"); 155 0 : goto fail; 156 : } 157 : 158 : // lower retries for run 159 0 : _dev->set_retries(3); 160 : 161 0 : _dev->get_semaphore()->give(); 162 : 163 : //register compass instance 164 0 : _dev->set_device_type(DEVTYPE_IST8308); 165 0 : if (!register_compass(_dev->get_bus_id())) { 166 0 : return false; 167 : } 168 : 169 0 : printf("%s found on bus %u id %u address 0x%02x\n", name, 170 0 : _dev->bus_num(), unsigned(_dev->get_bus_id()), _dev->get_bus_address()); 171 : 172 0 : set_rotation(_rotation); 173 : 174 0 : if (_force_external) { 175 0 : set_external(true); 176 : } 177 : 178 0 : _dev->register_periodic_callback(SAMPLING_PERIOD_USEC, 179 : FUNCTOR_BIND_MEMBER(&AP_Compass_IST8308::timer, void)); 180 : 181 0 : return true; 182 : 183 0 : fail: 184 0 : _dev->get_semaphore()->give(); 185 0 : return false; 186 : } 187 : 188 0 : void AP_Compass_IST8308::timer() 189 : { 190 : struct PACKED { 191 : le16_t rx; 192 : le16_t ry; 193 : le16_t rz; 194 : } buffer; 195 : uint8_t stat; 196 : 197 0 : if (!_dev->read_registers(STAT1_REG, &stat, 1) || 198 0 : !(stat & STAT1_VAL_DRDY)) { 199 0 : return; 200 : } 201 : 202 0 : if (!_dev->read_registers(DATAX_L_REG, (uint8_t *) &buffer, 203 : sizeof(buffer))) { 204 0 : return; 205 : } 206 : 207 0 : auto x = static_cast<int16_t>(le16toh(buffer.rx)); 208 0 : auto y = static_cast<int16_t>(le16toh(buffer.ry)); 209 0 : auto z = static_cast<int16_t>(le16toh(buffer.rz)); 210 : 211 : // flip Z to conform to right-hand rule convention 212 0 : z = -z; 213 : 214 : /* Resolution: 0.1515 µT/LSB - already convert to milligauss */ 215 0 : Vector3f field = Vector3f{x * 1.515f, y * 1.515f, z * 1.515f}; 216 : 217 0 : accumulate_sample(field); 218 : } 219 : 220 : #endif // AP_COMPASS_IST8308_ENABLED