aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-07-21 16:24:48 -0500
committerxoviat <[email protected]>2023-07-21 16:24:48 -0500
commit2cdd593290ea318d0ca9d71a270ac3b63e30470e (patch)
tree9dd8df6d49c26195c281e609a728d20a2ed716ec
parentc675208b8a90bee39e99c8cd3bb620b99c439482 (diff)
parent4d1d125f4157084668a949f9bc24e4417628f9fe (diff)
Merge branch 'main' of https://github.com/embassy-rs/embassy into mac
-rw-r--r--.gitattributes41
-rwxr-xr-x.github/ci/crlf.sh17
-rwxr-xr-x.github/ci/doc.sh1
-rw-r--r--embassy-net-esp-hosted/Cargo.toml6
-rw-r--r--embassy-net/src/lib.rs104
-rw-r--r--embassy-nrf/src/pdm.rs794
-rw-r--r--embassy-rp/src/adc.rs10
-rw-r--r--embassy-rp/src/i2c.rs5
-rw-r--r--embassy-stm32/src/eth/generic_smi.rs12
-rw-r--r--embassy-stm32/src/qspi/mod.rs664
-rw-r--r--embassy-stm32/src/usart/mod.rs27
-rw-r--r--examples/nrf52840/Cargo.toml3
-rw-r--r--examples/nrf52840/src/bin/pdm.rs46
-rw-r--r--examples/nrf52840/src/bin/pdm_continuous.rs81
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
5set -euo pipefail
6
7FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
8
9if [ -z "$FILES_WITH_CRLF" ]; then
10 echo -e "No files with CRLF endings found."
11 exit 0
12else
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"
17fi \ 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
37docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup 37docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
38docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup 38docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
39docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup 39docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
40docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
40docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static 41docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
41 42
42export KUBECONFIG=/ci/secrets/kubeconfig.yml 43export 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" }
18noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } 18noproto = { 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"] }
20heapless = "0.7.16" 20heapless = "0.7.16"
21
22[package.metadata.embassy_docs]
23src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
24src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
25target = "thumbv7em-none-eabi"
26features = ["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")]
493impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> { 493impl<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
5use core::marker::PhantomData; 5use core::marker::PhantomData;
6use core::sync::atomic::{compiler_fence, Ordering}; 6use core::sync::atomic::{compiler_fence, Ordering};
7use core::task::Poll; 7use core::task::Poll;
8 8
9use embassy_hal_common::drop::OnDrop; 9use embassy_hal_common::drop::OnDrop;
10use embassy_hal_common::{into_ref, PeripheralRef}; 10use embassy_hal_common::{into_ref, PeripheralRef};
11use futures::future::poll_fn; 11use fixed::types::I7F1;
12 12use futures::future::poll_fn;
13use crate::chip::EASY_DMA_SIZE; 13
14use crate::gpio::sealed::Pin; 14use crate::chip::EASY_DMA_SIZE;
15use crate::gpio::{AnyPin, Pin as GpioPin}; 15use crate::gpio::sealed::Pin;
16use crate::interrupt::typelevel::Interrupt; 16use crate::gpio::{AnyPin, Pin as GpioPin};
17use crate::{interrupt, Peripheral}; 17use crate::interrupt::typelevel::Interrupt;
18 18use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
19/// Interrupt handler. 19pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
20pub struct InterruptHandler<T: Instance> { 20#[cfg(any(
21 _phantom: PhantomData<T>, 21 feature = "nrf52840",
22} 22 feature = "nrf52833",
23 23 feature = "_nrf5340-app",
24impl<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()); 26pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
27 T::state().waker.wake(); 27use crate::{interrupt, Peripheral};
28 } 28
29} 29/// Interrupt handler.
30 30pub struct InterruptHandler<T: Instance> {
31/// PDM microphone interface 31 _phantom: PhantomData<T>,
32pub struct Pdm<'d, T: Instance> { 32}
33 _peri: PeripheralRef<'d, T>, 33
34} 34impl<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());
40pub 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 }
49static DUMMY_BUFFER: [i16; 1] = [0; 1]; 49
50 50 T::state().waker.wake();
51impl<'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, 55pub 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 63pub 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 74static 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 81pub 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); 88impl<'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
204pub 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
211impl 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();
222pub 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
231pub 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;
238impl<'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);
251pub(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.
274pub 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,
279macro_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
368pub 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
389impl 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)]
410pub enum OperationMode {
411 /// Mono (1 channel)
412 Mono,
413 /// Stereo (2 channels)
414 Stereo,
415}
416
417impl 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)]
428pub enum Edge {
429 /// Left edge is rising
430 LeftRising,
431 /// Left edge is falling
432 LeftFalling,
433}
434
435impl 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
444impl<'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
457pub(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.
480pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
481 /// Interrupt for this peripheral.
482 type Interrupt: interrupt::typelevel::Interrupt;
483}
484
485macro_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
84impl<'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
84impl<'d, M: Mode> Adc<'d, M> { 94impl<'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::*;
45pub struct GenericSMI { 45pub 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
50impl GenericSMI { 52impl 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
64unsafe impl PHY for GenericSMI { 63unsafe 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
104impl GenericSMI { 103impl 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
3pub mod enums; 3pub mod enums;
4 4
5use embassy_hal_common::{into_ref, PeripheralRef}; 5use embassy_hal_common::{into_ref, PeripheralRef};
6use enums::*; 6use enums::*;
7 7
8use crate::dma::Transfer; 8use crate::dma::Transfer;
9use crate::gpio::sealed::AFType; 9use crate::gpio::sealed::AFType;
10use crate::gpio::AnyPin; 10use crate::gpio::AnyPin;
11use crate::pac::quadspi::Quadspi as Regs; 11use crate::pac::quadspi::Quadspi as Regs;
12use crate::rcc::RccPeripheral; 12use crate::rcc::RccPeripheral;
13use crate::{peripherals, Peripheral}; 13use crate::{peripherals, Peripheral};
14 14
15pub struct TransferConfig { 15pub 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
32impl Default for TransferConfig { 32impl 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
46pub struct Config { 46pub 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
60impl Default for Config { 60impl 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)]
73pub struct Qspi<'d, T: Instance, Dma> { 73pub 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
85impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { 85impl<'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
305pub(crate) mod sealed { 305pub(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
313pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} 313pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
314 314
315pin_trait!(SckPin, Instance); 315pin_trait!(SckPin, Instance);
316pin_trait!(D0Pin, Instance); 316pin_trait!(D0Pin, Instance);
317pin_trait!(D1Pin, Instance); 317pin_trait!(D1Pin, Instance);
318pin_trait!(D2Pin, Instance); 318pin_trait!(D2Pin, Instance);
319pin_trait!(D3Pin, Instance); 319pin_trait!(D3Pin, Instance);
320pin_trait!(NSSPin, Instance); 320pin_trait!(NSSPin, Instance);
321 321
322dma_trait!(QuadDma, Instance); 322dma_trait!(QuadDma, Instance);
323 323
324foreach_peripheral!( 324foreach_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
121impl Default for Config { 125impl 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
43defmt = "0.3" 43defmt = "0.3"
44defmt-rtt = "0.4" 44defmt-rtt = "0.4"
45 45
46fixed = "1.10.0"
46static_cell = "1.1" 47static_cell = "1.1"
47cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 48cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
48cortex-m-rt = "0.7.0" 49cortex-m-rt = "0.7.0"
@@ -53,6 +54,8 @@ embedded-storage = "0.3.0"
53usbd-hid = "0.6.0" 54usbd-hid = "0.6.0"
54serde = { version = "1.0.136", default-features = false } 55serde = { version = "1.0.136", default-features = false }
55embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } 56embedded-hal-async = { version = "0.2.0-alpha.2", optional = true }
57num-integer = { version = "0.1.45", default-features = false }
58microfft = "0.5.0"
56 59
57[patch.crates-io] 60[patch.crates-io]
58lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } 61lora-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;
7use embassy_nrf::pdm::{self, Config, Pdm}; 7use embassy_nrf::pdm::{self, Config, Pdm};
8use embassy_nrf::{bind_interrupts, peripherals}; 8use embassy_nrf::{bind_interrupts, peripherals};
9use embassy_time::{Duration, Timer}; 9use embassy_time::{Duration, Timer};
10use fixed::types::I7F1;
11use num_integer::Roots;
10use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
11 13
12bind_interrupts!(struct Irqs { 14bind_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
5use core::cmp::Ordering;
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_nrf::pdm::{self, Config, Frequency, OperationMode, Pdm, Ratio, SamplerState};
10use embassy_nrf::{bind_interrupts, peripherals};
11use fixed::types::I7F1;
12use microfft::real::rfft_1024;
13use num_integer::Roots;
14use {defmt_rtt as _, panic_probe as _};
15
16// Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer
17
18bind_interrupts!(struct Irqs {
19 PDM => pdm::InterruptHandler<peripherals::PDM>;
20});
21
22#[embassy_executor::main]
23async 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
65fn 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}