Skip to main content

pamoja_sensors/tmp117/
driver.rs

1//! The TMP117 driven over I2C: identify, configure, and convert on demand.
2
3use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use pamoja_core::Sensor;
6
7use super::{
8    data_ready, device_id, eeprom_unlock_busy, raw_from_celsius, register, revision, Alerts,
9    Averaging, Configuration, ConversionMode, Reading, DEVICE_ID,
10};
11use crate::driver::{read_word, write_word};
12use crate::error::{DriverError, SensorError};
13
14/// How many times a status register is polled, 1 ms apart, before the part is called
15/// overdue: the EEPROM unlock register at initialization, for the power-on load to
16/// finish, and the configuration register after a conversion starts, for its
17/// data-ready flag. One conversion takes 15.5 ms typical and 17.5 ms at most
18/// (section 6.5), so 64 averaged conversions can outlast the typical wait by 128 ms;
19/// the polls cover that with room to spare.
20pub const STATUS_POLLS: u8 = 150;
21
22/// A TMP117 on an I2C bus, converting on demand in one-shot mode.
23///
24/// [`Tmp117::new`] wraps the bus. [`init`](Tmp117::init) checks the device id, waits
25/// for the EEPROM to finish loading, and writes the configuration with the part in
26/// shutdown and the chosen averaging. [`measure`](Tmp117::measure) starts one
27/// conversion, waits the conversion time for that averaging, polls the data-ready
28/// flag, and returns the temperature register as a [`Reading`]; the part powers
29/// itself down again once the conversion is done. The alert limits are written with
30/// [`set_alert_limits`](Tmp117::set_alert_limits) and their flags read with
31/// [`alerts`](Tmp117::alerts). As a [`Sensor`] its reading is the [`Reading`]; one
32/// unit is selected with [`Sensor::map`].
33///
34/// # Examples
35///
36/// An aquarium's water temperature, read through a TMP117 that is not plugged in yet:
37/// the simulated part holds 25.5 °C, and the driver waits out one conversion at the
38/// default averaging before it reads it.
39///
40/// ```
41/// use pamoja_core::Sensor;
42/// use pamoja_hal::script::{block_on, DelayLog};
43/// use pamoja_sensors::tmp117::{address, sim, Tmp117};
44///
45/// let part = sim::reporting(address::ADD0_GND, 25.5);
46/// let mut sensor = Tmp117::new(part, address::ADD0_GND, DelayLog::new());
47/// let reading = block_on(sensor.read())?;
48/// assert_eq!(reading.celsius(), 25.5);
49///
50/// let (_, delay) = sensor.release();
51/// assert_eq!(delay.total_micros(), 125_000, "eight averaged conversions");
52/// # Ok::<(), pamoja_core::Error>(())
53/// ```
54#[derive(Debug)]
55pub struct Tmp117<I2C, D> {
56    bus: I2C,
57    address: u8,
58    delay: D,
59    averaging: Averaging,
60    revision: Option<u8>,
61    pending: Alerts,
62}
63
64impl<I2C, D> Tmp117<I2C, D> {
65    /// Wraps an I2C bus and the part's address.
66    ///
67    /// # Arguments
68    ///
69    /// * `bus` - the I2C bus the part is on.
70    /// * `address` - one of the [`address`](super::address) constants, chosen by
71    ///   where the ADD0 pin is tied.
72    /// * `delay` - the timer that paces the conversions.
73    ///
74    /// # Returns
75    ///
76    /// The driver, averaging 8 conversions into each result, the factory setting.
77    pub fn new(bus: I2C, address: u8, delay: D) -> Self {
78        Tmp117 {
79            bus,
80            address,
81            delay,
82            averaging: Averaging::X8,
83            revision: None,
84            pending: Alerts::default(),
85        }
86    }
87
88    /// Sets how many conversions are averaged into each result.
89    ///
90    /// The setting is written to the part by [`init`](Tmp117::init) and with every
91    /// conversion; more averaging lengthens the conversion, see
92    /// [`Averaging::conversion_micros`].
93    ///
94    /// # Arguments
95    ///
96    /// * `averaging` - the number of conversions per result.
97    ///
98    /// # Returns
99    ///
100    /// The driver.
101    pub fn with_averaging(mut self, averaging: Averaging) -> Self {
102        self.averaging = averaging;
103        self
104    }
105
106    /// Returns the silicon revision read at initialization, if it has happened.
107    pub fn revision(&self) -> Option<u8> {
108        self.revision
109    }
110
111    /// Gives back the bus and the delay.
112    ///
113    /// # Returns
114    ///
115    /// The bus and delay the driver was built from.
116    pub fn release(self) -> (I2C, D) {
117        (self.bus, self.delay)
118    }
119}
120
121impl<I2C: I2c, D: DelayNs> Tmp117<I2C, D> {
122    /// Checks the part's identity, waits for its EEPROM to finish loading, and writes
123    /// the settings with the part in shutdown.
124    ///
125    /// The identity is the device id field of the Device_ID register (section
126    /// 7.6.11). The EEPROM_Busy flag of the EEPROM unlock register is then polled
127    /// until it clears, because the part ignores writes to the configuration and limit
128    /// registers while its power-on load runs (section 7.5.1.1). The configuration
129    /// register (Table 7-6) is written last, with the shutdown mode and the averaging:
130    /// that ends any continuous conversion and leaves the part powered down between
131    /// the one-shot conversions [`measure`](Tmp117::measure) starts (sections 7.4.2
132    /// and 7.4.3).
133    ///
134    /// # Errors
135    ///
136    /// Returns [`DriverError::Bus`] if the bus fails, [`DriverError::Sensor`] with
137    /// [`SensorError::Identity`] if the device id is not a TMP117's, and
138    /// [`DriverError::Timeout`] if the EEPROM never reports ready.
139    pub fn init(&mut self) -> Result<(), DriverError<I2C::Error>> {
140        let id = self.read(register::DEVICE_ID)?;
141        if device_id(id) != DEVICE_ID {
142            return Err(SensorError::Identity.into());
143        }
144        self.wait(register::EEPROM_UL, eeprom_unlock_busy)?;
145        self.write(
146            register::CONFIGURATION,
147            self.configuration(ConversionMode::Shutdown).bits(),
148        )?;
149        self.revision = Some(revision(id));
150        Ok(())
151    }
152
153    /// Runs one conversion and returns the temperature.
154    ///
155    /// Initializes the part first if [`init`](Tmp117::init) has not run. The
156    /// configuration register is written with the one-shot mode and the averaging,
157    /// which starts the conversion (section 7.4.3), and read back once so that a
158    /// Data_Ready flag left over from an earlier conversion is cleared rather than
159    /// trusted, since only a read clears it (Table 7-6). The wait is the conversion
160    /// time for the averaging in use, the no-standby cycle of Table 7-7, after which
161    /// the configuration register is polled until Data_Ready is set and the
162    /// temperature register is read (section 7.6.2).
163    ///
164    /// # Returns
165    ///
166    /// The temperature register as a [`Reading`].
167    ///
168    /// # Errors
169    ///
170    /// Returns [`DriverError::Bus`] if the bus fails, [`DriverError::Timeout`] if the
171    /// part never reports data ready, or any error of [`init`](Tmp117::init).
172    pub fn measure(&mut self) -> Result<Reading, DriverError<I2C::Error>> {
173        if self.revision.is_none() {
174            self.init()?;
175        }
176        self.write(
177            register::CONFIGURATION,
178            self.configuration(ConversionMode::OneShot).bits(),
179        )?;
180        let stale = self.read(register::CONFIGURATION)?;
181        self.pending = self.pending | Alerts::from_bits(stale);
182        self.delay.delay_us(self.averaging.conversion_micros());
183        let status = self.wait(register::CONFIGURATION, |config| !data_ready(config))?;
184        self.pending = self.pending | Alerts::from_bits(status);
185        let word = self.read(register::TEMP_RESULT)?;
186        Ok(Reading::new(word as i16))
187    }
188
189    /// Writes the high and low limit registers.
190    ///
191    /// The limits share the temperature register's format, 7.8125 m°C per count over
192    /// ±256 °C (sections 7.6.4 and 7.6.5); each is rounded to the nearest count and
193    /// saturated at the register range. At the end of every conversion the part sets
194    /// HIGH_Alert when the result is above the high limit and LOW_Alert when it is
195    /// below the low limit (section 7.4.4.1); [`alerts`](Tmp117::alerts) reads them.
196    ///
197    /// # Arguments
198    ///
199    /// * `high_celsius` - the high limit in degrees Celsius; the factory value is
200    ///   192 °C.
201    /// * `low_celsius` - the low limit in degrees Celsius; the factory value is
202    ///   -256 °C.
203    ///
204    /// # Errors
205    ///
206    /// Returns [`DriverError::Bus`] if the bus fails.
207    pub fn set_alert_limits(
208        &mut self,
209        high_celsius: f32,
210        low_celsius: f32,
211    ) -> Result<(), DriverError<I2C::Error>> {
212        self.write(register::THIGH_LIMIT, raw_from_celsius(high_celsius) as u16)?;
213        self.write(register::TLOW_LIMIT, raw_from_celsius(low_celsius) as u16)
214    }
215
216    /// Reads the HIGH_Alert and LOW_Alert flags: whether a result since the previous
217    /// call was above the high limit or below the low limit.
218    ///
219    /// The part sets the flags at the end of every conversion and, in alert mode,
220    /// clears them whenever the configuration register is read (Table 7-6), the reads
221    /// [`measure`](Tmp117::measure) makes to see a conversion finish included. The
222    /// driver keeps the flags those reads consumed, folds them into this answer, and
223    /// forgets them, so the register's read-to-clear behavior holds across the
224    /// driver's own reads.
225    ///
226    /// # Returns
227    ///
228    /// The two flags.
229    ///
230    /// # Errors
231    ///
232    /// Returns [`DriverError::Bus`] if the bus fails.
233    pub fn alerts(&mut self) -> Result<Alerts, DriverError<I2C::Error>> {
234        let live = Alerts::from_bits(self.read(register::CONFIGURATION)?);
235        Ok(live | core::mem::take(&mut self.pending))
236    }
237
238    fn configuration(&self, mode: ConversionMode) -> Configuration {
239        Configuration {
240            mode,
241            averaging: self.averaging,
242            ..Configuration::default()
243        }
244    }
245
246    fn read(&mut self, register: u8) -> Result<u16, DriverError<I2C::Error>> {
247        read_word(&mut self.bus, self.address, register).map_err(DriverError::Bus)
248    }
249
250    fn write(&mut self, register: u8, value: u16) -> Result<(), DriverError<I2C::Error>> {
251        write_word(&mut self.bus, self.address, register, value).map_err(DriverError::Bus)
252    }
253
254    fn wait(
255        &mut self,
256        register: u8,
257        busy: fn(u16) -> bool,
258    ) -> Result<u16, DriverError<I2C::Error>> {
259        for _ in 0..STATUS_POLLS {
260            let word = self.read(register)?;
261            if !busy(word) {
262                return Ok(word);
263            }
264            self.delay.delay_ms(1);
265        }
266        Err(DriverError::Timeout)
267    }
268}
269
270impl<I2C, D> Sensor for Tmp117<I2C, D>
271where
272    I2C: I2c + Send,
273    I2C::Error: core::fmt::Debug,
274    D: DelayNs + Send,
275{
276    type Reading = Reading;
277
278    async fn read(&mut self) -> pamoja_core::Result<Reading> {
279        self.measure().map_err(pamoja_core::Error::from)
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286    use crate::tmp117::address::{ADD0_GND, ADD0_SCL};
287    use alloc::vec;
288    use alloc::vec::Vec;
289    use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
290    use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep};
291
292    // Table 7-15: revision 0, device id 117h.
293    const ID: [u8; 2] = [0x01, 0x17];
294    // Table 7-10: EUN clear and EEPROM_Busy clear.
295    const EEPROM_READY: [u8; 2] = [0x00, 0x00];
296    // Table 7-1: 25 °C is 0C80h.
297    const AT_25_C: [u8; 2] = [0x0C, 0x80];
298    // Table 7-6: Data_Ready is bit 13, HIGH_Alert bit 15, LOW_Alert bit 14.
299    const DATA_READY: u16 = 1 << 13;
300    const HIGH_ALERT: u16 = 1 << 15;
301    const LOW_ALERT: u16 = 1 << 14;
302
303    fn config(mode: ConversionMode, averaging: Averaging) -> u16 {
304        Configuration {
305            mode,
306            averaging,
307            ..Configuration::default()
308        }
309        .bits()
310    }
311
312    fn write(address: u8, register: u8, value: u16) -> I2cStep {
313        let [high, low] = value.to_be_bytes();
314        I2cStep::write(address, [register, high, low])
315    }
316
317    fn read(address: u8, register: u8, value: u16) -> I2cStep {
318        I2cStep::write_read(address, [register], value.to_be_bytes())
319    }
320
321    fn init_steps(address: u8, averaging: Averaging) -> Vec<I2cStep> {
322        vec![
323            I2cStep::write_read(address, [register::DEVICE_ID], ID),
324            I2cStep::write_read(address, [register::EEPROM_UL], EEPROM_READY),
325            write(
326                address,
327                register::CONFIGURATION,
328                config(ConversionMode::Shutdown, averaging),
329            ),
330        ]
331    }
332
333    // The one-shot write, the read that clears a stale flag, one poll answered with
334    // `status`, and the result.
335    fn measure_steps(
336        address: u8,
337        averaging: Averaging,
338        status: u16,
339        result: [u8; 2],
340    ) -> Vec<I2cStep> {
341        let one_shot = config(ConversionMode::OneShot, averaging);
342        vec![
343            write(address, register::CONFIGURATION, one_shot),
344            read(address, register::CONFIGURATION, one_shot),
345            read(address, register::CONFIGURATION, status),
346            I2cStep::write_read(address, [register::TEMP_RESULT], result),
347        ]
348    }
349
350    fn ready(averaging: Averaging) -> u16 {
351        DATA_READY | config(ConversionMode::Shutdown, averaging)
352    }
353
354    #[test]
355    fn init_follows_the_datasheet_sequence_and_measure_reads_the_result() {
356        let mut steps = init_steps(ADD0_GND, Averaging::X8);
357        steps.extend(measure_steps(
358            ADD0_GND,
359            Averaging::X8,
360            ready(Averaging::X8),
361            AT_25_C,
362        ));
363        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
364        assert_eq!(sensor.revision(), None);
365        sensor.init().unwrap();
366        assert_eq!(sensor.revision(), Some(0));
367
368        let reading = sensor.measure().unwrap();
369        assert_eq!(reading, Reading::new(0x0C80));
370        assert_eq!(reading.celsius(), 25.0);
371
372        let (bus, delay) = sensor.release();
373        assert!(bus.done());
374        assert_eq!(
375            delay.waits_ns(),
376            [125_000 * 1_000],
377            "8 averages convert in 125 ms"
378        );
379    }
380
381    #[test]
382    fn the_averaging_reaches_the_configuration_and_sets_the_wait() {
383        for averaging in [
384            Averaging::None,
385            Averaging::X8,
386            Averaging::X32,
387            Averaging::X64,
388        ] {
389            let mut steps = init_steps(ADD0_SCL, averaging);
390            steps.extend(measure_steps(
391                ADD0_SCL,
392                averaging,
393                ready(averaging),
394                AT_25_C,
395            ));
396            let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_SCL, DelayLog::new())
397                .with_averaging(averaging);
398            assert_eq!(sensor.measure().unwrap().celsius(), 25.0);
399            let (bus, delay) = sensor.release();
400            assert!(bus.done());
401            assert_eq!(delay.waits_ns(), [averaging.conversion_micros() * 1_000]);
402        }
403        // Table 7-6: MOD 11, CONV 100, AVG 11; Table 7-7: 64 averages take 1 s.
404        assert_eq!(config(ConversionMode::OneShot, Averaging::X64), 0x0E60);
405        assert_eq!(Averaging::X64.conversion_micros(), 1_000_000);
406    }
407
408    #[test]
409    fn a_stale_data_ready_flag_is_cleared_before_the_wait_and_the_poll_repeats() {
410        let one_shot = config(ConversionMode::OneShot, Averaging::X8);
411        let mut steps = init_steps(ADD0_GND, Averaging::X8);
412        steps.extend([
413            write(ADD0_GND, register::CONFIGURATION, one_shot),
414            read(ADD0_GND, register::CONFIGURATION, DATA_READY | one_shot),
415            read(ADD0_GND, register::CONFIGURATION, one_shot),
416            read(ADD0_GND, register::CONFIGURATION, one_shot),
417            read(ADD0_GND, register::CONFIGURATION, ready(Averaging::X8)),
418            I2cStep::write_read(ADD0_GND, [register::TEMP_RESULT], AT_25_C),
419        ]);
420        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
421        assert_eq!(sensor.measure().unwrap().celsius(), 25.0);
422        let (bus, delay) = sensor.release();
423        assert!(bus.done());
424        assert_eq!(delay.waits_ns(), [125_000_000, 1_000_000, 1_000_000]);
425    }
426
427    #[test]
428    fn a_part_that_is_not_a_tmp117_is_refused_and_the_revision_is_kept() {
429        let steps = [I2cStep::write_read(
430            ADD0_GND,
431            [register::DEVICE_ID],
432            [0x01, 0x16],
433        )];
434        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
435        assert_eq!(
436            sensor.init(),
437            Err(DriverError::Sensor(SensorError::Identity))
438        );
439        assert_eq!(sensor.revision(), None);
440        assert!(sensor.release().0.done());
441
442        // Table 7-15: Rev[3:0] sits above DID[11:0], so a later revision still passes.
443        let mut steps = init_steps(ADD0_GND, Averaging::X8);
444        steps[0] = I2cStep::write_read(ADD0_GND, [register::DEVICE_ID], [0x21, 0x17]);
445        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
446        sensor.init().unwrap();
447        assert_eq!(sensor.revision(), Some(2));
448        assert!(sensor.release().0.done());
449    }
450
451    #[test]
452    fn a_conversion_that_never_finishes_is_a_timeout() {
453        let one_shot = config(ConversionMode::OneShot, Averaging::X8);
454        let mut steps = init_steps(ADD0_GND, Averaging::X8);
455        steps.push(write(ADD0_GND, register::CONFIGURATION, one_shot));
456        steps.push(read(ADD0_GND, register::CONFIGURATION, one_shot));
457        for _ in 0..STATUS_POLLS {
458            steps.push(read(ADD0_GND, register::CONFIGURATION, one_shot));
459        }
460        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
461        assert_eq!(sensor.measure(), Err(DriverError::Timeout));
462        let (bus, delay) = sensor.release();
463        assert!(bus.done());
464        assert_eq!(delay.waits_ns().len(), 1 + usize::from(STATUS_POLLS));
465        assert_eq!(delay.waits_ns()[0], 125_000_000);
466        assert_eq!(
467            delay.total_micros(),
468            125_000 + 1_000 * u64::from(STATUS_POLLS)
469        );
470    }
471
472    #[test]
473    fn an_eeprom_that_never_finishes_loading_is_a_timeout() {
474        // Table 7-10: EEPROM_Busy is bit 14 of the unlock register.
475        let mut steps = vec![I2cStep::write_read(ADD0_GND, [register::DEVICE_ID], ID)];
476        for _ in 0..STATUS_POLLS {
477            steps.push(read(ADD0_GND, register::EEPROM_UL, 1 << 14));
478        }
479        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
480        assert_eq!(sensor.init(), Err(DriverError::Timeout));
481        assert_eq!(sensor.revision(), None);
482        let (bus, delay) = sensor.release();
483        assert!(bus.done());
484        assert_eq!(delay.total_millis(), u64::from(STATUS_POLLS));
485    }
486
487    #[test]
488    fn a_bus_fault_surfaces_as_a_bus_error_and_maps_to_the_core_io_error() {
489        let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
490        let steps = [I2cStep::fault(ADD0_GND, kind)];
491        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
492        let error = sensor.init().unwrap_err();
493        assert!(matches!(error, DriverError::Bus(_)));
494        let core: pamoja_core::Error = error.into();
495        assert!(matches!(core, pamoja_core::Error::Io(_)));
496        assert!(sensor.release().0.done());
497    }
498
499    #[test]
500    fn as_a_sensor_it_reads_and_maps_to_one_channel() {
501        let mut steps = init_steps(ADD0_GND, Averaging::X8);
502        // Table 7-1: -25 °C is F380h.
503        steps.extend(measure_steps(
504            ADD0_GND,
505            Averaging::X8,
506            ready(Averaging::X8),
507            [0xF3, 0x80],
508        ));
509        let sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
510        let mut celsius = sensor.map(|reading| reading.celsius());
511        assert_eq!(block_on(celsius.read()).unwrap(), -25.0);
512        assert!(celsius.into_inner().release().0.done());
513    }
514
515    #[test]
516    fn alert_limits_are_written_in_register_format_and_flags_survive_the_polls() {
517        let shutdown = config(ConversionMode::Shutdown, Averaging::X8);
518        let mut steps = init_steps(ADD0_GND, Averaging::X8);
519        // Table 7-1's format: 30 °C is 3840 counts, 0F00h; -10 °C is -1280, FB00h.
520        steps.push(write(ADD0_GND, register::THIGH_LIMIT, 0x0F00));
521        steps.push(write(ADD0_GND, register::TLOW_LIMIT, 0xFB00));
522        // 32 °C, above the high limit, so the poll that sees the conversion finish
523        // also sees HIGH_Alert and clears it.
524        steps.extend(measure_steps(
525            ADD0_GND,
526            Averaging::X8,
527            HIGH_ALERT | ready(Averaging::X8),
528            [0x10, 0x00],
529        ));
530        steps.push(read(ADD0_GND, register::CONFIGURATION, shutdown));
531        steps.push(read(ADD0_GND, register::CONFIGURATION, shutdown));
532        steps.push(read(
533            ADD0_GND,
534            register::CONFIGURATION,
535            LOW_ALERT | shutdown,
536        ));
537        let mut sensor = Tmp117::new(I2cScript::new(steps), ADD0_GND, DelayLog::new());
538        sensor.init().unwrap();
539        sensor.set_alert_limits(30.0, -10.0).unwrap();
540        assert_eq!(sensor.measure().unwrap().celsius(), 32.0);
541        assert_eq!(
542            sensor.alerts().unwrap(),
543            Alerts {
544                high: true,
545                low: false
546            }
547        );
548        assert_eq!(sensor.alerts().unwrap(), Alerts::default());
549        assert_eq!(
550            sensor.alerts().unwrap(),
551            Alerts {
552                high: false,
553                low: true
554            }
555        );
556        assert!(sensor.release().0.done());
557    }
558
559    #[test]
560    fn init_and_one_conversion_make_the_transfers_the_datasheet_gives() {
561        const PART: u8 = ADD0_GND;
562        let bus = I2cScript::new([
563            I2cStep::write_read(PART, [0x0F], [0x01, 0x17]),
564            I2cStep::write_read(PART, [0x04], [0x00, 0x00]),
565            I2cStep::write(PART, [0x01, 0x06, 0x20]),
566            I2cStep::write(PART, [0x01, 0x0E, 0x20]),
567            I2cStep::write_read(PART, [0x01], [0x0E, 0x20]),
568            I2cStep::write_read(PART, [0x01], [0x26, 0x20]),
569            I2cStep::write_read(PART, [0x00], [0x0C, 0x80]),
570        ]);
571
572        let mut sensor = Tmp117::new(bus, PART, DelayLog::new());
573        let reading = block_on(Sensor::read(&mut sensor)).expect("the scripted part answers");
574        assert_eq!(reading.celsius(), 25.0);
575
576        let (bus, delay) = sensor.release();
577        assert!(bus.done());
578        assert_eq!(delay.total_micros(), 125_000);
579    }
580}