Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 127 additions & 7 deletions embassy-nxp/src/adc/lpc55.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

#![macro_use]

use embassy_hal_internal::Peri;
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;

use crate::pac;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;

use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::pac::adc0::{Adc0, vals};
use crate::peripherals::ADC0;
use crate::{Async, Blocking, Mode, pac};

/// Resolution selection
pub enum Resolution {
Expand Down Expand Up @@ -48,15 +54,58 @@ impl Default for Config {
}
}

pub(crate) struct Info {
pub(crate) waker: AtomicWaker,
}

pub(crate) trait SealedInstance {
fn info() -> &'static Info;
}

/// ADC instance
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
/// Interrupt for this instance
type Interrupt: crate::interrupt::typelevel::Interrupt;
}

impl SealedInstance for ADC0 {
fn info() -> &'static Info {
static INFO: Info = Info {
waker: AtomicWaker::new(),
};
&INFO
}
}

impl Instance for ADC0 {
type Interrupt = crate::interrupt::typelevel::ADC0;
}

/// Interrupt handler
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}

impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let adc: Adc0 = pac::ADC0;
adc.ie().modify(|w| w.set_fwmie0(0.into()));
T::info().waker.wake();
}
}

/// The main struct
pub struct Adc<'d> {
pub struct Adc<'d, M: Mode> {
_peri: Peri<'d, ADC0>,
config: Config,
_phantom: PhantomData<M>,
}

impl<'d> Adc<'d> {
/// Shared mode-generic implementation
impl<'d, M: Mode> Adc<'d, M> {
/// Creation and initialization of ADC
pub fn new(peri: Peri<'d, ADC0>, config: Config) -> Self {
fn new_inner(peri: Peri<'d, ADC0>, config: Config) -> Self {
let adc: Adc0 = pac::ADC0;

// Power & clocks
Expand Down Expand Up @@ -130,9 +179,14 @@ impl<'d> Adc<'d> {
// Set averaging
adc.ctrl().modify(|w| w.set_cal_avgs(avgs_bit.into()));

Self { _peri: peri, config }
Self {
_peri: peri,
config,
_phantom: PhantomData,
}
}

/// Enable clocks and provide power
fn enable_power_clocks() {
let syscon = pac::SYSCON;
let pmc = pac::PMC;
Expand Down Expand Up @@ -195,8 +249,16 @@ impl<'d> Adc<'d> {

while !(adc.stat().read().cal_rdy().to_bits() != 0) {}
}
}

/// Blocking mode implementation
impl<'d> Adc<'d, Blocking> {
/// Create a blocking ADC instance
pub fn new_blocking(peri: Peri<'d, ADC0>, config: Config) -> Self {
Self::new_inner(peri, config)
}

/// Reading the channel synchronously
/// Read the channel synchronously
pub fn blocking_read<P: AdcPin>(&mut self, pin: &mut crate::Peri<'_, P>) -> u16 {
let adc: Adc0 = pac::ADC0;
pin.configure_iocon();
Expand All @@ -223,6 +285,64 @@ impl<'d> Adc<'d> {
}
}

/// Async mode implementation
impl<'d> Adc<'d, Async> {
/// Create an async ADC instance
pub fn new<T: Instance>(
peri: Peri<'d, ADC0>,
_irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
config: Config,
) -> Self {
let adc = Self::new_inner(peri, config);

T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };

adc
}

/// Read the channel asyncronously
pub async fn read<P: AdcPin>(&mut self, pin: &mut Peri<'_, P>) -> u16 {
let adc: Adc0 = pac::ADC0;
pin.configure_iocon();
adc.cmdl1().modify(|w| {
w.set_adch(pin.channel().into());
w.set_ctype((pin.channel_side() as u8).into())
});

poll_fn(|cx| {
ADC0::info().waker.register(cx.waker());
if adc.fctrl(0).read().fcount() == 0 {
// ADC is not ready
adc.ie().modify(|w| w.set_fwmie0(1.into()));
adc.swtrig().write(|w| w.set_swt0(1.into()));

Poll::Pending
} else {
// ADC is ready
let result_reg = adc.resfifo(0).read();
let data_raw = result_reg.d();

let data = match self.config.resolution {
Resolution::Bits16 => data_raw,
Resolution::Bits12 => data_raw >> 3,
};

Poll::Ready(data)
}
})
.await
}
}

/// Getting maximum value for current resolution
pub fn resolution_to_max_count(resolution: Resolution) -> u16 {
match resolution {
Resolution::Bits12 => (1 << 12) - 1,
Resolution::Bits16 => u16::MAX,
}
}

/// Trait that provides channel numbers for pins that support ADC
pub trait AdcPin: crate::gpio::Pin {
/// Channel number
Expand Down
37 changes: 37 additions & 0 deletions examples/lpc55s69/src/bin/adc_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! This example has been made with the LPCXpresso55S69 board in mind, which has PIO0_16 labled as A0.

#![no_std]
#![no_main]

use defmt::*;
use defmt_rtt as _;
use embassy_executor::Spawner;
use embassy_nxp::adc::{Adc, Config, InterruptHandler, Resolution, resolution_to_max_count};
use embassy_nxp::{bind_interrupts, peripherals};
use embassy_time::Timer;
use panic_halt as _;

bind_interrupts!(struct Irqs {
ADC0 => InterruptHandler<peripherals::ADC0>;
});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_nxp::init(Default::default());

// The default configuration corresponds to Config::new(Resolution::Bits16, Averaging::None);
let config = Config::default();
let mut adc = Adc::new(p.ADC0, Irqs, config);

// PIO0_16 corresponds A0 on the dev board
let mut adc_pin = p.PIO0_16;

let max = resolution_to_max_count(Resolution::Bits16);

loop {
let reading = adc.read(&mut adc_pin).await;
info!("Raw ADC reading: {}", reading);
info!("Scaled: {}%", reading as f32 / max as f32 * 100f32);
Timer::after_millis(500).await;
}
}
9 changes: 6 additions & 3 deletions examples/lpc55s69/src/bin/adc_blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use defmt::*;
use defmt_rtt as _;
use embassy_executor::Spawner;
use embassy_nxp::adc::{Adc, Config};
use embassy_nxp::adc::{Adc, Config, Resolution, resolution_to_max_count};
use embassy_time::Timer;
use panic_halt as _;

Expand All @@ -16,14 +16,17 @@ async fn main(_spawner: Spawner) {

// The default configuration corresponds to Config::new(Resolution::Bits16, Averaging::None);
let config = Config::default();
let mut adc = Adc::new(p.ADC0, config);
let mut adc = Adc::new_blocking(p.ADC0, config);

// PIO0_16 corresponds A0 on the dev board
let mut adc_pin = p.PIO0_16;

let max = resolution_to_max_count(Resolution::Bits16);

loop {
let reading = adc.blocking_read(&mut adc_pin);
info!("ADC reading: {}", reading);
info!("Raw ADC reading: {}", reading);
info!("Scaled: {}%", reading as f32 / max as f32 * 100f32);
Timer::after_millis(500).await;
}
}