AP_RangeFinder_VL53L1X.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. driver for ST VL53L1X lidar
  15. Many thanks to Pololu, https://github.com/pololu/vl53l1x-arduino and
  16. the ST example code
  17. */
  18. #include "AP_RangeFinder_VL53L1X.h"
  19. #include <utility>
  20. #include <AP_HAL/AP_HAL.h>
  21. #include <AP_HAL/utility/sparse-endian.h>
  22. #include <stdio.h>
  23. extern const AP_HAL::HAL& hal;
  24. /*
  25. The constructor also initializes the rangefinder. Note that this
  26. constructor is not called until detect() returns true, so we
  27. already know that we should setup the rangefinder
  28. */
  29. AP_RangeFinder_VL53L1X::AP_RangeFinder_VL53L1X(RangeFinder::RangeFinder_State &_state, AP_RangeFinder_Params &_params, AP_HAL::OwnPtr<AP_HAL::I2CDevice> _dev)
  30. : AP_RangeFinder_Backend(_state, _params)
  31. , dev(std::move(_dev)) {}
  32. /*
  33. detect if a VL53L1X rangefinder is connected. We'll detect by
  34. trying to take a reading on I2C. If we get a result the sensor is
  35. there.
  36. */
  37. AP_RangeFinder_Backend *AP_RangeFinder_VL53L1X::detect(RangeFinder::RangeFinder_State &_state, AP_RangeFinder_Params &_params, AP_HAL::OwnPtr<AP_HAL::I2CDevice> dev)
  38. {
  39. if (!dev) {
  40. return nullptr;
  41. }
  42. AP_RangeFinder_VL53L1X *sensor
  43. = new AP_RangeFinder_VL53L1X(_state, _params, std::move(dev));
  44. if (!sensor) {
  45. delete sensor;
  46. return nullptr;
  47. }
  48. sensor->dev->get_semaphore()->take_blocking();
  49. if (!sensor->check_id() || !sensor->init()) {
  50. sensor->dev->get_semaphore()->give();
  51. delete sensor;
  52. return nullptr;
  53. }
  54. sensor->dev->get_semaphore()->give();
  55. return sensor;
  56. }
  57. // check sensor ID registers
  58. bool AP_RangeFinder_VL53L1X::check_id(void)
  59. {
  60. uint8_t v1, v2;
  61. if (!(read_register(0x010F, v1) &&
  62. read_register(0x0110, v2))) {
  63. return false;
  64. }
  65. if ((v1 != 0xEA) ||
  66. (v2 != 0xCC)) {
  67. return false;
  68. }
  69. printf("Detected VL53L1X on bus 0x%x\n", dev->get_bus_id());
  70. return true;
  71. }
  72. /*
  73. initialise sensor
  74. */
  75. bool AP_RangeFinder_VL53L1X::init()
  76. {
  77. uint8_t pad_i2c_hv_extsup_config = 0;
  78. uint16_t mm_config_outer_offset_mm = 0;
  79. if (!(// setup for 2.8V operation
  80. read_register(PAD_I2C_HV__EXTSUP_CONFIG, pad_i2c_hv_extsup_config) &&
  81. write_register(PAD_I2C_HV__EXTSUP_CONFIG,
  82. pad_i2c_hv_extsup_config | 0x01) &&
  83. // store oscillator info for later use
  84. read_register16(OSC_MEASURED__FAST_OSC__FREQUENCY, fast_osc_frequency) &&
  85. read_register16(RESULT__OSC_CALIBRATE_VAL, osc_calibrate_val) &&
  86. // static config
  87. write_register16(DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, TargetRate) && // should already be this value after reset
  88. write_register(GPIO__TIO_HV_STATUS, 0x02) &&
  89. write_register(SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS, 8) && // tuning parm default
  90. write_register(SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS, 16) && // tuning parm default
  91. write_register(ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM, 0x01) &&
  92. write_register(ALGO__RANGE_IGNORE_VALID_HEIGHT_MM, 0xFF) &&
  93. write_register(ALGO__RANGE_MIN_CLIP, 0) && // tuning parm default
  94. write_register(ALGO__CONSISTENCY_CHECK__TOLERANCE, 2) && // tuning parm default
  95. // general config
  96. write_register16(SYSTEM__THRESH_RATE_HIGH, 0x0000) &&
  97. write_register16(SYSTEM__THRESH_RATE_LOW, 0x0000) &&
  98. write_register(DSS_CONFIG__APERTURE_ATTENUATION, 0x38) &&
  99. // timing config
  100. write_register16(RANGE_CONFIG__SIGMA_THRESH, 360) && // tuning parm default
  101. write_register16(RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, 192) && // tuning parm default
  102. // dynamic config
  103. write_register(SYSTEM__GROUPED_PARAMETER_HOLD_0, 0x01) &&
  104. write_register(SYSTEM__GROUPED_PARAMETER_HOLD_1, 0x01) &&
  105. write_register(SD_CONFIG__QUANTIFIER, 2) && // tuning parm default
  106. // from VL53L1_preset_mode_timed_ranging_*
  107. // GPH is 0 after reset, but writing GPH0 and GPH1 above seem to set GPH to 1,
  108. // and things don't seem to work if we don't set GPH back to 0 (which the API
  109. // does here).
  110. write_register(SYSTEM__GROUPED_PARAMETER_HOLD, 0x00) &&
  111. write_register(SYSTEM__SEED_CONFIG, 1) && // tuning parm default
  112. // from VL53L1_config_low_power_auto_mode
  113. write_register(SYSTEM__SEQUENCE_CONFIG, 0x8B) && // VHV, PHASECAL, DSS1, RANGE
  114. write_register16(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, 200 << 8) &&
  115. write_register(DSS_CONFIG__ROI_MODE_CONTROL, 2) && // REQUESTED_EFFFECTIVE_SPADS
  116. read_register16(MM_CONFIG__OUTER_OFFSET_MM, mm_config_outer_offset_mm) &&
  117. setDistanceMode(Long) &&
  118. setMeasurementTimingBudget(40000) &&
  119. // the API triggers this change in VL53L1_init_and_start_range() once a
  120. // measurement is started; assumes MM1 and MM2 are disabled
  121. write_register16(ALGO__PART_TO_PART_RANGE_OFFSET_MM, mm_config_outer_offset_mm * 4) &&
  122. // set continuous mode
  123. startContinuous(50)
  124. )) {
  125. return false;
  126. }
  127. // call timer() every 50ms. We expect new data to be available every 50ms
  128. dev->register_periodic_callback(50000,
  129. FUNCTOR_BIND_MEMBER(&AP_RangeFinder_VL53L1X::timer, void));
  130. return true;
  131. }
  132. // set distance mode to Short, Medium, or Long
  133. // based on VL53L1_SetDistanceMode()
  134. bool AP_RangeFinder_VL53L1X::setDistanceMode(DistanceMode distance_mode)
  135. {
  136. // save existing timing budget
  137. uint32_t budget_us = 0;
  138. if (!getMeasurementTimingBudget(budget_us)) {
  139. return false;
  140. }
  141. switch (distance_mode) {
  142. case Short:
  143. // from VL53L1_preset_mode_standard_ranging_short_range()
  144. if (!(// timing config
  145. write_register(RANGE_CONFIG__VCSEL_PERIOD_A, 0x07) &&
  146. write_register(RANGE_CONFIG__VCSEL_PERIOD_B, 0x05) &&
  147. write_register(RANGE_CONFIG__VALID_PHASE_HIGH, 0x38) &&
  148. // dynamic config
  149. write_register(SD_CONFIG__WOI_SD0, 0x07) &&
  150. write_register(SD_CONFIG__WOI_SD1, 0x05) &&
  151. write_register(SD_CONFIG__INITIAL_PHASE_SD0, 6) && // tuning parm default
  152. write_register(SD_CONFIG__INITIAL_PHASE_SD1, 6))) { // tuning parm default
  153. return false;
  154. }
  155. break;
  156. case Medium:
  157. // from VL53L1_preset_mode_standard_ranging()
  158. if (!(// timing config
  159. write_register(RANGE_CONFIG__VCSEL_PERIOD_A, 0x0B) &&
  160. write_register(RANGE_CONFIG__VCSEL_PERIOD_B, 0x09) &&
  161. write_register(RANGE_CONFIG__VALID_PHASE_HIGH, 0x78) &&
  162. // dynamic config
  163. write_register(SD_CONFIG__WOI_SD0, 0x0B) &&
  164. write_register(SD_CONFIG__WOI_SD1, 0x09) &&
  165. write_register(SD_CONFIG__INITIAL_PHASE_SD0, 10) && // tuning parm default
  166. write_register(SD_CONFIG__INITIAL_PHASE_SD1, 10))) { // tuning parm default
  167. return false;
  168. }
  169. break;
  170. case Long:
  171. // from VL53L1_preset_mode_standard_ranging_long_range()
  172. if (!(// timing config
  173. write_register(RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F) &&
  174. write_register(RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D) &&
  175. write_register(RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8) &&
  176. // dynamic config
  177. write_register(SD_CONFIG__WOI_SD0, 0x0F) &&
  178. write_register(SD_CONFIG__WOI_SD1, 0x0D) &&
  179. write_register(SD_CONFIG__INITIAL_PHASE_SD0, 14) && // tuning parm default
  180. write_register(SD_CONFIG__INITIAL_PHASE_SD1, 14))) { // tuning parm default
  181. return false;
  182. }
  183. break;
  184. default:
  185. // unrecognized mode - do nothing
  186. return false;
  187. }
  188. // reapply timing budget
  189. return setMeasurementTimingBudget(budget_us);
  190. }
  191. // Set the measurement timing budget in microseconds, which is the time allowed
  192. // for one measurement. A longer timing budget allows for more accurate measurements.
  193. // based on VL53L1_SetMeasurementTimingBudgetMicroSeconds()
  194. bool AP_RangeFinder_VL53L1X::setMeasurementTimingBudget(uint32_t budget_us)
  195. {
  196. // assumes PresetMode is LOWPOWER_AUTONOMOUS
  197. if (budget_us <= TimingGuard) {
  198. return false;
  199. }
  200. uint32_t range_config_timeout_us = budget_us - TimingGuard;
  201. if (range_config_timeout_us > 1100000) {
  202. return false; // FDA_MAX_TIMING_BUDGET_US * 2
  203. }
  204. range_config_timeout_us /= 2;
  205. // VL53L1_calc_timeout_register_values() begin
  206. uint8_t range_config_vcsel_period = 0;
  207. if (!read_register(RANGE_CONFIG__VCSEL_PERIOD_A, range_config_vcsel_period)) {
  208. return false;
  209. }
  210. // "Update Macro Period for Range A VCSEL Period"
  211. uint32_t macro_period_us = calcMacroPeriod(range_config_vcsel_period);
  212. // "Update Phase timeout - uses Timing A"
  213. // Timeout of 1000 is tuning parm default (TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT)
  214. // via VL53L1_get_preset_mode_timing_cfg().
  215. uint32_t phasecal_timeout_mclks = timeoutMicrosecondsToMclks(1000, macro_period_us);
  216. if (phasecal_timeout_mclks > 0xFF) {
  217. phasecal_timeout_mclks = 0xFF;
  218. }
  219. if (!( write_register(PHASECAL_CONFIG__TIMEOUT_MACROP, phasecal_timeout_mclks) &&
  220. // "Update MM Timing A timeout"
  221. // Timeout of 1 is tuning parm default (LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT)
  222. // via VL53L1_get_preset_mode_timing_cfg(). With the API, the register
  223. // actually ends up with a slightly different value because it gets assigned,
  224. // retrieved, recalculated with a different macro period, and reassigned,
  225. // but it probably doesn't matter because it seems like the MM ("mode
  226. // mitigation"?) sequence steps are disabled in low power auto mode anyway.
  227. write_register16(MM_CONFIG__TIMEOUT_MACROP_A, encodeTimeout(
  228. timeoutMicrosecondsToMclks(1, macro_period_us))) &&
  229. // "Update Range Timing A timeout"
  230. write_register16(RANGE_CONFIG__TIMEOUT_MACROP_A, encodeTimeout(
  231. timeoutMicrosecondsToMclks(range_config_timeout_us, macro_period_us))) &&
  232. // "Update Macro Period for Range B VCSEL Period"
  233. read_register(RANGE_CONFIG__VCSEL_PERIOD_B, range_config_vcsel_period)
  234. )) {
  235. return false;
  236. }
  237. // "Update Macro Period for Range B VCSEL Period"
  238. macro_period_us = calcMacroPeriod(range_config_vcsel_period);
  239. // "Update MM Timing B timeout"
  240. // (See earlier comment about MM Timing A timeout.)
  241. return write_register16(MM_CONFIG__TIMEOUT_MACROP_B, encodeTimeout(
  242. timeoutMicrosecondsToMclks(1, macro_period_us))) &&
  243. // "Update Range Timing B timeout"
  244. write_register16(RANGE_CONFIG__TIMEOUT_MACROP_B, encodeTimeout(
  245. timeoutMicrosecondsToMclks(range_config_timeout_us, macro_period_us)));
  246. }
  247. // Get the measurement timing budget in microseconds
  248. // based on VL53L1_SetMeasurementTimingBudgetMicroSeconds()
  249. bool AP_RangeFinder_VL53L1X::getMeasurementTimingBudget(uint32_t &budget)
  250. {
  251. // assumes PresetMode is LOWPOWER_AUTONOMOUS and these sequence steps are
  252. // enabled: VHV, PHASECAL, DSS1, RANGE
  253. // "Update Macro Period for Range A VCSEL Period"
  254. uint8_t range_config_vcsel_period_a = 0;
  255. if (!read_register(RANGE_CONFIG__VCSEL_PERIOD_A, range_config_vcsel_period_a)) {
  256. return false;
  257. }
  258. uint32_t macro_period_us = calcMacroPeriod(range_config_vcsel_period_a);
  259. uint16_t timeout_macrop_a = 0;
  260. if (!read_register16(RANGE_CONFIG__TIMEOUT_MACROP_A, timeout_macrop_a)) {
  261. return false;
  262. }
  263. // "Get Range Timing A timeout"
  264. uint32_t range_config_timeout_us = timeoutMclksToMicroseconds(decodeTimeout(timeout_macrop_a), macro_period_us);
  265. budget = 2 * range_config_timeout_us + TimingGuard;
  266. return true;
  267. }
  268. // Start continuous ranging measurements, with the given inter-measurement
  269. // period in milliseconds determining how often the sensor takes a measurement.
  270. bool AP_RangeFinder_VL53L1X::startContinuous(uint32_t period_ms)
  271. {
  272. // fix for actual measurement period shorter than set
  273. uint32_t adjusted_period_ms = period_ms + (period_ms * 64 / 1000);
  274. // from VL53L1_set_inter_measurement_period_ms()
  275. return write_register32(SYSTEM__INTERMEASUREMENT_PERIOD, adjusted_period_ms * osc_calibrate_val) &&
  276. write_register(SYSTEM__INTERRUPT_CLEAR, 0x01) && // sys_interrupt_clear_range
  277. write_register(SYSTEM__MODE_START, 0x40); // mode_range__timed
  278. }
  279. // Decode sequence step timeout in MCLKs from register value
  280. // based on VL53L1_decode_timeout()
  281. uint32_t AP_RangeFinder_VL53L1X::decodeTimeout(uint16_t reg_val)
  282. {
  283. return ((uint32_t)(reg_val & 0xFF) << (reg_val >> 8)) + 1;
  284. }
  285. // Encode sequence step timeout register value from timeout in MCLKs
  286. // based on VL53L1_encode_timeout()
  287. uint16_t AP_RangeFinder_VL53L1X::encodeTimeout(uint32_t timeout_mclks)
  288. {
  289. // encoded format: "(LSByte * 2^MSByte) + 1"
  290. uint32_t ls_byte = 0;
  291. uint16_t ms_byte = 0;
  292. if (timeout_mclks > 0) {
  293. ls_byte = timeout_mclks - 1;
  294. while ((ls_byte & 0xFFFFFF00) > 0) {
  295. ls_byte >>= 1;
  296. ms_byte++;
  297. }
  298. return (ms_byte << 8) | (ls_byte & 0xFF);
  299. }
  300. else {
  301. return 0;
  302. }
  303. }
  304. // Convert sequence step timeout from macro periods to microseconds with given
  305. // macro period in microseconds (12.12 format)
  306. // based on VL53L1_calc_timeout_us()
  307. uint32_t AP_RangeFinder_VL53L1X::timeoutMclksToMicroseconds(uint32_t timeout_mclks, uint32_t macro_period_us)
  308. {
  309. return ((uint64_t)timeout_mclks * macro_period_us + 0x800) >> 12;
  310. }
  311. // Convert sequence step timeout from microseconds to macro periods with given
  312. // macro period in microseconds (12.12 format)
  313. // based on VL53L1_calc_timeout_mclks()
  314. uint32_t AP_RangeFinder_VL53L1X::timeoutMicrosecondsToMclks(uint32_t timeout_us, uint32_t macro_period_us)
  315. {
  316. return (((uint32_t)timeout_us << 12) + (macro_period_us >> 1)) / macro_period_us;
  317. }
  318. // Calculate macro period in microseconds (12.12 format) with given VCSEL period
  319. // assumes fast_osc_frequency has been read and stored
  320. // based on VL53L1_calc_macro_period_us()
  321. uint32_t AP_RangeFinder_VL53L1X::calcMacroPeriod(uint8_t vcsel_period)
  322. {
  323. // from VL53L1_calc_pll_period_us()
  324. // fast osc frequency in 4.12 format; PLL period in 0.24 format
  325. uint32_t pll_period_us = ((uint32_t)0x01 << 30) / fast_osc_frequency;
  326. // from VL53L1_decode_vcsel_period()
  327. uint8_t vcsel_period_pclks = (vcsel_period + 1) << 1;
  328. // VL53L1_MACRO_PERIOD_VCSEL_PERIODS = 2304
  329. uint32_t macro_period_us = (uint32_t)2304 * pll_period_us;
  330. macro_period_us >>= 6;
  331. macro_period_us *= vcsel_period_pclks;
  332. macro_period_us >>= 6;
  333. return macro_period_us;
  334. }
  335. // "Setup ranges after the first one in low power auto mode by turning off
  336. // FW calibration steps and programming static values"
  337. // based on VL53L1_low_power_auto_setup_manual_calibration()
  338. bool AP_RangeFinder_VL53L1X::setupManualCalibration(void)
  339. {
  340. uint8_t saved_vhv_init = 0;
  341. uint8_t saved_vhv_timeout = 0;
  342. uint8_t phasecal_result_vcsel_start = 0;
  343. return // "save original vhv configs"
  344. read_register(VHV_CONFIG__INIT, saved_vhv_init) &&
  345. read_register(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, saved_vhv_timeout) &&
  346. // "disable VHV init"
  347. write_register(VHV_CONFIG__INIT, saved_vhv_init & 0x7F) &&
  348. // "set loop bound to tuning param"
  349. write_register(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND,
  350. (saved_vhv_timeout & 0x03) + (3 << 2)) && // tuning parm default (LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT)
  351. // "override phasecal"
  352. write_register(PHASECAL_CONFIG__OVERRIDE, 0x01) &&
  353. read_register(PHASECAL_RESULT__VCSEL_START, phasecal_result_vcsel_start) &&
  354. write_register(CAL_CONFIG__VCSEL_START, phasecal_result_vcsel_start);
  355. }
  356. // check if sensor has new reading available
  357. // assumes interrupt is active low (GPIO_HV_MUX__CTRL bit 4 is 1)
  358. bool AP_RangeFinder_VL53L1X::dataReady(void)
  359. {
  360. uint8_t gpio_tio_hv_status = 0;
  361. return read_register(GPIO__TIO_HV_STATUS, gpio_tio_hv_status) &&
  362. ((gpio_tio_hv_status & 0x01) == 0);
  363. }
  364. // read - return last value measured by sensor
  365. bool AP_RangeFinder_VL53L1X::get_reading(uint16_t &reading_mm)
  366. {
  367. uint8_t tries = 10;
  368. while (!dataReady()) {
  369. tries--;
  370. hal.scheduler->delay(1);
  371. if (tries == 0) {
  372. return false;
  373. }
  374. }
  375. uint8_t range_status = 0;
  376. if (!(read_register(RESULT__RANGE_STATUS, range_status) &&
  377. read_register16(RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, reading_mm))) {
  378. return false;
  379. }
  380. // "apply correction gain"
  381. // gain factor of 2011 is tuning parm default (VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT)
  382. // Basically, this appears to scale the result by 2011/2048, or about 98%
  383. // (with the 1024 added for proper rounding).
  384. reading_mm = ((uint32_t)reading_mm * 2011 + 0x0400) / 0x0800;
  385. if (!write_register(SYSTEM__INTERRUPT_CLEAR, 0x01)) { // sys_interrupt_clear_range
  386. return false;
  387. }
  388. switch ((DeviceError)range_status) {
  389. case RANGECOMPLETE:
  390. break;
  391. default:
  392. #ifdef VL53L1X_DEBUG
  393. hal.console->printf("VL53L1X: %d ms status %d\n", AP_HAL::millis(), (int)range_status);
  394. #endif // VL53L1X_DEBUG
  395. return false;
  396. }
  397. if (!calibrated) {
  398. calibrated = setupManualCalibration();
  399. }
  400. return calibrated;
  401. }
  402. bool AP_RangeFinder_VL53L1X::read_register(uint16_t reg, uint8_t &value)
  403. {
  404. uint8_t b[2] = { uint8_t(reg >> 8), uint8_t(reg & 0xFF) };
  405. return dev->transfer(b, 2, &value, 1);
  406. }
  407. bool AP_RangeFinder_VL53L1X::read_register16(uint16_t reg, uint16_t & value)
  408. {
  409. uint16_t v = 0;
  410. uint8_t b[2] = { uint8_t(reg >> 8), uint8_t(reg & 0xFF) };
  411. if (!dev->transfer(b, 2, (uint8_t *)&v, 2)) {
  412. return false;
  413. }
  414. value = be16toh(v);
  415. return true;
  416. }
  417. bool AP_RangeFinder_VL53L1X::write_register(uint16_t reg, uint8_t value)
  418. {
  419. uint8_t b[3] = { uint8_t(reg >> 8), uint8_t(reg & 0xFF), value };
  420. return dev->transfer(b, 3, nullptr, 0);
  421. }
  422. bool AP_RangeFinder_VL53L1X::write_register16(uint16_t reg, uint16_t value)
  423. {
  424. uint8_t b[4] = { uint8_t(reg >> 8), uint8_t(reg & 0xFF), uint8_t(value >> 8), uint8_t(value & 0xFF) };
  425. return dev->transfer(b, 4, nullptr, 0);
  426. }
  427. bool AP_RangeFinder_VL53L1X::write_register32(uint16_t reg, uint32_t value)
  428. {
  429. uint8_t b[6] = { uint8_t(reg >> 8),
  430. uint8_t(reg & 0xFF),
  431. uint8_t((value >> 24) & 0xFF),
  432. uint8_t((value >> 16) & 0xFF),
  433. uint8_t((value >> 8) & 0xFF),
  434. uint8_t((value) & 0xFF) };
  435. return dev->transfer(b, 6, nullptr, 0);
  436. }
  437. /*
  438. timer called at 20Hz
  439. */
  440. void AP_RangeFinder_VL53L1X::timer(void)
  441. {
  442. uint16_t range_mm;
  443. if ((get_reading(range_mm)) && (range_mm <= 4000)) {
  444. WITH_SEMAPHORE(_sem);
  445. sum_mm += range_mm;
  446. counter++;
  447. }
  448. }
  449. /*
  450. update the state of the sensor
  451. */
  452. void AP_RangeFinder_VL53L1X::update(void)
  453. {
  454. WITH_SEMAPHORE(_sem);
  455. if (counter > 0) {
  456. state.distance_cm = sum_mm / (10*counter);
  457. state.last_reading_ms = AP_HAL::millis();
  458. update_status();
  459. sum_mm = 0;
  460. counter = 0;
  461. } else if (AP_HAL::millis() - state.last_reading_ms > 200) {
  462. // if no updates for 0.2s set no-data
  463. set_status(RangeFinder::RangeFinder_NoData);
  464. }
  465. }