Compare commits
9 Commits
executor-0
...
h7-sai4onl
Author | SHA1 | Date | |
---|---|---|---|
5221705495 | |||
5bc7557826 | |||
006260fedd | |||
e3ee24017d | |||
467b53076c | |||
27e6634c9d | |||
0f2208c0af | |||
6c42885d4a | |||
3b33cc4691 |
1
ci.sh
1
ci.sh
@ -110,6 +110,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h725re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
|
use core::iter::zip;
|
||||||
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
@ -16,6 +17,12 @@ pub struct Error {
|
|||||||
pub status: u32,
|
pub status: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AddMulticastAddressError {
|
||||||
|
NotMulticast,
|
||||||
|
NoFreeSlots,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Control<'a> {
|
pub struct Control<'a> {
|
||||||
state_ch: ch::StateRunner<'a>,
|
state_ch: ch::StateRunner<'a>,
|
||||||
events: &'a Events,
|
events: &'a Events,
|
||||||
@ -316,6 +323,54 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
|
self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add specified address to the list of hardware addresses the device
|
||||||
|
/// listens on. The address must be a Group address (I/G bit set). Up
|
||||||
|
/// to 10 addresses are supported by the firmware. Returns the number of
|
||||||
|
/// address slots filled after adding, or an error.
|
||||||
|
pub async fn add_multicast_address(&mut self, address: [u8; 6]) -> Result<usize, AddMulticastAddressError> {
|
||||||
|
// The firmware seems to ignore non-multicast addresses, so let's
|
||||||
|
// prevent the user from adding them and wasting space.
|
||||||
|
if address[0] & 0x01 != 1 {
|
||||||
|
return Err(AddMulticastAddressError::NotMulticast);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
self.get_iovar("mcast_list", &mut buf).await;
|
||||||
|
|
||||||
|
let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;
|
||||||
|
let (used, free) = buf[4..].split_at_mut(n * 6);
|
||||||
|
|
||||||
|
if used.chunks(6).any(|a| a == address) {
|
||||||
|
return Ok(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if free.len() < 6 {
|
||||||
|
return Err(AddMulticastAddressError::NoFreeSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
free[..6].copy_from_slice(&address);
|
||||||
|
let n = n + 1;
|
||||||
|
buf[..4].copy_from_slice(&(n as u32).to_le_bytes());
|
||||||
|
|
||||||
|
self.set_iovar_v::<80>("mcast_list", &buf).await;
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the list of configured multicast hardware addresses.
|
||||||
|
pub async fn list_mulistcast_addresses(&mut self, result: &mut [[u8; 6]; 10]) -> usize {
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
self.get_iovar("mcast_list", &mut buf).await;
|
||||||
|
|
||||||
|
let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;
|
||||||
|
let used = &buf[4..][..n * 6];
|
||||||
|
|
||||||
|
for (addr, output) in zip(used.chunks(6), result.iter_mut()) {
|
||||||
|
output.copy_from_slice(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
|
async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
|
||||||
let mut buf = [0; 8];
|
let mut buf = [0; 8];
|
||||||
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
||||||
|
@ -27,7 +27,7 @@ use ioctl::IoctlState;
|
|||||||
|
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
pub use crate::control::{Control, Error as ControlError, Scanner};
|
pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, Scanner};
|
||||||
pub use crate::runner::Runner;
|
pub use crate::runner::Runner;
|
||||||
pub use crate::structs::BssInfo;
|
pub use crate::structs::BssInfo;
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
|
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
|
||||||
mod run_queue;
|
mod run_queue;
|
||||||
|
|
||||||
#[cfg_attr(target_has_atomic = "8", path = "state_atomics.rs")]
|
#[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")]
|
||||||
|
#[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")]
|
||||||
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
|
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
|
103
embassy-executor/src/raw/state_atomics_arm.rs
Normal file
103
embassy-executor/src/raw/state_atomics_arm.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
use core::arch::asm;
|
||||||
|
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
||||||
|
|
||||||
|
// Must be kept in sync with the layout of `State`!
|
||||||
|
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||||
|
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
|
||||||
|
|
||||||
|
#[repr(C, align(4))]
|
||||||
|
pub(crate) struct State {
|
||||||
|
/// Task is spawned (has a future)
|
||||||
|
spawned: AtomicBool,
|
||||||
|
/// Task is in the executor run queue
|
||||||
|
run_queued: AtomicBool,
|
||||||
|
/// Task is in the executor timer queue
|
||||||
|
timer_queued: AtomicBool,
|
||||||
|
pad: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> State {
|
||||||
|
Self {
|
||||||
|
spawned: AtomicBool::new(false),
|
||||||
|
run_queued: AtomicBool::new(false),
|
||||||
|
timer_queued: AtomicBool::new(false),
|
||||||
|
pad: AtomicBool::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_u32(&self) -> &AtomicU32 {
|
||||||
|
unsafe { &*(self as *const _ as *const AtomicU32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If task is idle, mark it as spawned + run_queued and return true.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn spawn(&self) -> bool {
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
let r = self
|
||||||
|
.as_u32()
|
||||||
|
.compare_exchange(
|
||||||
|
0,
|
||||||
|
STATE_SPAWNED | STATE_RUN_QUEUED,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
)
|
||||||
|
.is_ok();
|
||||||
|
compiler_fence(Ordering::Acquire);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn despawn(&self) {
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
self.spawned.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_enqueue(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
loop {
|
||||||
|
let state: u32;
|
||||||
|
asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack));
|
||||||
|
|
||||||
|
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
||||||
|
asm!("clrex", options(nomem, nostack));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let outcome: usize;
|
||||||
|
let new_state = state | STATE_RUN_QUEUED;
|
||||||
|
asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack));
|
||||||
|
if outcome == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as run-queued. Return whether the task is spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_dequeue(&self) -> bool {
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
|
||||||
|
let r = self.spawned.load(Ordering::Relaxed);
|
||||||
|
self.run_queued.store(false, Ordering::Relaxed);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_enqueue(&self) -> bool {
|
||||||
|
!self.timer_queued.swap(true, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as timer-queued.
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_dequeue(&self) {
|
||||||
|
self.timer_queued.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
@ -61,6 +61,7 @@ fn main() {
|
|||||||
let mut singletons: Vec<String> = Vec::new();
|
let mut singletons: Vec<String> = Vec::new();
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
if let Some(r) = &p.registers {
|
if let Some(r) = &p.registers {
|
||||||
|
println!("cargo:rustc-cfg=peri_{}", p.name.to_ascii_lowercase());
|
||||||
match r.kind {
|
match r.kind {
|
||||||
// Generate singletons per pin, not per port
|
// Generate singletons per pin, not per port
|
||||||
"gpio" => {
|
"gpio" => {
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
use core::cmp;
|
use core::cmp;
|
||||||
#[cfg(feature = "time")]
|
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
#[cfg(feature = "time")]
|
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
#[cfg(feature = "time")]
|
|
||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::dma::NoDma;
|
use crate::dma::{NoDma, Transfer};
|
||||||
#[cfg(feature = "time")]
|
|
||||||
use crate::dma::Transfer;
|
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||||
@ -24,6 +19,23 @@ use crate::pac::i2c;
|
|||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{interrupt, Peripheral};
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> {
|
||||||
|
let deadline = Instant::now() + timeout;
|
||||||
|
move || {
|
||||||
|
if Instant::now() > deadline {
|
||||||
|
Err(Error::Timeout)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
|
||||||
|
move || Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Interrupt handler.
|
/// Interrupt handler.
|
||||||
pub struct InterruptHandler<T: Instance> {
|
pub struct InterruptHandler<T: Instance> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
@ -260,21 +272,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn flush_txdr(&self) {
|
fn flush_txdr(&self) {
|
||||||
//if $i2c.isr.read().txis().bit_is_set() {
|
|
||||||
//$i2c.txdr.write(|w| w.txdata().bits(0));
|
|
||||||
//}
|
|
||||||
|
|
||||||
if T::regs().isr().read().txis() {
|
if T::regs().isr().read().txis() {
|
||||||
T::regs().txdr().write(|w| w.set_txdata(0));
|
T::regs().txdr().write(|w| w.set_txdata(0));
|
||||||
}
|
}
|
||||||
if !T::regs().isr().read().txe() {
|
if !T::regs().isr().read().txe() {
|
||||||
T::regs().isr().modify(|w| w.set_txe(true))
|
T::regs().isr().modify(|w| w.set_txe(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If TXDR is not flagged as empty, write 1 to flush it
|
|
||||||
//if $i2c.isr.read().txe().is_not_empty() {
|
|
||||||
//$i2c.isr.write(|w| w.txe().set_bit());
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
||||||
@ -437,7 +440,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
async fn write_dma_internal(
|
async fn write_dma_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
address: u8,
|
address: u8,
|
||||||
@ -528,7 +530,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
async fn read_dma_internal(
|
async fn read_dma_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
address: u8,
|
address: u8,
|
||||||
@ -610,42 +611,38 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// Async public API
|
// Async public API
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||||||
where
|
|
||||||
TXDMA: crate::i2c::TxDma<T>,
|
|
||||||
{
|
|
||||||
self.write_timeout(address, write, self.timeout).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error>
|
|
||||||
where
|
where
|
||||||
TXDMA: crate::i2c::TxDma<T>,
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
{
|
{
|
||||||
if write.is_empty() {
|
if write.is_empty() {
|
||||||
self.write_internal(address, write, true, timeout_fn(timeout))
|
self.write_internal(address, write, true, timeout_fn(self.timeout))
|
||||||
} else {
|
} else {
|
||||||
embassy_time::with_timeout(
|
embassy_time::with_timeout(
|
||||||
timeout,
|
self.timeout,
|
||||||
self.write_dma_internal(address, write, true, true, timeout_fn(timeout)),
|
self.write_dma_internal(address, write, true, true, timeout_fn(self.timeout)),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(Err(Error::Timeout))
|
.unwrap_or(Err(Error::Timeout))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(not(feature = "time"))]
|
||||||
pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
|
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
TXDMA: crate::i2c::TxDma<T>,
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
{
|
{
|
||||||
self.write_vectored_timeout(address, write, self.timeout).await
|
if write.is_empty() {
|
||||||
|
self.write_internal(address, write, true, no_timeout_fn())
|
||||||
|
} else {
|
||||||
|
self.write_dma_internal(address, write, true, true, no_timeout_fn())
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error>
|
pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
TXDMA: crate::i2c::TxDma<T>,
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
{
|
{
|
||||||
@ -661,8 +658,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
let is_last = next.is_none();
|
let is_last = next.is_none();
|
||||||
|
|
||||||
embassy_time::with_timeout(
|
embassy_time::with_timeout(
|
||||||
timeout,
|
self.timeout,
|
||||||
self.write_dma_internal(address, c, first, is_last, timeout_fn(timeout)),
|
self.write_dma_internal(address, c, first, is_last, timeout_fn(self.timeout)),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(Err(Error::Timeout))?;
|
.unwrap_or(Err(Error::Timeout))?;
|
||||||
@ -672,66 +669,79 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
if write.is_empty() {
|
||||||
|
return Err(Error::ZeroLengthTransfer);
|
||||||
|
}
|
||||||
|
let mut iter = write.iter();
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
let mut current = iter.next();
|
||||||
|
while let Some(c) = current {
|
||||||
|
let next = iter.next();
|
||||||
|
let is_last = next.is_none();
|
||||||
|
|
||||||
|
self.write_dma_internal(address, c, first, is_last, no_timeout_fn())
|
||||||
|
.await?;
|
||||||
|
first = false;
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
RXDMA: crate::i2c::RxDma<T>,
|
RXDMA: crate::i2c::RxDma<T>,
|
||||||
{
|
{
|
||||||
self.read_timeout(address, buffer, self.timeout).await
|
if buffer.is_empty() {
|
||||||
|
self.read_internal(address, buffer, false, timeout_fn(self.timeout))
|
||||||
|
} else {
|
||||||
|
embassy_time::with_timeout(
|
||||||
|
self.timeout,
|
||||||
|
self.read_dma_internal(address, buffer, false, timeout_fn(self.timeout)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or(Err(Error::Timeout))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(not(feature = "time"))]
|
||||||
pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error>
|
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
RXDMA: crate::i2c::RxDma<T>,
|
RXDMA: crate::i2c::RxDma<T>,
|
||||||
{
|
{
|
||||||
if buffer.is_empty() {
|
if buffer.is_empty() {
|
||||||
self.read_internal(address, buffer, false, timeout_fn(timeout))
|
self.read_internal(address, buffer, false, no_timeout_fn())
|
||||||
} else {
|
} else {
|
||||||
embassy_time::with_timeout(
|
self.read_dma_internal(address, buffer, false, no_timeout_fn()).await
|
||||||
timeout,
|
|
||||||
self.read_dma_internal(address, buffer, false, timeout_fn(timeout)),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap_or(Err(Error::Timeout))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
||||||
where
|
|
||||||
TXDMA: super::TxDma<T>,
|
|
||||||
RXDMA: super::RxDma<T>,
|
|
||||||
{
|
|
||||||
self.write_read_timeout(address, write, read, self.timeout).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
pub async fn write_read_timeout(
|
|
||||||
&mut self,
|
|
||||||
address: u8,
|
|
||||||
write: &[u8],
|
|
||||||
read: &mut [u8],
|
|
||||||
timeout: Duration,
|
|
||||||
) -> Result<(), Error>
|
|
||||||
where
|
where
|
||||||
TXDMA: super::TxDma<T>,
|
TXDMA: super::TxDma<T>,
|
||||||
RXDMA: super::RxDma<T>,
|
RXDMA: super::RxDma<T>,
|
||||||
{
|
{
|
||||||
let start_instant = Instant::now();
|
let start_instant = Instant::now();
|
||||||
let check_timeout = timeout_fn(timeout);
|
let check_timeout = timeout_fn(self.timeout);
|
||||||
if write.is_empty() {
|
if write.is_empty() {
|
||||||
self.write_internal(address, write, false, &check_timeout)?;
|
self.write_internal(address, write, false, &check_timeout)?;
|
||||||
} else {
|
} else {
|
||||||
embassy_time::with_timeout(
|
embassy_time::with_timeout(
|
||||||
timeout,
|
self.timeout,
|
||||||
self.write_dma_internal(address, write, true, true, &check_timeout),
|
self.write_dma_internal(address, write, true, true, &check_timeout),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(Err(Error::Timeout))?;
|
.unwrap_or(Err(Error::Timeout))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_left_until_timeout = timeout - Instant::now().duration_since(start_instant);
|
let time_left_until_timeout = self.timeout - Instant::now().duration_since(start_instant);
|
||||||
|
|
||||||
if read.is_empty() {
|
if read.is_empty() {
|
||||||
self.read_internal(address, read, true, &check_timeout)?;
|
self.read_internal(address, read, true, &check_timeout)?;
|
||||||
@ -747,6 +757,28 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: super::TxDma<T>,
|
||||||
|
RXDMA: super::RxDma<T>,
|
||||||
|
{
|
||||||
|
let no_timeout = no_timeout_fn();
|
||||||
|
if write.is_empty() {
|
||||||
|
self.write_internal(address, write, false, &no_timeout)?;
|
||||||
|
} else {
|
||||||
|
self.write_dma_internal(address, write, true, true, &no_timeout).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if read.is_empty() {
|
||||||
|
self.read_internal(address, read, true, &no_timeout)?;
|
||||||
|
} else {
|
||||||
|
self.read_dma_internal(address, read, true, &no_timeout).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// Blocking public API
|
// Blocking public API
|
||||||
|
|
||||||
@ -1201,15 +1233,3 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> {
|
|
||||||
let deadline = Instant::now() + timeout;
|
|
||||||
move || {
|
|
||||||
if Instant::now() > deadline {
|
|
||||||
Err(Error::Timeout)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -207,27 +207,40 @@ impl Protocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum SyncEnable {
|
pub enum SyncInput {
|
||||||
Asynchronous,
|
/// Not synced to any other SAI unit.
|
||||||
|
None,
|
||||||
/// Syncs with the other A/B sub-block within the SAI unit
|
/// Syncs with the other A/B sub-block within the SAI unit
|
||||||
Internal,
|
Internal,
|
||||||
/// Syncs with a sub-block in the other SAI unit - use set_sync_output() and set_sync_input()
|
/// Syncs with a sub-block in the other SAI unit
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(sai_v4)]
|
||||||
External,
|
External(SyncInputInstance),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncEnable {
|
impl SyncInput {
|
||||||
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
||||||
pub const fn syncen(&self) -> vals::Syncen {
|
pub const fn syncen(&self) -> vals::Syncen {
|
||||||
match self {
|
match self {
|
||||||
SyncEnable::Asynchronous => vals::Syncen::ASYNCHRONOUS,
|
SyncInput::None => vals::Syncen::ASYNCHRONOUS,
|
||||||
SyncEnable::Internal => vals::Syncen::INTERNAL,
|
SyncInput::Internal => vals::Syncen::INTERNAL,
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(any(sai_v4))]
|
||||||
SyncEnable::External => vals::Syncen::EXTERNAL,
|
SyncInput::External(_) => vals::Syncen::EXTERNAL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(sai_v4)]
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
pub enum SyncInputInstance {
|
||||||
|
#[cfg(peri_sai1)]
|
||||||
|
Sai1 = 0,
|
||||||
|
#[cfg(peri_sai2)]
|
||||||
|
Sai2 = 1,
|
||||||
|
#[cfg(peri_sai3)]
|
||||||
|
Sai3 = 2,
|
||||||
|
#[cfg(peri_sai4)]
|
||||||
|
Sai4 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum StereoMono {
|
pub enum StereoMono {
|
||||||
Stereo,
|
Stereo,
|
||||||
@ -428,8 +441,8 @@ impl MasterClockDivider {
|
|||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub tx_rx: TxRx,
|
pub tx_rx: TxRx,
|
||||||
pub sync_enable: SyncEnable,
|
pub sync_input: SyncInput,
|
||||||
pub is_sync_output: bool,
|
pub sync_output: bool,
|
||||||
pub protocol: Protocol,
|
pub protocol: Protocol,
|
||||||
pub slot_size: SlotSize,
|
pub slot_size: SlotSize,
|
||||||
pub slot_count: word::U4,
|
pub slot_count: word::U4,
|
||||||
@ -459,8 +472,8 @@ impl Default for Config {
|
|||||||
Self {
|
Self {
|
||||||
mode: Mode::Master,
|
mode: Mode::Master,
|
||||||
tx_rx: TxRx::Transmitter,
|
tx_rx: TxRx::Transmitter,
|
||||||
is_sync_output: false,
|
sync_output: false,
|
||||||
sync_enable: SyncEnable::Asynchronous,
|
sync_input: SyncInput::None,
|
||||||
protocol: Protocol::Free,
|
protocol: Protocol::Free,
|
||||||
slot_size: SlotSize::DataSize,
|
slot_size: SlotSize::DataSize,
|
||||||
slot_count: word::U4(2),
|
slot_count: word::U4(2),
|
||||||
@ -608,18 +621,18 @@ impl<'d, T: Instance> Sai<'d, T> {
|
|||||||
|
|
||||||
fn update_synchronous_config(config: &mut Config) {
|
fn update_synchronous_config(config: &mut Config) {
|
||||||
config.mode = Mode::Slave;
|
config.mode = Mode::Slave;
|
||||||
config.is_sync_output = false;
|
config.sync_output = false;
|
||||||
|
|
||||||
#[cfg(any(sai_v1, sai_v2, sai_v3))]
|
#[cfg(any(sai_v1, sai_v2, sai_v3))]
|
||||||
{
|
{
|
||||||
config.sync_enable = SyncEnable::Internal;
|
config.sync_input = SyncInput::Internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(any(sai_v4))]
|
||||||
{
|
{
|
||||||
//this must either be Internal or External
|
//this must either be Internal or External
|
||||||
//The asynchronous sub-block on the same SAI needs to enable is_sync_output
|
//The asynchronous sub-block on the same SAI needs to enable sync_output
|
||||||
assert!(config.sync_enable != SyncEnable::Asynchronous);
|
assert!(config.sync_input != SyncInput::None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,20 +879,13 @@ impl<'d, T: Instance, C: Channel, W: word::Word> SubBlock<'d, T, C, W> {
|
|||||||
|
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(any(sai_v4))]
|
||||||
{
|
{
|
||||||
// Not totally clear from the datasheet if this is right
|
if let SyncInput::External(i) = config.sync_input {
|
||||||
// This is only used if using SyncEnable::External on the other SAI unit
|
|
||||||
// Syncing from SAIX subblock A to subblock B does not require this
|
|
||||||
// Only syncing from SAI1 subblock A/B to SAI2 subblock A/B
|
|
||||||
let value: u8 = if T::REGS.as_ptr() == stm32_metapac::SAI1.as_ptr() {
|
|
||||||
1 //this is SAI1, so sync with SAI2
|
|
||||||
} else {
|
|
||||||
0 //this is SAI2, so sync with SAI1
|
|
||||||
};
|
|
||||||
T::REGS.gcr().modify(|w| {
|
T::REGS.gcr().modify(|w| {
|
||||||
w.set_syncin(value);
|
w.set_syncin(i as u8);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if config.is_sync_output {
|
if config.sync_output {
|
||||||
let syncout: u8 = match sub_block {
|
let syncout: u8 = match sub_block {
|
||||||
WhichSubBlock::A => 0b01,
|
WhichSubBlock::A => 0b01,
|
||||||
WhichSubBlock::B => 0b10,
|
WhichSubBlock::B => 0b10,
|
||||||
@ -903,7 +909,7 @@ impl<'d, T: Instance, C: Channel, W: word::Word> SubBlock<'d, T, C, W> {
|
|||||||
w.set_ds(config.data_size.ds());
|
w.set_ds(config.data_size.ds());
|
||||||
w.set_lsbfirst(config.bit_order.lsbfirst());
|
w.set_lsbfirst(config.bit_order.lsbfirst());
|
||||||
w.set_ckstr(config.clock_strobe.ckstr());
|
w.set_ckstr(config.clock_strobe.ckstr());
|
||||||
w.set_syncen(config.sync_enable.syncen());
|
w.set_syncen(config.sync_input.syncen());
|
||||||
w.set_mono(config.stereo_mono.mono());
|
w.set_mono(config.stereo_mono.mono());
|
||||||
w.set_outdriv(config.output_drive.outdriv());
|
w.set_outdriv(config.output_drive.outdriv());
|
||||||
w.set_mckdiv(config.master_clock_divider.mckdiv());
|
w.set_mckdiv(config.master_clock_divider.mckdiv());
|
||||||
|
Reference in New Issue
Block a user