diff options
| author | pennae <[email protected]> | 2023-07-05 20:05:34 +0200 |
|---|---|---|
| committer | pennae <[email protected]> | 2023-07-07 17:46:35 +0200 |
| commit | 972cdd4265b24efb101c6b9df373466fcbccac5d (patch) | |
| tree | bce7704ef8990d5e9032e0db88235aa062e0f619 /embassy-rp/src | |
| parent | 46a46009520036cc4b4dd52e7e3bdc5191b67ea0 (diff) | |
rp/adc: rewrite the module
- don't require an irq binding for blocking-only adc
- abstract adc pins into an AnyPin like interface, erasing the actual
peripheral type at runtime.
- add pull-up/pull-down functions for adc pins
- add a test (mostly a copy of the example, to be honest)
- configure adc pads according to datasheet
- report conversion errors (although they seem exceedingly rare?)
- drop embedded-hal interfaces. embedded-hal channels can do neither
AnyPin nor pullup/pulldown without encoding both into the type
Diffstat (limited to 'embassy-rp/src')
| -rw-r--r-- | embassy-rp/src/adc.rs | 225 | ||||
| -rw-r--r-- | embassy-rp/src/gpio.rs | 2 |
2 files changed, 142 insertions, 85 deletions
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 699a0d61d..dfa1b877a 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs | |||
| @@ -3,22 +3,17 @@ use core::marker::PhantomData; | |||
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 3 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 6 | use embassy_sync::waitqueue::AtomicWaker; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
| 7 | use embedded_hal_02::adc::{Channel, OneShot}; | ||
| 8 | 8 | ||
| 9 | use crate::gpio::Pin; | 9 | use crate::gpio::sealed::Pin as GpioPin; |
| 10 | use crate::gpio::{self, AnyPin, Pull}; | ||
| 10 | use crate::interrupt::typelevel::Binding; | 11 | use crate::interrupt::typelevel::Binding; |
| 11 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 12 | use crate::peripherals::ADC; | 13 | use crate::peripherals::ADC; |
| 13 | use crate::{interrupt, pac, peripherals, Peripheral}; | 14 | use crate::{interrupt, pac, peripherals, Peripheral}; |
| 14 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 15 | 15 | ||
| 16 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 16 | static WAKER: AtomicWaker = AtomicWaker::new(); |
| 17 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 18 | #[non_exhaustive] | ||
| 19 | pub enum Error { | ||
| 20 | // No errors for now | ||
| 21 | } | ||
| 22 | 17 | ||
| 23 | #[non_exhaustive] | 18 | #[non_exhaustive] |
| 24 | pub struct Config {} | 19 | pub struct Config {} |
| @@ -28,11 +23,65 @@ impl Default for Config { | |||
| 28 | Self {} | 23 | Self {} |
| 29 | } | 24 | } |
| 30 | } | 25 | } |
| 31 | pub struct Adc<'d> { | 26 | |
| 32 | phantom: PhantomData<&'d ADC>, | 27 | pub struct Pin<'p> { |
| 28 | pin: PeripheralRef<'p, AnyPin>, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl<'p> Pin<'p> { | ||
| 32 | pub fn new(pin: impl Peripheral<P = impl AdcPin + 'p> + 'p, pull: Pull) -> Self { | ||
| 33 | into_ref!(pin); | ||
| 34 | pin.pad_ctrl().modify(|w| { | ||
| 35 | // manual says: | ||
| 36 | // | ||
| 37 | // > When using an ADC input shared with a GPIO pin, the pin’s | ||
| 38 | // > digital functions must be disabled by setting IE low and OD | ||
| 39 | // > high in the pin’s pad control register | ||
| 40 | w.set_ie(false); | ||
| 41 | w.set_od(true); | ||
| 42 | w.set_pue(pull == Pull::Up); | ||
| 43 | w.set_pde(pull == Pull::Down); | ||
| 44 | }); | ||
| 45 | Self { pin: pin.map_into() } | ||
| 46 | } | ||
| 47 | |||
| 48 | fn channel(&self) -> u8 { | ||
| 49 | // this requires adc pins to be sequential and matching the adc channels, | ||
| 50 | // which is the case for rp2040 | ||
| 51 | self.pin._pin() - 26 | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | impl<'d> Drop for Pin<'d> { | ||
| 56 | fn drop(&mut self) { | ||
| 57 | self.pin.pad_ctrl().modify(|w| { | ||
| 58 | w.set_ie(true); | ||
| 59 | w.set_od(false); | ||
| 60 | w.set_pue(false); | ||
| 61 | w.set_pde(true); | ||
| 62 | }); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
| 67 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 68 | pub enum Error { | ||
| 69 | ConversionFailed, | ||
| 70 | } | ||
| 71 | |||
| 72 | pub trait Mode {} | ||
| 73 | |||
| 74 | pub struct Async; | ||
| 75 | impl Mode for Async {} | ||
| 76 | |||
| 77 | pub struct Blocking; | ||
| 78 | impl Mode for Blocking {} | ||
| 79 | |||
| 80 | pub struct Adc<'d, M: Mode> { | ||
| 81 | phantom: PhantomData<(&'d ADC, M)>, | ||
| 33 | } | 82 | } |
| 34 | 83 | ||
| 35 | impl<'d> Adc<'d> { | 84 | impl<'d, M: Mode> Adc<'d, M> { |
| 36 | #[inline] | 85 | #[inline] |
| 37 | fn regs() -> pac::adc::Adc { | 86 | fn regs() -> pac::adc::Adc { |
| 38 | pac::ADC | 87 | pac::ADC |
| @@ -45,11 +94,7 @@ impl<'d> Adc<'d> { | |||
| 45 | ret | 94 | ret |
| 46 | } | 95 | } |
| 47 | 96 | ||
| 48 | pub fn new( | 97 | fn setup() { |
| 49 | _inner: impl Peripheral<P = ADC> + 'd, | ||
| 50 | _irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>, | ||
| 51 | _config: Config, | ||
| 52 | ) -> Self { | ||
| 53 | let reset = Self::reset(); | 98 | let reset = Self::reset(); |
| 54 | crate::reset::reset(reset); | 99 | crate::reset::reset(reset); |
| 55 | crate::reset::unreset_wait(reset); | 100 | crate::reset::unreset_wait(reset); |
| @@ -58,6 +103,43 @@ impl<'d> Adc<'d> { | |||
| 58 | r.cs().write(|w| w.set_en(true)); | 103 | r.cs().write(|w| w.set_en(true)); |
| 59 | // Wait for ADC ready | 104 | // Wait for ADC ready |
| 60 | while !r.cs().read().ready() {} | 105 | while !r.cs().read().ready() {} |
| 106 | } | ||
| 107 | |||
| 108 | fn sample_blocking(channel: u8) -> Result<u16, Error> { | ||
| 109 | let r = Self::regs(); | ||
| 110 | r.cs().modify(|w| { | ||
| 111 | w.set_ainsel(channel); | ||
| 112 | w.set_start_once(true); | ||
| 113 | w.set_err(true); | ||
| 114 | }); | ||
| 115 | while !r.cs().read().ready() {} | ||
| 116 | match r.cs().read().err() { | ||
| 117 | true => Err(Error::ConversionFailed), | ||
| 118 | false => Ok(r.result().read().result().into()), | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn blocking_read(&mut self, pin: &mut Pin) -> Result<u16, Error> { | ||
| 123 | Self::sample_blocking(pin.channel()) | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn blocking_read_temperature(&mut self) -> Result<u16, Error> { | ||
| 127 | let r = Self::regs(); | ||
| 128 | r.cs().modify(|w| w.set_ts_en(true)); | ||
| 129 | while !r.cs().read().ready() {} | ||
| 130 | let result = Self::sample_blocking(4); | ||
| 131 | r.cs().modify(|w| w.set_ts_en(false)); | ||
| 132 | result | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | impl<'d> Adc<'d, Async> { | ||
| 137 | pub fn new( | ||
| 138 | _inner: impl Peripheral<P = ADC> + 'd, | ||
| 139 | _irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>, | ||
| 140 | _config: Config, | ||
| 141 | ) -> Self { | ||
| 142 | Self::setup(); | ||
| 61 | 143 | ||
| 62 | // Setup IRQ | 144 | // Setup IRQ |
| 63 | interrupt::ADC_IRQ_FIFO.unpend(); | 145 | interrupt::ADC_IRQ_FIFO.unpend(); |
| @@ -80,102 +162,77 @@ impl<'d> Adc<'d> { | |||
| 80 | .await; | 162 | .await; |
| 81 | } | 163 | } |
| 82 | 164 | ||
| 83 | pub async fn read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { | 165 | async fn sample_async(channel: u8) -> Result<u16, Error> { |
| 84 | let r = Self::regs(); | 166 | let r = Self::regs(); |
| 85 | // disable pull-down and pull-up resistors | ||
| 86 | // pull-down resistors are enabled by default | ||
| 87 | pin.pad_ctrl().modify(|w| { | ||
| 88 | w.set_ie(true); | ||
| 89 | let (pu, pd) = (false, false); | ||
| 90 | w.set_pue(pu); | ||
| 91 | w.set_pde(pd); | ||
| 92 | }); | ||
| 93 | r.cs().modify(|w| { | 167 | r.cs().modify(|w| { |
| 94 | w.set_ainsel(PIN::channel()); | 168 | w.set_ainsel(channel); |
| 95 | w.set_start_once(true) | 169 | w.set_start_once(true); |
| 170 | w.set_err(true); | ||
| 96 | }); | 171 | }); |
| 97 | Self::wait_for_ready().await; | 172 | Self::wait_for_ready().await; |
| 98 | r.result().read().result().into() | 173 | match r.cs().read().err() { |
| 174 | true => Err(Error::ConversionFailed), | ||
| 175 | false => Ok(r.result().read().result().into()), | ||
| 176 | } | ||
| 99 | } | 177 | } |
| 100 | 178 | ||
| 101 | pub async fn read_temperature(&mut self) -> u16 { | 179 | pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result<u16, Error> { |
| 180 | Self::sample_async(pin.channel()).await | ||
| 181 | } | ||
| 182 | |||
| 183 | pub async fn read_temperature(&mut self) -> Result<u16, Error> { | ||
| 102 | let r = Self::regs(); | 184 | let r = Self::regs(); |
| 103 | r.cs().modify(|w| w.set_ts_en(true)); | 185 | r.cs().modify(|w| w.set_ts_en(true)); |
| 104 | if !r.cs().read().ready() { | 186 | if !r.cs().read().ready() { |
| 105 | Self::wait_for_ready().await; | 187 | Self::wait_for_ready().await; |
| 106 | } | 188 | } |
| 107 | r.cs().modify(|w| { | 189 | let result = Self::sample_async(4).await; |
| 108 | w.set_ainsel(4); | 190 | r.cs().modify(|w| w.set_ts_en(false)); |
| 109 | w.set_start_once(true) | 191 | result |
| 110 | }); | ||
| 111 | Self::wait_for_ready().await; | ||
| 112 | r.result().read().result().into() | ||
| 113 | } | 192 | } |
| 193 | } | ||
| 114 | 194 | ||
| 115 | pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { | 195 | impl<'d> Adc<'d, Blocking> { |
| 116 | let r = Self::regs(); | 196 | pub fn new_blocking(_inner: impl Peripheral<P = ADC> + 'd, _config: Config) -> Self { |
| 117 | pin.pad_ctrl().modify(|w| { | 197 | Self::setup(); |
| 118 | w.set_ie(true); | ||
| 119 | let (pu, pd) = (false, false); | ||
| 120 | w.set_pue(pu); | ||
| 121 | w.set_pde(pd); | ||
| 122 | }); | ||
| 123 | r.cs().modify(|w| { | ||
| 124 | w.set_ainsel(PIN::channel()); | ||
| 125 | w.set_start_once(true) | ||
| 126 | }); | ||
| 127 | while !r.cs().read().ready() {} | ||
| 128 | r.result().read().result().into() | ||
| 129 | } | ||
| 130 | 198 | ||
| 131 | pub fn blocking_read_temperature(&mut self) -> u16 { | 199 | Self { phantom: PhantomData } |
| 132 | let r = Self::regs(); | ||
| 133 | r.cs().modify(|w| w.set_ts_en(true)); | ||
| 134 | while !r.cs().read().ready() {} | ||
| 135 | r.cs().modify(|w| { | ||
| 136 | w.set_ainsel(4); | ||
| 137 | w.set_start_once(true) | ||
| 138 | }); | ||
| 139 | while !r.cs().read().ready() {} | ||
| 140 | r.result().read().result().into() | ||
| 141 | } | 200 | } |
| 142 | } | 201 | } |
| 143 | 202 | ||
| 144 | macro_rules! impl_pin { | ||
| 145 | ($pin:ident, $channel:expr) => { | ||
| 146 | impl Channel<Adc<'static>> for peripherals::$pin { | ||
| 147 | type ID = u8; | ||
| 148 | fn channel() -> u8 { | ||
| 149 | $channel | ||
| 150 | } | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | } | ||
| 154 | |||
| 155 | pub struct InterruptHandler { | 203 | pub struct InterruptHandler { |
| 156 | _empty: (), | 204 | _empty: (), |
| 157 | } | 205 | } |
| 158 | 206 | ||
| 159 | impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler { | 207 | impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler { |
| 160 | unsafe fn on_interrupt() { | 208 | unsafe fn on_interrupt() { |
| 161 | let r = Adc::regs(); | 209 | let r = Adc::<Async>::regs(); |
| 162 | r.inte().write(|w| w.set_fifo(false)); | 210 | r.inte().write(|w| w.set_fifo(false)); |
| 163 | WAKER.wake(); | 211 | WAKER.wake(); |
| 164 | } | 212 | } |
| 165 | } | 213 | } |
| 166 | 214 | ||
| 215 | mod sealed { | ||
| 216 | pub trait AdcPin: crate::gpio::sealed::Pin { | ||
| 217 | fn channel(&mut self) -> u8; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | pub trait AdcPin: sealed::AdcPin + gpio::Pin {} | ||
| 222 | |||
| 223 | macro_rules! impl_pin { | ||
| 224 | ($pin:ident, $channel:expr) => { | ||
| 225 | impl sealed::AdcPin for peripherals::$pin { | ||
| 226 | fn channel(&mut self) -> u8 { | ||
| 227 | $channel | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | impl AdcPin for peripherals::$pin {} | ||
| 232 | }; | ||
| 233 | } | ||
| 234 | |||
| 167 | impl_pin!(PIN_26, 0); | 235 | impl_pin!(PIN_26, 0); |
| 168 | impl_pin!(PIN_27, 1); | 236 | impl_pin!(PIN_27, 1); |
| 169 | impl_pin!(PIN_28, 2); | 237 | impl_pin!(PIN_28, 2); |
| 170 | impl_pin!(PIN_29, 3); | 238 | impl_pin!(PIN_29, 3); |
| 171 | |||
| 172 | impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static> | ||
| 173 | where | ||
| 174 | WORD: From<u16>, | ||
| 175 | PIN: Channel<Adc<'static>, ID = u8> + Pin, | ||
| 176 | { | ||
| 177 | type Error = (); | ||
| 178 | fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> { | ||
| 179 | Ok(self.blocking_read(pin).into()) | ||
| 180 | } | ||
| 181 | } | ||
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f8048a4dd..d18fb909c 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -41,7 +41,7 @@ impl From<Level> for bool { | |||
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | /// Represents a pull setting for an input. | 43 | /// Represents a pull setting for an input. |
| 44 | #[derive(Debug, Eq, PartialEq)] | 44 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| 45 | pub enum Pull { | 45 | pub enum Pull { |
| 46 | None, | 46 | None, |
| 47 | Up, | 47 | Up, |
