Merge branch 'main' into configurable-bank-support
This commit is contained in:
commit
f713f170a1
1
ci.sh
1
ci.sh
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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 );
|
||||||
|
|||||||
@ -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
|
||||||
))]
|
))]
|
||||||
|
|||||||
@ -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())),
|
||||||
|
|||||||
@ -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);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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>>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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<()>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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))])
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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...");
|
||||||
|
|
||||||
|
|||||||
@ -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...");
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user