Compare commits

..

1 Commits

15 changed files with 301 additions and 611 deletions

1
ci.sh
View File

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

View File

@ -58,7 +58,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
[features]

View File

@ -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" => {
@ -1137,23 +1138,6 @@ fn main() {
}
}
// ========
// Write peripheral_interrupts module.
let mut mt = TokenStream::new();
for p in METADATA.peripherals {
let mut pt = TokenStream::new();
for irq in p.interrupts {
let iname = format_ident!("{}", irq.interrupt);
let sname = format_ident!("{}", irq.signal);
pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));
}
let pname = format_ident!("{}", p.name);
mt.extend(quote!(pub mod #pname { #pt }));
}
g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt }));
// ========
// Write foreach_foo! macrotables
@ -1312,9 +1296,6 @@ fn main() {
let mut m = String::new();
// DO NOT ADD more macros like these.
// These turned to be a bad idea!
// Instead, make build.rs generate the final code.
make_table(&mut m, "foreach_flash_region", &flash_regions_table);
make_table(&mut m, "foreach_interrupt", &interrupts_table);
make_table(&mut m, "foreach_peripheral", &peripherals_table);

View File

@ -1,14 +1,11 @@
#![macro_use]
use core::marker::PhantomData;
use crate::interrupt;
#[cfg_attr(i2c_v1, path = "v1.rs")]
#[cfg_attr(i2c_v2, path = "v2.rs")]
mod _version;
pub use _version::*;
use embassy_sync::waitqueue::AtomicWaker;
use crate::peripherals;
@ -26,20 +23,6 @@ pub enum Error {
pub(crate) mod sealed {
use super::*;
pub struct State {
#[allow(unused)]
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance: crate::rcc::RccPeripheral {
fn regs() -> crate::pac::i2c::I2c;
fn state() -> &'static State;
@ -47,8 +30,7 @@ pub(crate) mod sealed {
}
pub trait Instance: sealed::Instance + 'static {
type EventInterrupt: interrupt::typelevel::Interrupt;
type ErrorInterrupt: interrupt::typelevel::Interrupt;
type Interrupt: interrupt::typelevel::Interrupt;
}
pin_trait!(SclPin, Instance);
@ -56,148 +38,21 @@ pin_trait!(SdaPin, Instance);
dma_trait!(RxDma, Instance);
dma_trait!(TxDma, Instance);
/// Interrupt handler.
pub struct EventInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> {
unsafe fn on_interrupt() {
_version::on_interrupt::<T>()
}
}
pub struct ErrorInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> {
unsafe fn on_interrupt() {
_version::on_interrupt::<T>()
}
}
foreach_peripheral!(
(i2c, $inst:ident) => {
foreach_interrupt!(
($inst:ident, i2c, $block:ident, EV, $irq:ident) => {
impl sealed::Instance for peripherals::$inst {
fn regs() -> crate::pac::i2c::I2c {
crate::pac::$inst
}
fn state() -> &'static sealed::State {
static STATE: sealed::State = sealed::State::new();
fn state() -> &'static State {
static STATE: State = State::new();
&STATE
}
}
impl Instance for peripherals::$inst {
type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;
type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
);
mod eh02 {
use super::*;
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
type Error = Error;
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
type Error = Error;
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
type Error = Error;
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
}
}
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
use crate::dma::NoDma;
impl embedded_hal_1::i2c::Error for Error {
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
match *self {
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
Self::Nack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
}
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
}
}
}
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, read)
}
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
fn transaction(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
}
}
}
#[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "time"))]
mod eha {
use super::*;
impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.read(address, read).await
}
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.write(address, write).await
}
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.write_read(address, write, read).await
}
async fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
let _ = address;
let _ = operations;
todo!()
}
}
}

View File

