Compare commits
2 Commits
main
...
asynci2cv1
Author | SHA1 | Date | |
---|---|---|---|
|
54123de7bd | ||
|
838a97c186 |
@ -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-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949" }
|
||||
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-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -1137,6 +1137,23 @@ 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
|
||||
|
||||
@ -1295,6 +1312,9 @@ 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);
|
||||
|
@ -1,11 +1,14 @@
|
||||
#![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;
|
||||
|
||||
@ -23,6 +26,20 @@ 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;
|
||||
@ -30,7 +47,8 @@ pub(crate) mod sealed {
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + 'static {
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
type EventInterrupt: interrupt::typelevel::Interrupt;
|
||||
type ErrorInterrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
pin_trait!(SclPin, Instance);
|
||||
@ -38,21 +56,148 @@ pin_trait!(SdaPin, Instance);
|
||||
dma_trait!(RxDma, Instance);
|
||||
dma_trait!(TxDma, Instance);
|
||||
|
||||
foreach_interrupt!(
|
||||
($inst:ident, i2c, $block:ident, EV, $irq:ident) => {
|
||||
/// 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) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::i2c::I2c {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
fn state() -> &'static State {
|
||||
static STATE: State = State::new();
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;
|
||||
type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,33 @@
|
||||
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 crate::dma::NoDma;
|
||||
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;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// 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() {}
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
@ -27,14 +37,6 @@ 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)]
|
||||
@ -48,7 +50,9 @@ 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::Interrupt, InterruptHandler<T>> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
|
||||
+ 'd,
|
||||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||
freq: Hertz,
|
||||
@ -98,6 +102,9 @@ 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,
|
||||
@ -105,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
|
||||
fn check_and_clear_error_flags() -> 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().modify(|reg| reg.set_timeout(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_timeout(false);
|
||||
});
|
||||
return Err(Error::Timeout);
|
||||
}
|
||||
|
||||
if sr1.pecerr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_pecerr(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_pecerr(false);
|
||||
});
|
||||
return Err(Error::Crc);
|
||||
}
|
||||
|
||||
if sr1.ovr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_ovr(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_ovr(false);
|
||||
});
|
||||
return Err(Error::Overrun);
|
||||
}
|
||||
|
||||
if sr1.af() {
|
||||
T::regs().sr1().modify(|reg| reg.set_af(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_af(false);
|
||||
});
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
|
||||
if sr1.arlo() {
|
||||
T::regs().sr1().modify(|reg| reg.set_arlo(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
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().modify(|reg| reg.set_berr(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_berr(false);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(sr1)
|
||||
@ -157,13 +182,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()
|
||||
@ -177,7 +202,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()?;
|
||||
}
|
||||
|
||||
@ -197,7 +222,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()?;
|
||||
}
|
||||
@ -208,7 +233,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()?;
|
||||
}
|
||||
@ -219,7 +244,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()
|
||||
} {
|
||||
@ -244,7 +269,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()?;
|
||||
}
|
||||
|
||||
@ -261,7 +286,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()?;
|
||||
}
|
||||
|
||||
@ -336,6 +361,322 @@ 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 didn’t 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 didn’t have a check_and_clear call here, but blocking write did so
|
||||
// I’m 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> {
|
||||
@ -344,77 +685,6 @@ 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,
|
||||
|
@ -1,19 +1,17 @@
|
||||
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;
|
||||
@ -36,13 +34,7 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
|
||||
move || Ok(())
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
pub unsafe fn on_interrupt<T: Instance>() {
|
||||
let regs = T::regs();
|
||||
let isr = regs.isr().read();
|
||||
|
||||
@ -55,7 +47,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
regs.cr1().modify(|w| w.set_tcie(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
@ -77,18 +68,6 @@ 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)]
|
||||
@ -104,7 +83,9 @@ 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::Interrupt, InterruptHandler<T>> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
|
||||
+ 'd,
|
||||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||
freq: Hertz,
|
||||
@ -150,8 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
reg.set_pe(true);
|
||||
});
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
unsafe { T::EventInterrupt::enable() };
|
||||
unsafe { T::ErrorInterrupt::enable() };
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
@ -987,35 +968,6 @@ 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
|
||||
@ -1140,83 +1092,6 @@ 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 = ();
|
||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -19,7 +19,8 @@ const HEIGHT: usize = 100;
|
||||
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>;
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
|
||||
});
|
||||
|
||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -16,7 +16,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -40,7 +40,8 @@ use static_cell::make_static;
|
||||
use {embassy_stm32 as hal, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
|
||||
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
|
||||
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user