From 48eac0b1462726ab292354b50e299c8ebfde85c6 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 21:01:56 +0200 Subject: [PATCH 1/8] rp: add AdcChannel trait this is more general than AdcPin and can also cover the temperature sensor. --- embassy-rp/src/adc.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 4fba31169..e0c2910b8 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -223,21 +223,23 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { - pub trait AdcPin: crate::gpio::sealed::Pin { + pub trait AdcChannel { fn channel(&mut self) -> u8; } } -pub trait AdcPin: sealed::AdcPin + gpio::Pin {} +pub trait AdcChannel: sealed::AdcChannel {} +pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $channel:expr) => { - impl sealed::AdcPin for peripherals::$pin { + impl sealed::AdcChannel for peripherals::$pin { fn channel(&mut self) -> u8 { $channel } } + impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } From 54d31c98fe44533c955c494ea58dd16810367c4f Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 21:33:53 +0200 Subject: [PATCH 2/8] rp: remove AdcChannel::channel we're not using it, and actually using it is more trouble than it's worth. remove the false assurance instead. --- embassy-rp/src/adc.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index e0c2910b8..38b323ec9 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -223,28 +223,21 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { - pub trait AdcChannel { - fn channel(&mut self) -> u8; - } + pub trait AdcChannel {} } pub trait AdcChannel: sealed::AdcChannel {} pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { - ($pin:ident, $channel:expr) => { - impl sealed::AdcChannel for peripherals::$pin { - fn channel(&mut self) -> u8 { - $channel - } - } - + ($pin:ident) => { + impl sealed::AdcChannel for peripherals::$pin {} impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } -impl_pin!(PIN_26, 0); -impl_pin!(PIN_27, 1); -impl_pin!(PIN_28, 2); -impl_pin!(PIN_29, 3); +impl_pin!(PIN_26); +impl_pin!(PIN_27); +impl_pin!(PIN_28); +impl_pin!(PIN_29); From b166ed6b78db0737005a65c1e444ce7563de7da3 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 21:31:44 +0200 Subject: [PATCH 3/8] rp: generalize adc inputs from pins to channels this lets us treat pins and the temperature sensor uniformly using the same interface. uniformity in turn lets us add more adc features without combinatorial explosion of methods and types needed to handle them all. --- embassy-rp/src/adc.rs | 102 +++++++++++++++++-------------------- embassy-rp/src/lib.rs | 1 + examples/rp/src/bin/adc.rs | 11 ++-- tests/rp/src/bin/adc.rs | 22 ++++---- 4 files changed, 66 insertions(+), 70 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 38b323ec9..2824d893c 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -10,8 +10,8 @@ use crate::gpio::sealed::Pin as GpioPin; use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; -use crate::peripherals::ADC; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; +use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -24,12 +24,15 @@ impl Default for Config { } } -pub struct Pin<'p> { - pin: PeripheralRef<'p, AnyPin>, +enum Source<'p> { + Pin(PeripheralRef<'p, AnyPin>), + TempSensor(PeripheralRef<'p, ADC_TEMP_SENSOR>), } -impl<'p> Pin<'p> { - pub fn new(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { +pub struct Channel<'p>(Source<'p>); + +impl<'p> Channel<'p> { + pub fn new_pin(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { into_ref!(pin); pin.pad_ctrl().modify(|w| { // manual says: @@ -42,24 +45,40 @@ impl<'p> Pin<'p> { w.set_pue(pull == Pull::Up); w.set_pde(pull == Pull::Down); }); - Self { pin: pin.map_into() } + Self(Source::Pin(pin.map_into())) + } + + pub fn new_sensor(s: impl Peripheral

+ 'p) -> Self { + let r = pac::ADC; + r.cs().write_set(|w| w.set_ts_en(true)); + Self(Source::TempSensor(s.into_ref())) } fn channel(&self) -> u8 { - // this requires adc pins to be sequential and matching the adc channels, - // which is the case for rp2040 - self.pin._pin() - 26 + match &self.0 { + // this requires adc pins to be sequential and matching the adc channels, + // which is the case for rp2040 + Source::Pin(p) => p._pin() - 26, + Source::TempSensor(_) => 4, + } } } -impl<'d> Drop for Pin<'d> { +impl<'p> Drop for Source<'p> { fn drop(&mut self) { - self.pin.pad_ctrl().modify(|w| { - w.set_ie(true); - w.set_od(false); - w.set_pue(false); - w.set_pde(true); - }); + match self { + Source::Pin(p) => { + p.pad_ctrl().modify(|w| { + w.set_ie(true); + w.set_od(false); + w.set_pue(false); + w.set_pde(true); + }); + } + Source::TempSensor(_) => { + pac::ADC.cs().write_clear(|w| w.set_ts_en(true)); + } + } } } @@ -115,10 +134,10 @@ impl<'d, M: Mode> Adc<'d, M> { while !r.cs().read().ready() {} } - fn sample_blocking(channel: u8) -> Result { + pub fn blocking_read(&mut self, ch: &mut Channel) -> Result { let r = Self::regs(); r.cs().modify(|w| { - w.set_ainsel(channel); + w.set_ainsel(ch.channel()); w.set_start_once(true); w.set_err(true); }); @@ -128,19 +147,6 @@ impl<'d, M: Mode> Adc<'d, M> { false => Ok(r.result().read().result().into()), } } - - pub fn blocking_read(&mut self, pin: &mut Pin) -> Result { - Self::sample_blocking(pin.channel()) - } - - pub fn blocking_read_temperature(&mut self) -> Result { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - while !r.cs().read().ready() {} - let result = Self::sample_blocking(4); - r.cs().modify(|w| w.set_ts_en(false)); - result - } } impl<'d> Adc<'d, Async> { @@ -172,10 +178,10 @@ impl<'d> Adc<'d, Async> { .await; } - async fn sample_async(channel: u8) -> Result { + pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result { let r = Self::regs(); r.cs().modify(|w| { - w.set_ainsel(channel); + w.set_ainsel(ch.channel()); w.set_start_once(true); w.set_err(true); }); @@ -185,21 +191,6 @@ impl<'d> Adc<'d, Async> { false => Ok(r.result().read().result().into()), } } - - pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result { - Self::sample_async(pin.channel()).await - } - - pub async fn read_temperature(&mut self) -> Result { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - if !r.cs().read().ready() { - Self::wait_for_ready().await; - } - let result = Self::sample_async(4).await; - r.cs().modify(|w| w.set_ts_en(false)); - result - } } impl<'d> Adc<'d, Blocking> { @@ -230,14 +221,17 @@ pub trait AdcChannel: sealed::AdcChannel {} pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { - ($pin:ident) => { + ($pin:ident, $channel:expr) => { impl sealed::AdcChannel for peripherals::$pin {} impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } -impl_pin!(PIN_26); -impl_pin!(PIN_27); -impl_pin!(PIN_28); -impl_pin!(PIN_29); +impl_pin!(PIN_26, 0); +impl_pin!(PIN_27, 1); +impl_pin!(PIN_28, 2); +impl_pin!(PIN_29, 3); + +impl sealed::AdcChannel for peripherals::ADC_TEMP_SENSOR {} +impl AdcChannel for peripherals::ADC_TEMP_SENSOR {} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 45156458d..49bd3533e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -183,6 +183,7 @@ embassy_hal_internal::peripherals! { FLASH, ADC, + ADC_TEMP_SENSOR, CORE1, diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 81a8b8340..c58695512 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use embassy_time::{Duration, Timer}; @@ -22,9 +22,10 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); - let mut p26 = Pin::new(p.PIN_26, Pull::None); - let mut p27 = Pin::new(p.PIN_27, Pull::None); - let mut p28 = Pin::new(p.PIN_28, Pull::None); + let mut p26 = Channel::new_pin(p.PIN_26, Pull::None); + let mut p27 = Channel::new_pin(p.PIN_27, Pull::None); + let mut p28 = Channel::new_pin(p.PIN_28, Pull::None); + let mut ts = Channel::new_sensor(p.ADC_TEMP_SENSOR); loop { let level = adc.read(&mut p26).await.unwrap(); @@ -33,7 +34,7 @@ async fn main(_spawner: Spawner) { info!("Pin 27 ADC: {}", level); let level = adc.read(&mut p28).await.unwrap(); info!("Pin 28 ADC: {}", level); - let temp = adc.read_temperature().await.unwrap(); + let temp = adc.read(&mut ts).await.unwrap(); info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index e659844ae..9006ce8cc 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -6,7 +6,7 @@ mod common; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use {defmt_rtt as _, panic_probe as _}; @@ -22,12 +22,12 @@ async fn main(_spawner: Spawner) { { { - let mut p = Pin::new(&mut p.PIN_26, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_26, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_26, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_26, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000); } @@ -35,21 +35,21 @@ async fn main(_spawner: Spawner) { // not bothering with async reads from now on { { - let mut p = Pin::new(&mut p.PIN_27, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_27, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_27, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_27, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); } } { { - let mut p = Pin::new(&mut p.PIN_28, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_28, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_28, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_28, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); } } @@ -57,22 +57,22 @@ async fn main(_spawner: Spawner) { // gp29 is connected to vsys through a 200k/100k divider, // adding pulls should change the value let low = { - let mut p = Pin::new(&mut p.PIN_29, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::Down); adc.blocking_read(&mut p).unwrap() }; let none = { - let mut p = Pin::new(&mut p.PIN_29, Pull::None); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::None); adc.blocking_read(&mut p).unwrap() }; let up = { - let mut p = Pin::new(&mut p.PIN_29, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::Up); adc.blocking_read(&mut p).unwrap() }; defmt::assert!(low < none); defmt::assert!(none < up); } - let temp = convert_to_celsius(adc.read_temperature().await.unwrap()); + let temp = convert_to_celsius(adc.read(&mut Channel::new_sensor(p.ADC_TEMP_SENSOR)).await.unwrap()); defmt::assert!(temp > 0.0); defmt::assert!(temp < 60.0); From a6b8f3d99478266b4f110e9c150ce3add5c3ffc6 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 23:34:12 +0200 Subject: [PATCH 4/8] rp: add single-channel dma from adc with uniform treatment of adc inputs it's easy enough to add a new sampling method. dma sampling only supports one channel at the moment, though round-robin sampling would be a simple extension (probably a new trait that's implemented for Channel and &[Channel]). continuous dma as proposed in #1608 also isn't done here, we'd expect that to be a compound dma::Channel that internally splits a buffer in half and dispatches callbacks or something like that. --- embassy-rp/src/adc.rs | 115 ++++++++++++++++++++++++++++++++++++- examples/rp/src/bin/adc.rs | 2 +- tests/rp/src/bin/adc.rs | 55 ++++++++++++++++-- 3 files changed, 165 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 2824d893c..bac455743 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -1,5 +1,6 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -11,7 +12,7 @@ use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; -use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; +use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -48,7 +49,7 @@ impl<'p> Channel<'p> { Self(Source::Pin(pin.map_into())) } - pub fn new_sensor(s: impl Peripheral

+ 'p) -> Self { + pub fn new_temp_sensor(s: impl Peripheral

+ 'p) -> Self { let r = pac::ADC; r.cs().write_set(|w| w.set_ts_en(true)); Self(Source::TempSensor(s.into_ref())) @@ -82,6 +83,21 @@ impl<'p> Drop for Source<'p> { } } +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] +pub struct Sample(u16); + +impl Sample { + pub fn good(&self) -> bool { + self.0 < 0x8000 + } + + pub fn value(&self) -> u16 { + self.0 & !0x8000 + } +} + #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -191,6 +207,91 @@ impl<'d> Adc<'d, Async> { false => Ok(r.result().read().result().into()), } } + + async fn read_many_inner( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [W], + fcs_err: bool, + dma: impl Peripheral

, + ) -> Result<(), Error> { + let r = Self::regs(); + // clear previous errors and set channel + r.cs().modify(|w| { + w.set_ainsel(ch.channel()); + w.set_err_sticky(true); // clear previous errors + w.set_start_many(false); + }); + // wait for previous conversions and drain fifo. an earlier batch read may have + // been cancelled, leaving the adc running. + while !r.cs().read().ready() {} + while !r.fcs().read().empty() { + r.fifo().read(); + } + + // set up fifo for dma + r.fcs().write(|w| { + w.set_thresh(1); + w.set_dreq_en(true); + w.set_shift(mem::size_of::() == 1); + w.set_en(true); + w.set_err(fcs_err); + }); + + // reset dma config on drop, regardless of whether it was a future being cancelled + // or the method returning normally. + struct ResetDmaConfig; + impl Drop for ResetDmaConfig { + fn drop(&mut self) { + pac::ADC.cs().write_clear(|w| w.set_start_many(true)); + while !pac::ADC.cs().read().ready() {} + pac::ADC.fcs().write_clear(|w| { + w.set_dreq_en(true); + w.set_shift(true); + w.set_en(true); + }); + } + } + let auto_reset = ResetDmaConfig; + + let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) }; + // start conversions and wait for dma to finish. we can't report errors early + // because there's no interrupt to signal them, and inspecting every element + // of the fifo is too costly to do here. + r.cs().write_set(|w| w.set_start_many(true)); + dma.await; + mem::drop(auto_reset); + // we can't report errors before the conversions have ended since no interrupt + // exists to report them early, and since they're exceedingly rare we probably don't + // want to anyway. + match r.cs().read().err_sticky() { + false => Ok(()), + true => Err(Error::ConversionFailed), + } + } + + #[inline] + pub async fn read_many( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [S], + dma: impl Peripheral

, + ) -> Result<(), Error> { + self.read_many_inner(ch, buf, false, dma).await + } + + #[inline] + pub async fn read_many_raw( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [Sample], + dma: impl Peripheral

, + ) { + // errors are reported in individual samples + let _ = self + .read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, dma) + .await; + } } impl<'d> Adc<'d, Blocking> { @@ -214,9 +315,19 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { + pub trait AdcSample: crate::dma::Word {} + pub trait AdcChannel {} } +pub trait AdcSample: sealed::AdcSample {} + +impl sealed::AdcSample for u16 {} +impl AdcSample for u16 {} + +impl sealed::AdcSample for u8 {} +impl AdcSample for u8 {} + pub trait AdcChannel: sealed::AdcChannel {} pub trait AdcPin: AdcChannel + gpio::Pin {} diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index c58695512..02bc493b6 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { let mut p26 = Channel::new_pin(p.PIN_26, Pull::None); let mut p27 = Channel::new_pin(p.PIN_27, Pull::None); let mut p28 = Channel::new_pin(p.PIN_28, Pull::None); - let mut ts = Channel::new_sensor(p.ADC_TEMP_SENSOR); + let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR); loop { let level = adc.read(&mut p26).await.unwrap(); diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index 9006ce8cc..d6d58f0c0 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -6,7 +6,7 @@ mod common; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use {defmt_rtt as _, panic_probe as _}; @@ -71,10 +71,57 @@ async fn main(_spawner: Spawner) { defmt::assert!(low < none); defmt::assert!(none < up); } + { + let temp = convert_to_celsius( + adc.read(&mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR)) + .await + .unwrap(), + ); + defmt::assert!(temp > 0.0); + defmt::assert!(temp < 60.0); + } - let temp = convert_to_celsius(adc.read(&mut Channel::new_sensor(p.ADC_TEMP_SENSOR)).await.unwrap()); - defmt::assert!(temp > 0.0); - defmt::assert!(temp < 60.0); + // run a bunch of conversions. we'll only check gp29 and the temp + // sensor here for brevity, if those two work the rest will too. + { + // gp29 is connected to vsys through a 200k/100k divider, + // adding pulls should change the value + let mut low = [0u16; 16]; + let mut none = [0u8; 16]; + let mut up = [Sample::default(); 16]; + adc.read_many( + &mut Channel::new_pin(&mut p.PIN_29, Pull::Down), + &mut low, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + adc.read_many( + &mut Channel::new_pin(&mut p.PIN_29, Pull::None), + &mut none, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + adc.read_many_raw(&mut Channel::new_pin(&mut p.PIN_29, Pull::Up), &mut up, &mut p.DMA_CH0) + .await; + defmt::assert!(low.iter().zip(none.iter()).all(|(l, n)| *l >> 4 < *n as u16)); + defmt::assert!(up.iter().all(|s| s.good())); + defmt::assert!(none.iter().zip(up.iter()).all(|(n, u)| (*n as u16) < u.value())); + } + { + let mut temp = [0u16; 16]; + adc.read_many( + &mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR), + &mut temp, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + let temp = temp.map(convert_to_celsius); + defmt::assert!(temp.iter().all(|t| *t > 0.0)); + defmt::assert!(temp.iter().all(|t| *t < 60.0)); + } info!("Test OK"); cortex_m::asm::bkpt(); From 33778d37721a076e1f64b0913cf1277257bd2b09 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Wed, 2 Aug 2023 20:13:48 +0000 Subject: [PATCH 5/8] Fix link to embassy-time in embassy-net/README.md --- embassy-net/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/README.md b/embassy-net/README.md index 48f9fd832..811321ca4 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -49,7 +49,7 @@ trait has not had breaking changes. This crate can run on any executor. -[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must +[`embassy-time`](https://crates.io/crates/embassy-time) is used for timekeeping and timeouts. You must link an `embassy-time` driver in your project to use this crate. ## License From bcaef1de189732f1744804cd7c9c038fe79f0be9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 2 Aug 2023 22:57:42 +0200 Subject: [PATCH 6/8] feat: make nrf bootloader watchdog generic for any flash --- embassy-boot/nrf/src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index bb702073c..65f57fcd1 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -6,7 +6,7 @@ mod fmt; #[cfg(feature = "nightly")] pub use embassy_boot::FirmwareUpdater; pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; -use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; +use embassy_nrf::nvmc::PAGE_SIZE; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; @@ -104,15 +104,15 @@ impl } } -/// A flash implementation that wraps NVMC and will pet a watchdog when touching flash. -pub struct WatchdogFlash<'d> { - flash: Nvmc<'d>, +/// A flash implementation that wraps any flash and will pet a watchdog when touching flash. +pub struct WatchdogFlash { + flash: FLASH, wdt: wdt::WatchdogHandle, } -impl<'d> WatchdogFlash<'d> { +impl WatchdogFlash { /// Start a new watchdog with a given flash and WDT peripheral and a timeout - pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self { + pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self { let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) { Ok(x) => x, Err(_) => { @@ -127,13 +127,13 @@ impl<'d> WatchdogFlash<'d> { } } -impl<'d> ErrorType for WatchdogFlash<'d> { - type Error = as ErrorType>::Error; +impl ErrorType for WatchdogFlash { + type Error = FLASH::Error; } -impl<'d> NorFlash for WatchdogFlash<'d> { - const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; - const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; +impl NorFlash for WatchdogFlash { + const WRITE_SIZE: usize = FLASH::WRITE_SIZE; + const ERASE_SIZE: usize = FLASH::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.wdt.pet(); @@ -145,8 +145,8 @@ impl<'d> NorFlash for WatchdogFlash<'d> { } } -impl<'d> ReadNorFlash for WatchdogFlash<'d> { - const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; +impl ReadNorFlash for WatchdogFlash { + const READ_SIZE: usize = FLASH::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.wdt.pet(); self.flash.read(offset, data) From 4d60c715e683aaadf25d9f066bde805c725fefb4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 3 Aug 2023 14:23:11 +0200 Subject: [PATCH 7/8] net: move tuntap from std example to separate crate. (#1737) --- embassy-net-tuntap/Cargo.toml | 19 +++++++++++++++++++ embassy-net-tuntap/README.md | 17 +++++++++++++++++ .../src/lib.rs | 1 + examples/std/Cargo.toml | 3 +-- examples/std/src/bin/net.rs | 5 +---- examples/std/src/bin/net_dns.rs | 5 +---- examples/std/src/bin/net_udp.rs | 5 +---- examples/std/src/bin/tcp_accept.rs | 5 +---- 8 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 embassy-net-tuntap/Cargo.toml create mode 100644 embassy-net-tuntap/README.md rename examples/std/src/tuntap.rs => embassy-net-tuntap/src/lib.rs (99%) diff --git a/embassy-net-tuntap/Cargo.toml b/embassy-net-tuntap/Cargo.toml new file mode 100644 index 000000000..08d309680 --- /dev/null +++ b/embassy-net-tuntap/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "embassy-net-tuntap" +version = "0.1.0" +description = "embassy-net driver for Linux TUN/TAP interfaces." +keywords = ["embedded", "tuntap", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +async-io = "1.6.0" +log = "0.4.14" +libc = "0.2.101" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/" +target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/embassy-net-tuntap/README.md b/embassy-net-tuntap/README.md new file mode 100644 index 000000000..c5d9e746c --- /dev/null +++ b/embassy-net-tuntap/README.md @@ -0,0 +1,17 @@ +# `embassy-net` integration for Linux TUN/TAP interfaces. + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces. + +## Interoperability + +This crate can run on any executor. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/examples/std/src/tuntap.rs b/embassy-net-tuntap/src/lib.rs similarity index 99% rename from examples/std/src/tuntap.rs rename to embassy-net-tuntap/src/lib.rs index 167c3da5f..75c54c487 100644 --- a/examples/std/src/tuntap.rs +++ b/embassy-net-tuntap/src/lib.rs @@ -19,6 +19,7 @@ const ETHERNET_HEADER_LEN: usize = 14; #[repr(C)] #[derive(Debug)] +#[allow(non_camel_case_types)] struct ifreq { ifr_name: [libc::c_char; libc::IF_NAMESIZE], ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 42adede10..544176828 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } -embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } +embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } @@ -18,7 +18,6 @@ env_logger = "0.9.0" futures = { version = "0.3.17" } log = "0.4.14" nix = "0.26.2" -libc = "0.2.101" clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 3aadb029d..e0de14162 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -6,6 +6,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use embassy_time::Duration; use embedded_io::asynch::Write; use heapless::Vec; @@ -13,10 +14,6 @@ use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 65b5a2cd9..6c19874d5 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -6,15 +6,12 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::dns::DnsQueryType; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 3fc46156c..98dcc9925 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -4,15 +4,12 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index df09986ac..0c920a3fb 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -7,6 +7,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write as _; use heapless::Vec; @@ -14,10 +15,6 @@ use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { From 9dfda46e0c43559a6ca2ed87c0ceb32d19456c62 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Jul 2023 02:10:48 +0200 Subject: [PATCH 8/8] rp: add dormant-wake functionality for Input this temporarily takes ownership of pins because we need to clear edge interrupts before waiting for them (otherwise we may wait indefinitely), we want to clean up the dormant-wake bits after a wakeup, and doing anything *else* with the input while we're waiting for a wakeup isn't possible at all. doing it like this lets us not impose any cost on those who don't use dormant wakes without entangling dormant waits too badly with regular interrupt waits. --- embassy-rp/src/gpio.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 73e893523..ad9d4262d 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -75,6 +75,15 @@ pub enum Bank { Qspi = 1, } +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DormantWakeConfig { + pub edge_high: bool, + pub edge_low: bool, + pub level_high: bool, + pub level_low: bool, +} + pub struct Input<'d, T: Pin> { pin: Flex<'d, T>, } @@ -128,6 +137,11 @@ impl<'d, T: Pin> Input<'d, T> { pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + self.pin.dormant_wake(cfg) + } } /// Interrupt trigger levels. @@ -639,15 +653,62 @@ impl<'d, T: Pin> Flex<'d, T> { pub async fn wait_for_any_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_set(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + w.set_level_high(idx % 8, cfg.level_high); + w.set_level_low(idx % 8, cfg.level_low); + }); + DormantWake { + pin: self.pin.reborrow(), + cfg, + } + } } impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { + let idx = self.pin._pin() as usize; self.pin.pad_ctrl().write(|_| {}); self.pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); + } +} + +pub struct DormantWake<'w, T: Pin> { + pin: PeripheralRef<'w, T>, + cfg: DormantWakeConfig, +} + +impl<'w, T: Pin> Drop for DormantWake<'w, T> { + fn drop(&mut self) { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, self.cfg.edge_high); + w.set_edge_low(idx % 8, self.cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); } }