Compare commits
2 Commits
cache-lock
...
asynci2cv1
Author | SHA1 | Date | |
---|---|---|---|
54123de7bd | |||
838a97c186 |
10
.github/ci/build-stable.sh
vendored
10
.github/ci/build-stable.sh
vendored
@ -12,19 +12,9 @@ export CARGO_TARGET_DIR=/ci/cache/target
|
|||||||
# used when pointing stm32-metapac to a CI-built one.
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
# Restore lockfiles
|
|
||||||
if [ -f /ci/cache/lockfiles.tar ]; then
|
|
||||||
echo Restoring lockfiles...
|
|
||||||
tar xf /ci/cache/lockfiles.tar
|
|
||||||
fi
|
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
|
sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
|
||||||
|
|
||||||
./ci_stable.sh
|
./ci_stable.sh
|
||||||
|
|
||||||
# Save lockfiles
|
|
||||||
echo Saving lockfiles...
|
|
||||||
find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+
|
|
10
.github/ci/build.sh
vendored
10
.github/ci/build.sh
vendored
@ -18,17 +18,7 @@ fi
|
|||||||
# used when pointing stm32-metapac to a CI-built one.
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
# Restore lockfiles
|
|
||||||
if [ -f /ci/cache/lockfiles.tar ]; then
|
|
||||||
echo Restoring lockfiles...
|
|
||||||
tar xf /ci/cache/lockfiles.tar
|
|
||||||
fi
|
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
./ci.sh
|
./ci.sh
|
||||||
|
|
||||||
# Save lockfiles
|
|
||||||
echo Saving lockfiles...
|
|
||||||
find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+
|
|
2
ci.sh
2
ci.sh
@ -38,7 +38,6 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,igmp,medium-ethernet \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly,dhcpv4-hostname \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly,dhcpv4-hostname \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||||
@ -111,7 +110,6 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h725re,defmt,exti,time-driver-any,unstable-traits,time \
|
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
@ -10,4 +10,3 @@
|
|||||||
* xref:examples.adoc[Examples]
|
* xref:examples.adoc[Examples]
|
||||||
* xref:developer.adoc[Developer]
|
* xref:developer.adoc[Developer]
|
||||||
** xref:developer_stm32.adoc[Developer: STM32]
|
** xref:developer_stm32.adoc[Developer: STM32]
|
||||||
* xref:faq.adoc[Frequently Asked Questions]
|
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
= Frequently Asked Questions
|
|
||||||
|
|
||||||
These are a list of unsorted, commonly asked questions and answers.
|
|
||||||
|
|
||||||
Please feel free to add items to link:https://github.com/embassy-rs/embassy/edit/main/docs/modules/ROOT/pages/faq.adoc[this page], especially if someone in the chat answered a question for you!
|
|
||||||
|
|
||||||
== Missing main macro
|
|
||||||
|
|
||||||
If you see an error like this:
|
|
||||||
|
|
||||||
[source,rust]
|
|
||||||
----
|
|
||||||
#[embassy_executor::main]
|
|
||||||
| ^^^^ could not find `main` in `embassy_executor`
|
|
||||||
----
|
|
||||||
|
|
||||||
You are likely missing some features of the `embassy-executor` crate.
|
|
||||||
|
|
||||||
For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate:
|
|
||||||
|
|
||||||
* `arch-cortex-m`
|
|
||||||
* `executor-thread`
|
|
||||||
* `nightly`
|
|
||||||
|
|
||||||
For Xtensa ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
|
|
@ -616,11 +616,9 @@ impl<D: Driver> Stack<D> {
|
|||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address());
|
|
||||||
let mut smoldev = DriverAdapter {
|
let mut smoldev = DriverAdapter {
|
||||||
cx: Some(cx),
|
cx: Some(cx),
|
||||||
inner: &mut i.device,
|
inner: &mut i.device,
|
||||||
medium,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match s
|
match s
|
||||||
@ -655,11 +653,9 @@ impl<D: Driver> Stack<D> {
|
|||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address());
|
|
||||||
let mut smoldev = DriverAdapter {
|
let mut smoldev = DriverAdapter {
|
||||||
cx: Some(cx),
|
cx: Some(cx),
|
||||||
inner: &mut i.device,
|
inner: &mut i.device,
|
||||||
medium,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match s
|
match s
|
||||||
|
@ -58,7 +58,7 @@ rand_core = "0.6.3"
|
|||||||
sdio-host = "0.5.0"
|
sdio-host = "0.5.0"
|
||||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||||
critical-section = "1.1"
|
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"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
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]
|
[features]
|
||||||
|
@ -61,7 +61,6 @@ fn main() {
|
|||||||
let mut singletons: Vec<String> = Vec::new();
|
let mut singletons: Vec<String> = Vec::new();
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
if let Some(r) = &p.registers {
|
if let Some(r) = &p.registers {
|
||||||
println!("cargo:rustc-cfg=peri_{}", p.name.to_ascii_lowercase());
|
|
||||||
match r.kind {
|
match r.kind {
|
||||||
// Generate singletons per pin, not per port
|
// Generate singletons per pin, not per port
|
||||||
"gpio" => {
|
"gpio" => {
|
||||||
@ -1138,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
|
// Write foreach_foo! macrotables
|
||||||
|
|
||||||
@ -1296,6 +1312,9 @@ fn main() {
|
|||||||
|
|
||||||
let mut m = String::new();
|
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_flash_region", &flash_regions_table);
|
||||||
make_table(&mut m, "foreach_interrupt", &interrupts_table);
|
make_table(&mut m, "foreach_interrupt", &interrupts_table);
|
||||||
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use core::cell::{RefCell, RefMut};
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
@ -83,7 +84,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Can<'d, T: Instance> {
|
pub struct Can<'d, T: Instance> {
|
||||||
pub can: bxcan::Can<BxcanInstance<'d, T>>,
|
pub can: RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -174,12 +175,17 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||||
|
|
||||||
let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled();
|
let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled();
|
||||||
Self { can }
|
let can_ref_cell = RefCell::new(can);
|
||||||
|
Self { can: can_ref_cell }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_bitrate(&mut self, bitrate: u32) {
|
pub fn set_bitrate(&mut self, bitrate: u32) {
|
||||||
let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap();
|
let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap();
|
||||||
self.can.modify_config().set_bit_timing(bit_timing).leave_disabled();
|
self.can
|
||||||
|
.borrow_mut()
|
||||||
|
.modify_config()
|
||||||
|
.set_bit_timing(bit_timing)
|
||||||
|
.leave_disabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables the peripheral and synchronizes with the bus.
|
/// Enables the peripheral and synchronizes with the bus.
|
||||||
@ -187,7 +193,7 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
/// This will wait for 11 consecutive recessive bits (bus idle state).
|
/// This will wait for 11 consecutive recessive bits (bus idle state).
|
||||||
/// Contrary to enable method from bxcan library, this will not freeze the executor while waiting.
|
/// Contrary to enable method from bxcan library, this will not freeze the executor while waiting.
|
||||||
pub async fn enable(&mut self) {
|
pub async fn enable(&mut self) {
|
||||||
while self.enable_non_blocking().is_err() {
|
while self.borrow_mut().enable_non_blocking().is_err() {
|
||||||
// SCE interrupt is only generated for entering sleep mode, but not leaving.
|
// SCE interrupt is only generated for entering sleep mode, but not leaving.
|
||||||
// Yield to allow other tasks to execute while can bus is initializing.
|
// Yield to allow other tasks to execute while can bus is initializing.
|
||||||
embassy_futures::yield_now().await;
|
embassy_futures::yield_now().await;
|
||||||
@ -196,46 +202,46 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
|
|
||||||
/// Queues the message to be sent but exerts backpressure
|
/// Queues the message to be sent but exerts backpressure
|
||||||
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
||||||
self.split().0.write(frame).await
|
CanTx { can: &self.can }.write(frame).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to transmit a frame without blocking.
|
/// Attempts to transmit a frame without blocking.
|
||||||
///
|
///
|
||||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||||
pub fn try_write(&mut self, frame: &Frame) -> Result<bxcan::TransmitStatus, TryWriteError> {
|
pub fn try_write(&mut self, frame: &Frame) -> Result<bxcan::TransmitStatus, TryWriteError> {
|
||||||
self.split().0.try_write(frame)
|
CanTx { can: &self.can }.try_write(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits for a specific transmit mailbox to become empty
|
/// Waits for a specific transmit mailbox to become empty
|
||||||
pub async fn flush(&self, mb: bxcan::Mailbox) {
|
pub async fn flush(&self, mb: bxcan::Mailbox) {
|
||||||
CanTx::<T>::flush_inner(mb).await
|
CanTx { can: &self.can }.flush(mb).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until any of the transmit mailboxes become empty
|
/// Waits until any of the transmit mailboxes become empty
|
||||||
pub async fn flush_any(&self) {
|
pub async fn flush_any(&self) {
|
||||||
CanTx::<T>::flush_any_inner().await
|
CanTx { can: &self.can }.flush_any().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until all of the transmit mailboxes become empty
|
/// Waits until all of the transmit mailboxes become empty
|
||||||
pub async fn flush_all(&self) {
|
pub async fn flush_all(&self) {
|
||||||
CanTx::<T>::flush_all_inner().await
|
CanTx { can: &self.can }.flush_all().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tuple of the time the message was received and the message frame
|
/// Returns a tuple of the time the message was received and the message frame
|
||||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||||
self.split().1.read().await
|
CanRx { can: &self.can }.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to read a can frame without blocking.
|
/// Attempts to read a can frame without blocking.
|
||||||
///
|
///
|
||||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||||
self.split().1.try_read()
|
CanRx { can: &self.can }.try_read()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits while receive queue is empty.
|
/// Waits while receive queue is empty.
|
||||||
pub async fn wait_not_empty(&mut self) {
|
pub async fn wait_not_empty(&mut self) {
|
||||||
self.split().1.wait_not_empty().await
|
CanRx { can: &self.can }.wait_not_empty().await
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn receive_fifo(fifo: RxFifo) {
|
unsafe fn receive_fifo(fifo: RxFifo) {
|
||||||
@ -379,25 +385,24 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1))
|
Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split<'c>(&'c mut self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) {
|
pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) {
|
||||||
let (tx, rx0, rx1) = self.can.split_by_ref();
|
(CanTx { can: &self.can }, CanRx { can: &self.can })
|
||||||
(CanTx { tx }, CanRx { rx0, rx1 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_mut(&mut self) -> &mut bxcan::Can<BxcanInstance<'d, T>> {
|
pub fn as_mut(&self) -> RefMut<'_, bxcan::Can<BxcanInstance<'d, T>>> {
|
||||||
&mut self.can
|
self.can.borrow_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CanTx<'c, 'd, T: Instance> {
|
pub struct CanTx<'c, 'd, T: Instance> {
|
||||||
tx: &'c mut bxcan::Tx<BxcanInstance<'d, T>>,
|
can: &'c RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
||||||
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
T::state().tx_waker.register(cx.waker());
|
T::state().tx_waker.register(cx.waker());
|
||||||
if let Ok(status) = self.tx.transmit(frame) {
|
if let Ok(status) = self.can.borrow_mut().transmit(frame) {
|
||||||
return Poll::Ready(status);
|
return Poll::Ready(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,10 +415,11 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
|||||||
///
|
///
|
||||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||||
pub fn try_write(&mut self, frame: &Frame) -> Result<bxcan::TransmitStatus, TryWriteError> {
|
pub fn try_write(&mut self, frame: &Frame) -> Result<bxcan::TransmitStatus, TryWriteError> {
|
||||||
self.tx.transmit(frame).map_err(|_| TryWriteError::Full)
|
self.can.borrow_mut().transmit(frame).map_err(|_| TryWriteError::Full)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn flush_inner(mb: bxcan::Mailbox) {
|
/// Waits for a specific transmit mailbox to become empty
|
||||||
|
pub async fn flush(&self, mb: bxcan::Mailbox) {
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
T::state().tx_waker.register(cx.waker());
|
T::state().tx_waker.register(cx.waker());
|
||||||
if T::regs().tsr().read().tme(mb.index()) {
|
if T::regs().tsr().read().tme(mb.index()) {
|
||||||
@ -425,12 +431,8 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits for a specific transmit mailbox to become empty
|
/// Waits until any of the transmit mailboxes become empty
|
||||||
pub async fn flush(&self, mb: bxcan::Mailbox) {
|
pub async fn flush_any(&self) {
|
||||||
Self::flush_inner(mb).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn flush_any_inner() {
|
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
T::state().tx_waker.register(cx.waker());
|
T::state().tx_waker.register(cx.waker());
|
||||||
|
|
||||||
@ -447,12 +449,8 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until any of the transmit mailboxes become empty
|
/// Waits until all of the transmit mailboxes become empty
|
||||||
pub async fn flush_any(&self) {
|
pub async fn flush_all(&self) {
|
||||||
Self::flush_any_inner().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn flush_all_inner() {
|
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
T::state().tx_waker.register(cx.waker());
|
T::state().tx_waker.register(cx.waker());
|
||||||
|
|
||||||
@ -468,17 +466,11 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until all of the transmit mailboxes become empty
|
|
||||||
pub async fn flush_all(&self) {
|
|
||||||
Self::flush_all_inner().await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct CanRx<'c, 'd, T: Instance> {
|
pub struct CanRx<'c, 'd, T: Instance> {
|
||||||
rx0: &'c mut bxcan::Rx0<BxcanInstance<'d, T>>,
|
can: &'c RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||||
rx1: &'c mut bxcan::Rx1<BxcanInstance<'d, T>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
|
impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
|
||||||
@ -546,7 +538,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Deref for Can<'d, T> {
|
impl<'d, T: Instance> Deref for Can<'d, T> {
|
||||||
type Target = bxcan::Can<BxcanInstance<'d, T>>;
|
type Target = RefCell<bxcan::Can<BxcanInstance<'d, T>>>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.can
|
&self.can
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
|
|
||||||
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
||||||
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
|
|
||||||
@ -23,6 +26,20 @@ pub enum Error {
|
|||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use super::*;
|
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 {
|
pub trait Instance: crate::rcc::RccPeripheral {
|
||||||
fn regs() -> crate::pac::i2c::I2c;
|
fn regs() -> crate::pac::i2c::I2c;
|
||||||
fn state() -> &'static State;
|
fn state() -> &'static State;
|
||||||
@ -30,7 +47,8 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance: sealed::Instance + 'static {
|
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);
|
pin_trait!(SclPin, Instance);
|
||||||
@ -38,21 +56,148 @@ pin_trait!(SdaPin, Instance);
|
|||||||
dma_trait!(RxDma, Instance);
|
dma_trait!(RxDma, Instance);
|
||||||
dma_trait!(TxDma, Instance);
|
dma_trait!(TxDma, Instance);
|
||||||
|
|
||||||
foreach_interrupt!(
|
/// Interrupt handler.
|
||||||
($inst:ident, i2c, $block:ident, EV, $irq:ident) => {
|
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 {
|
impl sealed::Instance for peripherals::$inst {
|
||||||
fn regs() -> crate::pac::i2c::I2c {
|
fn regs() -> crate::pac::i2c::I2c {
|
||||||
crate::pac::$inst
|
crate::pac::$inst
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state() -> &'static State {
|
fn state() -> &'static sealed::State {
|
||||||
static STATE: State = State::new();
|
static STATE: sealed::State = sealed::State::new();
|
||||||
&STATE
|
&STATE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for peripherals::$inst {
|
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::marker::PhantomData;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
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 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::sealed::AFType;
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::pac::i2c;
|
use crate::pac::i2c;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{interrupt, Peripheral};
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
/// Interrupt handler.
|
pub unsafe fn on_interrupt<T: Instance>() {
|
||||||
pub struct InterruptHandler<T: Instance> {
|
let regs = T::regs();
|
||||||
_phantom: PhantomData<T>,
|
// 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();
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
critical_section::with(|_| {
|
||||||
unsafe fn on_interrupt() {}
|
// Clear event interrupt flag.
|
||||||
|
regs.cr2().modify(|w| {
|
||||||
|
w.set_itevten(false);
|
||||||
|
w.set_iterren(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -27,14 +37,6 @@ pub struct Config {
|
|||||||
pub scl_pullup: bool,
|
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> {
|
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -48,7 +50,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
_peri: impl Peripheral<P = T> + 'd,
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||||
sda: impl Peripheral<P = impl SdaPin<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,
|
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
@ -98,6 +102,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
reg.set_pe(true);
|
reg.set_pe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
unsafe { T::EventInterrupt::enable() };
|
||||||
|
unsafe { T::ErrorInterrupt::enable() };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
tx_dma,
|
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
|
// 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.
|
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
||||||
let sr1 = T::regs().sr1().read();
|
let sr1 = T::regs().sr1().read();
|
||||||
|
|
||||||
if sr1.timeout() {
|
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);
|
return Err(Error::Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.pecerr() {
|
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);
|
return Err(Error::Crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.ovr() {
|
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);
|
return Err(Error::Overrun);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.af() {
|
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);
|
return Err(Error::Nack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if sr1.arlo() {
|
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);
|
return Err(Error::Arbitration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
||||||
// clearing the BERR bit instead.
|
// clearing the BERR bit instead.
|
||||||
if sr1.berr() {
|
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)
|
Ok(sr1)
|
||||||
@ -157,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait until START condition was generated
|
// Wait until START condition was generated
|
||||||
while !self.check_and_clear_error_flags()?.start() {
|
while !Self::check_and_clear_error_flags()?.start() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also wait until signalled we're master and everything is waiting for us
|
// Also wait until signalled we're master and everything is waiting for us
|
||||||
while {
|
while {
|
||||||
self.check_and_clear_error_flags()?;
|
Self::check_and_clear_error_flags()?;
|
||||||
|
|
||||||
let sr2 = T::regs().sr2().read();
|
let sr2 = T::regs().sr2().read();
|
||||||
!sr2.msl() && !sr2.busy()
|
!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 until address was sent
|
||||||
// Wait for the address to be acknowledged
|
// Wait for the address to be acknowledged
|
||||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
// 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()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// Wait until we're ready for sending
|
// Wait until we're ready for sending
|
||||||
while {
|
while {
|
||||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
// 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()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
@ -208,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// Wait until byte is transferred
|
// Wait until byte is transferred
|
||||||
while {
|
while {
|
||||||
// Check for any potential error conditions.
|
// Check for any potential error conditions.
|
||||||
!self.check_and_clear_error_flags()?.btf()
|
!Self::check_and_clear_error_flags()?.btf()
|
||||||
} {
|
} {
|
||||||
check_timeout()?;
|
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> {
|
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
||||||
while {
|
while {
|
||||||
// Check for any potential error conditions.
|
// Check for any potential error conditions.
|
||||||
self.check_and_clear_error_flags()?;
|
Self::check_and_clear_error_flags()?;
|
||||||
|
|
||||||
!T::regs().sr1().read().rxne()
|
!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
|
// Wait until START condition was generated
|
||||||
while !self.check_and_clear_error_flags()?.start() {
|
while !Self::check_and_clear_error_flags()?.start() {
|
||||||
check_timeout()?;
|
check_timeout()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
|
|
||||||
// Wait until address was sent
|
// Wait until address was sent
|
||||||
// Wait for the address to be acknowledged
|
// 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()?;
|
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> {
|
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(()))
|
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> {
|
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 {
|
enum Mode {
|
||||||
Fast,
|
Fast,
|
||||||
Standard,
|
Standard,
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::dma::{NoDma, Transfer};
|
use crate::dma::{NoDma, Transfer};
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::pac::i2c;
|
use crate::pac::i2c;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
@ -36,25 +34,18 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
|
|||||||
move || Ok(())
|
move || Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt handler.
|
pub unsafe fn on_interrupt<T: Instance>() {
|
||||||
pub struct InterruptHandler<T: Instance> {
|
let regs = T::regs();
|
||||||
_phantom: PhantomData<T>,
|
let isr = regs.isr().read();
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
if isr.tcr() || isr.tc() {
|
||||||
unsafe fn on_interrupt() {
|
T::state().waker.wake();
|
||||||
let regs = T::regs();
|
|
||||||
let isr = regs.isr().read();
|
|
||||||
|
|
||||||
if isr.tcr() || isr.tc() {
|
|
||||||
T::state().waker.wake();
|
|
||||||
}
|
|
||||||
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
|
||||||
// the interrupt
|
|
||||||
critical_section::with(|_| {
|
|
||||||
regs.cr1().modify(|w| w.set_tcie(false));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||||
|
// the interrupt
|
||||||
|
critical_section::with(|_| {
|
||||||
|
regs.cr1().modify(|w| w.set_tcie(false));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -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> {
|
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -104,7 +83,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||||
sda: impl Peripheral<P = impl SdaPin<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,
|
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
@ -150,8 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
reg.set_pe(true);
|
reg.set_pe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
T::Interrupt::unpend();
|
unsafe { T::EventInterrupt::enable() };
|
||||||
unsafe { T::Interrupt::enable() };
|
unsafe { T::ErrorInterrupt::enable() };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
_peri: peri,
|
_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
|
/// I2C Stop Configuration
|
||||||
///
|
///
|
||||||
/// Peripheral options for generating the STOP condition
|
/// 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> {
|
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||||
type Config = Hertz;
|
type Config = Hertz;
|
||||||
type ConfigError = ();
|
type ConfigError = ();
|
||||||
|
@ -31,9 +31,12 @@ impl From<OpAmpSpeed> for crate::pac::opamp::vals::OpampCsrOpahsm {
|
|||||||
|
|
||||||
/// OpAmp external outputs, wired to a GPIO pad.
|
/// OpAmp external outputs, wired to a GPIO pad.
|
||||||
///
|
///
|
||||||
|
/// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere.
|
||||||
|
///
|
||||||
/// This struct can also be used as an ADC input.
|
/// This struct can also be used as an ADC input.
|
||||||
pub struct OpAmpOutput<'d, T: Instance> {
|
pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin<T>> {
|
||||||
_inner: &'d OpAmp<'d, T>,
|
_inner: &'d OpAmp<'d, T>,
|
||||||
|
_output: &'p mut P,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OpAmp internal outputs, wired directly to ADC inputs.
|
/// OpAmp internal outputs, wired directly to ADC inputs.
|
||||||
@ -51,12 +54,19 @@ pub struct OpAmp<'d, T: Instance> {
|
|||||||
impl<'d, T: Instance> OpAmp<'d, T> {
|
impl<'d, T: Instance> OpAmp<'d, T> {
|
||||||
/// Create a new driver instance.
|
/// Create a new driver instance.
|
||||||
///
|
///
|
||||||
/// Does not enable the opamp, but does set the speed mode on some families.
|
/// Enables the OpAmp and configures the speed, but
|
||||||
|
/// does not set any other configuration.
|
||||||
pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
|
pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
|
||||||
into_ref!(opamp);
|
into_ref!(opamp);
|
||||||
|
|
||||||
|
#[cfg(opamp_f3)]
|
||||||
|
T::regs().opampcsr().modify(|w| {
|
||||||
|
w.set_opampen(true);
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(opamp_g4)]
|
#[cfg(opamp_g4)]
|
||||||
T::regs().opamp_csr().modify(|w| {
|
T::regs().opamp_csr().modify(|w| {
|
||||||
|
w.set_opaen(true);
|
||||||
w.set_opahsm(speed.into());
|
w.set_opahsm(speed.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -64,23 +74,24 @@ impl<'d, T: Instance> OpAmp<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the OpAmp as a buffer for the provided input pin,
|
/// Configure the OpAmp as a buffer for the provided input pin,
|
||||||
/// outputting to the provided output pin, and enable the opamp.
|
/// outputting to the provided output pin.
|
||||||
///
|
///
|
||||||
/// The input pin is configured for analogue mode but not consumed,
|
/// The input pin is configured for analogue mode but not consumed,
|
||||||
/// so it may subsequently be used for ADC or comparator inputs.
|
/// so it may subsequently be used for ADC or comparator inputs.
|
||||||
///
|
///
|
||||||
/// The output pin is held within the returned [`OpAmpOutput`] struct,
|
/// The output pin is held within the returned [`OpAmpOutput`] struct,
|
||||||
/// preventing it being used elsewhere. The `OpAmpOutput` can then be
|
/// preventing it being used elsewhere. The `OpAmpOutput` can then be
|
||||||
/// directly used as an ADC input. The opamp will be disabled when the
|
/// directly used as an ADC input.
|
||||||
/// [`OpAmpOutput`] is dropped.
|
pub fn buffer_ext<'a, 'b, IP, OP>(
|
||||||
pub fn buffer_ext(
|
&'a mut self,
|
||||||
&'d mut self,
|
in_pin: &IP,
|
||||||
in_pin: impl Peripheral<P = impl NonInvertingPin<T> + crate::gpio::sealed::Pin>,
|
out_pin: &'b mut OP,
|
||||||
out_pin: impl Peripheral<P = impl OutputPin<T> + crate::gpio::sealed::Pin> + 'd,
|
|
||||||
gain: OpAmpGain,
|
gain: OpAmpGain,
|
||||||
) -> OpAmpOutput<'d, T> {
|
) -> OpAmpOutput<'a, 'b, T, OP>
|
||||||
into_ref!(in_pin);
|
where
|
||||||
into_ref!(out_pin);
|
IP: NonInvertingPin<T> + crate::gpio::sealed::Pin,
|
||||||
|
OP: OutputPin<T> + crate::gpio::sealed::Pin,
|
||||||
|
{
|
||||||
in_pin.set_as_analog();
|
in_pin.set_as_analog();
|
||||||
out_pin.set_as_analog();
|
out_pin.set_as_analog();
|
||||||
|
|
||||||
@ -111,24 +122,24 @@ impl<'d, T: Instance> OpAmp<'d, T> {
|
|||||||
w.set_opaen(true);
|
w.set_opaen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
OpAmpOutput { _inner: self }
|
OpAmpOutput {
|
||||||
|
_inner: self,
|
||||||
|
_output: out_pin,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the OpAmp as a buffer for the provided input pin,
|
/// Configure the OpAmp as a buffer for the provided input pin,
|
||||||
/// with the output only used internally, and enable the opamp.
|
/// with the output only used internally.
|
||||||
///
|
///
|
||||||
/// The input pin is configured for analogue mode but not consumed,
|
/// The input pin is configured for analogue mode but not consumed,
|
||||||
/// so it may be subsequently used for ADC or comparator inputs.
|
/// so it may be subsequently used for ADC or comparator inputs.
|
||||||
///
|
///
|
||||||
/// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
|
/// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
|
||||||
/// The opamp output will be disabled when it is dropped.
|
|
||||||
#[cfg(opamp_g4)]
|
#[cfg(opamp_g4)]
|
||||||
pub fn buffer_int(
|
pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T>
|
||||||
&'d mut self,
|
where
|
||||||
pin: impl Peripheral<P = impl NonInvertingPin<T> + crate::gpio::sealed::Pin>,
|
P: NonInvertingPin<T> + crate::gpio::sealed::Pin,
|
||||||
gain: OpAmpGain,
|
{
|
||||||
) -> OpAmpInternalOutput<'d, T> {
|
|
||||||
into_ref!(pin);
|
|
||||||
pin.set_as_analog();
|
pin.set_as_analog();
|
||||||
|
|
||||||
let (vm_sel, pga_gain) = match gain {
|
let (vm_sel, pga_gain) = match gain {
|
||||||
@ -152,21 +163,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> {
|
impl<'d, T: Instance> Drop for OpAmp<'d, T> {
|
||||||
fn drop(&mut self) {
|
|
||||||
#[cfg(opamp_f3)]
|
|
||||||
T::regs().opampcsr().modify(|w| {
|
|
||||||
w.set_opampen(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(opamp_g4)]
|
|
||||||
T::regs().opamp_csr().modify(|w| {
|
|
||||||
w.set_opaen(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
#[cfg(opamp_f3)]
|
#[cfg(opamp_f3)]
|
||||||
T::regs().opampcsr().modify(|w| {
|
T::regs().opampcsr().modify(|w| {
|
||||||
@ -206,16 +203,16 @@ macro_rules! impl_opamp_external_output {
|
|||||||
($inst:ident, $adc:ident, $ch:expr) => {
|
($inst:ident, $adc:ident, $ch:expr) => {
|
||||||
foreach_adc!(
|
foreach_adc!(
|
||||||
($adc, $common_inst:ident, $adc_clock:ident) => {
|
($adc, $common_inst:ident, $adc_clock:ident) => {
|
||||||
impl<'d> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
||||||
for OpAmpOutput<'d, crate::peripherals::$inst>
|
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||||
{
|
{
|
||||||
fn channel(&self) -> u8 {
|
fn channel(&self) -> u8 {
|
||||||
$ch
|
$ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> crate::adc::AdcPin<crate::peripherals::$adc>
|
impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
|
||||||
for OpAmpOutput<'d, crate::peripherals::$inst>
|
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -168,12 +168,7 @@ impl Default for Config {
|
|||||||
apb4_pre: APBPrescaler::DIV1,
|
apb4_pre: APBPrescaler::DIV1,
|
||||||
|
|
||||||
per_clock_source: PerClockSource::HSI,
|
per_clock_source: PerClockSource::HSI,
|
||||||
|
adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5
|
||||||
#[cfg(stm32h5)]
|
|
||||||
adc_clock_source: AdcClockSource::HCLK1,
|
|
||||||
#[cfg(stm32h7)]
|
|
||||||
adc_clock_source: AdcClockSource::PER,
|
|
||||||
|
|
||||||
timer_prescaler: TimerPrescaler::DefaultX2,
|
timer_prescaler: TimerPrescaler::DefaultX2,
|
||||||
voltage_scale: VoltageScale::Scale0,
|
voltage_scale: VoltageScale::Scale0,
|
||||||
ls: Default::default(),
|
ls: Default::default(),
|
||||||
|
@ -207,40 +207,27 @@ impl Protocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum SyncInput {
|
pub enum SyncEnable {
|
||||||
/// Not synced to any other SAI unit.
|
Asynchronous,
|
||||||
None,
|
|
||||||
/// Syncs with the other A/B sub-block within the SAI unit
|
/// Syncs with the other A/B sub-block within the SAI unit
|
||||||
Internal,
|
Internal,
|
||||||
/// Syncs with a sub-block in the other SAI unit
|
/// Syncs with a sub-block in the other SAI unit - use set_sync_output() and set_sync_input()
|
||||||
#[cfg(sai_v4)]
|
#[cfg(any(sai_v4))]
|
||||||
External(SyncInputInstance),
|
External,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncInput {
|
impl SyncEnable {
|
||||||
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
||||||
pub const fn syncen(&self) -> vals::Syncen {
|
pub const fn syncen(&self) -> vals::Syncen {
|
||||||
match self {
|
match self {
|
||||||
SyncInput::None => vals::Syncen::ASYNCHRONOUS,
|
SyncEnable::Asynchronous => vals::Syncen::ASYNCHRONOUS,
|
||||||
SyncInput::Internal => vals::Syncen::INTERNAL,
|
SyncEnable::Internal => vals::Syncen::INTERNAL,
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(any(sai_v4))]
|
||||||
SyncInput::External(_) => vals::Syncen::EXTERNAL,
|
SyncEnable::External => vals::Syncen::EXTERNAL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(sai_v4)]
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
|
||||||
pub enum SyncInputInstance {
|
|
||||||
#[cfg(peri_sai1)]
|
|
||||||
Sai1 = 0,
|
|
||||||
#[cfg(peri_sai2)]
|
|
||||||
Sai2 = 1,
|
|
||||||
#[cfg(peri_sai3)]
|
|
||||||
Sai3 = 2,
|
|
||||||
#[cfg(peri_sai4)]
|
|
||||||
Sai4 = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum StereoMono {
|
pub enum StereoMono {
|
||||||
Stereo,
|
Stereo,
|
||||||
@ -441,8 +428,8 @@ impl MasterClockDivider {
|
|||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub tx_rx: TxRx,
|
pub tx_rx: TxRx,
|
||||||
pub sync_input: SyncInput,
|
pub sync_enable: SyncEnable,
|
||||||
pub sync_output: bool,
|
pub is_sync_output: bool,
|
||||||
pub protocol: Protocol,
|
pub protocol: Protocol,
|
||||||
pub slot_size: SlotSize,
|
pub slot_size: SlotSize,
|
||||||
pub slot_count: word::U4,
|
pub slot_count: word::U4,
|
||||||
@ -472,8 +459,8 @@ impl Default for Config {
|
|||||||
Self {
|
Self {
|
||||||
mode: Mode::Master,
|
mode: Mode::Master,
|
||||||
tx_rx: TxRx::Transmitter,
|
tx_rx: TxRx::Transmitter,
|
||||||
sync_output: false,
|
is_sync_output: false,
|
||||||
sync_input: SyncInput::None,
|
sync_enable: SyncEnable::Asynchronous,
|
||||||
protocol: Protocol::Free,
|
protocol: Protocol::Free,
|
||||||
slot_size: SlotSize::DataSize,
|
slot_size: SlotSize::DataSize,
|
||||||
slot_count: word::U4(2),
|
slot_count: word::U4(2),
|
||||||
@ -621,18 +608,18 @@ impl<'d, T: Instance> Sai<'d, T> {
|
|||||||
|
|
||||||
fn update_synchronous_config(config: &mut Config) {
|
fn update_synchronous_config(config: &mut Config) {
|
||||||
config.mode = Mode::Slave;
|
config.mode = Mode::Slave;
|
||||||
config.sync_output = false;
|
config.is_sync_output = false;
|
||||||
|
|
||||||
#[cfg(any(sai_v1, sai_v2, sai_v3))]
|
#[cfg(any(sai_v1, sai_v2, sai_v3))]
|
||||||
{
|
{
|
||||||
config.sync_input = SyncInput::Internal;
|
config.sync_enable = SyncEnable::Internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(any(sai_v4))]
|
||||||
{
|
{
|
||||||
//this must either be Internal or External
|
//this must either be Internal or External
|
||||||
//The asynchronous sub-block on the same SAI needs to enable sync_output
|
//The asynchronous sub-block on the same SAI needs to enable is_sync_output
|
||||||
assert!(config.sync_input != SyncInput::None);
|
assert!(config.sync_enable != SyncEnable::Asynchronous);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,13 +866,20 @@ impl<'d, T: Instance, C: Channel, W: word::Word> SubBlock<'d, T, C, W> {
|
|||||||
|
|
||||||
#[cfg(any(sai_v4))]
|
#[cfg(any(sai_v4))]
|
||||||
{
|
{
|
||||||
if let SyncInput::External(i) = config.sync_input {
|
// Not totally clear from the datasheet if this is right
|
||||||
T::REGS.gcr().modify(|w| {
|
// This is only used if using SyncEnable::External on the other SAI unit
|
||||||
w.set_syncin(i as u8);
|
// 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 config.sync_output {
|
if config.is_sync_output {
|
||||||
let syncout: u8 = match sub_block {
|
let syncout: u8 = match sub_block {
|
||||||
WhichSubBlock::A => 0b01,
|
WhichSubBlock::A => 0b01,
|
||||||
WhichSubBlock::B => 0b10,
|
WhichSubBlock::B => 0b10,
|
||||||
@ -909,7 +903,7 @@ impl<'d, T: Instance, C: Channel, W: word::Word> SubBlock<'d, T, C, W> {
|
|||||||
w.set_ds(config.data_size.ds());
|
w.set_ds(config.data_size.ds());
|
||||||
w.set_lsbfirst(config.bit_order.lsbfirst());
|
w.set_lsbfirst(config.bit_order.lsbfirst());
|
||||||
w.set_ckstr(config.clock_strobe.ckstr());
|
w.set_ckstr(config.clock_strobe.ckstr());
|
||||||
w.set_syncen(config.sync_input.syncen());
|
w.set_syncen(config.sync_enable.syncen());
|
||||||
w.set_mono(config.stereo_mono.mono());
|
w.set_mono(config.stereo_mono.mono());
|
||||||
w.set_outdriv(config.output_drive.outdriv());
|
w.set_outdriv(config.output_drive.outdriv());
|
||||||
w.set_mckdiv(config.master_clock_divider.mckdiv());
|
w.set_mckdiv(config.master_clock_divider.mckdiv());
|
||||||
|
@ -5,7 +5,6 @@ An [Embassy](https://embassy.dev) project.
|
|||||||
Synchronization primitives and data structures with async support:
|
Synchronization primitives and data structures with async support:
|
||||||
|
|
||||||
- [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer.
|
- [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer.
|
||||||
- [`PriorityChannel`](channel::priority::PriorityChannel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. Higher priority items are sifted to the front of the channel.
|
|
||||||
- [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers.
|
- [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers.
|
||||||
- [`Signal`](signal::Signal) - Signalling latest value to a single consumer.
|
- [`Signal`](signal::Signal) - Signalling latest value to a single consumer.
|
||||||
- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks.
|
- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks.
|
||||||
|
@ -76,7 +76,7 @@ where
|
|||||||
|
|
||||||
/// Send-only access to a [`Channel`] without knowing channel size.
|
/// Send-only access to a [`Channel`] without knowing channel size.
|
||||||
pub struct DynamicSender<'ch, T> {
|
pub struct DynamicSender<'ch, T> {
|
||||||
pub(crate) channel: &'ch dyn DynamicChannel<T>,
|
channel: &'ch dyn DynamicChannel<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ch, T> Clone for DynamicSender<'ch, T> {
|
impl<'ch, T> Clone for DynamicSender<'ch, T> {
|
||||||
@ -176,7 +176,7 @@ where
|
|||||||
|
|
||||||
/// Receive-only access to a [`Channel`] without knowing channel size.
|
/// Receive-only access to a [`Channel`] without knowing channel size.
|
||||||
pub struct DynamicReceiver<'ch, T> {
|
pub struct DynamicReceiver<'ch, T> {
|
||||||
pub(crate) channel: &'ch dyn DynamicChannel<T>,
|
channel: &'ch dyn DynamicChannel<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ch, T> Clone for DynamicReceiver<'ch, T> {
|
impl<'ch, T> Clone for DynamicReceiver<'ch, T> {
|
||||||
@ -321,7 +321,7 @@ impl<'ch, T> Future for DynamicSendFuture<'ch, T> {
|
|||||||
|
|
||||||
impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {}
|
impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {}
|
||||||
|
|
||||||
pub(crate) trait DynamicChannel<T> {
|
trait DynamicChannel<T> {
|
||||||
fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>;
|
fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>;
|
||||||
|
|
||||||
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>;
|
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>;
|
||||||
|
@ -15,7 +15,6 @@ pub mod blocking_mutex;
|
|||||||
pub mod channel;
|
pub mod channel;
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod priority_channel;
|
|
||||||
pub mod pubsub;
|
pub mod pubsub;
|
||||||
pub mod signal;
|
pub mod signal;
|
||||||
pub mod waitqueue;
|
pub mod waitqueue;
|
||||||
|
@ -1,613 +0,0 @@
|
|||||||
//! A queue for sending values between asynchronous tasks.
|
|
||||||
//!
|
|
||||||
//! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue.
|
|
||||||
//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`](heapless::binary_heap::Kind) parameter of the channel.
|
|
||||||
|
|
||||||
use core::cell::RefCell;
|
|
||||||
use core::future::Future;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
|
|
||||||
pub use heapless::binary_heap::{Kind, Max, Min};
|
|
||||||
use heapless::BinaryHeap;
|
|
||||||
|
|
||||||
use crate::blocking_mutex::raw::RawMutex;
|
|
||||||
use crate::blocking_mutex::Mutex;
|
|
||||||
use crate::channel::{DynamicChannel, DynamicReceiver, DynamicSender, TryReceiveError, TrySendError};
|
|
||||||
use crate::waitqueue::WakerRegistration;
|
|
||||||
|
|
||||||
/// Send-only access to a [`PriorityChannel`].
|
|
||||||
pub struct Sender<'ch, M, T, K, const N: usize>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
channel: &'ch PriorityChannel<M, T, K, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Clone for Sender<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Sender { channel: self.channel }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Copy for Sender<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Sender<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
/// Sends a value.
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::send()`]
|
|
||||||
pub fn send(&self, message: T) -> SendFuture<'ch, M, T, K, N> {
|
|
||||||
self.channel.send(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to immediately send a message.
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::send()`]
|
|
||||||
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
|
|
||||||
self.channel.try_send(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows a poll_fn to poll until the channel is ready to send
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::poll_ready_to_send()`]
|
|
||||||
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
self.channel.poll_ready_to_send(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> From<Sender<'ch, M, T, K, N>> for DynamicSender<'ch, T>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
fn from(s: Sender<'ch, M, T, K, N>) -> Self {
|
|
||||||
Self { channel: s.channel }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receive-only access to a [`PriorityChannel`].
|
|
||||||
pub struct Receiver<'ch, M, T, K, const N: usize>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
channel: &'ch PriorityChannel<M, T, K, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Clone for Receiver<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Receiver { channel: self.channel }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Copy for Receiver<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Receiver<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
/// Receive the next value.
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::receive()`].
|
|
||||||
pub fn receive(&self) -> ReceiveFuture<'_, M, T, K, N> {
|
|
||||||
self.channel.receive()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to immediately receive the next value.
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::try_receive()`]
|
|
||||||
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
|
|
||||||
self.channel.try_receive()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows a poll_fn to poll until the channel is ready to receive
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::poll_ready_to_receive()`]
|
|
||||||
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
self.channel.poll_ready_to_receive(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Poll the channel for the next item
|
|
||||||
///
|
|
||||||
/// See [`PriorityChannel::poll_receive()`]
|
|
||||||
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
|
||||||
self.channel.poll_receive(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> From<Receiver<'ch, M, T, K, N>> for DynamicReceiver<'ch, T>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
fn from(s: Receiver<'ch, M, T, K, N>) -> Self {
|
|
||||||
Self { channel: s.channel }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Future returned by [`PriorityChannel::receive`] and [`Receiver::receive`].
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct ReceiveFuture<'ch, M, T, K, const N: usize>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
channel: &'ch PriorityChannel<M, T, K, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Future for ReceiveFuture<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
|
||||||
self.channel.poll_receive(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Future returned by [`PriorityChannel::send`] and [`Sender::send`].
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct SendFuture<'ch, M, T, K, const N: usize>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
channel: &'ch PriorityChannel<M, T, K, N>,
|
|
||||||
message: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Future for SendFuture<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
match self.message.take() {
|
|
||||||
Some(m) => match self.channel.try_send_with_context(m, Some(cx)) {
|
|
||||||
Ok(..) => Poll::Ready(()),
|
|
||||||
Err(TrySendError::Full(m)) => {
|
|
||||||
self.message = Some(m);
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => panic!("Message cannot be None"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ch, M, T, K, const N: usize> Unpin for SendFuture<'ch, M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ChannelState<T, K, const N: usize> {
|
|
||||||
queue: BinaryHeap<T, K, N>,
|
|
||||||
receiver_waker: WakerRegistration,
|
|
||||||
senders_waker: WakerRegistration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, K, const N: usize> ChannelState<T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
const fn new() -> Self {
|
|
||||||
ChannelState {
|
|
||||||
queue: BinaryHeap::new(),
|
|
||||||
receiver_waker: WakerRegistration::new(),
|
|
||||||
senders_waker: WakerRegistration::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_receive(&mut self) -> Result<T, TryReceiveError> {
|
|
||||||
self.try_receive_with_context(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
|
||||||
if self.queue.len() == self.queue.capacity() {
|
|
||||||
self.senders_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(message) = self.queue.pop() {
|
|
||||||
Ok(message)
|
|
||||||
} else {
|
|
||||||
if let Some(cx) = cx {
|
|
||||||
self.receiver_waker.register(cx.waker());
|
|
||||||
}
|
|
||||||
Err(TryReceiveError::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll<T> {
|
|
||||||
if self.queue.len() == self.queue.capacity() {
|
|
||||||
self.senders_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(message) = self.queue.pop() {
|
|
||||||
Poll::Ready(message)
|
|
||||||
} else {
|
|
||||||
self.receiver_waker.register(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
self.receiver_waker.register(cx.waker());
|
|
||||||
|
|
||||||
if !self.queue.is_empty() {
|
|
||||||
Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> {
|
|
||||||
self.try_send_with_context(message, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_send_with_context(&mut self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {
|
|
||||||
match self.queue.push(message) {
|
|
||||||
Ok(()) => {
|
|
||||||
self.receiver_waker.wake();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(message) => {
|
|
||||||
if let Some(cx) = cx {
|
|
||||||
self.senders_waker.register(cx.waker());
|
|
||||||
}
|
|
||||||
Err(TrySendError::Full(message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
self.senders_waker.register(cx.waker());
|
|
||||||
|
|
||||||
if !self.queue.len() == self.queue.capacity() {
|
|
||||||
Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A bounded channel for communicating between asynchronous tasks
|
|
||||||
/// with backpressure.
|
|
||||||
///
|
|
||||||
/// The channel will buffer up to the provided number of messages. Once the
|
|
||||||
/// buffer is full, attempts to `send` new messages will wait until a message is
|
|
||||||
/// received from the channel.
|
|
||||||
///
|
|
||||||
/// Sent data may be reordered based on their priorty within the channel.
|
|
||||||
/// For example, in a [`Max`](heapless::binary_heap::Max) [`PriorityChannel`]
|
|
||||||
/// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be recieved as `[3, 2, 1]`.
|
|
||||||
pub struct PriorityChannel<M, T, K, const N: usize>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
inner: Mutex<M, RefCell<ChannelState<T, K, N>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M, T, K, const N: usize> PriorityChannel<M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
/// Establish a new bounded channel. For example, to create one with a NoopMutex:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use embassy_sync::priority_channel::{PriorityChannel, Max};
|
|
||||||
/// use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|
||||||
///
|
|
||||||
/// // Declare a bounded channel of 3 u32s.
|
|
||||||
/// let mut channel = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();
|
|
||||||
/// ```
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Mutex::new(RefCell::new(ChannelState::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lock<R>(&self, f: impl FnOnce(&mut ChannelState<T, K, N>) -> R) -> R {
|
|
||||||
self.inner.lock(|rc| f(&mut *unwrap!(rc.try_borrow_mut())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
|
||||||
self.lock(|c| c.try_receive_with_context(cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Poll the channel for the next message
|
|
||||||
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
|
||||||
self.lock(|c| c.poll_receive(cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {
|
|
||||||
self.lock(|c| c.try_send_with_context(m, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows a poll_fn to poll until the channel is ready to receive
|
|
||||||
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
self.lock(|c| c.poll_ready_to_receive(cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows a poll_fn to poll until the channel is ready to send
|
|
||||||
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
self.lock(|c| c.poll_ready_to_send(cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a sender for this channel.
|
|
||||||
pub fn sender(&self) -> Sender<'_, M, T, K, N> {
|
|
||||||
Sender { channel: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a receiver for this channel.
|
|
||||||
pub fn receiver(&self) -> Receiver<'_, M, T, K, N> {
|
|
||||||
Receiver { channel: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a value, waiting until there is capacity.
|
|
||||||
///
|
|
||||||
/// Sending completes when the value has been pushed to the channel's queue.
|
|
||||||
/// This doesn't mean the value has been received yet.
|
|
||||||
pub fn send(&self, message: T) -> SendFuture<'_, M, T, K, N> {
|
|
||||||
SendFuture {
|
|
||||||
channel: self,
|
|
||||||
message: Some(message),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to immediately send a message.
|
|
||||||
///
|
|
||||||
/// This method differs from [`send`](PriorityChannel::send) by returning immediately if the channel's
|
|
||||||
/// buffer is full, instead of waiting.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If the channel capacity has been reached, i.e., the channel has `n`
|
|
||||||
/// buffered values where `n` is the argument passed to [`PriorityChannel`], then an
|
|
||||||
/// error is returned.
|
|
||||||
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
|
|
||||||
self.lock(|c| c.try_send(message))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receive the next value.
|
|
||||||
///
|
|
||||||
/// If there are no messages in the channel's buffer, this method will
|
|
||||||
/// wait until a message is sent.
|
|
||||||
pub fn receive(&self) -> ReceiveFuture<'_, M, T, K, N> {
|
|
||||||
ReceiveFuture { channel: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to immediately receive a message.
|
|
||||||
///
|
|
||||||
/// This method will either receive a message from the channel immediately or return an error
|
|
||||||
/// if the channel is empty.
|
|
||||||
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
|
|
||||||
self.lock(|c| c.try_receive())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the
|
|
||||||
/// tradeoff cost of dynamic dispatch.
|
|
||||||
impl<M, T, K, const N: usize> DynamicChannel<T> for PriorityChannel<M, T, K, N>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {
|
|
||||||
PriorityChannel::try_send_with_context(self, m, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
|
||||||
PriorityChannel::try_receive_with_context(self, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
PriorityChannel::poll_ready_to_send(self, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
|
||||||
PriorityChannel::poll_ready_to_receive(self, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
|
||||||
PriorityChannel::poll_receive(self, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use core::time::Duration;
|
|
||||||
|
|
||||||
use futures_executor::ThreadPool;
|
|
||||||
use futures_timer::Delay;
|
|
||||||
use futures_util::task::SpawnExt;
|
|
||||||
use heapless::binary_heap::{Kind, Max};
|
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
|
||||||
|
|
||||||
fn capacity<T, K, const N: usize>(c: &ChannelState<T, K, N>) -> usize
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
c.queue.capacity() - c.queue.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sending_once() {
|
|
||||||
let mut c = ChannelState::<u32, Max, 3>::new();
|
|
||||||
assert!(c.try_send(1).is_ok());
|
|
||||||
assert_eq!(capacity(&c), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sending_when_full() {
|
|
||||||
let mut c = ChannelState::<u32, Max, 3>::new();
|
|
||||||
let _ = c.try_send(1);
|
|
||||||
let _ = c.try_send(1);
|
|
||||||
let _ = c.try_send(1);
|
|
||||||
match c.try_send(2) {
|
|
||||||
Err(TrySendError::Full(2)) => assert!(true),
|
|
||||||
_ => assert!(false),
|
|
||||||
}
|
|
||||||
assert_eq!(capacity(&c), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_priority() {
|
|
||||||
// Prio channel with kind `Max` sifts larger numbers to the front of the queue
|
|
||||||
let mut c = ChannelState::<u32, Max, 3>::new();
|
|
||||||
assert!(c.try_send(1).is_ok());
|
|
||||||
assert!(c.try_send(2).is_ok());
|
|
||||||
assert!(c.try_send(3).is_ok());
|
|
||||||
assert_eq!(c.try_receive().unwrap(), 3);
|
|
||||||
assert_eq!(c.try_receive().unwrap(), 2);
|
|
||||||
assert_eq!(c.try_receive().unwrap(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn receiving_once_with_one_send() {
|
|
||||||
let mut c = ChannelState::<u32, Max, 3>::new();
|
|
||||||
assert!(c.try_send(1).is_ok());
|
|
||||||
assert_eq!(c.try_receive().unwrap(), 1);
|
|
||||||
assert_eq!(capacity(&c), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn receiving_when_empty() {
|
|
||||||
let mut c = ChannelState::<u32, Max, 3>::new();
|
|
||||||
match c.try_receive() {
|
|
||||||
Err(TryReceiveError::Empty) => assert!(true),
|
|
||||||
_ => assert!(false),
|
|
||||||
}
|
|
||||||
assert_eq!(capacity(&c), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_send_and_receive() {
|
|
||||||
let c = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();
|
|
||||||
assert!(c.try_send(1).is_ok());
|
|
||||||
assert_eq!(c.try_receive().unwrap(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cloning() {
|
|
||||||
let c = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();
|
|
||||||
let r1 = c.receiver();
|
|
||||||
let s1 = c.sender();
|
|
||||||
|
|
||||||
let _ = r1.clone();
|
|
||||||
let _ = s1.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dynamic_dispatch() {
|
|
||||||
let c = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();
|
|
||||||
let s: DynamicSender<'_, u32> = c.sender().into();
|
|
||||||
let r: DynamicReceiver<'_, u32> = c.receiver().into();
|
|
||||||
|
|
||||||
assert!(s.try_send(1).is_ok());
|
|
||||||
assert_eq!(r.try_receive().unwrap(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[futures_test::test]
|
|
||||||
async fn receiver_receives_given_try_send_async() {
|
|
||||||
let executor = ThreadPool::new().unwrap();
|
|
||||||
|
|
||||||
static CHANNEL: StaticCell<PriorityChannel<CriticalSectionRawMutex, u32, Max, 3>> = StaticCell::new();
|
|
||||||
let c = &*CHANNEL.init(PriorityChannel::new());
|
|
||||||
let c2 = c;
|
|
||||||
assert!(executor
|
|
||||||
.spawn(async move {
|
|
||||||
assert!(c2.try_send(1).is_ok());
|
|
||||||
})
|
|
||||||
.is_ok());
|
|
||||||
assert_eq!(c.receive().await, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[futures_test::test]
|
|
||||||
async fn sender_send_completes_if_capacity() {
|
|
||||||
let c = PriorityChannel::<CriticalSectionRawMutex, u32, Max, 1>::new();
|
|
||||||
c.send(1).await;
|
|
||||||
assert_eq!(c.receive().await, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[futures_test::test]
|
|
||||||
async fn senders_sends_wait_until_capacity() {
|
|
||||||
let executor = ThreadPool::new().unwrap();
|
|
||||||
|
|
||||||
static CHANNEL: StaticCell<PriorityChannel<CriticalSectionRawMutex, u32, Max, 1>> = StaticCell::new();
|
|
||||||
let c = &*CHANNEL.init(PriorityChannel::new());
|
|
||||||
assert!(c.try_send(1).is_ok());
|
|
||||||
|
|
||||||
let c2 = c;
|
|
||||||
let send_task_1 = executor.spawn_with_handle(async move { c2.send(2).await });
|
|
||||||
let c2 = c;
|
|
||||||
let send_task_2 = executor.spawn_with_handle(async move { c2.send(3).await });
|
|
||||||
// Wish I could think of a means of determining that the async send is waiting instead.
|
|
||||||
// However, I've used the debugger to observe that the send does indeed wait.
|
|
||||||
Delay::new(Duration::from_millis(500)).await;
|
|
||||||
assert_eq!(c.receive().await, 1);
|
|
||||||
assert!(executor
|
|
||||||
.spawn(async move {
|
|
||||||
loop {
|
|
||||||
c.receive().await;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.is_ok());
|
|
||||||
send_task_1.unwrap().await;
|
|
||||||
send_task_2.unwrap().await;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,19 +5,7 @@ MEMORY
|
|||||||
BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K
|
||||||
FLASH : ORIGIN = 0x10007000, LENGTH = 512K
|
FLASH : ORIGIN = 0x10007000, LENGTH = 512K
|
||||||
DFU : ORIGIN = 0x10087000, LENGTH = 516K
|
DFU : ORIGIN = 0x10087000, LENGTH = 516K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
/* Pick one of the two options for RAM layout */
|
|
||||||
|
|
||||||
/* OPTION A: Use all RAM banks as one big block */
|
|
||||||
/* Reasonable, unless you are doing something */
|
|
||||||
/* really particular with DMA or other concurrent */
|
|
||||||
/* access that would benefit from striping */
|
|
||||||
RAM : ORIGIN = 0x20000000, LENGTH = 264K
|
|
||||||
|
|
||||||
/* OPTION B: Keep the unstriped sections separate */
|
|
||||||
/* RAM: ORIGIN = 0x20000000, LENGTH = 256K */
|
|
||||||
/* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K */
|
|
||||||
/* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);
|
||||||
|
@ -6,19 +6,7 @@ MEMORY
|
|||||||
BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K
|
||||||
ACTIVE : ORIGIN = 0x10007000, LENGTH = 512K
|
ACTIVE : ORIGIN = 0x10007000, LENGTH = 512K
|
||||||
DFU : ORIGIN = 0x10087000, LENGTH = 516K
|
DFU : ORIGIN = 0x10087000, LENGTH = 516K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
/* Pick one of the two options for RAM layout */
|
|
||||||
|
|
||||||
/* OPTION A: Use all RAM banks as one big block */
|
|
||||||
/* Reasonable, unless you are doing something */
|
|
||||||
/* really particular with DMA or other concurrent */
|
|
||||||
/* access that would benefit from striping */
|
|
||||||
RAM : ORIGIN = 0x20000000, LENGTH = 264K
|
|
||||||
|
|
||||||
/* OPTION B: Keep the unstriped sections separate */
|
|
||||||
/* RAM: ORIGIN = 0x20000000, LENGTH = 256K */
|
|
||||||
/* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K */
|
|
||||||
/* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);
|
||||||
|
@ -1,17 +1,5 @@
|
|||||||
MEMORY {
|
MEMORY {
|
||||||
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||||
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
/* Pick one of the two options for RAM layout */
|
}
|
||||||
|
|
||||||
/* OPTION A: Use all RAM banks as one big block */
|
|
||||||
/* Reasonable, unless you are doing something */
|
|
||||||
/* really particular with DMA or other concurrent */
|
|
||||||
/* access that would benefit from striping */
|
|
||||||
RAM : ORIGIN = 0x20000000, LENGTH = 264K
|
|
||||||
|
|
||||||
/* OPTION B: Keep the unstriped sections separate */
|
|
||||||
/* RAM: ORIGIN = 0x20000000, LENGTH = 256K */
|
|
||||||
/* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K */
|
|
||||||
/* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K */
|
|
||||||
}
|
|
@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! {
|
|||||||
|
|
||||||
let mut vrefint = adc.enable_vref(&mut Delay);
|
let mut vrefint = adc.enable_vref(&mut Delay);
|
||||||
let mut temperature = adc.enable_temperature();
|
let mut temperature = adc.enable_temperature();
|
||||||
let mut buffer = opamp.buffer_ext(&mut p.PA7, &mut p.PA6, OpAmpGain::Mul1);
|
let mut buffer = opamp.buffer_ext(&p.PA7, &mut p.PA6, OpAmpGain::Mul1);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let vref = adc.read(&mut vrefint).await;
|
let vref = adc.read(&mut vrefint).await;
|
||||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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]
|
#[embassy_executor::main]
|
||||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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]
|
#[embassy_executor::main]
|
||||||
|
@ -19,7 +19,8 @@ const HEIGHT: usize = 100;
|
|||||||
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
|
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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>;
|
DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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]
|
#[embassy_executor::main]
|
||||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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]
|
#[embassy_executor::main]
|
||||||
|
@ -16,7 +16,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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]
|
#[embassy_executor::main]
|
||||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
|||||||
const WHOAMI: u8 = 0x0F;
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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]
|
#[embassy_executor::main]
|
||||||
|
@ -40,7 +40,8 @@ use static_cell::make_static;
|
|||||||
use {embassy_stm32 as hal, panic_probe as _};
|
use {embassy_stm32 as hal, panic_probe as _};
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
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>;
|
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user