aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--embassy-rp/src/adc.rs225
-rw-r--r--embassy-rp/src/gpio.rs2
-rw-r--r--examples/rp/src/bin/adc.rs17
-rw-r--r--tests/rp/src/bin/adc.rs86
4 files changed, 237 insertions, 93 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,
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs
index 7c2ca19f7..65069cde1 100644
--- a/examples/rp/src/bin/adc.rs
+++ b/examples/rp/src/bin/adc.rs
@@ -4,8 +4,9 @@
4 4
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_rp::adc::{Adc, Config, InterruptHandler}; 7use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin};
8use embassy_rp::bind_interrupts; 8use embassy_rp::bind_interrupts;
9use embassy_rp::gpio::Pull;
9use embassy_time::{Duration, Timer}; 10use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
11 12
@@ -18,18 +19,18 @@ async fn main(_spawner: Spawner) {
18 let p = embassy_rp::init(Default::default()); 19 let p = embassy_rp::init(Default::default());
19 let mut adc = Adc::new(p.ADC, Irqs, Config::default()); 20 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
20 21
21 let mut p26 = p.PIN_26; 22 let mut p26 = Pin::new(p.PIN_26, Pull::None);
22 let mut p27 = p.PIN_27; 23 let mut p27 = Pin::new(p.PIN_27, Pull::None);
23 let mut p28 = p.PIN_28; 24 let mut p28 = Pin::new(p.PIN_28, Pull::None);
24 25
25 loop { 26 loop {
26 let level = adc.read(&mut p26).await; 27 let level = adc.read(&mut p26).await.unwrap();
27 info!("Pin 26 ADC: {}", level); 28 info!("Pin 26 ADC: {}", level);
28 let level = adc.read(&mut p27).await; 29 let level = adc.read(&mut p27).await.unwrap();
29 info!("Pin 27 ADC: {}", level); 30 info!("Pin 27 ADC: {}", level);
30 let level = adc.read(&mut p28).await; 31 let level = adc.read(&mut p28).await.unwrap();
31 info!("Pin 28 ADC: {}", level); 32 info!("Pin 28 ADC: {}", level);
32 let temp = adc.read_temperature().await; 33 let temp = adc.read_temperature().await.unwrap();
33 info!("Temp: {} degrees", convert_to_celsius(temp)); 34 info!("Temp: {} degrees", convert_to_celsius(temp));
34 Timer::after(Duration::from_secs(1)).await; 35 Timer::after(Duration::from_secs(1)).await;
35 } 36 }
diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs
new file mode 100644
index 000000000..e659844ae
--- /dev/null
+++ b/tests/rp/src/bin/adc.rs
@@ -0,0 +1,86 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4#[path = "../common.rs"]
5mod common;
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin};
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::Pull;
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 ADC_IRQ_FIFO => InterruptHandler;
16});
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let mut p = embassy_rp::init(Default::default());
21 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
22
23 {
24 {
25 let mut p = Pin::new(&mut p.PIN_26, Pull::Down);
26 defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000);
27 defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000);
28 }
29 {
30 let mut p = Pin::new(&mut p.PIN_26, Pull::Up);
31 defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000);
32 defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000);
33 }
34 }
35 // not bothering with async reads from now on
36 {
37 {
38 let mut p = Pin::new(&mut p.PIN_27, Pull::Down);
39 defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000);
40 }
41 {
42 let mut p = Pin::new(&mut p.PIN_27, Pull::Up);
43 defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000);
44 }
45 }
46 {
47 {
48 let mut p = Pin::new(&mut p.PIN_28, Pull::Down);
49 defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000);
50 }
51 {
52 let mut p = Pin::new(&mut p.PIN_28, Pull::Up);
53 defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000);
54 }
55 }
56 {
57 // gp29 is connected to vsys through a 200k/100k divider,
58 // adding pulls should change the value
59 let low = {
60 let mut p = Pin::new(&mut p.PIN_29, Pull::Down);
61 adc.blocking_read(&mut p).unwrap()
62 };
63 let none = {
64 let mut p = Pin::new(&mut p.PIN_29, Pull::None);
65 adc.blocking_read(&mut p).unwrap()
66 };
67 let up = {
68 let mut p = Pin::new(&mut p.PIN_29, Pull::Up);
69 adc.blocking_read(&mut p).unwrap()
70 };
71 defmt::assert!(low < none);
72 defmt::assert!(none < up);
73 }
74
75 let temp = convert_to_celsius(adc.read_temperature().await.unwrap());
76 defmt::assert!(temp > 0.0);
77 defmt::assert!(temp < 60.0);
78
79 info!("Test OK");
80 cortex_m::asm::bkpt();
81}
82
83fn convert_to_celsius(raw_temp: u16) -> f32 {
84 // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
85 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32
86}