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 {}