diff --git a/ci.sh b/ci.sh index e27ada508..5a438f0b1 100755 --- a/ci.sh +++ b/ci.sh @@ -324,7 +324,6 @@ DEFMT_RTT_BUFFER_SIZE="72" cargo batch \ # temporarily disabled, these boards are dead. rm -rf out/tests/stm32f103c8 -rm -rf out/tests/stm32l073rz rm -rf out/tests/nrf52840-dk rm -rf out/tests/nrf52833-dk diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 083b54b99..3d5e841d1 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -4,7 +4,6 @@ use core::future::{poll_fn, Future}; use core::marker::PhantomData; -use core::mem::MaybeUninit; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -17,7 +16,7 @@ use embassy_time::{Duration, Instant}; use embedded_hal_1::i2c::Operation; pub use pac::twim::vals::Frequency; -use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; +use crate::chip::EASY_DMA_SIZE; use crate::gpio::Pin as GpioPin; use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; @@ -75,8 +74,8 @@ pub enum Error { Transmit, /// Data reception failed. Receive, - /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. - BufferNotInRAM, + /// The buffer is not in data RAM and is larger than the RAM buffer. It's most likely in flash, and nRF's DMA cannot access flash. + RAMBufferTooSmall, /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly. AddressNack, /// Didn't receive an ACK bit after a data byte. @@ -115,16 +114,24 @@ impl interrupt::typelevel::Handler for InterruptHandl /// TWI driver. pub struct Twim<'d, T: Instance> { _p: Peri<'d, T>, + tx_ram_buffer: &'d mut [u8], } impl<'d, T: Instance> Twim<'d, T> { /// Create a new TWI driver. + /// + /// `tx_ram_buffer` is required if any write operations will be performed with data that is not in RAM. + /// Usually this is static data that the compiler locates in flash instead of RAM. The `tx_ram_buffer` + /// needs to be at least as large as the largest write operation that will be executed with a buffer + /// that is not in RAM. If all write operations will be performed from RAM, an empty buffer (`&[]`) may + /// be used. pub fn new( twim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sda: Peri<'d, impl GpioPin>, scl: Peri<'d, impl GpioPin>, config: Config, + tx_ram_buffer: &'d mut [u8], ) -> Self { let r = T::regs(); @@ -159,7 +166,10 @@ impl<'d, T: Instance> Twim<'d, T> { // Enable TWIM instance. r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - let mut twim = Self { _p: twim }; + let mut twim = Self { + _p: twim, + tx_ram_buffer, + }; // Apply runtime peripheral configuration Self::set_config(&mut twim, &config).unwrap(); @@ -174,21 +184,17 @@ impl<'d, T: Instance> Twim<'d, T> { } /// Set TX buffer, checking that it is in RAM and has suitable length. - unsafe fn set_tx_buffer( - &mut self, - buffer: &[u8], - ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, - ) -> Result<(), Error> { + unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { let buffer = if slice_in_ram(buffer) { buffer } else { - let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?; + if buffer.len() > self.tx_ram_buffer.len() { + return Err(Error::RAMBufferTooSmall); + } trace!("Copying TWIM tx buffer into RAM for DMA"); - let ram_buffer = &mut ram_buffer[..buffer.len()]; - // Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer) - let uninit_src: &[MaybeUninit] = unsafe { core::mem::transmute(buffer) }; - ram_buffer.copy_from_slice(uninit_src); - unsafe { &*(ram_buffer as *const [MaybeUninit] as *const [u8]) } + let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()]; + ram_buffer.copy_from_slice(buffer); + &*ram_buffer }; if buffer.len() > EASY_DMA_SIZE { @@ -358,7 +364,6 @@ impl<'d, T: Instance> Twim<'d, T> { &mut self, address: u8, operations: &mut [Operation<'_>], - tx_ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, last_op: Option<&Operation<'_>>, inten: bool, ) -> Result { @@ -397,7 +402,7 @@ impl<'d, T: Instance> Twim<'d, T> { // Set up DMA buffers. unsafe { - self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; + self.set_tx_buffer(wr_buffer)?; self.set_rx_buffer(rd_buffer)?; } @@ -450,7 +455,7 @@ impl<'d, T: Instance> Twim<'d, T> { { // Set up DMA buffers. unsafe { - self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; + self.set_tx_buffer(wr_buffer)?; self.set_rx_buffer(rd_buffer)?; } @@ -472,7 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> { // Set up DMA buffers. unsafe { - self.set_tx_buffer(buffer, tx_ram_buffer)?; + self.set_tx_buffer(buffer)?; } // Start write operation. @@ -539,28 +544,9 @@ impl<'d, T: Instance> Twim<'d, T> { /// An `Operation::Write` following an `Operation::Read` must have a /// non-empty buffer. pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { - let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; let mut last_op = None; while !operations.is_empty() { - let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; - let (in_progress, rest) = operations.split_at_mut(ops); - self.blocking_wait(); - self.check_operations(in_progress)?; - last_op = in_progress.last(); - operations = rest; - } - Ok(()) - } - - /// Same as [`blocking_transaction`](Twim::blocking_transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_transaction_from_ram( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - ) -> Result<(), Error> { - let mut last_op = None; - while !operations.is_empty() { - let ops = self.setup_operations(address, operations, None, last_op, false)?; + let ops = self.setup_operations(address, operations, last_op, false)?; let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait(); self.check_operations(in_progress)?; @@ -580,30 +566,9 @@ impl<'d, T: Instance> Twim<'d, T> { mut operations: &mut [Operation<'_>], timeout: Duration, ) -> Result<(), Error> { - let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; let mut last_op = None; while !operations.is_empty() { - let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; - let (in_progress, rest) = operations.split_at_mut(ops); - self.blocking_wait_timeout(timeout)?; - self.check_operations(in_progress)?; - last_op = in_progress.last(); - operations = rest; - } - Ok(()) - } - - /// Same as [`blocking_transaction_timeout`](Twim::blocking_transaction_timeout) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - #[cfg(feature = "time")] - pub fn blocking_transaction_from_ram_timeout( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - timeout: Duration, - ) -> Result<(), Error> { - let mut last_op = None; - while !operations.is_empty() { - let ops = self.setup_operations(address, operations, None, last_op, false)?; + let ops = self.setup_operations(address, operations, last_op, false)?; let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait_timeout(timeout)?; self.check_operations(in_progress)?; @@ -624,28 +589,9 @@ impl<'d, T: Instance> Twim<'d, T> { /// An `Operation::Write` following an `Operation::Read` must have a /// non-empty buffer. pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { - let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; let mut last_op = None; while !operations.is_empty() { - let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, true)?; - let (in_progress, rest) = operations.split_at_mut(ops); - self.async_wait().await?; - self.check_operations(in_progress)?; - last_op = in_progress.last(); - operations = rest; - } - Ok(()) - } - - /// Same as [`transaction`](Twim::transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn transaction_from_ram( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - ) -> Result<(), Error> { - let mut last_op = None; - while !operations.is_empty() { - let ops = self.setup_operations(address, operations, None, last_op, true)?; + let ops = self.setup_operations(address, operations, last_op, true)?; let (in_progress, rest) = operations.split_at_mut(ops); self.async_wait().await?; self.check_operations(in_progress)?; @@ -665,11 +611,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction(address, &mut [Operation::Write(buffer)]) } - /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)]) - } - /// Read from an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 @@ -687,16 +628,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) } - /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_read_from_ram( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - ) -> Result<(), Error> { - self.blocking_transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) - } - // =========================================== /// Write to an I2C slave with timeout. @@ -707,17 +638,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout) } - /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - #[cfg(feature = "time")] - pub fn blocking_write_from_ram_timeout( - &mut self, - address: u8, - buffer: &[u8], - timeout: Duration, - ) -> Result<(), Error> { - self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], timeout) - } - /// Read from an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 @@ -747,22 +667,6 @@ impl<'d, T: Instance> Twim<'d, T> { ) } - /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - #[cfg(feature = "time")] - pub fn blocking_write_read_from_ram_timeout( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - timeout: Duration, - ) -> Result<(), Error> { - self.blocking_transaction_from_ram_timeout( - address, - &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - timeout, - ) - } - // =========================================== /// Read from an I2C slave. @@ -781,12 +685,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.transaction(address, &mut [Operation::Write(buffer)]).await } - /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.transaction_from_ram(address, &mut [Operation::Write(buffer)]) - .await - } - /// Write data to an I2C slave, then read data from the slave without /// triggering a stop condition between the two. /// @@ -796,17 +694,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) .await } - - /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_read_from_ram( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - ) -> Result<(), Error> { - self.transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) - .await - } } impl<'a, T: Instance> Drop for Twim<'a, T> { @@ -904,7 +791,7 @@ impl embedded_hal_1::i2c::Error for Error { Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, - Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other, + Self::RAMBufferTooSmall => embedded_hal_1::i2c::ErrorKind::Other, Self::AddressNack => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) } diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs index 00783aab0..287ddab41 100644 --- a/embassy-rp/src/pio_programs/onewire.rs +++ b/embassy-rp/src/pio_programs/onewire.rs @@ -1,11 +1,17 @@ //! OneWire pio driver -use crate::pio::{Common, Config, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use crate::clocks::clk_sys_freq; +use crate::gpio::Level; +use crate::pio::{ + Common, Config, Direction, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; use crate::Peri; -/// This struct represents an onewire driver program +/// This struct represents a onewire driver program pub struct PioOneWireProgram<'a, PIO: Instance> { prg: LoadedProgram<'a, PIO>, + reset_addr: u8, + next_bit_addr: u8, } impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { @@ -13,56 +19,67 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { pub fn new(common: &mut Common<'a, PIO>) -> Self { let prg = pio::pio_asm!( r#" - .wrap_target - again: - pull block - mov x, osr - jmp !x, read - write: - set pindirs, 1 - set pins, 0 - loop1: - jmp x--,loop1 - set pindirs, 0 [31] - wait 1 pin 0 [31] - pull block - mov x, osr - bytes1: - pull block - set y, 7 - set pindirs, 1 - bit1: - set pins, 0 [1] - out pins,1 [31] - set pins, 1 [20] - jmp y--,bit1 - jmp x--,bytes1 - set pindirs, 0 [31] - jmp again - read: - pull block - mov x, osr - bytes2: - set y, 7 - bit2: - set pindirs, 1 - set pins, 0 [1] - set pindirs, 0 [5] - in pins,1 [10] - jmp y--,bit2 - jmp x--,bytes2 - .wrap - "#, - ); - let prg = common.load_program(&prg.program); + ; We need to use the pins direction to simulate open drain output + ; This results in all the side-set values being swapped from the actual pin value + .side_set 1 pindirs - Self { prg } + ; Set the origin to 0 so we can correctly use jmp instructions externally + .origin 0 + + ; Tick rate is 1 tick per 6us, so all delays should be calculated back to that + ; All the instructions have a calculated delay XX in us as [(XX / CLK) - 1]. + ; The - 1 is for the instruction which also takes one clock cyle. + ; The delay can be 0 which will result in just 6us for the instruction itself + .define CLK 6 + + ; Write the reset block after trigger + public reset: + set x, 4 side 0 [(60 / CLK) - 1] ; idle before reset + reset_inner: ; Repeat the following 5 times, so 5*96us = 480us in total + nop side 1 [(90 / CLK) - 1] + jmp x--, reset_inner side 1 [( 6 / CLK) - 1] + ; Fallthrough + + ; Check for presence of one or more devices. + ; This samples 32 times with an interval of 12us after a 18us delay. + ; If any bit is zero in the end value, there is a detection + ; This whole function takes 480us + set x, 31 side 0 [(24 / CLK) - 1] ; Loop 32 times -> 32*12us = 384us + presence_check: + in pins, 1 side 0 [( 6 / CLK) - 1] ; poll pin and push to isr + jmp x--, presence_check side 0 [( 6 / CLK) - 1] + jmp next_bit side 0 [(72 / CLK) - 1] + + ; The low pulse was already done, we only need to delay and poll the bit in case we are reading + write_1: + nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin + in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR + ; Fallthrough + + ; This is the entry point when reading and writing data + public next_bit: + .wrap_target + out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR + jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit + in null, 1 side 1 [(54 / CLK) - 1] ; Do the remainder of the low part of a 0 bit + ; This writes 0 into the ISR so that the shift count stays in sync + .wrap + "# + ); + + Self { + prg: common.load_program(&prg.program), + reset_addr: prg.public_defines.reset as u8, + next_bit_addr: prg.public_defines.next_bit as u8, + } } } - /// Pio backed OneWire driver pub struct PioOneWire<'d, PIO: Instance, const SM: usize> { sm: StateMachine<'d, PIO, SM>, + cfg: Config<'d, PIO>, + reset_addr: u8, + next_bit_addr: u8, } impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { @@ -74,37 +91,206 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { program: &PioOneWireProgram<'d, PIO>, ) -> Self { let pin = common.make_pio_pin(pin); + + sm.set_pin_dirs(Direction::In, &[&pin]); + sm.set_pins(Level::Low, &[&pin]); + let mut cfg = Config::default(); - cfg.use_program(&program.prg, &[]); - cfg.set_out_pins(&[&pin]); + cfg.use_program(&program.prg, &[&pin]); cfg.set_in_pins(&[&pin]); - cfg.set_set_pins(&[&pin]); - cfg.shift_in = ShiftConfig { + + let shift_cfg = ShiftConfig { auto_fill: true, direction: ShiftDirection::Right, threshold: 8, }; - cfg.clock_divider = 255_u8.into(); + cfg.shift_in = shift_cfg; + cfg.shift_out = shift_cfg; + + let divider = (clk_sys_freq() / 1000000) as u16 * 6; + cfg.clock_divider = divider.into(); + sm.set_config(&cfg); + sm.clear_fifos(); + sm.restart(); + unsafe { + sm.exec_jmp(program.next_bit_addr); + } sm.set_enable(true); - Self { sm } - } - /// Write bytes over the wire - pub async fn write_bytes(&mut self, bytes: &[u8]) { - self.sm.tx().wait_push(250).await; - self.sm.tx().wait_push(bytes.len() as u32 - 1).await; - for b in bytes { - self.sm.tx().wait_push(*b as u32).await; + Self { + sm, + cfg, + reset_addr: program.reset_addr, + next_bit_addr: program.next_bit_addr, } } - /// Read bytes from the wire - pub async fn read_bytes(&mut self, bytes: &mut [u8]) { - self.sm.tx().wait_push(0).await; - self.sm.tx().wait_push(bytes.len() as u32 - 1).await; - for b in bytes.iter_mut() { - *b = (self.sm.rx().wait_pull().await >> 24) as u8; + /// Perform an initialization sequence, will return true if a presence pulse was detected from a device + pub async fn reset(&mut self) -> bool { + // The state machine immediately starts running when jumping to this address + unsafe { + self.sm.exec_jmp(self.reset_addr); } + + let rx = self.sm.rx(); + let mut found = false; + for _ in 0..4 { + if rx.wait_pull().await != 0 { + found = true; + } + } + + found + } + + /// Write bytes to the onewire bus + pub async fn write_bytes(&mut self, data: &[u8]) { + let (rx, tx) = self.sm.rx_tx(); + for b in data { + tx.wait_push(*b as u32).await; + + // Empty the buffer that is being filled with every write + let _ = rx.wait_pull().await; + } + } + + /// Read bytes from the onewire bus + pub async fn read_bytes(&mut self, data: &mut [u8]) { + let (rx, tx) = self.sm.rx_tx(); + for b in data { + // Write all 1's so that we can read what the device responds + tx.wait_push(0xff).await; + + *b = (rx.wait_pull().await >> 24) as u8; + } + } + + async fn search(&mut self, state: &mut PioOneWireSearch) -> Option { + if !self.reset().await { + // No device present, no use in searching + state.finished = true; + return None; + } + self.write_bytes(&[0xF0]).await; // 0xF0 is the search rom command + + self.prepare_search(); + + let (rx, tx) = self.sm.rx_tx(); + + let mut value = 0; + let mut last_zero = 0; + + for bit in 0..64 { + // Write 2 dummy bits to read a bit and its complement + tx.wait_push(0x1).await; + tx.wait_push(0x1).await; + let in1 = rx.wait_pull().await; + let in2 = rx.wait_pull().await; + let push = match (in1, in2) { + (0, 0) => { + // If both are 0, it means we have devices with 0 and 1 bits in this position + let write_value = if bit < state.last_discrepancy { + (state.last_rom & (1 << bit)) != 0 + } else { + bit == state.last_discrepancy + }; + + if write_value { + 1 + } else { + last_zero = bit; + 0 + } + } + (0, 1) => 0, // Only devices with a 0 bit in this position + (1, 0) => 1, // Only devices with a 1 bit in this position + _ => { + // If both are 1, it means there is no device active and there is no point in continuing + self.restore_after_search(); + state.finished = true; + return None; + } + }; + value >>= 1; + if push == 1 { + value |= 1 << 63; + } + tx.wait_push(push).await; + let _ = rx.wait_pull().await; // Discard the result of the write action + } + + self.restore_after_search(); + + state.last_discrepancy = last_zero; + state.finished = last_zero == 0; + state.last_rom = value; + Some(value) + } + + fn prepare_search(&mut self) { + self.cfg.shift_in.threshold = 1; + self.cfg.shift_in.direction = ShiftDirection::Left; + self.cfg.shift_out.threshold = 1; + + self.sm.set_enable(false); + self.sm.set_config(&self.cfg); + + // set_config jumps to the wrong address so jump to the right one here + unsafe { + self.sm.exec_jmp(self.next_bit_addr); + } + self.sm.set_enable(true); + } + + fn restore_after_search(&mut self) { + self.cfg.shift_in.threshold = 8; + self.cfg.shift_in.direction = ShiftDirection::Right; + self.cfg.shift_out.threshold = 8; + + self.sm.set_enable(false); + self.sm.set_config(&self.cfg); + + // Clear the state in case we aborted prematurely with some bits still in the shift registers + self.sm.clear_fifos(); + self.sm.restart(); + + // set_config jumps to the wrong address so jump to the right one here + unsafe { + self.sm.exec_jmp(self.next_bit_addr); + } + self.sm.set_enable(true); + } +} + +/// Onewire search state +pub struct PioOneWireSearch { + last_rom: u64, + last_discrepancy: u8, + finished: bool, +} + +impl PioOneWireSearch { + /// Create a new Onewire search state + pub fn new() -> Self { + Self { + last_rom: 0, + last_discrepancy: 0, + finished: false, + } + } + + /// Search for the next address on the bus + pub async fn next(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option { + if self.finished { + None + } else { + pio.search(self).await + } + } + + /// Is finished when all devices have been found + pub fn is_finished(&self) -> bool { + self.finished } } diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs index 611fee83b..a8a0172be 100644 --- a/embassy-rp/src/trng.rs +++ b/embassy-rp/src/trng.rs @@ -78,6 +78,9 @@ impl From for u8 { /// failed entropy checks. /// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or /// 1 and sample count settings of 20-25. +/// Larger sample count settings (e.g. 100) provide proportionately slower average generation times. These settings +/// significantly reduce, but do not eliminate NIST test failures and entropy check failures. Results occasionally take an +/// especially long time to generate. /// /// --- /// @@ -108,9 +111,10 @@ pub struct Config { impl Default for Config { fn default() -> Self { Config { - disable_autocorrelation_test: true, - disable_crngt_test: true, - disable_von_neumann_balancer: true, + // WARNING: Disabling these tests increases likelihood of poor rng results. + disable_autocorrelation_test: false, + disable_crngt_test: false, + disable_von_neumann_balancer: false, sample_count: 25, inverter_chain_length: InverterChainLength::One, } @@ -148,6 +152,7 @@ impl Default for Config { /// ``` pub struct Trng<'d, T: Instance> { phantom: PhantomData<&'d mut T>, + config: Config, } /// 12.12.1. Overview @@ -159,28 +164,12 @@ const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8; impl<'d, T: Instance> Trng<'d, T> { /// Create a new TRNG driver. pub fn new(_trng: Peri<'d, T>, _irq: impl Binding> + 'd, config: Config) -> Self { - let regs = T::regs(); - - regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); - - let trng_config_register = regs.trng_config(); - trng_config_register.write(|w| { - w.set_rnd_src_sel(config.inverter_chain_length.clone().into()); - }); - - let sample_count_register = regs.sample_cnt1(); - sample_count_register.write(|w| { - *w = config.sample_count; - }); - - let debug_control_register = regs.trng_debug_control(); - debug_control_register.write(|w| { - w.set_auto_correlate_bypass(config.disable_autocorrelation_test); - w.set_trng_crngt_bypass(config.disable_crngt_test); - w.set_vnc_bypass(config.disable_von_neumann_balancer) - }); - - Trng { phantom: PhantomData } + let trng = Trng { + phantom: PhantomData, + config: config, + }; + trng.initialize_rng(); + trng } fn start_rng(&self) { @@ -198,6 +187,29 @@ impl<'d, T: Instance> Trng<'d, T> { reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true)); } + fn initialize_rng(&self) { + let regs = T::regs(); + + regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); + + let trng_config_register = regs.trng_config(); + trng_config_register.write(|w| { + w.set_rnd_src_sel(self.config.inverter_chain_length.clone().into()); + }); + + let sample_count_register = regs.sample_cnt1(); + sample_count_register.write(|w| { + *w = self.config.sample_count; + }); + + let debug_control_register = regs.trng_debug_control(); + debug_control_register.write(|w| { + w.set_auto_correlate_bypass(self.config.disable_autocorrelation_test); + w.set_trng_crngt_bypass(self.config.disable_crngt_test); + w.set_vnc_bypass(self.config.disable_von_neumann_balancer); + }); + } + fn enable_irq(&self) { unsafe { T::Interrupt::enable() } } @@ -218,6 +230,10 @@ impl<'d, T: Instance> Trng<'d, T> { if trng_valid_register.read().ehr_valid().not() { if regs.rng_isr().read().autocorr_err() { regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true)); + // Fixed delay is required after TRNG soft reset. This read is sufficient. + regs.trng_sw_reset().read(); + self.initialize_rng(); + self.start_rng(); } else { panic!("RNG not busy, but ehr is not valid!") } @@ -279,8 +295,11 @@ impl<'d, T: Instance> Trng<'d, T> { if trng_busy_register.read().trng_busy() { Poll::Pending } else { + // If woken up and EHR is *not* valid, assume the trng has been reset and reinitialize, restart. if trng_valid_register.read().ehr_valid().not() { - panic!("RNG not busy, but ehr is not valid!") + self.initialize_rng(); + self.start_rng(); + return Poll::Pending; } self.read_ehr_registers_into_array(&mut buffer); let remaining = destination_length - bytes_transferred; @@ -380,25 +399,36 @@ impl interrupt::typelevel::Handler for InterruptHandl unsafe fn on_interrupt() { let regs = T::regs(); let isr = regs.rng_isr().read(); - // Clear ehr bit - regs.rng_icr().write(|w| { - w.set_ehr_valid(true); - }); if isr.ehr_valid() { + regs.rng_icr().write(|w| { + w.set_ehr_valid(true); + }); T::waker().wake(); - } else { + } else if isr.crngt_err() { + warn!("TRNG CRNGT error! Increase sample count to reduce likelihood"); + regs.rng_icr().write(|w| { + w.set_crngt_err(true); + }); + } else if isr.vn_err() { + warn!("TRNG Von-Neumann balancer error! Increase sample count to reduce likelihood"); + regs.rng_icr().write(|w| { + w.set_vn_err(true); + }); + } else if isr.autocorr_err() { // 12.12.5. List of Registers // ... // TRNG: RNG_ISR Register // ... // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row. // When set, RNG ceases functioning until next reset - if isr.autocorr_err() { - warn!("TRNG Autocorrect error! Resetting TRNG"); - regs.trng_sw_reset().write(|w| { - w.set_trng_sw_reset(true); - }); - } + warn!("TRNG Autocorrect error! Resetting TRNG. Increase sample count to reduce likelihood"); + regs.trng_sw_reset().write(|w| { + w.set_trng_sw_reset(true); + }); + // Fixed delay is required after TRNG soft reset, this read is sufficient. + regs.trng_sw_reset().read(); + // Wake up to reinitialize and restart the TRNG. + T::waker().wake(); } } } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 946aa0399..b00b6a7ac 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -684,6 +684,8 @@ fn main() { PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock), }; + let bus_clock_frequency = clock_gen.gen_clock(p.name, &rcc.bus_clock); + // A refcount leak can result if the same field is shared by peripherals with different stop modes // This condition should be checked in stm32-data let stop_mode = match rcc.stop_mode { @@ -697,6 +699,9 @@ fn main() { fn frequency() -> crate::time::Hertz { #clock_frequency } + fn bus_frequency() -> crate::time::Hertz { + #bus_clock_frequency + } const RCC_INFO: crate::rcc::RccInfo = unsafe { crate::rcc::RccInfo::new( diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 91be53607..8ed102c1b 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -143,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); let frequency = Hertz(T::frequency().0 / prescaler.divisor()); - info!("ADC frequency set to {}", frequency); + trace!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 321db7431..f46e87f38 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -11,7 +11,7 @@ #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_l0, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] -#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] +#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0), path = "v3.rs")] #[cfg_attr(any(adc_v4, adc_u5), path = "v4.rs")] #[cfg_attr(adc_g4, path = "g4.rs")] #[cfg_attr(adc_c0, path = "c0.rs")] @@ -108,6 +108,7 @@ pub(crate) fn blocking_delay_us(us: u32) { adc_g0, adc_u0, adc_h5, + adc_h7rs, adc_u5, adc_c0 )))] @@ -129,6 +130,7 @@ pub trait Instance: SealedInstance + crate::PeripheralType { adc_g0, adc_u0, adc_h5, + adc_h7rs, adc_u5, adc_c0 ))] diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 2de12d1d6..1c5ad16e9 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -19,7 +19,7 @@ impl SealedAdcChannel for VrefInt { cfg_if! { if #[cfg(adc_g0)] { let val = 13; - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { let val = 17; } else if #[cfg(adc_u0)] { let val = 12; @@ -38,7 +38,7 @@ impl SealedAdcChannel for Temperature { cfg_if! { if #[cfg(adc_g0)] { let val = 12; - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { let val = 16; } else if #[cfg(adc_u0)] { let val = 11; @@ -57,9 +57,9 @@ impl SealedAdcChannel for Vbat { cfg_if! { if #[cfg(adc_g0)] { let val = 14; - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { let val = 2; - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { let val = 13; } else { let val = 18; @@ -70,7 +70,7 @@ impl SealedAdcChannel for Vbat { } cfg_if! { - if #[cfg(adc_h5)] { + if #[cfg(any(adc_h5, adc_h7rs))] { pub struct VddCore; impl AdcChannel for VddCore {} impl super::SealedAdcChannel for VddCore { @@ -171,7 +171,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().ccr().modify(|reg| { reg.set_tsen(true); }); - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { T::common_regs().ccr().modify(|reg| { reg.set_tsen(true); }); @@ -191,7 +191,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().ccr().modify(|reg| { reg.set_vbaten(true); }); - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { T::common_regs().ccr().modify(|reg| { reg.set_vbaten(true); }); @@ -414,7 +414,7 @@ impl<'d, T: Instance> Adc<'d, T> { fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." - #[cfg(adc_h5)] + #[cfg(any(adc_h5, adc_h7rs))] if channel.channel() == 0 { T::regs().or().modify(|reg| reg.set_op0(true)); } @@ -447,7 +447,7 @@ impl<'d, T: Instance> Adc<'d, T> { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." - #[cfg(adc_h5)] + #[cfg(any(adc_h5, adc_h7rs))] if channel.channel() == 0 { T::regs().or().modify(|reg| reg.set_op0(false)); } @@ -475,7 +475,7 @@ impl<'d, T: Instance> Adc<'d, T> { if #[cfg(any(adc_g0, adc_u0))] { // On G0 and U6 all channels use the same sampling time. T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); - } else if #[cfg(adc_h5)] { + } else if #[cfg(any(adc_h5, adc_h7rs))] { match _ch { 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 4d2e0f0df..e455b275c 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -305,7 +305,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr2().modify(|reg| { reg.set_rovse(enable); - reg.set_osvr(samples); + reg.set_ovsr(samples); reg.set_ovss(right_shift); }) } diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index c5467dfe8..c295b0f50 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -299,9 +299,9 @@ impl Registers { mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); mb.tdlr() - .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.data()[0..4].try_into()))); + .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.raw_data()[0..4].try_into()))); mb.tdhr() - .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.data()[4..8].try_into()))); + .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.raw_data()[4..8].try_into()))); let id: IdReg = frame.id().into(); mb.tir().write(|w| { w.0 = id.0; diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 39c4112ad..4873ee444 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -75,7 +75,7 @@ impl Registers { let mailbox = self.tx_buffer_element(bufidx); mailbox.reset(); put_tx_header(mailbox, header); - put_tx_data(mailbox, &buffer[..header.len() as usize]); + put_tx_data(mailbox, buffer); // Set as ready to transmit self.regs.txbar().modify(|w| w.set_ar(bufidx, true)); @@ -190,7 +190,7 @@ impl Registers { DataLength::Fdcan(len) => len, DataLength::Classic(len) => len, }; - if len as usize > ClassicData::MAX_DATA_LEN { + if len as usize > 8 { return None; } diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index f621b8bd5..0fbab053b 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -104,15 +104,13 @@ pub trait CanHeader: Sized { #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ClassicData { - pub(crate) bytes: [u8; Self::MAX_DATA_LEN], + pub(crate) bytes: [u8; 8], } impl ClassicData { - pub(crate) const MAX_DATA_LEN: usize = 8; /// Creates a data payload from a raw byte slice. /// - /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or - /// cannot be represented with an FDCAN DLC. + /// Returns `FrameCreateError` if `data` is more than 8 bytes (which is the maximum). pub fn new(data: &[u8]) -> Result { if data.len() > 8 { return Err(FrameCreateError::InvalidDataLength); @@ -211,12 +209,17 @@ impl Frame { /// Get reference to data pub fn data(&self) -> &[u8] { - &self.data.raw() + &self.data.raw()[..self.can_header.len as usize] + } + + /// Get reference to underlying 8-byte raw data buffer, some bytes on the tail might be undefined. + pub fn raw_data(&self) -> &[u8] { + self.data.raw() } /// Get mutable reference to data pub fn data_mut(&mut self) -> &mut [u8] { - self.data.raw_mut() + &mut self.data.raw_mut()[..self.can_header.len as usize] } /// Get priority of frame @@ -260,7 +263,7 @@ impl embedded_can::Frame for Frame { self.can_header.len as usize } fn data(&self) -> &[u8] { - &self.data.raw() + &self.data() } } @@ -405,12 +408,12 @@ impl FdFrame { /// Get reference to data pub fn data(&self) -> &[u8] { - &self.data.raw() + &self.data.raw()[..self.can_header.len as usize] } /// Get mutable reference to data pub fn data_mut(&mut self) -> &mut [u8] { - self.data.raw_mut() + &mut self.data.raw_mut()[..self.can_header.len as usize] } } @@ -448,7 +451,7 @@ impl embedded_can::Frame for FdFrame { self.can_header.len as usize } fn data(&self) -> &[u8] { - &self.data.raw() + &self.data() } } diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index 82de4a89b..b368df6c3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -37,22 +37,12 @@ enum OpAmpDifferentialPair { /// Speed #[allow(missing_docs)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub enum OpAmpSpeed { Normal, HighSpeed, } -#[cfg(opamp_g4)] -impl From for crate::pac::opamp::vals::Opahsm { - fn from(v: OpAmpSpeed) -> Self { - match v { - OpAmpSpeed::Normal => crate::pac::opamp::vals::Opahsm::NORMAL, - OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::Opahsm::HIGH_SPEED, - } - } -} - /// OpAmp external outputs, wired to a GPIO pad. /// /// This struct can also be used as an ADC input. @@ -80,7 +70,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { #[cfg(opamp_g4)] T::regs().csr().modify(|w| { - w.set_opahsm(speed.into()); + w.set_opahsm(speed == OpAmpSpeed::HighSpeed); }); Self { _inner: opamp } @@ -113,7 +103,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { w.set_vp_sel(VpSel::from_bits(in_pin.channel())); w.set_vm_sel(vm_sel); #[cfg(opamp_g4)] - w.set_opaintoen(Opaintoen::OUTPUT_PIN); + w.set_opaintoen(false); w.set_opampen(true); }); @@ -166,7 +156,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { w.set_vm_sel(vm_sel); w.set_pga_gain(pga_gain); #[cfg(opamp_g4)] - w.set_opaintoen(Opaintoen::OUTPUT_PIN); + w.set_opaintoen(false); w.set_opampen(true); }); @@ -189,7 +179,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { w.set_vm_sel(VmSel::OUTPUT); w.set_vp_sel(VpSel::DAC3_CH1); - w.set_opaintoen(Opaintoen::OUTPUT_PIN); + w.set_opaintoen(false); w.set_opampen(true); }); @@ -215,7 +205,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { w.set_vp_sel(VpSel::from_bits(pin.channel())); w.set_vm_sel(VmSel::OUTPUT); #[cfg(opamp_g4)] - w.set_opaintoen(Opaintoen::ADCCHANNEL); + w.set_opaintoen(true); w.set_opampen(true); }); @@ -251,7 +241,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { w.set_vp_sel(VpSel::from_bits(pin.channel())); w.set_vm_sel(VmSel::OUTPUT); w.set_pga_gain(pga_gain); - w.set_opaintoen(Opaintoen::ADCCHANNEL); + w.set_opaintoen(true); w.set_opampen(true); }); @@ -278,7 +268,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { use crate::pac::opamp::vals::*; w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx w.set_vm_sel(VmSel::from_bits(m_pin.channel())); - w.set_opaintoen(Opaintoen::ADCCHANNEL); + w.set_opaintoen(true); w.set_opampen(true); }); @@ -308,7 +298,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { use crate::pac::opamp::vals::*; w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx w.set_vm_sel(VmSel::from_bits(m_pin.channel())); - w.set_opaintoen(Opaintoen::OUTPUT_PIN); + w.set_opaintoen(false); w.set_opampen(true); }); @@ -340,7 +330,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { use crate::pac::opamp::vals::*; w.set_vp_sel(VpSel::from_bits(p_pin.channel())); w.set_vm_sel(VmSel::from_bits(m_pin.channel())); - w.set_opaintoen(Opaintoen::OUTPUT_PIN); + w.set_opaintoen(false); w.set_opampen(true); }); @@ -369,7 +359,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { use crate::pac::opamp::vals::*; w.set_vp_sel(VpSel::from_bits(p_pin.channel())); w.set_vm_sel(VmSel::from_bits(m_pin.channel())); - w.set_opaintoen(Opaintoen::ADCCHANNEL); + w.set_opaintoen(true); w.set_opampen(true); }); @@ -389,17 +379,14 @@ impl<'d, T: Instance> OpAmp<'d, T> { T::regs().csr().modify(|w| { w.set_opampen(true); w.set_calon(true); - w.set_usertrim(Usertrim::USER); + w.set_usertrim(true); }); - match T::regs().csr().read().opahsm() { - Opahsm::NORMAL => { - self.calibrate_differential_pair(OpAmpDifferentialPair::P); - self.calibrate_differential_pair(OpAmpDifferentialPair::N); - } - Opahsm::HIGH_SPEED => { - self.calibrate_differential_pair(OpAmpDifferentialPair::P); - } + if T::regs().csr().read().opahsm() { + self.calibrate_differential_pair(OpAmpDifferentialPair::P); + } else { + self.calibrate_differential_pair(OpAmpDifferentialPair::P); + self.calibrate_differential_pair(OpAmpDifferentialPair::N); } T::regs().csr().modify(|w| { @@ -435,11 +422,13 @@ impl<'d, T: Instance> OpAmp<'d, T> { T::regs().csr().modify(|w| match pair { OpAmpDifferentialPair::P => { - defmt::info!("p calibration. offset: {}", mid); + #[cfg(feature = "defmt")] + defmt::debug!("opamp p calibration. offset: {}", mid); w.set_trimoffsetp(mid); } OpAmpDifferentialPair::N => { - defmt::info!("n calibration. offset: {}", mid); + #[cfg(feature = "defmt")] + defmt::debug!("opamp n calibration. offset: {}", mid); w.set_trimoffsetn(mid); } }); @@ -448,7 +437,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 blocking_delay_ms(2); - if T::regs().csr().read().outcal() == Outcal::LOW { + if !T::regs().csr().read().calout() { if mid == 0 { break; } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 4f43d3748..150daa4a7 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -97,6 +97,8 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { pub(crate) trait SealedRccPeripheral { fn frequency() -> Hertz; + #[allow(dead_code)] + fn bus_frequency() -> Hertz; const RCC_INFO: RccInfo; } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 6578aa1aa..9e2ba093a 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -284,6 +284,10 @@ impl<'d, M: PeriMode> Spi<'d, M> { #[cfg(any(spi_v3, spi_v4, spi_v5))] { + self.info.regs.cr1().modify(|w| { + w.set_spe(false); + }); + self.info.regs.cfg2().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); @@ -292,6 +296,10 @@ impl<'d, M: PeriMode> Spi<'d, M> { self.info.regs.cfg1().modify(|w| { w.set_mbr(br); }); + + self.info.regs.cr1().modify(|w| { + w.set_spe(true); + }); } Ok(()) } diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index f543bafab..8eec6c0c7 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -240,11 +240,11 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { let (these_bits, result) = if target < 128 { (target as u8, target) } else if target < 255 { - (64 + (target / 2) as u8, (target - target % 2)) + ((64 + (target / 2) as u8) | 128, (target - target % 2)) } else if target < 508 { - (32 + (target / 8) as u8, (target - target % 8)) + ((32 + (target / 8) as u8) | 192, (target - target % 8)) } else if target < 1008 { - (32 + (target / 16) as u8, (target - target % 16)) + ((32 + (target / 16) as u8) | 224, (target - target % 16)) } else { (u8::MAX, 1008) }; @@ -300,7 +300,7 @@ mod tests { TestRun { value: 400, ckd: Ckd::DIV1, - bits: 32 + (400u16 / 8) as u8, + bits: 210, }, TestRun { value: 600, diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 2aca3d929..590d1a427 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -549,7 +549,7 @@ foreach_interrupt!( ); fn calculate_trdt(speed: Dspd) -> u8 { - let ahb_freq = T::frequency().0; + let ahb_freq = T::bus_frequency().0; match speed { Dspd::HIGH_SPEED => { // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) diff --git a/embassy-sync/README.md b/embassy-sync/README.md index 6871bcabc..91c59884f 100644 --- a/embassy-sync/README.md +++ b/embassy-sync/README.md @@ -12,7 +12,7 @@ Synchronization primitives and data structures with async support: - [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks. - [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits. - [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. -- [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API. +- [`AtomicWaker`](waitqueue::AtomicWaker) - Utility to register and wake a `Waker` from interrupt context. - [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s. - [`LazyLock`](lazy_lock::LazyLock) - A value which is initialized on the first access diff --git a/embassy-sync/src/waitqueue/atomic_waker.rs b/embassy-sync/src/waitqueue/atomic_waker.rs index 231902c5a..5a9910e7f 100644 --- a/embassy-sync/src/waitqueue/atomic_waker.rs +++ b/embassy-sync/src/waitqueue/atomic_waker.rs @@ -5,6 +5,9 @@ use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use crate::blocking_mutex::Mutex; /// Utility struct to register and wake a waker. +/// If a waker is registered, registering another waker will replace the previous one without waking it. +/// Intended to wake a task from an interrupt. Therefore, it is generally not expected, +/// that multiple tasks register try to register a waker simultaneously. pub struct GenericAtomicWaker { waker: Mutex>>, } diff --git a/embassy-sync/src/waitqueue/atomic_waker_turbo.rs b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs index 5c6a96ec8..c06b83056 100644 --- a/embassy-sync/src/waitqueue/atomic_waker_turbo.rs +++ b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs @@ -4,6 +4,9 @@ use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Waker; /// Utility struct to register and wake a waker. +/// If a waker is registered, registering another waker will replace the previous one without waking it. +/// The intended use case is to wake tasks from interrupts. Therefore, it is generally not expected, +/// that multiple tasks register try to register a waker simultaneously. pub struct AtomicWaker { waker: AtomicPtr<()>, } diff --git a/embassy-sync/src/waitqueue/multi_waker.rs b/embassy-sync/src/waitqueue/multi_waker.rs index 0e520bf40..0384d6bed 100644 --- a/embassy-sync/src/waitqueue/multi_waker.rs +++ b/embassy-sync/src/waitqueue/multi_waker.rs @@ -3,6 +3,8 @@ use core::task::Waker; use heapless::Vec; /// Utility struct to register and wake multiple wakers. +/// Queue of wakers with a maximum length of `N`. +/// Intended for waking multiple tasks. pub struct MultiWakerRegistration { wakers: Vec, } diff --git a/embassy-sync/src/waitqueue/waker_registration.rs b/embassy-sync/src/waitqueue/waker_registration.rs index 9b666e7c4..7f24f8fb6 100644 --- a/embassy-sync/src/waitqueue/waker_registration.rs +++ b/embassy-sync/src/waitqueue/waker_registration.rs @@ -2,6 +2,10 @@ use core::mem; use core::task::Waker; /// Utility struct to register and wake a waker. +/// If a waker is registered, registering another waker will replace the previous one. +/// The previous waker will be woken in this case, giving it a chance to reregister itself. +/// Although it is possible to wake multiple tasks this way, +/// this will cause them to wake each other in a loop registering themselves. #[derive(Debug, Default)] pub struct WakerRegistration { waker: Option, diff --git a/embassy-time-driver/Cargo.toml b/embassy-time-driver/Cargo.toml index b709cafc1..16213cb75 100644 --- a/embassy-time-driver/Cargo.toml +++ b/embassy-time-driver/Cargo.toml @@ -226,6 +226,8 @@ tick-hz-128_000_000 = [] tick-hz-130_000_000 = [] ## 131.072MHz Tick Rate tick-hz-131_072_000 = [] +## 133.0MHz Tick Rate +tick-hz-133_000_000 = [] ## 140.0MHz Tick Rate tick-hz-140_000_000 = [] ## 144.0MHz Tick Rate diff --git a/embassy-time-driver/gen_tick.py b/embassy-time-driver/gen_tick.py index af194c31f..080434457 100644 --- a/embassy-time-driver/gen_tick.py +++ b/embassy-time-driver/gen_tick.py @@ -22,6 +22,7 @@ for i in range(1, 30): ticks.append(10 * i * 1_000_000) for i in range(15, 50): ticks.append(20 * i * 1_000_000) +ticks.append(133 * 1_000_000) seen = set() ticks = sorted([x for x in ticks if not (x in seen or seen.add(x))]) diff --git a/embassy-time-driver/src/tick.rs b/embassy-time-driver/src/tick.rs index 916ae9498..5059e1628 100644 --- a/embassy-time-driver/src/tick.rs +++ b/embassy-time-driver/src/tick.rs @@ -182,6 +182,8 @@ pub const TICK_HZ: u64 = 128_000_000; pub const TICK_HZ: u64 = 130_000_000; #[cfg(feature = "tick-hz-131_072_000")] pub const TICK_HZ: u64 = 131_072_000; +#[cfg(feature = "tick-hz-133_000_000")] +pub const TICK_HZ: u64 = 133_000_000; #[cfg(feature = "tick-hz-140_000_000")] pub const TICK_HZ: u64 = 140_000_000; #[cfg(feature = "tick-hz-144_000_000")] @@ -410,6 +412,7 @@ pub const TICK_HZ: u64 = 5_242_880_000; feature = "tick-hz-128_000_000", feature = "tick-hz-130_000_000", feature = "tick-hz-131_072_000", + feature = "tick-hz-133_000_000", feature = "tick-hz-140_000_000", feature = "tick-hz-144_000_000", feature = "tick-hz-150_000_000", diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 80a39dbf5..dc144ec3c 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -274,6 +274,8 @@ tick-hz-128_000_000 = ["embassy-time-driver/tick-hz-128_000_000"] tick-hz-130_000_000 = ["embassy-time-driver/tick-hz-130_000_000"] ## 131.072MHz Tick Rate tick-hz-131_072_000 = ["embassy-time-driver/tick-hz-131_072_000"] +## 133.0MHz Tick Rate +tick-hz-133_000_000 = ["embassy-time-driver/tick-hz-133_000_000"] ## 140.0MHz Tick Rate tick-hz-140_000_000 = ["embassy-time-driver/tick-hz-140_000_000"] ## 144.0MHz Tick Rate diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 34e5762d2..d1162eadd 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -1,8 +1,7 @@ use core::future::{poll_fn, Future}; -use core::pin::{pin, Pin}; +use core::pin::Pin; use core::task::{Context, Poll}; -use futures_util::future::{select, Either}; use futures_util::stream::FusedStream; use futures_util::Stream; @@ -17,11 +16,10 @@ pub struct TimeoutError; /// /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. -pub async fn with_timeout(timeout: Duration, fut: F) -> Result { - let timeout_fut = Timer::after(timeout); - match select(pin!(fut), timeout_fut).await { - Either::Left((r, _)) => Ok(r), - Either::Right(_) => Err(TimeoutError), +pub fn with_timeout(timeout: Duration, fut: F) -> TimeoutFuture { + TimeoutFuture { + timer: Timer::after(timeout), + fut, } } @@ -29,16 +27,15 @@ pub async fn with_timeout(timeout: Duration, fut: F) -> Result(at: Instant, fut: F) -> Result { - let timeout_fut = Timer::at(at); - match select(pin!(fut), timeout_fut).await { - Either::Left((r, _)) => Ok(r), - Either::Right(_) => Err(TimeoutError), +pub fn with_deadline(at: Instant, fut: F) -> TimeoutFuture { + TimeoutFuture { + timer: Timer::at(at), + fut, } } /// Provides functions to run a given future with a timeout or a deadline. -pub trait WithTimeout { +pub trait WithTimeout: Sized { /// Output type of the future. type Output; @@ -46,24 +43,50 @@ pub trait WithTimeout { /// /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. - async fn with_timeout(self, timeout: Duration) -> Result; + fn with_timeout(self, timeout: Duration) -> TimeoutFuture; /// Runs a given future with a deadline time. /// /// If the future completes before the deadline, its output is returned. Otherwise, on timeout, /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. - async fn with_deadline(self, at: Instant) -> Result; + fn with_deadline(self, at: Instant) -> TimeoutFuture; } impl WithTimeout for F { type Output = F::Output; - async fn with_timeout(self, timeout: Duration) -> Result { - with_timeout(timeout, self).await + fn with_timeout(self, timeout: Duration) -> TimeoutFuture { + with_timeout(timeout, self) } - async fn with_deadline(self, at: Instant) -> Result { - with_deadline(at, self).await + fn with_deadline(self, at: Instant) -> TimeoutFuture { + with_deadline(at, self) + } +} + +/// Future for the [`with_timeout`] and [`with_deadline`] functions. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct TimeoutFuture { + timer: Timer, + fut: F, +} + +impl Unpin for TimeoutFuture {} + +impl Future for TimeoutFuture { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let fut = unsafe { Pin::new_unchecked(&mut this.fut) }; + let timer = unsafe { Pin::new_unchecked(&mut this.timer) }; + if let Poll::Ready(x) = fut.poll(cx) { + return Poll::Ready(Ok(x)); + } + if let Poll::Ready(_) = timer.poll(cx) { + return Poll::Ready(Err(TimeoutError)); + } + Poll::Pending } } diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index f0d7626f6..e93c241ad 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -13,9 +13,25 @@ use crate::consts::{ }; use crate::Reset; +/// Generic interface for a system that can signal to the bootloader that USB DFU mode is needed on the next boot. +/// +/// By default this trait is implemented for `BlockingFirmwareState<'d, STATE>` but you could also implement this generic +/// interface yourself instead in more complex situations. This could for instance be when you cannot hand ownership of a +/// `BlockingFirmwareState` instance over directly to the DFU `Control` instance and need to use a more complex mechanism. +pub trait DfuMarker { + /// Signal to the bootloader that DFU mode should be used on the next boot. + fn mark_dfu(&mut self); +} + +impl<'d, STATE: NorFlash> DfuMarker for BlockingFirmwareState<'d, STATE> { + fn mark_dfu(&mut self) { + self.mark_dfu().expect("Failed to mark DFU mode in bootloader") + } +} + /// Internal state for the DFU class -pub struct Control<'d, STATE: NorFlash, RST: Reset> { - firmware_state: BlockingFirmwareState<'d, STATE>, +pub struct Control { + dfu_marker: MARK, attrs: DfuAttributes, state: State, timeout: Option, @@ -23,11 +39,11 @@ pub struct Control<'d, STATE: NorFlash, RST: Reset> { _rst: PhantomData, } -impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { +impl Control { /// Create a new DFU instance to expose a DFU interface. - pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self { + pub fn new(dfu_marker: MARK, attrs: DfuAttributes) -> Self { Control { - firmware_state, + dfu_marker, attrs, state: State::AppIdle, detach_start: None, @@ -37,7 +53,7 @@ impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { } } -impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { +impl Handler for Control { fn reset(&mut self) { if let Some(start) = self.detach_start { let delta = Instant::now() - start; @@ -48,9 +64,7 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { timeout.as_millis() ); if delta < timeout { - self.firmware_state - .mark_dfu() - .expect("Failed to mark DFU mode in bootloader"); + self.dfu_marker.mark_dfu(); RST::sys_reset() } } @@ -109,9 +123,9 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { /// it should expose a DFU device, and a software reset will be issued. /// /// To apply USB DFU updates, the bootloader must be capable of recognizing the DFU magic and exposing a device to handle the full DFU transaction with the host. -pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash, RST: Reset>( +pub fn usb_dfu<'d, D: Driver<'d>, MARK: DfuMarker, RST: Reset>( builder: &mut Builder<'d, D>, - handler: &'d mut Control<'d, STATE, RST>, + handler: &'d mut Control, timeout: Duration, ) { let mut func = builder.function(0x00, 0x00, 0x00); diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 9a21b9a3b..6c4b3f9a4 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -218,10 +218,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.end_bos(); // Log the number of allocator bytes actually used in descriptor buffers - info!("USB: config_descriptor used: {}", self.config_descriptor.position()); - info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); - info!("USB: msos_descriptor used: {}", msos_descriptor.len()); - info!("USB: control_buf size: {}", self.control_buf.len()); + trace!("USB: config_descriptor used: {}", self.config_descriptor.position()); + trace!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); + trace!("USB: msos_descriptor used: {}", msos_descriptor.len()); + trace!("USB: control_buf size: {}", self.control_buf.len()); UsbDevice::build( self.driver, diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index ceaafd784..e30a3855d 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs @@ -9,6 +9,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::twim::{self, Twim}; use embassy_nrf::{bind_interrupts, peripherals}; +use static_cell::ConstStaticCell; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; @@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Initializing TWI..."); let config = twim::Config::default(); - let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); + static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]); + let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take()); info!("Reading..."); diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index 8a6f958eb..f7380e20d 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs @@ -30,6 +30,7 @@ async fn main(_p: Spawner) { loop { info!("Initializing TWI..."); let config = twim::Config::default(); + let mut ram_buffer = [0u8; 16]; // Create the TWIM instance with borrowed singletons, so they're not consumed. let mut twi = Twim::new( @@ -38,6 +39,7 @@ async fn main(_p: Spawner) { p.P0_03.reborrow(), p.P0_04.reborrow(), config, + &mut ram_buffer, ); info!("Reading..."); diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs index 991510851..379e2b8f9 100644 --- a/examples/rp/src/bin/pio_onewire.rs +++ b/examples/rp/src/bin/pio_onewire.rs @@ -1,4 +1,4 @@ -//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. +//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. #![no_std] #![no_main] @@ -6,9 +6,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{self, InterruptHandler, Pio}; -use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; use embassy_time::Timer; +use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -21,63 +22,66 @@ async fn main(_spawner: Spawner) { let mut pio = Pio::new(p.PIO0, Irqs); let prg = PioOneWireProgram::new(&mut pio.common); - let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); - let mut sensor = Ds18b20::new(onewire); + info!("Starting onewire search"); + + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found addres: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } + } + } + + info!("Search done, found {} devices", devices.len()); loop { - sensor.start().await; // Start a new measurement + onewire.reset().await; + // Skip rom and trigger conversion, we can trigger all devices on the bus immediately + onewire.write_bytes(&[0xCC, 0x44]).await; + Timer::after_secs(1).await; // Allow 1s for the measurement to finish - match sensor.temperature().await { - Ok(temp) => info!("temp = {:?} deg C", temp), - _ => error!("sensor error"), + + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed", device); + } } Timer::after_secs(1).await; } } -/// DS18B20 temperature sensor driver -pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { - wire: PioOneWire<'d, PIO, SM>, -} - -impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { - pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { - Self { wire } - } - - /// Calculate CRC8 of the data - fn crc8(data: &[u8]) -> u8 { - let mut temp; - let mut data_byte; - let mut crc = 0; - for b in data { - data_byte = *b; - for _ in 0..8 { - temp = (crc ^ data_byte) & 0x01; - crc >>= 1; - if temp != 0 { - crc ^= 0x8C; - } - data_byte >>= 1; +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; } - } - crc - } - - /// Start a new measurement. Allow at least 1000ms before getting `temperature`. - pub async fn start(&mut self) { - self.wire.write_bytes(&[0xCC, 0x44]).await; - } - - /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. - pub async fn temperature(&mut self) -> Result { - self.wire.write_bytes(&[0xCC, 0xBE]).await; - let mut data = [0; 9]; - self.wire.read_bytes(&mut data).await; - match Self::crc8(&data) == 0 { - true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), - false => Err(()), + data_byte >>= 1; } } + crc }