From 1ccf45058db4e77ac2c59357cab196b659201b63 Mon Sep 17 00:00:00 2001 From: Mathis Deroo Date: Fri, 5 Dec 2025 14:37:19 -0800 Subject: ADC driver improvement Signed-off-by: Mathis Deroo --- embassy-mcxa/src/adc.rs | 429 +++++++++++++++++++++++++----- embassy-mcxa/src/clocks/mod.rs | 3 + embassy-mcxa/src/clocks/periph_helpers.rs | 1 + embassy-mcxa/src/interrupt.rs | 122 ++++++++- embassy-mcxa/src/lib.rs | 10 +- embassy-mcxa/src/pins.rs | 33 --- examples/mcxa/src/bin/adc_interrupt.rs | 42 +-- examples/mcxa/src/bin/adc_polling.rs | 18 +- examples/mcxa/src/lib.rs | 16 -- 9 files changed, 519 insertions(+), 155 deletions(-) delete mode 100644 embassy-mcxa/src/pins.rs delete mode 100644 examples/mcxa/src/lib.rs diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 7475299ba..2e2fb0342 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs @@ -1,42 +1,29 @@ //! ADC driver -use core::sync::atomic::{AtomicBool, Ordering}; +use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; +use paste::paste; +use crate::pac; +use crate::interrupt::typelevel::{Handler, Interrupt}; +use crate::gpio::{GpioPin, SealedPin}; +use maitake_sync::WaitCell; + use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; use crate::clocks::{Gate, PoweredClock, enable_and_reset}; -use crate::pac; + use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; use crate::pac::adc1::ctrl::CalAvgs; use crate::pac::adc1::tctrl::{Tcmd, Tpri}; -type Regs = pac::adc1::RegisterBlock; +/// Global wait cell for alarm notifications +static WAKER: WaitCell = WaitCell::new(); -static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); -// Token-based instance pattern like embassy-imxrt -pub trait Instance: Gate + PeripheralType { - fn ptr() -> *const Regs; -} - -/// Token for ADC1 -pub type Adc1 = crate::peripherals::ADC1; -impl Instance for crate::peripherals::ADC1 { - #[inline(always)] - fn ptr() -> *const Regs { - pac::Adc1::ptr() - } -} - -// Also implement Instance for the Peri wrapper type -// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { -// #[inline(always)] -// fn ptr() -> *const Regs { -// pac::Adc1::ptr() -// } -// } +const G_LPADC_RESULT_SHIFT: u32 = 0; +/// Trigger priority policy for ADC conversions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum TriggerPriorityPolicy { @@ -52,20 +39,40 @@ pub enum TriggerPriorityPolicy { TriggerPriorityExceptionDisabled = 16, } +/// Configuration for the LPADC peripheral. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LpadcConfig { + /// Control system transition to Stop and Wait power modes while ADC is converting. + /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. + /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. pub enable_in_doze_mode: bool, + /// Auto-Calibration Averages. pub conversion_average_mode: CalAvgs, + /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption). pub enable_analog_preliminary: bool, + /// Power-up delay value (in ADC clock cycles) pub power_up_delay: u8, + /// Reference voltage source selection pub reference_voltage_source: Refsel, + /// Power configuration selection. pub power_level_mode: Pwrsel, + /// Trigger priority policy for handling multiple triggers pub trigger_priority_policy: TriggerPriorityPolicy, + /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, + /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. pub enable_conv_pause: bool, + /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. + /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. pub conv_pause_delay: u16, + /// FIFO watermark level for interrupt generation. + /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field, + /// the ready flag would be asserted to indicate stored data has reached the programmable threshold. pub fifo_watermark: u8, + /// Power configuration (normal/deep sleep behavior) pub power: PoweredClock, + /// ADC clock source selection pub source: AdcClockSel, + /// Clock divider for ADC clock pub div: Div4, } @@ -89,6 +96,9 @@ impl Default for LpadcConfig { } } +/// Configuration for a conversion command. +/// +/// Defines the parameters for a single ADC conversion operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvCommandConfig { pub sample_channel_mode: Ctype, @@ -105,6 +115,10 @@ pub struct ConvCommandConfig { pub enable_wait_trigger: bool, } + +/// Configuration for a conversion trigger. +/// +/// Defines how a trigger initiates ADC conversions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvTriggerConfig { pub target_command_id: Tcmd, @@ -113,6 +127,9 @@ pub struct ConvTriggerConfig { pub enable_hardware_trigger: bool, } +/// Result of an ADC conversion. +/// +/// Contains the conversion value and metadata about the conversion. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConvResult { pub command_id_source: u32, @@ -121,14 +138,59 @@ pub struct ConvResult { pub conv_value: u16, } +/// ADC interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +/// ADC driver instance. pub struct Adc<'a, I: Instance> { - _inst: core::marker::PhantomData<&'a mut I>, + _inst: PhantomData<&'a mut I>, } impl<'a, I: Instance> Adc<'a, I> { - /// initialize ADC - pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self { - let adc = unsafe { &*I::ptr() }; + /// Initialize ADC with interrupt support. + /// + /// # Arguments + /// * `_inst` - ADC peripheral instance + /// * `pin` - GPIO pin to use for ADC + /// * `_irq` - Interrupt binding for this ADC instance + /// * `config` - ADC configuration + pub fn new( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + _irq: impl crate::interrupt::typelevel::Binding> + 'a, + config: LpadcConfig, + ) -> Self { + let adc = Self::new_inner(_inst, pin, config); + + I::Interrupt::unpend(); + unsafe { I::Interrupt::enable() }; + + adc + } + + /// Initialize ADC without interrupt support (for polling mode). + /// + /// # Arguments + /// * `_inst` - ADC peripheral instance + /// * `pin` - GPIO pin to use for ADC input + /// * `config` - ADC configuration + pub fn new_polling( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + config: LpadcConfig, + ) -> Self { + Self::new_inner(_inst, pin, config) + } + + /// Internal initialization function shared by `new` and `new_polling`. + fn new_inner( + _inst: Peri<'a, I>, + pin: Peri<'a, impl AdcPin>, + config: LpadcConfig, + ) -> Self { + let adc = &*I::ptr(); let _clock_freq = unsafe { enable_and_reset::(&AdcConfig { @@ -139,6 +201,8 @@ impl<'a, I: Instance> Adc<'a, I> { .expect("Adc Init should not fail") }; + pin.mux(); + /* Reset the module. */ adc.ctrl().modify(|_, w| w.rst().held_in_reset()); adc.ctrl().modify(|_, w| w.rst().released_from_reset()); @@ -221,17 +285,20 @@ impl<'a, I: Instance> Adc<'a, I> { adc.ctrl().modify(|_, w| w.adcen().enabled()); Self { - _inst: core::marker::PhantomData, + _inst: PhantomData, } } + /// Deinitialize the ADC peripheral. pub fn deinit(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ctrl().modify(|_, w| w.adcen().disabled()); } + /// Perform offset calibration. + /// Waits for calibration to complete before returning. pub fn do_offset_calibration(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); // Enable calibration mode adc.ctrl() .modify(|_, w| w.calofs().offset_calibration_request_pending()); @@ -240,6 +307,13 @@ impl<'a, I: Instance> Adc<'a, I> { while adc.stat().read().cal_rdy().is_not_set() {} } + /// Calculate gain conversion result from gain adjustment factor. + /// + /// # Arguments + /// * `gain_adjustment` - Gain adjustment factor + /// + /// # Returns + /// Gain calibration register value pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { let mut gcra_array = [0u32; 17]; let mut gcalr: u32 = 0; @@ -258,8 +332,9 @@ impl<'a, I: Instance> Adc<'a, I> { gcalr } + /// Perform automatic gain calibration. pub fn do_auto_calibration(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} @@ -280,11 +355,21 @@ impl<'a, I: Instance> Adc<'a, I> { while adc.stat().read().cal_rdy().is_not_set() {} } + /// Trigger ADC conversion(s) via software. + /// + /// Initiates conversion(s) for the trigger(s) specified in the bitmask. + /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). + /// + /// # Arguments + /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) pub fn do_software_trigger(&self, trigger_id_mask: u32) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); } + /// Get default conversion command configuration. + /// # Returns + /// Default conversion command configuration pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { ConvCommandConfig { sample_channel_mode: Ctype::SingleEndedASideChannel, @@ -302,9 +387,17 @@ impl<'a, I: Instance> Adc<'a, I> { } } - //TBD Need to add cmdlx and cmdhx with x {2..7} + /// Set conversion command configuration. + /// + /// Configures a conversion command slot with the specified parameters. + /// Commands define how conversions are performed (channel, resolution, etc.). + /// + /// # Arguments + /// * `index` - Command index (currently only 1 is supported) + /// * `config` - Command configuration + ///TBD Need to add cmdlx and cmdhx with x {2..7} pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); match index { 1 => { @@ -338,6 +431,10 @@ impl<'a, I: Instance> Adc<'a, I> { } } + /// Get default conversion trigger configuration. + /// + /// # Returns + /// Default conversion trigger configuration pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { ConvTriggerConfig { target_command_id: Tcmd::NotValid, @@ -347,8 +444,16 @@ impl<'a, I: Instance> Adc<'a, I> { } } + /// Set conversion trigger configuration. + /// + /// Configures a trigger to initiate conversions. Triggers can be + /// activated by software or hardware signals. + /// + /// # Arguments + /// * `trigger_id` - Trigger index (0-15) + /// * `config` - Trigger configuration pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); let tctrl = &adc.tctrl(trigger_id); tctrl.write(|w| unsafe { @@ -363,47 +468,245 @@ impl<'a, I: Instance> Adc<'a, I> { }); } + /// Reset the FIFO buffer. + /// + /// Clears all pending conversion results from the FIFO. pub fn do_reset_fifo(&self) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); } + /// Enable ADC interrupts. + /// + /// Enables the interrupt sources specified in the bitmask. + /// + /// # Arguments + /// * `mask` - Bitmask of interrupt sources to enable pub fn enable_interrupt(&self, mask: u32) { - let adc = unsafe { &*I::ptr() }; + let adc = &*I::ptr(); adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); - INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst); } - pub fn is_interrupt_triggered(&self) -> bool { - INTERRUPT_TRIGGERED.load(Ordering::Relaxed) + /// Disable ADC interrupts. + /// + /// Disables the interrupt sources specified in the bitmask. + /// + /// # Arguments + /// * `mask` - Bitmask of interrupt sources to disable + pub fn disable_interrupt(&self, mask: u32) { + let adc = &*I::ptr(); + adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); } -} -pub fn get_conv_result() -> Option { - let adc = unsafe { &*pac::Adc1::ptr() }; - let fifo = adc.resfifo0().read().bits(); - const VALID_MASK: u32 = 1 << 31; - if fifo & VALID_MASK == 0 { - return None; - } + /// Get conversion result from FIFO. + /// + /// Reads and returns the next conversion result from the FIFO. + /// Returns `None` if the FIFO is empty. + /// + /// # Returns + /// - `Some(ConvResult)` if a result is available + /// - `None` if the FIFO is empty + pub fn get_conv_result(&self) -> Option { + let adc = &*I::ptr(); + let fifo = adc.resfifo0().read().bits(); + const VALID_MASK: u32 = 1 << 31; + if fifo & VALID_MASK == 0 { + return None; + } - Some(ConvResult { - command_id_source: (fifo >> 24) & 0x0F, - loop_count_index: (fifo >> 20) & 0x0F, - trigger_id_source: (fifo >> 16) & 0x0F, - conv_value: (fifo & 0xFFFF) as u16, - }) -} + Some(ConvResult { + command_id_source: (fifo >> 24) & 0x0F, + loop_count_index: (fifo >> 20) & 0x0F, + trigger_id_source: (fifo >> 16) & 0x0F, + conv_value: (fifo & 0xFFFF) as u16, + }) + } -pub fn on_interrupt() { - if get_conv_result().is_some() { - INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst); + /// Read ADC value asynchronously. + /// + /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. + /// + /// The function: + /// 1. Enables the FIFO watermark interrupt + /// 2. Triggers a software conversion on trigger 0 + /// 3. Waits for the conversion to complete + /// 4. Returns the conversion result + /// + /// # Returns + /// 16-bit ADC conversion value + pub async fn read(&mut self) -> u16 { + let wait = WAKER.subscribe().await; + + self.enable_interrupt(0x1); + self.do_software_trigger(1); + + let _ = wait.await; + + let result = self.get_conv_result(); + result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT } } -pub struct AdcHandler; -impl crate::interrupt::typelevel::Handler for AdcHandler { +impl Handler for InterruptHandler { unsafe fn on_interrupt() { - on_interrupt(); + T::ptr().ie().modify(|r, w| w.bits(r.bits() & !0x1)); + WAKER.wake(); } } + +mod sealed { + /// Seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for I {} + +trait SealedInstance { + fn ptr() -> &'static pac::adc0::RegisterBlock; +} + + +/// ADC Instance +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + Gate { + /// Interrupt for this ADC instance. + type Interrupt: Interrupt; +} + +macro_rules! impl_instance { + ($($n:expr),*) => { + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + fn ptr() -> &'static pac::adc0::RegisterBlock { + unsafe { &*pac::[]::ptr() } + } + + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + } + } + )* + }; +} + +impl_instance!(0, 1); //ADC2 and ADC3 missing in the PAC ?? + +pub trait AdcPin: GpioPin + sealed::Sealed + PeripheralType { + /// Set the given pin to the correct muxing state + fn mux(&self); +} + +macro_rules! impl_pin { + ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { + impl $trait for crate::peripherals::$pin { + fn mux(&self) { + self.set_pull(crate::gpio::Pull::Disabled); + self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); + self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); + self.set_function(crate::pac::port0::pcr0::Mux::$func); + } + } + }; + } + +impl_pin!(P2_0, ADC0, Mux0, AdcPin); +impl_pin!(P2_4, ADC0, Mux0, AdcPin); +impl_pin!(P2_15, ADC0, Mux0, AdcPin); +impl_pin!(P2_3, ADC0, Mux0, AdcPin); +impl_pin!(P2_2, ADC0, Mux0, AdcPin); +impl_pin!(P2_12, ADC0, Mux0, AdcPin); +impl_pin!(P2_16, ADC0, Mux0, AdcPin); +impl_pin!(P2_7, ADC0, Mux0, AdcPin); +impl_pin!(P0_18, ADC0, Mux0, AdcPin); +impl_pin!(P0_19, ADC0, Mux0, AdcPin); +impl_pin!(P0_20, ADC0, Mux0, AdcPin); +impl_pin!(P0_21, ADC0, Mux0, AdcPin); +impl_pin!(P0_22, ADC0, Mux0, AdcPin); +impl_pin!(P0_23, ADC0, Mux0, AdcPin); +impl_pin!(P0_3, ADC0, Mux0, AdcPin); +impl_pin!(P0_6, ADC0, Mux0, AdcPin); +impl_pin!(P1_0, ADC0, Mux0, AdcPin); +impl_pin!(P1_1, ADC0, Mux0, AdcPin); +impl_pin!(P1_2, ADC0, Mux0, AdcPin); +impl_pin!(P1_3, ADC0, Mux0, AdcPin); +impl_pin!(P1_4, ADC0, Mux0, AdcPin); +impl_pin!(P1_5, ADC0, Mux0, AdcPin); +impl_pin!(P1_6, ADC0, Mux0, AdcPin); +impl_pin!(P1_7, ADC0, Mux0, AdcPin); +impl_pin!(P1_10, ADC0, Mux0, AdcPin); + +impl_pin!(P2_1, ADC1, Mux0, AdcPin); +impl_pin!(P2_5, ADC1, Mux0, AdcPin); +impl_pin!(P2_19, ADC1, Mux0, AdcPin); +impl_pin!(P2_6, ADC1, Mux0, AdcPin); +impl_pin!(P2_3, ADC1, Mux0, AdcPin); +impl_pin!(P2_13, ADC1, Mux0, AdcPin); +impl_pin!(P2_17, ADC1, Mux0, AdcPin); +impl_pin!(P2_7, ADC1, Mux0, AdcPin); +impl_pin!(P1_10, ADC1, Mux0, AdcPin); +impl_pin!(P1_11, ADC1, Mux0, AdcPin); +impl_pin!(P1_12, ADC1, Mux0, AdcPin); +impl_pin!(P1_13, ADC1, Mux0, AdcPin); +impl_pin!(P1_14, ADC1, Mux0, AdcPin); +impl_pin!(P1_15, ADC1, Mux0, AdcPin); +impl_pin!(P1_16, ADC1, Mux0, AdcPin); +impl_pin!(P1_17, ADC1, Mux0, AdcPin); +impl_pin!(P1_18, ADC1, Mux0, AdcPin); +impl_pin!(P1_19, ADC1, Mux0, AdcPin); +impl_pin!(P3_31, ADC1, Mux0, AdcPin); +impl_pin!(P3_30, ADC1, Mux0, AdcPin); +impl_pin!(P3_29, ADC1, Mux0, AdcPin); + +impl_pin!(P2_4, ADC2, Mux0, AdcPin); +impl_pin!(P2_10, ADC2, Mux0, AdcPin); +impl_pin!(P4_4, ADC2, Mux0, AdcPin); +impl_pin!(P2_24, ADC2, Mux0, AdcPin); +impl_pin!(P2_16, ADC2, Mux0, AdcPin); +impl_pin!(P2_12, ADC2, Mux0, AdcPin); +impl_pin!(P2_20, ADC2, Mux0, AdcPin); +impl_pin!(P2_7, ADC2, Mux0, AdcPin); +impl_pin!(P0_2, ADC2, Mux0, AdcPin); +impl_pin!(P0_4, ADC2, Mux0, AdcPin); +impl_pin!(P0_5, ADC2, Mux0, AdcPin); +impl_pin!(P0_6, ADC2, Mux0, AdcPin); +impl_pin!(P0_7, ADC2, Mux0, AdcPin); +impl_pin!(P0_12, ADC2, Mux0, AdcPin); +impl_pin!(P0_13, ADC2, Mux0, AdcPin); +impl_pin!(P0_14, ADC2, Mux0, AdcPin); +impl_pin!(P0_15, ADC2, Mux0, AdcPin); +impl_pin!(P4_0, ADC2, Mux0, AdcPin); +impl_pin!(P4_1, ADC2, Mux0, AdcPin); +impl_pin!(P4_2, ADC2, Mux0, AdcPin); +impl_pin!(P4_3, ADC2, Mux0, AdcPin); +//impl_pin!(P4_4, ADC2, Mux0, AdcPin); // Conflit with ADC2_A3 and ADC2_A20 using the same pin +impl_pin!(P4_5, ADC2, Mux0, AdcPin); +impl_pin!(P4_6, ADC2, Mux0, AdcPin); +impl_pin!(P4_7, ADC2, Mux0, AdcPin); + +impl_pin!(P2_5, ADC3, Mux0, AdcPin); +impl_pin!(P2_11, ADC3, Mux0, AdcPin); +impl_pin!(P2_23, ADC3, Mux0, AdcPin); +impl_pin!(P2_25, ADC3, Mux0, AdcPin); +impl_pin!(P2_17, ADC3, Mux0, AdcPin); +impl_pin!(P2_13, ADC3, Mux0, AdcPin); +impl_pin!(P2_21, ADC3, Mux0, AdcPin); +impl_pin!(P2_7, ADC3, Mux0, AdcPin); +impl_pin!(P3_2, ADC3, Mux0, AdcPin); +impl_pin!(P3_3, ADC3, Mux0, AdcPin); +impl_pin!(P3_4, ADC3, Mux0, AdcPin); +impl_pin!(P3_5, ADC3, Mux0, AdcPin); +impl_pin!(P3_6, ADC3, Mux0, AdcPin); +impl_pin!(P3_7, ADC3, Mux0, AdcPin); +impl_pin!(P3_12, ADC3, Mux0, AdcPin); +impl_pin!(P3_13, ADC3, Mux0, AdcPin); +impl_pin!(P3_14, ADC3, Mux0, AdcPin); +impl_pin!(P3_15, ADC3, Mux0, AdcPin); +impl_pin!(P3_20, ADC3, Mux0, AdcPin); +impl_pin!(P3_21, ADC3, Mux0, AdcPin); +impl_pin!(P3_22, ADC3, Mux0, AdcPin); +impl_pin!(P3_23, ADC3, Mux0, AdcPin); +impl_pin!(P3_24, ADC3, Mux0, AdcPin); +impl_pin!(P3_25, ADC3, Mux0, AdcPin); diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 014a12519..667d79536 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs @@ -945,7 +945,10 @@ pub(crate) mod gate { impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); + impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig); impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); + impl_cc_gate!(ADC2, mrcc_glb_cc1, mrcc_glb_rst1, adc2, AdcConfig); + impl_cc_gate!(ADC3, mrcc_glb_cc1, mrcc_glb_rst1, adc3, AdcConfig); // DMA0 peripheral - uses NoConfig since it has no selectable clock source impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig); diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs index fed5e558e..f2f51c60c 100644 --- a/embassy-mcxa/src/clocks/periph_helpers.rs +++ b/embassy-mcxa/src/clocks/periph_helpers.rs @@ -427,6 +427,7 @@ impl SPConfHelper for OsTimerConfig { /// Selectable clocks for the ADC peripheral #[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AdcClockSel { /// Divided `fro_lf`/`clk_12m`/FRO12M source FroLfDiv, diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs index c960af7a2..7eabc6495 100644 --- a/embassy-mcxa/src/interrupt.rs +++ b/embassy-mcxa/src/interrupt.rs @@ -9,7 +9,10 @@ mod generated { #[rustfmt::skip] embassy_hal_internal::interrupt_mod!( + ADC0, ADC1, + ADC2, + ADC3, DMA_CH0, DMA_CH1, DMA_CH2, @@ -279,11 +282,48 @@ impl InterruptExt for Rtc { cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC) } } +pub struct Adc0; +pub const ADC0: Adc0 = Adc0; -pub struct Adc; -pub const ADC1: Adc = Adc; +impl InterruptExt for Adc0 { + /// Clear any pending ADC0 in NVIC. + #[inline] + fn unpend(&self) { + cortex_m::peripheral::NVIC::unpend(Interrupt::ADC0); + } + + /// Set NVIC priority for ADC0. + #[inline] + fn set_priority(&self, priority: Priority) { + unsafe { + let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; + nvic.set_priority(Interrupt::ADC0, u8::from(priority)); + } + } -impl InterruptExt for Adc { + /// Enable ADC0 in NVIC. + #[inline] + unsafe fn enable(&self) { + cortex_m::peripheral::NVIC::unmask(Interrupt::ADC0); + } + + /// Disable ADC0 in NVIC. + #[inline] + unsafe fn disable(&self) { + cortex_m::peripheral::NVIC::mask(Interrupt::ADC0); + } + + /// Check if ADC0 is pending in NVIC. + #[inline] + fn is_pending(&self) -> bool { + cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC0) + } +} + +pub struct Adc1; +pub const ADC1: Adc1 = Adc1; + +impl InterruptExt for Adc1 { /// Clear any pending ADC1 in NVIC. #[inline] fn unpend(&self) { @@ -318,6 +358,82 @@ impl InterruptExt for Adc { } } +pub struct Adc2; +pub const ADC2: Adc2 = Adc2; + +impl InterruptExt for Adc2 { + /// Clear any pending ADC2 in NVIC. + #[inline] + fn unpend(&self) { + cortex_m::peripheral::NVIC::unpend(Interrupt::ADC2); + } + + /// Set NVIC priority for ADC2. + #[inline] + fn set_priority(&self, priority: Priority) { + unsafe { + let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; + nvic.set_priority(Interrupt::ADC2, u8::from(priority)); + } + } + + /// Enable ADC2 in NVIC. + #[inline] + unsafe fn enable(&self) { + cortex_m::peripheral::NVIC::unmask(Interrupt::ADC2); + } + + /// Disable ADC2 in NVIC. + #[inline] + unsafe fn disable(&self) { + cortex_m::peripheral::NVIC::mask(Interrupt::ADC2); + } + + /// Check if ADC2 is pending in NVIC. + #[inline] + fn is_pending(&self) -> bool { + cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC2) + } +} + +pub struct Adc3; +pub const ADC3: Adc3 = Adc3; + +impl InterruptExt for Adc3 { + /// Clear any pending ADC3 in NVIC. + #[inline] + fn unpend(&self) { + cortex_m::peripheral::NVIC::unpend(Interrupt::ADC3); + } + + /// Set NVIC priority for ADC3. + #[inline] + fn set_priority(&self, priority: Priority) { + unsafe { + let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; + nvic.set_priority(Interrupt::ADC3, u8::from(priority)); + } + } + + /// Enable ADC3 in NVIC. + #[inline] + unsafe fn enable(&self) { + cortex_m::peripheral::NVIC::unmask(Interrupt::ADC3); + } + + /// Disable ADC3 in NVIC. + #[inline] + unsafe fn disable(&self) { + cortex_m::peripheral::NVIC::mask(Interrupt::ADC3); + } + + /// Check if ADC3 is pending in NVIC. + #[inline] + fn is_pending(&self) -> bool { + cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC3) + } +} + pub struct Gpio0; pub const GPIO0: Gpio0 = Gpio0; diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index 1bbdffa06..10b6167b6 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -8,7 +8,6 @@ pub mod clocks; // still provide clock helpers pub mod dma; pub mod gpio; -pub mod pins; // pin mux helpers pub mod adc; pub mod clkout; @@ -26,6 +25,8 @@ pub use crate::pac::NVIC_PRIO_BITS; embassy_hal_internal::peripherals!( ADC0, ADC1, + ADC2, + ADC3, AOI0, AOI1, @@ -336,7 +337,6 @@ embassy_hal_internal::peripherals!( // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. // Re-export interrupt traits and types -pub use adc::Adc1 as Adc1Token; pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; pub use interrupt::InterruptExt; #[cfg(feature = "unstable-pac")] @@ -354,8 +354,14 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps + crate::interrupt::ADC0.set_priority(cfg.adc_interrupt_priority); + // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps + crate::interrupt::ADC2.set_priority(cfg.adc_interrupt_priority); + // Apply user-configured priority early; enabling is left to examples/apps + crate::interrupt::ADC3.set_priority(cfg.adc_interrupt_priority); + // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); // Apply user-configured priority early; enabling is left to examples/apps crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs deleted file mode 100644 index 9adbe64c8..000000000 --- a/embassy-mcxa/src/pins.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Pin configuration helpers (separate from peripheral drivers). -use crate::pac; - -/// Configure pins for ADC usage. -/// -/// # Safety -/// -/// Must be called after PORT clocks are enabled. -pub unsafe fn configure_adc_pins() { - // P1_10 = ADC1_A8 - let port1 = &*pac::Port1::ptr(); - port1.pcr10().write(|w| { - w.ps() - .ps0() - .pe() - .pe0() - .sre() - .sre0() - .ode() - .ode0() - .dse() - .dse0() - .mux() - .mux0() - .ibe() - .ibe0() - .inv() - .inv0() - .lk() - .lk0() - }); - core::arch::asm!("dsb sy; isb sy"); -} diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index c88b1fe8d..9db1173e3 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -2,35 +2,33 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa_examples::init_adc_pins; -use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; +use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy, InterruptHandler}; use hal::clocks::PoweredClock; +use hal::config::Config; +use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; use hal::pac::adc1::tctrl::Tcmd; -use hal::{InterruptExt, bind_interrupts}; +use hal::bind_interrupts; +use hal::peripherals::ADC1; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; bind_interrupts!(struct Irqs { - ADC1 => hal::adc::AdcHandler; + ADC1 => InterruptHandler; }); -#[used] -#[no_mangle] -static KEEP_ADC: unsafe extern "C" fn() = ADC1; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); defmt::info!("ADC interrupt Example"); - unsafe { - init_adc_pins(); - } - let adc_config = LpadcConfig { enable_in_doze_mode: true, conversion_average_mode: CalAvgs::Average128, @@ -46,7 +44,7 @@ async fn main(_spawner: Spawner) { source: AdcClockSel::FroLfDiv, div: Div4::no_div(), }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + let mut adc = Adc::new(p.ADC1, p.P1_10, Irqs, adc_config); adc.do_offset_calibration(); adc.do_auto_calibration(); @@ -63,22 +61,8 @@ async fn main(_spawner: Spawner) { defmt::info!("ADC configuration done..."); - adc.enable_interrupt(0x1); - - unsafe { - hal::interrupt::ADC1.enable(); - } - - unsafe { - cortex_m::interrupt::enable(); - } - loop { - adc.do_software_trigger(1); - while !adc.is_interrupt_triggered() { - // Wait until the interrupt is triggered - } - defmt::info!("*** ADC interrupt TRIGGERED! ***"); - //TBD need to print the value + let value = adc.read().await; + defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); } } diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 07c50f224..65b66c8dd 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -2,9 +2,10 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa_examples::init_adc_pins; -use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; +use hal::adc::{Adc, ConvResult, LpadcConfig, TriggerPriorityPolicy}; use hal::clocks::PoweredClock; +use hal::config::Config; +use hal::clocks::config::Div8; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; @@ -16,11 +17,10 @@ const G_LPADC_RESULT_SHIFT: u32 = 0; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - unsafe { - init_adc_pins(); - } + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); defmt::info!("=== ADC polling Example ==="); @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { source: AdcClockSel::FroLfDiv, div: Div4::no_div(), }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + let adc = Adc::new_polling(p.ADC1, p.P1_10, adc_config); adc.do_offset_calibration(); adc.do_auto_calibration(); @@ -60,7 +60,7 @@ async fn main(_spawner: Spawner) { adc.do_software_trigger(1); let mut result: Option = None; while result.is_none() { - result = hal::adc::get_conv_result(); + result = adc.get_conv_result(); } let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; defmt::info!("value: {=u16}", value); diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs deleted file mode 100644 index 2573a6adc..000000000 --- a/examples/mcxa/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_std] -#![allow(clippy::missing_safety_doc)] - -//! Shared board-specific helpers for the FRDM-MCXA276 examples. -//! These live with the examples so the HAL stays generic. - -use hal::{clocks, pins}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -/// Initialize clocks and pin muxing for ADC. -pub unsafe fn init_adc_pins() { - // NOTE: Lpuart has been updated to properly enable + reset its own clocks. - // GPIO has not. - _ = clocks::enable_and_reset::(&clocks::periph_helpers::NoConfig); - pins::configure_adc_pins(); -} -- cgit