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.
This commit is contained in:
pennae 2023-07-21 21:31:44 +02:00
parent 54d31c98fe
commit b166ed6b78
4 changed files with 66 additions and 70 deletions

View File

@ -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 = impl AdcPin + 'p> + 'p, pull: Pull) -> Self {
pub struct Channel<'p>(Source<'p>);
impl<'p> Channel<'p> {
pub fn new_pin(pin: impl Peripheral<P = impl AdcPin + 'p> + 'p, pull: Pull) -> Self {
into_ref!(pin);
pin.pad_ctrl().modify(|w| {
// manual says:
@ -42,25 +45,41 @@ 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 = ADC_TEMP_SENSOR> + '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 {
match &self.0 {
// this requires adc pins to be sequential and matching the adc channels,
// which is the case for rp2040
self.pin._pin() - 26
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| {
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));
}
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
@ -115,10 +134,10 @@ impl<'d, M: Mode> Adc<'d, M> {
while !r.cs().read().ready() {}
}
fn sample_blocking(channel: u8) -> Result<u16, Error> {
pub fn blocking_read(&mut self, ch: &mut Channel) -> Result<u16, Error> {
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<u16, Error> {
Self::sample_blocking(pin.channel())
}
pub fn blocking_read_temperature(&mut self) -> Result<u16, Error> {
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<u16, Error> {
pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result<u16, Error> {
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<u16, Error> {
Self::sample_async(pin.channel()).await
}
pub async fn read_temperature(&mut self) -> Result<u16, Error> {
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 {}

View File

@ -183,6 +183,7 @@ embassy_hal_internal::peripherals! {
FLASH,
ADC,
ADC_TEMP_SENSOR,
CORE1,

View File

@ -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;
}

View File

@ -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);