Compare commits
9 Commits
embassy-ex
...
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,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,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,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 \
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::cmp::{max, min};
|
||||
use core::iter::zip;
|
||||
|
||||
use embassy_net_driver_channel as ch;
|
||||
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||
@ -16,6 +17,12 @@ pub struct Error {
|
||||
pub status: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AddMulticastAddressError {
|
||||
NotMulticast,
|
||||
NoFreeSlots,
|
||||
}
|
||||
|
||||
pub struct Control<'a> {
|
||||
state_ch: ch::StateRunner<'a>,
|
||||
events: &'a Events,
|
||||
@ -316,6 +323,54 @@ impl<'a> Control<'a> {
|
||||
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) {
|
||||
let mut buf = [0; 8];
|
||||
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
||||
|
@ -27,7 +27,7 @@ use ioctl::IoctlState;
|
||||
|
||||
use crate::bus::Bus;
|
||||
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::structs::BssInfo;
|
||||
|
||||
|
@ -11,7 +11,8 @@
|
||||
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
|
||||
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")]
|
||||
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();
|
||||
for p in METADATA.peripherals {
|
||||
if let Some(r) = &p.registers {
|
||||
println!("cargo:rustc-cfg=peri_{}", p.name.to_ascii_lowercase());
|
||||
match r.kind {
|
||||
// Generate singletons per pin, not per port
|
||||
"gpio" => {
|
||||
|
@ -1,21 +1,16 @@
|
||||
use core::cmp;
|
||||
#[cfg(feature = "time")]
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
#[cfg(feature = "time")]
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
#[cfg(feature = "time")]
|
||||
use crate::dma::Transfer;
|
||||
use crate::dma::{NoDma, Transfer};
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Pull;
|
||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||
@ -24,6 +19,23 @@ use crate::pac::i2c;
|
||||
use crate::time::Hertz;
|
||||
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.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
@ -260,21 +272,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
|
||||
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() {
|
||||
T::regs().txdr().write(|w| w.set_txdata(0));
|
||||
}
|
||||
if !T::regs().isr().read().txe() {
|
||||
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> {
|
||||
@ -437,7 +440,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
async fn write_dma_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
@ -528,7 +530,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
async fn read_dma_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
@ -610,42 +611,38 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
// =========================
|
||||
// Async public API
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
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
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
{
|
||||
if write.is_empty() {
|
||||
self.write_internal(address, write, true, timeout_fn(timeout))
|
||||
self.write_internal(address, write, true, timeout_fn(self.timeout))
|
||||
} else {
|
||||
embassy_time::with_timeout(
|
||||
timeout,
|
||||
self.write_dma_internal(address, write, true, true, timeout_fn(timeout)),
|
||||
self.timeout,
|
||||
self.write_dma_internal(address, write, true, true, timeout_fn(self.timeout)),
|
||||
)
|
||||
.await
|
||||
.unwrap_or(Err(Error::Timeout))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
|
||||
#[cfg(not(feature = "time"))]
|
||||
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||||
where
|
||||
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")]
|
||||
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
|
||||
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();
|
||||
|
||||
embassy_time::with_timeout(
|
||||
timeout,
|
||||
self.write_dma_internal(address, c, first, is_last, timeout_fn(timeout)),
|
||||
self.timeout,
|
||||
self.write_dma_internal(address, c, first, is_last, timeout_fn(self.timeout)),
|
||||
)
|
||||
.await
|
||||
.unwrap_or(Err(Error::Timeout))?;
|
||||
@ -672,66 +669,79 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
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")]
|
||||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||
where
|
||||
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")]
|
||||
pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error>
|
||||
#[cfg(not(feature = "time"))]
|
||||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||
where
|
||||
RXDMA: crate::i2c::RxDma<T>,
|
||||
{
|
||||
if buffer.is_empty() {
|
||||
self.read_internal(address, buffer, false, timeout_fn(timeout))
|
||||
self.read_internal(address, buffer, false, no_timeout_fn())
|
||||
} else {
|
||||
embassy_time::with_timeout(
|
||||
timeout,
|
||||
self.read_dma_internal(address, buffer, false, timeout_fn(timeout)),
|
||||
)
|
||||
.await
|
||||
.unwrap_or(Err(Error::Timeout))
|
||||
self.read_dma_internal(address, buffer, false, no_timeout_fn()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(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>,
|
||||
{
|
||||
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
|
||||
TXDMA: super::TxDma<T>,
|
||||
RXDMA: super::RxDma<T>,
|
||||
{
|
||||
let start_instant = Instant::now();
|
||||
let check_timeout = timeout_fn(timeout);
|
||||
let check_timeout = timeout_fn(self.timeout);
|
||||
if write.is_empty() {
|
||||
self.write_internal(address, write, false, &check_timeout)?;
|
||||
} else {
|
||||
embassy_time::with_timeout(
|
||||
timeout,
|
||||
self.timeout,
|
||||
self.write_dma_internal(address, write, true, true, &check_timeout),
|
||||
)
|
||||
.await
|
||||
.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() {
|
||||
self.read_internal(address, read, true, &check_timeout)?;
|
||||
@ -747,6 +757,28 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
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
|
||||
|
||||
@ -1201,15 +1233,3 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||
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)]
|
||||
pub enum SyncEnable {
|
||||
Asynchronous,
|
||||
pub enum SyncInput {
|
||||
/// Not synced to any other SAI unit.
|
||||
None,
|
||||
/// Syncs with the other A/B sub-block within the SAI unit
|
||||
Internal,
|
||||
/// Syncs with a sub-block in the other SAI unit - use set_sync_output() and set_sync_input()
|
||||
#[cfg(any(sai_v4))]
|
||||
External,
|
||||
/// Syncs with a sub-block in the other SAI unit
|
||||
#[cfg(sai_v4)]
|
||||
External(SyncInputInstance),
|
||||
}
|
||||
|
||||
impl SyncEnable {
|
||||
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
||||
impl SyncInput {
|
||||
pub const fn syncen(&self) -> vals::Syncen {
|
||||
match self {
|
||||
SyncEnable::Asynchronous => vals::Syncen::ASYNCHRONOUS,
|
||||
SyncEnable::Internal => vals::Syncen::INTERNAL,
|
||||
SyncInput::None => vals::Syncen::ASYNCHRONOUS,
|
||||
SyncInput::Internal => vals::Syncen::INTERNAL,
|
||||
#[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)]
|
||||
pub enum StereoMono {
|
||||
Stereo,
|
||||
@ -428,8 +441,8 @@ impl MasterClockDivider {
|
||||
pub struct Config {
|
||||
pub mode: Mode,
|
||||
pub tx_rx: TxRx,
|
||||
pub sync_enable: SyncEnable,
|
||||
pub is_sync_output: bool,
|
||||
pub sync_input: SyncInput,
|
||||
pub sync_output: bool,
|
||||
pub protocol: Protocol,
|
||||
pub slot_size: SlotSize,
|
||||
pub slot_count: word::U4,
|
||||
@ -459,8 +472,8 @@ impl Default for Config {
|
||||
Self {
|
||||
mode: Mode::Master,
|
||||
tx_rx: TxRx::Transmitter,
|
||||
is_sync_output: false,
|
||||
sync_enable: SyncEnable::Asynchronous,
|
||||
sync_output: false,
|
||||
sync_input: SyncInput::None,
|
||||
protocol: Protocol::Free,
|
||||
slot_size: SlotSize::DataSize,
|
||||
slot_count: word::U4(2),
|
||||
@ -608,18 +621,18 @@ impl<'d, T: Instance> Sai<'d, T> {
|
||||
|
||||
fn update_synchronous_config(config: &mut Config) {
|
||||
config.mode = Mode::Slave;
|
||||
config.is_sync_output = false;
|
||||
config.sync_output = false;
|
||||
|
||||
#[cfg(any(sai_v1, sai_v2, sai_v3))]
|
||||
{
|
||||
config.sync_enable = SyncEnable::Internal;
|
||||
config.sync_input = SyncInput::Internal;
|
||||
}
|
||||
|
||||
#[cfg(any(sai_v4))]
|
||||
{
|
||||
//this must either be Internal or External
|
||||
//The asynchronous sub-block on the same SAI needs to enable is_sync_output
|
||||
assert!(config.sync_enable != SyncEnable::Asynchronous);
|
||||
//The asynchronous sub-block on the same SAI needs to enable sync_output
|
||||
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))]
|
||||
{
|
||||
// Not totally clear from the datasheet if this is right
|
||||
// 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| {
|
||||
w.set_syncin(value);
|
||||
});
|
||||
if let SyncInput::External(i) = config.sync_input {
|
||||
T::REGS.gcr().modify(|w| {
|
||||
w.set_syncin(i as u8);
|
||||
});
|
||||
}
|
||||
|
||||
if config.is_sync_output {
|
||||
if config.sync_output {
|
||||
let syncout: u8 = match sub_block {
|
||||
WhichSubBlock::A => 0b01,
|
||||
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_lsbfirst(config.bit_order.lsbfirst());
|
||||
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_outdriv(config.output_drive.outdriv());
|
||||
w.set_mckdiv(config.master_clock_divider.mckdiv());
|
||||
|
Reference in New Issue
Block a user