@ -1,33 +1,23 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_futures::select::{select, Either};
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use super::*;
use crate::dma::{NoDma, Transfer};
use crate::dma::NoDma;
use crate::gpio::sealed::AFType;
use crate::gpio::Pull;
use crate::interrupt::typelevel::Interrupt;
use crate::i2c::{Error, Instance, SclPin, SdaPin};
use crate::pac::i2c;
use crate::time::Hertz;
use crate::{interrupt, Peripheral};
pub unsafe fn on_interrupt<T: Instance>() {
let regs = T::regs();
// i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of
// other stuff, so we wake the task on every interrupt.
T::state().waker.wake();
critical_section::with(|_| {
// Clear event interrupt flag.
regs.cr2().modify(|w| {
w.set_itevten(false);
w.set_iterren(false);
});
});
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {}
}
#[non_exhaustive]
@ -37,6 +27,14 @@ pub struct Config {
pub scl_pullup: bool,
}
pub struct State {}
impl State {
pub(crate) const fn new() -> Self {
Self {}
}
}
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
phantom: PhantomData<&'d mut T>,
#[allow(dead_code)]
@ -50,9 +48,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
_peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
+ 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
tx_dma: impl Peripheral<P = TXDMA> + 'd,
rx_dma: impl Peripheral<P = RXDMA> + 'd,
freq: Hertz,
@ -102,9 +98,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
reg.set_pe(true);
});
unsafe { T::EventInterrupt::enable() };
unsafe { T::ErrorInterrupt::enable() };
Self {
phantom: PhantomData,
tx_dma,
@ -112,58 +105,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> {
fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
// Note that flags should only be cleared once they have been registered. If flags are
// cleared otherwise, there may be an inherent race condition and flags may be missed.
let sr1 = T::regs().sr1().read();
if sr1.timeout() {
T::regs().sr1().write(|reg| {
reg.0 = !0;
reg.set_timeout(false);
});
T::regs().sr1().modify(|reg| reg.set_timeout(false));
return Err(Error::Timeout);
}
if sr1.pecerr() {
T::regs().sr1().write(|reg| {
reg.0 = !0;
reg.set_pecerr(false);
});
T::regs().sr1().modify(|reg| reg.set_pecerr(false));
return Err(Error::Crc);
}
if sr1.ovr() {
T::regs().sr1().write(|reg| {
reg.0 = !0;
reg.set_ovr(false);
});
T::regs().sr1().modify(|reg| reg.set_ovr(false));
return Err(Error::Overrun);
}
if sr1.af() {
T::regs().sr1().write(|reg| {
reg.0 = !0;
reg.set_af(false);
});
T::regs().sr1().modify(|reg| reg.set_af(false));
return Err(Error::Nack);
}
if sr1.arlo() {
T::regs().sr1().write(|reg| {
reg.0 = !0;
reg.set_arlo(false);
});
T::regs().sr1().modify(|reg| reg.set_arlo(false));
return Err(Error::Arbitration);
}
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
// clearing the BERR bit instead.
if sr1.berr() {
T::regs().sr1().write(|reg| {
reg.0 = !0;
reg.set_berr(false);
});
T::regs().sr1().modify(|reg| reg.set_berr(false));
}
Ok(sr1)
@ -182,13 +157,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
});
// Wait until START condition was generated
while !Self::check_and_clear_error_flags()?.start() {
while !self.check_and_clear_error_flags()?.start() {
check_timeout()?;
}
// Also wait until signalled we're master and everything is waiting for us
while {
Self::check_and_clear_error_flags()?;
self.check_and_clear_error_flags()?;
let sr2 = T::regs().sr2().read();
!sr2.msl() && !sr2.busy()
@ -202,7 +177,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until address was sent
// Wait for the address to be acknowledged
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
while !Self::check_and_clear_error_flags()?.addr() {
while !self.check_and_clear_error_flags()?.addr() {
check_timeout()?;
}
@ -222,7 +197,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until we're ready for sending
while {
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
!Self::check_and_clear_error_flags()?.txe()
!self.check_and_clear_error_flags()?.txe()
} {
check_timeout()?;
}
@ -233,7 +208,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until byte is transferred
while {
// Check for any potential error conditions.
!Self::check_and_clear_error_flags()?.btf()
!self.check_and_clear_error_flags()?.btf()
} {
check_timeout()?;
}
@ -244,7 +219,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
while {
// Check for any potential error conditions.
Self::check_and_clear_error_flags()?;
self.check_and_clear_error_flags()?;
!T::regs().sr1().read().rxne()
} {
@ -269,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
});
// Wait until START condition was generated
while !Self::check_and_clear_error_flags()?.start() {
while !self.check_and_clear_error_flags()?.start() {
check_timeout()?;
}
@ -286,7 +261,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until address was sent
// Wait for the address to be acknowledged
while !Self::check_and_clear_error_flags()?.addr() {
while !self.check_and_clear_error_flags()?.addr() {
check_timeout()?;
}
@ -361,322 +336,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
self.blocking_write_read_timeout(addr, write, read, || Ok(()))
}
// Async
#[inline] // pretty sure this should always be inlined
fn enable_interrupts() -> () {
T::regs().cr2().modify(|w| {
w.set_iterren(true);
w.set_itevten(true);
});
}
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
{
let dma_transfer = unsafe {
let regs = T::regs();
regs.cr2().modify(|w| {
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
w.set_dmaen(true);
w.set_itbufen(false);
});
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
let dst = regs.dr().as_ptr() as *mut u8;
let ch = &mut self.tx_dma;
let request = ch.request();
Transfer::new_write(ch, request, write, dst, Default::default())
};
let on_drop = OnDrop::new(|| {
let regs = T::regs();
regs.cr2().modify(|w| {
w.set_dmaen(false);
w.set_iterren(false);
w.set_itevten(false);
})
});
Self::enable_interrupts();
// Send a START condition
T::regs().cr1().modify(|reg| {
reg.set_start(true);
});
let state = T::state();
// Wait until START condition was generated
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.start() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Also wait until signalled we're master and everything is waiting for us
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(_) => {
let sr2 = T::regs().sr2().read();
if !sr2.msl() && !sr2.busy() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
}
})
.await?;
// Set up current address, we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr(address << 1));
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.addr() {
// Clear the ADDR condition by reading SR2.
T::regs().sr2().read();
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
Self::enable_interrupts();
let poll_error = poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
// Unclear why the Err turbofish is necessary here? The compiler didnt require it in the other
// identical poll_fn check_and_clear matches.
Err(e) => Poll::Ready(Err::<T, Error>(e)),
Ok(_) => Poll::Pending,
}
});
// Wait for either the DMA transfer to successfully finish, or an I2C error to occur.
match select(dma_transfer, poll_error).await {
Either::Second(Err(e)) => Err(e),
_ => Ok(()),
}?;
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
// requests then wait for a BTF event before programming the Stop condition.”
// TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
T::regs().cr2().modify(|w| {
w.set_dmaen(false);
});
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.btf() {
T::regs().cr1().modify(|w| {
w.set_stop(true);
});
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Wait for STOP condition to transmit.
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
if T::regs().cr1().read().stop() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
})
.await?;
drop(on_drop);
// Fallthrough is success
Ok(())
}
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
{
let state = T::state();
let buffer_len = buffer.len();
let dma_transfer = unsafe {
let regs = T::regs();
regs.cr2().modify(|w| {
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
w.set_itbufen(false);
w.set_dmaen(true);
});
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
let src = regs.dr().as_ptr() as *mut u8;
let ch = &mut self.rx_dma;
let request = ch.request();
Transfer::new_read(ch, request, src, buffer, Default::default())
};
let on_drop = OnDrop::new(|| {
let regs = T::regs();
regs.cr2().modify(|w| {
w.set_dmaen(false);
w.set_iterren(false);
w.set_itevten(false);
})
});
Self::enable_interrupts();
// Send a START condition and set ACK bit
T::regs().cr1().modify(|reg| {
reg.set_start(true);
reg.set_ack(true);
});
// Wait until START condition was generated
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.start() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Also wait until signalled we're master and everything is waiting for us
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
// blocking read didnt have a check_and_clear call here, but blocking write did so
// Im adding it here in case that was an oversight.
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(_) => {
let sr2 = T::regs().sr2().read();
if !sr2.msl() && !sr2.busy() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
}
})
.await?;
// Set up current address, we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
// Wait for the address to be acknowledged
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.addr() {
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. Then the
// user can program the STOP condition either after clearing ADDR flag, or in the
// DMA Transfer Complete interrupt routine.
if buffer_len == 1 {
T::regs().cr1().modify(|w| {
w.set_ack(false);
});
}
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Clear condition by reading SR2
T::regs().sr2().read();
// Wait for bytes to be received, or an error to occur.
Self::enable_interrupts();
let poll_error = poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err::<T, Error>(e)),
_ => Poll::Pending,
}
});
match select(dma_transfer, poll_error).await {
Either::Second(Err(e)) => Err(e),
_ => Ok(()),
};
// v1 blocking waits for STOP to be written, the manual says to write the STOP bit yourself.
// what to do…
// Wait for the STOP to be sent.
// while T::regs().cr1().read().stop() {
// check_timeout()?;
// }
// Fallthrough is success
Ok(())
}
pub async fn write_read(&mut self, _address: u8, _write: &[u8], _read: &mut [u8]) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
TXDMA: crate::i2c::TxDma<T>,
{
todo!()
}
}
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
@ -685,6 +344,77 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
type Error = Error;
fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(addr, read)
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
type Error = Error;
fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(addr, write)
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
type Error = Error;
fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(addr, write, read)
}
}
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
impl embedded_hal_1::i2c::Error for Error {
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
match *self {
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
Self::Nack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
}
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
}
}
}
impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for I2c<'d, T> {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, read)
}
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
fn transaction(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
}
}
}
enum Mode {
Fast,
Standard,

View File

@ -1,17 +1,19 @@
use core::cmp;
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
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 super::*;
use crate::dma::{NoDma, Transfer};
use crate::gpio::sealed::AFType;
use crate::gpio::Pull;
use crate::i2c::{Error, Instance, SclPin, SdaPin};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::i2c;
use crate::time::Hertz;
@ -34,7 +36,13 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
move || Ok(())
}
pub unsafe fn on_interrupt<T: Instance>() {
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let regs = T::regs();
let isr = regs.isr().read();
@ -46,6 +54,7 @@ pub unsafe fn on_interrupt<T: Instance>() {
critical_section::with(|_| {
regs.cr1().modify(|w| w.set_tcie(false));
});
}
}
#[non_exhaustive]
@ -68,6 +77,18 @@ impl Default for Config {
}
}
pub struct State {
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
_peri: PeripheralRef<'d, T>,
#[allow(dead_code)]
@ -83,9 +104,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
+ 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
tx_dma: impl Peripheral<P = TXDMA> + 'd,
rx_dma: impl Peripheral<P = RXDMA> + 'd,
freq: Hertz,
@ -131,8 +150,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
reg.set_pe(true);
});
unsafe { T::EventInterrupt::enable() };
unsafe { T::ErrorInterrupt::enable() };
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self {
_peri: peri,
@ -968,6 +987,35 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
}
}
#[cfg(feature = "time")]
mod eh02 {
use super::*;
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
type Error = Error;
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
type Error = Error;
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
type Error = Error;
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
}
}
/// I2C Stop Configuration
///
/// Peripheral options for generating the STOP condition
@ -1092,6 +1140,83 @@ impl Timings {
}
}
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
impl embedded_hal_1::i2c::Error for Error {
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
match *self {
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
Self::Nack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
}
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
}
}
}
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, read)
}
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
fn transaction(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
}
}
}
#[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "time"))]
mod eha {
use super::super::{RxDma, TxDma};
use super::*;
impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.read(address, read).await
}
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.write(address, write).await
}
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.write_read(address, write, read).await
}
async fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
let _ = address;
let _ = operations;
todo!()
}
}
}
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
type Config = Hertz;
type ConfigError = ();

