Merge branch 'main' into configurable-bank-support

This commit is contained in:
diondokter 2025-05-01 09:44:39 +02:00
commit f713f170a1
32 changed files with 572 additions and 396 deletions

1
ci.sh
View File

@ -324,7 +324,6 @@ DEFMT_RTT_BUFFER_SIZE="72" cargo batch \
# temporarily disabled, these boards are dead. # temporarily disabled, these boards are dead.
rm -rf out/tests/stm32f103c8 rm -rf out/tests/stm32f103c8
rm -rf out/tests/stm32l073rz
rm -rf out/tests/nrf52840-dk rm -rf out/tests/nrf52840-dk
rm -rf out/tests/nrf52833-dk rm -rf out/tests/nrf52833-dk

View File

@ -4,7 +4,6 @@
use core::future::{poll_fn, Future}; use core::future::{poll_fn, Future};
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::sync::atomic::compiler_fence; use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst; use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll; use core::task::Poll;
@ -17,7 +16,7 @@ use embassy_time::{Duration, Instant};
use embedded_hal_1::i2c::Operation; use embedded_hal_1::i2c::Operation;
pub use pac::twim::vals::Frequency; 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::gpio::Pin as GpioPin;
use crate::interrupt::typelevel::Interrupt; use crate::interrupt::typelevel::Interrupt;
use crate::pac::gpio::vals as gpiovals; use crate::pac::gpio::vals as gpiovals;
@ -75,8 +74,8 @@ pub enum Error {
Transmit, Transmit,
/// Data reception failed. /// Data reception failed.
Receive, Receive,
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. /// 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.
BufferNotInRAM, 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. /// 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, AddressNack,
/// Didn't receive an ACK bit after a data byte. /// Didn't receive an ACK bit after a data byte.
@ -115,16 +114,24 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
/// TWI driver. /// TWI driver.
pub struct Twim<'d, T: Instance> { pub struct Twim<'d, T: Instance> {
_p: Peri<'d, T>, _p: Peri<'d, T>,
tx_ram_buffer: &'d mut [u8],
} }
impl<'d, T: Instance> Twim<'d, T> { impl<'d, T: Instance> Twim<'d, T> {
/// Create a new TWI driver. /// 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( pub fn new(
twim: Peri<'d, T>, twim: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sda: Peri<'d, impl GpioPin>, sda: Peri<'d, impl GpioPin>,
scl: Peri<'d, impl GpioPin>, scl: Peri<'d, impl GpioPin>,
config: Config, config: Config,
tx_ram_buffer: &'d mut [u8],
) -> Self { ) -> Self {
let r = T::regs(); let r = T::regs();
@ -159,7 +166,10 @@ impl<'d, T: Instance> Twim<'d, T> {
// Enable TWIM instance. // Enable TWIM instance.
r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); 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 // Apply runtime peripheral configuration
Self::set_config(&mut twim, &config).unwrap(); 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. /// Set TX buffer, checking that it is in RAM and has suitable length.
unsafe fn set_tx_buffer( unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
&mut self,
buffer: &[u8],
ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
) -> Result<(), Error> {
let buffer = if slice_in_ram(buffer) { let buffer = if slice_in_ram(buffer) {
buffer buffer
} else { } 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"); trace!("Copying TWIM tx buffer into RAM for DMA");
let ram_buffer = &mut ram_buffer[..buffer.len()]; let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()];
// Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer) ram_buffer.copy_from_slice(buffer);
let uninit_src: &[MaybeUninit<u8>] = unsafe { core::mem::transmute(buffer) }; &*ram_buffer
ram_buffer.copy_from_slice(uninit_src);
unsafe { &*(ram_buffer as *const [MaybeUninit<u8>] as *const [u8]) }
}; };
if buffer.len() > EASY_DMA_SIZE { if buffer.len() > EASY_DMA_SIZE {
@ -358,7 +364,6 @@ impl<'d, T: Instance> Twim<'d, T> {
&mut self, &mut self,
address: u8, address: u8,
operations: &mut [Operation<'_>], operations: &mut [Operation<'_>],
tx_ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
last_op: Option<&Operation<'_>>, last_op: Option<&Operation<'_>>,
inten: bool, inten: bool,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
@ -397,7 +402,7 @@ impl<'d, T: Instance> Twim<'d, T> {
// Set up DMA buffers. // Set up DMA buffers.
unsafe { unsafe {
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; self.set_tx_buffer(wr_buffer)?;
self.set_rx_buffer(rd_buffer)?; self.set_rx_buffer(rd_buffer)?;
} }
@ -450,7 +455,7 @@ impl<'d, T: Instance> Twim<'d, T> {
{ {
// Set up DMA buffers. // Set up DMA buffers.
unsafe { unsafe {
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; self.set_tx_buffer(wr_buffer)?;
self.set_rx_buffer(rd_buffer)?; self.set_rx_buffer(rd_buffer)?;
} }
@ -472,7 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> {
// Set up DMA buffers. // Set up DMA buffers.
unsafe { unsafe {
self.set_tx_buffer(buffer, tx_ram_buffer)?; self.set_tx_buffer(buffer)?;
} }
// Start write operation. // Start write operation.
@ -539,28 +544,9 @@ impl<'d, T: Instance> Twim<'d, T> {
/// An `Operation::Write` following an `Operation::Read` must have a /// An `Operation::Write` following an `Operation::Read` must have a
/// non-empty buffer. /// non-empty buffer.
pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { 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; let mut last_op = None;
while !operations.is_empty() { while !operations.is_empty() {
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), 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)?;
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 (in_progress, rest) = operations.split_at_mut(ops); let (in_progress, rest) = operations.split_at_mut(ops);
self.blocking_wait(); self.blocking_wait();
self.check_operations(in_progress)?; self.check_operations(in_progress)?;
@ -580,30 +566,9 @@ impl<'d, T: Instance> Twim<'d, T> {
mut operations: &mut [Operation<'_>], mut operations: &mut [Operation<'_>],
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
let mut last_op = None; let mut last_op = None;
while !operations.is_empty() { while !operations.is_empty() {
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), 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)?;
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 (in_progress, rest) = operations.split_at_mut(ops); let (in_progress, rest) = operations.split_at_mut(ops);
self.blocking_wait_timeout(timeout)?; self.blocking_wait_timeout(timeout)?;
self.check_operations(in_progress)?; 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 /// An `Operation::Write` following an `Operation::Read` must have a
/// non-empty buffer. /// non-empty buffer.
pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { 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; let mut last_op = None;
while !operations.is_empty() { while !operations.is_empty() {
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), 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)?;
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 (in_progress, rest) = operations.split_at_mut(ops); let (in_progress, rest) = operations.split_at_mut(ops);
self.async_wait().await?; self.async_wait().await?;
self.check_operations(in_progress)?; self.check_operations(in_progress)?;
@ -665,11 +611,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.blocking_transaction(address, &mut [Operation::Write(buffer)]) 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. /// Read from an I2C slave.
/// ///
/// The buffer must have a length of at most 255 bytes on the nRF52832 /// 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)]) 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. /// 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) 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. /// Read from an I2C slave.
/// ///
/// The buffer must have a length of at most 255 bytes on the nRF52832 /// 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. /// Read from an I2C slave.
@ -781,12 +685,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.transaction(address, &mut [Operation::Write(buffer)]).await 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 /// Write data to an I2C slave, then read data from the slave without
/// triggering a stop condition between the two. /// 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)]) self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
.await .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> { 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::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,
Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,
Self::Receive => 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 => { Self::AddressNack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
} }

View File

@ -1,11 +1,17 @@
//! OneWire pio driver //! 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; use crate::Peri;
/// This struct represents an onewire driver program /// This struct represents a onewire driver program
pub struct PioOneWireProgram<'a, PIO: Instance> { pub struct PioOneWireProgram<'a, PIO: Instance> {
prg: LoadedProgram<'a, PIO>, prg: LoadedProgram<'a, PIO>,
reset_addr: u8,
next_bit_addr: u8,
} }
impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { 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 { pub fn new(common: &mut Common<'a, PIO>) -> Self {
let prg = pio::pio_asm!( let prg = pio::pio_asm!(
r#" r#"
.wrap_target ; We need to use the pins direction to simulate open drain output
again: ; This results in all the side-set values being swapped from the actual pin value
pull block .side_set 1 pindirs
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);
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 /// Pio backed OneWire driver
pub struct PioOneWire<'d, PIO: Instance, const SM: usize> { pub struct PioOneWire<'d, PIO: Instance, const SM: usize> {
sm: StateMachine<'d, PIO, SM>, 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> { 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>, program: &PioOneWireProgram<'d, PIO>,
) -> Self { ) -> Self {
let pin = common.make_pio_pin(pin); 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(); let mut cfg = Config::default();
cfg.use_program(&program.prg, &[]); cfg.use_program(&program.prg, &[&pin]);
cfg.set_out_pins(&[&pin]);
cfg.set_in_pins(&[&pin]); cfg.set_in_pins(&[&pin]);
cfg.set_set_pins(&[&pin]);
cfg.shift_in = ShiftConfig { let shift_cfg = ShiftConfig {
auto_fill: true, auto_fill: true,
direction: ShiftDirection::Right, direction: ShiftDirection::Right,
threshold: 8, 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.set_config(&cfg);
sm.clear_fifos();
sm.restart();
unsafe {
sm.exec_jmp(program.next_bit_addr);
}
sm.set_enable(true); sm.set_enable(true);
Self { sm }
}
/// Write bytes over the wire Self {
pub async fn write_bytes(&mut self, bytes: &[u8]) { sm,
self.sm.tx().wait_push(250).await; cfg,
self.sm.tx().wait_push(bytes.len() as u32 - 1).await; reset_addr: program.reset_addr,
for b in bytes { next_bit_addr: program.next_bit_addr,
self.sm.tx().wait_push(*b as u32).await;
} }
} }
/// Read bytes from the wire /// Perform an initialization sequence, will return true if a presence pulse was detected from a device
pub async fn read_bytes(&mut self, bytes: &mut [u8]) { pub async fn reset(&mut self) -> bool {
self.sm.tx().wait_push(0).await; // The state machine immediately starts running when jumping to this address
self.sm.tx().wait_push(bytes.len() as u32 - 1).await; unsafe {
for b in bytes.iter_mut() { self.sm.exec_jmp(self.reset_addr);
*b = (self.sm.rx().wait_pull().await >> 24) as u8;
} }
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<u64> {
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<PIO: Instance, const SM: usize>(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option<u64> {
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
} }
} }

View File

@ -78,6 +78,9 @@ impl From<InverterChainLength> for u8 {
/// failed entropy checks. /// failed entropy checks.
/// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or /// 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. /// 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 { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Config { Config {
disable_autocorrelation_test: true, // WARNING: Disabling these tests increases likelihood of poor rng results.
disable_crngt_test: true, disable_autocorrelation_test: false,
disable_von_neumann_balancer: true, disable_crngt_test: false,
disable_von_neumann_balancer: false,
sample_count: 25, sample_count: 25,
inverter_chain_length: InverterChainLength::One, inverter_chain_length: InverterChainLength::One,
} }
@ -148,6 +152,7 @@ impl Default for Config {
/// ``` /// ```
pub struct Trng<'d, T: Instance> { pub struct Trng<'d, T: Instance> {
phantom: PhantomData<&'d mut T>, phantom: PhantomData<&'d mut T>,
config: Config,
} }
/// 12.12.1. Overview /// 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> { impl<'d, T: Instance> Trng<'d, T> {
/// Create a new TRNG driver. /// Create a new TRNG driver.
pub fn new(_trng: Peri<'d, T>, _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, config: Config) -> Self { pub fn new(_trng: Peri<'d, T>, _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, config: Config) -> Self {
let regs = T::regs(); let trng = Trng {
phantom: PhantomData,
regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); config: config,
};
let trng_config_register = regs.trng_config(); trng.initialize_rng();
trng_config_register.write(|w| { trng
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 }
} }
fn start_rng(&self) { 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)); 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) { fn enable_irq(&self) {
unsafe { T::Interrupt::enable() } unsafe { T::Interrupt::enable() }
} }
@ -218,6 +230,10 @@ impl<'d, T: Instance> Trng<'d, T> {
if trng_valid_register.read().ehr_valid().not() { if trng_valid_register.read().ehr_valid().not() {
if regs.rng_isr().read().autocorr_err() { if regs.rng_isr().read().autocorr_err() {
regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true)); 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 { } else {
panic!("RNG not busy, but ehr is not valid!") 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() { if trng_busy_register.read().trng_busy() {
Poll::Pending Poll::Pending
} else { } 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() { 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); self.read_ehr_registers_into_array(&mut buffer);
let remaining = destination_length - bytes_transferred; let remaining = destination_length - bytes_transferred;
@ -380,25 +399,36 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let regs = T::regs(); let regs = T::regs();
let isr = regs.rng_isr().read(); let isr = regs.rng_isr().read();
// Clear ehr bit
regs.rng_icr().write(|w| {
w.set_ehr_valid(true);
});
if isr.ehr_valid() { if isr.ehr_valid() {
regs.rng_icr().write(|w| {
w.set_ehr_valid(true);
});
T::waker().wake(); 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 // 12.12.5. List of Registers
// ... // ...
// TRNG: RNG_ISR Register // TRNG: RNG_ISR Register
// ... // ...
// AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row. // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row.
// When set, RNG ceases functioning until next reset // When set, RNG ceases functioning until next reset
if isr.autocorr_err() { warn!("TRNG Autocorrect error! Resetting TRNG. Increase sample count to reduce likelihood");
warn!("TRNG Autocorrect error! Resetting TRNG"); regs.trng_sw_reset().write(|w| {
regs.trng_sw_reset().write(|w| { w.set_trng_sw_reset(true);
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();
} }
} }
} }

View File

@ -684,6 +684,8 @@ fn main() {
PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock), 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 // 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 // This condition should be checked in stm32-data
let stop_mode = match rcc.stop_mode { let stop_mode = match rcc.stop_mode {
@ -697,6 +699,9 @@ fn main() {
fn frequency() -> crate::time::Hertz { fn frequency() -> crate::time::Hertz {
#clock_frequency #clock_frequency
} }
fn bus_frequency() -> crate::time::Hertz {
#bus_clock_frequency
}
const RCC_INFO: crate::rcc::RccInfo = unsafe { const RCC_INFO: crate::rcc::RccInfo = unsafe {
crate::rcc::RccInfo::new( crate::rcc::RccInfo::new(

View File

@ -143,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> {
T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc()));
let frequency = Hertz(T::frequency().0 / prescaler.divisor()); 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 { 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 ); 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 );

View File

@ -11,7 +11,7 @@
#[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_l0, path = "v1.rs")] #[cfg_attr(adc_l0, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.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(any(adc_v4, adc_u5), path = "v4.rs")]
#[cfg_attr(adc_g4, path = "g4.rs")] #[cfg_attr(adc_g4, path = "g4.rs")]
#[cfg_attr(adc_c0, path = "c0.rs")] #[cfg_attr(adc_c0, path = "c0.rs")]
@ -108,6 +108,7 @@ pub(crate) fn blocking_delay_us(us: u32) {
adc_g0, adc_g0,
adc_u0, adc_u0,
adc_h5, adc_h5,
adc_h7rs,
adc_u5, adc_u5,
adc_c0 adc_c0
)))] )))]
@ -129,6 +130,7 @@ pub trait Instance: SealedInstance + crate::PeripheralType {
adc_g0, adc_g0,
adc_u0, adc_u0,
adc_h5, adc_h5,
adc_h7rs,
adc_u5, adc_u5,
adc_c0 adc_c0
))] ))]

View File

@ -19,7 +19,7 @@ impl<T: Instance> SealedAdcChannel<T> for VrefInt {
cfg_if! { cfg_if! {
if #[cfg(adc_g0)] { if #[cfg(adc_g0)] {
let val = 13; let val = 13;
} else if #[cfg(adc_h5)] { } else if #[cfg(any(adc_h5, adc_h7rs))] {
let val = 17; let val = 17;
} else if #[cfg(adc_u0)] { } else if #[cfg(adc_u0)] {
let val = 12; let val = 12;
@ -38,7 +38,7 @@ impl<T: Instance> SealedAdcChannel<T> for Temperature {
cfg_if! { cfg_if! {
if #[cfg(adc_g0)] { if #[cfg(adc_g0)] {
let val = 12; let val = 12;
} else if #[cfg(adc_h5)] { } else if #[cfg(any(adc_h5, adc_h7rs))] {
let val = 16; let val = 16;
} else if #[cfg(adc_u0)] { } else if #[cfg(adc_u0)] {
let val = 11; let val = 11;
@ -57,9 +57,9 @@ impl<T: Instance> SealedAdcChannel<T> for Vbat {
cfg_if! { cfg_if! {
if #[cfg(adc_g0)] { if #[cfg(adc_g0)] {
let val = 14; let val = 14;
} else if #[cfg(adc_h5)] { } else if #[cfg(any(adc_h5, adc_h7rs))] {
let val = 2; let val = 2;
} else if #[cfg(adc_h5)] { } else if #[cfg(any(adc_h5, adc_h7rs))] {
let val = 13; let val = 13;
} else { } else {
let val = 18; let val = 18;
@ -70,7 +70,7 @@ impl<T: Instance> SealedAdcChannel<T> for Vbat {
} }
cfg_if! { cfg_if! {
if #[cfg(adc_h5)] { if #[cfg(any(adc_h5, adc_h7rs))] {
pub struct VddCore; pub struct VddCore;
impl<T: Instance> AdcChannel<T> for VddCore {} impl<T: Instance> AdcChannel<T> for VddCore {}
impl<T: Instance> super::SealedAdcChannel<T> for VddCore { impl<T: Instance> super::SealedAdcChannel<T> for VddCore {
@ -171,7 +171,7 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().ccr().modify(|reg| { T::regs().ccr().modify(|reg| {
reg.set_tsen(true); reg.set_tsen(true);
}); });
} else if #[cfg(adc_h5)] { } else if #[cfg(any(adc_h5, adc_h7rs))] {
T::common_regs().ccr().modify(|reg| { T::common_regs().ccr().modify(|reg| {
reg.set_tsen(true); reg.set_tsen(true);
}); });
@ -191,7 +191,7 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().ccr().modify(|reg| { T::regs().ccr().modify(|reg| {
reg.set_vbaten(true); reg.set_vbaten(true);
}); });
} else if #[cfg(adc_h5)] { } else if #[cfg(any(adc_h5, adc_h7rs))] {
T::common_regs().ccr().modify(|reg| { T::common_regs().ccr().modify(|reg| {
reg.set_vbaten(true); reg.set_vbaten(true);
}); });
@ -414,7 +414,7 @@ impl<'d, T: Instance> Adc<'d, T> {
fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
// RM0492, RM0481, etc. // RM0492, RM0481, etc.
// "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." // "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 { if channel.channel() == 0 {
T::regs().or().modify(|reg| reg.set_op0(true)); T::regs().or().modify(|reg| reg.set_op0(true));
} }
@ -447,7 +447,7 @@ impl<'d, T: Instance> Adc<'d, T> {
// RM0492, RM0481, etc. // RM0492, RM0481, etc.
// "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." // "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 { if channel.channel() == 0 {
T::regs().or().modify(|reg| reg.set_op0(false)); 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))] { if #[cfg(any(adc_g0, adc_u0))] {
// On G0 and U6 all channels use the same sampling time. // On G0 and U6 all channels use the same sampling time.
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); 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 { match _ch {
0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), 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())), _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())),

View File

@ -305,7 +305,7 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().cfgr2().modify(|reg| { T::regs().cfgr2().modify(|reg| {
reg.set_rovse(enable); reg.set_rovse(enable);
reg.set_osvr(samples); reg.set_ovsr(samples);
reg.set_ovss(right_shift); reg.set_ovss(right_shift);
}) })
} }

View File

@ -299,9 +299,9 @@ impl Registers {
mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8));
mb.tdlr() 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() 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(); let id: IdReg = frame.id().into();
mb.tir().write(|w| { mb.tir().write(|w| {
w.0 = id.0; w.0 = id.0;

View File

@ -75,7 +75,7 @@ impl Registers {
let mailbox = self.tx_buffer_element(bufidx); let mailbox = self.tx_buffer_element(bufidx);
mailbox.reset(); mailbox.reset();
put_tx_header(mailbox, header); put_tx_header(mailbox, header);
put_tx_data(mailbox, &buffer[..header.len() as usize]); put_tx_data(mailbox, buffer);
// Set <idx as Mailbox> as ready to transmit // Set <idx as Mailbox> as ready to transmit
self.regs.txbar().modify(|w| w.set_ar(bufidx, true)); self.regs.txbar().modify(|w| w.set_ar(bufidx, true));
@ -190,7 +190,7 @@ impl Registers {
DataLength::Fdcan(len) => len, DataLength::Fdcan(len) => len,
DataLength::Classic(len) => len, DataLength::Classic(len) => len,
}; };
if len as usize > ClassicData::MAX_DATA_LEN { if len as usize > 8 {
return None; return None;
} }

View File

@ -104,15 +104,13 @@ pub trait CanHeader: Sized {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClassicData { pub struct ClassicData {
pub(crate) bytes: [u8; Self::MAX_DATA_LEN], pub(crate) bytes: [u8; 8],
} }
impl ClassicData { impl ClassicData {
pub(crate) const MAX_DATA_LEN: usize = 8;
/// Creates a data payload from a raw byte slice. /// Creates a data payload from a raw byte slice.
/// ///
/// Returns `None` if `data` is more than 64 bytes (which is the maximum) or /// Returns `FrameCreateError` if `data` is more than 8 bytes (which is the maximum).
/// cannot be represented with an FDCAN DLC.
pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> { pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> {
if data.len() > 8 { if data.len() > 8 {
return Err(FrameCreateError::InvalidDataLength); return Err(FrameCreateError::InvalidDataLength);
@ -211,12 +209,17 @@ impl Frame {
/// Get reference to data /// Get reference to data
pub fn data(&self) -> &[u8] { 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 /// Get mutable reference to data
pub fn data_mut(&mut self) -> &mut [u8] { 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 /// Get priority of frame
@ -260,7 +263,7 @@ impl embedded_can::Frame for Frame {
self.can_header.len as usize self.can_header.len as usize
} }
fn data(&self) -> &[u8] { fn data(&self) -> &[u8] {
&self.data.raw() &self.data()
} }
} }
@ -405,12 +408,12 @@ impl FdFrame {
/// Get reference to data /// Get reference to data
pub fn data(&self) -> &[u8] { pub fn data(&self) -> &[u8] {
&self.data.raw() &self.data.raw()[..self.can_header.len as usize]
} }
/// Get mutable reference to data /// Get mutable reference to data
pub fn data_mut(&mut self) -> &mut [u8] { 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 self.can_header.len as usize
} }
fn data(&self) -> &[u8] { fn data(&self) -> &[u8] {
&self.data.raw() &self.data()
} }
} }

View File

@ -37,22 +37,12 @@ enum OpAmpDifferentialPair {
/// Speed /// Speed
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Clone, Copy)] #[derive(Clone, Copy, PartialEq)]
pub enum OpAmpSpeed { pub enum OpAmpSpeed {
Normal, Normal,
HighSpeed, HighSpeed,
} }
#[cfg(opamp_g4)]
impl From<OpAmpSpeed> 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. /// OpAmp external outputs, wired to a GPIO pad.
/// ///
/// This struct can also be used as an ADC input. /// 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 { pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
#[cfg(opamp_g4)] #[cfg(opamp_g4)]
T::regs().csr().modify(|w| { T::regs().csr().modify(|w| {
w.set_opahsm(speed.into()); w.set_opahsm(speed == OpAmpSpeed::HighSpeed);
}); });
Self { _inner: opamp } 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_vp_sel(VpSel::from_bits(in_pin.channel()));
w.set_vm_sel(vm_sel); w.set_vm_sel(vm_sel);
#[cfg(opamp_g4)] #[cfg(opamp_g4)]
w.set_opaintoen(Opaintoen::OUTPUT_PIN); w.set_opaintoen(false);
w.set_opampen(true); w.set_opampen(true);
}); });
@ -166,7 +156,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
w.set_vm_sel(vm_sel); w.set_vm_sel(vm_sel);
w.set_pga_gain(pga_gain); w.set_pga_gain(pga_gain);
#[cfg(opamp_g4)] #[cfg(opamp_g4)]
w.set_opaintoen(Opaintoen::OUTPUT_PIN); w.set_opaintoen(false);
w.set_opampen(true); w.set_opampen(true);
}); });
@ -189,7 +179,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
w.set_vm_sel(VmSel::OUTPUT); w.set_vm_sel(VmSel::OUTPUT);
w.set_vp_sel(VpSel::DAC3_CH1); w.set_vp_sel(VpSel::DAC3_CH1);
w.set_opaintoen(Opaintoen::OUTPUT_PIN); w.set_opaintoen(false);
w.set_opampen(true); 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_vp_sel(VpSel::from_bits(pin.channel()));
w.set_vm_sel(VmSel::OUTPUT); w.set_vm_sel(VmSel::OUTPUT);
#[cfg(opamp_g4)] #[cfg(opamp_g4)]
w.set_opaintoen(Opaintoen::ADCCHANNEL); w.set_opaintoen(true);
w.set_opampen(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_vp_sel(VpSel::from_bits(pin.channel()));
w.set_vm_sel(VmSel::OUTPUT); w.set_vm_sel(VmSel::OUTPUT);
w.set_pga_gain(pga_gain); w.set_pga_gain(pga_gain);
w.set_opaintoen(Opaintoen::ADCCHANNEL); w.set_opaintoen(true);
w.set_opampen(true); w.set_opampen(true);
}); });
@ -278,7 +268,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
use crate::pac::opamp::vals::*; use crate::pac::opamp::vals::*;
w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx
w.set_vm_sel(VmSel::from_bits(m_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); w.set_opampen(true);
}); });
@ -308,7 +298,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
use crate::pac::opamp::vals::*; use crate::pac::opamp::vals::*;
w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx
w.set_vm_sel(VmSel::from_bits(m_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); w.set_opampen(true);
}); });
@ -340,7 +330,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
use crate::pac::opamp::vals::*; use crate::pac::opamp::vals::*;
w.set_vp_sel(VpSel::from_bits(p_pin.channel())); w.set_vp_sel(VpSel::from_bits(p_pin.channel()));
w.set_vm_sel(VmSel::from_bits(m_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); w.set_opampen(true);
}); });
@ -369,7 +359,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
use crate::pac::opamp::vals::*; use crate::pac::opamp::vals::*;
w.set_vp_sel(VpSel::from_bits(p_pin.channel())); w.set_vp_sel(VpSel::from_bits(p_pin.channel()));
w.set_vm_sel(VmSel::from_bits(m_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); w.set_opampen(true);
}); });
@ -389,17 +379,14 @@ impl<'d, T: Instance> OpAmp<'d, T> {
T::regs().csr().modify(|w| { T::regs().csr().modify(|w| {
w.set_opampen(true); w.set_opampen(true);
w.set_calon(true); w.set_calon(true);
w.set_usertrim(Usertrim::USER); w.set_usertrim(true);
}); });
match T::regs().csr().read().opahsm() { if T::regs().csr().read().opahsm() {
Opahsm::NORMAL => { self.calibrate_differential_pair(OpAmpDifferentialPair::P);
self.calibrate_differential_pair(OpAmpDifferentialPair::P); } else {
self.calibrate_differential_pair(OpAmpDifferentialPair::N); self.calibrate_differential_pair(OpAmpDifferentialPair::P);
} self.calibrate_differential_pair(OpAmpDifferentialPair::N);
Opahsm::HIGH_SPEED => {
self.calibrate_differential_pair(OpAmpDifferentialPair::P);
}
} }
T::regs().csr().modify(|w| { T::regs().csr().modify(|w| {
@ -435,11 +422,13 @@ impl<'d, T: Instance> OpAmp<'d, T> {
T::regs().csr().modify(|w| match pair { T::regs().csr().modify(|w| match pair {
OpAmpDifferentialPair::P => { OpAmpDifferentialPair::P => {
defmt::info!("p calibration. offset: {}", mid); #[cfg(feature = "defmt")]
defmt::debug!("opamp p calibration. offset: {}", mid);
w.set_trimoffsetp(mid); w.set_trimoffsetp(mid);
} }
OpAmpDifferentialPair::N => { OpAmpDifferentialPair::N => {
defmt::info!("n calibration. offset: {}", mid); #[cfg(feature = "defmt")]
defmt::debug!("opamp n calibration. offset: {}", mid);
w.set_trimoffsetn(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 // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7
blocking_delay_ms(2); blocking_delay_ms(2);
if T::regs().csr().read().outcal() == Outcal::LOW { if !T::regs().csr().read().calout() {
if mid == 0 { if mid == 0 {
break; break;
} }

View File

@ -97,6 +97,8 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks {
pub(crate) trait SealedRccPeripheral { pub(crate) trait SealedRccPeripheral {
fn frequency() -> Hertz; fn frequency() -> Hertz;
#[allow(dead_code)]
fn bus_frequency() -> Hertz;
const RCC_INFO: RccInfo; const RCC_INFO: RccInfo;
} }

View File

@ -284,6 +284,10 @@ impl<'d, M: PeriMode> Spi<'d, M> {
#[cfg(any(spi_v3, spi_v4, spi_v5))] #[cfg(any(spi_v3, spi_v4, spi_v5))]
{ {
self.info.regs.cr1().modify(|w| {
w.set_spe(false);
});
self.info.regs.cfg2().modify(|w| { self.info.regs.cfg2().modify(|w| {
w.set_cpha(cpha); w.set_cpha(cpha);
w.set_cpol(cpol); w.set_cpol(cpol);
@ -292,6 +296,10 @@ impl<'d, M: PeriMode> Spi<'d, M> {
self.info.regs.cfg1().modify(|w| { self.info.regs.cfg1().modify(|w| {
w.set_mbr(br); w.set_mbr(br);
}); });
self.info.regs.cr1().modify(|w| {
w.set_spe(true);
});
} }
Ok(()) Ok(())
} }

View File

@ -240,11 +240,11 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) {
let (these_bits, result) = if target < 128 { let (these_bits, result) = if target < 128 {
(target as u8, target) (target as u8, target)
} else if target < 255 { } 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 { } 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 { } else if target < 1008 {
(32 + (target / 16) as u8, (target - target % 16)) ((32 + (target / 16) as u8) | 224, (target - target % 16))
} else { } else {
(u8::MAX, 1008) (u8::MAX, 1008)
}; };
@ -300,7 +300,7 @@ mod tests {
TestRun { TestRun {
value: 400, value: 400,
ckd: Ckd::DIV1, ckd: Ckd::DIV1,
bits: 32 + (400u16 / 8) as u8, bits: 210,
}, },
TestRun { TestRun {
value: 600, value: 600,

View File

@ -549,7 +549,7 @@ foreach_interrupt!(
); );
fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 { fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 {
let ahb_freq = T::frequency().0; let ahb_freq = T::bus_frequency().0;
match speed { match speed {
Dspd::HIGH_SPEED => { Dspd::HIGH_SPEED => {
// From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)

View File

@ -12,7 +12,7 @@ Synchronization primitives and data structures with async support:
- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks. - [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks.
- [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits. - [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits.
- [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. - [`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. - [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s.
- [`LazyLock`](lazy_lock::LazyLock) - A value which is initialized on the first access - [`LazyLock`](lazy_lock::LazyLock) - A value which is initialized on the first access

View File

@ -5,6 +5,9 @@ use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex};
use crate::blocking_mutex::Mutex; use crate::blocking_mutex::Mutex;
/// Utility struct to register and wake a 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.
/// 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<M: RawMutex> { pub struct GenericAtomicWaker<M: RawMutex> {
waker: Mutex<M, Cell<Option<Waker>>>, waker: Mutex<M, Cell<Option<Waker>>>,
} }

View File

@ -4,6 +4,9 @@ use core::sync::atomic::{AtomicPtr, Ordering};
use core::task::Waker; use core::task::Waker;
/// Utility struct to register and wake a 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 { pub struct AtomicWaker {
waker: AtomicPtr<()>, waker: AtomicPtr<()>,
} }

View File

@ -3,6 +3,8 @@ use core::task::Waker;
use heapless::Vec; use heapless::Vec;
/// Utility struct to register and wake multiple wakers. /// 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<const N: usize> { pub struct MultiWakerRegistration<const N: usize> {
wakers: Vec<Waker, N>, wakers: Vec<Waker, N>,
} }

View File

@ -2,6 +2,10 @@ use core::mem;
use core::task::Waker; use core::task::Waker;
/// Utility struct to register and wake a 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)] #[derive(Debug, Default)]
pub struct WakerRegistration { pub struct WakerRegistration {
waker: Option<Waker>, waker: Option<Waker>,

View File

@ -226,6 +226,8 @@ tick-hz-128_000_000 = []
tick-hz-130_000_000 = [] tick-hz-130_000_000 = []
## 131.072MHz Tick Rate ## 131.072MHz Tick Rate
tick-hz-131_072_000 = [] tick-hz-131_072_000 = []
## 133.0MHz Tick Rate
tick-hz-133_000_000 = []
## 140.0MHz Tick Rate ## 140.0MHz Tick Rate
tick-hz-140_000_000 = [] tick-hz-140_000_000 = []
## 144.0MHz Tick Rate ## 144.0MHz Tick Rate

View File

@ -22,6 +22,7 @@ for i in range(1, 30):
ticks.append(10 * i * 1_000_000) ticks.append(10 * i * 1_000_000)
for i in range(15, 50): for i in range(15, 50):
ticks.append(20 * i * 1_000_000) ticks.append(20 * i * 1_000_000)
ticks.append(133 * 1_000_000)
seen = set() seen = set()
ticks = sorted([x for x in ticks if not (x in seen or seen.add(x))]) ticks = sorted([x for x in ticks if not (x in seen or seen.add(x))])

View File

@ -182,6 +182,8 @@ pub const TICK_HZ: u64 = 128_000_000;
pub const TICK_HZ: u64 = 130_000_000; pub const TICK_HZ: u64 = 130_000_000;
#[cfg(feature = "tick-hz-131_072_000")] #[cfg(feature = "tick-hz-131_072_000")]
pub const TICK_HZ: u64 = 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")] #[cfg(feature = "tick-hz-140_000_000")]
pub const TICK_HZ: u64 = 140_000_000; pub const TICK_HZ: u64 = 140_000_000;
#[cfg(feature = "tick-hz-144_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-128_000_000",
feature = "tick-hz-130_000_000", feature = "tick-hz-130_000_000",
feature = "tick-hz-131_072_000", feature = "tick-hz-131_072_000",
feature = "tick-hz-133_000_000",
feature = "tick-hz-140_000_000", feature = "tick-hz-140_000_000",
feature = "tick-hz-144_000_000", feature = "tick-hz-144_000_000",
feature = "tick-hz-150_000_000", feature = "tick-hz-150_000_000",

View File

@ -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"] tick-hz-130_000_000 = ["embassy-time-driver/tick-hz-130_000_000"]
## 131.072MHz Tick Rate ## 131.072MHz Tick Rate
tick-hz-131_072_000 = ["embassy-time-driver/tick-hz-131_072_000"] 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 ## 140.0MHz Tick Rate
tick-hz-140_000_000 = ["embassy-time-driver/tick-hz-140_000_000"] tick-hz-140_000_000 = ["embassy-time-driver/tick-hz-140_000_000"]
## 144.0MHz Tick Rate ## 144.0MHz Tick Rate

View File

@ -1,8 +1,7 @@
use core::future::{poll_fn, Future}; use core::future::{poll_fn, Future};
use core::pin::{pin, Pin}; use core::pin::Pin;
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use futures_util::future::{select, Either};
use futures_util::stream::FusedStream; use futures_util::stream::FusedStream;
use futures_util::Stream; 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, /// 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. /// 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<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { pub fn with_timeout<F: Future>(timeout: Duration, fut: F) -> TimeoutFuture<F> {
let timeout_fut = Timer::after(timeout); TimeoutFuture {
match select(pin!(fut), timeout_fut).await { timer: Timer::after(timeout),
Either::Left((r, _)) => Ok(r), fut,
Either::Right(_) => Err(TimeoutError),
} }
} }
@ -29,16 +27,15 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
/// ///
/// If the future completes before the deadline, its output is returned. Otherwise, on timeout, /// 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. /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> { pub fn with_deadline<F: Future>(at: Instant, fut: F) -> TimeoutFuture<F> {
let timeout_fut = Timer::at(at); TimeoutFuture {
match select(pin!(fut), timeout_fut).await { timer: Timer::at(at),
Either::Left((r, _)) => Ok(r), fut,
Either::Right(_) => Err(TimeoutError),
} }
} }
/// Provides functions to run a given future with a timeout or a deadline. /// 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. /// Output type of the future.
type Output; type Output;
@ -46,24 +43,50 @@ pub trait WithTimeout {
/// ///
/// If the future completes before the timeout, its output is returned. Otherwise, on timeout, /// 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. /// 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<Self::Output, TimeoutError>; fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self>;
/// Runs a given future with a deadline time. /// Runs a given future with a deadline time.
/// ///
/// If the future completes before the deadline, its output is returned. Otherwise, on timeout, /// 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. /// 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<Self::Output, TimeoutError>; fn with_deadline(self, at: Instant) -> TimeoutFuture<Self>;
} }
impl<F: Future> WithTimeout for F { impl<F: Future> WithTimeout for F {
type Output = F::Output; type Output = F::Output;
async fn with_timeout(self, timeout: Duration) -> Result<Self::Output, TimeoutError> { fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self> {
with_timeout(timeout, self).await with_timeout(timeout, self)
} }
async fn with_deadline(self, at: Instant) -> Result<Self::Output, TimeoutError> { fn with_deadline(self, at: Instant) -> TimeoutFuture<Self> {
with_deadline(at, self).await 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<F> {
timer: Timer,
fut: F,
}
impl<F: Unpin> Unpin for TimeoutFuture<F> {}
impl<F: Future> Future for TimeoutFuture<F> {
type Output = Result<F::Output, TimeoutError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
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
} }
} }

View File

@ -13,9 +13,25 @@ use crate::consts::{
}; };
use crate::Reset; 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 /// Internal state for the DFU class
pub struct Control<'d, STATE: NorFlash, RST: Reset> { pub struct Control<MARK: DfuMarker, RST: Reset> {
firmware_state: BlockingFirmwareState<'d, STATE>, dfu_marker: MARK,
attrs: DfuAttributes, attrs: DfuAttributes,
state: State, state: State,
timeout: Option<Duration>, timeout: Option<Duration>,
@ -23,11 +39,11 @@ pub struct Control<'d, STATE: NorFlash, RST: Reset> {
_rst: PhantomData<RST>, _rst: PhantomData<RST>,
} }
impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { impl<MARK: DfuMarker, RST: Reset> Control<MARK, RST> {
/// Create a new DFU instance to expose a DFU interface. /// 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 { Control {
firmware_state, dfu_marker,
attrs, attrs,
state: State::AppIdle, state: State::AppIdle,
detach_start: None, 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<MARK: DfuMarker, RST: Reset> Handler for Control<MARK, RST> {
fn reset(&mut self) { fn reset(&mut self) {
if let Some(start) = self.detach_start { if let Some(start) = self.detach_start {
let delta = Instant::now() - 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() timeout.as_millis()
); );
if delta < timeout { if delta < timeout {
self.firmware_state self.dfu_marker.mark_dfu();
.mark_dfu()
.expect("Failed to mark DFU mode in bootloader");
RST::sys_reset() 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. /// 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. /// 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>, builder: &mut Builder<'d, D>,
handler: &'d mut Control<'d, STATE, RST>, handler: &'d mut Control<MARK, RST>,
timeout: Duration, timeout: Duration,
) { ) {
let mut func = builder.function(0x00, 0x00, 0x00); let mut func = builder.function(0x00, 0x00, 0x00);

View File

@ -218,10 +218,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.bos_descriptor.end_bos(); self.bos_descriptor.end_bos();
// Log the number of allocator bytes actually used in descriptor buffers // Log the number of allocator bytes actually used in descriptor buffers
info!("USB: config_descriptor used: {}", self.config_descriptor.position()); trace!("USB: config_descriptor used: {}", self.config_descriptor.position());
info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); trace!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
info!("USB: msos_descriptor used: {}", msos_descriptor.len()); trace!("USB: msos_descriptor used: {}", msos_descriptor.len());
info!("USB: control_buf size: {}", self.control_buf.len()); trace!("USB: control_buf size: {}", self.control_buf.len());
UsbDevice::build( UsbDevice::build(
self.driver, self.driver,

View File

@ -9,6 +9,7 @@ use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_nrf::twim::{self, Twim}; use embassy_nrf::twim::{self, Twim};
use embassy_nrf::{bind_interrupts, peripherals}; use embassy_nrf::{bind_interrupts, peripherals};
use static_cell::ConstStaticCell;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
const ADDRESS: u8 = 0x50; const ADDRESS: u8 = 0x50;
@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default()); let p = embassy_nrf::init(Default::default());
info!("Initializing TWI..."); info!("Initializing TWI...");
let config = twim::Config::default(); 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..."); info!("Reading...");

View File

@ -30,6 +30,7 @@ async fn main(_p: Spawner) {
loop { loop {
info!("Initializing TWI..."); info!("Initializing TWI...");
let config = twim::Config::default(); let config = twim::Config::default();
let mut ram_buffer = [0u8; 16];
// Create the TWIM instance with borrowed singletons, so they're not consumed. // Create the TWIM instance with borrowed singletons, so they're not consumed.
let mut twi = Twim::new( let mut twi = Twim::new(
@ -38,6 +39,7 @@ async fn main(_p: Spawner) {
p.P0_03.reborrow(), p.P0_03.reborrow(),
p.P0_04.reborrow(), p.P0_04.reborrow(),
config, config,
&mut ram_buffer,
); );
info!("Reading..."); info!("Reading...");

View File

@ -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_std]
#![no_main] #![no_main]
@ -6,9 +6,10 @@ use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::bind_interrupts; use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::PIO0; use embassy_rp::peripherals::PIO0;
use embassy_rp::pio::{self, InterruptHandler, Pio}; use embassy_rp::pio::{InterruptHandler, Pio};
use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
use embassy_time::Timer; use embassy_time::Timer;
use heapless::Vec;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs { bind_interrupts!(struct Irqs {
@ -21,63 +22,66 @@ async fn main(_spawner: Spawner) {
let mut pio = Pio::new(p.PIO0, Irqs); let mut pio = Pio::new(p.PIO0, Irqs);
let prg = PioOneWireProgram::new(&mut pio.common); 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::<u64, 10>::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 { 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 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
match sensor.temperature().await {
Ok(temp) => info!("temp = {:?} deg C", temp), // Read all devices one by one
_ => error!("sensor error"), 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; Timer::after_secs(1).await;
} }
} }
/// DS18B20 temperature sensor driver fn crc8(data: &[u8]) -> u8 {
pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { let mut crc = 0;
wire: PioOneWire<'d, PIO, SM>, for b in data {
} let mut data_byte = *b;
for _ in 0..8 {
impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { let temp = (crc ^ data_byte) & 0x01;
pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { crc >>= 1;
Self { wire } if temp != 0 {
} crc ^= 0x8C;
/// 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;
} }
} data_byte >>= 1;
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<f32, ()> {
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(()),
} }
} }
crc
} }