aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-07-05 20:05:34 +0200
committerpennae <[email protected]>2023-07-07 17:46:35 +0200
commit972cdd4265b24efb101c6b9df373466fcbccac5d (patch)
treebce7704ef8990d5e9032e0db88235aa062e0f619 /embassy-rp/src
parent46a46009520036cc4b4dd52e7e3bdc5191b67ea0 (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.rs225
-rw-r--r--embassy-rp/src/gpio.rs2
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;
3use core::sync::atomic::{compiler_fence, Ordering}; 3use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll; 4use core::task::Poll;
5 5
6use embassy_hal_common::{into_ref, PeripheralRef};
6use embassy_sync::waitqueue::AtomicWaker; 7use embassy_sync::waitqueue::AtomicWaker;
7use embedded_hal_02::adc::{Channel, OneShot};
8 8
9use crate::gpio::Pin; 9use crate::gpio::sealed::Pin as GpioPin;
10use crate::gpio::{self, AnyPin, Pull};
10use crate::interrupt::typelevel::Binding; 11use crate::interrupt::typelevel::Binding;
11use crate::interrupt::InterruptExt; 12use crate::interrupt::InterruptExt;
12use crate::peripherals::ADC; 13use crate::peripherals::ADC;
13use crate::{interrupt, pac, peripherals, Peripheral}; 14use crate::{interrupt, pac, peripherals, Peripheral};
14static WAKER: AtomicWaker = AtomicWaker::new();
15 15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)] 16static WAKER: AtomicWaker = AtomicWaker::new();
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18#[non_exhaustive]
19pub enum Error {
20 // No errors for now
21}
22 17
23#[non_exhaustive] 18#[non_exhaustive]
24pub struct Config {} 19pub struct Config {}
@@ -28,11 +23,65 @@ impl Default for Config {
28 Self {} 23 Self {}
29 } 24 }
30} 25}
31pub struct Adc<'d> { 26
32 phantom: PhantomData<&'d ADC>, 27pub struct Pin<'p> {
28 pin: PeripheralRef<'p, AnyPin>,
29}
30
31impl<'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
55impl<'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))]
68pub enum Error {
69 ConversionFailed,
70}
71
72pub trait Mode {}
73
74pub struct Async;
75impl Mode for Async {}
76
77pub struct Blocking;
78impl Mode for Blocking {}
79
80pub struct Adc<'d, M: Mode> {
81 phantom: PhantomData<(&'d ADC, M)>,
33} 82}
34 83
35impl<'d> Adc<'d> { 84impl<'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
136impl<'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 { 195impl<'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
144macro_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
155pub struct InterruptHandler { 203pub struct InterruptHandler {
156 _empty: (), 204 _empty: (),
157} 205}
158 206
159impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler { 207impl 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
215mod sealed {
216 pub trait AdcPin: crate::gpio::sealed::Pin {
217 fn channel(&mut self) -> u8;
218 }
219}
220
221pub trait AdcPin: sealed::AdcPin + gpio::Pin {}
222
223macro_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
167impl_pin!(PIN_26, 0); 235impl_pin!(PIN_26, 0);
168impl_pin!(PIN_27, 1); 236impl_pin!(PIN_27, 1);
169impl_pin!(PIN_28, 2); 237impl_pin!(PIN_28, 2);
170impl_pin!(PIN_29, 3); 238impl_pin!(PIN_29, 3);
171
172impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
173where
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)]
45pub enum Pull { 45pub enum Pull {
46 None, 46 None,
47 Up, 47 Up,