From c05c85f80e6e5d684d5c4dd836741d78998de864 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 5 Aug 2026 13:04:47 +0300 Subject: [PATCH 1/5] Resolution to max count with example --- embassy-nxp/src/adc/lpc55.rs | 8 ++++++++ examples/lpc55s69/src/bin/adc_blocking.rs | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/embassy-nxp/src/adc/lpc55.rs b/embassy-nxp/src/adc/lpc55.rs index 74820c8b45..ac45b4ac4e 100644 --- a/embassy-nxp/src/adc/lpc55.rs +++ b/embassy-nxp/src/adc/lpc55.rs @@ -223,6 +223,14 @@ impl<'d> Adc<'d> { } } +/// 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 diff --git a/examples/lpc55s69/src/bin/adc_blocking.rs b/examples/lpc55s69/src/bin/adc_blocking.rs index 5a7ca182ce..4e3c32e7af 100644 --- a/examples/lpc55s69/src/bin/adc_blocking.rs +++ b/examples/lpc55s69/src/bin/adc_blocking.rs @@ -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 _; @@ -21,9 +21,12 @@ async fn main(_spawner: Spawner) { // 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; } } From 81cab18030b329cfde237c5c0f5db2155bfca631 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 6 Aug 2026 13:51:19 +0300 Subject: [PATCH 2/5] Split async and blocking mode --- embassy-nxp/src/adc/lpc55.rs | 120 +++++++++++++++++++++- examples/lpc55s69/src/bin/adc_blocking.rs | 2 +- 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/embassy-nxp/src/adc/lpc55.rs b/embassy-nxp/src/adc/lpc55.rs index ac45b4ac4e..63e27640a0 100644 --- a/embassy-nxp/src/adc/lpc55.rs +++ b/embassy-nxp/src/adc/lpc55.rs @@ -2,12 +2,20 @@ #![macro_use] -use embassy_hal_internal::Peri; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; +use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::pac; use crate::pac::adc0::{Adc0, vals}; use crate::peripherals::ADC0; +use crate::{Async, Blocking, Mode}; + /// Resolution selection pub enum Resolution { Bits16, @@ -48,15 +56,57 @@ impl Default for Config { } } +pub(crate) struct Info { + pub(crate) waker: AtomicWaker, +} + +pub(crate) trait SealedInstance { + fn info() -> &'static Info; +} + +#[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 { + _phantom: PhantomData, +} + +impl crate::interrupt::typelevel::Handler for InterruptHandler { + 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, } -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 @@ -130,9 +180,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; @@ -195,6 +250,13 @@ impl<'d> Adc<'d> { while !(adc.stat().read().cal_rdy().to_bits() != 0) {} } +} + +/// Blocking mode implementation +impl<'d> Adc<'d, Blocking> { + pub fn new_blocking(peri: Peri<'d, ADC0>, config: Config) -> Self { + Self::new_inner(peri, config) + } /// Reading the channel synchronously pub fn blocking_read(&mut self, pin: &mut crate::Peri<'_, P>) -> u16 { @@ -223,6 +285,54 @@ impl<'d> Adc<'d> { } } +/// Async mode implementation +impl<'d> Adc<'d, Async> { + pub fn new( + peri: Peri<'d, ADC0>, + _irq: impl Binding>, + config: Config, + ) -> Self { + let adc = Self::new_inner(peri, config); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + adc + } + + pub async fn read(&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 { diff --git a/examples/lpc55s69/src/bin/adc_blocking.rs b/examples/lpc55s69/src/bin/adc_blocking.rs index 4e3c32e7af..89b33507c7 100644 --- a/examples/lpc55s69/src/bin/adc_blocking.rs +++ b/examples/lpc55s69/src/bin/adc_blocking.rs @@ -16,7 +16,7 @@ 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; From 2074e39401c5c79aa4def3e5b172ca6e71571ff9 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 6 Aug 2026 14:18:40 +0300 Subject: [PATCH 3/5] Async example --- embassy-nxp/src/adc/lpc55.rs | 10 ++++--- examples/lpc55s69/src/bin/adc_async.rs | 37 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 examples/lpc55s69/src/bin/adc_async.rs diff --git a/embassy-nxp/src/adc/lpc55.rs b/embassy-nxp/src/adc/lpc55.rs index 63e27640a0..6b68762a2f 100644 --- a/embassy-nxp/src/adc/lpc55.rs +++ b/embassy-nxp/src/adc/lpc55.rs @@ -64,9 +64,10 @@ pub(crate) trait SealedInstance { fn info() -> &'static Info; } +/// ADC instance #[allow(private_bounds)] pub trait Instance: SealedInstance + PeripheralType { - /// Interrupt for this instance. + /// Interrupt for this instance type Interrupt: crate::interrupt::typelevel::Interrupt; } @@ -83,7 +84,7 @@ impl Instance for ADC0 { type Interrupt = crate::interrupt::typelevel::ADC0; } -/// Interrupt handler. +/// Interrupt handler pub struct InterruptHandler { _phantom: PhantomData, } @@ -254,11 +255,12 @@ impl<'d, M: Mode> Adc<'d, M> { /// 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(&mut self, pin: &mut crate::Peri<'_, P>) -> u16 { let adc: Adc0 = pac::ADC0; pin.configure_iocon(); @@ -287,6 +289,7 @@ impl<'d> Adc<'d, Blocking> { /// Async mode implementation impl<'d> Adc<'d, Async> { + /// Create an async ADC instance pub fn new( peri: Peri<'d, ADC0>, _irq: impl Binding>, @@ -300,6 +303,7 @@ impl<'d> Adc<'d, Async> { adc } + /// Read the channel asyncronously pub async fn read(&mut self, pin: &mut Peri<'_, P>) -> u16 { let adc: Adc0 = pac::ADC0; pin.configure_iocon(); diff --git a/examples/lpc55s69/src/bin/adc_async.rs b/examples/lpc55s69/src/bin/adc_async.rs new file mode 100644 index 0000000000..49d0b48fbf --- /dev/null +++ b/examples/lpc55s69/src/bin/adc_async.rs @@ -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; +}); + +#[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; + } +} From b04aad815f7570b57114132645d4a5585b175ad3 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 11 Aug 2026 11:17:57 +0300 Subject: [PATCH 4/5] Rustfmt --- embassy-nxp/src/adc/lpc55.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-nxp/src/adc/lpc55.rs b/embassy-nxp/src/adc/lpc55.rs index 6b68762a2f..f25930596c 100644 --- a/embassy-nxp/src/adc/lpc55.rs +++ b/embassy-nxp/src/adc/lpc55.rs @@ -10,11 +10,10 @@ use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::typelevel::{Binding, Interrupt}; -use crate::pac; use crate::pac::adc0::{Adc0, vals}; use crate::peripherals::ADC0; -use crate::{Async, Blocking, Mode}; +use crate::{Async, Blocking, Mode, pac}; /// Resolution selection pub enum Resolution { From d8a282cc52ca3221f3fbb8be477a6e1856fbd5af Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 11 Aug 2026 11:22:01 +0300 Subject: [PATCH 5/5] Rustfmt --- embassy-nxp/src/adc/lpc55.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-nxp/src/adc/lpc55.rs b/embassy-nxp/src/adc/lpc55.rs index f25930596c..a574ef6807 100644 --- a/embassy-nxp/src/adc/lpc55.rs +++ b/embassy-nxp/src/adc/lpc55.rs @@ -12,7 +12,6 @@ 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