View File

@ -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
};
if let SyncInput::External(i) = config.sync_input {
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 {
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());

View File

@ -14,8 +14,7 @@ const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
bind_interrupts!(struct Irqs {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
});
#[embassy_executor::main]

View File

@ -13,8 +13,7 @@ const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
bind_interrupts!(struct Irqs {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
});
#[embassy_executor::main]

View File

@ -19,8 +19,7 @@ const HEIGHT: usize = 100;
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
bind_interrupts!(struct Irqs {
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>;
DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
});

View File

@ -13,8 +13,7 @@ const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
bind_interrupts!(struct Irqs {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
});
#[embassy_executor::main]

View File

@ -14,8 +14,7 @@ const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
bind_interrupts!(struct Irqs {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
});
#[embassy_executor::main]

View File

@ -16,8 +16,7 @@ const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
bind_interrupts!(struct Irqs {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
});
#[embassy_executor::main]

View File

@ -13,8 +13,7 @@ const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
bind_interrupts!(struct Irqs {
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
});
#[embassy_executor::main]

View File

@ -40,8 +40,7 @@ use static_cell::make_static;
use {embassy_stm32 as hal, panic_probe as _};
bind_interrupts!(struct Irqs {
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
RNG => rng::InterruptHandler<peripherals::RNG>;
});