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.
rm -rf out/tests/stm32f103c8
rm -rf out/tests/stm32l073rz
rm -rf out/tests/nrf52840-dk
rm -rf out/tests/nrf52833-dk

View File

@ -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<T: Instance> interrupt::typelevel::Handler<T::Interrupt> 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<T::Interrupt, InterruptHandler<T>> + '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<u8>; 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<u8>] = unsafe { core::mem::transmute(buffer) };
ram_buffer.copy_from_slice(uninit_src);
unsafe { &*(ram_buffer as *const [MaybeUninit<u8>] 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<u8>; FORCE_COPY_BUFFER_SIZE]>,
last_op: Option<&Operation<'_>>,
inten: bool,
) -> Result<usize, Error> {
@ -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)
}

View File

@ -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#"
; 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
; 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
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
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
"#,
"#
);
let prg = common.load_program(&prg.program);
Self { prg }
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<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.
/// 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<T::Interrupt, InterruptHandler<T>> + '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<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
unsafe fn on_interrupt() {
let regs = T::regs();
let isr = regs.rng_isr().read();
// Clear ehr bit
if isr.ehr_valid() {
regs.rng_icr().write(|w| {
w.set_ehr_valid(true);
});
if isr.ehr_valid() {
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");
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();
}
}
}

View File

@ -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(

View File

@ -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 );

View File

@ -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
))]

View File

@ -19,7 +19,7 @@ impl<T: Instance> SealedAdcChannel<T> 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<T: Instance> SealedAdcChannel<T> 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<T: Instance> SealedAdcChannel<T> 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<T: Instance> SealedAdcChannel<T> for Vbat {
}
cfg_if! {
if #[cfg(adc_h5)] {
if #[cfg(any(adc_h5, adc_h7rs))] {
pub struct VddCore;
impl<T: Instance> AdcChannel<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| {
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<T>, 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())),

View File

@ -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);
})
}

View File

@ -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;

View File

@ -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 <idx as Mailbox> 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;
}

View File

@ -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<Self, FrameCreateError> {
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()
}
}

View File

@ -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<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.
///
/// 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,18 +379,15 @@ 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 => {
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);
}
Opahsm::HIGH_SPEED => {
self.calibrate_differential_pair(OpAmpDifferentialPair::P);
}
}
T::regs().csr().modify(|w| {
w.set_calon(false);
@ -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;
}

View File

@ -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;
}

View File

@ -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(())
}

View File

@ -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,

View File

@ -549,7 +549,7 @@ foreach_interrupt!(
);
fn calculate_trdt<T: Instance>(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)

View File

@ -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

View File

@ -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<M: RawMutex> {
waker: Mutex<M, Cell<Option<Waker>>>,
}

View File

@ -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<()>,
}

View File

@ -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<const N: usize> {
wakers: Vec<Waker, N>,
}

View File

@ -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<Waker>,

View File

@ -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

View File

@ -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))])

View File

@ -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",

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"]
## 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

View File

@ -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<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> {
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<F: Future>(timeout: Duration, fut: F) -> TimeoutFuture<F> {
TimeoutFuture {
timer: Timer::after(timeout),
fut,
}
}
@ -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,
/// 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> {
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<F: Future>(at: Instant, fut: F) -> TimeoutFuture<F> {
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<Self::Output, TimeoutError>;
fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self>;
/// 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<Self::Output, TimeoutError>;
fn with_deadline(self, at: Instant) -> TimeoutFuture<Self>;
}
impl<F: Future> WithTimeout for F {
type Output = F::Output;
async fn with_timeout(self, timeout: Duration) -> Result<Self::Output, TimeoutError> {
with_timeout(timeout, self).await
fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self> {
with_timeout(timeout, self)
}
async fn with_deadline(self, at: Instant) -> Result<Self::Output, TimeoutError> {
with_deadline(at, self).await
fn with_deadline(self, at: Instant) -> TimeoutFuture<Self> {
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;
/// 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<MARK: DfuMarker, RST: Reset> {
dfu_marker: MARK,
attrs: DfuAttributes,
state: State,
timeout: Option<Duration>,
@ -23,11 +39,11 @@ pub struct Control<'d, STATE: NorFlash, RST: Reset> {
_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.
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<MARK: DfuMarker, RST: Reset> Handler for Control<MARK, RST> {
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<MARK, RST>,
timeout: Duration,
) {
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();
// 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,

View File

@ -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...");

View File

@ -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...");

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_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,40 +22,60 @@ 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::<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 {
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;
let mut data_byte = *b;
for _ in 0..8 {
temp = (crc ^ data_byte) & 0x01;
let temp = (crc ^ data_byte) & 0x01;
crc >>= 1;
if temp != 0 {
crc ^= 0x8C;
@ -64,20 +85,3 @@ impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
}
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(()),
}
}
}