diff options
| author | xoviat <[email protected]> | 2023-07-21 16:24:48 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-07-21 16:24:48 -0500 |
| commit | 2cdd593290ea318d0ca9d71a270ac3b63e30470e (patch) | |
| tree | 9dd8df6d49c26195c281e609a728d20a2ed716ec | |
| parent | c675208b8a90bee39e99c8cd3bb620b99c439482 (diff) | |
| parent | 4d1d125f4157084668a949f9bc24e4417628f9fe (diff) | |
Merge branch 'main' of https://github.com/embassy-rs/embassy into mac
| -rw-r--r-- | .gitattributes | 41 | ||||
| -rwxr-xr-x | .github/ci/crlf.sh | 17 | ||||
| -rwxr-xr-x | .github/ci/doc.sh | 1 | ||||
| -rw-r--r-- | embassy-net-esp-hosted/Cargo.toml | 6 | ||||
| -rw-r--r-- | embassy-net/src/lib.rs | 104 | ||||
| -rw-r--r-- | embassy-nrf/src/pdm.rs | 794 | ||||
| -rw-r--r-- | embassy-rp/src/adc.rs | 10 | ||||
| -rw-r--r-- | embassy-rp/src/i2c.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/generic_smi.rs | 12 | ||||
| -rw-r--r-- | embassy-stm32/src/qspi/mod.rs | 664 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 27 | ||||
| -rw-r--r-- | examples/nrf52840/Cargo.toml | 3 | ||||
| -rw-r--r-- | examples/nrf52840/src/bin/pdm.rs | 46 | ||||
| -rw-r--r-- | examples/nrf52840/src/bin/pdm_continuous.rs | 81 |
14 files changed, 1141 insertions, 670 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..4db9edae7 --- /dev/null +++ b/.gitattributes | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | * text=auto | ||
| 2 | |||
| 3 | *.adoc text | ||
| 4 | *.html text | ||
| 5 | *.in text | ||
| 6 | *.json text | ||
| 7 | *.md text | ||
| 8 | *.proto text | ||
| 9 | *.py text | ||
| 10 | *.rs text | ||
| 11 | *.service text | ||
| 12 | *.sh text | ||
| 13 | *.toml text | ||
| 14 | *.txt text | ||
| 15 | *.x text | ||
| 16 | *.yml text | ||
| 17 | |||
| 18 | *.raw binary | ||
| 19 | *.bin binary | ||
| 20 | *.png binary | ||
| 21 | *.jpg binary | ||
| 22 | *.jpeg binary | ||
| 23 | *.gif binary | ||
| 24 | *.ico binary | ||
| 25 | *.mov binary | ||
| 26 | *.mp4 binary | ||
| 27 | *.mp3 binary | ||
| 28 | *.flv binary | ||
| 29 | *.fla binary | ||
| 30 | *.swf binary | ||
| 31 | *.gz binary | ||
| 32 | *.zip binary | ||
| 33 | *.7z binary | ||
| 34 | *.ttf binary | ||
| 35 | *.eot binary | ||
| 36 | *.woff binary | ||
| 37 | *.pyc binary | ||
| 38 | *.pdf binary | ||
| 39 | *.ez binary | ||
| 40 | *.bz2 binary | ||
| 41 | *.swp binary \ No newline at end of file | ||
diff --git a/.github/ci/crlf.sh b/.github/ci/crlf.sh new file mode 100755 index 000000000..457510407 --- /dev/null +++ b/.github/ci/crlf.sh | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | ## on push branch~=gh-readonly-queue/main/.* | ||
| 3 | ## on pull_request | ||
| 4 | |||
| 5 | set -euo pipefail | ||
| 6 | |||
| 7 | FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true)) | ||
| 8 | |||
| 9 | if [ -z "$FILES_WITH_CRLF" ]; then | ||
| 10 | echo -e "No files with CRLF endings found." | ||
| 11 | exit 0 | ||
| 12 | else | ||
| 13 | NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l) | ||
| 14 | echo -e "ERROR: Found ${NR_FILES} files with CRLF endings." | ||
| 15 | echo "$FILES_WITH_CRLF" | ||
| 16 | exit "$NR_FILES" | ||
| 17 | fi \ No newline at end of file | ||
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 9e9c78a42..06c6fa00b 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh | |||
| @@ -37,6 +37,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g | |||
| 37 | docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup | 37 | docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup |
| 38 | docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup | 38 | docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup |
| 39 | docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup | 39 | docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup |
| 40 | docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup | ||
| 40 | docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static | 41 | docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static |
| 41 | 42 | ||
| 42 | export KUBECONFIG=/ci/secrets/kubeconfig.yml | 43 | export KUBECONFIG=/ci/secrets/kubeconfig.yml |
diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 26f5b40bd..0053c49a4 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml | |||
| @@ -18,3 +18,9 @@ embedded-hal-async = { version = "=0.2.0-alpha.2" } | |||
| 18 | noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } | 18 | noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } |
| 19 | #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } | 19 | #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } |
| 20 | heapless = "0.7.16" | 20 | heapless = "0.7.16" |
| 21 | |||
| 22 | [package.metadata.embassy_docs] | ||
| 23 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" | ||
| 24 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/" | ||
| 25 | target = "thumbv7em-none-eabi" | ||
| 26 | features = ["defmt"] \ No newline at end of file | ||
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ad98d7f68..81c751a2c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -490,30 +490,78 @@ impl<D: Driver + 'static> Stack<D> { | |||
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | #[cfg(feature = "igmp")] | 492 | #[cfg(feature = "igmp")] |
| 493 | impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> { | 493 | impl<D: Driver + 'static> Stack<D> { |
| 494 | /// Join a multicast group. | ||
| 495 | pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | ||
| 496 | where | ||
| 497 | T: Into<IpAddress>, | ||
| 498 | { | ||
| 499 | let addr = addr.into(); | ||
| 500 | |||
| 501 | poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await | ||
| 502 | } | ||
| 503 | |||
| 494 | /// Join a multicast group. | 504 | /// Join a multicast group. |
| 495 | pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | 505 | /// |
| 506 | /// When the send queue is full, this method will return `Poll::Pending` | ||
| 507 | /// and register the current task to be notified when the queue has space available. | ||
| 508 | pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>> | ||
| 496 | where | 509 | where |
| 497 | T: Into<IpAddress>, | 510 | T: Into<IpAddress>, |
| 498 | { | 511 | { |
| 499 | let addr = addr.into(); | 512 | let addr = addr.into(); |
| 500 | 513 | ||
| 501 | self.with_mut(|s, i| { | 514 | self.with_mut(|s, i| { |
| 502 | s.iface | 515 | let mut smoldev = DriverAdapter { |
| 503 | .join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) | 516 | cx: Some(cx), |
| 517 | inner: &mut i.device, | ||
| 518 | }; | ||
| 519 | |||
| 520 | match s | ||
| 521 | .iface | ||
| 522 | .join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) | ||
| 523 | { | ||
| 524 | Ok(announce_sent) => Poll::Ready(Ok(announce_sent)), | ||
| 525 | Err(MulticastError::Exhausted) => Poll::Pending, | ||
| 526 | Err(other) => Poll::Ready(Err(other)), | ||
| 527 | } | ||
| 504 | }) | 528 | }) |
| 505 | } | 529 | } |
| 506 | 530 | ||
| 507 | /// Leave a multicast group. | 531 | /// Leave a multicast group. |
| 508 | pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | 532 | pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> |
| 533 | where | ||
| 534 | T: Into<IpAddress>, | ||
| 535 | { | ||
| 536 | let addr = addr.into(); | ||
| 537 | |||
| 538 | poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Leave a multicast group. | ||
| 542 | /// | ||
| 543 | /// When the send queue is full, this method will return `Poll::Pending` | ||
| 544 | /// and register the current task to be notified when the queue has space available. | ||
| 545 | pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>> | ||
| 509 | where | 546 | where |
| 510 | T: Into<IpAddress>, | 547 | T: Into<IpAddress>, |
| 511 | { | 548 | { |
| 512 | let addr = addr.into(); | 549 | let addr = addr.into(); |
| 513 | 550 | ||
| 514 | self.with_mut(|s, i| { | 551 | self.with_mut(|s, i| { |
| 515 | s.iface | 552 | let mut smoldev = DriverAdapter { |
| 516 | .leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) | 553 | cx: Some(cx), |
| 554 | inner: &mut i.device, | ||
| 555 | }; | ||
| 556 | |||
| 557 | match s | ||
| 558 | .iface | ||
| 559 | .leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) | ||
| 560 | { | ||
| 561 | Ok(leave_sent) => Poll::Ready(Ok(leave_sent)), | ||
| 562 | Err(MulticastError::Exhausted) => Poll::Pending, | ||
| 563 | Err(other) => Poll::Ready(Err(other)), | ||
| 564 | } | ||
| 517 | }) | 565 | }) |
| 518 | } | 566 | } |
| 519 | 567 | ||
| @@ -542,11 +590,14 @@ impl<D: Driver + 'static> Inner<D> { | |||
| 542 | 590 | ||
| 543 | debug!(" IP address: {}", config.address); | 591 | debug!(" IP address: {}", config.address); |
| 544 | s.iface.update_ip_addrs(|addrs| { | 592 | s.iface.update_ip_addrs(|addrs| { |
| 545 | if addrs.is_empty() { | 593 | if let Some((index, _)) = addrs |
| 546 | addrs.push(IpCidr::Ipv4(config.address)).unwrap(); | 594 | .iter() |
| 547 | } else { | 595 | .enumerate() |
| 548 | addrs[0] = IpCidr::Ipv4(config.address); | 596 | .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) |
| 597 | { | ||
| 598 | addrs.remove(index); | ||
| 549 | } | 599 | } |
| 600 | addrs.push(IpCidr::Ipv4(config.address)).unwrap(); | ||
| 550 | }); | 601 | }); |
| 551 | 602 | ||
| 552 | #[cfg(feature = "medium-ethernet")] | 603 | #[cfg(feature = "medium-ethernet")] |
| @@ -581,11 +632,14 @@ impl<D: Driver + 'static> Inner<D> { | |||
| 581 | 632 | ||
| 582 | debug!(" IP address: {}", config.address); | 633 | debug!(" IP address: {}", config.address); |
| 583 | s.iface.update_ip_addrs(|addrs| { | 634 | s.iface.update_ip_addrs(|addrs| { |
| 584 | if addrs.is_empty() { | 635 | if let Some((index, _)) = addrs |
| 585 | addrs.push(IpCidr::Ipv6(config.address)).unwrap(); | 636 | .iter() |
| 586 | } else { | 637 | .enumerate() |
| 587 | addrs[0] = IpCidr::Ipv6(config.address); | 638 | .find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_))) |
| 639 | { | ||
| 640 | addrs.remove(index); | ||
| 588 | } | 641 | } |
| 642 | addrs.push(IpCidr::Ipv6(config.address)).unwrap(); | ||
| 589 | }); | 643 | }); |
| 590 | 644 | ||
| 591 | #[cfg(feature = "medium-ethernet")] | 645 | #[cfg(feature = "medium-ethernet")] |
| @@ -653,13 +707,21 @@ impl<D: Driver + 'static> Inner<D> { | |||
| 653 | socket.set_retry_config(config.retry_config); | 707 | socket.set_retry_config(config.retry_config); |
| 654 | } | 708 | } |
| 655 | 709 | ||
| 656 | #[allow(unused)] // used only with dhcp | 710 | #[cfg(feature = "dhcpv4")] |
| 657 | fn unapply_config(&mut self, s: &mut SocketStack) { | 711 | fn unapply_config_v4(&mut self, s: &mut SocketStack) { |
| 658 | #[cfg(feature = "medium-ethernet")] | 712 | #[cfg(feature = "medium-ethernet")] |
| 659 | let medium = self.device.capabilities().medium; | 713 | let medium = self.device.capabilities().medium; |
| 660 | |||
| 661 | debug!("Lost IP configuration"); | 714 | debug!("Lost IP configuration"); |
| 662 | s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); | 715 | s.iface.update_ip_addrs(|ip_addrs| { |
| 716 | #[cfg(feature = "proto-ipv4")] | ||
| 717 | if let Some((index, _)) = ip_addrs | ||
| 718 | .iter() | ||
| 719 | .enumerate() | ||
| 720 | .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) | ||
| 721 | { | ||
| 722 | ip_addrs.remove(index); | ||
| 723 | } | ||
| 724 | }); | ||
| 663 | #[cfg(feature = "medium-ethernet")] | 725 | #[cfg(feature = "medium-ethernet")] |
| 664 | if medium == Medium::Ethernet { | 726 | if medium == Medium::Ethernet { |
| 665 | #[cfg(feature = "proto-ipv4")] | 727 | #[cfg(feature = "proto-ipv4")] |
| @@ -706,7 +768,7 @@ impl<D: Driver + 'static> Inner<D> { | |||
| 706 | if self.link_up { | 768 | if self.link_up { |
| 707 | match socket.poll() { | 769 | match socket.poll() { |
| 708 | None => {} | 770 | None => {} |
| 709 | Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), | 771 | Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), |
| 710 | Some(dhcpv4::Event::Configured(config)) => { | 772 | Some(dhcpv4::Event::Configured(config)) => { |
| 711 | let config = StaticConfigV4 { | 773 | let config = StaticConfigV4 { |
| 712 | address: config.address, | 774 | address: config.address, |
| @@ -718,7 +780,7 @@ impl<D: Driver + 'static> Inner<D> { | |||
| 718 | } | 780 | } |
| 719 | } else if old_link_up { | 781 | } else if old_link_up { |
| 720 | socket.reset(); | 782 | socket.reset(); |
| 721 | self.unapply_config(s); | 783 | self.unapply_config_v4(s); |
| 722 | } | 784 | } |
| 723 | } | 785 | } |
| 724 | //if old_link_up || self.link_up { | 786 | //if old_link_up || self.link_up { |
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 0e30f7002..217884d1c 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs | |||
| @@ -1,294 +1,500 @@ | |||
| 1 | //! Pulse Density Modulation (PDM) mirophone driver. | 1 | //! Pulse Density Modulation (PDM) mirophone driver. |
| 2 | 2 | ||
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | 4 | ||
| 5 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 6 | use core::sync::atomic::{compiler_fence, Ordering}; | 6 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 7 | use core::task::Poll; | 7 | use core::task::Poll; |
| 8 | 8 | ||
| 9 | use embassy_hal_common::drop::OnDrop; | 9 | use embassy_hal_common::drop::OnDrop; |
| 10 | use embassy_hal_common::{into_ref, PeripheralRef}; | 10 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 11 | use futures::future::poll_fn; | 11 | use fixed::types::I7F1; |
| 12 | 12 | use futures::future::poll_fn; | |
| 13 | use crate::chip::EASY_DMA_SIZE; | 13 | |
| 14 | use crate::gpio::sealed::Pin; | 14 | use crate::chip::EASY_DMA_SIZE; |
| 15 | use crate::gpio::{AnyPin, Pin as GpioPin}; | 15 | use crate::gpio::sealed::Pin; |
| 16 | use crate::interrupt::typelevel::Interrupt; | 16 | use crate::gpio::{AnyPin, Pin as GpioPin}; |
| 17 | use crate::{interrupt, Peripheral}; | 17 | use crate::interrupt::typelevel::Interrupt; |
| 18 | 18 | use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; | |
| 19 | /// Interrupt handler. | 19 | pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; |
| 20 | pub struct InterruptHandler<T: Instance> { | 20 | #[cfg(any( |
| 21 | _phantom: PhantomData<T>, | 21 | feature = "nrf52840", |
| 22 | } | 22 | feature = "nrf52833", |
| 23 | 23 | feature = "_nrf5340-app", | |
| 24 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 24 | feature = "_nrf9160", |
| 25 | unsafe fn on_interrupt() { | 25 | ))] |
| 26 | T::regs().intenclr.write(|w| w.end().clear()); | 26 | pub use crate::pac::pdm::ratio::RATIO_A as Ratio; |
| 27 | T::state().waker.wake(); | 27 | use crate::{interrupt, Peripheral}; |
| 28 | } | 28 | |
| 29 | } | 29 | /// Interrupt handler. |
| 30 | 30 | pub struct InterruptHandler<T: Instance> { | |
| 31 | /// PDM microphone interface | 31 | _phantom: PhantomData<T>, |
| 32 | pub struct Pdm<'d, T: Instance> { | 32 | } |
| 33 | _peri: PeripheralRef<'d, T>, | 33 | |
| 34 | } | 34 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 35 | 35 | unsafe fn on_interrupt() { | |
| 36 | /// PDM error. | 36 | let r = T::regs(); |
| 37 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 37 | |
| 38 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 38 | if r.events_end.read().bits() != 0 { |
| 39 | #[non_exhaustive] | 39 | r.intenclr.write(|w| w.end().clear()); |
| 40 | pub enum Error { | 40 | } |
| 41 | /// Buffer is too long. | 41 | |
| 42 | BufferTooLong, | 42 | if r.events_started.read().bits() != 0 { |
| 43 | /// Buffer is empty | 43 | r.intenclr.write(|w| w.started().clear()); |
| 44 | BufferZeroLength, | 44 | } |
| 45 | /// PDM is not running | 45 | |
| 46 | NotRunning, | 46 | if r.events_stopped.read().bits() != 0 { |
| 47 | } | 47 | r.intenclr.write(|w| w.stopped().clear()); |
| 48 | 48 | } | |
| 49 | static DUMMY_BUFFER: [i16; 1] = [0; 1]; | 49 | |
| 50 | 50 | T::state().waker.wake(); | |
| 51 | impl<'d, T: Instance> Pdm<'d, T> { | 51 | } |
| 52 | /// Create PDM driver | 52 | } |
| 53 | pub fn new( | 53 | |
| 54 | pdm: impl Peripheral<P = T> + 'd, | 54 | /// PDM microphone interface |
| 55 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 55 | pub struct Pdm<'d, T: Instance> { |
| 56 | clk: impl Peripheral<P = impl GpioPin> + 'd, | 56 | _peri: PeripheralRef<'d, T>, |
| 57 | din: impl Peripheral<P = impl GpioPin> + 'd, | 57 | } |
| 58 | config: Config, | 58 | |
| 59 | ) -> Self { | 59 | /// PDM error. |
| 60 | into_ref!(pdm, clk, din); | 60 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 61 | Self::new_inner(pdm, clk.map_into(), din.map_into(), config) | 61 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 62 | } | 62 | #[non_exhaustive] |
| 63 | 63 | pub enum Error { | |
| 64 | fn new_inner( | 64 | /// Buffer is too long. |
| 65 | pdm: PeripheralRef<'d, T>, | 65 | BufferTooLong, |
| 66 | clk: PeripheralRef<'d, AnyPin>, | 66 | /// Buffer is empty |
| 67 | din: PeripheralRef<'d, AnyPin>, | 67 | BufferZeroLength, |
| 68 | config: Config, | 68 | /// PDM is not running |
| 69 | ) -> Self { | 69 | NotRunning, |
| 70 | into_ref!(pdm); | 70 | /// PDM is already running |
| 71 | 71 | AlreadyRunning, | |
| 72 | let r = T::regs(); | 72 | } |
| 73 | 73 | ||
| 74 | // setup gpio pins | 74 | static DUMMY_BUFFER: [i16; 1] = [0; 1]; |
| 75 | din.conf().write(|w| w.input().set_bit()); | 75 | |
| 76 | r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); | 76 | /// The state of a continuously running sampler. While it reflects |
| 77 | clk.set_low(); | 77 | /// the progress of a sampler, it also signals what should be done |
| 78 | clk.conf().write(|w| w.dir().output()); | 78 | /// next. For example, if the sampler has stopped then the Pdm implementation |
| 79 | r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); | 79 | /// can then tear down its infrastructure. |
| 80 | 80 | #[derive(PartialEq)] | |
| 81 | // configure | 81 | pub enum SamplerState { |
| 82 | // use default for | 82 | /// The sampler processed the samples and is ready for more. |
| 83 | // - gain right | 83 | Sampled, |
| 84 | // - gain left | 84 | /// The sampler is done processing samples. |
| 85 | // - clk | 85 | Stopped, |
| 86 | // - ratio | 86 | } |
| 87 | r.mode.write(|w| { | 87 | |
| 88 | w.edge().bit(config.edge == Edge::LeftRising); | 88 | impl<'d, T: Instance> Pdm<'d, T> { |
| 89 | w.operation().bit(config.operation_mode == OperationMode::Mono); | 89 | /// Create PDM driver |
| 90 | w | 90 | pub fn new( |
| 91 | }); | 91 | pdm: impl Peripheral<P = T> + 'd, |
| 92 | r.gainl.write(|w| w.gainl().default_gain()); | 92 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 93 | r.gainr.write(|w| w.gainr().default_gain()); | 93 | clk: impl Peripheral<P = impl GpioPin> + 'd, |
| 94 | 94 | din: impl Peripheral<P = impl GpioPin> + 'd, | |
| 95 | // IRQ | 95 | config: Config, |
| 96 | T::Interrupt::unpend(); | 96 | ) -> Self { |
| 97 | unsafe { T::Interrupt::enable() }; | 97 | into_ref!(pdm, clk, din); |
| 98 | 98 | Self::new_inner(pdm, clk.map_into(), din.map_into(), config) | |
| 99 | r.enable.write(|w| w.enable().set_bit()); | 99 | } |
| 100 | 100 | ||
| 101 | Self { _peri: pdm } | 101 | fn new_inner( |
| 102 | } | 102 | pdm: PeripheralRef<'d, T>, |
| 103 | 103 | clk: PeripheralRef<'d, AnyPin>, | |
| 104 | /// Start sampling microphon data into a dummy buffer | 104 | din: PeripheralRef<'d, AnyPin>, |
| 105 | /// Usefull to start the microphon and keep it active between recording samples | 105 | config: Config, |
| 106 | pub async fn start(&mut self) { | 106 | ) -> Self { |
| 107 | let r = T::regs(); | 107 | into_ref!(pdm); |
| 108 | 108 | ||
| 109 | // start dummy sampling because microphon needs some setup time | 109 | let r = T::regs(); |
| 110 | r.sample | 110 | |
| 111 | .ptr | 111 | // setup gpio pins |
| 112 | .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); | 112 | din.conf().write(|w| w.input().set_bit()); |
| 113 | r.sample | 113 | r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); |
| 114 | .maxcnt | 114 | clk.set_low(); |
| 115 | .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); | 115 | clk.conf().write(|w| w.dir().output()); |
| 116 | 116 | r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); | |
| 117 | r.tasks_start.write(|w| unsafe { w.bits(1) }); | 117 | |
| 118 | } | 118 | // configure |
| 119 | 119 | r.pdmclkctrl.write(|w| w.freq().variant(config.frequency)); | |
| 120 | /// Stop sampling microphon data inta a dummy buffer | 120 | #[cfg(any( |
| 121 | pub async fn stop(&mut self) { | 121 | feature = "nrf52840", |
| 122 | let r = T::regs(); | 122 | feature = "nrf52833", |
| 123 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); | 123 | feature = "_nrf5340-app", |
| 124 | r.events_started.reset(); | 124 | feature = "_nrf9160", |
| 125 | } | 125 | ))] |
| 126 | 126 | r.ratio.write(|w| w.ratio().variant(config.ratio)); | |
| 127 | /// Sample data into the given buffer. | 127 | r.mode.write(|w| { |
| 128 | pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { | 128 | w.operation().variant(config.operation_mode.into()); |
| 129 | if buffer.len() == 0 { | 129 | w.edge().variant(config.edge.into()); |
| 130 | return Err(Error::BufferZeroLength); | 130 | w |
| 131 | } | 131 | }); |
| 132 | if buffer.len() > EASY_DMA_SIZE { | 132 | |
| 133 | return Err(Error::BufferTooLong); | 133 | Self::_set_gain(r, config.gain_left, config.gain_right); |
| 134 | } | 134 | |
| 135 | 135 | // Disable all events interrupts | |
| 136 | let r = T::regs(); | 136 | r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); |
| 137 | 137 | ||
| 138 | if r.events_started.read().bits() == 0 { | 138 | // IRQ |
| 139 | return Err(Error::NotRunning); | 139 | T::Interrupt::unpend(); |
| 140 | } | 140 | unsafe { T::Interrupt::enable() }; |
| 141 | 141 | ||
| 142 | let drop = OnDrop::new(move || { | 142 | r.enable.write(|w| w.enable().set_bit()); |
| 143 | r.intenclr.write(|w| w.end().clear()); | 143 | |
| 144 | r.events_stopped.reset(); | 144 | Self { _peri: pdm } |
| 145 | 145 | } | |
| 146 | // reset to dummy buffer | 146 | |
| 147 | r.sample | 147 | fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { |
| 148 | .ptr | 148 | let gain_left = gain_left |
| 149 | .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); | 149 | .saturating_add(I7F1::from_bits(40)) |
| 150 | r.sample | 150 | .saturating_to_num::<u8>() |
| 151 | .maxcnt | 151 | .clamp(0, 0x50); |
| 152 | .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); | 152 | let gain_right = gain_right |
| 153 | 153 | .saturating_add(I7F1::from_bits(40)) | |
| 154 | while r.events_stopped.read().bits() == 0 {} | 154 | .saturating_to_num::<u8>() |
| 155 | }); | 155 | .clamp(0, 0x50); |
| 156 | 156 | ||
| 157 | // setup user buffer | 157 | r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); |
| 158 | let ptr = buffer.as_ptr(); | 158 | r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); |
| 159 | let len = buffer.len(); | 159 | } |
| 160 | r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); | 160 | |
| 161 | r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); | 161 | /// Adjust the gain of the PDM microphone on the fly |
| 162 | 162 | pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { | |
| 163 | // wait till the current sample is finished and the user buffer sample is started | 163 | Self::_set_gain(T::regs(), gain_left, gain_right) |
| 164 | Self::wait_for_sample().await; | 164 | } |
| 165 | 165 | ||
| 166 | // reset the buffer back to the dummy buffer | 166 | /// Start sampling microphon data into a dummy buffer |
| 167 | r.sample | 167 | /// Usefull to start the microphon and keep it active between recording samples |
| 168 | .ptr | 168 | pub async fn start(&mut self) { |
| 169 | .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); | 169 | let r = T::regs(); |
| 170 | r.sample | 170 | |
| 171 | .maxcnt | 171 | // start dummy sampling because microphon needs some setup time |
| 172 | .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); | 172 | r.sample |
| 173 | 173 | .ptr | |
| 174 | // wait till the user buffer is sampled | 174 | .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); |
| 175 | Self::wait_for_sample().await; | 175 | r.sample |
| 176 | 176 | .maxcnt | |
| 177 | drop.defuse(); | 177 | .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); |
| 178 | 178 | ||
| 179 | Ok(()) | 179 | r.tasks_start.write(|w| unsafe { w.bits(1) }); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | async fn wait_for_sample() { | 182 | /// Stop sampling microphon data inta a dummy buffer |
| 183 | let r = T::regs(); | 183 | pub async fn stop(&mut self) { |
| 184 | 184 | let r = T::regs(); | |
| 185 | r.events_end.reset(); | 185 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); |
| 186 | r.intenset.write(|w| w.end().set()); | 186 | r.events_started.reset(); |
| 187 | 187 | } | |
| 188 | compiler_fence(Ordering::SeqCst); | 188 | |
| 189 | 189 | /// Sample data into the given buffer. | |
| 190 | poll_fn(|cx| { | 190 | pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { |
| 191 | T::state().waker.register(cx.waker()); | 191 | if buffer.len() == 0 { |
| 192 | if r.events_end.read().bits() != 0 { | 192 | return Err(Error::BufferZeroLength); |
| 193 | return Poll::Ready(()); | 193 | } |
| 194 | } | 194 | if buffer.len() > EASY_DMA_SIZE { |
| 195 | Poll::Pending | 195 | return Err(Error::BufferTooLong); |
| 196 | }) | 196 | } |
| 197 | .await; | 197 | |
| 198 | 198 | let r = T::regs(); | |
| 199 | compiler_fence(Ordering::SeqCst); | 199 | |
| 200 | } | 200 | if r.events_started.read().bits() == 0 { |
| 201 | } | 201 | return Err(Error::NotRunning); |
| 202 | 202 | } | |
| 203 | /// PDM microphone driver Config | 203 | |
| 204 | pub struct Config { | 204 | let drop = OnDrop::new(move || { |
| 205 | /// Use stero or mono operation | 205 | r.intenclr.write(|w| w.end().clear()); |
| 206 | pub operation_mode: OperationMode, | 206 | r.events_stopped.reset(); |
| 207 | /// On which edge the left channel should be samples | 207 | |
| 208 | pub edge: Edge, | 208 | // reset to dummy buffer |
| 209 | } | 209 | r.sample |
| 210 | 210 | .ptr | |
| 211 | impl Default for Config { | 211 | .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); |
| 212 | fn default() -> Self { | 212 | r.sample |
| 213 | Self { | 213 | .maxcnt |
| 214 | operation_mode: OperationMode::Mono, | 214 | .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); |
| 215 | edge: Edge::LeftFalling, | 215 | |
| 216 | } | 216 | while r.events_stopped.read().bits() == 0 {} |
| 217 | } | 217 | }); |
| 218 | } | 218 | |
| 219 | 219 | // setup user buffer | |
| 220 | /// PDM operation mode. | 220 | let ptr = buffer.as_ptr(); |
| 221 | #[derive(PartialEq)] | 221 | let len = buffer.len(); |
| 222 | pub enum OperationMode { | 222 | r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); |
| 223 | /// Mono (1 channel) | 223 | r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); |
| 224 | Mono, | 224 | |
| 225 | /// Stereo (2 channels) | 225 | // wait till the current sample is finished and the user buffer sample is started |
| 226 | Stereo, | 226 | Self::wait_for_sample().await; |
| 227 | } | 227 | |
| 228 | 228 | // reset the buffer back to the dummy buffer | |
| 229 | /// PDM edge polarity | 229 | r.sample |
| 230 | #[derive(PartialEq)] | 230 | .ptr |
| 231 | pub enum Edge { | 231 | .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); |
| 232 | /// Left edge is rising | 232 | r.sample |
| 233 | LeftRising, | 233 | .maxcnt |
| 234 | /// Left edge is falling | 234 | .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); |
| 235 | LeftFalling, | 235 | |
| 236 | } | 236 | // wait till the user buffer is sampled |
| 237 | 237 | Self::wait_for_sample().await; | |
| 238 | impl<'d, T: Instance> Drop for Pdm<'d, T> { | 238 | |
| 239 | fn drop(&mut self) { | 239 | drop.defuse(); |
| 240 | let r = T::regs(); | 240 | |
| 241 | 241 | Ok(()) | |
| 242 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); | 242 | } |
| 243 | 243 | ||
| 244 | r.enable.write(|w| w.enable().disabled()); | 244 | async fn wait_for_sample() { |
| 245 | 245 | let r = T::regs(); | |
| 246 | r.psel.din.reset(); | 246 | |
| 247 | r.psel.clk.reset(); | 247 | r.events_end.reset(); |
| 248 | } | 248 | r.intenset.write(|w| w.end().set()); |
| 249 | } | 249 | |
| 250 | 250 | compiler_fence(Ordering::SeqCst); | |
| 251 | pub(crate) mod sealed { | 251 | |
| 252 | use embassy_sync::waitqueue::AtomicWaker; | 252 | poll_fn(|cx| { |
| 253 | 253 | T::state().waker.register(cx.waker()); | |
| 254 | /// Peripheral static state | 254 | if r.events_end.read().bits() != 0 { |
| 255 | pub struct State { | 255 | return Poll::Ready(()); |
| 256 | pub waker: AtomicWaker, | 256 | } |
| 257 | } | 257 | Poll::Pending |
| 258 | 258 | }) | |
| 259 | impl State { | 259 | .await; |
| 260 | pub const fn new() -> Self { | 260 | |
| 261 | Self { | 261 | compiler_fence(Ordering::SeqCst); |
| 262 | waker: AtomicWaker::new(), | 262 | } |
| 263 | } | 263 | |
| 264 | } | 264 | /// Continuous sampling with double buffers. |
| 265 | } | 265 | /// |
| 266 | 266 | /// A sampler closure is provided that receives the buffer of samples, noting | |
| 267 | pub trait Instance { | 267 | /// that the size of this buffer can be less than the original buffer's size. |
| 268 | fn regs() -> &'static crate::pac::pdm::RegisterBlock; | 268 | /// A command is return from the closure that indicates whether the sampling |
| 269 | fn state() -> &'static State; | 269 | /// should continue or stop. |
| 270 | } | 270 | /// |
| 271 | } | 271 | /// NOTE: The time spent within the callback supplied should not exceed the time |
| 272 | 272 | /// taken to acquire the samples into a single buffer. You should measure the | |
| 273 | /// PDM peripheral instance. | 273 | /// time taken by the callback and set the sample buffer size accordingly. |
| 274 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | 274 | /// Exceeding this time can lead to samples becoming dropped. |
| 275 | /// Interrupt for this peripheral. | 275 | pub async fn run_task_sampler<S, const N: usize>( |
| 276 | type Interrupt: interrupt::typelevel::Interrupt; | 276 | &mut self, |
| 277 | } | 277 | bufs: &mut [[i16; N]; 2], |
| 278 | 278 | mut sampler: S, | |
| 279 | macro_rules! impl_pdm { | 279 | ) -> Result<(), Error> |
| 280 | ($type:ident, $pac_type:ident, $irq:ident) => { | 280 | where |
| 281 | impl crate::pdm::sealed::Instance for peripherals::$type { | 281 | S: FnMut(&[i16; N]) -> SamplerState, |
| 282 | fn regs() -> &'static crate::pac::pdm::RegisterBlock { | 282 | { |
| 283 | unsafe { &*pac::$pac_type::ptr() } | 283 | let r = T::regs(); |
| 284 | } | 284 | |
| 285 | fn state() -> &'static crate::pdm::sealed::State { | 285 | if r.events_started.read().bits() != 0 { |
| 286 | static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); | 286 | return Err(Error::AlreadyRunning); |
| 287 | &STATE | 287 | } |
| 288 | } | 288 | |
| 289 | } | 289 | r.sample |
| 290 | impl crate::pdm::Instance for peripherals::$type { | 290 | .ptr |
| 291 | type Interrupt = crate::interrupt::typelevel::$irq; | 291 | .write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); |
| 292 | } | 292 | r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); |
| 293 | }; | 293 | |
| 294 | } | 294 | // Reset and enable the events |
| 295 | r.events_end.reset(); | ||
| 296 | r.events_started.reset(); | ||
| 297 | r.events_stopped.reset(); | ||
| 298 | r.intenset.write(|w| { | ||
| 299 | w.end().set(); | ||
| 300 | w.started().set(); | ||
| 301 | w.stopped().set(); | ||
| 302 | w | ||
| 303 | }); | ||
| 304 | |||
| 305 | // Don't reorder the start event before the previous writes. Hopefully self | ||
| 306 | // wouldn't happen anyway. | ||
| 307 | compiler_fence(Ordering::SeqCst); | ||
| 308 | |||
| 309 | r.tasks_start.write(|w| unsafe { w.bits(1) }); | ||
| 310 | |||
| 311 | let mut current_buffer = 0; | ||
| 312 | |||
| 313 | let mut done = false; | ||
| 314 | |||
| 315 | let drop = OnDrop::new(|| { | ||
| 316 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); | ||
| 317 | // N.B. It would be better if this were async, but Drop only support sync code. | ||
| 318 | while r.events_stopped.read().bits() != 0 {} | ||
| 319 | }); | ||
| 320 | |||
| 321 | // Wait for events and complete when the sampler indicates it has had enough. | ||
| 322 | poll_fn(|cx| { | ||
| 323 | let r = T::regs(); | ||
| 324 | |||
| 325 | T::state().waker.register(cx.waker()); | ||
| 326 | |||
| 327 | if r.events_end.read().bits() != 0 { | ||
| 328 | compiler_fence(Ordering::SeqCst); | ||
| 329 | |||
| 330 | r.events_end.reset(); | ||
| 331 | r.intenset.write(|w| w.end().set()); | ||
| 332 | |||
| 333 | if !done { | ||
| 334 | // Discard the last buffer after the user requested a stop. | ||
| 335 | if sampler(&bufs[current_buffer]) == SamplerState::Sampled { | ||
| 336 | let next_buffer = 1 - current_buffer; | ||
| 337 | current_buffer = next_buffer; | ||
| 338 | } else { | ||
| 339 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); | ||
| 340 | done = true; | ||
| 341 | }; | ||
| 342 | }; | ||
| 343 | } | ||
| 344 | |||
| 345 | if r.events_started.read().bits() != 0 { | ||
| 346 | r.events_started.reset(); | ||
| 347 | r.intenset.write(|w| w.started().set()); | ||
| 348 | |||
| 349 | let next_buffer = 1 - current_buffer; | ||
| 350 | r.sample | ||
| 351 | .ptr | ||
| 352 | .write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) }); | ||
| 353 | } | ||
| 354 | |||
| 355 | if r.events_stopped.read().bits() != 0 { | ||
| 356 | return Poll::Ready(()); | ||
| 357 | } | ||
| 358 | |||
| 359 | Poll::Pending | ||
| 360 | }) | ||
| 361 | .await; | ||
| 362 | drop.defuse(); | ||
| 363 | Ok(()) | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | /// PDM microphone driver Config | ||
| 368 | pub struct Config { | ||
| 369 | /// Use stero or mono operation | ||
| 370 | pub operation_mode: OperationMode, | ||
| 371 | /// On which edge the left channel should be samples | ||
| 372 | pub edge: Edge, | ||
| 373 | /// Clock frequency | ||
| 374 | pub frequency: Frequency, | ||
| 375 | /// Clock ratio | ||
| 376 | #[cfg(any( | ||
| 377 | feature = "nrf52840", | ||
| 378 | feature = "nrf52833", | ||
| 379 | feature = "_nrf5340-app", | ||
| 380 | feature = "_nrf9160", | ||
| 381 | ))] | ||
| 382 | pub ratio: Ratio, | ||
| 383 | /// Gain left in dB | ||
| 384 | pub gain_left: I7F1, | ||
| 385 | /// Gain right in dB | ||
| 386 | pub gain_right: I7F1, | ||
| 387 | } | ||
| 388 | |||
| 389 | impl Default for Config { | ||
| 390 | fn default() -> Self { | ||
| 391 | Self { | ||
| 392 | operation_mode: OperationMode::Mono, | ||
| 393 | edge: Edge::LeftFalling, | ||
| 394 | frequency: Frequency::DEFAULT, | ||
| 395 | #[cfg(any( | ||
| 396 | feature = "nrf52840", | ||
| 397 | feature = "nrf52833", | ||
| 398 | feature = "_nrf5340-app", | ||
| 399 | feature = "_nrf9160", | ||
| 400 | ))] | ||
| 401 | ratio: Ratio::RATIO80, | ||
| 402 | gain_left: I7F1::ZERO, | ||
| 403 | gain_right: I7F1::ZERO, | ||
| 404 | } | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | /// PDM operation mode. | ||
| 409 | #[derive(PartialEq)] | ||
| 410 | pub enum OperationMode { | ||
| 411 | /// Mono (1 channel) | ||
| 412 | Mono, | ||
| 413 | /// Stereo (2 channels) | ||
| 414 | Stereo, | ||
| 415 | } | ||
| 416 | |||
| 417 | impl From<OperationMode> for OPERATION_A { | ||
| 418 | fn from(mode: OperationMode) -> Self { | ||
| 419 | match mode { | ||
| 420 | OperationMode::Mono => OPERATION_A::MONO, | ||
| 421 | OperationMode::Stereo => OPERATION_A::STEREO, | ||
| 422 | } | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | /// PDM edge polarity | ||
| 427 | #[derive(PartialEq)] | ||
| 428 | pub enum Edge { | ||
| 429 | /// Left edge is rising | ||
| 430 | LeftRising, | ||
| 431 | /// Left edge is falling | ||
| 432 | LeftFalling, | ||
| 433 | } | ||
| 434 | |||
| 435 | impl From<Edge> for EDGE_A { | ||
| 436 | fn from(edge: Edge) -> Self { | ||
| 437 | match edge { | ||
| 438 | Edge::LeftRising => EDGE_A::LEFT_RISING, | ||
| 439 | Edge::LeftFalling => EDGE_A::LEFT_FALLING, | ||
| 440 | } | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | impl<'d, T: Instance> Drop for Pdm<'d, T> { | ||
| 445 | fn drop(&mut self) { | ||
| 446 | let r = T::regs(); | ||
| 447 | |||
| 448 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); | ||
| 449 | |||
| 450 | r.enable.write(|w| w.enable().disabled()); | ||
| 451 | |||
| 452 | r.psel.din.reset(); | ||
| 453 | r.psel.clk.reset(); | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | pub(crate) mod sealed { | ||
| 458 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 459 | |||
| 460 | /// Peripheral static state | ||
| 461 | pub struct State { | ||
| 462 | pub waker: AtomicWaker, | ||
| 463 | } | ||
| 464 | |||
| 465 | impl State { | ||
| 466 | pub const fn new() -> Self { | ||
| 467 | Self { | ||
| 468 | waker: AtomicWaker::new(), | ||
| 469 | } | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | pub trait Instance { | ||
| 474 | fn regs() -> &'static crate::pac::pdm::RegisterBlock; | ||
| 475 | fn state() -> &'static State; | ||
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | /// PDM peripheral instance. | ||
| 480 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | ||
| 481 | /// Interrupt for this peripheral. | ||
| 482 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 483 | } | ||
| 484 | |||
| 485 | macro_rules! impl_pdm { | ||
| 486 | ($type:ident, $pac_type:ident, $irq:ident) => { | ||
| 487 | impl crate::pdm::sealed::Instance for peripherals::$type { | ||
| 488 | fn regs() -> &'static crate::pac::pdm::RegisterBlock { | ||
| 489 | unsafe { &*pac::$pac_type::ptr() } | ||
| 490 | } | ||
| 491 | fn state() -> &'static crate::pdm::sealed::State { | ||
| 492 | static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); | ||
| 493 | &STATE | ||
| 494 | } | ||
| 495 | } | ||
| 496 | impl crate::pdm::Instance for peripherals::$type { | ||
| 497 | type Interrupt = crate::interrupt::typelevel::$irq; | ||
| 498 | } | ||
| 499 | }; | ||
| 500 | } | ||
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index dfa1b877a..95780c068 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs | |||
| @@ -81,6 +81,16 @@ pub struct Adc<'d, M: Mode> { | |||
| 81 | phantom: PhantomData<(&'d ADC, M)>, | 81 | phantom: PhantomData<(&'d ADC, M)>, |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | impl<'d, M: Mode> Drop for Adc<'d, M> { | ||
| 85 | fn drop(&mut self) { | ||
| 86 | let r = Self::regs(); | ||
| 87 | // disable ADC. leaving it enabled comes with a ~150µA static | ||
| 88 | // current draw. the temperature sensor has already been disabled | ||
| 89 | // by the temperature-reading methods, so we don't need to touch that. | ||
| 90 | r.cs().write(|w| w.set_en(false)); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 84 | impl<'d, M: Mode> Adc<'d, M> { | 94 | impl<'d, M: Mode> Adc<'d, M> { |
| 85 | #[inline] | 95 | #[inline] |
| 86 | fn regs() -> pac::adc::Adc { | 96 | fn regs() -> pac::adc::Adc { |
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 791c64554..9b85b2345 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs | |||
| @@ -716,6 +716,9 @@ mod nightly { | |||
| 716 | async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { | 716 | async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { |
| 717 | let addr: u16 = address.into(); | 717 | let addr: u16 = address.into(); |
| 718 | 718 | ||
| 719 | if operations.len() > 0 { | ||
| 720 | Self::setup(addr)?; | ||
| 721 | } | ||
| 719 | let mut iterator = operations.iter_mut(); | 722 | let mut iterator = operations.iter_mut(); |
| 720 | 723 | ||
| 721 | while let Some(op) = iterator.next() { | 724 | while let Some(op) = iterator.next() { |
| @@ -723,11 +726,9 @@ mod nightly { | |||
| 723 | 726 | ||
| 724 | match op { | 727 | match op { |
| 725 | Operation::Read(buffer) => { | 728 | Operation::Read(buffer) => { |
| 726 | Self::setup(addr)?; | ||
| 727 | self.read_async_internal(buffer, false, last).await?; | 729 | self.read_async_internal(buffer, false, last).await?; |
| 728 | } | 730 | } |
| 729 | Operation::Write(buffer) => { | 731 | Operation::Write(buffer) => { |
| 730 | Self::setup(addr)?; | ||
| 731 | self.write_async_internal(buffer.into_iter().cloned(), last).await?; | 732 | self.write_async_internal(buffer.into_iter().cloned(), last).await?; |
| 732 | } | 733 | } |
| 733 | } | 734 | } |
diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 90631b175..2ed46ca2c 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs | |||
| @@ -45,20 +45,19 @@ use self::phy_consts::*; | |||
| 45 | pub struct GenericSMI { | 45 | pub struct GenericSMI { |
| 46 | #[cfg(feature = "time")] | 46 | #[cfg(feature = "time")] |
| 47 | poll_interval: Duration, | 47 | poll_interval: Duration, |
| 48 | #[cfg(not(feature = "time"))] | ||
| 49 | _private: (), | ||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | impl GenericSMI { | 52 | impl GenericSMI { |
| 51 | #[cfg(feature = "time")] | ||
| 52 | pub fn new() -> Self { | 53 | pub fn new() -> Self { |
| 53 | Self { | 54 | Self { |
| 55 | #[cfg(feature = "time")] | ||
| 54 | poll_interval: Duration::from_millis(500), | 56 | poll_interval: Duration::from_millis(500), |
| 57 | #[cfg(not(feature = "time"))] | ||
| 58 | _private: (), | ||
| 55 | } | 59 | } |
| 56 | } | 60 | } |
| 57 | |||
| 58 | #[cfg(not(feature = "time"))] | ||
| 59 | pub fn new() -> Self { | ||
| 60 | Self {} | ||
| 61 | } | ||
| 62 | } | 61 | } |
| 63 | 62 | ||
| 64 | unsafe impl PHY for GenericSMI { | 63 | unsafe impl PHY for GenericSMI { |
| @@ -102,6 +101,7 @@ unsafe impl PHY for GenericSMI { | |||
| 102 | 101 | ||
| 103 | /// Public functions for the PHY | 102 | /// Public functions for the PHY |
| 104 | impl GenericSMI { | 103 | impl GenericSMI { |
| 104 | #[cfg(feature = "time")] | ||
| 105 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { | 105 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { |
| 106 | self.poll_interval = poll_interval | 106 | self.poll_interval = poll_interval |
| 107 | } | 107 | } |
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index e9db934bf..31b676088 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -1,332 +1,332 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | pub mod enums; | 3 | pub mod enums; |
| 4 | 4 | ||
| 5 | use embassy_hal_common::{into_ref, PeripheralRef}; | 5 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 6 | use enums::*; | 6 | use enums::*; |
| 7 | 7 | ||
| 8 | use crate::dma::Transfer; | 8 | use crate::dma::Transfer; |
| 9 | use crate::gpio::sealed::AFType; | 9 | use crate::gpio::sealed::AFType; |
| 10 | use crate::gpio::AnyPin; | 10 | use crate::gpio::AnyPin; |
| 11 | use crate::pac::quadspi::Quadspi as Regs; | 11 | use crate::pac::quadspi::Quadspi as Regs; |
| 12 | use crate::rcc::RccPeripheral; | 12 | use crate::rcc::RccPeripheral; |
| 13 | use crate::{peripherals, Peripheral}; | 13 | use crate::{peripherals, Peripheral}; |
| 14 | 14 | ||
| 15 | pub struct TransferConfig { | 15 | pub struct TransferConfig { |
| 16 | /// Instraction width (IMODE) | 16 | /// Instraction width (IMODE) |
| 17 | pub iwidth: QspiWidth, | 17 | pub iwidth: QspiWidth, |
| 18 | /// Address width (ADMODE) | 18 | /// Address width (ADMODE) |
| 19 | pub awidth: QspiWidth, | 19 | pub awidth: QspiWidth, |
| 20 | /// Data width (DMODE) | 20 | /// Data width (DMODE) |
| 21 | pub dwidth: QspiWidth, | 21 | pub dwidth: QspiWidth, |
| 22 | /// Instruction Id | 22 | /// Instruction Id |
| 23 | pub instruction: u8, | 23 | pub instruction: u8, |
| 24 | /// Flash memory address | 24 | /// Flash memory address |
| 25 | pub address: Option<u32>, | 25 | pub address: Option<u32>, |
| 26 | /// Number of dummy cycles (DCYC) | 26 | /// Number of dummy cycles (DCYC) |
| 27 | pub dummy: DummyCycles, | 27 | pub dummy: DummyCycles, |
| 28 | /// Length of data | 28 | /// Length of data |
| 29 | pub data_len: Option<usize>, | 29 | pub data_len: Option<usize>, |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | impl Default for TransferConfig { | 32 | impl Default for TransferConfig { |
| 33 | fn default() -> Self { | 33 | fn default() -> Self { |
| 34 | Self { | 34 | Self { |
| 35 | iwidth: QspiWidth::NONE, | 35 | iwidth: QspiWidth::NONE, |
| 36 | awidth: QspiWidth::NONE, | 36 | awidth: QspiWidth::NONE, |
| 37 | dwidth: QspiWidth::NONE, | 37 | dwidth: QspiWidth::NONE, |
| 38 | instruction: 0, | 38 | instruction: 0, |
| 39 | address: None, | 39 | address: None, |
| 40 | dummy: DummyCycles::_0, | 40 | dummy: DummyCycles::_0, |
| 41 | data_len: None, | 41 | data_len: None, |
| 42 | } | 42 | } |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | pub struct Config { | 46 | pub struct Config { |
| 47 | /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. | 47 | /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. |
| 48 | /// If you need other value the whose predefined use `Other` variant. | 48 | /// If you need other value the whose predefined use `Other` variant. |
| 49 | pub memory_size: MemorySize, | 49 | pub memory_size: MemorySize, |
| 50 | /// Address size (8/16/24/32-bit) | 50 | /// Address size (8/16/24/32-bit) |
| 51 | pub address_size: AddressSize, | 51 | pub address_size: AddressSize, |
| 52 | /// Scalar factor for generating CLK [0-255] | 52 | /// Scalar factor for generating CLK [0-255] |
| 53 | pub prescaler: u8, | 53 | pub prescaler: u8, |
| 54 | /// Number of bytes to trigger FIFO threshold flag. | 54 | /// Number of bytes to trigger FIFO threshold flag. |
| 55 | pub fifo_threshold: FIFOThresholdLevel, | 55 | pub fifo_threshold: FIFOThresholdLevel, |
| 56 | /// Minimum number of cycles that chip select must be high between issued commands | 56 | /// Minimum number of cycles that chip select must be high between issued commands |
| 57 | pub cs_high_time: ChipSelectHightTime, | 57 | pub cs_high_time: ChipSelectHightTime, |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | impl Default for Config { | 60 | impl Default for Config { |
| 61 | fn default() -> Self { | 61 | fn default() -> Self { |
| 62 | Self { | 62 | Self { |
| 63 | memory_size: MemorySize::Other(0), | 63 | memory_size: MemorySize::Other(0), |
| 64 | address_size: AddressSize::_24bit, | 64 | address_size: AddressSize::_24bit, |
| 65 | prescaler: 128, | 65 | prescaler: 128, |
| 66 | fifo_threshold: FIFOThresholdLevel::_17Bytes, | 66 | fifo_threshold: FIFOThresholdLevel::_17Bytes, |
| 67 | cs_high_time: ChipSelectHightTime::_5Cycle, | 67 | cs_high_time: ChipSelectHightTime::_5Cycle, |
| 68 | } | 68 | } |
| 69 | } | 69 | } |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | #[allow(dead_code)] | 72 | #[allow(dead_code)] |
| 73 | pub struct Qspi<'d, T: Instance, Dma> { | 73 | pub struct Qspi<'d, T: Instance, Dma> { |
| 74 | _peri: PeripheralRef<'d, T>, | 74 | _peri: PeripheralRef<'d, T>, |
| 75 | sck: Option<PeripheralRef<'d, AnyPin>>, | 75 | sck: Option<PeripheralRef<'d, AnyPin>>, |
| 76 | d0: Option<PeripheralRef<'d, AnyPin>>, | 76 | d0: Option<PeripheralRef<'d, AnyPin>>, |
| 77 | d1: Option<PeripheralRef<'d, AnyPin>>, | 77 | d1: Option<PeripheralRef<'d, AnyPin>>, |
| 78 | d2: Option<PeripheralRef<'d, AnyPin>>, | 78 | d2: Option<PeripheralRef<'d, AnyPin>>, |
| 79 | d3: Option<PeripheralRef<'d, AnyPin>>, | 79 | d3: Option<PeripheralRef<'d, AnyPin>>, |
| 80 | nss: Option<PeripheralRef<'d, AnyPin>>, | 80 | nss: Option<PeripheralRef<'d, AnyPin>>, |
| 81 | dma: PeripheralRef<'d, Dma>, | 81 | dma: PeripheralRef<'d, Dma>, |
| 82 | config: Config, | 82 | config: Config, |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { | 85 | impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { |
| 86 | pub fn new( | 86 | pub fn new( |
| 87 | peri: impl Peripheral<P = T> + 'd, | 87 | peri: impl Peripheral<P = T> + 'd, |
| 88 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 88 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
| 89 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 89 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
| 90 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 90 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
| 91 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 91 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
| 92 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 92 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
| 93 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 93 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, |
| 94 | dma: impl Peripheral<P = Dma> + 'd, | 94 | dma: impl Peripheral<P = Dma> + 'd, |
| 95 | config: Config, | 95 | config: Config, |
| 96 | ) -> Self { | 96 | ) -> Self { |
| 97 | into_ref!(peri, d0, d1, d2, d3, sck, nss); | 97 | into_ref!(peri, d0, d1, d2, d3, sck, nss); |
| 98 | 98 | ||
| 99 | sck.set_as_af(sck.af_num(), AFType::OutputPushPull); | 99 | sck.set_as_af(sck.af_num(), AFType::OutputPushPull); |
| 100 | sck.set_speed(crate::gpio::Speed::VeryHigh); | 100 | sck.set_speed(crate::gpio::Speed::VeryHigh); |
| 101 | nss.set_as_af(nss.af_num(), AFType::OutputPushPull); | 101 | nss.set_as_af(nss.af_num(), AFType::OutputPushPull); |
| 102 | nss.set_speed(crate::gpio::Speed::VeryHigh); | 102 | nss.set_speed(crate::gpio::Speed::VeryHigh); |
| 103 | d0.set_as_af(d0.af_num(), AFType::OutputPushPull); | 103 | d0.set_as_af(d0.af_num(), AFType::OutputPushPull); |
| 104 | d0.set_speed(crate::gpio::Speed::VeryHigh); | 104 | d0.set_speed(crate::gpio::Speed::VeryHigh); |
| 105 | d1.set_as_af(d1.af_num(), AFType::OutputPushPull); | 105 | d1.set_as_af(d1.af_num(), AFType::OutputPushPull); |
| 106 | d1.set_speed(crate::gpio::Speed::VeryHigh); | 106 | d1.set_speed(crate::gpio::Speed::VeryHigh); |
| 107 | d2.set_as_af(d2.af_num(), AFType::OutputPushPull); | 107 | d2.set_as_af(d2.af_num(), AFType::OutputPushPull); |
| 108 | d2.set_speed(crate::gpio::Speed::VeryHigh); | 108 | d2.set_speed(crate::gpio::Speed::VeryHigh); |
| 109 | d3.set_as_af(d3.af_num(), AFType::OutputPushPull); | 109 | d3.set_as_af(d3.af_num(), AFType::OutputPushPull); |
| 110 | d3.set_speed(crate::gpio::Speed::VeryHigh); | 110 | d3.set_speed(crate::gpio::Speed::VeryHigh); |
| 111 | 111 | ||
| 112 | Self::new_inner( | 112 | Self::new_inner( |
| 113 | peri, | 113 | peri, |
| 114 | Some(d0.map_into()), | 114 | Some(d0.map_into()), |
| 115 | Some(d1.map_into()), | 115 | Some(d1.map_into()), |
| 116 | Some(d2.map_into()), | 116 | Some(d2.map_into()), |
| 117 | Some(d3.map_into()), | 117 | Some(d3.map_into()), |
| 118 | Some(sck.map_into()), | 118 | Some(sck.map_into()), |
| 119 | Some(nss.map_into()), | 119 | Some(nss.map_into()), |
| 120 | dma, | 120 | dma, |
| 121 | config, | 121 | config, |
| 122 | ) | 122 | ) |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | fn new_inner( | 125 | fn new_inner( |
| 126 | peri: impl Peripheral<P = T> + 'd, | 126 | peri: impl Peripheral<P = T> + 'd, |
| 127 | d0: Option<PeripheralRef<'d, AnyPin>>, | 127 | d0: Option<PeripheralRef<'d, AnyPin>>, |
| 128 | d1: Option<PeripheralRef<'d, AnyPin>>, | 128 | d1: Option<PeripheralRef<'d, AnyPin>>, |
| 129 | d2: Option<PeripheralRef<'d, AnyPin>>, | 129 | d2: Option<PeripheralRef<'d, AnyPin>>, |
| 130 | d3: Option<PeripheralRef<'d, AnyPin>>, | 130 | d3: Option<PeripheralRef<'d, AnyPin>>, |
| 131 | sck: Option<PeripheralRef<'d, AnyPin>>, | 131 | sck: Option<PeripheralRef<'d, AnyPin>>, |
| 132 | nss: Option<PeripheralRef<'d, AnyPin>>, | 132 | nss: Option<PeripheralRef<'d, AnyPin>>, |
| 133 | dma: impl Peripheral<P = Dma> + 'd, | 133 | dma: impl Peripheral<P = Dma> + 'd, |
| 134 | config: Config, | 134 | config: Config, |
| 135 | ) -> Self { | 135 | ) -> Self { |
| 136 | into_ref!(peri, dma); | 136 | into_ref!(peri, dma); |
| 137 | 137 | ||
| 138 | T::enable(); | 138 | T::enable(); |
| 139 | T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); | 139 | T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); |
| 140 | 140 | ||
| 141 | while T::REGS.sr().read().busy() {} | 141 | while T::REGS.sr().read().busy() {} |
| 142 | 142 | ||
| 143 | T::REGS.cr().write(|w| { | 143 | T::REGS.cr().write(|w| { |
| 144 | w.set_prescaler(config.prescaler); | 144 | w.set_prescaler(config.prescaler); |
| 145 | w.set_en(true); | 145 | w.set_en(true); |
| 146 | }); | 146 | }); |
| 147 | T::REGS.dcr().write(|w| { | 147 | T::REGS.dcr().write(|w| { |
| 148 | w.set_fsize(config.memory_size.into()); | 148 | w.set_fsize(config.memory_size.into()); |
| 149 | w.set_csht(config.cs_high_time.into()); | 149 | w.set_csht(config.cs_high_time.into()); |
| 150 | w.set_ckmode(false); | 150 | w.set_ckmode(false); |
| 151 | }); | 151 | }); |
| 152 | 152 | ||
| 153 | Self { | 153 | Self { |
| 154 | _peri: peri, | 154 | _peri: peri, |
| 155 | sck, | 155 | sck, |
| 156 | d0, | 156 | d0, |
| 157 | d1, | 157 | d1, |
| 158 | d2, | 158 | d2, |
| 159 | d3, | 159 | d3, |
| 160 | nss, | 160 | nss, |
| 161 | dma, | 161 | dma, |
| 162 | config, | 162 | config, |
| 163 | } | 163 | } |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | pub fn command(&mut self, transaction: TransferConfig) { | 166 | pub fn command(&mut self, transaction: TransferConfig) { |
| 167 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | 167 | T::REGS.cr().modify(|v| v.set_dmaen(false)); |
| 168 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | 168 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); |
| 169 | 169 | ||
| 170 | while !T::REGS.sr().read().tcf() {} | 170 | while !T::REGS.sr().read().tcf() {} |
| 171 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | 171 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { | 174 | pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { |
| 175 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | 175 | T::REGS.cr().modify(|v| v.set_dmaen(false)); |
| 176 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | 176 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); |
| 177 | 177 | ||
| 178 | if let Some(len) = transaction.data_len { | 178 | if let Some(len) = transaction.data_len { |
| 179 | let current_ar = T::REGS.ar().read().address(); | 179 | let current_ar = T::REGS.ar().read().address(); |
| 180 | T::REGS.ccr().modify(|v| { | 180 | T::REGS.ccr().modify(|v| { |
| 181 | v.set_fmode(QspiMode::IndirectRead.into()); | 181 | v.set_fmode(QspiMode::IndirectRead.into()); |
| 182 | }); | 182 | }); |
| 183 | T::REGS.ar().write(|v| { | 183 | T::REGS.ar().write(|v| { |
| 184 | v.set_address(current_ar); | 184 | v.set_address(current_ar); |
| 185 | }); | 185 | }); |
| 186 | 186 | ||
| 187 | for idx in 0..len { | 187 | for idx in 0..len { |
| 188 | while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} | 188 | while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} |
| 189 | buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; | 189 | buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; |
| 190 | } | 190 | } |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | while !T::REGS.sr().read().tcf() {} | 193 | while !T::REGS.sr().read().tcf() {} |
| 194 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | 194 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { | 197 | pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { |
| 198 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | 198 | T::REGS.cr().modify(|v| v.set_dmaen(false)); |
| 199 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | 199 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); |
| 200 | 200 | ||
| 201 | if let Some(len) = transaction.data_len { | 201 | if let Some(len) = transaction.data_len { |
| 202 | T::REGS.ccr().modify(|v| { | 202 | T::REGS.ccr().modify(|v| { |
| 203 | v.set_fmode(QspiMode::IndirectWrite.into()); | 203 | v.set_fmode(QspiMode::IndirectWrite.into()); |
| 204 | }); | 204 | }); |
| 205 | 205 | ||
| 206 | for idx in 0..len { | 206 | for idx in 0..len { |
| 207 | while !T::REGS.sr().read().ftf() {} | 207 | while !T::REGS.sr().read().ftf() {} |
| 208 | unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; | 208 | unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; |
| 209 | } | 209 | } |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | while !T::REGS.sr().read().tcf() {} | 212 | while !T::REGS.sr().read().tcf() {} |
| 213 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | 213 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) | 216 | pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) |
| 217 | where | 217 | where |
| 218 | Dma: QuadDma<T>, | 218 | Dma: QuadDma<T>, |
| 219 | { | 219 | { |
| 220 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | 220 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); |
| 221 | 221 | ||
| 222 | T::REGS.ccr().modify(|v| { | 222 | T::REGS.ccr().modify(|v| { |
| 223 | v.set_fmode(QspiMode::IndirectRead.into()); | 223 | v.set_fmode(QspiMode::IndirectRead.into()); |
| 224 | }); | 224 | }); |
| 225 | let current_ar = T::REGS.ar().read().address(); | 225 | let current_ar = T::REGS.ar().read().address(); |
| 226 | T::REGS.ar().write(|v| { | 226 | T::REGS.ar().write(|v| { |
| 227 | v.set_address(current_ar); | 227 | v.set_address(current_ar); |
| 228 | }); | 228 | }); |
| 229 | 229 | ||
| 230 | let request = self.dma.request(); | 230 | let request = self.dma.request(); |
| 231 | let transfer = unsafe { | 231 | let transfer = unsafe { |
| 232 | Transfer::new_read( | 232 | Transfer::new_read( |
| 233 | &mut self.dma, | 233 | &mut self.dma, |
| 234 | request, | 234 | request, |
| 235 | T::REGS.dr().as_ptr() as *mut u8, | 235 | T::REGS.dr().as_ptr() as *mut u8, |
| 236 | buf, | 236 | buf, |
| 237 | Default::default(), | 237 | Default::default(), |
| 238 | ) | 238 | ) |
| 239 | }; | 239 | }; |
| 240 | 240 | ||
| 241 | T::REGS.cr().modify(|v| v.set_dmaen(true)); | 241 | T::REGS.cr().modify(|v| v.set_dmaen(true)); |
| 242 | 242 | ||
| 243 | transfer.blocking_wait(); | 243 | transfer.blocking_wait(); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) | 246 | pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) |
| 247 | where | 247 | where |
| 248 | Dma: QuadDma<T>, | 248 | Dma: QuadDma<T>, |
| 249 | { | 249 | { |
| 250 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | 250 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); |
| 251 | 251 | ||
| 252 | T::REGS.ccr().modify(|v| { | 252 | T::REGS.ccr().modify(|v| { |
| 253 | v.set_fmode(QspiMode::IndirectWrite.into()); | 253 | v.set_fmode(QspiMode::IndirectWrite.into()); |
| 254 | }); | 254 | }); |
| 255 | 255 | ||
| 256 | let request = self.dma.request(); | 256 | let request = self.dma.request(); |
| 257 | let transfer = unsafe { | 257 | let transfer = unsafe { |
| 258 | Transfer::new_write( | 258 | Transfer::new_write( |
| 259 | &mut self.dma, | 259 | &mut self.dma, |
| 260 | request, | 260 | request, |
| 261 | buf, | 261 | buf, |
| 262 | T::REGS.dr().as_ptr() as *mut u8, | 262 | T::REGS.dr().as_ptr() as *mut u8, |
| 263 | Default::default(), | 263 | Default::default(), |
| 264 | ) | 264 | ) |
| 265 | }; | 265 | }; |
| 266 | 266 | ||
| 267 | T::REGS.cr().modify(|v| v.set_dmaen(true)); | 267 | T::REGS.cr().modify(|v| v.set_dmaen(true)); |
| 268 | 268 | ||
| 269 | transfer.blocking_wait(); | 269 | transfer.blocking_wait(); |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { | 272 | fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { |
| 273 | T::REGS.fcr().modify(|v| { | 273 | T::REGS.fcr().modify(|v| { |
| 274 | v.set_csmf(true); | 274 | v.set_csmf(true); |
| 275 | v.set_ctcf(true); | 275 | v.set_ctcf(true); |
| 276 | v.set_ctef(true); | 276 | v.set_ctef(true); |
| 277 | v.set_ctof(true); | 277 | v.set_ctof(true); |
| 278 | }); | 278 | }); |
| 279 | 279 | ||
| 280 | while T::REGS.sr().read().busy() {} | 280 | while T::REGS.sr().read().busy() {} |
| 281 | 281 | ||
| 282 | if let Some(len) = transaction.data_len { | 282 | if let Some(len) = transaction.data_len { |
| 283 | T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); | 283 | T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); |
| 284 | } | 284 | } |
| 285 | 285 | ||
| 286 | T::REGS.ccr().write(|v| { | 286 | T::REGS.ccr().write(|v| { |
| 287 | v.set_fmode(fmode.into()); | 287 | v.set_fmode(fmode.into()); |
| 288 | v.set_imode(transaction.iwidth.into()); | 288 | v.set_imode(transaction.iwidth.into()); |
| 289 | v.set_instruction(transaction.instruction); | 289 | v.set_instruction(transaction.instruction); |
| 290 | v.set_admode(transaction.awidth.into()); | 290 | v.set_admode(transaction.awidth.into()); |
| 291 | v.set_adsize(self.config.address_size.into()); | 291 | v.set_adsize(self.config.address_size.into()); |
| 292 | v.set_dmode(transaction.dwidth.into()); | 292 | v.set_dmode(transaction.dwidth.into()); |
| 293 | v.set_abmode(QspiWidth::NONE.into()); | 293 | v.set_abmode(QspiWidth::NONE.into()); |
| 294 | v.set_dcyc(transaction.dummy.into()); | 294 | v.set_dcyc(transaction.dummy.into()); |
| 295 | }); | 295 | }); |
| 296 | 296 | ||
| 297 | if let Some(addr) = transaction.address { | 297 | if let Some(addr) = transaction.address { |
| 298 | T::REGS.ar().write(|v| { | 298 | T::REGS.ar().write(|v| { |
| 299 | v.set_address(addr); | 299 | v.set_address(addr); |
| 300 | }); | 300 | }); |
| 301 | } | 301 | } |
| 302 | } | 302 | } |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | pub(crate) mod sealed { | 305 | pub(crate) mod sealed { |
| 306 | use super::*; | 306 | use super::*; |
| 307 | 307 | ||
| 308 | pub trait Instance { | 308 | pub trait Instance { |
| 309 | const REGS: Regs; | 309 | const REGS: Regs; |
| 310 | } | 310 | } |
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | 313 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} |
| 314 | 314 | ||
| 315 | pin_trait!(SckPin, Instance); | 315 | pin_trait!(SckPin, Instance); |
| 316 | pin_trait!(D0Pin, Instance); | 316 | pin_trait!(D0Pin, Instance); |
| 317 | pin_trait!(D1Pin, Instance); | 317 | pin_trait!(D1Pin, Instance); |
| 318 | pin_trait!(D2Pin, Instance); | 318 | pin_trait!(D2Pin, Instance); |
| 319 | pin_trait!(D3Pin, Instance); | 319 | pin_trait!(D3Pin, Instance); |
| 320 | pin_trait!(NSSPin, Instance); | 320 | pin_trait!(NSSPin, Instance); |
| 321 | 321 | ||
| 322 | dma_trait!(QuadDma, Instance); | 322 | dma_trait!(QuadDma, Instance); |
| 323 | 323 | ||
| 324 | foreach_peripheral!( | 324 | foreach_peripheral!( |
| 325 | (quadspi, $inst:ident) => { | 325 | (quadspi, $inst:ident) => { |
| 326 | impl sealed::Instance for peripherals::$inst { | 326 | impl sealed::Instance for peripherals::$inst { |
| 327 | const REGS: Regs = crate::pac::$inst; | 327 | const REGS: Regs = crate::pac::$inst; |
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | impl Instance for peripherals::$inst {} | 330 | impl Instance for peripherals::$inst {} |
| 331 | }; | 331 | }; |
| 332 | ); | 332 | ); |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c97efbf0a..ea8e525ea 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -116,6 +116,10 @@ pub struct Config { | |||
| 116 | /// but will effectively disable noise detection. | 116 | /// but will effectively disable noise detection. |
| 117 | #[cfg(not(usart_v1))] | 117 | #[cfg(not(usart_v1))] |
| 118 | pub assume_noise_free: bool, | 118 | pub assume_noise_free: bool, |
| 119 | |||
| 120 | /// Set this to true to swap the RX and TX pins. | ||
| 121 | #[cfg(any(usart_v3, usart_v4))] | ||
| 122 | pub swap_rx_tx: bool, | ||
| 119 | } | 123 | } |
| 120 | 124 | ||
| 121 | impl Default for Config { | 125 | impl Default for Config { |
| @@ -129,6 +133,8 @@ impl Default for Config { | |||
| 129 | detect_previous_overrun: false, | 133 | detect_previous_overrun: false, |
| 130 | #[cfg(not(usart_v1))] | 134 | #[cfg(not(usart_v1))] |
| 131 | assume_noise_free: false, | 135 | assume_noise_free: false, |
| 136 | #[cfg(any(usart_v3, usart_v4))] | ||
| 137 | swap_rx_tx: false, | ||
| 132 | } | 138 | } |
| 133 | } | 139 | } |
| 134 | } | 140 | } |
| @@ -688,8 +694,22 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { | |||
| 688 | 694 | ||
| 689 | let r = T::regs(); | 695 | let r = T::regs(); |
| 690 | 696 | ||
| 691 | rx.set_as_af(rx.af_num(), AFType::Input); | 697 | // Some chips do not have swap_rx_tx bit |
| 692 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | 698 | cfg_if::cfg_if! { |
| 699 | if #[cfg(any(usart_v3, usart_v4))] { | ||
| 700 | if config.swap_rx_tx { | ||
| 701 | let (rx, tx) = (tx, rx); | ||
| 702 | rx.set_as_af(rx.af_num(), AFType::Input); | ||
| 703 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | ||
| 704 | } else { | ||
| 705 | rx.set_as_af(rx.af_num(), AFType::Input); | ||
| 706 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | ||
| 707 | } | ||
| 708 | } else { | ||
| 709 | rx.set_as_af(rx.af_num(), AFType::Input); | ||
| 710 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | ||
| 711 | } | ||
| 712 | } | ||
| 693 | 713 | ||
| 694 | configure(r, &config, T::frequency(), T::KIND, true, true); | 714 | configure(r, &config, T::frequency(), T::KIND, true, true); |
| 695 | 715 | ||
| @@ -847,6 +867,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: | |||
| 847 | StopBits::STOP1P5 => vals::Stop::STOP1P5, | 867 | StopBits::STOP1P5 => vals::Stop::STOP1P5, |
| 848 | StopBits::STOP2 => vals::Stop::STOP2, | 868 | StopBits::STOP2 => vals::Stop::STOP2, |
| 849 | }); | 869 | }); |
| 870 | |||
| 871 | #[cfg(any(usart_v3, usart_v4))] | ||
| 872 | w.set_swap(config.swap_rx_tx); | ||
| 850 | }); | 873 | }); |
| 851 | r.cr1().write(|w| { | 874 | r.cr1().write(|w| { |
| 852 | // enable uart | 875 | // enable uart |
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 7b9c371bb..9b41ec5ab 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -43,6 +43,7 @@ embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-host | |||
| 43 | defmt = "0.3" | 43 | defmt = "0.3" |
| 44 | defmt-rtt = "0.4" | 44 | defmt-rtt = "0.4" |
| 45 | 45 | ||
| 46 | fixed = "1.10.0" | ||
| 46 | static_cell = "1.1" | 47 | static_cell = "1.1" |
| 47 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 48 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 48 | cortex-m-rt = "0.7.0" | 49 | cortex-m-rt = "0.7.0" |
| @@ -53,6 +54,8 @@ embedded-storage = "0.3.0" | |||
| 53 | usbd-hid = "0.6.0" | 54 | usbd-hid = "0.6.0" |
| 54 | serde = { version = "1.0.136", default-features = false } | 55 | serde = { version = "1.0.136", default-features = false } |
| 55 | embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } | 56 | embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } |
| 57 | num-integer = { version = "0.1.45", default-features = false } | ||
| 58 | microfft = "0.5.0" | ||
| 56 | 59 | ||
| 57 | [patch.crates-io] | 60 | [patch.crates-io] |
| 58 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } | 61 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } |
diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 6b41320ca..444b9137f 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs | |||
| @@ -7,6 +7,8 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_nrf::pdm::{self, Config, Pdm}; | 7 | use embassy_nrf::pdm::{self, Config, Pdm}; |
| 8 | use embassy_nrf::{bind_interrupts, peripherals}; | 8 | use embassy_nrf::{bind_interrupts, peripherals}; |
| 9 | use embassy_time::{Duration, Timer}; | 9 | use embassy_time::{Duration, Timer}; |
| 10 | use fixed::types::I7F1; | ||
| 11 | use num_integer::Roots; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 13 | ||
| 12 | bind_interrupts!(struct Irqs { | 14 | bind_interrupts!(struct Irqs { |
| @@ -20,18 +22,36 @@ async fn main(_p: Spawner) { | |||
| 20 | let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); | 22 | let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); |
| 21 | 23 | ||
| 22 | loop { | 24 | loop { |
| 23 | pdm.start().await; | 25 | for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] { |
| 24 | 26 | pdm.set_gain(gain, gain); | |
| 25 | // wait some time till the microphon settled | 27 | info!("Gain = {} dB", defmt::Debug2Format(&gain)); |
| 26 | Timer::after(Duration::from_millis(1000)).await; | 28 | pdm.start().await; |
| 27 | 29 | ||
| 28 | const SAMPLES: usize = 2048; | 30 | // wait some time till the microphon settled |
| 29 | let mut buf = [0i16; SAMPLES]; | 31 | Timer::after(Duration::from_millis(1000)).await; |
| 30 | pdm.sample(&mut buf).await.unwrap(); | 32 | |
| 31 | 33 | const SAMPLES: usize = 2048; | |
| 32 | info!("samples: {:?}", &buf); | 34 | let mut buf = [0i16; SAMPLES]; |
| 33 | 35 | pdm.sample(&mut buf).await.unwrap(); | |
| 34 | pdm.stop().await; | 36 | |
| 35 | Timer::after(Duration::from_millis(100)).await; | 37 | let mean = (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16; |
| 38 | info!( | ||
| 39 | "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", | ||
| 40 | buf.len(), | ||
| 41 | buf.iter().min().unwrap(), | ||
| 42 | buf.iter().max().unwrap(), | ||
| 43 | mean, | ||
| 44 | (buf.iter() | ||
| 45 | .map(|v| i32::from(*v - mean).pow(2)) | ||
| 46 | .fold(0i32, |a, b| a.saturating_add(b)) | ||
| 47 | / buf.len() as i32) | ||
| 48 | .sqrt() as i16, | ||
| 49 | ); | ||
| 50 | |||
| 51 | info!("samples: {:?}", &buf); | ||
| 52 | |||
| 53 | pdm.stop().await; | ||
| 54 | Timer::after(Duration::from_millis(100)).await; | ||
| 55 | } | ||
| 36 | } | 56 | } |
| 37 | } | 57 | } |
diff --git a/examples/nrf52840/src/bin/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs new file mode 100644 index 000000000..7d8531475 --- /dev/null +++ b/examples/nrf52840/src/bin/pdm_continuous.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::cmp::Ordering; | ||
| 6 | |||
| 7 | use defmt::info; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::pdm::{self, Config, Frequency, OperationMode, Pdm, Ratio, SamplerState}; | ||
| 10 | use embassy_nrf::{bind_interrupts, peripherals}; | ||
| 11 | use fixed::types::I7F1; | ||
| 12 | use microfft::real::rfft_1024; | ||
| 13 | use num_integer::Roots; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | PDM => pdm::InterruptHandler<peripherals::PDM>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_p: Spawner) { | ||
| 24 | let mut p = embassy_nrf::init(Default::default()); | ||
| 25 | let mut config = Config::default(); | ||
| 26 | // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. | ||
| 27 | config.frequency = Frequency::_1280K; // 16 kHz sample rate | ||
| 28 | config.ratio = Ratio::RATIO80; | ||
| 29 | config.operation_mode = OperationMode::Mono; | ||
| 30 | config.gain_left = I7F1::from_bits(5); // 2.5 dB | ||
| 31 | let mut pdm = Pdm::new(p.PDM, Irqs, &mut p.P0_00, &mut p.P0_01, config); | ||
| 32 | |||
| 33 | let mut bufs = [[0; 1024]; 2]; | ||
| 34 | |||
| 35 | pdm.run_task_sampler(&mut bufs, move |buf| { | ||
| 36 | // NOTE: It is important that the time spent within this callback | ||
| 37 | // does not exceed the time taken to acquire the 1500 samples we | ||
| 38 | // have in this example, which would be 10us + 2us per | ||
| 39 | // sample * 1500 = 18ms. You need to measure the time taken here | ||
| 40 | // and set the sample buffer size accordingly. Exceeding this | ||
| 41 | // time can lead to the peripheral re-writing the other buffer. | ||
| 42 | let mean = (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16; | ||
| 43 | let (peak_freq_index, peak_mag) = fft_peak_freq(&buf); | ||
| 44 | let peak_freq = peak_freq_index * 16000 / buf.len(); | ||
| 45 | info!( | ||
| 46 | "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz", | ||
| 47 | buf.len(), | ||
| 48 | buf.iter().min().unwrap(), | ||
| 49 | buf.iter().max().unwrap(), | ||
| 50 | mean, | ||
| 51 | (buf.iter() | ||
| 52 | .map(|v| i32::from(*v - mean).pow(2)) | ||
| 53 | .fold(0i32, |a, b| a.saturating_add(b)) | ||
| 54 | / buf.len() as i32) | ||
| 55 | .sqrt() as i16, | ||
| 56 | peak_mag, | ||
| 57 | peak_freq, | ||
| 58 | ); | ||
| 59 | SamplerState::Sampled | ||
| 60 | }) | ||
| 61 | .await | ||
| 62 | .unwrap(); | ||
| 63 | } | ||
| 64 | |||
| 65 | fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { | ||
| 66 | let mut f = [0f32; 1024]; | ||
| 67 | for i in 0..input.len() { | ||
| 68 | f[i] = (input[i] as f32) / 32768.0; | ||
| 69 | } | ||
| 70 | // N.B. rfft_1024 does the FFT in-place so result is actually also a reference to f. | ||
| 71 | let result = rfft_1024(&mut f); | ||
| 72 | result[0].im = 0.0; | ||
| 73 | |||
| 74 | result | ||
| 75 | .iter() | ||
| 76 | .map(|c| c.norm_sqr()) | ||
| 77 | .enumerate() | ||
| 78 | .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) | ||
| 79 | .map(|(i, v)| (i, ((v * 32768.0) as u32).sqrt())) | ||
| 80 | .unwrap() | ||
| 81 | } | ||
