vl53l4cd/lib.rs
1//! Async driver for the [VL53L4CD ToF distance sensor](https://www.st.com/en/imaging-and-photonics-solutions/vl53l4cd.html).
2//!
3//! This crate is very much a port of the [STM32Duino VL53L4CD library](https://github.com/stm32duino/VL53L4CD).
4//!
5//! [Datasheet](https://www.st.com/resource/en/datasheet/vl53l4cd.pdf)
6
7#![warn(missing_docs)]
8#![cfg_attr(not(feature = "std"), no_std)]
9
10mod i2c;
11pub mod wait;
12
13use wait::WaitForMeasurement;
14
15use core::fmt;
16
17use embedded_hal_async::{delay::DelayNs, i2c::I2c};
18use i2c::Device;
19
20pub(crate) const DEFAULT_CONFIG_MSG: &[u8] = &[
21 0x00, // first byte of register to write to
22 0x2d, // second byte of register to write to
23 // value addr : description
24 0x12, // 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C), else don't touch
25 0x00, // 0x2e : bit 0 if I2C pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD)
26 0x00, // 0x2f : bit 0 if GPIO pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD)
27 0x11, // 0x30 : set bit 4 to 0 for active high interrupt and 1 for active low (bits 3:0 must be 0x1)
28 0x02, // 0x31 : bit 1 = interrupt depending on the polarity
29 0x00, // 0x32 : not user-modifiable
30 0x02, // 0x33 : not user-modifiable
31 0x08, // 0x34 : not user-modifiable
32 0x00, // 0x35 : not user-modifiable
33 0x08, // 0x36 : not user-modifiable
34 0x10, // 0x37 : not user-modifiable
35 0x01, // 0x38 : not user-modifiable
36 0x01, // 0x39 : not user-modifiable
37 0x00, // 0x3a : not user-modifiable
38 0x00, // 0x3b : not user-modifiable
39 0x00, // 0x3c : not user-modifiable
40 0x00, // 0x3d : not user-modifiable
41 0xFF, // 0x3e : not user-modifiable
42 0x00, // 0x3f : not user-modifiable
43 0x0F, // 0x40 : not user-modifiable
44 0x00, // 0x41 : not user-modifiable
45 0x00, // 0x42 : not user-modifiable
46 0x00, // 0x43 : not user-modifiable
47 0x00, // 0x44 : not user-modifiable
48 0x00, // 0x45 : not user-modifiable
49 0x20, // 0x46 : interrupt configuration 0->level low detection, 1-> level high, 2-> Out of window, 3->In window, 0x20-> New sample ready , TBC
50 0x0B, // 0x47 : not user-modifiable
51 0x00, // 0x48 : not user-modifiable
52 0x00, // 0x49 : not user-modifiable
53 0x02, // 0x4a : not user-modifiable
54 0x14, // 0x4b : not user-modifiable
55 0x21, // 0x4c : not user-modifiable
56 0x00, // 0x4d : not user-modifiable
57 0x00, // 0x4e : not user-modifiable
58 0x05, // 0x4f : not user-modifiable
59 0x00, // 0x50 : not user-modifiable
60 0x00, // 0x51 : not user-modifiable
61 0x00, // 0x52 : not user-modifiable
62 0x00, // 0x53 : not user-modifiable
63 0xC8, // 0x54 : not user-modifiable
64 0x00, // 0x55 : not user-modifiable
65 0x00, // 0x56 : not user-modifiable
66 0x38, // 0x57 : not user-modifiable
67 0xFF, // 0x58 : not user-modifiable
68 0x01, // 0x59 : not user-modifiable
69 0x00, // 0x5a : not user-modifiable
70 0x08, // 0x5b : not user-modifiable
71 0x00, // 0x5c : not user-modifiable
72 0x00, // 0x5d : not user-modifiable
73 0x01, // 0x5e : not user-modifiable
74 0xCC, // 0x5f : not user-modifiable
75 0x07, // 0x60 : not user-modifiable
76 0x01, // 0x61 : not user-modifiable
77 0xF1, // 0x62 : not user-modifiable
78 0x05, // 0x63 : not user-modifiable
79 0x00, // 0x64 : Sigma threshold MSB (mm in 14.2 format for MSB+LSB), default value 90 mm
80 0xA0, // 0x65 : Sigma threshold LSB
81 0x00, // 0x66 : Min count Rate MSB (MCPS in 9.7 format for MSB+LSB)
82 0x80, // 0x67 : Min count Rate LSB
83 0x08, // 0x68 : not user-modifiable
84 0x38, // 0x69 : not user-modifiable
85 0x00, // 0x6a : not user-modifiable
86 0x00, // 0x6b : not user-modifiable
87 0x00, // 0x6c : Intermeasurement period MSB, 32 bits register
88 0x00, // 0x6d : Intermeasurement period
89 0x0F, // 0x6e : Intermeasurement period
90 0x89, // 0x6f : Intermeasurement period LSB
91 0x00, // 0x70 : not user-modifiable
92 0x00, // 0x71 : not user-modifiable
93 0x00, // 0x72 : distance threshold high MSB (in mm, MSB+LSB)
94 0x00, // 0x73 : distance threshold high LSB
95 0x00, // 0x74 : distance threshold low MSB ( in mm, MSB+LSB)
96 0x00, // 0x75 : distance threshold low LSB
97 0x00, // 0x76 : not user-modifiable
98 0x01, // 0x77 : not user-modifiable
99 0x07, // 0x78 : not user-modifiable
100 0x05, // 0x79 : not user-modifiable
101 0x06, // 0x7a : not user-modifiable
102 0x06, // 0x7b : not user-modifiable
103 0x00, // 0x7c : not user-modifiable
104 0x00, // 0x7d : not user-modifiable
105 0x02, // 0x7e : not user-modifiable
106 0xC7, // 0x7f : not user-modifiable
107 0xFF, // 0x80 : not user-modifiable
108 0x9B, // 0x81 : not user-modifiable
109 0x00, // 0x82 : not user-modifiable
110 0x00, // 0x83 : not user-modifiable
111 0x00, // 0x84 : not user-modifiable
112 0x01, // 0x85 : not user-modifiable
113 0x00, // 0x86 : clear interrupt, 0x01=clear
114 0x00, // 0x87 : ranging, 0x00=stop, 0x40=start
115];
116
117/// A register on the device, identified by a 16-bit address.
118#[derive(Debug, Clone, Copy)]
119#[allow(non_camel_case_types)]
120#[allow(missing_docs)]
121#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
122pub enum Register {
123 OSC_FREQ = 0x0006,
124 VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND = 0x0008,
125 /// This name is temporary.
126 MYSTERY_1 = 0x000b,
127 /// This name is also temporary.
128 MYSTERY_2 = 0x0024,
129 SYSTEM_START = 0x0087,
130 GPIO_HV_MUX_CTRL = 0x0030,
131 GPIO_TIO_HV_STATUS = 0x0031,
132 RANGE_CONFIG_A = 0x005e,
133 RANGE_CONFIG_B = 0x0061,
134 INTERMEASUREMENT_MS = 0x006c,
135 SYSTEM_INTERRUPT_CLEAR = 0x0086,
136 RESULT_RANGE_STATUS = 0x0089,
137 RESULT_NUM_SPADS = 0x008c,
138 RESULT_SIGNAL_RATE = 0x008e,
139 RESULT_AMBIENT_RATE = 0x0090,
140 RESULT_SIGMA = 0x0092,
141 RESULT_DISTANCE = 0x0096,
142 RESULT_OSC_CALIBRATE_VAL = 0x00de,
143 SYSTEM_STATUS = 0x00e5,
144 IDENTIFICATION_MODEL_ID = 0x010f,
145}
146
147impl Register {
148 /// Get the 16-bit address of the register.
149 ///
150 /// ```
151 /// # use vl53l4cd::Register;
152 /// assert_eq!(0x010f, Register::IDENTIFICATION_MODEL_ID.addr());
153 /// ```
154 pub const fn addr(&self) -> u16 {
155 *self as u16
156 }
157
158 /// Get the big-endian bytes of the register address.
159 pub const fn as_bytes(&self) -> [u8; 2] {
160 self.addr().to_be_bytes()
161 }
162}
163
164/// Default I²C address of the VL53L4CD.
165pub const PERIPHERAL_ADDR: u8 = 0x29;
166
167/// Measurement status as per the user manual.
168#[derive(Debug, PartialEq, Eq, Clone, Copy)]
169#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
170#[repr(u8)]
171pub enum Status {
172 /// Returned distance is valid.
173 Valid = 0,
174 /// Sigma is above the defined threshol.
175 SigmaAboveThreshold,
176 /// Signal is below the defined threshold.
177 SigmaBelowThreshold,
178 /// Measured distance is below detection threshold.
179 DistanceBelowDetectionThreshold,
180 /// Phase out of valid limit.
181 InvalidPhase,
182 /// Hardware failure.
183 HardwareFail,
184 /// Phase valid but no wrap around check performed.
185 NoWrapAroundCheck,
186 /// Wrapped target, phase does not match.
187 WrappedTargetPhaseMismatch,
188 /// Processing fail.
189 ProcessingFail,
190 /// Crosstalk signal fail.
191 XTalkFail,
192 /// Interrupt error.
193 InterruptError,
194 /// Merged target.
195 MergedTarget,
196 /// Too low signal.
197 SignalTooWeak,
198 /// Other error (e.g. boot error).
199 Other = 255,
200}
201
202/// Severity of a measurement status.
203#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
204#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
205pub enum Severity {
206 /// The measurement is completely valid.
207 None,
208 /// The computed measurement might be somewhat correct.
209 Warning,
210 /// Something went very wrong.
211 Error,
212}
213
214impl Status {
215 const fn from_rtn(rtn: u8) -> Self {
216 assert!(rtn < 24); // if rtn >= 24, return rtn
217
218 match rtn {
219 3 => Self::HardwareFail,
220 4 | 5 => Self::SigmaBelowThreshold,
221 6 => Self::SigmaAboveThreshold,
222 7 => Self::WrappedTargetPhaseMismatch,
223 8 => Self::DistanceBelowDetectionThreshold,
224 9 => Self::Valid,
225 12 => Self::XTalkFail,
226 13 => Self::InterruptError,
227 18 => Self::InterruptError,
228 19 => Self::NoWrapAroundCheck,
229 22 => Self::MergedTarget,
230 23 => Self::SignalTooWeak,
231 _ => Self::Other,
232 }
233 }
234
235 /// Severity of this status as per the user manual.
236 pub const fn severity(&self) -> Severity {
237 match self {
238 Status::Valid => Severity::None,
239 Status::SigmaAboveThreshold => Severity::Warning,
240 Status::SigmaBelowThreshold => Severity::Warning,
241 Status::DistanceBelowDetectionThreshold => Severity::Error,
242 Status::InvalidPhase => Severity::Error,
243 Status::HardwareFail => Severity::Error,
244 Status::NoWrapAroundCheck => Severity::Warning,
245 Status::WrappedTargetPhaseMismatch => Severity::Error,
246 Status::ProcessingFail => Severity::Error,
247 Status::XTalkFail => Severity::Error,
248 Status::InterruptError => Severity::Error,
249 Status::MergedTarget => Severity::Error,
250 Status::SignalTooWeak => Severity::Error,
251 Status::Other => Severity::Error,
252 }
253 }
254}
255
256/// A VL53L4CD measurement.
257#[derive(Debug, Clone, Copy)]
258#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
259pub struct Measurement {
260 /// Validity of the measurement.
261 pub status: Status,
262 /// Measured distance to the target (millimeters).
263 pub distance: u16,
264 /// Ambient rate measurement performed on the
265 /// return array, with no active photon emission, to
266 /// measure the ambient signal rate due to noise.
267 ///
268 /// The returned value is specified in thousand counts
269 /// per second (kcps).
270 pub ambient_rate: u16,
271 /// Number of detected photons during the VCSEL pulse.
272 ///
273 /// The returned value is specified in thousand counts
274 /// per second (kcps).
275 pub signal_rate: u16,
276 /// Number of SPADs enabled for this measurement. Targets that
277 /// are far away or have low reflectance will activate
278 /// more SPADs.
279 pub spads_enabled: u16,
280 /// Sigma estimator for the noise in the reported
281 /// target distance (millimeters).
282 pub sigma: u16,
283}
284
285impl Measurement {
286 /// Whether this measurement is valid or not, given its status.
287 pub fn is_valid(&self) -> bool {
288 self.status == Status::Valid
289 }
290}
291
292/// A VL53L4CD ToF range sensor.
293pub struct Vl53l4cd<I2C, DELAY, WAIT> {
294 i2c: Device<I2C>,
295 delay: DELAY,
296 wait: WAIT,
297}
298
299impl<I2C: I2c, DELAY: DelayNs, WAIT: WaitForMeasurement<I2C, DELAY>> Vl53l4cd<I2C, DELAY, WAIT> {
300 /// Construct a new sensor with the default I²C address.
301 ///
302 /// See [`Vl53l4cd::with_addr`] and [`PERIPHERAL_ADDR`].
303 pub const fn new(bus: I2C, delay: DELAY, wait: WAIT) -> Self {
304 Self::with_addr(bus, PERIPHERAL_ADDR, delay, wait)
305 }
306
307 /// Construct a new sensor, without sending
308 /// any commands. To begin measuring, you
309 /// need to call [`Vl53l4cd::init`] as well as
310 /// [`Vl53l4cd::start_ranging`].
311 pub const fn with_addr(bus: I2C, addr: u8, delay: DELAY, wait: WAIT) -> Self {
312 Self {
313 i2c: Device { addr, bus },
314 delay,
315 wait,
316 }
317 }
318
319 /// Initialize the sensor.
320 ///
321 /// # Errors
322 ///
323 /// If the device id reported by the sensor isn't `0xebaa`, this
324 /// function returns an error. This is mostly done to prevent
325 /// strange I²C bugs where all returned bytes are zeroed.
326 pub async fn init(&mut self) -> Result<(), Error<I2C::Error>> {
327 let id = self
328 .i2c
329 .read_word(Register::IDENTIFICATION_MODEL_ID)
330 .await?;
331 if id != 0xebaa {
332 #[cfg(feature = "defmt-03")]
333 defmt::error!("strange device id {:#06x}", id);
334 return Err(Error::InvalidArgument);
335 }
336
337 #[cfg(feature = "defmt-03")]
338 defmt::debug!("waiting for boot");
339
340 self.wait_for_boot().await?;
341
342 #[cfg(feature = "defmt-03")]
343 defmt::debug!("booted");
344
345 self.i2c.write(DEFAULT_CONFIG_MSG).await?;
346
347 // start VHV
348 self.start_ranging().await?;
349 self.stop_ranging().await?;
350 self.i2c
351 .write_byte(Register::VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, 0x09)
352 .await?;
353 self.i2c.write_byte(Register::MYSTERY_1, 0).await?;
354 self.i2c.write_word(Register::MYSTERY_2, 0x500).await?;
355
356 self.set_range_timing(50, 0).await?;
357
358 Ok(())
359 }
360
361 async fn wait_for_boot(&mut self) -> Result<(), Error<I2C::Error>> {
362 for _ in 0u16..1000 {
363 if self.i2c.read_byte(Register::SYSTEM_STATUS).await? == 0x3 {
364 return Ok(());
365 }
366 self.delay.delay_ms(1).await;
367 }
368
369 #[cfg(feature = "defmt-03")]
370 defmt::error!("timeout waiting for boot");
371 Err(Error::Timeout)
372 }
373
374 /// Set the range timing for this sensor. The timing budget *must*
375 /// be greater than or equal to 10 ms and less than or equal to 200 ms.
376 /// From the manufacturer's user manual:
377 ///
378 /// > The range timing is a single function which allows the user to define the VCSEL timeout and the ranging
379 /// > frequency of the sensor. It is composed of two elements:
380 /// > * The Timing budget: It corresponds to the VCSEL enabled time. The user can choose a value between 10 ms
381 /// > and 200 ms. If the InterMeasurement is set to 0, the VCSEL is always enabled, so the TimingBudget is
382 /// > equal to the ranging period between consecutive measurements.
383 /// > * The InterMeasurement: It allows the user to define the time between two consecutive measurements.
384 /// > To use the InterMeasurement, the user needs to set a value greater than the TimingBudget. When the
385 /// > TimingBudget is consumed, the device goes into low power mode until the InterMeasurement is reached. A
386 /// > value set to 0 disables the InterMeasurement.
387 ///
388 /// # Errors
389 ///
390 /// If the oscillation frequency reported by the sensor (2 bytes starting
391 /// at [`OSC_FREQ`]) is zero, this function returns an error.
392 ///
393 /// # Panics
394 ///
395 /// Panics if the timing budget is less than 10 ms or more than 200 ms,
396 /// or if the timing budget is less than the inter-measurement time
397 /// (except when the inter-measurement time is zero).
398 ///
399 /// [`OSC_FREQ`]: Register#variant.OSC_FREQ
400 pub async fn set_range_timing(
401 &mut self,
402 timing_budget_ms: u32,
403 inter_measurement_ms: u32,
404 ) -> Result<(), Error<I2C::Error>> {
405 assert!(
406 (10..=200).contains(&timing_budget_ms),
407 "timing budget must be in range [10, 200]"
408 );
409
410 let osc_freq = self.i2c.read_word(Register::OSC_FREQ).await?;
411
412 if osc_freq == 0 {
413 #[cfg(feature = "defmt-03")]
414 defmt::error!("oscillation frequency is zero");
415 return Err(Error::InvalidArgument);
416 }
417
418 let mut timing_budget_us = timing_budget_ms * 1000;
419
420 if inter_measurement_ms == 0 {
421 // continuous mode
422 self.i2c
423 .write_dword(Register::INTERMEASUREMENT_MS, 0)
424 .await?;
425 timing_budget_us -= 2500;
426 } else {
427 assert!(
428 inter_measurement_ms <= timing_budget_ms,
429 "timing budget must be greater than or equal to inter-measurement"
430 );
431
432 // autonomous low power mode
433 let clock_pll = u32::from(
434 self.i2c
435 .read_word(Register::RESULT_OSC_CALIBRATE_VAL)
436 .await?
437 & 0x3ff,
438 );
439 let inter_measurement_fac = 1.055 * (inter_measurement_ms * clock_pll) as f32;
440 self.i2c
441 .write_dword(Register::INTERMEASUREMENT_MS, inter_measurement_fac as u32)
442 .await?;
443
444 timing_budget_us -= 4300;
445 timing_budget_us /= 2;
446 }
447
448 let (a, b) = range_config_values(timing_budget_us, osc_freq);
449
450 self.i2c.write_word(Register::RANGE_CONFIG_A, a).await?;
451 self.i2c.write_word(Register::RANGE_CONFIG_B, b).await?;
452
453 Ok(())
454 }
455
456 /// Wait for a measurement to be available on the sensor and then read
457 /// the measurement. This function polls the sensor for a measurement
458 /// until one is available, reads the measurement and finally clears
459 /// the interrupt in order to request another measurement.
460 pub async fn measure(&mut self) -> Result<Measurement, Error<I2C::Error>> {
461 self.wait_for_measurement().await?;
462
463 #[cfg(feature = "defmt-03")]
464 defmt::debug!("measurement ready; reading");
465
466 let measurement = self.read_measurement().await?;
467 self.clear_interrupt().await?;
468
469 Ok(measurement)
470 }
471
472 /// Adjust the sensor to prevent the measurements from deviating due to
473 /// ambient temperature variations. The ranging needs to be stopped with
474 /// [`Vl53l4cd::stop_ranging`] before calling this function.
475 ///
476 /// > Ambient temperature has an effect on ranging accuracy. In order to ensure the best performances, a temperature
477 /// > update needs to be applied to the sensor. This update needs to be performed when the temperature might have
478 /// > changed by more than 8 degrees Celsius.
479 pub async fn start_temperature_update(&mut self) -> Result<(), Error<I2C::Error>> {
480 self.i2c
481 .write_byte(Register::VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, 0x81)
482 .await?;
483 self.i2c.write_byte(Register::MYSTERY_1, 0x92).await?;
484 self.i2c.write_byte(Register::SYSTEM_START, 0x40).await?;
485
486 self.wait_for_measurement().await?;
487 self.clear_interrupt().await?;
488 self.stop_ranging().await?;
489
490 self.i2c
491 .write_byte(Register::VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, 0x09)
492 .await?;
493 self.i2c.write_byte(Register::MYSTERY_1, 0).await?;
494
495 Ok(())
496 }
497
498 /// Poll the sensor until a measurement is ready.
499 #[inline]
500 pub async fn wait_for_measurement(&mut self) -> Result<(), Error<I2C::Error>> {
501 #[cfg(feature = "defmt-03")]
502 defmt::debug!("waiting for measurement");
503
504 self.wait
505 .wait_for_measurement(&mut self.i2c, &mut self.delay)
506 .await
507 }
508
509 /// Check if the sensor has a measurement ready.
510 #[inline]
511 pub async fn has_measurement(&mut self) -> Result<bool, Error<I2C::Error>> {
512 Ok(wait::has_measurement(&mut self.i2c).await?)
513 }
514
515 /// *Use [`Vl53l4cd::measure`] unless you really like low-level.*
516 ///
517 /// Read the current measurement from the sensor. Wait for
518 /// [`Vl53l4cd::has_measurement`] to return true before running this so that
519 /// the measurement doesn't get overwritten halfway through you reading it.
520 /// Instruct the sensor to resume measuring with [`Vl53l4cd::clear_interrupt`]
521 /// afterwards.
522 pub async fn read_measurement(&mut self) -> Result<Measurement, Error<I2C::Error>> {
523 let status = self.i2c.read_byte(Register::RESULT_RANGE_STATUS).await? & 0x1f;
524
525 Ok(Measurement {
526 status: Status::from_rtn(status),
527 distance: self.i2c.read_word(Register::RESULT_DISTANCE).await?,
528 spads_enabled: self.i2c.read_word(Register::RESULT_NUM_SPADS).await? / 256,
529 ambient_rate: self.i2c.read_word(Register::RESULT_AMBIENT_RATE).await? * 8,
530 signal_rate: self.i2c.read_word(Register::RESULT_SIGNAL_RATE).await? * 8,
531 sigma: self.i2c.read_word(Register::RESULT_SIGMA).await? / 4,
532 })
533 }
534
535 /// Clear the interrupt which will eventually trigger a new measurement.
536 pub async fn clear_interrupt(&mut self) -> Result<(), Error<I2C::Error>> {
537 self.i2c
538 .write_byte(Register::SYSTEM_INTERRUPT_CLEAR, 0x01)
539 .await?;
540 Ok(())
541 }
542
543 /// Begin ranging.
544 pub async fn start_ranging(&mut self) -> Result<(), Error<I2C::Error>> {
545 if self.i2c.read_word(Register::INTERMEASUREMENT_MS).await? == 0 {
546 // autonomous mode
547 self.i2c.write_byte(Register::SYSTEM_START, 0x21).await?;
548 } else {
549 // continuous mode
550 self.i2c.write_byte(Register::SYSTEM_START, 0x40).await?;
551 }
552
553 self.wait_for_measurement().await?;
554 self.clear_interrupt().await
555 }
556
557 /// Stop ranging.
558 pub async fn stop_ranging(&mut self) -> Result<(), Error<I2C::Error>> {
559 self.i2c.write_byte(Register::SYSTEM_START, 0x00).await?;
560 Ok(())
561 }
562}
563
564/// Calculate valid values for [`Register::RANGE_CONFIG_A`] and
565/// [`Register::RANGE_CONFIG_B`].
566///
567/// ```
568/// let (a, b) = vl53l4cd::range_config_values(197500, 48250);
569///
570/// assert_eq!(a, 0x04fc);
571/// assert_eq!(b, 0x05a8);
572/// ```
573pub fn range_config_values(mut timing_budget_us: u32, osc_freq: u16) -> (u16, u16) {
574 // I didn't make these values up because I'm not a wizard.
575 // https://github.com/stm32duino/VL53L4CD/blob/b64ff4fa877c3cf156e11639e5fa305208dd3be9/src/vl53l4cd_api.cpp#L370
576
577 let macro_period_us = (2304 * (0x40000000 / u32::from(osc_freq))) >> 6;
578 timing_budget_us <<= 12;
579
580 let f = |x: u32| {
581 let mut ms_byte = 0;
582 let tmp = macro_period_us * x;
583 let mut ls_byte = ((timing_budget_us + (tmp >> 7)) / (tmp >> 6)) - 1;
584
585 while (ls_byte & 0xffffff00) > 0 {
586 ls_byte >>= 1;
587 ms_byte += 1;
588 }
589
590 (ms_byte << 8) | (ls_byte & 0xff) as u16
591 };
592
593 (f(16), f(12))
594}
595
596/// VL53L4CD driver error. In order to get more details,
597/// enable the `defmt` feature.
598#[derive(Debug)]
599pub enum Error<E> {
600 /// I²C (I/O) error.
601 I2c(E),
602
603 /// GPIO error.
604 Gpio,
605
606 /// Invalid argument, often as a result of an I/O
607 /// error.
608 InvalidArgument,
609
610 /// Timeout waiting for the sensor.
611 Timeout,
612}
613
614impl<E> From<E> for Error<E> {
615 fn from(e: E) -> Self {
616 Self::I2c(e)
617 }
618}
619
620impl<E: embedded_hal_async::i2c::Error> fmt::Display for Error<E> {
621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622 match self {
623 Error::I2c(err) => write!(f, "i2c error: {}", err.kind()),
624 Error::Gpio => write!(f, "gpio error"),
625 Error::InvalidArgument => write!(f, "invalid argument"),
626 Error::Timeout => write!(f, "timeout"),
627 }
628 }
629}
630
631#[cfg(feature = "std")]
632impl<E> std::error::Error for Error<E> where E: embedded_hal_async::i2c::Error {}