From e0db45736d42ca9b75f4702c9b66646742a50e44 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 10 Dec 2024 08:13:35 +0100 Subject: [PATCH 01/65] Allow delay to be constructed with source (Clock V2) --- hal/src/delay.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hal/src/delay.rs b/hal/src/delay.rs index b683a97c8da9..0ba8b4ac458b 100644 --- a/hal/src/delay.rs +++ b/hal/src/delay.rs @@ -7,6 +7,8 @@ use crate::clock::GenericClockController; use crate::ehal::delay::DelayNs; use crate::ehal_02; use crate::time::Hertz; +use crate::typelevel::Increment; +use crate::clock::v2::Source; /// System timer (SysTick) as a delay provider pub struct Delay { @@ -25,6 +27,18 @@ impl Delay { } } + pub fn new_with_source(mut syst: SYST, source: S) -> (Self, S::Inc) + where S: Source + Increment { + syst.set_clock_source(SystClkSource::Core); + ( + Delay { + syst, + sysclock: source.freq(), + }, + source.inc() + ) + } + /// Releases the system timer (SysTick) resource pub fn free(self) -> SYST { self.syst From 29168c7dd05da67c1a803633221f4d15703449d1 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 10 Dec 2024 08:31:48 +0100 Subject: [PATCH 02/65] Fix compile tests and restrict source to Gclk0 --- hal/src/delay.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/hal/src/delay.rs b/hal/src/delay.rs index 0ba8b4ac458b..cfc423bcf121 100644 --- a/hal/src/delay.rs +++ b/hal/src/delay.rs @@ -1,5 +1,6 @@ //! Delays +use atsamd_hal_macros::hal_cfg; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::SYST; @@ -8,7 +9,11 @@ use crate::ehal::delay::DelayNs; use crate::ehal_02; use crate::time::Hertz; use crate::typelevel::Increment; -use crate::clock::v2::Source; + +#[hal_cfg("rtc-d5x")] +use crate::clock::v2::{ + Source, gclk::Gclk0Id +}; /// System timer (SysTick) as a delay provider pub struct Delay { @@ -27,15 +32,18 @@ impl Delay { } } - pub fn new_with_source(mut syst: SYST, source: S) -> (Self, S::Inc) - where S: Source + Increment { + #[hal_cfg("rtc-d5x")] + /// Configures the system timer (SysTick) as a delay provide, compatible + /// with the V2 clocking API + pub fn new_with_source(mut syst: SYST, gclk0: S) -> (Self, S::Inc) + where S: Source + Increment { syst.set_clock_source(SystClkSource::Core); ( Delay { syst, - sysclock: source.freq(), + sysclock: gclk0.freq(), }, - source.inc() + gclk0.inc() ) } From d8056c162958a4cd7186068cdc2d74df4947a7ae Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 6 Jan 2025 09:13:09 +0000 Subject: [PATCH 03/65] Fix compile for sam51 --- hal/src/delay.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hal/src/delay.rs b/hal/src/delay.rs index cfc423bcf121..35591260280f 100644 --- a/hal/src/delay.rs +++ b/hal/src/delay.rs @@ -8,6 +8,8 @@ use crate::clock::GenericClockController; use crate::ehal::delay::DelayNs; use crate::ehal_02; use crate::time::Hertz; + +#[hal_cfg("rtc-d5x")] use crate::typelevel::Increment; #[hal_cfg("rtc-d5x")] From ca1e48659ca6541588b3b63329c28bc299da8f5d Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 6 Jan 2025 09:26:52 +0000 Subject: [PATCH 04/65] Cargo fmt --- hal/src/delay.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hal/src/delay.rs b/hal/src/delay.rs index 35591260280f..e3bb65d6e32c 100644 --- a/hal/src/delay.rs +++ b/hal/src/delay.rs @@ -13,9 +13,7 @@ use crate::time::Hertz; use crate::typelevel::Increment; #[hal_cfg("rtc-d5x")] -use crate::clock::v2::{ - Source, gclk::Gclk0Id -}; +use crate::clock::v2::{gclk::Gclk0Id, Source}; /// System timer (SysTick) as a delay provider pub struct Delay { @@ -38,14 +36,16 @@ impl Delay { /// Configures the system timer (SysTick) as a delay provide, compatible /// with the V2 clocking API pub fn new_with_source(mut syst: SYST, gclk0: S) -> (Self, S::Inc) - where S: Source + Increment { + where + S: Source + Increment, + { syst.set_clock_source(SystClkSource::Core); ( Delay { syst, sysclock: gclk0.freq(), }, - gclk0.inc() + gclk0.inc(), ) } From 5dd776ba12cc45f71aa9143e03f7d77ce1417b50 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 10 Jan 2025 07:52:50 +0000 Subject: [PATCH 05/65] Add ADC sample settings --- hal/src/peripherals/adc/adc_settings.rs | 159 ++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 hal/src/peripherals/adc/adc_settings.rs diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs new file mode 100644 index 000000000000..ff9af2cfd6bc --- /dev/null +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -0,0 +1,159 @@ + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum AdcSampleCount { + Count1 = 1, + Count2 = 2, + Count4 = 4, + Count8 = 8, + Count16 = 16, + Count32 = 32, + Count64 = 64, + Count128 = 128, + Count256 = 256, + Count512 = 512, + Count1024 = 1024 +} + +#[derive(Copy, Clone)] +pub enum AdcBitWidth { + Eight = 8, + Ten = 10, + Twelve = 12, +} + +/// Result accumulation strategy for the ADC +pub enum AdcAccumulation { + /// The ADC will read once and then the result is ready + Single, + /// The ADC will read [AdcSampleCount] samples, average them out + /// into a 16 bit wide value, and then the result is ready + Average(AdcSampleCount), + /// The ADC will read [AdcSampleCount] samples, sum them + /// into a 16 bit wide value, and then the result is ready + Summed(AdcSampleCount) +} + +#[derive(Copy, Clone)] +pub enum AdcDivider { + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, + Div32 = 32, + Div64 = 64, + Div128 = 128, + Div256 = 256 +} + +/// # ADC sampling rate settings +/// +/// Multiple factors can affect the ADCs overall sampling rate, and this structure +/// allows for the configuring of the majority of factors that affect the sample rate of the ADC +/// +/// To begin with, the ADC Clock is driven by the peripheral clock divided with a divider ([AdcDivider]). +/// +/// Each sample is read by the ADC over [AdcSettingsBuilder::sample_clock_cycles] clock cycles, and then +/// transmitted to the ADC register over [AdcSettingsBuilder::bit_width] clock cycles (1 clock cycle per bit) +/// +/// The ADC can also be configured to combine multiple simultaneous readings in either an average or summed mode +/// (See [AdcAccumulation]), this also affects the overall sample rate of the ADC as the ADC has to do multiple +/// samples before a result is ready. +/// +/// Therefore, the overall formula for calculating Sample rate (SPS) can be calculated like so: +/// +/// ## For single sample +/// ``` +/// SPS = (GCLK_ADC / clk_divider) / (sample_clock_cycles + bit_width) +/// ``` +/// ## For multiple samples (Averaging or Summed) +/// ``` +/// SPS = (GCLK_ADC / clk_divider) / (n * (sample_clock_cycles + bit_width)) +/// ``` +pub struct AdcSettingsBuilder { + pub clk_divider: AdcDivider, + pub sample_clock_cycles: u8, + pub bit_width: AdcBitWidth, + pub accumulation: AdcAccumulation +} + +impl AdcSettingsBuilder { + /// + /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is 48_000_000) using the following settings: + /// * clock divider factor of 32 + /// * 5 clock cycles per sample + /// * 12bit sampling + /// * Single accumulation (No averaging or summing) + /// + pub fn new() -> Self { + Self { + clk_divider: AdcDivider::Div32, + sample_clock_cycles: 5, + bit_width: AdcBitWidth::Twelve, + accumulation: AdcAccumulation::Single + } + } + + /// + /// This setting adjusts the ADC clock frequency by dividing the input clock for the ADC. + /// + /// ## Example: + /// * Input clock 48MHz, div 32 => ADC Clock is 1.5MHz + /// + pub fn clock_divider(mut self, div: AdcDivider) -> Self { + self.clk_divider = div; + self + } + + /// This setting adjusts the bit width of each ADC sample + pub fn sample_bit_width(mut self, bit_width: AdcBitWidth) -> Self { + self.bit_width = bit_width; + self + } + + /// Sets how the ADC will accumulate values before actually returning a value. + /// + /// The default is single (ADC will return a sample as soon as it is measured) + /// + /// Setting [AdcAccumulation::Summed] will make the ADC take 'n' samples, and sum the + /// total before returning it + /// + /// Setting [AdcAccumulation::Average] will make the ADC take 'n' samples, and average the + /// total before returning it + /// + /// NOTE: Selecting [AdcAccumulation::Summed] or [AdcAccumulation::Average] will reduce the overall + /// ADC sample rate by a factor of 1/n, and the returned value will be 16bits long no matter + /// what the sample Bit width was selected as + pub fn accumulation_method(mut self, method: AdcAccumulation) -> Self { + self.accumulation = method; + self + } + + /// This adjusts the number of ADC clock cycles taken to sample a single sample. + /// The higher this number, the longer it will take the ADC to sample each sample. + /// + /// ## Safety + /// Internally, this function will clamp the minimum input value to 1 to avoid 0 + /// + pub fn clock_cycles_per_sample(mut self, num: u8) -> Self { + self.sample_clock_cycles = 1.max(num); // Prevent 0 + self + } + + /// + /// Returns a calculated sample rate of the ADC with these settings + /// + pub fn calculate_sps(&self, clock_freq: u32) -> u32 { + let div = self.clk_divider as u32; + let adc_clk_freq = clock_freq / div; + + let mut clocks_per_sample = self.sample_clock_cycles as u32 + (self.bit_width as u32); + + let multi = match self.accumulation { + AdcAccumulation::Single => 1, + AdcAccumulation::Average(adc_sample_count) => adc_sample_count as u32, + AdcAccumulation::Summed(adc_sample_count) => adc_sample_count as u32, + }; + clocks_per_sample *= multi as u32; + adc_clk_freq / clocks_per_sample + } +} \ No newline at end of file From 53818c722076338085b728614f13a5679f8a9ca4 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 15 Jan 2025 13:49:23 +0000 Subject: [PATCH 06/65] Begin constructing an async ADC concept for D5X --- hal/src/async_hal/interrupts.rs | 4 + hal/src/peripherals/adc/async_api.rs | 37 ++++++++ hal/src/peripherals/adc/d5x.rs | 133 +++++++++++++++++++++++++-- 3 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 hal/src/peripherals/adc/async_api.rs diff --git a/hal/src/async_hal/interrupts.rs b/hal/src/async_hal/interrupts.rs index b9a6f55705a7..89b15ec5a6da 100644 --- a/hal/src/async_hal/interrupts.rs +++ b/hal/src/async_hal/interrupts.rs @@ -199,6 +199,10 @@ seq_macro::seq!(N in 0..= 15 { } }); +// ---------- ADC Interrupt ---------- // +declare_multiple_interrupts!(ADC0: [ADC0_RESRDY, ADC0_OTHER]); +declare_multiple_interrupts!(ADC1: [ADC1_RESRDY, ADC1_OTHER]); + /// An interrupt source that may have one or many interrupt bindings. /// /// This trait may implemented directly when multiple interrupt sources are diff --git a/hal/src/peripherals/adc/async_api.rs b/hal/src/peripherals/adc/async_api.rs new file mode 100644 index 000000000000..5a05f6ce1a8f --- /dev/null +++ b/hal/src/peripherals/adc/async_api.rs @@ -0,0 +1,37 @@ +use core::marker::PhantomData; + +use crate::async_hal::interrupts::Handler; + + +pub(super) mod waker { + use embassy_sync::waitqueue::AtomicWaker; + + #[allow(clippy::declare_interior_mutable_const)] + const NEW_WAKER: AtomicWaker = AtomicWaker::new(); + pub static ADC_WAKERS: [AtomicWaker; super::super::NUM_ADC] = [NEW_WAKER; super::super::NUM_ADC]; +} + +/// Interrupt handler for the ADC peripheral. +pub struct InterruptHandler { + _private: (), + _adc: PhantomData +} + +impl crate::typelevel::Sealed for InterruptHandler{} + +impl Handler for InterruptHandler { + unsafe fn on_interrupt() { + let mut peripherals = unsafe { crate::pac::Peripherals::steal() }; + let adc = A::reg_block(&mut peripherals); + critical_section::with(|_| { + // Just check if result ready is set. Todo - Handle overrun and other interrupt reasons + if adc.intflag().read().resrdy().bit_is_set() { + adc.intflag().modify(|_, w| w.resrdy().set_bit()); + // Wake up! + A::waker().wake(); + } else { + // Handle other cases + } + }) + } +} \ No newline at end of file diff --git a/hal/src/peripherals/adc/d5x.rs b/hal/src/peripherals/adc/d5x.rs index 3a997089ee55..0b8aac38d30d 100644 --- a/hal/src/peripherals/adc/d5x.rs +++ b/hal/src/peripherals/adc/d5x.rs @@ -1,5 +1,11 @@ //! Analogue-to-Digital Conversion +use core::future::poll_fn; +use core::ops::Deref; +use core::task::Poll; + +use adc_settings::AdcSettingsBuilder; use atsamd_hal_macros::hal_cfg; +use atsame51j::Peripherals; use crate::clock::GenericClockController; #[rustfmt::skip] @@ -10,6 +16,10 @@ use crate::pac::gclk::pchctrl::Genselect; use crate::pac::{adc0, Adc0, Adc1, Mclk}; use crate::calibration; +use crate::typelevel::Sealed; + +pub mod async_api; +pub mod adc_settings; /// Samples per reading pub use adc0::avgctrl::Samplenumselect as SampleRate; @@ -20,15 +30,123 @@ pub use adc0::ctrlb::Resselselect as Resolution; /// Reference voltage (or its source) pub use adc0::refctrl::Refselselect as Reference; -/// An ADC where results are accessible via interrupt servicing. -pub struct InterruptAdc -where - C: ConversionMode, -{ - adc: Adc, - m: core::marker::PhantomData, +pub const NUM_ADC: usize = 2; + +pub trait Adc: Sealed + Deref { + /// ADC number + const NUM: usize; + + #[cfg(feature = "async")] + type Interrupt: crate::async_hal::interrupts::InterruptSource; + + /// Get a reference to the Adc from a + /// [`Peripherals`] block + fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock; + + /// Get a reference to this [`Adc`]'s associated RX Waker + #[cfg(feature = "async")] + #[inline] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + &async_api::waker::ADC_WAKERS[Self::NUM] + } +} + +pub struct ADC0 { + adc: Adc0 +} + +impl Sealed for ADC0 {} + +impl Adc for ADC0 { + const NUM: usize = 0; + + type Interrupt = crate::async_hal::interrupts::ADC0; + + fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock { + &peripherals.adc0 + } +} + +impl Deref for ADC0 { + type Target = adc0::RegisterBlock; + + fn deref(&self) -> &Self::Target { + todo!() + } +} + +impl ADC0 { + pub fn new(cfg: AdcSettingsBuilder, adc: Adc0 , mclk: &mut Mclk) -> Self { + Self { + adc: adc + } + } + + fn power_up(&mut self) { + while self.adc.syncbusy().read().enable().bit_is_set() {} + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} + } + + fn power_down(&mut self) { + while self.adc.syncbusy().read().enable().bit_is_set() {} + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} + } + + /// Enables an interrupt when conversion is ready. + fn enable_interrupts(&mut self) { + self.adc.intflag().write(|w| w.resrdy().set_bit()); + self.adc.intenset().write(|w| w.resrdy().set_bit()); + } + + /// Disables the interrupt for when conversion is ready. + fn disable_interrupts(&mut self) { + self.adc.intenclr().write(|w| w.resrdy().set_bit()); + } + + #[inline(always)] + fn start_conversion(&mut self) { + // start conversion + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + // do it again because the datasheet tells us to + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + } + + fn conversion_ready(&mut self) -> Option { + if self.adc.intflag().read().resrdy().bit_is_set() { + self.adc.intflag().write(|w| w.resrdy().set_bit()); + Some(self.adc.result().read().result().bits()) + } else { + None + } + } + + pub async fn oneshot_read>(&mut self, _pin: &mut PIN) -> u16 { + let channel = PIN::channel(); + while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + self.adc.inputctrl().modify(|_, w| unsafe{ w.muxpos().bits(channel) }); + + self.enable_interrupts(); + self.start_conversion(); + let v: u16 = poll_fn(|cx| { + if let Some(r) = self.conversion_ready() { + return Poll::Ready(r); + } + Self::waker().register(cx.waker()); + + if let Some(r) = self.conversion_ready() { + return Poll::Ready(r); + } + Poll::Pending + }).await; + self.disable_interrupts(); + self.power_down(); + v + } } +/* /// `Adc` encapsulates the device ADC pub struct Adc { adc: ADC, @@ -271,6 +389,7 @@ adc_hal! { Adc0: (adc0, apbdmask, adc0_, adc0_biascomp_scale_cal, adc0_biasref_scale_cal, adc0_biasr2r_scale_cal), Adc1: (adc1, apbdmask, adc1_, adc1_biascomp_scale_cal, adc1_biasref_scale_cal, adc1_biasr2r_scale_cal), } +*/ macro_rules! adc_pins { ( From 7e06a82b1f817ddbbcb7c369be6dba4be49d5117 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 20 Jan 2025 16:55:21 +0000 Subject: [PATCH 07/65] Draft up new ADC API --- hal/src/peripherals/adc.rs | 163 ++++++++++++++++++ hal/src/peripherals/adc/d11/mod.rs | 0 hal/src/peripherals/adc/d5x.rs | 1 + hal/src/peripherals/adc/d5x/mod.rs | 1 + hal/src/peripherals/adc/d5x/pin.rs | 119 +++++++++++++ hal/src/peripherals/{adc => }/adc_settings.rs | 0 hal/src/peripherals/mod.rs | 15 +- 7 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 hal/src/peripherals/adc.rs create mode 100644 hal/src/peripherals/adc/d11/mod.rs create mode 100644 hal/src/peripherals/adc/d5x/mod.rs create mode 100644 hal/src/peripherals/adc/d5x/pin.rs rename hal/src/peripherals/{adc => }/adc_settings.rs (100%) diff --git a/hal/src/peripherals/adc.rs b/hal/src/peripherals/adc.rs new file mode 100644 index 000000000000..e9e636974cc7 --- /dev/null +++ b/hal/src/peripherals/adc.rs @@ -0,0 +1,163 @@ +use core::{marker::PhantomData, mem::ManuallyDrop}; + +use atsamd_hal_macros::{hal_cfg, hal_module}; +use atsame51j::Mclk; +use seq_macro::seq; + +use crate::{pac, gpio::AnyPin, typelevel::{NoneT, Sealed}}; + +#[hal_module( + any("adc-d11", "adc-d21") => "adc/d11/mod.rs", + "adc-d5x" => "adc/d5x/mod.rs", +)] +mod impls {} + +pub use crate::adc_settings::*; + +use super::{calibration, clock::Adc0Clock}; + +/// Marker type that represents an ADC channel capable of doing async +/// operations. +#[cfg(feature = "async")] +pub enum AdcFuture {} + +/// Trait representing a GPIO pin which can be used as an input for ADC0 +pub trait Adc0Pin: AnyPin + Sealed { + const CHANNEL: u8; +} + +/// Trait representing a GPIO pin which can be used as an input for ADC1 +pub trait Adc1Pin: AnyPin + Sealed { + const CHANNEL: u8; +} + +pub struct Adc0Channel { + id: u8 +} + +impl Adc0Channel { + + pub fn new(_pin: Id) -> Self { + Self { + id: Id::CHANNEL + } + } + + fn select(&self, adc: &mut Adc0) { + while adc.adc.syncbusy().read().inputctrl().bit_is_set() {} + adc.adc.inputctrl().modify(|_, w| unsafe{ w.muxpos().bits(self.id) }); + } + + pub fn read_blocking(&self, adc: &mut Adc0) -> u16 { + self.select(adc); + adc.power_up(); + adc.start_conversion(); + while adc.adc.intflag().read().resrdy().bit_is_clear() {} + let res = adc.adc.result().read().result().bits(); + adc.power_down(); + res + } + + pub fn read_buffer_blocking(&self, _adc: &mut Adc0, dst: &mut [u16]) { + todo!() + } + + #[cfg(feature="async")] + pub async fn read(&self, _adc: &mut Adc0) -> u16 { + todo!() + } + + #[cfg(feature="async")] + pub async fn read_buffer(&self, _adc: &mut Adc0, dst: &mut [u16]) { + todo!() + } +} + +pub struct Adc0 { + adc: pac::Adc0 +} + +impl Adc0 { + pub fn new(adc0: pac::Adc0, settings: AdcSettingsBuilder, mclk: &mut Mclk, _clock: Adc0Clock) -> Self { + mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); + + // Calibrate and setup the Vref (This is done once) + adc0.calib().write(|w| unsafe { + w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); + w.biasrefbuf().bits(calibration::adc0_biasref_scale_cal()); + w.biasr2r().bits(calibration::adc0_biasr2r_scale_cal()) + }); + + let mut new_adc = Self { + adc: adc0 + }; + + new_adc.configure(settings); + new_adc + } + + pub fn configure(&mut self, settings: AdcSettingsBuilder) { + self.adc.ctrla().modify(|_, w| match settings.clk_divider { + AdcDivider::Div2 => w.prescaler().div2(), + AdcDivider::Div4 => w.prescaler().div4(), + AdcDivider::Div8 => w.prescaler().div8(), + AdcDivider::Div16 => w.prescaler().div16(), + AdcDivider::Div32 => w.prescaler().div32(), + AdcDivider::Div64 => w.prescaler().div64(), + AdcDivider::Div128 => w.prescaler().div128(), + AdcDivider::Div256 => w.prescaler().div256(), + }); + self.adc.ctrlb().modify(|_, w| match settings.bit_width { + AdcBitWidth::Eight => w.ressel()._8bit(), + AdcBitWidth::Ten => w.ressel()._10bit(), + AdcBitWidth::Twelve => w.ressel()._12bit(), + }); + while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + + self.adc.sampctrl().modify(|_, w| unsafe {w.samplen().bits(settings.sample_clock_cycles)}); // sample length + while self.adc.syncbusy().read().sampctrl().bit_is_set() {} + self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) + while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + } + + fn power_up(&mut self) { + while self.adc.syncbusy().read().enable().bit_is_set() {} + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} + } + + fn power_down(&mut self) { + while self.adc.syncbusy().read().enable().bit_is_set() {} + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} + } + + #[inline(always)] + fn start_conversion(&mut self) { + // start conversion + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + // do it again because the datasheet tells us to + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + } + + fn enable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + } + + fn disable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + } + + /// Enables an interrupt when conversion is ready. + fn enable_interrupts(&mut self) { + self.adc.intflag().write(|w| w.resrdy().set_bit()); + self.adc.intenset().write(|w| w.resrdy().set_bit()); + } + + /// Disables the interrupt for when conversion is ready. + fn disable_interrupts(&mut self) { + self.adc.intenclr().write(|w| w.resrdy().set_bit()); + } +} \ No newline at end of file diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/hal/src/peripherals/adc/d5x.rs b/hal/src/peripherals/adc/d5x.rs index 0b8aac38d30d..9797b54567a9 100644 --- a/hal/src/peripherals/adc/d5x.rs +++ b/hal/src/peripherals/adc/d5x.rs @@ -167,6 +167,7 @@ macro_rules! adc_hal { ($($ADC:ident: ($init:ident, $mclk:ident, $apmask:ident, $compcal:ident, $refcal:ident, $r2rcal:ident),)+) => { $( impl Adc<$ADC> { +//Adc0: (adc0, apbdmask, adc0_, adc0_biascomp_scale_cal, adc0_biasref_scale_cal, adc0_biasr2r_scale_cal), pub fn $init(adc: $ADC, mclk: &mut Mclk, clocks: &mut GenericClockController, gclk:Genselect) -> Self { mclk.$mclk().modify(|_, w| w.$apmask().set_bit()); // set to 1/(1/(48000000/32) * 6) = 250000 SPS diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs new file mode 100644 index 000000000000..5725e4f141f0 --- /dev/null +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -0,0 +1 @@ +pub mod pin; \ No newline at end of file diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs new file mode 100644 index 000000000000..056c22b0ebbf --- /dev/null +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -0,0 +1,119 @@ +use atsamd_hal_macros::hal_cfg; +use crate::adc::*; + +use crate::gpio::{ + self, pin::*, PinMode, +}; + +/* +#[allow(unused_macros)] +macro_rules! adc_pins { + ( + $Adc:ident { + $( + $(#[$attr:meta])* + ($PinType:ident, &Channel:literal), + )+ + } + ) => { + + crate::paste::item! { + $( + $(#[$attr])* + impl [<$Adc Pin>] for Pin { + //type ChId = []; + const CHANNEL: u8 = $Channel; + } + )+ + + } + }; +} +*/ + +macro_rules! adc_pins { + ( + $( + $( #[$cfg:meta] )? + $PinId:ident: ($Adc:ident, $CHAN:literal) + ),+ + $(,)? + ) => { + crate::paste::item! { + $( + $( #[$cfg] )? + impl [<$Adc Pin>] for Pin<$PinId, AlternateB> { + const CHANNEL: u8 = $CHAN; + } + )+ + } + }; +} + +adc_pins! { + #[hal_cfg("pa02")] + PA02: (Adc0, 0), + #[hal_cfg("pa03")] + PA03: (Adc0, 1), + #[hal_cfg("pb08")] + PB08: (Adc0, 2), + #[hal_cfg("pb09")] + PB09: (Adc0, 3), + #[hal_cfg("pa04")] + PA04: (Adc0, 4), + #[hal_cfg("pa05")] + PA05: (Adc0, 5), + #[hal_cfg("pa06")] + PA06: (Adc0, 6), + #[hal_cfg("pa07")] + PA07: (Adc0, 7), + #[hal_cfg("pa08")] + PA08: (Adc0, 8), + #[hal_cfg("pa09")] + PA09: (Adc0, 9), + #[hal_cfg("pa10")] + PA10: (Adc0, 10), + #[hal_cfg("pa11")] + PA11: (Adc0, 11), + #[hal_cfg("pb00")] + PB00: (Adc0, 12), + #[hal_cfg("pb01")] + PB01: (Adc0, 13), + #[hal_cfg("pb02")] + PB02: (Adc0, 14), + #[hal_cfg("pb03")] + PB03: (Adc0, 15), + + #[hal_cfg("pb08")] + PB08: (Adc1, 0), + #[hal_cfg("pb09")] + PB09: (Adc1, 1), + #[hal_cfg("pa08")] + PA08: (Adc1, 2), + #[hal_cfg("pa09")] + PA09: (Adc1, 3), + #[hal_cfg("pc02")] + PC02: (Adc1, 4), + #[hal_cfg("pc03")] + PC03: (Adc1, 5), + #[hal_cfg("pb04")] + PB04: (Adc1, 6), + #[hal_cfg("pb05")] + PB05: (Adc1, 7), + #[hal_cfg("pb06")] + PB06: (Adc1, 8), + #[hal_cfg("pb07")] + PB07: (Adc1, 9), + #[hal_cfg("pc00")] + PC00: (Adc1, 10), + #[hal_cfg("pc01")] + PC01: (Adc1, 11), + #[hal_cfg("pc30")] + PC30: (Adc1, 12), + #[hal_cfg("pc31")] + PC31: (Adc1, 13), + #[hal_cfg("pd00")] + PD00: (Adc1, 14), + #[hal_cfg("pd01")] + PD01: (Adc1, 15), +} \ No newline at end of file diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc_settings.rs similarity index 100% rename from hal/src/peripherals/adc/adc_settings.rs rename to hal/src/peripherals/adc_settings.rs diff --git a/hal/src/peripherals/mod.rs b/hal/src/peripherals/mod.rs index 3e31d190e762..62ef4e0c309e 100644 --- a/hal/src/peripherals/mod.rs +++ b/hal/src/peripherals/mod.rs @@ -1,10 +1,15 @@ use atsamd_hal_macros::{hal_cfg, hal_module}; -#[hal_module( - any("adc-d11", "adc-d21") => "adc/d11.rs", - "adc-d5x" => "adc/d5x.rs", -)] -pub mod adc {} +//#[hal_module( +// any("adc-d11", "adc-d21") => "adc/d11.rs", +// "adc-d5x" => "adc/d5x.rs", +//)] +//pub mod adc {} + +#[cfg(feature = "device")] +pub mod adc; +#[cfg(feature = "device")] +pub mod adc_settings; #[hal_module( any("nvmctrl-d11", "nvmctrl-d21") => "calibration/d11.rs", From f2b6cc865ebd03362c0b6c84b344810c16e67aca Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 20 Jan 2025 21:24:23 +0000 Subject: [PATCH 08/65] Test implementation of async read for ADC --- Cargo.toml | 6 +- hal/src/peripherals/adc.rs | 92 ++++++++++++++++++- .../peripherals/adc/{ => d5x}/async_api.rs | 16 ++-- hal/src/peripherals/adc/d5x/mod.rs | 3 +- 4 files changed, 99 insertions(+), 18 deletions(-) rename hal/src/peripherals/adc/{ => d5x}/async_api.rs (61%) diff --git a/Cargo.toml b/Cargo.toml index 6613515bbc01..3db7568625ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,9 @@ resolver = "2" default-members = ["hal"] members = [ "hal", - "atsamd-hal-macros", - "pac/*", - "boards/*", + #"atsamd-hal-macros", + #"pac/*", + #"boards/*", ] [profile.dev] diff --git a/hal/src/peripherals/adc.rs b/hal/src/peripherals/adc.rs index e9e636974cc7..394de668e152 100644 --- a/hal/src/peripherals/adc.rs +++ b/hal/src/peripherals/adc.rs @@ -1,7 +1,7 @@ use core::{marker::PhantomData, mem::ManuallyDrop}; use atsamd_hal_macros::{hal_cfg, hal_module}; -use atsame51j::Mclk; +use atsame51j::{Mclk, Peripherals}; use seq_macro::seq; use crate::{pac, gpio::AnyPin, typelevel::{NoneT, Sealed}}; @@ -10,10 +10,21 @@ use crate::{pac, gpio::AnyPin, typelevel::{NoneT, Sealed}}; any("adc-d11", "adc-d21") => "adc/d11/mod.rs", "adc-d5x" => "adc/d5x/mod.rs", )] -mod impls {} +pub mod impls {} pub use crate::adc_settings::*; +use crate::pac::adc0; + +/// Samples per reading +pub use adc0::avgctrl::Samplenumselect as SampleRate; +/// Clock frequency relative to the system clock +pub use adc0::ctrla::Prescalerselect as Prescaler; +/// Reading resolution in bits +pub use adc0::ctrlb::Resselselect as Resolution; +/// Reference voltage (or its source) +pub use adc0::refctrl::Refselselect as Reference; + use super::{calibration, clock::Adc0Clock}; /// Marker type that represents an ADC channel capable of doing async @@ -52,7 +63,9 @@ impl Adc0Channel { self.select(adc); adc.power_up(); adc.start_conversion(); + adc.enable_interrupts(); while adc.adc.intflag().read().resrdy().bit_is_clear() {} + adc.disable_interrupts(); let res = adc.adc.result().read().result().bits(); adc.power_down(); res @@ -63,8 +76,28 @@ impl Adc0Channel { } #[cfg(feature="async")] - pub async fn read(&self, _adc: &mut Adc0) -> u16 { - todo!() + pub async fn read(&self, adc: &mut Adc0) -> u16 { + use core::{future::poll_fn, task::Poll}; + + self.select(adc); + adc.power_up(); + adc.start_conversion(); + poll_fn(|cx| { + + if adc.is_interrupt() { + let result = adc.adc.result().read().result().bits(); + + adc.power_down(); + adc.disable_interrupts(); + return Poll::Ready(result); + } + + + impls::async_api::ADC_WAKERS[0].register(cx.waker()); + adc.enable_interrupts(); + + Poll::Pending + }).await } #[cfg(feature="async")] @@ -73,6 +106,29 @@ impl Adc0Channel { } } +pub trait Adc { + #[cfg(feature = "async")] + type Interrupt: crate::async_hal::interrupts::InterruptSource; + fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock; + + /// Get a reference to this [`Adc`]'s associated RX Waker + #[cfg(feature = "async")] + #[inline] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; +} + +impl Adc for Adc0 { + type Interrupt = crate::async_hal::interrupts::ADC0; + + fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock { + &peripherals.adc0 + } + + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + &impls::async_api::ADC_WAKERS[0] + } +} + pub struct Adc0 { adc: pac::Adc0 } @@ -80,7 +136,6 @@ pub struct Adc0 { impl Adc0 { pub fn new(adc0: pac::Adc0, settings: AdcSettingsBuilder, mclk: &mut Mclk, _clock: Adc0Clock) -> Self { mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); - // Calibrate and setup the Vref (This is done once) adc0.calib().write(|w| unsafe { w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); @@ -93,6 +148,12 @@ impl Adc0 { }; new_adc.configure(settings); + + new_adc.adc + .refctrl() + .modify(|_, w| w.refsel().variant(Reference::Intref)); + while new_adc.adc.syncbusy().read().refctrl().bit_is_set() {} + new_adc } @@ -118,6 +179,27 @@ impl Adc0 { while self.adc.syncbusy().read().sampctrl().bit_is_set() {} self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + + + use adc0::avgctrl::Samplenumselect; + + match settings.accumulation { + AdcAccumulation::Single => { + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(Samplenumselect::_1); + unsafe { + w.adjres().bits(0) + } + }); + }, + AdcAccumulation::Average(adc_sample_count) => todo!(), + AdcAccumulation::Summed(adc_sample_count) => todo!(), + } + while self.adc.syncbusy().read().avgctrl().bit_is_set() {} + } + + fn is_interrupt(&self) -> bool { + self.adc.intflag().read().resrdy().bit_is_set() } fn power_up(&mut self) { diff --git a/hal/src/peripherals/adc/async_api.rs b/hal/src/peripherals/adc/d5x/async_api.rs similarity index 61% rename from hal/src/peripherals/adc/async_api.rs rename to hal/src/peripherals/adc/d5x/async_api.rs index 5a05f6ce1a8f..d221d9922226 100644 --- a/hal/src/peripherals/adc/async_api.rs +++ b/hal/src/peripherals/adc/d5x/async_api.rs @@ -2,24 +2,22 @@ use core::marker::PhantomData; use crate::async_hal::interrupts::Handler; +use embassy_sync::waitqueue::AtomicWaker; -pub(super) mod waker { - use embassy_sync::waitqueue::AtomicWaker; +#[allow(clippy::declare_interior_mutable_const)] +const NEW_WAKER: AtomicWaker = AtomicWaker::new(); +pub static ADC_WAKERS: [AtomicWaker; 2] = [NEW_WAKER; 2]; - #[allow(clippy::declare_interior_mutable_const)] - const NEW_WAKER: AtomicWaker = AtomicWaker::new(); - pub static ADC_WAKERS: [AtomicWaker; super::super::NUM_ADC] = [NEW_WAKER; super::super::NUM_ADC]; -} /// Interrupt handler for the ADC peripheral. -pub struct InterruptHandler { +pub struct InterruptHandler { _private: (), _adc: PhantomData } -impl crate::typelevel::Sealed for InterruptHandler{} +impl crate::typelevel::Sealed for InterruptHandler{} -impl Handler for InterruptHandler { +impl Handler for InterruptHandler { unsafe fn on_interrupt() { let mut peripherals = unsafe { crate::pac::Peripherals::steal() }; let adc = A::reg_block(&mut peripherals); diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 5725e4f141f0..9436fc928e30 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -1 +1,2 @@ -pub mod pin; \ No newline at end of file +pub mod pin; +pub mod async_api; \ No newline at end of file From 98261d7cc6c83859b50ae70638e2d84e15835441 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 20 Jan 2025 21:25:57 +0000 Subject: [PATCH 09/65] Resolve rest of the adc read future --- hal/src/peripherals/adc.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc.rs b/hal/src/peripherals/adc.rs index 394de668e152..4df5ddd4c381 100644 --- a/hal/src/peripherals/adc.rs +++ b/hal/src/peripherals/adc.rs @@ -96,7 +96,15 @@ impl Adc0Channel { impls::async_api::ADC_WAKERS[0].register(cx.waker()); adc.enable_interrupts(); - Poll::Pending + if adc.is_interrupt() { + let result = adc.adc.result().read().result().bits(); + + adc.power_down(); + adc.disable_interrupts(); + Poll::Ready(result) + } else { + Poll::Pending + } }).await } From afc543000ba062e427b08ca54a77e89f67ca101c Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 21 Jan 2025 13:04:42 +0000 Subject: [PATCH 10/65] Move to type based ADC implementation --- hal/src/peripherals/adc.rs | 253 ------------ hal/src/peripherals/{ => adc}/adc_settings.rs | 0 hal/src/peripherals/adc/d5x/async_api.rs | 10 +- hal/src/peripherals/adc/d5x/pin.rs | 42 +- hal/src/peripherals/adc/mod.rs | 361 ++++++++++++++++++ hal/src/peripherals/mod.rs | 8 - 6 files changed, 375 insertions(+), 299 deletions(-) delete mode 100644 hal/src/peripherals/adc.rs rename hal/src/peripherals/{ => adc}/adc_settings.rs (100%) create mode 100644 hal/src/peripherals/adc/mod.rs diff --git a/hal/src/peripherals/adc.rs b/hal/src/peripherals/adc.rs deleted file mode 100644 index 4df5ddd4c381..000000000000 --- a/hal/src/peripherals/adc.rs +++ /dev/null @@ -1,253 +0,0 @@ -use core::{marker::PhantomData, mem::ManuallyDrop}; - -use atsamd_hal_macros::{hal_cfg, hal_module}; -use atsame51j::{Mclk, Peripherals}; -use seq_macro::seq; - -use crate::{pac, gpio::AnyPin, typelevel::{NoneT, Sealed}}; - -#[hal_module( - any("adc-d11", "adc-d21") => "adc/d11/mod.rs", - "adc-d5x" => "adc/d5x/mod.rs", -)] -pub mod impls {} - -pub use crate::adc_settings::*; - -use crate::pac::adc0; - -/// Samples per reading -pub use adc0::avgctrl::Samplenumselect as SampleRate; -/// Clock frequency relative to the system clock -pub use adc0::ctrla::Prescalerselect as Prescaler; -/// Reading resolution in bits -pub use adc0::ctrlb::Resselselect as Resolution; -/// Reference voltage (or its source) -pub use adc0::refctrl::Refselselect as Reference; - -use super::{calibration, clock::Adc0Clock}; - -/// Marker type that represents an ADC channel capable of doing async -/// operations. -#[cfg(feature = "async")] -pub enum AdcFuture {} - -/// Trait representing a GPIO pin which can be used as an input for ADC0 -pub trait Adc0Pin: AnyPin + Sealed { - const CHANNEL: u8; -} - -/// Trait representing a GPIO pin which can be used as an input for ADC1 -pub trait Adc1Pin: AnyPin + Sealed { - const CHANNEL: u8; -} - -pub struct Adc0Channel { - id: u8 -} - -impl Adc0Channel { - - pub fn new(_pin: Id) -> Self { - Self { - id: Id::CHANNEL - } - } - - fn select(&self, adc: &mut Adc0) { - while adc.adc.syncbusy().read().inputctrl().bit_is_set() {} - adc.adc.inputctrl().modify(|_, w| unsafe{ w.muxpos().bits(self.id) }); - } - - pub fn read_blocking(&self, adc: &mut Adc0) -> u16 { - self.select(adc); - adc.power_up(); - adc.start_conversion(); - adc.enable_interrupts(); - while adc.adc.intflag().read().resrdy().bit_is_clear() {} - adc.disable_interrupts(); - let res = adc.adc.result().read().result().bits(); - adc.power_down(); - res - } - - pub fn read_buffer_blocking(&self, _adc: &mut Adc0, dst: &mut [u16]) { - todo!() - } - - #[cfg(feature="async")] - pub async fn read(&self, adc: &mut Adc0) -> u16 { - use core::{future::poll_fn, task::Poll}; - - self.select(adc); - adc.power_up(); - adc.start_conversion(); - poll_fn(|cx| { - - if adc.is_interrupt() { - let result = adc.adc.result().read().result().bits(); - - adc.power_down(); - adc.disable_interrupts(); - return Poll::Ready(result); - } - - - impls::async_api::ADC_WAKERS[0].register(cx.waker()); - adc.enable_interrupts(); - - if adc.is_interrupt() { - let result = adc.adc.result().read().result().bits(); - - adc.power_down(); - adc.disable_interrupts(); - Poll::Ready(result) - } else { - Poll::Pending - } - }).await - } - - #[cfg(feature="async")] - pub async fn read_buffer(&self, _adc: &mut Adc0, dst: &mut [u16]) { - todo!() - } -} - -pub trait Adc { - #[cfg(feature = "async")] - type Interrupt: crate::async_hal::interrupts::InterruptSource; - fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock; - - /// Get a reference to this [`Adc`]'s associated RX Waker - #[cfg(feature = "async")] - #[inline] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; -} - -impl Adc for Adc0 { - type Interrupt = crate::async_hal::interrupts::ADC0; - - fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock { - &peripherals.adc0 - } - - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - &impls::async_api::ADC_WAKERS[0] - } -} - -pub struct Adc0 { - adc: pac::Adc0 -} - -impl Adc0 { - pub fn new(adc0: pac::Adc0, settings: AdcSettingsBuilder, mclk: &mut Mclk, _clock: Adc0Clock) -> Self { - mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); - // Calibrate and setup the Vref (This is done once) - adc0.calib().write(|w| unsafe { - w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); - w.biasrefbuf().bits(calibration::adc0_biasref_scale_cal()); - w.biasr2r().bits(calibration::adc0_biasr2r_scale_cal()) - }); - - let mut new_adc = Self { - adc: adc0 - }; - - new_adc.configure(settings); - - new_adc.adc - .refctrl() - .modify(|_, w| w.refsel().variant(Reference::Intref)); - while new_adc.adc.syncbusy().read().refctrl().bit_is_set() {} - - new_adc - } - - pub fn configure(&mut self, settings: AdcSettingsBuilder) { - self.adc.ctrla().modify(|_, w| match settings.clk_divider { - AdcDivider::Div2 => w.prescaler().div2(), - AdcDivider::Div4 => w.prescaler().div4(), - AdcDivider::Div8 => w.prescaler().div8(), - AdcDivider::Div16 => w.prescaler().div16(), - AdcDivider::Div32 => w.prescaler().div32(), - AdcDivider::Div64 => w.prescaler().div64(), - AdcDivider::Div128 => w.prescaler().div128(), - AdcDivider::Div256 => w.prescaler().div256(), - }); - self.adc.ctrlb().modify(|_, w| match settings.bit_width { - AdcBitWidth::Eight => w.ressel()._8bit(), - AdcBitWidth::Ten => w.ressel()._10bit(), - AdcBitWidth::Twelve => w.ressel()._12bit(), - }); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} - - self.adc.sampctrl().modify(|_, w| unsafe {w.samplen().bits(settings.sample_clock_cycles)}); // sample length - while self.adc.syncbusy().read().sampctrl().bit_is_set() {} - self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) - while self.adc.syncbusy().read().inputctrl().bit_is_set() {} - - - use adc0::avgctrl::Samplenumselect; - - match settings.accumulation { - AdcAccumulation::Single => { - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(Samplenumselect::_1); - unsafe { - w.adjres().bits(0) - } - }); - }, - AdcAccumulation::Average(adc_sample_count) => todo!(), - AdcAccumulation::Summed(adc_sample_count) => todo!(), - } - while self.adc.syncbusy().read().avgctrl().bit_is_set() {} - } - - fn is_interrupt(&self) -> bool { - self.adc.intflag().read().resrdy().bit_is_set() - } - - fn power_up(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} - } - - fn power_down(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} - } - - #[inline(always)] - fn start_conversion(&mut self) { - // start conversion - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - // do it again because the datasheet tells us to - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - } - - fn enable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} - } - - fn disable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} - } - - /// Enables an interrupt when conversion is ready. - fn enable_interrupts(&mut self) { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - self.adc.intenset().write(|w| w.resrdy().set_bit()); - } - - /// Disables the interrupt for when conversion is ready. - fn disable_interrupts(&mut self) { - self.adc.intenclr().write(|w| w.resrdy().set_bit()); - } -} \ No newline at end of file diff --git a/hal/src/peripherals/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs similarity index 100% rename from hal/src/peripherals/adc_settings.rs rename to hal/src/peripherals/adc/adc_settings.rs diff --git a/hal/src/peripherals/adc/d5x/async_api.rs b/hal/src/peripherals/adc/d5x/async_api.rs index d221d9922226..f86a49e33cea 100644 --- a/hal/src/peripherals/adc/d5x/async_api.rs +++ b/hal/src/peripherals/adc/d5x/async_api.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use crate::async_hal::interrupts::Handler; +use crate::{adc::AdcInstance, async_hal::interrupts::Handler}; use embassy_sync::waitqueue::AtomicWaker; @@ -10,17 +10,17 @@ pub static ADC_WAKERS: [AtomicWaker; 2] = [NEW_WAKER; 2]; /// Interrupt handler for the ADC peripheral. -pub struct InterruptHandler { +pub struct InterruptHandler { _private: (), _adc: PhantomData } -impl crate::typelevel::Sealed for InterruptHandler{} +impl crate::typelevel::Sealed for InterruptHandler{} -impl Handler for InterruptHandler { +impl Handler for InterruptHandler { unsafe fn on_interrupt() { let mut peripherals = unsafe { crate::pac::Peripherals::steal() }; - let adc = A::reg_block(&mut peripherals); + let adc = A::peripheral_reg_block(&mut peripherals); critical_section::with(|_| { // Just check if result ready is set. Todo - Handle overrun and other interrupt reasons if adc.intflag().read().resrdy().bit_is_set() { diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs index 056c22b0ebbf..d68f56f906a6 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -1,35 +1,7 @@ -use atsamd_hal_macros::hal_cfg; use crate::adc::*; +use atsamd_hal_macros::hal_cfg; -use crate::gpio::{ - self, pin::*, PinMode, -}; - -/* -#[allow(unused_macros)] -macro_rules! adc_pins { - ( - $Adc:ident { - $( - $(#[$attr:meta])* - ($PinType:ident, &Channel:literal), - )+ - } - ) => { - - crate::paste::item! { - $( - $(#[$attr])* - impl [<$Adc Pin>] for Pin { - //type ChId = []; - const CHANNEL: u8 = $Channel; - } - )+ - - } - }; -} -*/ +use crate::gpio::{self, pin::*, PinMode}; macro_rules! adc_pins { ( @@ -42,8 +14,12 @@ macro_rules! adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl [<$Adc Pin>] for Pin<$PinId, AlternateB> { - const CHANNEL: u8 = $CHAN; + impl AdcPin<[<$Adc>], []>for Pin<$PinId, AlternateB> { + type Configured = Self; + + fn into_function(self) -> Self::Configured { + self + } } )+ } @@ -116,4 +92,4 @@ adc_pins! { PD00: (Adc1, 14), #[hal_cfg("pd01")] PD01: (Adc1, 15), -} \ No newline at end of file +} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs new file mode 100644 index 000000000000..994ddb4c11a6 --- /dev/null +++ b/hal/src/peripherals/adc/mod.rs @@ -0,0 +1,361 @@ +use core::{marker::PhantomData, ops::Deref}; + +use atsamd_hal_macros::{hal_cfg, hal_module}; +use atsame51j::Peripherals; +use pac::Mclk; +use seq_macro::seq; + +use crate::{ + gpio::AnyPin, + pac, + typelevel::{NoneT, Sealed}, +}; + +#[hal_module( + any("adc-d11", "adc-d21") => "d11/mod.rs", + "adc-d5x" => "d5x/mod.rs", +)] +mod impls {} +mod adc_settings; + +pub use adc_settings::*; + +use super::{ + calibration, + clock::{self, Adc0Clock}, +}; + +/// Marker type that represents an ADC channel capable of doing async +/// operations. +#[cfg(feature = "async")] +pub enum AdcFuture {} + +/// Trait representing an ADC instance +pub trait AdcInstance { + #[cfg(feature = "async")] + type Interrupt: crate::async_hal::interrupts::InterruptSource; + + // The Adc0 and Adc1 PAC types implement Deref + type Instance: Deref; + type Clock; + + fn reg_block(&self) -> &pac::adc0::RegisterBlock; + fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock; + fn enable_mclk(mclk: &mut Mclk); + fn calibrate(instance: &Self::Instance); + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; +} + +// TODO: The next few lines will need to be adjusted for SAMD11 and SAMD21: they only have 1 ADC +pub struct Adc0 { + adc: pac::Adc0, +} +impl AdcInstance for Adc0 { + type Instance = pac::Adc0; + type Clock = clock::Adc0Clock; + + #[cfg(feature = "async")] + type Interrupt = crate::async_hal::interrupts::ADC0; + + fn reg_block(&self) -> &pac::adc0::RegisterBlock { + &self.adc + } + + fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { + &p.adc0 + } + + fn enable_mclk(mclk: &mut Mclk) { + mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); + } + + fn calibrate(instance: &Self::Instance) { + instance.calib().write(|w| unsafe { + w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); + w.biasrefbuf().bits(calibration::adc0_biasref_scale_cal()); + w.biasr2r().bits(calibration::adc0_biasr2r_scale_cal()) + }); + } + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + &impls::async_api::ADC_WAKERS[0] + } +} + +pub struct Adc1 { + adc: pac::Adc1, +} + +impl AdcInstance for Adc1 { + type Instance = pac::Adc1; + type Clock = clock::Adc1Clock; + + #[cfg(feature = "async")] + type Interrupt = crate::async_hal::interrupts::ADC1; + + fn reg_block(&self) -> &pac::adc0::RegisterBlock { + &self.adc + } + + fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { + &p.adc1 + } + + fn enable_mclk(mclk: &mut Mclk) { + mclk.apbdmask().modify(|_, w| w.adc1_().set_bit()); + } + fn calibrate(instance: &Self::Instance) { + instance.calib().write(|w| unsafe { + w.biascomp().bits(calibration::adc1_biascomp_scale_cal()); + w.biasrefbuf().bits(calibration::adc1_biasref_scale_cal()); + w.biasr2r().bits(calibration::adc1_biasr2r_scale_cal()) + }); + } + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + &impls::async_api::ADC_WAKERS[1] + } +} + +/// Trait representing a GPIO pin which can be used as an input for an ADC +pub trait AdcPin: AnyPin + Sealed +where + I: AdcInstance, + C: ChId, +{ + type Configured; + + fn into_function(self) -> Self::Configured; +} + +/// Trait representing an ADC channel ID. +pub trait ChId { + const ID: u8; +} + +/// ADC channel. +/// +/// This struct must hold a concrete [`Pin`](crate::gpio::Pin) which implements +/// [`AdcPin`] in order to perform conversions. By default, channels don't hold any pin when they are created by [`Adc::new`]. Use [`Channel::with_pin`](Self::with_pin) to give a pin to this [`Channel`]. +pub struct Channel { + _pin: P, + _instance: PhantomData, + _id: PhantomData, +} + +// These methods are only implemented for a Channel that doesn't hold a pin yet +impl Channel { + // NOTE: `new`` must be private so a channel isn't accidentally created outside this + // module, breaking the typelevel guarantees laid out by the adc driver + fn new() -> Channel { + Channel { + _pin: NoneT, + _instance: PhantomData, + _id: PhantomData, + } + } + + /// Give a concrete pin to this [`Channel`], which will be used by the ADC + /// to measure voltage. + /// + /// This methods accepts any pin that can potentially be configured as an + /// ADC channel, and automatically puts it in the Alternate B mode. + pub fn with_pin>(self, pin: N) -> Channel { + // NOTE: While AdcPin is implemented for any pin that has the *potential* to be + // turned into an AlternateB pin (which is the ADC function), we know that any + // Channel holding a type implementing AdcPin must have already configured the + // pin to the alternate B function, since the with_pin method is the only way to insert a + // pin into the Channel. + Channel { + _pin: pin.into_function(), + _instance: PhantomData, + _id: PhantomData, + } + } +} + +// These methods are only implemented for a Channel that holds a configured pin +impl> Channel { + pub fn read_blocking(&self, adc: &mut Adc) -> u16 { + self.select(adc); + adc.power_up(); + adc.start_conversion(); + while adc.adc.intflag().read().resrdy().bit_is_clear() {} + let res = adc.adc.result().read().result().bits(); + adc.power_down(); + res + } + + pub fn read_buffer_blocking(&self, _adc: &mut Adc, dst: &mut [u16]) { + todo!() + } + + #[cfg(feature = "async")] + pub async fn read(&self, _adc: &mut Adc) -> u16 { + todo!() + } + + #[cfg(feature = "async")] + pub async fn read_buffer(&self, _adc: &mut Adc, dst: &mut [u16]) { + todo!() + } + + fn select(&self, adc: &mut Adc) { + let adc = adc.reg_block(); + + while adc.syncbusy().read().inputctrl().bit_is_set() {} + adc.inputctrl() + .modify(|_, w| unsafe { w.muxpos().bits(Id::ID) }); + } +} + +pub struct Adc { + adc: I::Instance, +} + +impl Adc { + pub fn new( + adc: I::Instance, + settings: AdcSettingsBuilder, + mclk: &mut Mclk, + _clock: I::Clock, + ) -> (Self, Channels) { + I::enable_mclk(mclk); + // Calibrate and setup the Vref (This is done once) + let mut new_adc = Self { adc }; + + new_adc.configure(settings); + I::calibrate(&new_adc.adc); + + (new_adc, Channels::new()) + } + + pub fn configure(&mut self, settings: AdcSettingsBuilder) { + self.adc.ctrla().modify(|_, w| match settings.clk_divider { + AdcDivider::Div2 => w.prescaler().div2(), + AdcDivider::Div4 => w.prescaler().div4(), + AdcDivider::Div8 => w.prescaler().div8(), + AdcDivider::Div16 => w.prescaler().div16(), + AdcDivider::Div32 => w.prescaler().div32(), + AdcDivider::Div64 => w.prescaler().div64(), + AdcDivider::Div128 => w.prescaler().div128(), + AdcDivider::Div256 => w.prescaler().div256(), + }); + self.adc.ctrlb().modify(|_, w| match settings.bit_width { + AdcBitWidth::Eight => w.ressel()._8bit(), + AdcBitWidth::Ten => w.ressel()._10bit(), + AdcBitWidth::Twelve => w.ressel()._12bit(), + }); + while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + + self.adc + .sampctrl() + .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length + while self.adc.syncbusy().read().sampctrl().bit_is_set() {} + self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) + while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + } + + /// Access the struct's underlying PAC object + fn reg_block(&self) -> &pac::adc0::RegisterBlock { + &self.adc + } + + fn power_up(&mut self) { + while self.adc.syncbusy().read().enable().bit_is_set() {} + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} + } + + fn power_down(&mut self) { + while self.adc.syncbusy().read().enable().bit_is_set() {} + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} + } + + #[inline(always)] + fn start_conversion(&mut self) { + // start conversion + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + // do it again because the datasheet tells us to + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + } + + fn enable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + } + + fn disable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + } + + /// Enables an interrupt when conversion is ready. + fn enable_interrupts(&mut self) { + self.adc.intflag().write(|w| w.resrdy().set_bit()); + self.adc.intenset().write(|w| w.resrdy().set_bit()); + } + + /// Disables the interrupt for when conversion is ready. + fn disable_interrupts(&mut self) { + self.adc.intenclr().write(|w| w.resrdy().set_bit()); + } +} + +// Here I declare the number of channels for the SAME51 ADC +// TODO: declare channels for other chip variants +#[hal_cfg(any("eic-d5x"))] +macro_rules! with_num_channels { + ($some_macro:ident) => { + $some_macro! {16} + }; +} + +/// Get the number of channels as a literal +macro_rules! get { + ($literal:literal) => { + $literal + }; +} + +/// The number of ADC channels per instance on this chip. +pub const NUM_CHANNELS: usize = with_num_channels!(get); + +macro_rules! define_channels_struct { + ($num_channels:literal) => { + seq!(N in 0..$num_channels { + #( + /// Type alias for a channel number + pub enum Ch~N {} + + impl ChId for Ch~N { + const ID: u8 = N; + } + )* + + /// Struct generating individual handles to each ADC channel + pub struct Channels( + #( + pub Channel, + )* + ); + + impl Channels { + fn new() -> Self { + Self ( + #( + Channel::new(), + )* + ) + } + } + }); + }; +} + +with_num_channels!(define_channels_struct); diff --git a/hal/src/peripherals/mod.rs b/hal/src/peripherals/mod.rs index 62ef4e0c309e..78eb4048a08c 100644 --- a/hal/src/peripherals/mod.rs +++ b/hal/src/peripherals/mod.rs @@ -1,15 +1,7 @@ use atsamd_hal_macros::{hal_cfg, hal_module}; -//#[hal_module( -// any("adc-d11", "adc-d21") => "adc/d11.rs", -// "adc-d5x" => "adc/d5x.rs", -//)] -//pub mod adc {} - #[cfg(feature = "device")] pub mod adc; -#[cfg(feature = "device")] -pub mod adc_settings; #[hal_module( any("nvmctrl-d11", "nvmctrl-d21") => "calibration/d11.rs", From 7c76cb63fff18f1fd4e9ee22d132279d1422b536 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 21 Jan 2025 21:00:27 +0000 Subject: [PATCH 11/65] Broken read_blocking implementation --- hal/src/peripherals/adc/d5x.rs | 23 ---- hal/src/peripherals/adc/d5x/async_api.rs | 8 +- hal/src/peripherals/adc/mod.rs | 165 ++++++++++++++++++----- 3 files changed, 134 insertions(+), 62 deletions(-) diff --git a/hal/src/peripherals/adc/d5x.rs b/hal/src/peripherals/adc/d5x.rs index 9797b54567a9..0f6501e081b7 100644 --- a/hal/src/peripherals/adc/d5x.rs +++ b/hal/src/peripherals/adc/d5x.rs @@ -121,29 +121,6 @@ impl ADC0 { None } } - - pub async fn oneshot_read>(&mut self, _pin: &mut PIN) -> u16 { - let channel = PIN::channel(); - while self.adc.syncbusy().read().inputctrl().bit_is_set() {} - self.adc.inputctrl().modify(|_, w| unsafe{ w.muxpos().bits(channel) }); - - self.enable_interrupts(); - self.start_conversion(); - let v: u16 = poll_fn(|cx| { - if let Some(r) = self.conversion_ready() { - return Poll::Ready(r); - } - Self::waker().register(cx.waker()); - - if let Some(r) = self.conversion_ready() { - return Poll::Ready(r); - } - Poll::Pending - }).await; - self.disable_interrupts(); - self.power_down(); - v - } } /* diff --git a/hal/src/peripherals/adc/d5x/async_api.rs b/hal/src/peripherals/adc/d5x/async_api.rs index f86a49e33cea..55ec450024ab 100644 --- a/hal/src/peripherals/adc/d5x/async_api.rs +++ b/hal/src/peripherals/adc/d5x/async_api.rs @@ -8,14 +8,13 @@ use embassy_sync::waitqueue::AtomicWaker; const NEW_WAKER: AtomicWaker = AtomicWaker::new(); pub static ADC_WAKERS: [AtomicWaker; 2] = [NEW_WAKER; 2]; - /// Interrupt handler for the ADC peripheral. pub struct InterruptHandler { _private: (), - _adc: PhantomData + _adc: PhantomData, } -impl crate::typelevel::Sealed for InterruptHandler{} +impl crate::typelevel::Sealed for InterruptHandler {} impl Handler for InterruptHandler { unsafe fn on_interrupt() { @@ -24,7 +23,6 @@ impl Handler for InterruptHandler { critical_section::with(|_| { // Just check if result ready is set. Todo - Handle overrun and other interrupt reasons if adc.intflag().read().resrdy().bit_is_set() { - adc.intflag().modify(|_, w| w.resrdy().set_bit()); // Wake up! A::waker().wake(); } else { @@ -32,4 +30,4 @@ impl Handler for InterruptHandler { } }) } -} \ No newline at end of file +} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 994ddb4c11a6..d37b7e64fba6 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -25,6 +25,20 @@ use super::{ clock::{self, Adc0Clock}, }; +use crate::pac::adc0; + +pub use adc0::avgctrl::Samplenumselect; +/// Samples per reading +pub use adc0::avgctrl::Samplenumselect as SampleRate; +/// Clock frequency relative to the system clock +pub use adc0::ctrla::Prescalerselect as Prescaler; +/// Reading resolution in bits +pub use adc0::ctrlb::Resselselect as Resolution; +/// Reference voltage (or its source) +pub use adc0::refctrl::Refselselect as Reference; + +pub use impls::async_api::InterruptHandler as AdcInterruptHandler; + /// Marker type that represents an ADC channel capable of doing async /// operations. #[cfg(feature = "async")] @@ -39,7 +53,6 @@ pub trait AdcInstance { type Instance: Deref; type Clock; - fn reg_block(&self) -> &pac::adc0::RegisterBlock; fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock; fn enable_mclk(mclk: &mut Mclk); fn calibrate(instance: &Self::Instance); @@ -59,10 +72,6 @@ impl AdcInstance for Adc0 { #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC0; - fn reg_block(&self) -> &pac::adc0::RegisterBlock { - &self.adc - } - fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { &p.adc0 } @@ -96,10 +105,6 @@ impl AdcInstance for Adc1 { #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC1; - fn reg_block(&self) -> &pac::adc0::RegisterBlock { - &self.adc - } - fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { &p.adc1 } @@ -180,37 +185,26 @@ impl Channel { // These methods are only implemented for a Channel that holds a configured pin impl> Channel { - pub fn read_blocking(&self, adc: &mut Adc) -> u16 { - self.select(adc); - adc.power_up(); - adc.start_conversion(); - while adc.adc.intflag().read().resrdy().bit_is_clear() {} - let res = adc.adc.result().read().result().bits(); - adc.power_down(); - res + #[inline(always)] + pub fn read_blocking(&self, adc: &mut Adc, f: F) -> u16 { + //f(Id::ID as u16); + adc.read_blocking(Id::ID, f) } - pub fn read_buffer_blocking(&self, _adc: &mut Adc, dst: &mut [u16]) { + pub fn read_buffer_blocking(&self, adc: &mut Adc, dst: &mut [u16]) { + //adc.read_buffer_blocking(Id::ID) todo!() } #[cfg(feature = "async")] - pub async fn read(&self, _adc: &mut Adc) -> u16 { - todo!() + pub async fn read(&self, adc: &mut Adc) -> u16 { + adc.read(Id::ID).await } #[cfg(feature = "async")] pub async fn read_buffer(&self, _adc: &mut Adc, dst: &mut [u16]) { todo!() } - - fn select(&self, adc: &mut Adc) { - let adc = adc.reg_block(); - - while adc.syncbusy().read().inputctrl().bit_is_set() {} - adc.inputctrl() - .modify(|_, w| unsafe { w.muxpos().bits(Id::ID) }); - } } pub struct Adc { @@ -225,16 +219,25 @@ impl Adc { _clock: I::Clock, ) -> (Self, Channels) { I::enable_mclk(mclk); + + // Reset ADC here as we cannot guarantee its state + // This also disables the ADC + //adc.ctrla().modify(|_, w| w.swrst().set_bit()); + //while adc.syncbusy().read().swrst().bit_is_set() {} // Calibrate and setup the Vref (This is done once) let mut new_adc = Self { adc }; - new_adc.configure(settings); - I::calibrate(&new_adc.adc); - + //I::calibrate(&new_adc.adc); + //new_adc.configure(settings); + //new_adc.power_down(); // Make sure ADC is offline (new_adc, Channels::new()) } pub fn configure(&mut self, settings: AdcSettingsBuilder) { + /* + // Disable ADC before we do anything! + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); + while self.adc.syncbusy().read().enable().bit_is_set() {} self.adc.ctrla().modify(|_, w| match settings.clk_divider { AdcDivider::Div2 => w.prescaler().div2(), AdcDivider::Div4 => w.prescaler().div4(), @@ -254,10 +257,22 @@ impl Adc { self.adc .sampctrl() - .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length + .modify(|_, w| unsafe { w.samplen().bits(0) }); // sample length while self.adc.syncbusy().read().sampctrl().bit_is_set() {} self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(Samplenumselect::_1); + unsafe { w.adjres().bits(0) } + }); + while self.adc.syncbusy().read().avgctrl().bit_is_set() {} + + self.adc + .refctrl() + .modify(|_, w| w.refsel().variant(Reference::Intref)); + while self.adc.syncbusy().read().refctrl().bit_is_set() {} + */ } /// Access the struct's underlying PAC object @@ -265,12 +280,19 @@ impl Adc { &self.adc } + #[inline(always)] + fn sync(&self) { + while self.adc.syncbusy().read().bits() != 0 {} + } + + #[inline(always)] fn power_up(&mut self) { while self.adc.syncbusy().read().enable().bit_is_set() {} self.adc.ctrla().modify(|_, w| w.enable().set_bit()); while self.adc.syncbusy().read().enable().bit_is_set() {} } + #[inline(always)] fn power_down(&mut self) { while self.adc.syncbusy().read().enable().bit_is_set() {} self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); @@ -281,7 +303,8 @@ impl Adc { fn start_conversion(&mut self) { // start conversion self.adc.swtrig().modify(|_, w| w.start().set_bit()); - // do it again because the datasheet tells us to + while self.adc.syncbusy().read().swtrig().bit_is_set() {} + //// do it again because the datasheet tells us to self.adc.swtrig().modify(|_, w| w.start().set_bit()); } @@ -305,11 +328,85 @@ impl Adc { fn disable_interrupts(&mut self) { self.adc.intenclr().write(|w| w.resrdy().set_bit()); } + + fn interrupt_result_ready(&self) -> Option { + if self.adc.intflag().read().resrdy().bit_is_set() { + self.adc.intflag().write(|w| w.resrdy().set_bit()); + Some(self.adc.result().read().result().bits()) + } else { + None + } + } + + fn mux(&mut self, ch: u8) { + while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + self.adc + .inputctrl() + .modify(|_, w| unsafe { w.muxpos().bits(ch) }); + while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + } + + #[inline(always)] + pub fn read_blocking(&mut self, ch: u8, f: F) -> u16 { + // Manually testing things to see why ADC does not + // function + while self.adc.syncbusy().read().bits() != 0 {} + self.mux(ch); + while self.adc.syncbusy().read().bits() != 0 {} + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); + for _ in 0..99 {cortex_m::asm::nop();} + while self.adc.syncbusy().read().enable().bit_is_set(){ + core::hint::spin_loop(); + }; + + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + while self.adc.intflag().read().resrdy().bit_is_clear() { + core::hint::spin_loop(); + } + let res = self.adc.result().read().bits(); + self.power_down(); + res + } + + #[cfg(feature = "async")] + async fn read(&mut self, ch: u8) -> u16 { + use core::{future::poll_fn, task::Poll}; + self.mux(ch); + self.enable_interrupts(); + self.power_up(); + self.start_conversion(); + let result = poll_fn(|cx| { + if let Some(v) = self.interrupt_result_ready() { + self.disable_interrupts(); + return Poll::Ready(v); + } + + I::waker().register(cx.waker()); + self.enable_interrupts(); + if let Some(v) = self.interrupt_result_ready() { + self.disable_interrupts(); + return Poll::Ready(v); + } + + Poll::Pending + }) + .await; + self.power_down(); + result + } + + pub fn read_ptat_blocking(&mut self) -> u16 { + self.read_blocking(0x19, |_| {}) + } + + pub fn read_ctat_blocking(&mut self) -> u16 { + self.read_blocking(0x1A, |_| {}) + } } // Here I declare the number of channels for the SAME51 ADC // TODO: declare channels for other chip variants -#[hal_cfg(any("eic-d5x"))] +#[hal_cfg(any("adc-d5x"))] macro_rules! with_num_channels { ($some_macro:ident) => { $some_macro! {16} From 282c02e02dc5cfa3bdcc6a22e0db0c96eae9ab49 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 12:58:35 +0000 Subject: [PATCH 12/65] Begin implementing async reading ADC --- hal/src/peripherals/adc/adc_settings.rs | 55 +++++----- hal/src/peripherals/adc/d5x/pin.rs | 3 +- hal/src/peripherals/adc/mod.rs | 140 ++++++++++++++---------- 3 files changed, 113 insertions(+), 85 deletions(-) diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs index ff9af2cfd6bc..f32cb6da0ed9 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -1,4 +1,3 @@ - #[derive(Copy, Clone, PartialEq, Eq)] pub enum AdcSampleCount { Count1 = 1, @@ -11,7 +10,7 @@ pub enum AdcSampleCount { Count128 = 128, Count256 = 256, Count512 = 512, - Count1024 = 1024 + Count1024 = 1024, } #[derive(Copy, Clone)] @@ -30,7 +29,7 @@ pub enum AdcAccumulation { Average(AdcSampleCount), /// The ADC will read [AdcSampleCount] samples, sum them /// into a 16 bit wide value, and then the result is ready - Summed(AdcSampleCount) + Summed(AdcSampleCount), } #[derive(Copy, Clone)] @@ -42,25 +41,25 @@ pub enum AdcDivider { Div32 = 32, Div64 = 64, Div128 = 128, - Div256 = 256 + Div256 = 256, } /// # ADC sampling rate settings -/// +/// /// Multiple factors can affect the ADCs overall sampling rate, and this structure /// allows for the configuring of the majority of factors that affect the sample rate of the ADC -/// +/// /// To begin with, the ADC Clock is driven by the peripheral clock divided with a divider ([AdcDivider]). -/// +/// /// Each sample is read by the ADC over [AdcSettingsBuilder::sample_clock_cycles] clock cycles, and then /// transmitted to the ADC register over [AdcSettingsBuilder::bit_width] clock cycles (1 clock cycle per bit) -/// +/// /// The ADC can also be configured to combine multiple simultaneous readings in either an average or summed mode /// (See [AdcAccumulation]), this also affects the overall sample rate of the ADC as the ADC has to do multiple /// samples before a result is ready. -/// +/// /// Therefore, the overall formula for calculating Sample rate (SPS) can be calculated like so: -/// +/// /// ## For single sample /// ``` /// SPS = (GCLK_ADC / clk_divider) / (sample_clock_cycles + bit_width) @@ -73,32 +72,32 @@ pub struct AdcSettingsBuilder { pub clk_divider: AdcDivider, pub sample_clock_cycles: u8, pub bit_width: AdcBitWidth, - pub accumulation: AdcAccumulation + pub accumulation: AdcAccumulation, } impl AdcSettingsBuilder { - /// + /// /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is 48_000_000) using the following settings: /// * clock divider factor of 32 /// * 5 clock cycles per sample /// * 12bit sampling /// * Single accumulation (No averaging or summing) - /// + /// pub fn new() -> Self { Self { clk_divider: AdcDivider::Div32, sample_clock_cycles: 5, bit_width: AdcBitWidth::Twelve, - accumulation: AdcAccumulation::Single + accumulation: AdcAccumulation::Single, } } - /// + /// /// This setting adjusts the ADC clock frequency by dividing the input clock for the ADC. - /// + /// /// ## Example: /// * Input clock 48MHz, div 32 => ADC Clock is 1.5MHz - /// + /// pub fn clock_divider(mut self, div: AdcDivider) -> Self { self.clk_divider = div; self @@ -111,15 +110,15 @@ impl AdcSettingsBuilder { } /// Sets how the ADC will accumulate values before actually returning a value. - /// + /// /// The default is single (ADC will return a sample as soon as it is measured) - /// - /// Setting [AdcAccumulation::Summed] will make the ADC take 'n' samples, and sum the + /// + /// Setting [AdcAccumulation::Summed] will make the ADC take 'n' samples, and sum the /// total before returning it - /// - /// Setting [AdcAccumulation::Average] will make the ADC take 'n' samples, and average the + /// + /// Setting [AdcAccumulation::Average] will make the ADC take 'n' samples, and average the /// total before returning it - /// + /// /// NOTE: Selecting [AdcAccumulation::Summed] or [AdcAccumulation::Average] will reduce the overall /// ADC sample rate by a factor of 1/n, and the returned value will be 16bits long no matter /// what the sample Bit width was selected as @@ -130,18 +129,18 @@ impl AdcSettingsBuilder { /// This adjusts the number of ADC clock cycles taken to sample a single sample. /// The higher this number, the longer it will take the ADC to sample each sample. - /// + /// /// ## Safety /// Internally, this function will clamp the minimum input value to 1 to avoid 0 - /// + /// pub fn clock_cycles_per_sample(mut self, num: u8) -> Self { self.sample_clock_cycles = 1.max(num); // Prevent 0 self } - /// + /// /// Returns a calculated sample rate of the ADC with these settings - /// + /// pub fn calculate_sps(&self, clock_freq: u32) -> u32 { let div = self.clk_divider as u32; let adc_clk_freq = clock_freq / div; @@ -156,4 +155,4 @@ impl AdcSettingsBuilder { clocks_per_sample *= multi as u32; adc_clk_freq / clocks_per_sample } -} \ No newline at end of file +} diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs index d68f56f906a6..352a1b83a725 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -1,8 +1,7 @@ use crate::adc::*; +use crate::gpio::pin::*; use atsamd_hal_macros::hal_cfg; -use crate::gpio::{self, pin::*, PinMode}; - macro_rules! adc_pins { ( $( diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index d37b7e64fba6..575ea751bf40 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -2,12 +2,15 @@ use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_module}; use atsame51j::Peripherals; +use fugit::RateExtU32; +use impls::async_api; use pac::Mclk; use seq_macro::seq; use crate::{ gpio::AnyPin, pac, + time::Hertz, typelevel::{NoneT, Sealed}, }; @@ -39,11 +42,6 @@ pub use adc0::refctrl::Refselselect as Reference; pub use impls::async_api::InterruptHandler as AdcInterruptHandler; -/// Marker type that represents an ADC channel capable of doing async -/// operations. -#[cfg(feature = "async")] -pub enum AdcFuture {} - /// Trait representing an ADC instance pub trait AdcInstance { #[cfg(feature = "async")] @@ -51,7 +49,7 @@ pub trait AdcInstance { // The Adc0 and Adc1 PAC types implement Deref type Instance: Deref; - type Clock; + type Clock: Into; fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock; fn enable_mclk(mclk: &mut Mclk); @@ -185,10 +183,9 @@ impl Channel { // These methods are only implemented for a Channel that holds a configured pin impl> Channel { - #[inline(always)] - pub fn read_blocking(&self, adc: &mut Adc, f: F) -> u16 { + pub fn read_blocking(&self, adc: &mut Adc) -> u16 { //f(Id::ID as u16); - adc.read_blocking(Id::ID, f) + adc.read_blocking(Id::ID) } pub fn read_buffer_blocking(&self, adc: &mut Adc, dst: &mut [u16]) { @@ -207,34 +204,52 @@ impl> Channel { } } +/// ADC Instance pub struct Adc { adc: I::Instance, } +pub struct AsyncAdc { + inner: A, + _irqs: PhantomData, +} + impl Adc { + /// Construct a new ADC instance + /// + /// ## Important + /// This function will return None (No ADC) if the clock source provided + /// is faster than 100Mhz, since this is the maximum frequency for GCLK_ADCx as per + /// the datasheet. + /// + /// NOTE: If you plan to run the chip up to 125C, then the maximum GCLK frequency for the ADC + /// is restricted to 90Mhz for stable performance. pub fn new( adc: I::Instance, settings: AdcSettingsBuilder, mclk: &mut Mclk, - _clock: I::Clock, - ) -> (Self, Channels) { + clock: I::Clock, + ) -> Option<(Self, Channels)> { + if (clock.into() as Hertz).to_Hz() > 100_000_000 { + // Clock source is too fast + return None; + } I::enable_mclk(mclk); // Reset ADC here as we cannot guarantee its state // This also disables the ADC - //adc.ctrla().modify(|_, w| w.swrst().set_bit()); - //while adc.syncbusy().read().swrst().bit_is_set() {} + adc.ctrla().modify(|_, w| w.swrst().set_bit()); + while adc.syncbusy().read().swrst().bit_is_set() {} // Calibrate and setup the Vref (This is done once) let mut new_adc = Self { adc }; - //I::calibrate(&new_adc.adc); - //new_adc.configure(settings); - //new_adc.power_down(); // Make sure ADC is offline - (new_adc, Channels::new()) + I::calibrate(&new_adc.adc); + new_adc.configure(settings); + new_adc.power_down(); // Make sure ADC is offline + Some((new_adc, Channels::new())) } pub fn configure(&mut self, settings: AdcSettingsBuilder) { - /* // Disable ADC before we do anything! self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); while self.adc.syncbusy().read().enable().bit_is_set() {} @@ -257,7 +272,7 @@ impl Adc { self.adc .sampctrl() - .modify(|_, w| unsafe { w.samplen().bits(0) }); // sample length + .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length while self.adc.syncbusy().read().sampctrl().bit_is_set() {} self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) while self.adc.syncbusy().read().inputctrl().bit_is_set() {} @@ -272,16 +287,12 @@ impl Adc { .refctrl() .modify(|_, w| w.refsel().variant(Reference::Intref)); while self.adc.syncbusy().read().refctrl().bit_is_set() {} - */ - } - - /// Access the struct's underlying PAC object - fn reg_block(&self) -> &pac::adc0::RegisterBlock { - &self.adc } #[inline(always)] fn sync(&self) { + // Slightly more performant than checking the individual bits + // since we avoid an extra instruction to bit shift while self.adc.syncbusy().read().bits() != 0 {} } @@ -320,7 +331,7 @@ impl Adc { /// Enables an interrupt when conversion is ready. fn enable_interrupts(&mut self) { - self.adc.intflag().write(|w| w.resrdy().set_bit()); + //self.adc.intflag().write(|w| w.resrdy().set_bit()); self.adc.intenset().write(|w| w.resrdy().set_bit()); } @@ -338,6 +349,21 @@ impl Adc { } } + #[inline(always)] + fn result(&self) -> u16 { + self.adc.result().read().result().bits() + } + + #[inline(always)] + fn is_interrupt(&self) -> bool { + self.adc.intflag().read().bits() != 0 + } + + #[inline(always)] + fn clear_interrupt(&self) { + self.adc.intflag().write(|w| w.resrdy().set_bit()); + } + fn mux(&mut self, ch: u8) { while self.adc.syncbusy().read().inputctrl().bit_is_set() {} self.adc @@ -346,61 +372,65 @@ impl Adc { while self.adc.syncbusy().read().inputctrl().bit_is_set() {} } - #[inline(always)] - pub fn read_blocking(&mut self, ch: u8, f: F) -> u16 { - // Manually testing things to see why ADC does not - // function - while self.adc.syncbusy().read().bits() != 0 {} + pub fn read_blocking(&mut self, ch: u8) -> u16 { + self.sync(); self.mux(ch); - while self.adc.syncbusy().read().bits() != 0 {} - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - for _ in 0..99 {cortex_m::asm::nop();} - while self.adc.syncbusy().read().enable().bit_is_set(){ - core::hint::spin_loop(); - }; - - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - while self.adc.intflag().read().resrdy().bit_is_clear() { - core::hint::spin_loop(); - } + self.sync(); + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC + self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); // Start sample + while self.adc.intflag().read().resrdy().bit_is_clear() {} let res = self.adc.result().read().bits(); - self.power_down(); + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) res } #[cfg(feature = "async")] - async fn read(&mut self, ch: u8) -> u16 { + pub async fn read(&mut self, ch: u8) -> u16 { + use crate::async_hal::interrupts::InterruptSource; use core::{future::poll_fn, task::Poll}; + self.disable_interrupts(); + unsafe { + I::Interrupt::unpend(); + I::Interrupt::enable(); + } + self.sync(); self.mux(ch); - self.enable_interrupts(); - self.power_up(); - self.start_conversion(); + self.sync(); + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC + self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); let result = poll_fn(|cx| { - if let Some(v) = self.interrupt_result_ready() { + if self.is_interrupt() { + self.clear_interrupt(); self.disable_interrupts(); - return Poll::Ready(v); + return Poll::Ready(self.result()); } I::waker().register(cx.waker()); self.enable_interrupts(); - if let Some(v) = self.interrupt_result_ready() { + + if self.is_interrupt() { + self.clear_interrupt(); self.disable_interrupts(); - return Poll::Ready(v); + return Poll::Ready(self.result()); } - Poll::Pending }) .await; - self.power_down(); + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) + unsafe { + I::Interrupt::disable(); + } result } pub fn read_ptat_blocking(&mut self) -> u16 { - self.read_blocking(0x19, |_| {}) + self.read_blocking(0x19) } pub fn read_ctat_blocking(&mut self) -> u16 { - self.read_blocking(0x1A, |_| {}) + self.read_blocking(0x1A) } } From 18cbbfe6fc349e1408f4b122e466d1671fcc55dd Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 21 Jan 2025 13:01:10 -0500 Subject: [PATCH 13/65] Automatically convert pins to correct mode in with_pins --- hal/src/peripherals/adc/d5x/pin.rs | 6 +++--- hal/src/peripherals/adc/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs index 352a1b83a725..227b9975dbc2 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -13,11 +13,11 @@ macro_rules! adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl AdcPin<[<$Adc>], []>for Pin<$PinId, AlternateB> { - type Configured = Self; + impl AdcPin<$Adc, []> for Pin<$PinId, M> { + type Configured = Pin<$PinId, AlternateB>; fn into_function(self) -> Self::Configured { - self + self.into_alternate() } } )+ diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 575ea751bf40..837721976750 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,10 +1,10 @@ use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_module}; -use atsame51j::Peripherals; use fugit::RateExtU32; use impls::async_api; use pac::Mclk; +use pac::Peripherals; use seq_macro::seq; use crate::{ From ba755bcf9bdb247aa83d8a3dcfb7fd52ce8e3272 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 21 Jan 2025 13:04:57 -0500 Subject: [PATCH 14/65] Remove explicit reference to a chip in favor of PAC --- hal/src/peripherals/adc/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 837721976750..6da806137ec6 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,10 +1,7 @@ use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_module}; -use fugit::RateExtU32; -use impls::async_api; -use pac::Mclk; -use pac::Peripherals; +use pac::{Mclk, Peripherals}; use seq_macro::seq; use crate::{ @@ -23,10 +20,7 @@ mod adc_settings; pub use adc_settings::*; -use super::{ - calibration, - clock::{self, Adc0Clock}, -}; +use super::{calibration, clock}; use crate::pac::adc0; From 9a0219d97511a2a313e379e34dde604de3e5e150 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 15:50:52 +0000 Subject: [PATCH 15/65] Split async and sync ADC into different implementations --- boards/same51_curiosity_nano/src/lib.rs | 194 ++++++++++++++++++++++++ hal/src/peripherals/adc/mod.rs | 90 ++++++----- 2 files changed, 248 insertions(+), 36 deletions(-) create mode 100644 boards/same51_curiosity_nano/src/lib.rs diff --git a/boards/same51_curiosity_nano/src/lib.rs b/boards/same51_curiosity_nano/src/lib.rs new file mode 100644 index 000000000000..ccecef928286 --- /dev/null +++ b/boards/same51_curiosity_nano/src/lib.rs @@ -0,0 +1,194 @@ +#![no_std] +#![deny(missing_docs)] + +//! Board support crate for Adafruit's Feather M4 Express, +//! an ATSAMD51-based board in Feather form factor. + +#[cfg(feature = "rt")] +pub use cortex_m_rt::entry; + +pub use atsamd_hal as hal; +pub use hal::ehal; +pub use hal::pac; + +use hal::clock::GenericClockController; +use hal::sercom::{ + i2c, spi, + uart::{self, BaudMode, Oversampling}, + IoSet1, UndocIoSet1, +}; +use hal::time::Hertz; + +#[cfg(feature = "usb")] +use hal::usb::usb_device::bus::UsbBusAllocator; +#[cfg(feature = "usb")] +pub use hal::usb::UsbBus; + +hal::bsp_peripherals!( + Sercom1 { SpiSercom } + Sercom2 { I2cSercom } + Sercom5 { UartSercom } +); + +hal::bsp_pins!( + PA02 { + /// Analog pin 0. Can act as a true analog output + /// as it has a DAC (which is not currently supported + /// by this hal) as well as input. + name: a0, + } + PA05 { + /// Analog Pin 1 + name: a1, + } + PB08 { + /// Analog Pin 2 + name: a2, + } + PB09 { + /// Analog Pin 3 + name: a3, + } + PA04 { + /// Analog Pin 4 + name: a4, + } + PA06 { + /// Analog Pin 5 + name: a5, + } + PB01 { + /// Analog Vdiv (1/2 resistor divider for monitoring the battery) + name: battery, + } + + PB17 { + /// Pin 0, UART rx + name: d0, + aliases: { + AlternateC: UartRx + } + } + PB16 { + /// Pin 1, UART tx + name: d1, + aliases: { + AlternateC: UartTx + } + } + PA14 { + /// Pin 4, PWM capable + name: d4, + } + PA16 { + /// Pin 5, PWM capable + name: d5, + } + PA18 { + /// Pin 6, PWM capable + name: d6, + } + PB03 { + /// Neopixel Pin + name: neopixel, + } + PA19 { + /// Pin 9, PWM capable. Also analog input (A7) + name: d9, + } + PA20 { + /// Pin 10, PWM capable + name: d10, + } + PA21 { + /// Pin 11, PWM capable + name: d11, + } + PA22 { + /// Pin 12, PWM capable + name: d12, + } + PA23 { + /// Pin 13, which is also attached to the red LED. PWM capable. + name: d13, + aliases: { + PushPullOutput: RedLed, + AlternateE: RedLedPwm + } + } + PA12 { + /// The I2C data line + name: sda, + aliases: { + AlternateC: Sda + } + } + PA13 { + /// The I2C clock line + name: scl, + aliases: { + AlternateC: Scl + } + } + PA17 { + /// The SPI SCK + name: sck, + aliases: { + AlternateC: Sclk + } + } + PB23 { + /// The SPI MOSI + name: mosi, + aliases: { + AlternateC: Mosi + } + } + PB22 { + /// The SPI MISO + name: miso, + aliases: { + AlternateC: Miso + } + } + PA24 { + /// The USB D- pad + name: usb_dm, + aliases: { + AlternateH: UsbDm + } + } + PA25 { + /// The USB D+ pad + name: usb_dp, + aliases: { + AlternateH: UsbDp + } + } +); + +/// UART pads for the labelled RX & TX pins +pub type UartPads = uart::Pads; + +/// UART device for the labelled RX & TX pins +pub type Uart = uart::Uart, uart::Duplex>; + +/// Convenience for setting up the labelled RX, TX pins to +/// operate as a UART device running at the specified baud. +pub fn uart( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: UartSercom, + mclk: &mut pac::Mclk, + rx: impl Into, + tx: impl Into, +) -> Uart { + let gclk0 = clocks.gclk0(); + + let clock = &clocks.sercom5_core(&gclk0).unwrap(); + let baud = baud.into(); + let pads = uart::Pads::default().rx(rx.into()).tx(tx.into()); + uart::Config::new(mclk, sercom, pads, clock.freq()) + .baud(baud, BaudMode::Fractional(Oversampling::Bits16)) + .enable() +} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 575ea751bf40..bae7b1c27322 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -183,7 +183,7 @@ impl Channel { // These methods are only implemented for a Channel that holds a configured pin impl> Channel { - pub fn read_blocking(&self, adc: &mut Adc) -> u16 { + pub fn read_blocking(&self, adc: &mut Adc) -> u16 { //f(Id::ID as u16); adc.read_blocking(Id::ID) } @@ -194,7 +194,10 @@ impl> Channel { } #[cfg(feature = "async")] - pub async fn read(&self, adc: &mut Adc) -> u16 { + pub async fn read(&self, adc: &mut Adc) -> u16 + where + F: crate::async_hal::interrupts::Binding>, + { adc.read(Id::ID).await } @@ -205,14 +208,12 @@ impl> Channel { } /// ADC Instance -pub struct Adc { +pub struct Adc { adc: I::Instance, + _irqs: PhantomData, } -pub struct AsyncAdc { - inner: A, - _irqs: PhantomData, -} +pub struct AdcFuture; impl Adc { /// Construct a new ADC instance @@ -241,7 +242,10 @@ impl Adc { adc.ctrla().modify(|_, w| w.swrst().set_bit()); while adc.syncbusy().read().swrst().bit_is_set() {} // Calibrate and setup the Vref (This is done once) - let mut new_adc = Self { adc }; + let mut new_adc = Self { + adc, + _irqs: PhantomData, + }; I::calibrate(&new_adc.adc); new_adc.configure(settings); @@ -289,6 +293,29 @@ impl Adc { while self.adc.syncbusy().read().refctrl().bit_is_set() {} } + pub fn read_blocking(&mut self, ch: u8) -> u16 { + self.sync(); + self.mux(ch); + self.sync(); + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC + self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); // Start sample + while self.adc.intflag().read().resrdy().bit_is_clear() {} + let res = self.adc.result().read().bits(); + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) + res + } + + pub fn read_ptat_blocking(&mut self) -> u16 { + self.read_blocking(0x19) + } + + pub fn read_ctat_blocking(&mut self) -> u16 { + self.read_blocking(0x1A) + } +} + +impl Adc { #[inline(always)] fn sync(&self) { // Slightly more performant than checking the individual bits @@ -372,28 +399,31 @@ impl Adc { while self.adc.syncbusy().read().inputctrl().bit_is_set() {} } - pub fn read_blocking(&mut self, ch: u8) -> u16 { - self.sync(); - self.mux(ch); - self.sync(); - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC - self.sync(); - self.adc.swtrig().modify(|_, w| w.start().set_bit()); // Start sample - while self.adc.intflag().read().resrdy().bit_is_clear() {} - let res = self.adc.result().read().bits(); - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) - res - } - #[cfg(feature = "async")] - pub async fn read(&mut self, ch: u8) -> u16 { + pub fn into_future(self, _irqs: F) -> Adc + where + F: crate::async_hal::interrupts::Binding>, + { use crate::async_hal::interrupts::InterruptSource; - use core::{future::poll_fn, task::Poll}; - self.disable_interrupts(); unsafe { I::Interrupt::unpend(); I::Interrupt::enable(); } + Adc { + adc: self.adc, + _irqs: PhantomData, + } + } +} + +#[cfg(feature = "async")] +impl Adc +where + F: crate::async_hal::interrupts::Binding>, +{ + pub async fn read(&mut self, ch: u8) -> u16 { + use core::{future::poll_fn, task::Poll}; + self.disable_interrupts(); self.sync(); self.mux(ch); self.sync(); @@ -406,7 +436,6 @@ impl Adc { self.disable_interrupts(); return Poll::Ready(self.result()); } - I::waker().register(cx.waker()); self.enable_interrupts(); @@ -419,19 +448,8 @@ impl Adc { }) .await; self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) - unsafe { - I::Interrupt::disable(); - } result } - - pub fn read_ptat_blocking(&mut self) -> u16 { - self.read_blocking(0x19) - } - - pub fn read_ctat_blocking(&mut self) -> u16 { - self.read_blocking(0x1A) - } } // Here I declare the number of channels for the SAME51 ADC From fa512702d90c93c2f877f62609b22d0e28d728aa Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 16:17:38 +0000 Subject: [PATCH 16/65] Clean up ADC --- hal/src/peripherals/adc/d11.rs | 253 ------------------ hal/src/peripherals/adc/d5x.rs | 456 --------------------------------- hal/src/peripherals/adc/mod.rs | 126 +++++---- 3 files changed, 79 insertions(+), 756 deletions(-) delete mode 100644 hal/src/peripherals/adc/d11.rs delete mode 100644 hal/src/peripherals/adc/d5x.rs diff --git a/hal/src/peripherals/adc/d11.rs b/hal/src/peripherals/adc/d11.rs deleted file mode 100644 index 0982d3a31dc6..000000000000 --- a/hal/src/peripherals/adc/d11.rs +++ /dev/null @@ -1,253 +0,0 @@ -//! Analogue-to-Digital Conversion -use atsamd_hal_macros::hal_cfg; - -use crate::clock::GenericClockController; -use crate::ehal_02::adc::{Channel, OneShot}; -use crate::gpio::*; -use crate::pac::{self, adc, Pm}; - -/// Samples per reading -pub use adc::avgctrl::Samplenumselect as SampleRate; -/// Clock frequency relative to the system clock -pub use adc::ctrlb::Prescalerselect as Prescaler; -/// Reading resolution in bits -/// -/// For the resolution of Arduino boards, -/// see the [analogueRead](https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/) docs. -pub use adc::ctrlb::Resselselect as Resolution; -/// The gain level -pub use adc::inputctrl::Gainselect as Gain; -/// Reference voltage (or its source) -pub use adc::refctrl::Refselselect as Reference; - -/// `Adc` encapsulates the device ADC -pub struct Adc { - adc: ADC, -} - -impl Adc { - /// Create a new `Adc` instance. The default configuration is: - /// * 1/32 prescaler - /// * 12 bit resolution - /// * 1 sample - /// * 1/2 gain - /// * 1/2 VDDANA reference voltage - #[allow(clippy::self_named_constructors)] - pub fn adc(adc: pac::Adc, pm: &mut Pm, clocks: &mut GenericClockController) -> Self { - pm.apbcmask().modify(|_, w| w.adc_().set_bit()); - - // set to 1 / (1 / (48000000 / 32) * 6) = 250000 SPS - let gclk0 = clocks.gclk0(); - clocks.adc(&gclk0).expect("adc clock setup failed"); - while adc.status().read().syncbusy().bit_is_set() {} - - adc.ctrla().modify(|_, w| w.swrst().set_bit()); - while adc.status().read().syncbusy().bit_is_set() {} - - adc.ctrlb().modify(|_, w| { - w.prescaler().div32(); - w.ressel()._12bit() - }); - while adc.status().read().syncbusy().bit_is_set() {} - - adc.sampctrl().modify(|_, w| unsafe { w.samplen().bits(5) }); //sample length - while adc.status().read().syncbusy().bit_is_set() {} - - adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) - while adc.status().read().syncbusy().bit_is_set() {} - - let mut newadc = Self { adc }; - newadc.samples(adc::avgctrl::Samplenumselect::_1); - newadc.gain(adc::inputctrl::Gainselect::Div2); - newadc.reference(adc::refctrl::Refselselect::Intvcc1); - - newadc - } - - /// Set the sample rate - pub fn samples(&mut self, samples: SampleRate) { - use adc::avgctrl::Samplenumselect; - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(samples); - unsafe { - // Table 32-3 (32.6.7) specifies the adjres - // values necessary for each SAMPLENUM value. - w.adjres().bits(match samples { - Samplenumselect::_1 => 0, - Samplenumselect::_2 => 1, - Samplenumselect::_4 => 2, - Samplenumselect::_8 => 3, - _ => 4, - }) - } - }); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - /// Set the gain factor - pub fn gain(&mut self, gain: Gain) { - self.adc.inputctrl().modify(|_, w| w.gain().variant(gain)); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - /// Set the voltage reference - pub fn reference(&mut self, reference: Reference) { - self.adc - .refctrl() - .modify(|_, w| w.refsel().variant(reference)); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - /// Set the prescaler for adjusting the clock relative to the system clock - pub fn prescaler(&mut self, prescaler: Prescaler) { - self.adc - .ctrlb() - .modify(|_, w| w.prescaler().variant(prescaler)); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - /// Set the input resolution. - pub fn resolution(&mut self, resolution: Resolution) { - self.adc - .ctrlb() - .modify(|_, w| w.ressel().variant(resolution)); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - fn power_up(&mut self) { - while self.adc.status().read().syncbusy().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - fn power_down(&mut self) { - while self.adc.status().read().syncbusy().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - while self.adc.status().read().syncbusy().bit_is_set() {} - } - - fn convert(&mut self) -> u16 { - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - while self.adc.intflag().read().resrdy().bit_is_clear() {} - while self.adc.status().read().syncbusy().bit_is_set() {} - - // Clear the interrupt flag - self.adc.intflag().modify(|_, w| w.resrdy().set_bit()); - - // Start conversion again, since The first conversion after the reference is - // changed must not be used. - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - while self.adc.intflag().read().resrdy().bit_is_clear() {} - while self.adc.status().read().syncbusy().bit_is_set() {} - - self.adc.result().read().result().bits() - } -} - -impl OneShot for Adc -where - WORD: From, - PIN: Channel, -{ - type Error = (); - - fn read(&mut self, _pin: &mut PIN) -> nb::Result { - let chan = PIN::channel(); - while self.adc.status().read().syncbusy().bit_is_set() {} - - self.adc - .inputctrl() - .modify(|_, w| unsafe { w.muxpos().bits(chan) }); - self.power_up(); - let result = self.convert(); - self.power_down(); - - Ok(result.into()) - } -} - -macro_rules! adc_pins { - ( - $( - $( #[$cfg:meta] )? - $PinId:ident: $CHAN:literal - ),+ - $(,)? - ) => { - $( - $( #[$cfg] )? - impl Channel<$crate::pac::Adc> for Pin<$PinId, AlternateB> { - type ID = u8; - fn channel() -> u8 { $CHAN } - } - )+ - } -} - -#[hal_cfg("adc-d11")] -adc_pins! { - #[hal_cfg("pa02")] - PA02: 0, - #[hal_cfg("pa03")] - PA03: 1, - #[hal_cfg("pa04")] - PA04: 2, - #[hal_cfg("pa05")] - PA05: 3, - #[hal_cfg("pa06")] - PA06: 4, - #[hal_cfg("pa07")] - PA07: 5, - #[hal_cfg("pa14")] - PA14: 6, - #[hal_cfg("pa15")] - PA15: 7, - #[hal_cfg("pa10")] - PA10: 8, - #[hal_cfg("pa11")] - PA11: 9, -} - -#[hal_cfg("adc-d21")] -adc_pins! { - #[hal_cfg("pa02")] - PA02: 0, - #[hal_cfg("pa03")] - PA03: 1, - #[hal_cfg("pb08")] - PB08: 2, - #[hal_cfg("pb09")] - PB09: 3, - #[hal_cfg("pa04")] - PA04: 4, - #[hal_cfg("pa05")] - PA05: 5, - #[hal_cfg("pa06")] - PA06: 6, - #[hal_cfg("pa07")] - PA07: 7, - #[hal_cfg("pb00")] - PB00: 8, - #[hal_cfg("pb01")] - PB01: 9, - #[hal_cfg("pb02")] - PB02: 10, - #[hal_cfg("pb03")] - PB03: 11, - #[hal_cfg("pb04")] - PB04: 12, - #[hal_cfg("pb05")] - PB05: 13, - #[hal_cfg("pb06")] - PB06: 14, - #[hal_cfg("pb07")] - PB07: 15, - #[hal_cfg("pa08")] - PA08: 16, - #[hal_cfg("pa09")] - PA09: 17, - #[hal_cfg("pa10")] - PA10: 18, - #[hal_cfg("pa11")] - PA11: 19, -} diff --git a/hal/src/peripherals/adc/d5x.rs b/hal/src/peripherals/adc/d5x.rs deleted file mode 100644 index 0f6501e081b7..000000000000 --- a/hal/src/peripherals/adc/d5x.rs +++ /dev/null @@ -1,456 +0,0 @@ -//! Analogue-to-Digital Conversion -use core::future::poll_fn; -use core::ops::Deref; -use core::task::Poll; - -use adc_settings::AdcSettingsBuilder; -use atsamd_hal_macros::hal_cfg; -use atsame51j::Peripherals; - -use crate::clock::GenericClockController; -#[rustfmt::skip] -use crate::gpio::*; -use crate::ehal_02::adc::{Channel, OneShot}; -use crate::pac::gclk::genctrl::Srcselect::Dfll; -use crate::pac::gclk::pchctrl::Genselect; -use crate::pac::{adc0, Adc0, Adc1, Mclk}; - -use crate::calibration; -use crate::typelevel::Sealed; - -pub mod async_api; -pub mod adc_settings; - -/// Samples per reading -pub use adc0::avgctrl::Samplenumselect as SampleRate; -/// Clock frequency relative to the system clock -pub use adc0::ctrla::Prescalerselect as Prescaler; -/// Reading resolution in bits -pub use adc0::ctrlb::Resselselect as Resolution; -/// Reference voltage (or its source) -pub use adc0::refctrl::Refselselect as Reference; - -pub const NUM_ADC: usize = 2; - -pub trait Adc: Sealed + Deref { - /// ADC number - const NUM: usize; - - #[cfg(feature = "async")] - type Interrupt: crate::async_hal::interrupts::InterruptSource; - - /// Get a reference to the Adc from a - /// [`Peripherals`] block - fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock; - - /// Get a reference to this [`Adc`]'s associated RX Waker - #[cfg(feature = "async")] - #[inline] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - &async_api::waker::ADC_WAKERS[Self::NUM] - } -} - -pub struct ADC0 { - adc: Adc0 -} - -impl Sealed for ADC0 {} - -impl Adc for ADC0 { - const NUM: usize = 0; - - type Interrupt = crate::async_hal::interrupts::ADC0; - - fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::adc0::RegisterBlock { - &peripherals.adc0 - } -} - -impl Deref for ADC0 { - type Target = adc0::RegisterBlock; - - fn deref(&self) -> &Self::Target { - todo!() - } -} - -impl ADC0 { - pub fn new(cfg: AdcSettingsBuilder, adc: Adc0 , mclk: &mut Mclk) -> Self { - Self { - adc: adc - } - } - - fn power_up(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} - } - - fn power_down(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} - } - - /// Enables an interrupt when conversion is ready. - fn enable_interrupts(&mut self) { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - self.adc.intenset().write(|w| w.resrdy().set_bit()); - } - - /// Disables the interrupt for when conversion is ready. - fn disable_interrupts(&mut self) { - self.adc.intenclr().write(|w| w.resrdy().set_bit()); - } - - #[inline(always)] - fn start_conversion(&mut self) { - // start conversion - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - // do it again because the datasheet tells us to - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - } - - fn conversion_ready(&mut self) -> Option { - if self.adc.intflag().read().resrdy().bit_is_set() { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - Some(self.adc.result().read().result().bits()) - } else { - None - } - } -} - -/* -/// `Adc` encapsulates the device ADC -pub struct Adc { - adc: ADC, -} - -/// Describes how an interrupt-driven ADC should finalize the peripheral -/// upon the completion of a conversion. -pub trait ConversionMode { - fn on_start(adc: &mut Adc); - fn on_complete(adc: &mut Adc); - fn on_stop(adc: &mut Adc); -} - -pub struct SingleConversion; -pub struct FreeRunning; - -macro_rules! adc_hal { - ($($ADC:ident: ($init:ident, $mclk:ident, $apmask:ident, $compcal:ident, $refcal:ident, $r2rcal:ident),)+) => { - $( -impl Adc<$ADC> { -//Adc0: (adc0, apbdmask, adc0_, adc0_biascomp_scale_cal, adc0_biasref_scale_cal, adc0_biasr2r_scale_cal), - pub fn $init(adc: $ADC, mclk: &mut Mclk, clocks: &mut GenericClockController, gclk:Genselect) -> Self { - mclk.$mclk().modify(|_, w| w.$apmask().set_bit()); - // set to 1/(1/(48000000/32) * 6) = 250000 SPS - let adc_clock = clocks.configure_gclk_divider_and_source(gclk, 1, Dfll, false) - .expect("adc clock setup failed"); - clocks.$init(&adc_clock).expect("adc clock setup failed"); - adc.ctrla().modify(|_, w| w.prescaler().div32()); - adc.ctrlb().modify(|_, w| w.ressel()._12bit()); - while adc.syncbusy().read().ctrlb().bit_is_set() {} - adc.sampctrl().modify(|_, w| unsafe {w.samplen().bits(5)}); // sample length - while adc.syncbusy().read().sampctrl().bit_is_set() {} - adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) - while adc.syncbusy().read().inputctrl().bit_is_set() {} - - adc.calib().write(|w| unsafe { - w.biascomp().bits(calibration::$compcal()); - w.biasrefbuf().bits(calibration::$refcal()); - w.biasr2r().bits(calibration::$r2rcal()) - }); - - let mut newadc = Self { adc }; - newadc.samples(adc0::avgctrl::Samplenumselect::_1); - newadc.reference(adc0::refctrl::Refselselect::Intvcc1); - - newadc - } - - /// Set the sample rate - pub fn samples(&mut self, samples: SampleRate) { - use adc0::avgctrl::Samplenumselect; - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(samples); - unsafe { - // Table 45-3 (45.6.2.10) specifies the adjres - // values necessary for each SAMPLENUM value. - w.adjres().bits(match samples { - Samplenumselect::_1 => 0, - Samplenumselect::_2 => 1, - Samplenumselect::_4 => 2, - Samplenumselect::_8 => 3, - _ => 4, - }) - } - }); - while self.adc.syncbusy().read().avgctrl().bit_is_set() {} - } - - /// Set the voltage reference - pub fn reference(&mut self, reference: Reference) { - self.adc - .refctrl() - .modify(|_, w| w.refsel().variant(reference)); - while self.adc.syncbusy().read().refctrl().bit_is_set() {} - } - - /// Set the prescaler for adjusting the clock relative to the system clock - pub fn prescaler(&mut self, prescaler: Prescaler) { - self.adc - .ctrla() - .modify(|_, w| w.prescaler().variant(prescaler)); - // Note there is no syncbusy for ctrla - } - - /// Set the input resolution - pub fn resolution(&mut self, resolution: Resolution) { - self.adc - .ctrlb() - .modify(|_, w| w.ressel().variant(resolution)); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} - } - - fn power_up(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} - } - - fn power_down(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} - } - - #[inline(always)] - fn start_conversion(&mut self) { - // start conversion - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - // do it again because the datasheet tells us to - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - } - - fn enable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} - } - - fn disable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} - } - - fn synchronous_convert(&mut self) -> u16 { - self.start_conversion(); - while self.adc.intflag().read().resrdy().bit_is_clear() {} - - self.adc.result().read().result().bits() - } - - /// Enables an interrupt when conversion is ready. - fn enable_interrupts(&mut self) { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - self.adc.intenset().write(|w| w.resrdy().set_bit()); - } - - /// Disables the interrupt for when conversion is ready. - fn disable_interrupts(&mut self) { - self.adc.intenclr().write(|w| w.resrdy().set_bit()); - } - - fn service_interrupt_ready(&mut self) -> Option { - if self.adc.intflag().read().resrdy().bit_is_set() { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - - Some(self.adc.result().read().result().bits()) - } else { - None - } - } - - /// Sets the mux to a particular pin. The pin mux is enabled-protected, - /// so must be called while the peripheral is disabled. - fn mux>(&mut self, _pin: &mut PIN) { - let chan = PIN::channel(); - while self.adc.syncbusy().read().inputctrl().bit_is_set() {} - self.adc.inputctrl().modify(|_, w| unsafe{ w.muxpos().bits(chan) }); - } -} - -impl ConversionMode<$ADC> for SingleConversion { - fn on_start(_adc: &mut Adc<$ADC>) { - } - fn on_complete(adc: &mut Adc<$ADC>) { - adc.disable_interrupts(); - adc.power_down(); - } - fn on_stop(_adc: &mut Adc<$ADC>) { - } -} - -impl ConversionMode<$ADC> for FreeRunning { - fn on_start(adc: &mut Adc<$ADC>) { - adc.enable_freerunning(); - } - fn on_complete(_adc: &mut Adc<$ADC>) { - } - fn on_stop(adc: &mut Adc<$ADC>) { - adc.disable_interrupts(); - adc.power_down(); - adc.disable_freerunning(); - } -} - -impl InterruptAdc<$ADC, C> - where C: ConversionMode<$ADC> -{ - pub fn service_interrupt_ready(&mut self) -> Option { - if let Some(res) = self.adc.service_interrupt_ready() { - C::on_complete(&mut self.adc); - Some(res) - } else { - None - } - } - - /// Starts a conversion sampling the specified pin. - pub fn start_conversion>(&mut self, pin: &mut PIN) { - self.adc.mux(pin); - self.adc.power_up(); - C::on_start(&mut self.adc); - self.adc.enable_interrupts(); - self.adc.start_conversion(); - } - - pub fn stop_conversion(&mut self) { - C::on_stop(&mut self.adc); - } -} - -impl From> for InterruptAdc<$ADC, C> - where C: ConversionMode<$ADC> -{ - fn from(adc: Adc<$ADC>) -> Self { - Self { - adc, - m: core::marker::PhantomData{}, - } - } -} - -impl OneShot<$ADC, WORD, PIN> for Adc<$ADC> -where - WORD: From, - PIN: Channel<$ADC, ID=u8>, -{ - type Error = (); - - fn read(&mut self, pin: &mut PIN) -> nb::Result { - self.mux(pin); - self.power_up(); - let result = self.synchronous_convert(); - self.power_down(); - Ok(result.into()) - } -} - )+ - } -} - -adc_hal! { - Adc0: (adc0, apbdmask, adc0_, adc0_biascomp_scale_cal, adc0_biasref_scale_cal, adc0_biasr2r_scale_cal), - Adc1: (adc1, apbdmask, adc1_, adc1_biascomp_scale_cal, adc1_biasref_scale_cal, adc1_biasr2r_scale_cal), -} -*/ - -macro_rules! adc_pins { - ( - $( - $( #[$cfg:meta] )? - $PinId:ident: ($ADC:ident, $CHAN:literal) - ),+ - $(,)? - ) => { - $( - $( #[$cfg] )? - impl Channel<$ADC> for Pin<$PinId, AlternateB> { - type ID = u8; - fn channel() -> u8 { $CHAN } - } - )+ - } -} - -adc_pins! { - #[hal_cfg("pa02")] - PA02: (Adc0, 0), - #[hal_cfg("pa03")] - PA03: (Adc0, 1), - #[hal_cfg("pb08")] - PB08: (Adc0, 2), - #[hal_cfg("pb09")] - PB09: (Adc0, 3), - #[hal_cfg("pa04")] - PA04: (Adc0, 4), - #[hal_cfg("pa05")] - PA05: (Adc0, 5), - #[hal_cfg("pa06")] - PA06: (Adc0, 6), - #[hal_cfg("pa07")] - PA07: (Adc0, 7), - #[hal_cfg("pa08")] - PA08: (Adc0, 8), - #[hal_cfg("pa09")] - PA09: (Adc0, 9), - #[hal_cfg("pa10")] - PA10: (Adc0, 10), - #[hal_cfg("pa11")] - PA11: (Adc0, 11), - #[hal_cfg("pb00")] - PB00: (Adc0, 12), - #[hal_cfg("pb01")] - PB01: (Adc0, 13), - #[hal_cfg("pb02")] - PB02: (Adc0, 14), - #[hal_cfg("pb03")] - PB03: (Adc0, 15), - - #[hal_cfg("pb08")] - PB08: (Adc1, 0), - #[hal_cfg("pb09")] - PB09: (Adc1, 1), - #[hal_cfg("pa08")] - PA08: (Adc1, 2), - #[hal_cfg("pa09")] - PA09: (Adc1, 3), - #[hal_cfg("pc02")] - PC02: (Adc1, 4), - #[hal_cfg("pc03")] - PC03: (Adc1, 5), - #[hal_cfg("pb04")] - PB04: (Adc1, 6), - #[hal_cfg("pb05")] - PB05: (Adc1, 7), - #[hal_cfg("pb06")] - PB06: (Adc1, 8), - #[hal_cfg("pb07")] - PB07: (Adc1, 9), - #[hal_cfg("pc00")] - PC00: (Adc1, 10), - #[hal_cfg("pc01")] - PC01: (Adc1, 11), - #[hal_cfg("pc30")] - PC30: (Adc1, 12), - #[hal_cfg("pc31")] - PC31: (Adc1, 13), - #[hal_cfg("pd00")] - PD00: (Adc1, 14), - #[hal_cfg("pd01")] - PD01: (Adc1, 15), -} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 09b94502cf17..435a3f64f171 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -190,7 +190,10 @@ impl> Channel { #[cfg(feature = "async")] pub async fn read(&self, adc: &mut Adc) -> u16 where - F: crate::async_hal::interrupts::Binding>, + F: crate::async_hal::interrupts::Binding< + I::Interrupt, + impls::async_api::InterruptHandler, + >, { adc.read(Id::ID).await } @@ -231,26 +234,25 @@ impl Adc { } I::enable_mclk(mclk); - // Reset ADC here as we cannot guarantee its state - // This also disables the ADC - adc.ctrla().modify(|_, w| w.swrst().set_bit()); - while adc.syncbusy().read().swrst().bit_is_set() {} - // Calibrate and setup the Vref (This is done once) let mut new_adc = Self { adc, _irqs: PhantomData, }; + // Reset ADC here as we cannot guarantee its state + // This also disables the ADC + new_adc.adc.ctrla().modify(|_, w| w.swrst().set_bit()); + new_adc.sync(); + I::calibrate(&new_adc.adc); new_adc.configure(settings); - new_adc.power_down(); // Make sure ADC is offline Some((new_adc, Channels::new())) } pub fn configure(&mut self, settings: AdcSettingsBuilder) { // Disable ADC before we do anything! - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} + self.power_down(); + self.sync(); self.adc.ctrla().modify(|_, w| match settings.clk_divider { AdcDivider::Div2 => w.prescaler().div2(), AdcDivider::Div4 => w.prescaler().div4(), @@ -266,37 +268,35 @@ impl Adc { AdcBitWidth::Ten => w.ressel()._10bit(), AdcBitWidth::Twelve => w.ressel()._12bit(), }); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + self.sync(); self.adc .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length - while self.adc.syncbusy().read().sampctrl().bit_is_set() {} + self.sync(); self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) - while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + self.sync(); self.adc.avgctrl().modify(|_, w| { w.samplenum().variant(Samplenumselect::_1); unsafe { w.adjres().bits(0) } }); - while self.adc.syncbusy().read().avgctrl().bit_is_set() {} + self.sync(); self.adc .refctrl() .modify(|_, w| w.refsel().variant(Reference::Intref)); - while self.adc.syncbusy().read().refctrl().bit_is_set() {} + self.sync(); } pub fn read_blocking(&mut self, ch: u8) -> u16 { - self.sync(); self.mux(ch); + self.power_up(); self.sync(); - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC - self.sync(); - self.adc.swtrig().modify(|_, w| w.start().set_bit()); // Start sample + self.start_conversion(); while self.adc.intflag().read().resrdy().bit_is_clear() {} - let res = self.adc.result().read().bits(); - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) + let res = self.result(); + self.power_down(); res } @@ -319,25 +319,20 @@ impl Adc { #[inline(always)] fn power_up(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} + self.sync(); } #[inline(always)] fn power_down(&mut self) { - while self.adc.syncbusy().read().enable().bit_is_set() {} self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - while self.adc.syncbusy().read().enable().bit_is_set() {} + self.sync(); } #[inline(always)] fn start_conversion(&mut self) { - // start conversion - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - while self.adc.syncbusy().read().swtrig().bit_is_set() {} - //// do it again because the datasheet tells us to self.adc.swtrig().modify(|_, w| w.start().set_bit()); + self.sync(); } fn enable_freerunning(&mut self) { @@ -351,23 +346,18 @@ impl Adc { } /// Enables an interrupt when conversion is ready. + #[inline(always)] fn enable_interrupts(&mut self) { //self.adc.intflag().write(|w| w.resrdy().set_bit()); self.adc.intenset().write(|w| w.resrdy().set_bit()); + self.sync(); } /// Disables the interrupt for when conversion is ready. + #[inline(always)] fn disable_interrupts(&mut self) { self.adc.intenclr().write(|w| w.resrdy().set_bit()); - } - - fn interrupt_result_ready(&self) -> Option { - if self.adc.intflag().read().resrdy().bit_is_set() { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - Some(self.adc.result().read().result().bits()) - } else { - None - } + self.sync(); } #[inline(always)] @@ -386,17 +376,19 @@ impl Adc { } fn mux(&mut self, ch: u8) { - while self.adc.syncbusy().read().inputctrl().bit_is_set() {} self.adc .inputctrl() .modify(|_, w| unsafe { w.muxpos().bits(ch) }); - while self.adc.syncbusy().read().inputctrl().bit_is_set() {} + self.sync() } #[cfg(feature = "async")] pub fn into_future(self, _irqs: F) -> Adc where - F: crate::async_hal::interrupts::Binding>, + F: crate::async_hal::interrupts::Binding< + I::Interrupt, + impls::async_api::InterruptHandler, + >, { use crate::async_hal::interrupts::InterruptSource; unsafe { @@ -413,17 +405,14 @@ impl Adc { #[cfg(feature = "async")] impl Adc where - F: crate::async_hal::interrupts::Binding>, + F: crate::async_hal::interrupts::Binding>, { pub async fn read(&mut self, ch: u8) -> u16 { use core::{future::poll_fn, task::Poll}; self.disable_interrupts(); - self.sync(); self.mux(ch); - self.sync(); - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC - self.sync(); - self.adc.swtrig().modify(|_, w| w.start().set_bit()); + self.power_up(); // Enable ADC + self.start_conversion(); let result = poll_fn(|cx| { if self.is_interrupt() { self.clear_interrupt(); @@ -441,9 +430,52 @@ where Poll::Pending }) .await; - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) + self.power_down(); result } + + /* + pub async fn read_into_buffer(&mut self, ch: u8, buffer: &mut [u16], f: X) { + if buffer.is_empty() { + return; + } + use core::{future::poll_fn, task::Poll}; + self.disable_interrupts(); + self.sync(); + self.mux(ch); + self.sync(); + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC + self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + let mut pos = 0; + poll_fn(|cx| { + if self.is_interrupt() { + self.clear_interrupt(); + buffer[pos] = self.result(); + pos += 1; + if pos == buffer.len() { + self.disable_interrupts(); + return Poll::Ready(()); + } + } + f(pos); + I::waker().register(cx.waker()); + self.enable_interrupts(); + if self.is_interrupt() { + self.clear_interrupt(); + buffer[pos] = self.result(); + pos += 1; + if pos == buffer.len() { + self.disable_interrupts(); + return Poll::Ready(()); + } + } + Poll::Pending + }) + .await; + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) + } + */ } // Here I declare the number of channels for the SAME51 ADC From ebeb28ae9ed52ebf2d0b9a83ee7e008914d98c61 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 22 Jan 2025 10:56:11 -0500 Subject: [PATCH 17/65] Add inlines everywhere --- .../peripherals/adc/{d5x => }/async_api.rs | 0 hal/src/peripherals/adc/d5x/mod.rs | 1 - hal/src/peripherals/adc/mod.rs | 69 ++++++++++++------- 3 files changed, 46 insertions(+), 24 deletions(-) rename hal/src/peripherals/adc/{d5x => }/async_api.rs (100%) diff --git a/hal/src/peripherals/adc/d5x/async_api.rs b/hal/src/peripherals/adc/async_api.rs similarity index 100% rename from hal/src/peripherals/adc/d5x/async_api.rs rename to hal/src/peripherals/adc/async_api.rs diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9436fc928e30..b1959fe756aa 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -1,2 +1 @@ pub mod pin; -pub mod async_api; \ No newline at end of file diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 435a3f64f171..dc4dbf302caa 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -16,8 +16,13 @@ use crate::{ "adc-d5x" => "d5x/mod.rs", )] mod impls {} -mod adc_settings; +#[cfg(feature = "async")] +mod async_api; +#[cfg(feature = "async")] +pub use async_api::*; + +mod adc_settings; pub use adc_settings::*; use super::{calibration, clock}; @@ -34,8 +39,6 @@ pub use adc0::ctrlb::Resselselect as Resolution; /// Reference voltage (or its source) pub use adc0::refctrl::Refselselect as Reference; -pub use impls::async_api::InterruptHandler as AdcInterruptHandler; - /// Trait representing an ADC instance pub trait AdcInstance { #[cfg(feature = "async")] @@ -64,14 +67,17 @@ impl AdcInstance for Adc0 { #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC0; + #[inline] fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { &p.adc0 } + #[inline] fn enable_mclk(mclk: &mut Mclk) { mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); } + #[inline] fn calibrate(instance: &Self::Instance) { instance.calib().write(|w| unsafe { w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); @@ -81,8 +87,9 @@ impl AdcInstance for Adc0 { } #[cfg(feature = "async")] + #[inline] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - &impls::async_api::ADC_WAKERS[0] + &async_api::ADC_WAKERS[0] } } @@ -97,13 +104,17 @@ impl AdcInstance for Adc1 { #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC1; + #[inline] fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { &p.adc1 } + #[inline] fn enable_mclk(mclk: &mut Mclk) { mclk.apbdmask().modify(|_, w| w.adc1_().set_bit()); } + + #[inline] fn calibrate(instance: &Self::Instance) { instance.calib().write(|w| unsafe { w.biascomp().bits(calibration::adc1_biascomp_scale_cal()); @@ -113,8 +124,9 @@ impl AdcInstance for Adc1 { } #[cfg(feature = "async")] + #[inline] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - &impls::async_api::ADC_WAKERS[1] + &async_api::ADC_WAKERS[1] } } @@ -148,6 +160,7 @@ pub struct Channel { impl Channel { // NOTE: `new`` must be private so a channel isn't accidentally created outside this // module, breaking the typelevel guarantees laid out by the adc driver + #[inline] fn new() -> Channel { Channel { _pin: NoneT, @@ -161,6 +174,7 @@ impl Channel { /// /// This methods accepts any pin that can potentially be configured as an /// ADC channel, and automatically puts it in the Alternate B mode. + #[inline] pub fn with_pin>(self, pin: N) -> Channel { // NOTE: While AdcPin is implemented for any pin that has the *potential* to be // turned into an AlternateB pin (which is the ADC function), we know that any @@ -177,28 +191,29 @@ impl Channel { // These methods are only implemented for a Channel that holds a configured pin impl> Channel { + #[inline] pub fn read_blocking(&self, adc: &mut Adc) -> u16 { //f(Id::ID as u16); adc.read_blocking(Id::ID) } + #[inline] pub fn read_buffer_blocking(&self, adc: &mut Adc, dst: &mut [u16]) { //adc.read_buffer_blocking(Id::ID) todo!() } #[cfg(feature = "async")] + #[inline] pub async fn read(&self, adc: &mut Adc) -> u16 where - F: crate::async_hal::interrupts::Binding< - I::Interrupt, - impls::async_api::InterruptHandler, - >, + F: crate::async_hal::interrupts::Binding>, { adc.read(Id::ID).await } #[cfg(feature = "async")] + #[inline] pub async fn read_buffer(&self, _adc: &mut Adc, dst: &mut [u16]) { todo!() } @@ -222,6 +237,7 @@ impl Adc { /// /// NOTE: If you plan to run the chip up to 125C, then the maximum GCLK frequency for the ADC /// is restricted to 90Mhz for stable performance. + #[inline] pub fn new( adc: I::Instance, settings: AdcSettingsBuilder, @@ -249,6 +265,7 @@ impl Adc { Some((new_adc, Channels::new())) } + #[inline] pub fn configure(&mut self, settings: AdcSettingsBuilder) { // Disable ADC before we do anything! self.power_down(); @@ -289,6 +306,7 @@ impl Adc { self.sync(); } + #[inline] pub fn read_blocking(&mut self, ch: u8) -> u16 { self.mux(ch); self.power_up(); @@ -300,53 +318,57 @@ impl Adc { res } + #[inline] pub fn read_ptat_blocking(&mut self) -> u16 { self.read_blocking(0x19) } + #[inline] pub fn read_ctat_blocking(&mut self) -> u16 { self.read_blocking(0x1A) } } impl Adc { - #[inline(always)] + #[inline] fn sync(&self) { // Slightly more performant than checking the individual bits // since we avoid an extra instruction to bit shift while self.adc.syncbusy().read().bits() != 0 {} } - #[inline(always)] + #[inline] fn power_up(&mut self) { self.adc.ctrla().modify(|_, w| w.enable().set_bit()); self.sync(); } - #[inline(always)] + #[inline] fn power_down(&mut self) { self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); self.sync(); } - #[inline(always)] + #[inline] fn start_conversion(&mut self) { self.adc.swtrig().modify(|_, w| w.start().set_bit()); self.sync(); } + #[inline] fn enable_freerunning(&mut self) { self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); while self.adc.syncbusy().read().ctrlb().bit_is_set() {} } + #[inline] fn disable_freerunning(&mut self) { self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); while self.adc.syncbusy().read().ctrlb().bit_is_set() {} } /// Enables an interrupt when conversion is ready. - #[inline(always)] + #[inline] fn enable_interrupts(&mut self) { //self.adc.intflag().write(|w| w.resrdy().set_bit()); self.adc.intenset().write(|w| w.resrdy().set_bit()); @@ -354,27 +376,28 @@ impl Adc { } /// Disables the interrupt for when conversion is ready. - #[inline(always)] + #[inline] fn disable_interrupts(&mut self) { self.adc.intenclr().write(|w| w.resrdy().set_bit()); self.sync(); } - #[inline(always)] + #[inline] fn result(&self) -> u16 { self.adc.result().read().result().bits() } - #[inline(always)] + #[inline] fn is_interrupt(&self) -> bool { self.adc.intflag().read().bits() != 0 } - #[inline(always)] + #[inline] fn clear_interrupt(&self) { self.adc.intflag().write(|w| w.resrdy().set_bit()); } + #[inline] fn mux(&mut self, ch: u8) { self.adc .inputctrl() @@ -383,12 +406,10 @@ impl Adc { } #[cfg(feature = "async")] + #[inline] pub fn into_future(self, _irqs: F) -> Adc where - F: crate::async_hal::interrupts::Binding< - I::Interrupt, - impls::async_api::InterruptHandler, - >, + F: crate::async_hal::interrupts::Binding>, { use crate::async_hal::interrupts::InterruptSource; unsafe { @@ -405,8 +426,9 @@ impl Adc { #[cfg(feature = "async")] impl Adc where - F: crate::async_hal::interrupts::Binding>, + F: crate::async_hal::interrupts::Binding>, { + #[inline] pub async fn read(&mut self, ch: u8) -> u16 { use core::{future::poll_fn, task::Poll}; self.disable_interrupts(); @@ -517,6 +539,7 @@ macro_rules! define_channels_struct { ); impl Channels { + #[inline] fn new() -> Self { Self ( #( From f37b36faa8c751518bb46bf0d78ab2846c6872c7 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 22 Jan 2025 12:33:30 -0500 Subject: [PATCH 18/65] Add wait_flags method and do some extra cleanup --- hal/src/peripherals/adc/adc_settings.rs | 61 ++++--- hal/src/peripherals/adc/async_api.rs | 59 ++++++- hal/src/peripherals/adc/mod.rs | 214 +++++++++++++++--------- 3 files changed, 222 insertions(+), 112 deletions(-) diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs index f32cb6da0ed9..0c7387505538 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -46,19 +46,25 @@ pub enum AdcDivider { /// # ADC sampling rate settings /// -/// Multiple factors can affect the ADCs overall sampling rate, and this structure -/// allows for the configuring of the majority of factors that affect the sample rate of the ADC +/// Multiple factors can affect the ADCs overall sampling rate, and this +/// structure allows for the configuring of the majority of factors that affect +/// the sample rate of the ADC /// -/// To begin with, the ADC Clock is driven by the peripheral clock divided with a divider ([AdcDivider]). +/// To begin with, the ADC Clock is driven by the peripheral clock divided with +/// a divider ([AdcDivider]). /// -/// Each sample is read by the ADC over [AdcSettingsBuilder::sample_clock_cycles] clock cycles, and then -/// transmitted to the ADC register over [AdcSettingsBuilder::bit_width] clock cycles (1 clock cycle per bit) +/// Each sample is read by the ADC over +/// [AdcSettingsBuilder::sample_clock_cycles] clock cycles, and then transmitted +/// to the ADC register over [AdcSettingsBuilder::bit_width] clock cycles (1 +/// clock cycle per bit) /// -/// The ADC can also be configured to combine multiple simultaneous readings in either an average or summed mode -/// (See [AdcAccumulation]), this also affects the overall sample rate of the ADC as the ADC has to do multiple +/// The ADC can also be configured to combine multiple simultaneous readings in +/// either an average or summed mode (See [AdcAccumulation]), this also affects +/// the overall sample rate of the ADC as the ADC has to do multiple /// samples before a result is ready. /// -/// Therefore, the overall formula for calculating Sample rate (SPS) can be calculated like so: +/// Therefore, the overall formula for calculating Sample rate (SPS) can be +/// calculated like so: /// /// ## For single sample /// ``` @@ -77,12 +83,12 @@ pub struct AdcSettingsBuilder { impl AdcSettingsBuilder { /// - /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is 48_000_000) using the following settings: + /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is + /// 48_000_000) using the following settings: /// * clock divider factor of 32 /// * 5 clock cycles per sample /// * 12bit sampling /// * Single accumulation (No averaging or summing) - /// pub fn new() -> Self { Self { clk_divider: AdcDivider::Div32, @@ -93,11 +99,11 @@ impl AdcSettingsBuilder { } /// - /// This setting adjusts the ADC clock frequency by dividing the input clock for the ADC. + /// This setting adjusts the ADC clock frequency by dividing the input clock + /// for the ADC. /// /// ## Example: /// * Input clock 48MHz, div 32 => ADC Clock is 1.5MHz - /// pub fn clock_divider(mut self, div: AdcDivider) -> Self { self.clk_divider = div; self @@ -109,30 +115,34 @@ impl AdcSettingsBuilder { self } - /// Sets how the ADC will accumulate values before actually returning a value. + /// Sets how the ADC will accumulate values before actually returning a + /// value. /// - /// The default is single (ADC will return a sample as soon as it is measured) + /// The default is single (ADC will return a sample as soon as it is + /// measured) /// - /// Setting [AdcAccumulation::Summed] will make the ADC take 'n' samples, and sum the - /// total before returning it + /// Setting [AdcAccumulation::Summed] will make the ADC take 'n' samples, + /// and sum the total before returning it /// - /// Setting [AdcAccumulation::Average] will make the ADC take 'n' samples, and average the - /// total before returning it + /// Setting [AdcAccumulation::Average] will make the ADC take 'n' samples, + /// and average the total before returning it /// - /// NOTE: Selecting [AdcAccumulation::Summed] or [AdcAccumulation::Average] will reduce the overall - /// ADC sample rate by a factor of 1/n, and the returned value will be 16bits long no matter - /// what the sample Bit width was selected as + /// NOTE: Selecting [AdcAccumulation::Summed] or [AdcAccumulation::Average] + /// will reduce the overall ADC sample rate by a factor of 1/n, and the + /// returned value will be 16bits long no matter what the sample Bit + /// width was selected as pub fn accumulation_method(mut self, method: AdcAccumulation) -> Self { self.accumulation = method; self } - /// This adjusts the number of ADC clock cycles taken to sample a single sample. - /// The higher this number, the longer it will take the ADC to sample each sample. + /// This adjusts the number of ADC clock cycles taken to sample a single + /// sample. The higher this number, the longer it will take the ADC to + /// sample each sample. /// /// ## Safety - /// Internally, this function will clamp the minimum input value to 1 to avoid 0 - /// + /// Internally, this function will clamp the minimum input value to 1 to + /// avoid 0 pub fn clock_cycles_per_sample(mut self, num: u8) -> Self { self.sample_clock_cycles = 1.max(num); // Prevent 0 self @@ -140,7 +150,6 @@ impl AdcSettingsBuilder { /// /// Returns a calculated sample rate of the ADC with these settings - /// pub fn calculate_sps(&self, clock_freq: u32) -> u32 { let div = self.clk_divider as u32; let adc_clk_freq = clock_freq / div; diff --git a/hal/src/peripherals/adc/async_api.rs b/hal/src/peripherals/adc/async_api.rs index 55ec450024ab..8c9ab8bd41f7 100644 --- a/hal/src/peripherals/adc/async_api.rs +++ b/hal/src/peripherals/adc/async_api.rs @@ -1,6 +1,9 @@ use core::marker::PhantomData; -use crate::{adc::AdcInstance, async_hal::interrupts::Handler}; +use crate::{ + adc::{Adc, AdcInstance, Error, Flags}, + async_hal::interrupts::Handler, +}; use embassy_sync::waitqueue::AtomicWaker; @@ -18,16 +21,56 @@ impl crate::typelevel::Sealed for InterruptHandler {} impl Handler for InterruptHandler { unsafe fn on_interrupt() { - let mut peripherals = unsafe { crate::pac::Peripherals::steal() }; + let mut peripherals = crate::pac::Peripherals::steal(); let adc = A::peripheral_reg_block(&mut peripherals); - critical_section::with(|_| { - // Just check if result ready is set. Todo - Handle overrun and other interrupt reasons - if adc.intflag().read().resrdy().bit_is_set() { - // Wake up! - A::waker().wake(); + + let flags_pending = Flags::from_bits_truncate(adc.intflag().read().bits()); + let enabled_flags = Flags::from_bits_truncate(adc.intenset().read().bits()); + + if enabled_flags.intersects(flags_pending) { + adc.intenclr().write(|w| w.bits(flags_pending.bits())); + // Wake up! + A::waker().wake(); + } + } +} + +impl Adc +where + F: crate::async_hal::interrupts::Binding>, +{ + #[inline] + pub(super) async fn wait_flags(&mut self, flags_to_wait: Flags) -> Result<(), Error> { + use core::task::Poll; + + // We automatically check for errors + let flags_to_wait = flags_to_wait | Flags::OVERRUN; + self.disable_interrupts(Flags::all()); + + core::future::poll_fn(|cx| { + // Scope maybe_pending so we don't forget to re-poll the register later down. + { + let maybe_pending = self.read_flags(); + if flags_to_wait.intersects(maybe_pending) { + let result = self.check_and_clear_flags(maybe_pending); + self.disable_interrupts(flags_to_wait); + return Poll::Ready(result); + } + } + + I::waker().register(cx.waker()); + self.enable_interrupts(flags_to_wait); + + let maybe_pending = self.read_flags(); + + if !flags_to_wait.intersects(maybe_pending) { + Poll::Pending } else { - // Handle other cases + let result = self.check_and_clear_flags(maybe_pending); + self.disable_interrupts(flags_to_wait); + Poll::Ready(result) } }) + .await } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index dc4dbf302caa..7d6ad7d85bcc 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -39,6 +39,28 @@ pub use adc0::ctrlb::Resselselect as Resolution; /// Reference voltage (or its source) pub use adc0::refctrl::Refselselect as Reference; +pub enum Error { + /// Clock too fast. + /// + /// The ADC requires that it's fed a GCLK running at MAXIMUM 100 MHz + /// (SAMDx5x). Above 100°C, the GCLK must run at or below 90 MHz. + ClockTooFast, + /// Buffer overflowed + BufferOverrun, +} + +bitflags::bitflags! { + #[derive(Clone, Copy)] + pub struct Flags: u8 { + /// Window monitor interrupt + const WINMON = 0x04; + /// Buffer overrun interrupt + const OVERRUN = 0x02; + /// Result ready interrupt + const RESRDY = 0x01; + } +} + /// Trait representing an ADC instance pub trait AdcInstance { #[cfg(feature = "async")] @@ -56,9 +78,10 @@ pub trait AdcInstance { fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } -// TODO: The next few lines will need to be adjusted for SAMD11 and SAMD21: they only have 1 ADC +// TODO: The next few lines will need to be adjusted for SAMD11 and SAMD21: they +// only have 1 ADC pub struct Adc0 { - adc: pac::Adc0, + _adc: pac::Adc0, } impl AdcInstance for Adc0 { type Instance = pac::Adc0; @@ -94,7 +117,7 @@ impl AdcInstance for Adc0 { } pub struct Adc1 { - adc: pac::Adc1, + _adc: pac::Adc1, } impl AdcInstance for Adc1 { @@ -149,7 +172,9 @@ pub trait ChId { /// ADC channel. /// /// This struct must hold a concrete [`Pin`](crate::gpio::Pin) which implements -/// [`AdcPin`] in order to perform conversions. By default, channels don't hold any pin when they are created by [`Adc::new`]. Use [`Channel::with_pin`](Self::with_pin) to give a pin to this [`Channel`]. +/// [`AdcPin`] in order to perform conversions. By default, channels don't hold +/// any pin when they are created by [`Adc::new`]. Use +/// [`Channel::with_pin`](Self::with_pin) to give a pin to this [`Channel`]. pub struct Channel { _pin: P, _instance: PhantomData, @@ -158,8 +183,8 @@ pub struct Channel { // These methods are only implemented for a Channel that doesn't hold a pin yet impl Channel { - // NOTE: `new`` must be private so a channel isn't accidentally created outside this - // module, breaking the typelevel guarantees laid out by the adc driver + // NOTE: `new`` must be private so a channel isn't accidentally created outside + // this module, breaking the typelevel guarantees laid out by the adc driver #[inline] fn new() -> Channel { Channel { @@ -179,8 +204,8 @@ impl Channel { // NOTE: While AdcPin is implemented for any pin that has the *potential* to be // turned into an AlternateB pin (which is the ADC function), we know that any // Channel holding a type implementing AdcPin must have already configured the - // pin to the alternate B function, since the with_pin method is the only way to insert a - // pin into the Channel. + // pin to the alternate B function, since the with_pin method is the only way to + // insert a pin into the Channel. Channel { _pin: pin.into_function(), _instance: PhantomData, @@ -193,7 +218,6 @@ impl Channel { impl> Channel { #[inline] pub fn read_blocking(&self, adc: &mut Adc) -> u16 { - //f(Id::ID as u16); adc.read_blocking(Id::ID) } @@ -214,7 +238,10 @@ impl> Channel { #[cfg(feature = "async")] #[inline] - pub async fn read_buffer(&self, _adc: &mut Adc, dst: &mut [u16]) { + pub async fn read_buffer(&self, _adc: &mut Adc, dst: &mut [u16]) + where + F: crate::async_hal::interrupts::Binding>, + { todo!() } } @@ -231,22 +258,22 @@ impl Adc { /// Construct a new ADC instance /// /// ## Important - /// This function will return None (No ADC) if the clock source provided - /// is faster than 100Mhz, since this is the maximum frequency for GCLK_ADCx as per - /// the datasheet. + /// This function will return `Err` if the clock source provided + /// is faster than 100Mhz, since this is the maximum frequency for GCLK_ADCx + /// as per the datasheet. /// - /// NOTE: If you plan to run the chip up to 125C, then the maximum GCLK frequency for the ADC - /// is restricted to 90Mhz for stable performance. + /// NOTE: If you plan to run the chip above 100°C, then the maximum GCLK + /// frequency for the ADC is restricted to 90Mhz for stable performance. #[inline] pub fn new( adc: I::Instance, settings: AdcSettingsBuilder, mclk: &mut Mclk, clock: I::Clock, - ) -> Option<(Self, Channels)> { + ) -> Result<(Self, Channels), Error> { if (clock.into() as Hertz).to_Hz() > 100_000_000 { // Clock source is too fast - return None; + return Err(Error::ClockTooFast); } I::enable_mclk(mclk); @@ -257,12 +284,11 @@ impl Adc { // Reset ADC here as we cannot guarantee its state // This also disables the ADC - new_adc.adc.ctrla().modify(|_, w| w.swrst().set_bit()); - new_adc.sync(); + new_adc.software_reset(); I::calibrate(&new_adc.adc); new_adc.configure(settings); - Some((new_adc, Channels::new())) + Ok((new_adc, Channels::new())) } #[inline] @@ -308,12 +334,19 @@ impl Adc { #[inline] pub fn read_blocking(&mut self, ch: u8) -> u16 { + self.disable_freerunning(); + // Clear overrun errors that might've occured before we try to read anything + let _ = self.check_and_clear_flags(self.read_flags()); + self.mux(ch); self.power_up(); - self.sync(); self.start_conversion(); - while self.adc.intflag().read().resrdy().bit_is_clear() {} - let res = self.result(); + + while !self.read_flags().contains(Flags::RESRDY) { + core::hint::spin_loop(); + } + + let res = self.conversion_result(); self.power_down(); res } @@ -327,14 +360,79 @@ impl Adc { pub fn read_ctat_blocking(&mut self) -> u16 { self.read_blocking(0x1A) } + + /// Return the underlying ADC PAC object + /// + /// You must also return all channels to the ADC to free its resources. + #[inline] + pub fn free(mut self, _channels: Channels) -> I::Instance { + self.software_reset(); + self.adc + } + + /// Reset the peripheral. + /// + /// This also disables the ADC. + #[inline] + fn software_reset(&mut self) { + self.adc.ctrla().modify(|_, w| w.swrst().set_bit()); + self.sync(); + } } impl Adc { + #[cfg(feature = "async")] + #[inline] + pub fn into_future(self, _irqs: F) -> Adc + where + F: crate::async_hal::interrupts::Binding>, + { + use crate::async_hal::interrupts::InterruptSource; + unsafe { + I::Interrupt::unpend(); + I::Interrupt::enable(); + } + Adc { + adc: self.adc, + _irqs: PhantomData, + } + } + + #[inline] + fn read_flags(&self) -> Flags { + let bits = self.adc.intflag().read().bits(); + Flags::from_bits_truncate(bits) + } + + #[inline] + fn clear_flags(&mut self, flags: Flags) { + unsafe { + self.adc.intflag().write(|w| w.bits(flags.bits())); + } + } + + /// Check the interrupt flags, clears them and returns `Err` if an ovreflow + /// occured + #[inline] + fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { + // Keep a copy around so we can check for errors later + let flags_to_clear = flags; + self.clear_flags(flags_to_clear); + + if flags.contains(Flags::OVERRUN) { + Err(Error::BufferOverrun) + } else { + Ok(()) + } + } + #[inline] fn sync(&self) { // Slightly more performant than checking the individual bits // since we avoid an extra instruction to bit shift - while self.adc.syncbusy().read().bits() != 0 {} + while self.adc.syncbusy().read().bits() != 0 { + core::hint::spin_loop(); + } } #[inline] @@ -369,34 +467,21 @@ impl Adc { /// Enables an interrupt when conversion is ready. #[inline] - fn enable_interrupts(&mut self) { - //self.adc.intflag().write(|w| w.resrdy().set_bit()); - self.adc.intenset().write(|w| w.resrdy().set_bit()); - self.sync(); + fn enable_interrupts(&mut self, flags: Flags) { + unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) }; } /// Disables the interrupt for when conversion is ready. #[inline] - fn disable_interrupts(&mut self) { - self.adc.intenclr().write(|w| w.resrdy().set_bit()); - self.sync(); + fn disable_interrupts(&mut self, flags: Flags) { + unsafe { self.adc.intenclr().write(|w| w.bits(flags.bits())) }; } #[inline] - fn result(&self) -> u16 { + fn conversion_result(&self) -> u16 { self.adc.result().read().result().bits() } - #[inline] - fn is_interrupt(&self) -> bool { - self.adc.intflag().read().bits() != 0 - } - - #[inline] - fn clear_interrupt(&self) { - self.adc.intflag().write(|w| w.resrdy().set_bit()); - } - #[inline] fn mux(&mut self, ch: u8) { self.adc @@ -404,23 +489,6 @@ impl Adc { .modify(|_, w| unsafe { w.muxpos().bits(ch) }); self.sync() } - - #[cfg(feature = "async")] - #[inline] - pub fn into_future(self, _irqs: F) -> Adc - where - F: crate::async_hal::interrupts::Binding>, - { - use crate::async_hal::interrupts::InterruptSource; - unsafe { - I::Interrupt::unpend(); - I::Interrupt::enable(); - } - Adc { - adc: self.adc, - _irqs: PhantomData, - } - } } #[cfg(feature = "async")] @@ -430,28 +498,18 @@ where { #[inline] pub async fn read(&mut self, ch: u8) -> u16 { - use core::{future::poll_fn, task::Poll}; - self.disable_interrupts(); + self.disable_freerunning(); + // Clear overrun errors that might've occured before we try to read anything + let _ = self.check_and_clear_flags(self.read_flags()); + self.mux(ch); - self.power_up(); // Enable ADC + self.power_up(); self.start_conversion(); - let result = poll_fn(|cx| { - if self.is_interrupt() { - self.clear_interrupt(); - self.disable_interrupts(); - return Poll::Ready(self.result()); - } - I::waker().register(cx.waker()); - self.enable_interrupts(); + // Here we explicitly ignore the result, because we know that + // overrun errors are impossible since the ADC is configured in one-shot mode. + let _ = self.wait_flags(Flags::RESRDY).await; + let result = self.conversion_result(); - if self.is_interrupt() { - self.clear_interrupt(); - self.disable_interrupts(); - return Poll::Ready(self.result()); - } - Poll::Pending - }) - .await; self.power_down(); result } From bf25449477c5d057b5935ca0017540c83ae77982 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 22 Jan 2025 13:00:49 -0500 Subject: [PATCH 19/65] Add blocking and async buffered read methods --- hal/src/peripherals/adc/adc_settings.rs | 8 +++- hal/src/peripherals/adc/mod.rs | 53 ++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs index 0c7387505538..c8db251ea681 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -161,7 +161,13 @@ impl AdcSettingsBuilder { AdcAccumulation::Average(adc_sample_count) => adc_sample_count as u32, AdcAccumulation::Summed(adc_sample_count) => adc_sample_count as u32, }; - clocks_per_sample *= multi as u32; + clocks_per_sample *= multi; adc_clk_freq / clocks_per_sample } } + +impl Default for AdcSettingsBuilder { + fn default() -> Self { + Self::new() + } +} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 7d6ad7d85bcc..feb5f5a79630 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -222,9 +222,8 @@ impl> Channel { } #[inline] - pub fn read_buffer_blocking(&self, adc: &mut Adc, dst: &mut [u16]) { - //adc.read_buffer_blocking(Id::ID) - todo!() + pub fn read_buffer_blocking(&self, adc: &mut Adc, dst: &mut [u16]) -> Result<(), Error> { + adc.read_buffer_blocking(Id::ID, dst) } #[cfg(feature = "async")] @@ -238,11 +237,11 @@ impl> Channel { #[cfg(feature = "async")] #[inline] - pub async fn read_buffer(&self, _adc: &mut Adc, dst: &mut [u16]) + pub async fn read_buffer(&self, adc: &mut Adc, dst: &mut [u16]) -> Result<(), Error> where F: crate::async_hal::interrupts::Binding>, { - todo!() + adc.read_buffer(Id::ID, dst).await } } @@ -338,6 +337,7 @@ impl Adc { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); + self.disable_interrupts(Flags::all()); self.mux(ch); self.power_up(); self.start_conversion(); @@ -351,6 +351,30 @@ impl Adc { res } + #[inline] + pub fn read_buffer_blocking(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + // Clear overrun errors that might've occured before we try to read anything + let _ = self.check_and_clear_flags(self.read_flags()); + self.enable_freerunning(); + + self.disable_interrupts(Flags::all()); + self.mux(ch); + self.power_up(); + + for result in dst.iter_mut() { + while !self.read_flags().contains(Flags::RESRDY) { + core::hint::spin_loop(); + } + + *result = self.conversion_result(); + self.check_and_clear_flags(Flags::OVERRUN)?; + } + + self.power_down(); + + Ok(()) + } + #[inline] pub fn read_ptat_blocking(&mut self) -> u16 { self.read_blocking(0x19) @@ -467,6 +491,7 @@ impl Adc { /// Enables an interrupt when conversion is ready. #[inline] + #[allow(dead_code)] fn enable_interrupts(&mut self, flags: Flags) { unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) }; } @@ -514,6 +539,24 @@ where result } + #[inline] + pub async fn read_buffer(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + // Clear overrun errors that might've occured before we try to read anything + let _ = self.check_and_clear_flags(self.read_flags()); + self.enable_freerunning(); + + self.mux(ch); + self.power_up(); + + for result in dst.iter_mut() { + self.wait_flags(Flags::RESRDY).await?; + *result = self.conversion_result(); + } + + self.power_down(); + Ok(()) + } + /* pub async fn read_into_buffer(&mut self, ch: u8, buffer: &mut [u16], f: X) { if buffer.is_empty() { From 6bbbfda0f402bfa750d333c0a6bee6ab77885487 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 18:10:59 +0000 Subject: [PATCH 20/65] Derive debug for Adc Error --- hal/src/peripherals/adc/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index feb5f5a79630..820993871fc0 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -39,6 +39,7 @@ pub use adc0::ctrlb::Resselselect as Resolution; /// Reference voltage (or its source) pub use adc0::refctrl::Refselselect as Reference; +#[derive(Debug)] pub enum Error { /// Clock too fast. /// From 53aafe0cfa2f4a94e8bec03f054c49143c0f3543 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 18:22:37 +0000 Subject: [PATCH 21/65] Fix ADC read_buffer not ever starting --- hal/src/peripherals/adc/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 820993871fc0..a2e885eb64af 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -361,7 +361,7 @@ impl Adc { self.disable_interrupts(Flags::all()); self.mux(ch); self.power_up(); - + self.start_conversion(); for result in dst.iter_mut() { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); @@ -481,13 +481,13 @@ impl Adc { #[inline] fn enable_freerunning(&mut self) { self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + self.sync(); } #[inline] fn disable_freerunning(&mut self) { self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - while self.adc.syncbusy().read().ctrlb().bit_is_set() {} + self.sync(); } /// Enables an interrupt when conversion is ready. @@ -548,7 +548,7 @@ where self.mux(ch); self.power_up(); - + self.start_conversion(); for result in dst.iter_mut() { self.wait_flags(Flags::RESRDY).await?; *result = self.conversion_result(); From 88f74f59b15f9a103e3bd47203f491fd5261a51e Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 19:42:04 +0000 Subject: [PATCH 22/65] Fix ADC reading blocking buffer always returning BufferOverrun --- hal/src/peripherals/adc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index a2e885eb64af..432d92b9302f 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -368,7 +368,7 @@ impl Adc { } *result = self.conversion_result(); - self.check_and_clear_flags(Flags::OVERRUN)?; + self.check_and_clear_flags(self.read_flags())?; } self.power_down(); @@ -436,7 +436,7 @@ impl Adc { } } - /// Check the interrupt flags, clears them and returns `Err` if an ovreflow + /// Check the interrupt flags, clears them and returns `Err` if an overflow /// occured #[inline] fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { From 53955a625e4c43ec1844c189eb311cd7e13f5620 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 20:55:09 +0000 Subject: [PATCH 23/65] Implement more ADCSettings --- hal/src/peripherals/adc/adc_settings.rs | 31 ++------ hal/src/peripherals/adc/mod.rs | 100 ++++++++++-------------- 2 files changed, 48 insertions(+), 83 deletions(-) diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs index c8db251ea681..f56d36ec7c0c 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -1,26 +1,10 @@ -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum AdcSampleCount { - Count1 = 1, - Count2 = 2, - Count4 = 4, - Count8 = 8, - Count16 = 16, - Count32 = 32, - Count64 = 64, - Count128 = 128, - Count256 = 256, - Count512 = 512, - Count1024 = 1024, -} +use crate::pac::adc0; -#[derive(Copy, Clone)] -pub enum AdcBitWidth { - Eight = 8, - Ten = 10, - Twelve = 12, -} +pub use adc0::avgctrl::Samplenumselect as AdcSampleCount; +pub use adc0::ctrlb::Resselselect as AdcResolution; /// Result accumulation strategy for the ADC +#[derive(Copy, Clone)] pub enum AdcAccumulation { /// The ADC will read once and then the result is ready Single, @@ -74,10 +58,11 @@ pub enum AdcDivider { /// ``` /// SPS = (GCLK_ADC / clk_divider) / (n * (sample_clock_cycles + bit_width)) /// ``` +#[derive(Copy, Clone)] pub struct AdcSettingsBuilder { pub clk_divider: AdcDivider, pub sample_clock_cycles: u8, - pub bit_width: AdcBitWidth, + pub bit_width: AdcResolution, pub accumulation: AdcAccumulation, } @@ -93,7 +78,7 @@ impl AdcSettingsBuilder { Self { clk_divider: AdcDivider::Div32, sample_clock_cycles: 5, - bit_width: AdcBitWidth::Twelve, + bit_width: AdcResolution::_12bit, accumulation: AdcAccumulation::Single, } } @@ -110,7 +95,7 @@ impl AdcSettingsBuilder { } /// This setting adjusts the bit width of each ADC sample - pub fn sample_bit_width(mut self, bit_width: AdcBitWidth) -> Self { + pub fn sample_resolution(mut self, bit_width: AdcResolution) -> Self { self.bit_width = bit_width; self } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 432d92b9302f..ed03aa13eec8 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,10 +1,12 @@ use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_module}; +use pac::dmac::channel::chctrla::Trigactselect; use pac::{Mclk, Peripherals}; use seq_macro::seq; use crate::{ + dmac::{self, sram::DmacDescriptor, AnyChannel, Beat, ReadyFuture}, gpio::AnyPin, pac, time::Hertz, @@ -281,20 +283,16 @@ impl Adc { adc, _irqs: PhantomData, }; - - // Reset ADC here as we cannot guarantee its state - // This also disables the ADC - new_adc.software_reset(); - - I::calibrate(&new_adc.adc); new_adc.configure(settings); Ok((new_adc, Channels::new())) } #[inline] pub fn configure(&mut self, settings: AdcSettingsBuilder) { - // Disable ADC before we do anything! - self.power_down(); + // Reset ADC here as we cannot guarantee its state + // This also disables the ADC + self.software_reset(); + I::calibrate(&self.adc); self.sync(); self.adc.ctrla().modify(|_, w| match settings.clk_divider { AdcDivider::Div2 => w.prescaler().div2(), @@ -306,11 +304,10 @@ impl Adc { AdcDivider::Div128 => w.prescaler().div128(), AdcDivider::Div256 => w.prescaler().div256(), }); - self.adc.ctrlb().modify(|_, w| match settings.bit_width { - AdcBitWidth::Eight => w.ressel()._8bit(), - AdcBitWidth::Ten => w.ressel()._10bit(), - AdcBitWidth::Twelve => w.ressel()._12bit(), - }); + self.sync(); + self.adc + .ctrlb() + .modify(|_, w| w.ressel().variant(settings.bit_width)); self.sync(); self.adc @@ -320,10 +317,36 @@ impl Adc { self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) self.sync(); - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(Samplenumselect::_1); - unsafe { w.adjres().bits(0) } - }); + match settings.accumulation { + AdcAccumulation::Single => { + // 1 sample to be used as is + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(Samplenumselect::_1); + unsafe { w.adjres().bits(0) } + }); + } + AdcAccumulation::Average(adc_sample_count) => { + // A total of `adc_sample_count` elements will be averaged by the ADC + // before it returns the result + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(adc_sample_count); + unsafe { + // Table 45-3 SAME51 datasheet + w.adjres() + .bits(core::cmp::min(adc_sample_count as u8, 0x04)) + } + }); + } + AdcAccumulation::Summed(adc_sample_count) => { + // A total of `adc_sample_count` elements will be summed by the ADC + // before it returns the result + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(adc_sample_count); + unsafe { w.adjres().bits(0) } + }); + } + } + self.sync(); self.adc @@ -557,49 +580,6 @@ where self.power_down(); Ok(()) } - - /* - pub async fn read_into_buffer(&mut self, ch: u8, buffer: &mut [u16], f: X) { - if buffer.is_empty() { - return; - } - use core::{future::poll_fn, task::Poll}; - self.disable_interrupts(); - self.sync(); - self.mux(ch); - self.sync(); - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); // Enable ADC - self.sync(); - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - let mut pos = 0; - poll_fn(|cx| { - if self.is_interrupt() { - self.clear_interrupt(); - buffer[pos] = self.result(); - pos += 1; - if pos == buffer.len() { - self.disable_interrupts(); - return Poll::Ready(()); - } - } - f(pos); - I::waker().register(cx.waker()); - self.enable_interrupts(); - if self.is_interrupt() { - self.clear_interrupt(); - buffer[pos] = self.result(); - pos += 1; - if pos == buffer.len() { - self.disable_interrupts(); - return Poll::Ready(()); - } - } - Poll::Pending - }) - .await; - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); // Stop ADC (No sync required) - } - */ } // Here I declare the number of channels for the SAME51 ADC From 9262d339ed71d99018918d607b6998a83b3f1a3b Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 22 Jan 2025 22:48:06 +0000 Subject: [PATCH 24/65] Add in temperature calibration parameters to calibration --- hal/src/peripherals/calibration/d5x.rs | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/hal/src/peripherals/calibration/d5x.rs b/hal/src/peripherals/calibration/d5x.rs index 340e2415ad3b..ac6d965af945 100644 --- a/hal/src/peripherals/calibration/d5x.rs +++ b/hal/src/peripherals/calibration/d5x.rs @@ -59,3 +59,43 @@ pub fn adc1_biasref_scale_cal() -> u8 { pub fn adc1_biasr2r_scale_cal() -> u8 { cal(3, 0, 0b111) as u8 } + +/// Temperature calibration - Integer part of calibration temperature TL +pub fn tli() -> u8 { + cal(0x80, 7, 0b11111111) as u8 +} + +/// Temperature calibration - Decimal part of calibration temperature TL +pub fn tld() -> u8 { + cal(0x80 + 1, 3, 0b1111) as u8 +} + +/// Temperature calibration - Integer part of calibration temperature TH +pub fn thi() -> u8 { + cal(0x80 + 2, 3, 0b11111111) as u8 +} + +/// Temperature calibration - Decimal part of calibration temperature TH +pub fn thd() -> u8 { + cal(0x80 + 2, 7, 0b1111) as u8 +} + +/// Temperature calibration - Parameter VPL +pub fn vpl() -> u16 { + cal(0x80 + 6, 3, 0b111111111111) as u16 +} + +/// Temperature calibration - Parameter VPH +pub fn vph() -> u16 { + cal(0x80 + 7, 7, 0b111111111111) as u16 +} + +/// Temperature calibration - Parameter VCL +pub fn vcl() -> u16 { + cal(0x80 + 8, 7, 0b111111111111) as u16 +} + +/// Temperature calibration - Parameter VCH +pub fn vch() -> u16 { + cal(0x80 + 9, 7, 0b111111111111) as u16 +} From 627d9a9abb3e66b530f7399f02db7e195d803a42 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 23 Jan 2025 07:30:58 +0000 Subject: [PATCH 25/65] Add in CPU Temperature reading --- hal/src/peripherals/adc/mod.rs | 69 +++++++++++++++++++++++--- hal/src/peripherals/calibration/d5x.rs | 22 ++++++++ 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index ed03aa13eec8..7c212cb3753e 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -50,6 +50,12 @@ pub enum Error { ClockTooFast, /// Buffer overflowed BufferOverrun, + /// Temperature sensor not enabled + /// + /// This is returned when attempting to read the CPU temperature, and + /// the SUPC peripheral has not been configured correctly to expose + /// the temperature sensors. + TemperatureSensorNotEnabled, } bitflags::bitflags! { @@ -252,6 +258,7 @@ impl> Channel { pub struct Adc { adc: I::Instance, _irqs: PhantomData, + cfg: AdcSettingsBuilder, } pub struct AdcFuture; @@ -282,6 +289,7 @@ impl Adc { let mut new_adc = Self { adc, _irqs: PhantomData, + cfg: settings.clone(), }; new_adc.configure(settings); Ok((new_adc, Channels::new())) @@ -400,13 +408,27 @@ impl Adc { } #[inline] - pub fn read_ptat_blocking(&mut self) -> u16 { - self.read_blocking(0x19) - } - - #[inline] - pub fn read_ctat_blocking(&mut self) -> u16 { - self.read_blocking(0x1A) + /// Returns the CPU temperature in degrees C + /// + /// This requires that the [pac::Supc] peripheral is configured with + /// tsen and ondemand bits enabled, otherwise this function will return + /// [Error::TemperatureSensorNotEnabled] + pub fn read_cpu_temperature_blocking(&mut self, supc: &pac::Supc) -> Result { + let vref = supc.vref().read(); + if vref.tsen().bit_is_clear() || vref.ondemand().bit_is_clear() { + return Err(Error::TemperatureSensorNotEnabled); + } + let mut tp = self.read_blocking(0x1C) as f32; + let mut tc = self.read_blocking(0x1D) as f32; + + if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { + // to prevent incorrect readings, divide by number of samples if the + // ADC was already configured in summation mode + let div: f32 = (2u16.pow(sum as u32)) as f32; + tp /= div; + tc /= div; + } + Ok(self.tp_tc_to_temp(tp, tc)) } /// Return the underlying ADC PAC object @@ -443,6 +465,7 @@ impl Adc { Adc { adc: self.adc, _irqs: PhantomData, + cfg: self.cfg, } } @@ -474,6 +497,19 @@ impl Adc { } } + #[inline] + fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { + let tl = calibration::tl(); + let th = calibration::th(); + let vpl = calibration::vpl() as f32; + let vph = calibration::vph() as f32; + let vcl = calibration::vcl() as f32; + let vch = calibration::vch() as f32; + + (tl * vph * tc - vpl * th * tc - tl * vch * tp + th * vcl * tp) + / (vcl * tp - vch * tp - vpl * tc + vph * tc) + } + #[inline] fn sync(&self) { // Slightly more performant than checking the individual bits @@ -580,6 +616,25 @@ where self.power_down(); Ok(()) } + + #[inline] + pub async fn read_cpu_temperature(&mut self, supc: &pac::Supc) -> Result { + let vref = supc.vref().read(); + if vref.tsen().bit_is_clear() || vref.ondemand().bit_is_clear() { + return Err(Error::TemperatureSensorNotEnabled); + } + let mut tp = self.read(0x1C).await as f32; + let mut tc = self.read(0x1D).await as f32; + + if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { + // to prevent incorrect readings, divide by number of samples if the + // ADC was already configured in summation mode + let div: f32 = (2u16.pow(sum as u32)) as f32; + tp /= div; + tc /= div; + } + Ok(self.tp_tc_to_temp(tp, tc)) + } } // Here I declare the number of channels for the SAME51 ADC diff --git a/hal/src/peripherals/calibration/d5x.rs b/hal/src/peripherals/calibration/d5x.rs index ac6d965af945..0329e2f58f28 100644 --- a/hal/src/peripherals/calibration/d5x.rs +++ b/hal/src/peripherals/calibration/d5x.rs @@ -15,6 +15,20 @@ fn cal(addr_offset: u32, bit_shift: u32, bit_mask: u32) -> u32 { } } +// Needed for temperature calibration values stored in NVM +fn parts_to_f32(int: u32, dec: u32) -> f32 { + let mut dec = dec as f32; + + if dec < 10.0 { + dec /= 10.0; + } else if dec <= 100.0 { + dec /= 100.0; + } else { + dec /= 1000.0; + } + int as f32 + dec +} + /// USB TRANSN calibration value. Should be written to USB PADCAL register. pub fn usb_transn_cal() -> u8 { cal(4, 0, 0b11111) as u8 @@ -60,6 +74,14 @@ pub fn adc1_biasr2r_scale_cal() -> u8 { cal(3, 0, 0b111) as u8 } +pub fn tl() -> f32 { + parts_to_f32(cal(0x80, 7, 0b11111111), cal(0x80 + 1, 3, 0b1111)) +} + +pub fn th() -> f32 { + parts_to_f32(cal(0x80 + 2, 3, 0b11111111), cal(0x80 + 2, 7, 0b1111)) +} + /// Temperature calibration - Integer part of calibration temperature TL pub fn tli() -> u8 { cal(0x80, 7, 0b11111111) as u8 From d0e56025b6b3f35cab1031afa0b64931cdda4698 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 23 Jan 2025 10:59:42 +0000 Subject: [PATCH 26/65] Add CPU internal sensors reading, and setting VREF for adc --- hal/src/peripherals/adc/adc_settings.rs | 22 +++++-- hal/src/peripherals/adc/mod.rs | 81 ++++++++++++++++++++----- 2 files changed, 85 insertions(+), 18 deletions(-) diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs index f56d36ec7c0c..7234d5f57a53 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -2,6 +2,7 @@ use crate::pac::adc0; pub use adc0::avgctrl::Samplenumselect as AdcSampleCount; pub use adc0::ctrlb::Resselselect as AdcResolution; +pub use adc0::refctrl::Refselselect; /// Result accumulation strategy for the ADC #[derive(Copy, Clone)] @@ -28,7 +29,10 @@ pub enum AdcDivider { Div256 = 256, } -/// # ADC sampling rate settings +#[derive(Copy, Clone)] +pub enum VrefSource {} + +/// # ADC sampling settings /// /// Multiple factors can affect the ADCs overall sampling rate, and this /// structure allows for the configuring of the majority of factors that affect @@ -64,22 +68,27 @@ pub struct AdcSettingsBuilder { pub sample_clock_cycles: u8, pub bit_width: AdcResolution, pub accumulation: AdcAccumulation, + pub vref: Refselselect, } impl AdcSettingsBuilder { /// /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is - /// 48_000_000) using the following settings: + /// 48MHz) using the following settings: /// * clock divider factor of 32 - /// * 5 clock cycles per sample + /// * 6 clock cycles per sample /// * 12bit sampling /// * Single accumulation (No averaging or summing) + /// + /// ## Additional reading settings by default + /// * Use VDDANA as reference voltage for a full 0.0-3.3V reading pub fn new() -> Self { Self { clk_divider: AdcDivider::Div32, - sample_clock_cycles: 5, + sample_clock_cycles: 6, bit_width: AdcResolution::_12bit, accumulation: AdcAccumulation::Single, + vref: Refselselect::Intvcc1, } } @@ -100,6 +109,11 @@ impl AdcSettingsBuilder { self } + pub fn with_vref(mut self, reference: Refselselect) -> Self { + self.vref = reference; + self + } + /// Sets how the ADC will accumulate values before actually returning a /// value. /// diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 7c212cb3753e..7442d98f4dbc 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -58,6 +58,16 @@ pub enum Error { TemperatureSensorNotEnabled, } +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum CpuVoltageSource { + /// Core voltage + Core, + /// VBAT supply voltage + Vbat, + /// IO supply voltage + Io, +} + bitflags::bitflags! { #[derive(Clone, Copy)] pub struct Flags: u8 { @@ -356,30 +366,43 @@ impl Adc { } self.sync(); + self.set_reference(settings.vref); + } + /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where 1.0 = 2^(reading_bitwidth) + fn reading_to_f32(&self, raw: u16) -> f32 { + let max = match self.cfg.bit_width { + AdcResolution::_16bit => 65536, + AdcResolution::_12bit => 4096, + AdcResolution::_10bit => 1024, + AdcResolution::_8bit => 256, + }; + raw as f32 / max as f32 + } + + #[inline] + fn set_reference(&mut self, reference: Reference) { self.adc .refctrl() - .modify(|_, w| w.refsel().variant(Reference::Intref)); + .modify(|_, w| w.refsel().variant(reference)); self.sync(); } #[inline] pub fn read_blocking(&mut self, ch: u8) -> u16 { - self.disable_freerunning(); // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); - self.disable_interrupts(Flags::all()); self.mux(ch); self.power_up(); self.start_conversion(); - + self.clear_flags(Flags::RESRDY); + let _discard = self.conversion_result(); while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - - let res = self.conversion_result(); self.power_down(); + let res = self.conversion_result(); res } @@ -397,13 +420,11 @@ impl Adc { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - *result = self.conversion_result(); self.check_and_clear_flags(self.read_flags())?; } - self.power_down(); - + self.disable_freerunning(); Ok(()) } @@ -431,6 +452,35 @@ impl Adc { Ok(self.tp_tc_to_temp(tp, tc)) } + /// Read one of the CPU internal voltage supply, and return the value in + /// millivolts (Volts/1000) + pub fn read_internal_voltage(&mut self, src: CpuVoltageSource) -> u16 { + let chan = match src { + CpuVoltageSource::Core => 0x18, + CpuVoltageSource::Vbat => 0x19, + CpuVoltageSource::Io => 0x1A + }; + // Before reading, we have to select VDDANA as our reference voltage + // so we get the full 3v3 range + if self.cfg.vref != Reference::Intvcc1 { + // Modify it + self.set_reference(Reference::Intvcc1); + } + + let mut adc_val = self.read_blocking(chan); + if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { + let div: u16 = 2u16.pow(sum as u32); + adc_val /= div; + } + let mut res = self.reading_to_f32(adc_val) * 3.3 * 4.0; + + // Restore our settings + if Reference::Intvcc1 != self.cfg.vref { + self.set_reference(self.cfg.vref); + } + (res * 1000.0) as u16 + } + /// Return the underlying ADC PAC object /// /// You must also return all channels to the ADC to free its resources. @@ -533,8 +583,13 @@ impl Adc { #[inline] fn start_conversion(&mut self) { + // The double trigger here is in case the VREF value changed between + // reads, this discards the conversion made just after the VREF changed, + // which the data sheet tells us to do in order to not get a faulty reading + // right after changing VREF value self.adc.swtrig().modify(|_, w| w.start().set_bit()); self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); } #[inline] @@ -583,18 +638,15 @@ where { #[inline] pub async fn read(&mut self, ch: u8) -> u16 { - self.disable_freerunning(); // Clear overrun errors that might've occured before we try to read anything - let _ = self.check_and_clear_flags(self.read_flags()); - self.mux(ch); self.power_up(); + let _ = self.check_and_clear_flags(self.read_flags()); self.start_conversion(); // Here we explicitly ignore the result, because we know that // overrun errors are impossible since the ADC is configured in one-shot mode. let _ = self.wait_flags(Flags::RESRDY).await; let result = self.conversion_result(); - self.power_down(); result } @@ -602,11 +654,11 @@ where #[inline] pub async fn read_buffer(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything - let _ = self.check_and_clear_flags(self.read_flags()); self.enable_freerunning(); self.mux(ch); self.power_up(); + let _ = self.check_and_clear_flags(self.read_flags()); self.start_conversion(); for result in dst.iter_mut() { self.wait_flags(Flags::RESRDY).await?; @@ -614,6 +666,7 @@ where } self.power_down(); + self.disable_freerunning(); Ok(()) } From 61e8fc605fb6ace75565e01e0cb3c743ec12e222 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 23 Jan 2025 13:22:51 +0000 Subject: [PATCH 27/65] Restrict internal CPU sensors to primary CPU ADC --- hal/src/peripherals/adc/mod.rs | 91 +++++++++++++++------------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 7442d98f4dbc..e567dc00cfe4 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -80,6 +80,9 @@ bitflags::bitflags! { } } +/// Marker for which ADC has access to the CPUs internal sensors +pub trait PrimaryAdc {} + /// Trait representing an ADC instance pub trait AdcInstance { #[cfg(feature = "async")] @@ -102,6 +105,9 @@ pub trait AdcInstance { pub struct Adc0 { _adc: pac::Adc0, } + +impl PrimaryAdc for Adc0 {} + impl AdcInstance for Adc0 { type Instance = pac::Adc0; type Clock = clock::Adc0Clock; @@ -428,6 +434,39 @@ impl Adc { Ok(()) } + /// Return the underlying ADC PAC object + /// + /// You must also return all channels to the ADC to free its resources. + #[inline] + pub fn free(mut self, _channels: Channels) -> I::Instance { + self.software_reset(); + self.adc + } + + /// Reset the peripheral. + /// + /// This also disables the ADC. + #[inline] + fn software_reset(&mut self) { + self.adc.ctrla().modify(|_, w| w.swrst().set_bit()); + self.sync(); + } +} + +impl Adc { + #[inline] + fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { + let tl = calibration::tl(); + let th = calibration::th(); + let vpl = calibration::vpl() as f32; + let vph = calibration::vph() as f32; + let vcl = calibration::vcl() as f32; + let vch = calibration::vch() as f32; + + (tl * vph * tc - vpl * th * tc - tl * vch * tp + th * vcl * tp) + / (vcl * tp - vch * tp - vpl * tc + vph * tc) + } + #[inline] /// Returns the CPU temperature in degrees C /// @@ -458,7 +497,7 @@ impl Adc { let chan = match src { CpuVoltageSource::Core => 0x18, CpuVoltageSource::Vbat => 0x19, - CpuVoltageSource::Io => 0x1A + CpuVoltageSource::Io => 0x1A, }; // Before reading, we have to select VDDANA as our reference voltage // so we get the full 3v3 range @@ -480,24 +519,6 @@ impl Adc { } (res * 1000.0) as u16 } - - /// Return the underlying ADC PAC object - /// - /// You must also return all channels to the ADC to free its resources. - #[inline] - pub fn free(mut self, _channels: Channels) -> I::Instance { - self.software_reset(); - self.adc - } - - /// Reset the peripheral. - /// - /// This also disables the ADC. - #[inline] - fn software_reset(&mut self) { - self.adc.ctrla().modify(|_, w| w.swrst().set_bit()); - self.sync(); - } } impl Adc { @@ -547,19 +568,6 @@ impl Adc { } } - #[inline] - fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { - let tl = calibration::tl(); - let th = calibration::th(); - let vpl = calibration::vpl() as f32; - let vph = calibration::vph() as f32; - let vcl = calibration::vcl() as f32; - let vch = calibration::vch() as f32; - - (tl * vph * tc - vpl * th * tc - tl * vch * tp + th * vcl * tp) - / (vcl * tp - vch * tp - vpl * tc + vph * tc) - } - #[inline] fn sync(&self) { // Slightly more performant than checking the individual bits @@ -669,25 +677,6 @@ where self.disable_freerunning(); Ok(()) } - - #[inline] - pub async fn read_cpu_temperature(&mut self, supc: &pac::Supc) -> Result { - let vref = supc.vref().read(); - if vref.tsen().bit_is_clear() || vref.ondemand().bit_is_clear() { - return Err(Error::TemperatureSensorNotEnabled); - } - let mut tp = self.read(0x1C).await as f32; - let mut tc = self.read(0x1D).await as f32; - - if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { - // to prevent incorrect readings, divide by number of samples if the - // ADC was already configured in summation mode - let div: f32 = (2u16.pow(sum as u32)) as f32; - tp /= div; - tc /= div; - } - Ok(self.tp_tc_to_temp(tp, tc)) - } } // Here I declare the number of channels for the SAME51 ADC From 6fa6e30e6b5d8cd90f91edaa6c66feed94f61a88 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 23 Jan 2025 13:31:54 +0000 Subject: [PATCH 28/65] Remove breaking comments in Cargo.toml --- Cargo.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3db7568625ea..4beae164e53a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,7 @@ [workspace] resolver = "2" default-members = ["hal"] -members = [ - "hal", - #"atsamd-hal-macros", - #"pac/*", - #"boards/*", -] +members = ["hal", "atsamd-hal-macros", "pac/*", "boards/*"] [profile.dev] debug = true From 4cf54ef9db59ec25b00feb47019d1966c9ff051a Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 22 Jan 2025 13:21:39 -0500 Subject: [PATCH 29/65] Allow blocking methods on async-enabled ADCs --- hal/src/peripherals/adc/mod.rs | 62 +++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index e567dc00cfe4..4d823c9f8830 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -242,12 +242,16 @@ impl Channel { // These methods are only implemented for a Channel that holds a configured pin impl> Channel { #[inline] - pub fn read_blocking(&self, adc: &mut Adc) -> u16 { + pub fn read_blocking(&self, adc: &mut Adc) -> u16 { adc.read_blocking(Id::ID) } #[inline] - pub fn read_buffer_blocking(&self, adc: &mut Adc, dst: &mut [u16]) -> Result<(), Error> { + pub fn read_buffer_blocking( + &self, + adc: &mut Adc, + dst: &mut [u16], + ) -> Result<(), Error> { adc.read_buffer_blocking(Id::ID, dst) } @@ -279,7 +283,7 @@ pub struct Adc { pub struct AdcFuture; -impl Adc { +impl Adc { /// Construct a new ADC instance /// /// ## Important @@ -311,6 +315,26 @@ impl Adc { Ok((new_adc, Channels::new())) } + #[cfg(feature = "async")] + #[inline] + pub fn into_future(self, _irqs: F) -> Adc + where + F: crate::async_hal::interrupts::Binding>, + { + use crate::async_hal::interrupts::InterruptSource; + unsafe { + I::Interrupt::unpend(); + I::Interrupt::enable(); + } + Adc { + adc: self.adc, + cfg: self.cfg, + _irqs: PhantomData, + } + } +} + +impl Adc { #[inline] pub fn configure(&mut self, settings: AdcSettingsBuilder) { // Reset ADC here as we cannot guarantee its state @@ -453,7 +477,7 @@ impl Adc { } } -impl Adc { +impl Adc { #[inline] fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { let tl = calibration::tl(); @@ -493,7 +517,7 @@ impl Adc { /// Read one of the CPU internal voltage supply, and return the value in /// millivolts (Volts/1000) - pub fn read_internal_voltage(&mut self, src: CpuVoltageSource) -> u16 { + pub fn read_internal_voltage_blocking(&mut self, src: CpuVoltageSource) -> u16 { let chan = match src { CpuVoltageSource::Core => 0x18, CpuVoltageSource::Vbat => 0x19, @@ -612,6 +636,34 @@ impl Adc { self.sync(); } + #[inline] + fn read_flags(&self) -> Flags { + let bits = self.adc.intflag().read().bits(); + Flags::from_bits_truncate(bits) + } + + #[inline] + fn clear_flags(&mut self, flags: Flags) { + unsafe { + self.adc.intflag().write(|w| w.bits(flags.bits())); + } + } + + /// Check the interrupt flags, clears them and returns `Err` if an overflow + /// occured + #[inline] + fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { + // Keep a copy around so we can check for errors later + let flags_to_clear = flags; + self.clear_flags(flags_to_clear); + + if flags.contains(Flags::OVERRUN) { + Err(Error::BufferOverrun) + } else { + Ok(()) + } + } + /// Enables an interrupt when conversion is ready. #[inline] #[allow(dead_code)] From 8914319d192761fd2c6ed0a878d204a8b91a5012 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 22 Jan 2025 15:38:34 -0500 Subject: [PATCH 30/65] Derive defmt::Format for error type --- hal/src/peripherals/adc/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 4d823c9f8830..811d827ead0f 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -42,6 +42,7 @@ pub use adc0::ctrlb::Resselselect as Resolution; pub use adc0::refctrl::Refselselect as Reference; #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// Clock too fast. /// From a25b15c118e40cc36b7ba32e72977e41a7e2ec5e Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Thu, 23 Jan 2025 10:17:48 -0500 Subject: [PATCH 31/65] Remove duplicate methods --- hal/src/peripherals/adc/mod.rs | 46 ---------------------------------- 1 file changed, 46 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 811d827ead0f..06906d9a8128 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -547,52 +547,6 @@ impl Adc { } impl Adc { - #[cfg(feature = "async")] - #[inline] - pub fn into_future(self, _irqs: F) -> Adc - where - F: crate::async_hal::interrupts::Binding>, - { - use crate::async_hal::interrupts::InterruptSource; - unsafe { - I::Interrupt::unpend(); - I::Interrupt::enable(); - } - Adc { - adc: self.adc, - _irqs: PhantomData, - cfg: self.cfg, - } - } - - #[inline] - fn read_flags(&self) -> Flags { - let bits = self.adc.intflag().read().bits(); - Flags::from_bits_truncate(bits) - } - - #[inline] - fn clear_flags(&mut self, flags: Flags) { - unsafe { - self.adc.intflag().write(|w| w.bits(flags.bits())); - } - } - - /// Check the interrupt flags, clears them and returns `Err` if an overflow - /// occured - #[inline] - fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { - // Keep a copy around so we can check for errors later - let flags_to_clear = flags; - self.clear_flags(flags_to_clear); - - if flags.contains(Flags::OVERRUN) { - Err(Error::BufferOverrun) - } else { - Ok(()) - } - } - #[inline] fn sync(&self) { // Slightly more performant than checking the individual bits From 6cbc27ebfd28fd6df8ad9fe7d1cd0e0f90bbba46 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 23 Jan 2025 17:18:10 +0000 Subject: [PATCH 32/65] ADC: Add support for samd21 line --- hal/src/async_hal/interrupts.rs | 5 +- hal/src/peripherals/adc/adc_settings.rs | 25 +- hal/src/peripherals/adc/async_api.rs | 5 + hal/src/peripherals/adc/d11/mod.rs | 205 ++++++++++++++ hal/src/peripherals/adc/d11/pin.rs | 75 +++++ hal/src/peripherals/adc/d5x/mod.rs | 283 +++++++++++++++++++ hal/src/peripherals/adc/mod.rs | 358 ++++-------------------- 7 files changed, 647 insertions(+), 309 deletions(-) create mode 100644 hal/src/peripherals/adc/d11/pin.rs diff --git a/hal/src/async_hal/interrupts.rs b/hal/src/async_hal/interrupts.rs index 89b15ec5a6da..207e661f2832 100644 --- a/hal/src/async_hal/interrupts.rs +++ b/hal/src/async_hal/interrupts.rs @@ -200,9 +200,12 @@ seq_macro::seq!(N in 0..= 15 { }); // ---------- ADC Interrupt ---------- // +#[hal_cfg("adc-d5x")] declare_multiple_interrupts!(ADC0: [ADC0_RESRDY, ADC0_OTHER]); +#[hal_cfg("adc-d5x")] declare_multiple_interrupts!(ADC1: [ADC1_RESRDY, ADC1_OTHER]); - +#[hal_cfg(any("adc-d11", "adc-d21"))] +declare_interrupts!(ADC); /// An interrupt source that may have one or many interrupt bindings. /// /// This trait may implemented directly when multiple interrupt sources are diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/adc_settings.rs index 7234d5f57a53..41df2423858c 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/adc_settings.rs @@ -1,6 +1,19 @@ +use atsamd_hal_macros::hal_cfg; + +#[hal_cfg(any("adc-d5x"))] use crate::pac::adc0; +#[hal_cfg(any("adc-d21", "adc-d11"))] +use crate::pac::adc as adc0; + +#[hal_cfg(any("adc-d21", "adc-d11"))] +pub use adc0::ctrlb::Prescalerselect as AdcDivider; + +#[hal_cfg(any("adc-d5x"))] +use adc0::ctrla::Prescalerselect as AdcDivider; + pub use adc0::avgctrl::Samplenumselect as AdcSampleCount; + pub use adc0::ctrlb::Resselselect as AdcResolution; pub use adc0::refctrl::Refselselect; @@ -17,18 +30,6 @@ pub enum AdcAccumulation { Summed(AdcSampleCount), } -#[derive(Copy, Clone)] -pub enum AdcDivider { - Div2 = 2, - Div4 = 4, - Div8 = 8, - Div16 = 16, - Div32 = 32, - Div64 = 64, - Div128 = 128, - Div256 = 256, -} - #[derive(Copy, Clone)] pub enum VrefSource {} diff --git a/hal/src/peripherals/adc/async_api.rs b/hal/src/peripherals/adc/async_api.rs index 8c9ab8bd41f7..8557ab10b3f5 100644 --- a/hal/src/peripherals/adc/async_api.rs +++ b/hal/src/peripherals/adc/async_api.rs @@ -5,10 +5,15 @@ use crate::{ async_hal::interrupts::Handler, }; +use atsamd_hal_macros::hal_module; use embassy_sync::waitqueue::AtomicWaker; #[allow(clippy::declare_interior_mutable_const)] const NEW_WAKER: AtomicWaker = AtomicWaker::new(); + +#[hal_module(any("adc-d11", "adc-d21"))] +pub static ADC_WAKERS: [AtomicWaker; 1] = [NEW_WAKER; 1]; +#[hal_module(any("adc-d5x"))] pub static ADC_WAKERS: [AtomicWaker; 2] = [NEW_WAKER; 2]; /// Interrupt handler for the ADC peripheral. diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index e69de29bb2d1..83cff3009fe8 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -0,0 +1,205 @@ +use crate::{calibration, typelevel::NoneT}; + +use super::{ + Adc, AdcAccumulation, AdcDivider, AdcInstance, AdcSettingsBuilder, Error, Flags, PrimaryAdc, +}; + +use crate::pac; +use pac::adc::avgctrl::Samplenumselect; +use pac::Peripherals; +pub mod pin; + +pub struct Adc0 { + _adc: pac::Adc, +} + +impl PrimaryAdc for Adc0 {} + +impl AdcInstance for Adc0 { + type Instance = pac::Adc; + type Clock = crate::clock::AdcClock; + + #[cfg(feature = "async")] + type Interrupt = crate::async_hal::interrupts::ADC; + + #[inline] + fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc::RegisterBlock { + &p.adc + } + + #[inline] + fn enable_pm(pm: &mut pac::Pm) { + pm.apbcmask().modify(|_, w| w.adc_().set_bit()); + } + + #[inline] + fn calibrate(instance: &Self::Instance) {} + + #[cfg(feature = "async")] + #[inline] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + use super::async_api; + &async_api::ADC_WAKERS[0] + } +} + +impl Adc { + #[inline] + pub fn configure(&mut self, settings: AdcSettingsBuilder) { + // Reset ADC here as we cannot guarantee its state + // This also disables the ADC + self.software_reset(); + I::calibrate(&self.adc); + self.sync(); + self.adc + .ctrlb() + .modify(|_, w| w.prescaler().variant(settings.clk_divider)); + self.sync(); + self.adc + .ctrlb() + .modify(|_, w| w.ressel().variant(settings.bit_width)); + self.sync(); + + self.adc + .sampctrl() + .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length + self.sync(); + self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) + self.sync(); + + match settings.accumulation { + AdcAccumulation::Single => { + // 1 sample to be used as is + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(Samplenumselect::_1); + unsafe { w.adjres().bits(0) } + }); + } + AdcAccumulation::Average(adc_sample_count) => { + // A total of `adc_sample_count` elements will be averaged by the ADC + // before it returns the result + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(adc_sample_count); + unsafe { + // Table 45-3 SAME51 datasheet + w.adjres() + .bits(core::cmp::min(adc_sample_count as u8, 0x04)) + } + }); + } + AdcAccumulation::Summed(adc_sample_count) => { + // A total of `adc_sample_count` elements will be summed by the ADC + // before it returns the result + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(adc_sample_count); + unsafe { w.adjres().bits(0) } + }); + } + } + + self.sync(); + self.set_reference(settings.vref); + } +} + +impl Adc {} + +impl Adc { + #[inline] + pub(super) fn sync(&self) { + // Slightly more performant than checking the individual bits + // since we avoid an extra instruction to bit shift + while self.adc.status().read().syncbusy().bit_is_set() { + core::hint::spin_loop(); + } + } + + #[inline] + pub(super) fn power_up(&mut self) { + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); + self.sync(); + } + + #[inline] + pub(super) fn power_down(&mut self) { + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); + self.sync(); + } + + #[inline] + pub(super) fn start_conversion(&mut self) { + // The double trigger here is in case the VREF value changed between + // reads, this discards the conversion made just after the VREF changed, + // which the data sheet tells us to do in order to not get a faulty reading + // right after changing VREF value + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + } + + #[inline] + pub(super) fn enable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + self.sync(); + } + + #[inline] + pub(super) fn disable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + self.sync(); + } + + #[inline] + pub(super) fn read_flags(&self) -> Flags { + let bits = self.adc.intflag().read().bits(); + Flags::from_bits_truncate(bits) + } + + #[inline] + pub(super) fn clear_flags(&mut self, flags: Flags) { + unsafe { + self.adc.intflag().write(|w| w.bits(flags.bits())); + } + } + + /// Check the interrupt flags, clears them and returns `Err` if an overflow + /// occured + #[inline] + pub(super) fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { + // Keep a copy around so we can check for errors later + let flags_to_clear = flags; + self.clear_flags(flags_to_clear); + + if flags.contains(Flags::OVERRUN) { + Err(Error::BufferOverrun) + } else { + Ok(()) + } + } + + /// Enables an interrupt when conversion is ready. + #[inline] + #[allow(dead_code)] + pub(super) fn enable_interrupts(&mut self, flags: Flags) { + unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) }; + } + + /// Disables the interrupt for when conversion is ready. + #[inline] + pub(super) fn disable_interrupts(&mut self, flags: Flags) { + unsafe { self.adc.intenclr().write(|w| w.bits(flags.bits())) }; + } + + #[inline] + pub(super) fn conversion_result(&self) -> u16 { + self.adc.result().read().result().bits() + } + + #[inline] + pub(super) fn mux(&mut self, ch: u8) { + self.adc + .inputctrl() + .modify(|_, w| unsafe { w.muxpos().bits(ch) }); + self.sync() + } +} diff --git a/hal/src/peripherals/adc/d11/pin.rs b/hal/src/peripherals/adc/d11/pin.rs new file mode 100644 index 000000000000..88ebe31b0f47 --- /dev/null +++ b/hal/src/peripherals/adc/d11/pin.rs @@ -0,0 +1,75 @@ +use crate::adc::*; +use crate::gpio::pin::*; +use atsamd_hal_macros::hal_cfg; + +macro_rules! adc_pins { + ( + $( + $( #[$cfg:meta] )? + $PinId:ident: ($Adc:ident, $CHAN:literal) + ),+ + $(,)? + ) => { + crate::paste::item! { + $( + $( #[$cfg] )? + impl AdcPin<$Adc, []> for Pin<$PinId, M> { + type Configured = Pin<$PinId, AlternateB>; + + fn into_function(self) -> Self::Configured { + self.into_alternate() + } + } + )+ + } + }; +} + +adc_pins! { + #[hal_cfg("pa02")] + PA02: (Adc0, 0), + #[hal_cfg("pa03")] + PA03: (Adc0, 1), + + #[hal_cfg("pb08")] + PB08: (Adc0, 2), + #[hal_cfg("pb09")] + PB09: (Adc0, 3), + + #[hal_cfg("pa04")] + PA04: (Adc0, 4), + #[hal_cfg("pa05")] + PA05: (Adc0, 5), + #[hal_cfg("pa06")] + PA06: (Adc0, 6), + #[hal_cfg("pa07")] + PA07: (Adc0, 7), + + #[hal_cfg("pb00")] + PB00: (Adc0, 8), + #[hal_cfg("pb01")] + PB01: (Adc0, 9), + #[hal_cfg("pb02")] + PB02: (Adc0, 10), + #[hal_cfg("pb03")] + PB03: (Adc0, 11), + + #[hal_cfg("pb04")] + PB04: (Adc0, 12), + #[hal_cfg("pb05")] + PB05: (Adc0, 13), + #[hal_cfg("pb06")] + PB06: (Adc0, 14), + #[hal_cfg("pb07")] + PB07: (Adc0, 15), + + #[hal_cfg("pa08")] + PA08: (Adc0, 16), + #[hal_cfg("pa09")] + PA09: (Adc0, 17), + #[hal_cfg("pa10")] + PA10: (Adc0, 18), + #[hal_cfg("pa11")] + PA11: (Adc0, 19), + +} diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index b1959fe756aa..de6df3244474 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -1 +1,284 @@ pub mod pin; + +use atsamd_hal_macros::hal_cfg; +use pac::adc0::avgctrl::Samplenumselect; + +use super::{async_api, Adc, AdcAccumulation, AdcSettingsBuilder, Error, Flags}; +use super::{AdcInstance, PrimaryAdc}; +use crate::typelevel::NoneT; +use crate::{calibration, pac}; + +pub struct Adc0 { + _adc: pac::Adc0, +} + +impl PrimaryAdc for Adc0 {} + +impl AdcInstance for Adc0 { + type Instance = pac::Adc0; + type Clock = crate::clock::Adc0Clock; + + #[cfg(feature = "async")] + type Interrupt = crate::async_hal::interrupts::ADC0; + + #[inline] + fn peripheral_reg_block(p: &mut pac::Peripherals) -> &pac::adc0::RegisterBlock { + &p.adc0 + } + + #[inline] + fn enable_mclk(mclk: &mut pac::Mclk) { + mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); + } + + #[inline] + fn calibrate(instance: &Self::Instance) { + instance.calib().write(|w| unsafe { + w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); + w.biasrefbuf().bits(calibration::adc0_biasref_scale_cal()); + w.biasr2r().bits(calibration::adc0_biasr2r_scale_cal()) + }); + } + + #[cfg(feature = "async")] + #[inline] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + use super::async_api; + &async_api::ADC_WAKERS[0] + } +} + +pub struct Adc1 { + _adc: pac::Adc1, +} + +impl AdcInstance for Adc1 { + type Instance = pac::Adc1; + type Clock = crate::clock::Adc1Clock; + + #[cfg(feature = "async")] + type Interrupt = crate::async_hal::interrupts::ADC1; + + #[inline] + fn peripheral_reg_block(p: &mut pac::Peripherals) -> &pac::adc0::RegisterBlock { + &p.adc1 + } + + #[inline] + fn enable_mclk(mclk: &mut pac::Mclk) { + mclk.apbdmask().modify(|_, w| w.adc1_().set_bit()); + } + + #[inline] + fn calibrate(instance: &Self::Instance) { + instance.calib().write(|w| unsafe { + w.biascomp().bits(calibration::adc1_biascomp_scale_cal()); + w.biasrefbuf().bits(calibration::adc1_biasref_scale_cal()); + w.biasr2r().bits(calibration::adc1_biasr2r_scale_cal()) + }); + } + + #[cfg(feature = "async")] + #[inline] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + &async_api::ADC_WAKERS[1] + } +} + +impl Adc { + #[inline] + pub fn configure(&mut self, settings: AdcSettingsBuilder) { + // Reset ADC here as we cannot guarantee its state + // This also disables the ADC + self.software_reset(); + I::calibrate(&self.adc); + self.sync(); + self.adc + .ctrla() + .modify(|_, w| w.prescaler().variant(settings.clk_divider)); + self.sync(); + self.adc + .ctrlb() + .modify(|_, w| w.ressel().variant(settings.bit_width)); + self.sync(); + + self.adc + .sampctrl() + .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length + self.sync(); + self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) + self.sync(); + + match settings.accumulation { + AdcAccumulation::Single => { + // 1 sample to be used as is + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(Samplenumselect::_1); + unsafe { w.adjres().bits(0) } + }); + } + AdcAccumulation::Average(adc_sample_count) => { + // A total of `adc_sample_count` elements will be averaged by the ADC + // before it returns the result + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(adc_sample_count); + unsafe { + // Table 45-3 SAME51 datasheet + w.adjres() + .bits(core::cmp::min(adc_sample_count as u8, 0x04)) + } + }); + } + AdcAccumulation::Summed(adc_sample_count) => { + // A total of `adc_sample_count` elements will be summed by the ADC + // before it returns the result + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(adc_sample_count); + unsafe { w.adjres().bits(0) } + }); + } + } + + self.sync(); + self.set_reference(settings.vref); + } +} + +impl Adc { + #[inline] + fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { + let tl = calibration::tl(); + let th = calibration::th(); + let vpl = calibration::vpl() as f32; + let vph = calibration::vph() as f32; + let vcl = calibration::vcl() as f32; + let vch = calibration::vch() as f32; + + (tl * vph * tc - vpl * th * tc - tl * vch * tp + th * vcl * tp) + / (vcl * tp - vch * tp - vpl * tc + vph * tc) + } + + #[inline] + /// Returns the CPU temperature in degrees C + /// + /// This requires that the [pac::Supc] peripheral is configured with + /// tsen and ondemand bits enabled, otherwise this function will return + /// [Error::TemperatureSensorNotEnabled] + pub fn read_cpu_temperature_blocking(&mut self, supc: &pac::Supc) -> Result { + let vref = supc.vref().read(); + if vref.tsen().bit_is_clear() || vref.ondemand().bit_is_clear() { + return Err(Error::TemperatureSensorNotEnabled); + } + let mut tp = self.read_blocking(0x1C) as f32; + let mut tc = self.read_blocking(0x1D) as f32; + + if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { + // to prevent incorrect readings, divide by number of samples if the + // ADC was already configured in summation mode + let div: f32 = (2u16.pow(sum as u32)) as f32; + tp /= div; + tc /= div; + } + Ok(self.tp_tc_to_temp(tp, tc)) + } +} + +impl Adc { + #[inline] + pub(super) fn sync(&self) { + // Slightly more performant than checking the individual bits + // since we avoid an extra instruction to bit shift + while self.adc.syncbusy().read().bits() != 0 { + core::hint::spin_loop(); + } + } + + #[inline] + pub(super) fn power_up(&mut self) { + self.adc.ctrla().modify(|_, w| w.enable().set_bit()); + self.sync(); + } + + #[inline] + pub(super) fn power_down(&mut self) { + self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); + self.sync(); + } + + #[inline] + pub(super) fn start_conversion(&mut self) { + // The double trigger here is in case the VREF value changed between + // reads, this discards the conversion made just after the VREF changed, + // which the data sheet tells us to do in order to not get a faulty reading + // right after changing VREF value + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + self.sync(); + self.adc.swtrig().modify(|_, w| w.start().set_bit()); + } + + #[inline] + pub(super) fn enable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + self.sync(); + } + + #[inline] + pub(super) fn disable_freerunning(&mut self) { + self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + self.sync(); + } + + #[inline] + pub(super) fn read_flags(&self) -> Flags { + let bits = self.adc.intflag().read().bits(); + Flags::from_bits_truncate(bits) + } + + #[inline] + pub(super) fn clear_flags(&mut self, flags: Flags) { + unsafe { + self.adc.intflag().write(|w| w.bits(flags.bits())); + } + } + + /// Check the interrupt flags, clears them and returns `Err` if an overflow + /// occured + #[inline] + pub(super) fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { + // Keep a copy around so we can check for errors later + let flags_to_clear = flags; + self.clear_flags(flags_to_clear); + + if flags.contains(Flags::OVERRUN) { + Err(Error::BufferOverrun) + } else { + Ok(()) + } + } + + /// Enables an interrupt when conversion is ready. + #[inline] + #[allow(dead_code)] + pub(super) fn enable_interrupts(&mut self, flags: Flags) { + unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) }; + } + + /// Disables the interrupt for when conversion is ready. + #[inline] + pub(super) fn disable_interrupts(&mut self, flags: Flags) { + unsafe { self.adc.intenclr().write(|w| w.bits(flags.bits())) }; + } + + #[inline] + pub(super) fn conversion_result(&self) -> u16 { + self.adc.result().read().result().bits() + } + + #[inline] + pub(super) fn mux(&mut self, ch: u8) { + self.adc + .inputctrl() + .modify(|_, w| unsafe { w.muxpos().bits(ch) }); + self.sync() + } +} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 06906d9a8128..a75fbf83fb45 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,12 +1,10 @@ use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_module}; -use pac::dmac::channel::chctrla::Trigactselect; -use pac::{Mclk, Peripherals}; +use pac::Peripherals; use seq_macro::seq; use crate::{ - dmac::{self, sram::DmacDescriptor, AnyChannel, Beat, ReadyFuture}, gpio::AnyPin, pac, time::Hertz, @@ -19,6 +17,8 @@ use crate::{ )] mod impls {} +pub use impls::*; + #[cfg(feature = "async")] mod async_api; #[cfg(feature = "async")] @@ -29,13 +29,14 @@ pub use adc_settings::*; use super::{calibration, clock}; +#[hal_module(any("adc-d11", "adc-d21"))] +use crate::pac::adc as adc0; +#[hal_module(any("adc-d5x"))] use crate::pac::adc0; pub use adc0::avgctrl::Samplenumselect; /// Samples per reading pub use adc0::avgctrl::Samplenumselect as SampleRate; -/// Clock frequency relative to the system clock -pub use adc0::ctrla::Prescalerselect as Prescaler; /// Reading resolution in bits pub use adc0::ctrlb::Resselselect as Resolution; /// Reference voltage (or its source) @@ -59,14 +60,26 @@ pub enum Error { TemperatureSensorNotEnabled, } +#[hal_cfg(any("adc-d5x"))] #[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u8)] pub enum CpuVoltageSource { /// Core voltage - Core, + Core = 0x18, /// VBAT supply voltage - Vbat, + Vbat = 0x19, /// IO supply voltage - Io, + Io = 0x1A, +} + +#[hal_cfg(any("adc-d21", "adc-d11"))] +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum CpuVoltageSource { + /// Core voltage + Core = 0x1A, + /// VBAT supply voltage + Vbat = 0x1B, } bitflags::bitflags! { @@ -90,93 +103,20 @@ pub trait AdcInstance { type Interrupt: crate::async_hal::interrupts::InterruptSource; // The Adc0 and Adc1 PAC types implement Deref - type Instance: Deref; + type Instance: Deref; type Clock: Into; - fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock; - fn enable_mclk(mclk: &mut Mclk); - fn calibrate(instance: &Self::Instance); - - #[cfg(feature = "async")] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; -} - -// TODO: The next few lines will need to be adjusted for SAMD11 and SAMD21: they -// only have 1 ADC -pub struct Adc0 { - _adc: pac::Adc0, -} - -impl PrimaryAdc for Adc0 {} - -impl AdcInstance for Adc0 { - type Instance = pac::Adc0; - type Clock = clock::Adc0Clock; - - #[cfg(feature = "async")] - type Interrupt = crate::async_hal::interrupts::ADC0; - - #[inline] - fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { - &p.adc0 - } - - #[inline] - fn enable_mclk(mclk: &mut Mclk) { - mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); - } - - #[inline] - fn calibrate(instance: &Self::Instance) { - instance.calib().write(|w| unsafe { - w.biascomp().bits(calibration::adc0_biascomp_scale_cal()); - w.biasrefbuf().bits(calibration::adc0_biasref_scale_cal()); - w.biasr2r().bits(calibration::adc0_biasr2r_scale_cal()) - }); - } - - #[cfg(feature = "async")] - #[inline] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - &async_api::ADC_WAKERS[0] - } -} - -pub struct Adc1 { - _adc: pac::Adc1, -} - -impl AdcInstance for Adc1 { - type Instance = pac::Adc1; - type Clock = clock::Adc1Clock; + fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock; - #[cfg(feature = "async")] - type Interrupt = crate::async_hal::interrupts::ADC1; + #[hal_module(any("adc-d5x"))] + fn enable_mclk(mclk: &mut pac::Mclk); + #[hal_module(any("adc-d11", "adc-d21"))] + fn enable_pm(pm: &mut pac::Pm); - #[inline] - fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc0::RegisterBlock { - &p.adc1 - } - - #[inline] - fn enable_mclk(mclk: &mut Mclk) { - mclk.apbdmask().modify(|_, w| w.adc1_().set_bit()); - } - - #[inline] - fn calibrate(instance: &Self::Instance) { - instance.calib().write(|w| unsafe { - w.biascomp().bits(calibration::adc1_biascomp_scale_cal()); - w.biasrefbuf().bits(calibration::adc1_biasref_scale_cal()); - w.biasr2r().bits(calibration::adc1_biasr2r_scale_cal()) - }); - } + fn calibrate(instance: &Self::Instance); #[cfg(feature = "async")] - #[inline] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - &async_api::ADC_WAKERS[1] - } + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } /// Trait representing a GPIO pin which can be used as an input for an ADC @@ -294,19 +234,43 @@ impl Adc { /// /// NOTE: If you plan to run the chip above 100°C, then the maximum GCLK /// frequency for the ADC is restricted to 90Mhz for stable performance. + #[hal_cfg(any("adc-d5x"))] #[inline] pub fn new( adc: I::Instance, settings: AdcSettingsBuilder, - mclk: &mut Mclk, + mclk: &mut pac::Mclk, clock: I::Clock, ) -> Result<(Self, Channels), Error> { if (clock.into() as Hertz).to_Hz() > 100_000_000 { // Clock source is too fast return Err(Error::ClockTooFast); } + I::enable_mclk(mclk); + let mut new_adc = Self { + adc, + _irqs: PhantomData, + cfg: settings.clone(), + }; + new_adc.configure(settings); + Ok((new_adc, Channels::new())) + } + #[hal_cfg(any("adc-d11", "adc-d21"))] + #[inline] + pub fn new( + adc: I::Instance, + settings: AdcSettingsBuilder, + pm: &mut pac::Pm, + clock: I::Clock, + ) -> Result<(Self, Channels), Error> { + if (clock.into() as Hertz).to_Hz() > 48_000_000 { + // Clock source is too fast + return Err(Error::ClockTooFast); + } + + I::enable_pm(pm); let mut new_adc = Self { adc, _irqs: PhantomData, @@ -336,70 +300,6 @@ impl Adc { } impl Adc { - #[inline] - pub fn configure(&mut self, settings: AdcSettingsBuilder) { - // Reset ADC here as we cannot guarantee its state - // This also disables the ADC - self.software_reset(); - I::calibrate(&self.adc); - self.sync(); - self.adc.ctrla().modify(|_, w| match settings.clk_divider { - AdcDivider::Div2 => w.prescaler().div2(), - AdcDivider::Div4 => w.prescaler().div4(), - AdcDivider::Div8 => w.prescaler().div8(), - AdcDivider::Div16 => w.prescaler().div16(), - AdcDivider::Div32 => w.prescaler().div32(), - AdcDivider::Div64 => w.prescaler().div64(), - AdcDivider::Div128 => w.prescaler().div128(), - AdcDivider::Div256 => w.prescaler().div256(), - }); - self.sync(); - self.adc - .ctrlb() - .modify(|_, w| w.ressel().variant(settings.bit_width)); - self.sync(); - - self.adc - .sampctrl() - .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length - self.sync(); - self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) - self.sync(); - - match settings.accumulation { - AdcAccumulation::Single => { - // 1 sample to be used as is - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(Samplenumselect::_1); - unsafe { w.adjres().bits(0) } - }); - } - AdcAccumulation::Average(adc_sample_count) => { - // A total of `adc_sample_count` elements will be averaged by the ADC - // before it returns the result - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(adc_sample_count); - unsafe { - // Table 45-3 SAME51 datasheet - w.adjres() - .bits(core::cmp::min(adc_sample_count as u8, 0x04)) - } - }); - } - AdcAccumulation::Summed(adc_sample_count) => { - // A total of `adc_sample_count` elements will be summed by the ADC - // before it returns the result - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(adc_sample_count); - unsafe { w.adjres().bits(0) } - }); - } - } - - self.sync(); - self.set_reference(settings.vref); - } - /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where 1.0 = 2^(reading_bitwidth) fn reading_to_f32(&self, raw: u16) -> f32 { let max = match self.cfg.bit_width { @@ -479,51 +379,10 @@ impl Adc { } impl Adc { - #[inline] - fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { - let tl = calibration::tl(); - let th = calibration::th(); - let vpl = calibration::vpl() as f32; - let vph = calibration::vph() as f32; - let vcl = calibration::vcl() as f32; - let vch = calibration::vch() as f32; - - (tl * vph * tc - vpl * th * tc - tl * vch * tp + th * vcl * tp) - / (vcl * tp - vch * tp - vpl * tc + vph * tc) - } - - #[inline] - /// Returns the CPU temperature in degrees C - /// - /// This requires that the [pac::Supc] peripheral is configured with - /// tsen and ondemand bits enabled, otherwise this function will return - /// [Error::TemperatureSensorNotEnabled] - pub fn read_cpu_temperature_blocking(&mut self, supc: &pac::Supc) -> Result { - let vref = supc.vref().read(); - if vref.tsen().bit_is_clear() || vref.ondemand().bit_is_clear() { - return Err(Error::TemperatureSensorNotEnabled); - } - let mut tp = self.read_blocking(0x1C) as f32; - let mut tc = self.read_blocking(0x1D) as f32; - - if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { - // to prevent incorrect readings, divide by number of samples if the - // ADC was already configured in summation mode - let div: f32 = (2u16.pow(sum as u32)) as f32; - tp /= div; - tc /= div; - } - Ok(self.tp_tc_to_temp(tp, tc)) - } - /// Read one of the CPU internal voltage supply, and return the value in /// millivolts (Volts/1000) pub fn read_internal_voltage_blocking(&mut self, src: CpuVoltageSource) -> u16 { - let chan = match src { - CpuVoltageSource::Core => 0x18, - CpuVoltageSource::Vbat => 0x19, - CpuVoltageSource::Io => 0x1A, - }; + let chan = src as u8; // Before reading, we have to select VDDANA as our reference voltage // so we get the full 3v3 range if self.cfg.vref != Reference::Intvcc1 { @@ -546,106 +405,6 @@ impl Adc { } } -impl Adc { - #[inline] - fn sync(&self) { - // Slightly more performant than checking the individual bits - // since we avoid an extra instruction to bit shift - while self.adc.syncbusy().read().bits() != 0 { - core::hint::spin_loop(); - } - } - - #[inline] - fn power_up(&mut self) { - self.adc.ctrla().modify(|_, w| w.enable().set_bit()); - self.sync(); - } - - #[inline] - fn power_down(&mut self) { - self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); - self.sync(); - } - - #[inline] - fn start_conversion(&mut self) { - // The double trigger here is in case the VREF value changed between - // reads, this discards the conversion made just after the VREF changed, - // which the data sheet tells us to do in order to not get a faulty reading - // right after changing VREF value - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - self.sync(); - self.adc.swtrig().modify(|_, w| w.start().set_bit()); - } - - #[inline] - fn enable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - self.sync(); - } - - #[inline] - fn disable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); - self.sync(); - } - - #[inline] - fn read_flags(&self) -> Flags { - let bits = self.adc.intflag().read().bits(); - Flags::from_bits_truncate(bits) - } - - #[inline] - fn clear_flags(&mut self, flags: Flags) { - unsafe { - self.adc.intflag().write(|w| w.bits(flags.bits())); - } - } - - /// Check the interrupt flags, clears them and returns `Err` if an overflow - /// occured - #[inline] - fn check_and_clear_flags(&mut self, flags: Flags) -> Result<(), Error> { - // Keep a copy around so we can check for errors later - let flags_to_clear = flags; - self.clear_flags(flags_to_clear); - - if flags.contains(Flags::OVERRUN) { - Err(Error::BufferOverrun) - } else { - Ok(()) - } - } - - /// Enables an interrupt when conversion is ready. - #[inline] - #[allow(dead_code)] - fn enable_interrupts(&mut self, flags: Flags) { - unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) }; - } - - /// Disables the interrupt for when conversion is ready. - #[inline] - fn disable_interrupts(&mut self, flags: Flags) { - unsafe { self.adc.intenclr().write(|w| w.bits(flags.bits())) }; - } - - #[inline] - fn conversion_result(&self) -> u16 { - self.adc.result().read().result().bits() - } - - #[inline] - fn mux(&mut self, ch: u8) { - self.adc - .inputctrl() - .modify(|_, w| unsafe { w.muxpos().bits(ch) }); - self.sync() - } -} - #[cfg(feature = "async")] impl Adc where @@ -695,6 +454,13 @@ macro_rules! with_num_channels { }; } +#[hal_cfg(any("adc-d11", "adc-d21"))] +macro_rules! with_num_channels { + ($some_macro:ident) => { + $some_macro! {20} + }; +} + /// Get the number of channels as a literal macro_rules! get { ($literal:literal) => { From 7f7002f37b23a8138dbe5a5ffaacbc25e1b0a67f Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 24 Jan 2025 07:43:21 +0000 Subject: [PATCH 33/65] Remove curiosity nano reference --- boards/same51_curiosity_nano/src/lib.rs | 194 ------------------------ 1 file changed, 194 deletions(-) delete mode 100644 boards/same51_curiosity_nano/src/lib.rs diff --git a/boards/same51_curiosity_nano/src/lib.rs b/boards/same51_curiosity_nano/src/lib.rs deleted file mode 100644 index ccecef928286..000000000000 --- a/boards/same51_curiosity_nano/src/lib.rs +++ /dev/null @@ -1,194 +0,0 @@ -#![no_std] -#![deny(missing_docs)] - -//! Board support crate for Adafruit's Feather M4 Express, -//! an ATSAMD51-based board in Feather form factor. - -#[cfg(feature = "rt")] -pub use cortex_m_rt::entry; - -pub use atsamd_hal as hal; -pub use hal::ehal; -pub use hal::pac; - -use hal::clock::GenericClockController; -use hal::sercom::{ - i2c, spi, - uart::{self, BaudMode, Oversampling}, - IoSet1, UndocIoSet1, -}; -use hal::time::Hertz; - -#[cfg(feature = "usb")] -use hal::usb::usb_device::bus::UsbBusAllocator; -#[cfg(feature = "usb")] -pub use hal::usb::UsbBus; - -hal::bsp_peripherals!( - Sercom1 { SpiSercom } - Sercom2 { I2cSercom } - Sercom5 { UartSercom } -); - -hal::bsp_pins!( - PA02 { - /// Analog pin 0. Can act as a true analog output - /// as it has a DAC (which is not currently supported - /// by this hal) as well as input. - name: a0, - } - PA05 { - /// Analog Pin 1 - name: a1, - } - PB08 { - /// Analog Pin 2 - name: a2, - } - PB09 { - /// Analog Pin 3 - name: a3, - } - PA04 { - /// Analog Pin 4 - name: a4, - } - PA06 { - /// Analog Pin 5 - name: a5, - } - PB01 { - /// Analog Vdiv (1/2 resistor divider for monitoring the battery) - name: battery, - } - - PB17 { - /// Pin 0, UART rx - name: d0, - aliases: { - AlternateC: UartRx - } - } - PB16 { - /// Pin 1, UART tx - name: d1, - aliases: { - AlternateC: UartTx - } - } - PA14 { - /// Pin 4, PWM capable - name: d4, - } - PA16 { - /// Pin 5, PWM capable - name: d5, - } - PA18 { - /// Pin 6, PWM capable - name: d6, - } - PB03 { - /// Neopixel Pin - name: neopixel, - } - PA19 { - /// Pin 9, PWM capable. Also analog input (A7) - name: d9, - } - PA20 { - /// Pin 10, PWM capable - name: d10, - } - PA21 { - /// Pin 11, PWM capable - name: d11, - } - PA22 { - /// Pin 12, PWM capable - name: d12, - } - PA23 { - /// Pin 13, which is also attached to the red LED. PWM capable. - name: d13, - aliases: { - PushPullOutput: RedLed, - AlternateE: RedLedPwm - } - } - PA12 { - /// The I2C data line - name: sda, - aliases: { - AlternateC: Sda - } - } - PA13 { - /// The I2C clock line - name: scl, - aliases: { - AlternateC: Scl - } - } - PA17 { - /// The SPI SCK - name: sck, - aliases: { - AlternateC: Sclk - } - } - PB23 { - /// The SPI MOSI - name: mosi, - aliases: { - AlternateC: Mosi - } - } - PB22 { - /// The SPI MISO - name: miso, - aliases: { - AlternateC: Miso - } - } - PA24 { - /// The USB D- pad - name: usb_dm, - aliases: { - AlternateH: UsbDm - } - } - PA25 { - /// The USB D+ pad - name: usb_dp, - aliases: { - AlternateH: UsbDp - } - } -); - -/// UART pads for the labelled RX & TX pins -pub type UartPads = uart::Pads; - -/// UART device for the labelled RX & TX pins -pub type Uart = uart::Uart, uart::Duplex>; - -/// Convenience for setting up the labelled RX, TX pins to -/// operate as a UART device running at the specified baud. -pub fn uart( - clocks: &mut GenericClockController, - baud: impl Into, - sercom: UartSercom, - mclk: &mut pac::Mclk, - rx: impl Into, - tx: impl Into, -) -> Uart { - let gclk0 = clocks.gclk0(); - - let clock = &clocks.sercom5_core(&gclk0).unwrap(); - let baud = baud.into(); - let pads = uart::Pads::default().rx(rx.into()).tx(tx.into()); - uart::Config::new(mclk, sercom, pads, clock.freq()) - .baud(baud, BaudMode::Fractional(Oversampling::Bits16)) - .enable() -} From b236399a1aab0d29af4d10660ddd816a82b29a31 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 27 Jan 2025 15:41:26 +0000 Subject: [PATCH 34/65] Make names more consistent with the rest of the crate --- .../adc/{adc_settings.rs => config.rs} | 8 ++--- hal/src/peripherals/adc/d11/mod.rs | 16 ++++------ hal/src/peripherals/adc/d5x/mod.rs | 14 ++++---- hal/src/peripherals/adc/mod.rs | 32 +++++++++---------- 4 files changed, 34 insertions(+), 36 deletions(-) rename hal/src/peripherals/adc/{adc_settings.rs => config.rs} (97%) diff --git a/hal/src/peripherals/adc/adc_settings.rs b/hal/src/peripherals/adc/config.rs similarity index 97% rename from hal/src/peripherals/adc/adc_settings.rs rename to hal/src/peripherals/adc/config.rs index 41df2423858c..218015d77b06 100644 --- a/hal/src/peripherals/adc/adc_settings.rs +++ b/hal/src/peripherals/adc/config.rs @@ -33,7 +33,7 @@ pub enum AdcAccumulation { #[derive(Copy, Clone)] pub enum VrefSource {} -/// # ADC sampling settings +/// # ADC configuration builder /// /// Multiple factors can affect the ADCs overall sampling rate, and this /// structure allows for the configuring of the majority of factors that affect @@ -64,7 +64,7 @@ pub enum VrefSource {} /// SPS = (GCLK_ADC / clk_divider) / (n * (sample_clock_cycles + bit_width)) /// ``` #[derive(Copy, Clone)] -pub struct AdcSettingsBuilder { +pub struct Config { pub clk_divider: AdcDivider, pub sample_clock_cycles: u8, pub bit_width: AdcResolution, @@ -72,7 +72,7 @@ pub struct AdcSettingsBuilder { pub vref: Refselselect, } -impl AdcSettingsBuilder { +impl Config { /// /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is /// 48MHz) using the following settings: @@ -166,7 +166,7 @@ impl AdcSettingsBuilder { } } -impl Default for AdcSettingsBuilder { +impl Default for Config { fn default() -> Self { Self::new() } diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 83cff3009fe8..52eb39c7baef 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,8 +1,6 @@ use crate::{calibration, typelevel::NoneT}; -use super::{ - Adc, AdcAccumulation, AdcDivider, AdcInstance, AdcSettingsBuilder, Error, Flags, PrimaryAdc, -}; +use super::{Adc, AdcAccumulation, AdcDivider, AdcInstance, Config, Error, Flags, PrimaryAdc}; use crate::pac; use pac::adc::avgctrl::Samplenumselect; @@ -45,7 +43,7 @@ impl AdcInstance for Adc0 { impl Adc { #[inline] - pub fn configure(&mut self, settings: AdcSettingsBuilder) { + pub fn configure(&mut self, config: Config) { // Reset ADC here as we cannot guarantee its state // This also disables the ADC self.software_reset(); @@ -53,21 +51,21 @@ impl Adc { self.sync(); self.adc .ctrlb() - .modify(|_, w| w.prescaler().variant(settings.clk_divider)); + .modify(|_, w| w.prescaler().variant(config.clk_divider)); self.sync(); self.adc .ctrlb() - .modify(|_, w| w.ressel().variant(settings.bit_width)); + .modify(|_, w| w.ressel().variant(config.bit_width)); self.sync(); self.adc .sampctrl() - .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length + .modify(|_, w| unsafe { w.samplen().bits(config.sample_clock_cycles) }); // sample length self.sync(); self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) self.sync(); - match settings.accumulation { + match config.accumulation { AdcAccumulation::Single => { // 1 sample to be used as is self.adc.avgctrl().modify(|_, w| { @@ -98,7 +96,7 @@ impl Adc { } self.sync(); - self.set_reference(settings.vref); + self.set_reference(config.vref); } } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index de6df3244474..9d4f379cad56 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -3,7 +3,7 @@ pub mod pin; use atsamd_hal_macros::hal_cfg; use pac::adc0::avgctrl::Samplenumselect; -use super::{async_api, Adc, AdcAccumulation, AdcSettingsBuilder, Error, Flags}; +use super::{async_api, Adc, AdcAccumulation, Config, Error, Flags}; use super::{AdcInstance, PrimaryAdc}; use crate::typelevel::NoneT; use crate::{calibration, pac}; @@ -87,7 +87,7 @@ impl AdcInstance for Adc1 { impl Adc { #[inline] - pub fn configure(&mut self, settings: AdcSettingsBuilder) { + pub fn configure(&mut self, config: Config) { // Reset ADC here as we cannot guarantee its state // This also disables the ADC self.software_reset(); @@ -95,21 +95,21 @@ impl Adc { self.sync(); self.adc .ctrla() - .modify(|_, w| w.prescaler().variant(settings.clk_divider)); + .modify(|_, w| w.prescaler().variant(config.clk_divider)); self.sync(); self.adc .ctrlb() - .modify(|_, w| w.ressel().variant(settings.bit_width)); + .modify(|_, w| w.ressel().variant(config.bit_width)); self.sync(); self.adc .sampctrl() - .modify(|_, w| unsafe { w.samplen().bits(settings.sample_clock_cycles) }); // sample length + .modify(|_, w| unsafe { w.samplen().bits(config.sample_clock_cycles) }); // sample length self.sync(); self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) self.sync(); - match settings.accumulation { + match config.accumulation { AdcAccumulation::Single => { // 1 sample to be used as is self.adc.avgctrl().modify(|_, w| { @@ -140,7 +140,7 @@ impl Adc { } self.sync(); - self.set_reference(settings.vref); + self.set_reference(config.vref); } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index a75fbf83fb45..d3ef5a878709 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -24,14 +24,12 @@ mod async_api; #[cfg(feature = "async")] pub use async_api::*; -mod adc_settings; -pub use adc_settings::*; +mod config; +pub use config::*; -use super::{calibration, clock}; - -#[hal_module(any("adc-d11", "adc-d21"))] +#[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; -#[hal_module(any("adc-d5x"))] +#[hal_cfg(any("adc-d5x"))] use crate::pac::adc0; pub use adc0::avgctrl::Samplenumselect; @@ -108,9 +106,10 @@ pub trait AdcInstance { fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock; - #[hal_module(any("adc-d5x"))] + #[hal_cfg(any("adc-d5x"))] fn enable_mclk(mclk: &mut pac::Mclk); - #[hal_module(any("adc-d11", "adc-d21"))] + + #[hal_cfg(any("adc-d11", "adc-d21"))] fn enable_pm(pm: &mut pac::Pm); fn calibrate(instance: &Self::Instance); @@ -219,7 +218,7 @@ impl> Channel { pub struct Adc { adc: I::Instance, _irqs: PhantomData, - cfg: AdcSettingsBuilder, + cfg: Config, } pub struct AdcFuture; @@ -238,7 +237,7 @@ impl Adc { #[inline] pub fn new( adc: I::Instance, - settings: AdcSettingsBuilder, + config: Config, mclk: &mut pac::Mclk, clock: I::Clock, ) -> Result<(Self, Channels), Error> { @@ -251,9 +250,9 @@ impl Adc { let mut new_adc = Self { adc, _irqs: PhantomData, - cfg: settings.clone(), + cfg: config.clone(), }; - new_adc.configure(settings); + new_adc.configure(config); Ok((new_adc, Channels::new())) } @@ -261,7 +260,7 @@ impl Adc { #[inline] pub fn new( adc: I::Instance, - settings: AdcSettingsBuilder, + config: Config, pm: &mut pac::Pm, clock: I::Clock, ) -> Result<(Self, Channels), Error> { @@ -274,9 +273,9 @@ impl Adc { let mut new_adc = Self { adc, _irqs: PhantomData, - cfg: settings.clone(), + cfg: config.clone(), }; - new_adc.configure(settings); + new_adc.configure(config); Ok((new_adc, Channels::new())) } @@ -300,7 +299,8 @@ impl Adc { } impl Adc { - /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where 1.0 = 2^(reading_bitwidth) + /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where 1.0 = + /// 2^(reading_bitwidth) fn reading_to_f32(&self, raw: u16) -> f32 { let max = match self.cfg.bit_width { AdcResolution::_16bit => 65536, From a1e8fb98b9e17c25c998a0892a90e7d090e8a180 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 27 Jan 2025 16:03:19 +0000 Subject: [PATCH 35/65] samd21 - Fix ADC results being multiplied by 2x --- hal/src/peripherals/adc/d11/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 52eb39c7baef..07d587b9c928 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -4,6 +4,8 @@ use super::{Adc, AdcAccumulation, AdcDivider, AdcInstance, Config, Error, Flags, use crate::pac; use pac::adc::avgctrl::Samplenumselect; +use pac::adc::ctrlb::Resselselect; +use pac::adc::inputctrl::Gainselect; use pac::Peripherals; pub mod pin; @@ -62,7 +64,10 @@ impl Adc { .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(config.sample_clock_cycles) }); // sample length self.sync(); - self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) + self.adc.inputctrl().modify(|_, w| { + w.muxneg().gnd(); + w.gain().variant(Gainselect::Div2) + }); // No negative input (internal gnd) self.sync(); match config.accumulation { From 8e0bda249887b07eaf59ae63966f2cfba963cc36 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 27 Jan 2025 16:05:17 +0000 Subject: [PATCH 36/65] Error if ADC is configured for summation/averaging but user has selected an incorrect bit width --- hal/src/peripherals/adc/config.rs | 2 +- hal/src/peripherals/adc/d11/mod.rs | 9 ++++++++- hal/src/peripherals/adc/d5x/mod.rs | 12 +++++++++--- hal/src/peripherals/adc/mod.rs | 15 ++++++++++----- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/hal/src/peripherals/adc/config.rs b/hal/src/peripherals/adc/config.rs index 218015d77b06..e26c377ad45b 100644 --- a/hal/src/peripherals/adc/config.rs +++ b/hal/src/peripherals/adc/config.rs @@ -18,7 +18,7 @@ pub use adc0::ctrlb::Resselselect as AdcResolution; pub use adc0::refctrl::Refselselect; /// Result accumulation strategy for the ADC -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum AdcAccumulation { /// The ADC will read once and then the result is ready Single, diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 07d587b9c928..249bc17e57df 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -45,7 +45,7 @@ impl AdcInstance for Adc0 { impl Adc { #[inline] - pub fn configure(&mut self, config: Config) { + pub fn configure(&mut self, config: Config) -> Result<(), super::Error> { // Reset ADC here as we cannot guarantee its state // This also disables the ADC self.software_reset(); @@ -70,6 +70,12 @@ impl Adc { }); // No negative input (internal gnd) self.sync(); + // Check bit width selected + if config.accumulation != AdcAccumulation::Single + && config.bit_width != Resselselect::_16bit + { + return Err(super::Error::InvalidSampleBitWidth); + } match config.accumulation { AdcAccumulation::Single => { // 1 sample to be used as is @@ -102,6 +108,7 @@ impl Adc { self.sync(); self.set_reference(config.vref); + Ok(()) } } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9d4f379cad56..6a604bccbd5b 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -1,7 +1,7 @@ pub mod pin; -use atsamd_hal_macros::hal_cfg; use pac::adc0::avgctrl::Samplenumselect; +use pac::adc0::ctrlb::Resselselect; use super::{async_api, Adc, AdcAccumulation, Config, Error, Flags}; use super::{AdcInstance, PrimaryAdc}; @@ -87,7 +87,7 @@ impl AdcInstance for Adc1 { impl Adc { #[inline] - pub fn configure(&mut self, config: Config) { + pub fn configure(&mut self, config: Config) -> Result<(), super::Error> { // Reset ADC here as we cannot guarantee its state // This also disables the ADC self.software_reset(); @@ -108,7 +108,12 @@ impl Adc { self.sync(); self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) self.sync(); - + // Check bit width selected + if config.accumulation != AdcAccumulation::Single + && config.bit_width != Resselselect::_16bit + { + return Err(super::Error::InvalidSampleBitWidth); + } match config.accumulation { AdcAccumulation::Single => { // 1 sample to be used as is @@ -141,6 +146,7 @@ impl Adc { self.sync(); self.set_reference(config.vref); + Ok(()) } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index d3ef5a878709..da02d015d6af 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -56,6 +56,11 @@ pub enum Error { /// the SUPC peripheral has not been configured correctly to expose /// the temperature sensors. TemperatureSensorNotEnabled, + /// Invalid ADC Setting sample bit width + /// + /// This can happen if you are trying to average/sum ADC values, + /// and did not select 16bit bitwidth for ADC outputs + InvalidSampleBitWidth, } #[hal_cfg(any("adc-d5x"))] @@ -252,7 +257,7 @@ impl Adc { _irqs: PhantomData, cfg: config.clone(), }; - new_adc.configure(config); + new_adc.configure(config)?; Ok((new_adc, Channels::new())) } @@ -275,7 +280,7 @@ impl Adc { _irqs: PhantomData, cfg: config.clone(), }; - new_adc.configure(config); + new_adc.configure(config)?; Ok((new_adc, Channels::new())) } @@ -299,8 +304,8 @@ impl Adc { } impl Adc { - /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where 1.0 = - /// 2^(reading_bitwidth) + /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where + /// 1.0 = 2^(reading_bitwidth) fn reading_to_f32(&self, raw: u16) -> f32 { let max = match self.cfg.bit_width { AdcResolution::_16bit => 65536, @@ -395,7 +400,7 @@ impl Adc { let div: u16 = 2u16.pow(sum as u32); adc_val /= div; } - let mut res = self.reading_to_f32(adc_val) * 3.3 * 4.0; + let res = self.reading_to_f32(adc_val) * 3.3 * 4.0; // Restore our settings if Reference::Intvcc1 != self.cfg.vref { From 17b2aa621b3dd41888432d0fc4c1fbd5f866d2fe Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 31 Jan 2025 10:50:25 +0000 Subject: [PATCH 37/65] Improve ADC comments --- hal/src/peripherals/adc/d11/mod.rs | 2 -- hal/src/peripherals/adc/mod.rs | 24 +++++++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 249bc17e57df..7cda4f652f49 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -117,8 +117,6 @@ impl Adc {} impl Adc { #[inline] pub(super) fn sync(&self) { - // Slightly more performant than checking the individual bits - // since we avoid an extra instruction to bit shift while self.adc.status().read().syncbusy().bit_is_set() { core::hint::spin_loop(); } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index da02d015d6af..284d18246fbb 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -45,8 +45,15 @@ pub use adc0::refctrl::Refselselect as Reference; pub enum Error { /// Clock too fast. /// - /// The ADC requires that it's fed a GCLK running at MAXIMUM 100 MHz - /// (SAMDx5x). Above 100°C, the GCLK must run at or below 90 MHz. + /// The ADC requires that it's fed a GCLK that does not exceed a certain frequency. + /// These maximums are: + /// + /// * **SAMD/E5x** - 100Mhz + /// * **SAMC/D21** - 48Mhz + /// * **SAMD11** - 48Mhz + /// + /// SAMx51 specific: If you are running the CPU at temperatures past 100C, then + /// the maximum GCLK clock speed should be 90Mhz ClockTooFast, /// Buffer overflowed BufferOverrun, @@ -450,8 +457,8 @@ where } } -// Here I declare the number of channels for the SAME51 ADC -// TODO: declare channels for other chip variants +// Channel implementation + #[hal_cfg(any("adc-d5x"))] macro_rules! with_num_channels { ($some_macro:ident) => { @@ -459,13 +466,20 @@ macro_rules! with_num_channels { }; } -#[hal_cfg(any("adc-d11", "adc-d21"))] +#[hal_cfg(any("adc-d21"))] macro_rules! with_num_channels { ($some_macro:ident) => { $some_macro! {20} }; } +#[hal_cfg(any("adc-d11"))] +macro_rules! with_num_channels { + ($some_macro:ident) => { + $some_macro! {10} + }; +} + /// Get the number of channels as a literal macro_rules! get { ($literal:literal) => { From 92fc81da77ca2212a25764e213d83f93d1606a95 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 31 Jan 2025 10:54:29 +0000 Subject: [PATCH 38/65] Resolve clippy warnings --- hal/src/peripherals/adc/d11/mod.rs | 6 +++--- hal/src/peripherals/adc/mod.rs | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 7cda4f652f49..138bb918edb3 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,6 +1,6 @@ -use crate::{calibration, typelevel::NoneT}; +use crate::typelevel::NoneT; -use super::{Adc, AdcAccumulation, AdcDivider, AdcInstance, Config, Error, Flags, PrimaryAdc}; +use super::{Adc, AdcAccumulation, AdcInstance, Config, Error, Flags, PrimaryAdc}; use crate::pac; use pac::adc::avgctrl::Samplenumselect; @@ -33,7 +33,7 @@ impl AdcInstance for Adc0 { } #[inline] - fn calibrate(instance: &Self::Instance) {} + fn calibrate(_instance: &Self::Instance) {} #[cfg(feature = "async")] #[inline] diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 284d18246fbb..fa5e9afcf9ab 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -233,8 +233,6 @@ pub struct Adc { cfg: Config, } -pub struct AdcFuture; - impl Adc { /// Construct a new ADC instance /// @@ -262,7 +260,7 @@ impl Adc { let mut new_adc = Self { adc, _irqs: PhantomData, - cfg: config.clone(), + cfg: config, }; new_adc.configure(config)?; Ok((new_adc, Channels::new())) @@ -285,7 +283,7 @@ impl Adc { let mut new_adc = Self { adc, _irqs: PhantomData, - cfg: config.clone(), + cfg: config, }; new_adc.configure(config)?; Ok((new_adc, Channels::new())) @@ -345,8 +343,7 @@ impl Adc { core::hint::spin_loop(); } self.power_down(); - let res = self.conversion_result(); - res + self.conversion_result() } #[inline] From 183d873dc75190cd5f5c4b22acb8be2a422b6180 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 31 Jan 2025 13:14:51 +0000 Subject: [PATCH 39/65] Add ADC Pins for D11 --- hal/src/peripherals/adc/d11/pin.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hal/src/peripherals/adc/d11/pin.rs b/hal/src/peripherals/adc/d11/pin.rs index 88ebe31b0f47..b059f1d58ca4 100644 --- a/hal/src/peripherals/adc/d11/pin.rs +++ b/hal/src/peripherals/adc/d11/pin.rs @@ -25,6 +25,7 @@ macro_rules! adc_pins { }; } +#[hal_cfg(any("adc-d21"))] adc_pins! { #[hal_cfg("pa02")] PA02: (Adc0, 0), @@ -71,5 +72,28 @@ adc_pins! { PA10: (Adc0, 18), #[hal_cfg("pa11")] PA11: (Adc0, 19), +} +#[hal_cfg(any("adc-d11"))] +adc_pins! { + #[hal_cfg("pa02")] + PA02: (Adc0, 0), + #[hal_cfg("pa03")] + PA03: (Adc0, 1), + #[hal_cfg("pa04")] + PA04: (Adc0, 2), + #[hal_cfg("pa05")] + PA05: (Adc0, 3), + #[hal_cfg("pa06")] + PA06: (Adc0, 4), + #[hal_cfg("pa07")] + PA07: (Adc0, 5), + #[hal_cfg("pa14")] + PA14: (Adc0, 6), + #[hal_cfg("pa15")] + PA15: (Adc0, 7), + #[hal_cfg("pa10")] + PA10: (Adc0, 8), + #[hal_cfg("pa11")] + PA11: (Adc0, 9), } From 582b5540ed882c92c251d83b74b3195d64d35fae Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 31 Jan 2025 13:17:57 +0000 Subject: [PATCH 40/65] Improve imports and hal_cfg usage for ADC --- hal/src/peripherals/adc/async_api.rs | 2 +- hal/src/peripherals/adc/config.rs | 9 +++------ hal/src/peripherals/adc/d5x/mod.rs | 3 ++- hal/src/peripherals/adc/mod.rs | 10 +++++----- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/hal/src/peripherals/adc/async_api.rs b/hal/src/peripherals/adc/async_api.rs index 8557ab10b3f5..4898164b7392 100644 --- a/hal/src/peripherals/adc/async_api.rs +++ b/hal/src/peripherals/adc/async_api.rs @@ -13,7 +13,7 @@ const NEW_WAKER: AtomicWaker = AtomicWaker::new(); #[hal_module(any("adc-d11", "adc-d21"))] pub static ADC_WAKERS: [AtomicWaker; 1] = [NEW_WAKER; 1]; -#[hal_module(any("adc-d5x"))] +#[hal_module("adc-d5x")] pub static ADC_WAKERS: [AtomicWaker; 2] = [NEW_WAKER; 2]; /// Interrupt handler for the ADC peripheral. diff --git a/hal/src/peripherals/adc/config.rs b/hal/src/peripherals/adc/config.rs index e26c377ad45b..7105c787868a 100644 --- a/hal/src/peripherals/adc/config.rs +++ b/hal/src/peripherals/adc/config.rs @@ -1,6 +1,6 @@ use atsamd_hal_macros::hal_cfg; -#[hal_cfg(any("adc-d5x"))] +#[hal_cfg("adc-d5x")] use crate::pac::adc0; #[hal_cfg(any("adc-d21", "adc-d11"))] @@ -9,8 +9,8 @@ use crate::pac::adc as adc0; #[hal_cfg(any("adc-d21", "adc-d11"))] pub use adc0::ctrlb::Prescalerselect as AdcDivider; -#[hal_cfg(any("adc-d5x"))] -use adc0::ctrla::Prescalerselect as AdcDivider; +#[hal_cfg("adc-d5x")] +pub use adc0::ctrla::Prescalerselect as AdcDivider; pub use adc0::avgctrl::Samplenumselect as AdcSampleCount; @@ -30,9 +30,6 @@ pub enum AdcAccumulation { Summed(AdcSampleCount), } -#[derive(Copy, Clone)] -pub enum VrefSource {} - /// # ADC configuration builder /// /// Multiple factors can affect the ADCs overall sampling rate, and this diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 6a604bccbd5b..7202380f6b95 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -3,7 +3,7 @@ pub mod pin; use pac::adc0::avgctrl::Samplenumselect; use pac::adc0::ctrlb::Resselselect; -use super::{async_api, Adc, AdcAccumulation, Config, Error, Flags}; +use super::{Adc, AdcAccumulation, Config, Error, Flags}; use super::{AdcInstance, PrimaryAdc}; use crate::typelevel::NoneT; use crate::{calibration, pac}; @@ -81,6 +81,7 @@ impl AdcInstance for Adc1 { #[cfg(feature = "async")] #[inline] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + use super::async_api; &async_api::ADC_WAKERS[1] } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index fa5e9afcf9ab..50fe2cd572e0 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -29,7 +29,7 @@ pub use config::*; #[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; -#[hal_cfg(any("adc-d5x"))] +#[hal_cfg("adc-d5x")] use crate::pac::adc0; pub use adc0::avgctrl::Samplenumselect; @@ -70,7 +70,7 @@ pub enum Error { InvalidSampleBitWidth, } -#[hal_cfg(any("adc-d5x"))] +#[hal_cfg("adc-d5x")] #[derive(Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum CpuVoltageSource { @@ -118,7 +118,7 @@ pub trait AdcInstance { fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock; - #[hal_cfg(any("adc-d5x"))] + #[hal_cfg("adc-d5x")] fn enable_mclk(mclk: &mut pac::Mclk); #[hal_cfg(any("adc-d11", "adc-d21"))] @@ -243,7 +243,7 @@ impl Adc { /// /// NOTE: If you plan to run the chip above 100°C, then the maximum GCLK /// frequency for the ADC is restricted to 90Mhz for stable performance. - #[hal_cfg(any("adc-d5x"))] + #[hal_cfg("adc-d5x")] #[inline] pub fn new( adc: I::Instance, @@ -456,7 +456,7 @@ where // Channel implementation -#[hal_cfg(any("adc-d5x"))] +#[hal_cfg("adc-d5x")] macro_rules! with_num_channels { ($some_macro:ident) => { $some_macro! {16} From c01f9ed6fa901a5526f66849c0bc6a9e2e152be0 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 11:04:02 -0500 Subject: [PATCH 41/65] Leave workspace Cargo.toml unchanged --- Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4beae164e53a..6aa87e95e025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,12 @@ [workspace] resolver = "2" default-members = ["hal"] -members = ["hal", "atsamd-hal-macros", "pac/*", "boards/*"] +members = [ + "hal", + "atsamd-hal-macros", + "pac/*", + "boards/*" +] [profile.dev] debug = true From 34db6e00b3d36b7ac06ab83f3c39e675fd7642a1 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 12:21:23 -0500 Subject: [PATCH 42/65] Make good use of the typelevel guarantees provided by clock::v2 --- hal/src/peripherals/adc/d5x/mod.rs | 19 ++------ hal/src/peripherals/adc/mod.rs | 73 +++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 7202380f6b95..db5933b6d629 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -3,8 +3,7 @@ pub mod pin; use pac::adc0::avgctrl::Samplenumselect; use pac::adc0::ctrlb::Resselselect; -use super::{Adc, AdcAccumulation, Config, Error, Flags}; -use super::{AdcInstance, PrimaryAdc}; +use super::{Adc, AdcAccumulation, AdcInstance, Config, Error, Flags, PrimaryAdc}; use crate::typelevel::NoneT; use crate::{calibration, pac}; @@ -16,7 +15,8 @@ impl PrimaryAdc for Adc0 {} impl AdcInstance for Adc0 { type Instance = pac::Adc0; - type Clock = crate::clock::Adc0Clock; + + type ClockId = crate::clock::v2::pclk::ids::Adc0; #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC0; @@ -26,11 +26,6 @@ impl AdcInstance for Adc0 { &p.adc0 } - #[inline] - fn enable_mclk(mclk: &mut pac::Mclk) { - mclk.apbdmask().modify(|_, w| w.adc0_().set_bit()); - } - #[inline] fn calibrate(instance: &Self::Instance) { instance.calib().write(|w| unsafe { @@ -54,7 +49,8 @@ pub struct Adc1 { impl AdcInstance for Adc1 { type Instance = pac::Adc1; - type Clock = crate::clock::Adc1Clock; + + type ClockId = crate::clock::v2::pclk::ids::Adc1; #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC1; @@ -64,11 +60,6 @@ impl AdcInstance for Adc1 { &p.adc1 } - #[inline] - fn enable_mclk(mclk: &mut pac::Mclk) { - mclk.apbdmask().modify(|_, w| w.adc1_().set_bit()); - } - #[inline] fn calibrate(instance: &Self::Instance) { instance.calib().write(|w| unsafe { diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 50fe2cd572e0..3e444413527a 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,13 +1,12 @@ use core::{marker::PhantomData, ops::Deref}; -use atsamd_hal_macros::{hal_cfg, hal_module}; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper, hal_module}; use pac::Peripherals; use seq_macro::seq; use crate::{ gpio::AnyPin, pac, - time::Hertz, typelevel::{NoneT, Sealed}, }; @@ -45,15 +44,15 @@ pub use adc0::refctrl::Refselselect as Reference; pub enum Error { /// Clock too fast. /// - /// The ADC requires that it's fed a GCLK that does not exceed a certain frequency. - /// These maximums are: + /// The ADC requires that it's fed a GCLK that does not exceed a certain + /// frequency. These maximums are: /// /// * **SAMD/E5x** - 100Mhz /// * **SAMC/D21** - 48Mhz /// * **SAMD11** - 48Mhz /// - /// SAMx51 specific: If you are running the CPU at temperatures past 100C, then - /// the maximum GCLK clock speed should be 90Mhz + /// SAMx51 specific: If you are running the CPU at temperatures past 100C, + /// then the maximum GCLK clock speed should be 90Mhz ClockTooFast, /// Buffer overflowed BufferOverrun, @@ -114,12 +113,14 @@ pub trait AdcInstance { // The Adc0 and Adc1 PAC types implement Deref type Instance: Deref; - type Clock: Into; - fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock; + #[hal_cfg(any("adc-d11", "adc-d21"))] + type Clock: Into; #[hal_cfg("adc-d5x")] - fn enable_mclk(mclk: &mut pac::Mclk); + type ClockId: crate::clock::v2::apb::ApbId + crate::clock::v2::pclk::PclkId; + + fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock; #[hal_cfg(any("adc-d11", "adc-d21"))] fn enable_pm(pm: &mut pac::Pm); @@ -227,9 +228,19 @@ impl> Channel { } /// ADC Instance +#[hal_cfg(any("adc-d11", "adc-d21"))] +pub struct Adc { + adc: I::Instance, + _irqs: PhantomData, + cfg: Config, +} + +/// ADC Instance +#[hal_cfg("adc-d5x")] pub struct Adc { adc: I::Instance, _irqs: PhantomData, + _apbclk: crate::clock::v2::apb::ApbClk, cfg: Config, } @@ -241,25 +252,38 @@ impl Adc { /// is faster than 100Mhz, since this is the maximum frequency for GCLK_ADCx /// as per the datasheet. /// + /// The [`new`](Self::new) function currently takes an `&` reference to a + /// [`Pclk`](crate::clock::v2::pclk::Pclk). In the future this will likely + /// change to taking full ownership of it; in the meantime, you must ensure + /// that the PCLK is enabled for the `Adc` struct's lifetime. + /// /// NOTE: If you plan to run the chip above 100°C, then the maximum GCLK /// frequency for the ADC is restricted to 90Mhz for stable performance. #[hal_cfg("adc-d5x")] #[inline] - pub fn new( + pub fn new( adc: I::Instance, config: Config, - mclk: &mut pac::Mclk, - clock: I::Clock, + clk: crate::clock::v2::apb::ApbClk, + pclk: &crate::clock::v2::pclk::Pclk, ) -> Result<(Self, Channels), Error> { - if (clock.into() as Hertz).to_Hz() > 100_000_000 { + // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. However, since + // clock::v2 is not implemented for all chips yet, the generics for the Adc type would be + // different between chip families, leading to massive and unnecessary code duplication. In + // the meantime, we use a "lite" variation of the typelevel guarantees laid out by the + // clock::v2 module, meaning that we can guarantee that the clocks are enabled at the time + // of creation of the Adc struct; however we can't guarantee that the clock will stay + // enabled for the duration of its lifetime. + + if pclk.freq() > fugit::HertzU32::from_raw(100_000_000) { // Clock source is too fast return Err(Error::ClockTooFast); } - I::enable_mclk(mclk); let mut new_adc = Self { adc, _irqs: PhantomData, + _apbclk: clk, cfg: config, }; new_adc.configure(config)?; @@ -274,7 +298,7 @@ impl Adc { pm: &mut pac::Pm, clock: I::Clock, ) -> Result<(Self, Channels), Error> { - if (clock.into() as Hertz).to_Hz() > 48_000_000 { + if (clock.into() as crate::time::Hertz).to_Hz() > 48_000_000 { // Clock source is too fast return Err(Error::ClockTooFast); } @@ -290,6 +314,7 @@ impl Adc { } #[cfg(feature = "async")] + #[hal_macro_helper] #[inline] pub fn into_future(self, _irqs: F) -> Adc where @@ -303,6 +328,8 @@ impl Adc { Adc { adc: self.adc, cfg: self.cfg, + #[hal_cfg("adc-d5x")] + _apbclk: self._apbclk, _irqs: PhantomData, } } @@ -368,15 +395,29 @@ impl Adc { Ok(()) } - /// Return the underlying ADC PAC object + /// Return the underlying ADC PAC object. /// /// You must also return all channels to the ADC to free its resources. + #[hal_cfg(any("adc-d11", "adc-d21"))] #[inline] pub fn free(mut self, _channels: Channels) -> I::Instance { self.software_reset(); self.adc } + /// Return the underlying ADC PAC object and the enabled APB ADC clock. + /// + /// You must also return all channels to the ADC to free its resources. + #[hal_cfg("adc-d5x")] + #[inline] + pub fn free( + mut self, + _channels: Channels, + ) -> (I::Instance, crate::clock::v2::apb::ApbClk) { + self.software_reset(); + (self.adc, self._apbclk) + } + /// Reset the peripheral. /// /// This also disables the ADC. From d7922c1663cdf29f3d63c0daaacac58734fa5253 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 12:43:20 -0500 Subject: [PATCH 43/65] Remove the need for the Channel machinery --- hal/src/peripherals/adc/d11/pin.rs | 8 +- hal/src/peripherals/adc/d5x/mod.rs | 4 +- hal/src/peripherals/adc/d5x/pin.rs | 8 +- hal/src/peripherals/adc/mod.rs | 226 +++++++---------------------- 4 files changed, 55 insertions(+), 191 deletions(-) diff --git a/hal/src/peripherals/adc/d11/pin.rs b/hal/src/peripherals/adc/d11/pin.rs index b059f1d58ca4..c09d9e70a658 100644 --- a/hal/src/peripherals/adc/d11/pin.rs +++ b/hal/src/peripherals/adc/d11/pin.rs @@ -13,12 +13,8 @@ macro_rules! adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl AdcPin<$Adc, []> for Pin<$PinId, M> { - type Configured = Pin<$PinId, AlternateB>; - - fn into_function(self) -> Self::Configured { - self.into_alternate() - } + impl AdcPin<$Adc> for Pin<$PinId, AlternateB> { + const CHANNEL: u8 = $CHAN; } )+ } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index db5933b6d629..d180d959ebc7 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -167,8 +167,8 @@ impl Adc { if vref.tsen().bit_is_clear() || vref.ondemand().bit_is_clear() { return Err(Error::TemperatureSensorNotEnabled); } - let mut tp = self.read_blocking(0x1C) as f32; - let mut tc = self.read_blocking(0x1D) as f32; + let mut tp = self.read_blocking_channel(0x1C) as f32; + let mut tc = self.read_blocking_channel(0x1D) as f32; if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { // to prevent incorrect readings, divide by number of samples if the diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs index 227b9975dbc2..e7664e509cd5 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -13,12 +13,8 @@ macro_rules! adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl AdcPin<$Adc, []> for Pin<$PinId, M> { - type Configured = Pin<$PinId, AlternateB>; - - fn into_function(self) -> Self::Configured { - self.into_alternate() - } + impl AdcPin<$Adc> for Pin<$PinId, AlternateB> { + const CHANNEL: u8 = $CHAN; } )+ } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 3e444413527a..b91d72ef7e8c 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -2,7 +2,6 @@ use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_macro_helper, hal_module}; use pac::Peripherals; -use seq_macro::seq; use crate::{ gpio::AnyPin, @@ -132,99 +131,11 @@ pub trait AdcInstance { } /// Trait representing a GPIO pin which can be used as an input for an ADC -pub trait AdcPin: AnyPin + Sealed +pub trait AdcPin: AnyPin + Sealed where I: AdcInstance, - C: ChId, { - type Configured; - - fn into_function(self) -> Self::Configured; -} - -/// Trait representing an ADC channel ID. -pub trait ChId { - const ID: u8; -} - -/// ADC channel. -/// -/// This struct must hold a concrete [`Pin`](crate::gpio::Pin) which implements -/// [`AdcPin`] in order to perform conversions. By default, channels don't hold -/// any pin when they are created by [`Adc::new`]. Use -/// [`Channel::with_pin`](Self::with_pin) to give a pin to this [`Channel`]. -pub struct Channel { - _pin: P, - _instance: PhantomData, - _id: PhantomData, -} - -// These methods are only implemented for a Channel that doesn't hold a pin yet -impl Channel { - // NOTE: `new`` must be private so a channel isn't accidentally created outside - // this module, breaking the typelevel guarantees laid out by the adc driver - #[inline] - fn new() -> Channel { - Channel { - _pin: NoneT, - _instance: PhantomData, - _id: PhantomData, - } - } - - /// Give a concrete pin to this [`Channel`], which will be used by the ADC - /// to measure voltage. - /// - /// This methods accepts any pin that can potentially be configured as an - /// ADC channel, and automatically puts it in the Alternate B mode. - #[inline] - pub fn with_pin>(self, pin: N) -> Channel { - // NOTE: While AdcPin is implemented for any pin that has the *potential* to be - // turned into an AlternateB pin (which is the ADC function), we know that any - // Channel holding a type implementing AdcPin must have already configured the - // pin to the alternate B function, since the with_pin method is the only way to - // insert a pin into the Channel. - Channel { - _pin: pin.into_function(), - _instance: PhantomData, - _id: PhantomData, - } - } -} - -// These methods are only implemented for a Channel that holds a configured pin -impl> Channel { - #[inline] - pub fn read_blocking(&self, adc: &mut Adc) -> u16 { - adc.read_blocking(Id::ID) - } - - #[inline] - pub fn read_buffer_blocking( - &self, - adc: &mut Adc, - dst: &mut [u16], - ) -> Result<(), Error> { - adc.read_buffer_blocking(Id::ID, dst) - } - - #[cfg(feature = "async")] - #[inline] - pub async fn read(&self, adc: &mut Adc) -> u16 - where - F: crate::async_hal::interrupts::Binding>, - { - adc.read(Id::ID).await - } - - #[cfg(feature = "async")] - #[inline] - pub async fn read_buffer(&self, adc: &mut Adc, dst: &mut [u16]) -> Result<(), Error> - where - F: crate::async_hal::interrupts::Binding>, - { - adc.read_buffer(Id::ID, dst).await - } + const CHANNEL: u8; } /// ADC Instance @@ -266,7 +177,7 @@ impl Adc { config: Config, clk: crate::clock::v2::apb::ApbClk, pclk: &crate::clock::v2::pclk::Pclk, - ) -> Result<(Self, Channels), Error> { + ) -> Result { // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. However, since // clock::v2 is not implemented for all chips yet, the generics for the Adc type would be // different between chip families, leading to massive and unnecessary code duplication. In @@ -287,7 +198,7 @@ impl Adc { cfg: config, }; new_adc.configure(config)?; - Ok((new_adc, Channels::new())) + Ok(new_adc) } #[hal_cfg(any("adc-d11", "adc-d21"))] @@ -297,7 +208,7 @@ impl Adc { config: Config, pm: &mut pac::Pm, clock: I::Clock, - ) -> Result<(Self, Channels), Error> { + ) -> Result { if (clock.into() as crate::time::Hertz).to_Hz() > 48_000_000 { // Clock source is too fast return Err(Error::ClockTooFast); @@ -310,7 +221,7 @@ impl Adc { cfg: config, }; new_adc.configure(config)?; - Ok((new_adc, Channels::new())) + Ok(new_adc) } #[cfg(feature = "async")] @@ -356,8 +267,15 @@ impl Adc { self.sync(); } + /// Read a single value from the provided ADC pin, in a blocking fashion #[inline] - pub fn read_blocking(&mut self, ch: u8) -> u16 { + pub fn read_blocking>(&mut self, _pin: &mut P) -> u16 { + self.read_blocking_channel(P::CHANNEL) + } + + /// Read a single value from the provided channel, in a blocking fashion + #[inline] + fn read_blocking_channel(&mut self, ch: u8) -> u16 { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); self.disable_interrupts(Flags::all()); @@ -373,8 +291,19 @@ impl Adc { self.conversion_result() } + /// Read into a buffer from the provided ADC pin, in a blocking fashion + #[inline] + pub fn read_buffer_blocking>( + &mut self, + _pin: &mut P, + dst: &mut [u16], + ) -> Result<(), Error> { + self.read_buffer_blocking_channel(P::CHANNEL, dst) + } + + /// Read into a buffer from the provided channel, in a blocking fashion #[inline] - pub fn read_buffer_blocking(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + fn read_buffer_blocking_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); self.enable_freerunning(); @@ -396,24 +325,17 @@ impl Adc { } /// Return the underlying ADC PAC object. - /// - /// You must also return all channels to the ADC to free its resources. #[hal_cfg(any("adc-d11", "adc-d21"))] #[inline] - pub fn free(mut self, _channels: Channels) -> I::Instance { + pub fn free(mut self) -> I::Instance { self.software_reset(); self.adc } /// Return the underlying ADC PAC object and the enabled APB ADC clock. - /// - /// You must also return all channels to the ADC to free its resources. #[hal_cfg("adc-d5x")] #[inline] - pub fn free( - mut self, - _channels: Channels, - ) -> (I::Instance, crate::clock::v2::apb::ApbClk) { + pub fn free(mut self) -> (I::Instance, crate::clock::v2::apb::ApbClk) { self.software_reset(); (self.adc, self._apbclk) } @@ -440,7 +362,7 @@ impl Adc { self.set_reference(Reference::Intvcc1); } - let mut adc_val = self.read_blocking(chan); + let mut adc_val = self.read_blocking_channel(chan); if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { let div: u16 = 2u16.pow(sum as u32); adc_val /= div; @@ -460,8 +382,15 @@ impl Adc where F: crate::async_hal::interrupts::Binding>, { + /// Read a single value from the provided ADC pin. #[inline] - pub async fn read(&mut self, ch: u8) -> u16 { + pub async fn read>(&mut self, _pin: &mut P) -> u16 { + self.read_channel(P::CHANNEL).await + } + + /// Read a single value from the provided channel ID + #[inline] + async fn read_channel(&mut self, ch: u8) -> u16 { // Clear overrun errors that might've occured before we try to read anything self.mux(ch); self.power_up(); @@ -475,8 +404,19 @@ where result } + /// Read into a buffer from the provided ADC pin #[inline] - pub async fn read_buffer(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + pub async fn read_buffer>( + &mut self, + _pin: &mut P, + dst: &mut [u16], + ) -> Result<(), Error> { + self.read_buffer_channel(P::CHANNEL, dst).await + } + + /// Read into a buffer from the provided channel ID + #[inline] + async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything self.enable_freerunning(); @@ -494,71 +434,3 @@ where Ok(()) } } - -// Channel implementation - -#[hal_cfg("adc-d5x")] -macro_rules! with_num_channels { - ($some_macro:ident) => { - $some_macro! {16} - }; -} - -#[hal_cfg(any("adc-d21"))] -macro_rules! with_num_channels { - ($some_macro:ident) => { - $some_macro! {20} - }; -} - -#[hal_cfg(any("adc-d11"))] -macro_rules! with_num_channels { - ($some_macro:ident) => { - $some_macro! {10} - }; -} - -/// Get the number of channels as a literal -macro_rules! get { - ($literal:literal) => { - $literal - }; -} - -/// The number of ADC channels per instance on this chip. -pub const NUM_CHANNELS: usize = with_num_channels!(get); - -macro_rules! define_channels_struct { - ($num_channels:literal) => { - seq!(N in 0..$num_channels { - #( - /// Type alias for a channel number - pub enum Ch~N {} - - impl ChId for Ch~N { - const ID: u8 = N; - } - )* - - /// Struct generating individual handles to each ADC channel - pub struct Channels( - #( - pub Channel, - )* - ); - - impl Channels { - #[inline] - fn new() -> Self { - Self ( - #( - Channel::new(), - )* - ) - } - } - }); - }; -} - -with_num_channels!(define_channels_struct); From 932b9f68131f2a106fc75ff1ec7dd1d2759a383a Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 4 Feb 2025 20:52:58 +0000 Subject: [PATCH 44/65] D5x - Document TH and TL parameters better --- hal/src/peripherals/calibration/d5x.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/hal/src/peripherals/calibration/d5x.rs b/hal/src/peripherals/calibration/d5x.rs index 0329e2f58f28..95469a49b56d 100644 --- a/hal/src/peripherals/calibration/d5x.rs +++ b/hal/src/peripherals/calibration/d5x.rs @@ -74,32 +74,34 @@ pub fn adc1_biasr2r_scale_cal() -> u8 { cal(3, 0, 0b111) as u8 } +/// Calibration temperature parameter 'TL', formed by TLI and TLD (TL'Integer', TL'Decimal') pub fn tl() -> f32 { - parts_to_f32(cal(0x80, 7, 0b11111111), cal(0x80 + 1, 3, 0b1111)) + parts_to_f32(tli(), tld()) } +/// Calibration temperature parameter 'TH', formed by THI and THD (TH'Integer', TH'Decimal') pub fn th() -> f32 { - parts_to_f32(cal(0x80 + 2, 3, 0b11111111), cal(0x80 + 2, 7, 0b1111)) + parts_to_f32(thi(), thd()) } /// Temperature calibration - Integer part of calibration temperature TL -pub fn tli() -> u8 { - cal(0x80, 7, 0b11111111) as u8 +pub fn tli() -> u32 { + cal(0x80, 7, 0b11111111) } /// Temperature calibration - Decimal part of calibration temperature TL -pub fn tld() -> u8 { - cal(0x80 + 1, 3, 0b1111) as u8 +pub fn tld() -> u32 { + cal(0x80 + 1, 3, 0b1111) } /// Temperature calibration - Integer part of calibration temperature TH -pub fn thi() -> u8 { - cal(0x80 + 2, 3, 0b11111111) as u8 +pub fn thi() -> u32 { + cal(0x80 + 2, 3, 0b11111111) } /// Temperature calibration - Decimal part of calibration temperature TH -pub fn thd() -> u8 { - cal(0x80 + 2, 7, 0b1111) as u8 +pub fn thd() -> u32 { + cal(0x80 + 2, 7, 0b1111) } /// Temperature calibration - Parameter VPL From f6571bc5fd78a4447ca4b50f6308585e540fc3db Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 21:36:36 -0500 Subject: [PATCH 45/65] Improve D11/D21 ADC clocking --- hal/src/peripherals/adc/d11/mod.rs | 1 - hal/src/peripherals/adc/mod.rs | 11 ++++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 138bb918edb3..2d0ef06da34a 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -17,7 +17,6 @@ impl PrimaryAdc for Adc0 {} impl AdcInstance for Adc0 { type Instance = pac::Adc; - type Clock = crate::clock::AdcClock; #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC; diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index b91d72ef7e8c..bb1f3ed72391 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,6 +1,6 @@ use core::{marker::PhantomData, ops::Deref}; -use atsamd_hal_macros::{hal_cfg, hal_macro_helper, hal_module}; +use atsamd_hal_macros::{hal_cfg, hal_module}; use pac::Peripherals; use crate::{ @@ -113,9 +113,6 @@ pub trait AdcInstance { // The Adc0 and Adc1 PAC types implement Deref type Instance: Deref; - #[hal_cfg(any("adc-d11", "adc-d21"))] - type Clock: Into; - #[hal_cfg("adc-d5x")] type ClockId: crate::clock::v2::apb::ApbId + crate::clock::v2::pclk::PclkId; @@ -207,9 +204,9 @@ impl Adc { adc: I::Instance, config: Config, pm: &mut pac::Pm, - clock: I::Clock, + clock: &crate::clock::AdcClock, ) -> Result { - if (clock.into() as crate::time::Hertz).to_Hz() > 48_000_000 { + if (clock.freq() as crate::time::Hertz).to_Hz() > 48_000_000 { // Clock source is too fast return Err(Error::ClockTooFast); } @@ -225,7 +222,7 @@ impl Adc { } #[cfg(feature = "async")] - #[hal_macro_helper] + #[atsamd_hal_macros::hal_macro_helper] #[inline] pub fn into_future(self, _irqs: F) -> Adc where From a85b6af7bff0c842d3bf5076c30a9dd874a202ef Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 21:49:54 -0500 Subject: [PATCH 46/65] Cleanup config type aliases --- hal/src/peripherals/adc/config.rs | 47 +++++++++++++++--------------- hal/src/peripherals/adc/d11/mod.rs | 18 +++++------- hal/src/peripherals/adc/d5x/mod.rs | 21 ++++++------- hal/src/peripherals/adc/mod.rs | 18 ++++-------- 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/hal/src/peripherals/adc/config.rs b/hal/src/peripherals/adc/config.rs index 7105c787868a..87ccbb5913b7 100644 --- a/hal/src/peripherals/adc/config.rs +++ b/hal/src/peripherals/adc/config.rs @@ -7,27 +7,28 @@ use crate::pac::adc0; use crate::pac::adc as adc0; #[hal_cfg(any("adc-d21", "adc-d11"))] -pub use adc0::ctrlb::Prescalerselect as AdcDivider; +pub use adc0::ctrlb::Prescalerselect as Prescaler; #[hal_cfg("adc-d5x")] -pub use adc0::ctrla::Prescalerselect as AdcDivider; +pub use adc0::ctrla::Prescalerselect as Prescaler; -pub use adc0::avgctrl::Samplenumselect as AdcSampleCount; +pub use adc0::avgctrl::Samplenumselect as SampleCount; -pub use adc0::ctrlb::Resselselect as AdcResolution; -pub use adc0::refctrl::Refselselect; +pub use adc0::ctrlb::Resselselect as Resolution; + +pub use adc0::refctrl::Refselselect as Reference; /// Result accumulation strategy for the ADC #[derive(Copy, Clone, PartialEq, Eq)] -pub enum AdcAccumulation { +pub enum Accumulation { /// The ADC will read once and then the result is ready Single, /// The ADC will read [AdcSampleCount] samples, average them out /// into a 16 bit wide value, and then the result is ready - Average(AdcSampleCount), + Average(SampleCount), /// The ADC will read [AdcSampleCount] samples, sum them /// into a 16 bit wide value, and then the result is ready - Summed(AdcSampleCount), + Summed(SampleCount), } /// # ADC configuration builder @@ -62,11 +63,11 @@ pub enum AdcAccumulation { /// ``` #[derive(Copy, Clone)] pub struct Config { - pub clk_divider: AdcDivider, + pub clk_divider: Prescaler, pub sample_clock_cycles: u8, - pub bit_width: AdcResolution, - pub accumulation: AdcAccumulation, - pub vref: Refselselect, + pub bit_width: Resolution, + pub accumulation: Accumulation, + pub vref: Reference, } impl Config { @@ -82,11 +83,11 @@ impl Config { /// * Use VDDANA as reference voltage for a full 0.0-3.3V reading pub fn new() -> Self { Self { - clk_divider: AdcDivider::Div32, + clk_divider: Prescaler::Div32, sample_clock_cycles: 6, - bit_width: AdcResolution::_12bit, - accumulation: AdcAccumulation::Single, - vref: Refselselect::Intvcc1, + bit_width: Resolution::_12bit, + accumulation: Accumulation::Single, + vref: Reference::Intvcc1, } } @@ -96,18 +97,18 @@ impl Config { /// /// ## Example: /// * Input clock 48MHz, div 32 => ADC Clock is 1.5MHz - pub fn clock_divider(mut self, div: AdcDivider) -> Self { + pub fn clock_divider(mut self, div: Prescaler) -> Self { self.clk_divider = div; self } /// This setting adjusts the bit width of each ADC sample - pub fn sample_resolution(mut self, bit_width: AdcResolution) -> Self { + pub fn sample_resolution(mut self, bit_width: Resolution) -> Self { self.bit_width = bit_width; self } - pub fn with_vref(mut self, reference: Refselselect) -> Self { + pub fn with_vref(mut self, reference: Reference) -> Self { self.vref = reference; self } @@ -128,7 +129,7 @@ impl Config { /// will reduce the overall ADC sample rate by a factor of 1/n, and the /// returned value will be 16bits long no matter what the sample Bit /// width was selected as - pub fn accumulation_method(mut self, method: AdcAccumulation) -> Self { + pub fn accumulation_method(mut self, method: Accumulation) -> Self { self.accumulation = method; self } @@ -154,9 +155,9 @@ impl Config { let mut clocks_per_sample = self.sample_clock_cycles as u32 + (self.bit_width as u32); let multi = match self.accumulation { - AdcAccumulation::Single => 1, - AdcAccumulation::Average(adc_sample_count) => adc_sample_count as u32, - AdcAccumulation::Summed(adc_sample_count) => adc_sample_count as u32, + Accumulation::Single => 1, + Accumulation::Average(adc_sample_count) => adc_sample_count as u32, + Accumulation::Summed(adc_sample_count) => adc_sample_count as u32, }; clocks_per_sample *= multi; adc_clk_freq / clocks_per_sample diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 2d0ef06da34a..2497a96763ae 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,10 +1,10 @@ use crate::typelevel::NoneT; -use super::{Adc, AdcAccumulation, AdcInstance, Config, Error, Flags, PrimaryAdc}; +use super::{ + Accumulation, Adc, AdcInstance, Config, Error, Flags, PrimaryAdc, Resolution, SampleCount, +}; use crate::pac; -use pac::adc::avgctrl::Samplenumselect; -use pac::adc::ctrlb::Resselselect; use pac::adc::inputctrl::Gainselect; use pac::Peripherals; pub mod pin; @@ -70,20 +70,18 @@ impl Adc { self.sync(); // Check bit width selected - if config.accumulation != AdcAccumulation::Single - && config.bit_width != Resselselect::_16bit - { + if config.accumulation != Accumulation::Single && config.bit_width != Resolution::_16bit { return Err(super::Error::InvalidSampleBitWidth); } match config.accumulation { - AdcAccumulation::Single => { + Accumulation::Single => { // 1 sample to be used as is self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(Samplenumselect::_1); + w.samplenum().variant(SampleCount::_1); unsafe { w.adjres().bits(0) } }); } - AdcAccumulation::Average(adc_sample_count) => { + Accumulation::Average(adc_sample_count) => { // A total of `adc_sample_count` elements will be averaged by the ADC // before it returns the result self.adc.avgctrl().modify(|_, w| { @@ -95,7 +93,7 @@ impl Adc { } }); } - AdcAccumulation::Summed(adc_sample_count) => { + Accumulation::Summed(adc_sample_count) => { // A total of `adc_sample_count` elements will be summed by the ADC // before it returns the result self.adc.avgctrl().modify(|_, w| { diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index d180d959ebc7..79db12f873bd 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -1,9 +1,8 @@ pub mod pin; -use pac::adc0::avgctrl::Samplenumselect; -use pac::adc0::ctrlb::Resselselect; - -use super::{Adc, AdcAccumulation, AdcInstance, Config, Error, Flags, PrimaryAdc}; +use super::{ + Accumulation, Adc, AdcInstance, Config, Error, Flags, PrimaryAdc, Resolution, SampleCount, +}; use crate::typelevel::NoneT; use crate::{calibration, pac}; @@ -101,20 +100,18 @@ impl Adc { self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) self.sync(); // Check bit width selected - if config.accumulation != AdcAccumulation::Single - && config.bit_width != Resselselect::_16bit - { + if config.accumulation != Accumulation::Single && config.bit_width != Resolution::_16bit { return Err(super::Error::InvalidSampleBitWidth); } match config.accumulation { - AdcAccumulation::Single => { + Accumulation::Single => { // 1 sample to be used as is self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(Samplenumselect::_1); + w.samplenum().variant(SampleCount::_1); unsafe { w.adjres().bits(0) } }); } - AdcAccumulation::Average(adc_sample_count) => { + Accumulation::Average(adc_sample_count) => { // A total of `adc_sample_count` elements will be averaged by the ADC // before it returns the result self.adc.avgctrl().modify(|_, w| { @@ -126,7 +123,7 @@ impl Adc { } }); } - AdcAccumulation::Summed(adc_sample_count) => { + Accumulation::Summed(adc_sample_count) => { // A total of `adc_sample_count` elements will be summed by the ADC // before it returns the result self.adc.avgctrl().modify(|_, w| { @@ -170,7 +167,7 @@ impl Adc { let mut tp = self.read_blocking_channel(0x1C) as f32; let mut tc = self.read_blocking_channel(0x1D) as f32; - if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { + if let Accumulation::Summed(sum) = self.cfg.accumulation { // to prevent incorrect readings, divide by number of samples if the // ADC was already configured in summation mode let div: f32 = (2u16.pow(sum as u32)) as f32; diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index bb1f3ed72391..9f70a75bf58b 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -30,14 +30,6 @@ use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] use crate::pac::adc0; -pub use adc0::avgctrl::Samplenumselect; -/// Samples per reading -pub use adc0::avgctrl::Samplenumselect as SampleRate; -/// Reading resolution in bits -pub use adc0::ctrlb::Resselselect as Resolution; -/// Reference voltage (or its source) -pub use adc0::refctrl::Refselselect as Reference; - #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -248,10 +240,10 @@ impl Adc { /// 1.0 = 2^(reading_bitwidth) fn reading_to_f32(&self, raw: u16) -> f32 { let max = match self.cfg.bit_width { - AdcResolution::_16bit => 65536, - AdcResolution::_12bit => 4096, - AdcResolution::_10bit => 1024, - AdcResolution::_8bit => 256, + Resolution::_16bit => 65536, + Resolution::_12bit => 4096, + Resolution::_10bit => 1024, + Resolution::_8bit => 256, }; raw as f32 / max as f32 } @@ -360,7 +352,7 @@ impl Adc { } let mut adc_val = self.read_blocking_channel(chan); - if let AdcAccumulation::Summed(sum) = self.cfg.accumulation { + if let Accumulation::Summed(sum) = self.cfg.accumulation { let div: u16 = 2u16.pow(sum as u32); adc_val /= div; } From d0384ab83040e99ce5fc47b651c2f7f7edb81df4 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 21:58:38 -0500 Subject: [PATCH 47/65] Add doc comment for ADC::new (thumbv6) --- boards/feather_m0/examples/adc.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 9e5ed86da6af..99c545362714 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -1,10 +1,13 @@ #![no_std] #![no_main] -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; +// #[cfg(not(feature = "use_semihosting"))] +// use panic_halt as _; +// #[cfg(feature = "use_semihosting")] +// use panic_semihosting as _; + +use defmt_rtt as _; +use panic_probe as _; use cortex_m_semihosting::hprintln; @@ -13,7 +16,7 @@ use bsp::pac; use feather_m0 as bsp; use bsp::entry; -use hal::adc::Adc; +use hal::adc::{Accumulation, Adc, Config, Prescaler, Resolution}; use hal::clock::GenericClockController; use hal::prelude::*; use pac::{CorePeripherals, Peripherals}; @@ -30,12 +33,22 @@ fn main() -> ! { ); let pins = bsp::Pins::new(peripherals.port); let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); - let mut adc = Adc::adc(peripherals.adc, &mut peripherals.pm, &mut clocks); + + let gclk0 = clocks.gclk0(); + let adc_clock = clocks.adc(&gclk0).unwrap(); + + let adc_config = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div4) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc = Adc::new(peripherals.adc, adc_config, &mut peripherals.pm, &adc_clock).unwrap(); let mut a0: bsp::A0 = pins.a0.into(); loop { - let data: u16 = adc.read(&mut a0).unwrap(); - hprintln!("{}", data).ok(); + let data = adc.read_blocking(&mut a0); + defmt::info!("{}", data); delay.delay_ms(1000u16); } } From 2e398ea1c5a3d2ae9211c045435810ac196b7dfa Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 22:58:59 -0500 Subject: [PATCH 48/65] Simplify some snippets, add missing doc comment, and add missing sync calls --- hal/src/peripherals/adc/d11/mod.rs | 65 ++++++++++++------------------ hal/src/peripherals/adc/d5x/mod.rs | 48 +++++++++------------- hal/src/peripherals/adc/mod.rs | 24 ++++++++--- 3 files changed, 64 insertions(+), 73 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 2497a96763ae..0213e8b17de9 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -49,62 +49,49 @@ impl Adc { // This also disables the ADC self.software_reset(); I::calibrate(&self.adc); - self.sync(); - self.adc - .ctrlb() - .modify(|_, w| w.prescaler().variant(config.clk_divider)); - self.sync(); - self.adc - .ctrlb() - .modify(|_, w| w.ressel().variant(config.bit_width)); + + self.adc.ctrlb().modify(|_, w| { + w.prescaler().variant(config.clk_divider); + w.ressel().variant(config.bit_width) + }); self.sync(); self.adc .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(config.sample_clock_cycles) }); // sample length - self.sync(); + self.adc.inputctrl().modify(|_, w| { + // No negative input (internal gnd) w.muxneg().gnd(); w.gain().variant(Gainselect::Div2) - }); // No negative input (internal gnd) + }); self.sync(); // Check bit width selected if config.accumulation != Accumulation::Single && config.bit_width != Resolution::_16bit { return Err(super::Error::InvalidSampleBitWidth); } - match config.accumulation { - Accumulation::Single => { - // 1 sample to be used as is - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(SampleCount::_1); - unsafe { w.adjres().bits(0) } - }); - } - Accumulation::Average(adc_sample_count) => { - // A total of `adc_sample_count` elements will be averaged by the ADC - // before it returns the result - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(adc_sample_count); - unsafe { - // Table 45-3 SAME51 datasheet - w.adjres() - .bits(core::cmp::min(adc_sample_count as u8, 0x04)) - } - }); - } - Accumulation::Summed(adc_sample_count) => { - // A total of `adc_sample_count` elements will be summed by the ADC - // before it returns the result - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(adc_sample_count); - unsafe { w.adjres().bits(0) } - }); - } - } + let (sample_count, adjres) = match config.accumulation { + // 1 sample to be used as is + Accumulation::Single => (SampleCount::_1, 0), + // A total of `adc_sample_count` elements will be averaged by the ADC + // before it returns the result + Accumulation::Average(cnt) => (cnt, core::cmp::min(cnt as u8, 0x04)), + // A total of `adc_sample_count` elements will be summed by the ADC + // before it returns the result + Accumulation::Summed(cnt) => (cnt, 0), + }; + + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(sample_count); + unsafe { w.adjres().bits(adjres) } + }); self.sync(); + self.set_reference(config.vref); + self.sync(); + Ok(()) } } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 79db12f873bd..21705678bbe6 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -103,38 +103,28 @@ impl Adc { if config.accumulation != Accumulation::Single && config.bit_width != Resolution::_16bit { return Err(super::Error::InvalidSampleBitWidth); } - match config.accumulation { - Accumulation::Single => { - // 1 sample to be used as is - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(SampleCount::_1); - unsafe { w.adjres().bits(0) } - }); - } - Accumulation::Average(adc_sample_count) => { - // A total of `adc_sample_count` elements will be averaged by the ADC - // before it returns the result - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(adc_sample_count); - unsafe { - // Table 45-3 SAME51 datasheet - w.adjres() - .bits(core::cmp::min(adc_sample_count as u8, 0x04)) - } - }); - } - Accumulation::Summed(adc_sample_count) => { - // A total of `adc_sample_count` elements will be summed by the ADC - // before it returns the result - self.adc.avgctrl().modify(|_, w| { - w.samplenum().variant(adc_sample_count); - unsafe { w.adjres().bits(0) } - }); - } - } + + let (sample_cnt, adjres) = match config.accumulation { + // 1 sample to be used as is + Accumulation::Single => (SampleCount::_1, 0), + // A total of `adc_sample_count` elements will be averaged by the ADC + // before it returns the result + // Table 45-3 SAME51 datasheet + Accumulation::Average(cnt) => (cnt, core::cmp::min(cnt as u8, 0x04)), + // A total of `adc_sample_count` elements will be summed by the ADC + // before it returns the result + Accumulation::Summed(cnt) => (cnt, 0), + }; + self.adc.avgctrl().modify(|_, w| { + w.samplenum().variant(sample_cnt); + unsafe { w.adjres().bits(adjres) } + }); self.sync(); + self.set_reference(config.vref); + self.sync(); + Ok(()) } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 9f70a75bf58b..a6c8b01b58e3 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -148,8 +148,9 @@ impl Adc { /// Construct a new ADC instance /// /// ## Important + /// /// This function will return `Err` if the clock source provided - /// is faster than 100Mhz, since this is the maximum frequency for GCLK_ADCx + /// is faster than 100 MHz, since this is the maximum frequency for GCLK_ADCx /// as per the datasheet. /// /// The [`new`](Self::new) function currently takes an `&` reference to a @@ -190,6 +191,13 @@ impl Adc { Ok(new_adc) } + /// Construct a new ADC instance + /// + /// ## Important + /// + /// This function will return `Err` if the clock source provided + /// is faster than 48 MHz, since this is the maximum frequency for the + /// ADC as per the datasheet. #[hal_cfg(any("adc-d11", "adc-d21"))] #[inline] pub fn new( @@ -267,9 +275,11 @@ impl Adc { fn read_blocking_channel(&mut self, ch: u8) -> u16 { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); + self.disable_interrupts(Flags::all()); self.mux(ch); self.power_up(); + self.start_conversion(); self.clear_flags(Flags::RESRDY); let _discard = self.conversion_result(); @@ -295,11 +305,12 @@ impl Adc { fn read_buffer_blocking_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); - self.enable_freerunning(); + self.enable_freerunning(); self.disable_interrupts(Flags::all()); self.mux(ch); self.power_up(); + self.start_conversion(); for result in dst.iter_mut() { while !self.read_flags().contains(Flags::RESRDY) { @@ -381,9 +392,11 @@ where #[inline] async fn read_channel(&mut self, ch: u8) -> u16 { // Clear overrun errors that might've occured before we try to read anything + let _ = self.check_and_clear_flags(self.read_flags()); + self.mux(ch); self.power_up(); - let _ = self.check_and_clear_flags(self.read_flags()); + self.start_conversion(); // Here we explicitly ignore the result, because we know that // overrun errors are impossible since the ADC is configured in one-shot mode. @@ -407,11 +420,12 @@ where #[inline] async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything - self.enable_freerunning(); + let _ = self.check_and_clear_flags(self.read_flags()); + self.enable_freerunning(); self.mux(ch); self.power_up(); - let _ = self.check_and_clear_flags(self.read_flags()); + self.start_conversion(); for result in dst.iter_mut() { self.wait_flags(Flags::RESRDY).await?; From ddccfe0f4af0665cdbd655ce75ca21e4683e5328 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Feb 2025 23:02:40 -0500 Subject: [PATCH 49/65] Minimal working ADC example for feather_m0 --- boards/feather_m0/examples/adc.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 99c545362714..29a1af96e3b2 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -1,13 +1,10 @@ #![no_std] #![no_main] -// #[cfg(not(feature = "use_semihosting"))] -// use panic_halt as _; -// #[cfg(feature = "use_semihosting")] -// use panic_semihosting as _; - -use defmt_rtt as _; -use panic_probe as _; +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; use cortex_m_semihosting::hprintln; @@ -16,11 +13,15 @@ use bsp::pac; use feather_m0 as bsp; use bsp::entry; -use hal::adc::{Accumulation, Adc, Config, Prescaler, Resolution}; +use hal::adc::{Accumulation, Adc, Adc0, Config, Prescaler, Resolution}; use hal::clock::GenericClockController; use hal::prelude::*; use pac::{CorePeripherals, Peripherals}; +atsamd_hal::bind_interrupts!(struct Irqs { + ADC => atsamd_hal::adc::InterruptHandler; +}); + #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); @@ -39,16 +40,19 @@ fn main() -> ! { let adc_config = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div4) + .clock_divider(Prescaler::Div8) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); - let mut adc = Adc::new(peripherals.adc, adc_config, &mut peripherals.pm, &adc_clock).unwrap(); - let mut a0: bsp::A0 = pins.a0.into(); + let mut adc = Adc::new(peripherals.adc, adc_config, &mut peripherals.pm, &adc_clock) + .unwrap() + .into_future(Irqs); + let mut a0 = pins.a0.into_alternate(); loop { - let data = adc.read_blocking(&mut a0); - defmt::info!("{}", data); + let mut buf = [0; 16]; + let data = adc.read_buffer_blocking(&mut a0, &mut buf); + hprintln!("buf: {}", buf); delay.delay_ms(1000u16); } } From e45049f5c95e3cac4a6efd3a5d96f3eec42ea39e Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 5 Feb 2025 12:19:20 -0500 Subject: [PATCH 50/65] Fix some bugs in conversion methods --- hal/src/peripherals/adc/d11/mod.rs | 7 ++++- hal/src/peripherals/adc/d5x/mod.rs | 7 +++-- hal/src/peripherals/adc/mod.rs | 43 +++++++++++++++++++----------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 0213e8b17de9..cc4216ed9226 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -77,6 +77,7 @@ impl Adc { Accumulation::Single => (SampleCount::_1, 0), // A total of `adc_sample_count` elements will be averaged by the ADC // before it returns the result + // SAMD21 datasheet table 32-3 / SAMD11 datasheet table 31-3 Accumulation::Average(cnt) => (cnt, core::cmp::min(cnt as u8, 0x04)), // A total of `adc_sample_count` elements will be summed by the ADC // before it returns the result @@ -92,6 +93,10 @@ impl Adc { self.set_reference(config.vref); self.sync(); + self.disable_freerunning(); + + self.power_up(); + Ok(()) } } @@ -137,7 +142,7 @@ impl Adc { #[inline] pub(super) fn disable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + self.adc.ctrlb().modify(|_, w| w.freerun().clear_bit()); self.sync(); } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 21705678bbe6..f4fb66587c44 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -109,7 +109,7 @@ impl Adc { Accumulation::Single => (SampleCount::_1, 0), // A total of `adc_sample_count` elements will be averaged by the ADC // before it returns the result - // Table 45-3 SAME51 datasheet + // Table 45-3 SAMx5x datasheet Accumulation::Average(cnt) => (cnt, core::cmp::min(cnt as u8, 0x04)), // A total of `adc_sample_count` elements will be summed by the ADC // before it returns the result @@ -119,12 +119,15 @@ impl Adc { w.samplenum().variant(sample_cnt); unsafe { w.adjres().bits(adjres) } }); - self.sync(); self.set_reference(config.vref); self.sync(); + self.disable_freerunning(); + + self.power_up(); + Ok(()) } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index a6c8b01b58e3..9fc96fe1d02d 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -278,15 +278,14 @@ impl Adc { self.disable_interrupts(Flags::all()); self.mux(ch); - self.power_up(); - self.start_conversion(); - self.clear_flags(Flags::RESRDY); + // Discard any potentially old measurements still lingering in the buffer let _discard = self.conversion_result(); + + self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - self.power_down(); self.conversion_result() } @@ -306,20 +305,27 @@ impl Adc { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); - self.enable_freerunning(); self.disable_interrupts(Flags::all()); self.mux(ch); - self.power_up(); + // Discard any potentially old measurements still lingering in the buffer + let _discard = self.conversion_result(); + + self.enable_freerunning(); self.start_conversion(); + for result in dst.iter_mut() { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } *result = self.conversion_result(); - self.check_and_clear_flags(self.read_flags())?; + + if let Err(e) = self.check_and_clear_flags(self.read_flags()) { + self.disable_freerunning(); + return Err(e); + } } - self.power_down(); + self.disable_freerunning(); Ok(()) } @@ -395,15 +401,15 @@ where let _ = self.check_and_clear_flags(self.read_flags()); self.mux(ch); - self.power_up(); + + // Discard any potentially old measurements still lingering in the buffer + let _discard = self.conversion_result(); self.start_conversion(); // Here we explicitly ignore the result, because we know that // overrun errors are impossible since the ADC is configured in one-shot mode. let _ = self.wait_flags(Flags::RESRDY).await; - let result = self.conversion_result(); - self.power_down(); - result + self.conversion_result() } /// Read into a buffer from the provided ADC pin @@ -422,17 +428,22 @@ where // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); - self.enable_freerunning(); self.mux(ch); - self.power_up(); + // Discard any potentially old measurements still lingering in the buffer + let _discard = self.conversion_result(); + + self.enable_freerunning(); self.start_conversion(); + for result in dst.iter_mut() { - self.wait_flags(Flags::RESRDY).await?; + if let Err(e) = self.wait_flags(Flags::RESRDY).await { + self.disable_freerunning(); + return Err(e); + } *result = self.conversion_result(); } - self.power_down(); self.disable_freerunning(); Ok(()) } From d28c3f0610a92ab966989cdc0c9c93609004f599 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 5 Feb 2025 13:12:45 -0500 Subject: [PATCH 51/65] Slightly improve docs --- hal/src/peripherals/adc/d11/mod.rs | 2 ++ hal/src/peripherals/adc/d5x/mod.rs | 3 +++ hal/src/peripherals/adc/mod.rs | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index cc4216ed9226..bf3c672b983e 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -9,6 +9,7 @@ use pac::adc::inputctrl::Gainselect; use pac::Peripherals; pub mod pin; +/// Wrapper around the ADC instance pub struct Adc0 { _adc: pac::Adc, } @@ -118,6 +119,7 @@ impl Adc { } #[inline] + #[allow(dead_code)] pub(super) fn power_down(&mut self) { self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); self.sync(); diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index f4fb66587c44..4123170848a9 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -6,6 +6,7 @@ use super::{ use crate::typelevel::NoneT; use crate::{calibration, pac}; +/// ADC instance 0 pub struct Adc0 { _adc: pac::Adc0, } @@ -42,6 +43,7 @@ impl AdcInstance for Adc0 { } } +/// ADC instance 0 pub struct Adc1 { _adc: pac::Adc1, } @@ -188,6 +190,7 @@ impl Adc { } #[inline] + #[allow(dead_code)] pub(super) fn power_down(&mut self) { self.adc.ctrla().modify(|_, w| w.enable().clear_bit()); self.sync(); diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 9fc96fe1d02d..3e02291305ee 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -1,3 +1,5 @@ +//! Analog-to-Digital Converter + use core::{marker::PhantomData, ops::Deref}; use atsamd_hal_macros::{hal_cfg, hal_module}; @@ -30,6 +32,7 @@ use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] use crate::pac::adc0; +/// Errors that may occur when operating the ADC #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -60,6 +63,7 @@ pub enum Error { InvalidSampleBitWidth, } +/// Voltage source to use when using the ADC to measure the CPU voltage #[hal_cfg("adc-d5x")] #[derive(Copy, Clone, PartialEq, Eq)] #[repr(u8)] @@ -72,6 +76,7 @@ pub enum CpuVoltageSource { Io = 0x1A, } +/// Voltage source to use when using the ADC to measure the CPU voltage #[hal_cfg(any("adc-d21", "adc-d11"))] #[derive(Copy, Clone, PartialEq, Eq)] #[repr(u8)] @@ -83,6 +88,7 @@ pub enum CpuVoltageSource { } bitflags::bitflags! { + /// ADC interrupt flags #[derive(Clone, Copy)] pub struct Flags: u8 { /// Window monitor interrupt From 1667fbf4476b4dcd32640b65c5feddd6506a42e5 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 10 Feb 2025 19:36:21 +0000 Subject: [PATCH 52/65] Add D11 ADC calibration --- hal/src/peripherals/adc/d11/mod.rs | 11 +++++++++-- hal/src/peripherals/calibration/d11.rs | 12 +++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 138bb918edb3..91157aaa80d6 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -2,7 +2,7 @@ use crate::typelevel::NoneT; use super::{Adc, AdcAccumulation, AdcInstance, Config, Error, Flags, PrimaryAdc}; -use crate::pac; +use crate::{calibration, pac}; use pac::adc::avgctrl::Samplenumselect; use pac::adc::ctrlb::Resselselect; use pac::adc::inputctrl::Gainselect; @@ -33,7 +33,14 @@ impl AdcInstance for Adc0 { } #[inline] - fn calibrate(_instance: &Self::Instance) {} + fn calibrate(instance: &Self::Instance) { + instance.calib().write(|w| { + unsafe { + w.bias_cal().bits(calibration::adc_bias_cal()); + w.linearity_cal().bits(calibration::adc_linearity_cal()) + } + }); + } #[cfg(feature = "async")] #[inline] diff --git a/hal/src/peripherals/calibration/d11.rs b/hal/src/peripherals/calibration/d11.rs index ee855d7ef2b9..77f248ec82be 100644 --- a/hal/src/peripherals/calibration/d11.rs +++ b/hal/src/peripherals/calibration/d11.rs @@ -33,9 +33,19 @@ fn cal_with_errata( } } +/// ADC Linearity Calibration. Should be written to ADC CALIB register. +pub fn adc_linearity_cal() -> u8 { + cal(3, 3, 0b1111_111) as u8 +} + +/// ADC Bias Calibration. Should be written to ADC CALIB register. +pub fn adc_bias_cal() -> u8 { + cal(4, 5, 0b11) as u8 +} + /// Returns the osc32k calibration value from the NVM calibration area pub fn osc32k_cal() -> u8 { - cal(4, 6, 0x7f) as u8 + cal(4, 6, 0x7f) as u8 // 44:38 } /// Returns the dfll48m coarse calibration value From 4303f46c1d556e358cdbc2686e6a4bcf215bd739 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Mon, 10 Feb 2025 15:35:20 -0500 Subject: [PATCH 53/65] Revert workspace Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6aa87e95e025..6613515bbc01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "hal", "atsamd-hal-macros", "pac/*", - "boards/*" + "boards/*", ] [profile.dev] From 9492091df130c4461db010254147b8cc90283e16 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 11 Feb 2025 12:21:18 -0500 Subject: [PATCH 54/65] Update T1 BSPs --- boards/feather_m0/Cargo.toml | 4 ++ boards/feather_m0/examples/adc.rs | 55 ++++++++++--------- boards/feather_m0/examples/async_adc.rs | 65 ++++++++++++++++++++++ boards/feather_m4/Cargo.toml | 4 ++ boards/feather_m4/examples/adc.rs | 65 ++++++++++++++++++++++ boards/metro_m4/Cargo.toml | 4 ++ boards/metro_m4/examples/adc.rs | 58 +++++++++++++------- boards/metro_m4/examples/async_adc.rs | 70 ++++++++++++++++++++++++ boards/pygamer/src/pins.rs | 11 ++-- boards/samd11_bare/Cargo.toml | 1 + boards/samd11_bare/examples/adc.rs | 73 ++++++++++++++----------- 11 files changed, 326 insertions(+), 84 deletions(-) create mode 100644 boards/feather_m0/examples/async_adc.rs create mode 100644 boards/feather_m4/examples/adc.rs create mode 100644 boards/metro_m4/examples/async_adc.rs diff --git a/boards/feather_m0/Cargo.toml b/boards/feather_m0/Cargo.toml index ba0a8baee637..575d0a60a94a 100644 --- a/boards/feather_m0/Cargo.toml +++ b/boards/feather_m0/Cargo.toml @@ -87,6 +87,10 @@ required-features = ["adalogger", "usb", "sdmmc"] [[example]] name = "adc" +[[example]] +name = "async_adc" +required-features = ["async"] + [[example]] name = "async_dmac" required-features = ["dma", "async"] diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 29a1af96e3b2..e3b6116eb265 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -1,58 +1,61 @@ #![no_std] #![no_main] +use feather_m0 as bsp; + +use bsp::hal; +use bsp::pac; + #[cfg(not(feature = "use_semihosting"))] use panic_halt as _; #[cfg(feature = "use_semihosting")] use panic_semihosting as _; -use cortex_m_semihosting::hprintln; - -use bsp::hal; -use bsp::pac; -use feather_m0 as bsp; - use bsp::entry; -use hal::adc::{Accumulation, Adc, Adc0, Config, Prescaler, Resolution}; -use hal::clock::GenericClockController; -use hal::prelude::*; +use bsp::Pins; use pac::{CorePeripherals, Peripherals}; -atsamd_hal::bind_interrupts!(struct Irqs { - ADC => atsamd_hal::adc::InterruptHandler; -}); +use hal::{ + adc::{Accumulation, Adc, Config, Prescaler, Resolution}, + clock::GenericClockController, +}; #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + // SAMD21 targets currently don't support clock::v2 let mut clocks = GenericClockController::with_external_32kosc( peripherals.gclk, &mut peripherals.pm, &mut peripherals.sysctrl, &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.port); - let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); - let gclk0 = clocks.gclk0(); let adc_clock = clocks.adc(&gclk0).unwrap(); - let adc_config = Config::new() + let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div8) + .clock_divider(Prescaler::Div32) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); - let mut adc = Adc::new(peripherals.adc, adc_config, &mut peripherals.pm, &adc_clock) - .unwrap() - .into_future(Irqs); - let mut a0 = pins.a0.into_alternate(); + let mut adc = Adc::new( + peripherals.adc, + adc_settings, + &mut peripherals.pm, + &adc_clock, + ) + .unwrap(); + let mut adc_pin = pins.a0.into_alternate(); loop { - let mut buf = [0; 16]; - let data = adc.read_buffer_blocking(&mut a0, &mut buf); - hprintln!("buf: {}", buf); - delay.delay_ms(1000u16); + let mut buffer = [0; 16]; + let res = adc.read_buffer_blocking(&mut adc_pin, &mut buffer).unwrap(); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("Result: {:?}", res).unwrap(); } } diff --git a/boards/feather_m0/examples/async_adc.rs b/boards/feather_m0/examples/async_adc.rs new file mode 100644 index 000000000000..b355daa440f8 --- /dev/null +++ b/boards/feather_m0/examples/async_adc.rs @@ -0,0 +1,65 @@ +#![no_std] +#![no_main] + +use feather_m0 as bsp; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Adc, Adc0, Config, Prescaler, Resolution}, + clock::GenericClockController, +}; + +atsamd_hal::bind_interrupts!(struct Irqs { + ADC => atsamd_hal::adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + // SAMD21 targets currently don't support clock::v2 + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + let gclk0 = clocks.gclk0(); + let adc_clock = clocks.adc(&gclk0).unwrap(); + + let adc_settings = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div32) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc = Adc::new( + peripherals.adc, + adc_settings, + &mut peripherals.pm, + &adc_clock, + ) + .unwrap() + .into_future(Irqs); + let mut adc_pin = pins.a0.into_alternate(); + + loop { + let mut buffer = [0; 16]; + let res = adc.read_buffer(&mut adc_pin, &mut buffer).await.unwrap(); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("Result: {:?}", res).unwrap(); + } +} diff --git a/boards/feather_m4/Cargo.toml b/boards/feather_m4/Cargo.toml index cba1729982d1..7d8a05182cdd 100644 --- a/boards/feather_m4/Cargo.toml +++ b/boards/feather_m4/Cargo.toml @@ -35,6 +35,7 @@ version = "0.3.1" [dev-dependencies] heapless = "0.7" panic-halt = "0.2" +cortex-m-semihosting = "0.5.0" panic-semihosting = "0.5" rtic = {version = "2.1.1", features = ["thumbv7-backend"]} smart-leds = "0.3" @@ -53,6 +54,9 @@ usb = ["atsamd-hal/usb", "usb-device"] async = ["atsamd-hal/async"] use_semihosting = [] +[[example]] +name = "adc" + [[example]] name = "blinky_basic" diff --git a/boards/feather_m4/examples/adc.rs b/boards/feather_m4/examples/adc.rs new file mode 100644 index 000000000000..e4dc27733004 --- /dev/null +++ b/boards/feather_m4/examples/adc.rs @@ -0,0 +1,65 @@ +#![no_std] +#![no_main] + +use feather_m4 as bsp; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Adc, Config, Prescaler, Resolution}, + clock::v2::{clock_system_at_reset, pclk::Pclk}, +}; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + // TODO: currently this example only runs at the chip's + // 48 MHz CPU frequency at reset. There is currently a + // bug with the clock::v2 module that affects speeding + // up the CPU clock to a nominal 100 or 120 MHz. The bug + // is currently under investigation, and this example should + // be updated accordingly when it's fixed. + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, + peripherals.gclk, + peripherals.mclk, + &mut peripherals.nvmctrl, + ); + + // Enable the ADC0 ABP clock... + let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); + // ...and enable the ADC0 PCLK. Both of these are required for the + // ADC to run. + let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + + let adc0_settings = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div32) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc = Adc::new(peripherals.adc0, adc0_settings, apb_adc0, &pclk_adc0).unwrap(); + let mut adc_pin = pins.a0.into_alternate(); + + loop { + let mut buffer = [0; 16]; + let res = adc.read_buffer_blocking(&mut adc_pin, &mut buffer).unwrap(); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("Result: {:?}", res); + } +} diff --git a/boards/metro_m4/Cargo.toml b/boards/metro_m4/Cargo.toml index 2343cdcc2c9e..4ab2e081459e 100644 --- a/boards/metro_m4/Cargo.toml +++ b/boards/metro_m4/Cargo.toml @@ -67,6 +67,10 @@ use_semihosting = [] [[example]] name = "adc" +[[example]] +name = "async_adc" +required-features = ["async"] + [[example]] name = "async_dmac" required-features = ["dma", "async"] diff --git a/boards/metro_m4/examples/adc.rs b/boards/metro_m4/examples/adc.rs index b86098925d34..6705b012a3bc 100644 --- a/boards/metro_m4/examples/adc.rs +++ b/boards/metro_m4/examples/adc.rs @@ -11,35 +11,55 @@ use panic_halt as _; #[cfg(feature = "use_semihosting")] use panic_semihosting as _; -use cortex_m_semihosting::hprintln; - use bsp::entry; -use hal::adc::Adc; -use hal::clock::GenericClockController; -use hal::gpio::B; -use hal::prelude::*; -use pac::gclk::pchctrl::Genselect::Gclk11; +use bsp::Pins; use pac::{CorePeripherals, Peripherals}; +use hal::{ + adc::{Accumulation, Adc, Config, Prescaler, Resolution}, + clock::v2::{clock_system_at_reset, pclk::Pclk}, +}; + #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_external_32kosc( + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + // TODO: currently this example only runs at the chip's + // 48 MHz CPU frequency at reset. There is currently a + // bug with the clock::v2 module that affects speeding + // up the CPU clock to a nominal 100 or 120 MHz. The bug + // is currently under investigation, and this example should + // be updated accordingly when it's fixed. + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, + peripherals.mclk, &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.port); - let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); - let mut adc0 = Adc::adc0(peripherals.adc0, &mut peripherals.mclk, &mut clocks, Gclk11); - let mut a0 = pins.a0.into_alternate::(); + + // Enable the ADC0 ABP clock... + let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); + // ...and enable the ADC0 PCLK. Both of these are required for the + // ADC to run. + let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + + let adc0_settings = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div32) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc = Adc::new(peripherals.adc0, adc0_settings, apb_adc0, &pclk_adc0).unwrap(); + let mut adc_pin = pins.a0.into_alternate(); loop { - let data: u16 = adc0.read(&mut a0).unwrap(); - hprintln!("{}", data).ok(); - delay.delay_ms(1000u16); + let mut buffer = [0; 16]; + let res = adc.read_buffer_blocking(&mut adc_pin, &mut buffer).unwrap(); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("Result: {:?}", res).unwrap(); } } diff --git a/boards/metro_m4/examples/async_adc.rs b/boards/metro_m4/examples/async_adc.rs new file mode 100644 index 000000000000..f650271b205d --- /dev/null +++ b/boards/metro_m4/examples/async_adc.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] + +use metro_m4 as bsp; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Adc, Adc0, Config, Prescaler, Resolution}, + clock::v2::{clock_system_at_reset, pclk::Pclk}, +}; + +atsamd_hal::bind_multiple_interrupts!(struct Irqs { + ADC0: [ADC0_RESRDY, ADC0_OTHER] => atsamd_hal::adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + // TODO: currently this example only runs at the chip's + // 48 MHz CPU frequency at reset. There is currently a + // bug with the clock::v2 module that affects speeding + // up the CPU clock to a nominal 100 or 120 MHz. The bug + // is currently under investigation, and this example should + // be updated accordingly when it's fixed. + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, + peripherals.gclk, + peripherals.mclk, + &mut peripherals.nvmctrl, + ); + + // Enable the ADC0 ABP clock... + let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); + // ...and enable the ADC0 PCLK. Both of these are required for the + // ADC to run. + let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + + let adc0_settings = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div32) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc = Adc::new(peripherals.adc0, adc0_settings, apb_adc0, &pclk_adc0) + .unwrap() + .into_future(Irqs); + let mut adc_pin = pins.a0.into_alternate(); + + loop { + let mut buffer = [0; 16]; + let res = adc.read_buffer(&mut adc_pin, &mut buffer).await.unwrap(); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("Result: {:?}", res).unwrap(); + } +} diff --git a/boards/pygamer/src/pins.rs b/boards/pygamer/src/pins.rs index 7e0ebcb619c9..c978e6347b7c 100644 --- a/boards/pygamer/src/pins.rs +++ b/boards/pygamer/src/pins.rs @@ -31,7 +31,6 @@ hal::bsp_peripherals!( pub use crate::buttons::ButtonReader; pub use crate::buttons::Keys; use hal::pwm::Pwm2; -use pac::{Adc0, Adc1}; /// Pin constants and type aliases pub use aliases::*; @@ -930,13 +929,13 @@ pub struct JoystickReader { impl JoystickReader { /// returns a tuple (x,y) where values are 12 bit, between 0-4095 /// values are NOT centered, but could be by subtracting 2048 - pub fn read(&mut self, adc: &mut hal::adc::Adc) -> (u16, u16) { + pub fn read(&mut self, adc: &mut hal::adc::Adc) -> (u16, u16) { //note adafruit averages 3 readings on x and y (not inside the adc) seems // unnecessary? note adafruit recenters around zero.. Im not doing that // either atm. - let y_data: u16 = adc.read(&mut self.joy_y).unwrap(); - let x_data: u16 = adc.read(&mut self.joy_x).unwrap(); + let y_data: u16 = adc.read_blocking(&mut self.joy_y); + let x_data: u16 = adc.read_blocking(&mut self.joy_x); (x_data, y_data) } @@ -969,8 +968,8 @@ pub struct BatteryReader { impl BatteryReader { /// Returns a float for voltage of battery - pub fn read(&mut self, adc: &mut hal::adc::Adc) -> f32 { - let data: u16 = adc.read(&mut self.battery).unwrap(); + pub fn read(&mut self, adc: &mut hal::adc::Adc) -> f32 { + let data: u16 = adc.read_blocking(&mut self.battery); let result: f32 = (data as f32 / 4095.0) * 2.0 * 3.3; result } diff --git a/boards/samd11_bare/Cargo.toml b/boards/samd11_bare/Cargo.toml index 3c44ed894bd3..52eb4a966bc9 100644 --- a/boards/samd11_bare/Cargo.toml +++ b/boards/samd11_bare/Cargo.toml @@ -35,6 +35,7 @@ version = "2.1.1" cortex-m = "0.7" panic-halt = "0.2" panic-probe = "0.2.0" +cortex-m-semihosting = "0.5" panic-semihosting = "0.5" rtt-target = {version = "0.3.0", features = ["cortex-m"]} diff --git a/boards/samd11_bare/examples/adc.rs b/boards/samd11_bare/examples/adc.rs index 3bd8c127266b..53bdf6fc8aa0 100644 --- a/boards/samd11_bare/examples/adc.rs +++ b/boards/samd11_bare/examples/adc.rs @@ -1,54 +1,61 @@ -//! Reads the adc and prints to rtt every second. -//! -//! Requires `cargo install probe-run` -//! -//! probe-run builds, uploads, and runs your code on device and in combination -//! with rtt-target and panic-probe prints debug and panic information to your -//! console. Its used for short running sessions like seeing the results of a -//! calculation or a measurement, a panic message or backtrace of an error right -//! on your command line. You can also force an exit with a -//! cortex_m::asm::bkpt() -//! -//! `cargo run --release --example adc` - #![no_std] #![no_main] -use panic_probe as _; +use samd11_bare as bsp; use bsp::hal; -use samd11_bare as bsp; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; use bsp::entry; -use hal::adc::Adc; -use hal::clock::GenericClockController; -use hal::gpio::*; -use hal::pac::{CorePeripherals, Peripherals}; -use hal::prelude::*; -use rtt_target::{rprintln, rtt_init_print}; +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Adc, Config, Prescaler, Resolution}, + clock::GenericClockController, +}; #[entry] fn main() -> ! { - rtt_init_print!(); - let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); - let mut clocks = GenericClockController::with_internal_32kosc( + // SAMD11 targets currently don't support clock::v2 + let mut clocks = GenericClockController::with_external_32kosc( peripherals.gclk, &mut peripherals.pm, &mut peripherals.sysctrl, &mut peripherals.nvmctrl, ); - let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); - let pins = bsp::Pins::new(peripherals.port); - - let mut adc = Adc::adc(peripherals.adc, &mut peripherals.pm, &mut clocks); - let mut a0: Pin<_, AlternateB> = pins.d1.into_mode(); + let gclk0 = clocks.gclk0(); + let adc_clock = clocks.adc(&gclk0).unwrap(); + + let adc_settings = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div32) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc = Adc::new( + peripherals.adc, + adc_settings, + &mut peripherals.pm, + &adc_clock, + ) + .unwrap(); + let mut adc_pin = pins.d1.into_alternate(); loop { - let data: u16 = adc.read(&mut a0).unwrap(); - rprintln!("{}", data); - delay.delay_ms(1000u16); + let mut buffer = [0; 16]; + let res = adc.read_buffer_blocking(&mut adc_pin, &mut buffer).unwrap(); + #[cfg(feature = "use_semihosting")] + cortex_m_semihosting::hprintln!("Result: {:?}", res); } } From e42ee42ebb0d22a97ae34c28ecd40f491ff8755a Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Sun, 16 Feb 2025 19:13:14 +1300 Subject: [PATCH 55/65] Fix misaligned flash read --- hal/src/peripherals/calibration/d11.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hal/src/peripherals/calibration/d11.rs b/hal/src/peripherals/calibration/d11.rs index 77f248ec82be..7780a18414ba 100644 --- a/hal/src/peripherals/calibration/d11.rs +++ b/hal/src/peripherals/calibration/d11.rs @@ -35,7 +35,15 @@ fn cal_with_errata( /// ADC Linearity Calibration. Should be written to ADC CALIB register. pub fn adc_linearity_cal() -> u8 { - cal(3, 3, 0b1111_111) as u8 + // Value in flash is bits 34:27, which spans a 32b boundary + + // bits 4:0 + let low = cal(0, 27, 0x1F) as u8; + + // bits 7:5 + let high = cal(4, 0, 0x7) as u8; + + high << 5 | low } /// ADC Bias Calibration. Should be written to ADC CALIB register. From 39b984a2df2e674fb26b6648df50cdf8d9c0b2c7 Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Sun, 16 Feb 2025 19:14:42 +1300 Subject: [PATCH 56/65] Make internal calibration API safer --- hal/src/peripherals/calibration/d11.rs | 31 ++++++++++++-------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/hal/src/peripherals/calibration/d11.rs b/hal/src/peripherals/calibration/d11.rs index 7780a18414ba..fbd1a021ae40 100644 --- a/hal/src/peripherals/calibration/d11.rs +++ b/hal/src/peripherals/calibration/d11.rs @@ -7,23 +7,20 @@ use core::ptr; const ADDR: u32 = 0x806020u32; -fn cal(addr_offset: u32, bit_shift: u32, bit_mask: u32) -> u32 { - unsafe { - let addr: *const u32 = (ADDR + addr_offset) as *const _; - let value = ptr::read(addr); - - (value >> bit_shift) & bit_mask - } +fn cal(word_offset: u32, bit_shift: u32, bit_mask: u32) -> u32 { + let addr = (ADDR + 4 * word_offset) as *const u32; + let value = unsafe { ptr::read(addr) }; + (value >> bit_shift) & bit_mask } fn cal_with_errata( - addr_offset: u32, + word_offset: u32, bit_shift: u32, bit_mask: u32, bad_val: u32, def_val: u32, ) -> u32 { - let val = cal(addr_offset, bit_shift, bit_mask); + let val = cal(word_offset, bit_shift, bit_mask); // if the value matches the bad value, use an alternative value // specified in the the errata section of the datasheet if val == bad_val { @@ -41,44 +38,44 @@ pub fn adc_linearity_cal() -> u8 { let low = cal(0, 27, 0x1F) as u8; // bits 7:5 - let high = cal(4, 0, 0x7) as u8; + let high = cal(1, 0, 0x7) as u8; high << 5 | low } /// ADC Bias Calibration. Should be written to ADC CALIB register. pub fn adc_bias_cal() -> u8 { - cal(4, 5, 0b11) as u8 + cal(1, 5, 0b11) as u8 } /// Returns the osc32k calibration value from the NVM calibration area pub fn osc32k_cal() -> u8 { - cal(4, 6, 0x7f) as u8 // 44:38 + cal(1, 6, 0x7f) as u8 // 44:38 } /// Returns the dfll48m coarse calibration value pub fn dfll48m_coarse_cal() -> u8 { - cal_with_errata(4, 26, 0x3f, 0x3f, 0x1f) as u8 + cal_with_errata(1, 26, 0x3f, 0x3f, 0x1f) as u8 } /// USB TRANSN calibration value. Should be written to USB PADCAL register. pub fn usb_transn_cal() -> u8 { - cal_with_errata(4, 13, 0x1f, 0x1f, 5) as u8 + cal_with_errata(1, 13, 0x1f, 0x1f, 5) as u8 } /// USB TRANSP calibration value. Should be written to USB PADCAL register. pub fn usb_transp_cal() -> u8 { - cal_with_errata(4, 18, 0x1f, 0x1f, 29) as u8 + cal_with_errata(1, 18, 0x1f, 0x1f, 29) as u8 } /// USB TRIM calibration value. Should be written to USB PADCAL register. #[hal_cfg("nvmctrl-d11")] pub fn usb_trim_cal() -> u8 { - cal_with_errata(4, 23, 7, 7, 5) as u8 + cal_with_errata(1, 23, 7, 7, 5) as u8 } /// USB TRIM calibration value. Should be written to USB PADCAL register. #[hal_cfg("nvmctrl-d21")] pub fn usb_trim_cal() -> u8 { - cal_with_errata(4, 23, 7, 7, 3) as u8 + cal_with_errata(1, 23, 7, 7, 3) as u8 } From 872e5bf29d5984c910ef07ee3f962d8bb03fc670 Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Sun, 16 Feb 2025 19:25:32 +1300 Subject: [PATCH 57/65] Fix math error in ADC bias calibration --- hal/src/peripherals/calibration/d11.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/src/peripherals/calibration/d11.rs b/hal/src/peripherals/calibration/d11.rs index fbd1a021ae40..b442147bace5 100644 --- a/hal/src/peripherals/calibration/d11.rs +++ b/hal/src/peripherals/calibration/d11.rs @@ -45,7 +45,7 @@ pub fn adc_linearity_cal() -> u8 { /// ADC Bias Calibration. Should be written to ADC CALIB register. pub fn adc_bias_cal() -> u8 { - cal(1, 5, 0b11) as u8 + cal(1, 3, 0b111) as u8 } /// Returns the osc32k calibration value from the NVM calibration area From cfcedf1e8a16e174ab1dd1a778d3396e9f7e8f87 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 4 Mar 2025 14:40:18 +0000 Subject: [PATCH 58/65] Improve ADC reading results by powering down and up ADC --- hal/src/peripherals/adc/d11/mod.rs | 9 ++++----- hal/src/peripherals/adc/d5x/mod.rs | 8 +++++++- hal/src/peripherals/adc/mod.rs | 30 ++++++++++++------------------ 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 6394b13a3de2..7efd08eaf017 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -36,11 +36,9 @@ impl AdcInstance for Adc0 { #[inline] fn calibrate(instance: &Self::Instance) { - instance.calib().write(|w| { - unsafe { - w.bias_cal().bits(calibration::adc_bias_cal()); - w.linearity_cal().bits(calibration::adc_linearity_cal()) - } + instance.calib().write(|w| unsafe { + w.bias_cal().bits(calibration::adc_bias_cal()); + w.linearity_cal().bits(calibration::adc_linearity_cal()) }); } @@ -142,6 +140,7 @@ impl Adc { // right after changing VREF value self.adc.swtrig().modify(|_, w| w.start().set_bit()); self.sync(); + self.adc.intflag().write(|w| w.resrdy().set_bit()); // Clear RESRDY self.adc.swtrig().modify(|_, w| w.start().set_bit()); } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 4123170848a9..6a0a4240617c 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -84,6 +84,7 @@ impl Adc { // Reset ADC here as we cannot guarantee its state // This also disables the ADC self.software_reset(); + self.sync(); I::calibrate(&self.adc); self.sync(); self.adc @@ -99,8 +100,12 @@ impl Adc { .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(config.sample_clock_cycles) }); // sample length self.sync(); - self.adc.inputctrl().modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) + self.adc.inputctrl().modify(|_, w| { + w.muxneg().gnd(); + w.diffmode().clear_bit() + }); // No negative input (internal gnd) self.sync(); + // Check bit width selected if config.accumulation != Accumulation::Single && config.bit_width != Resolution::_16bit { return Err(super::Error::InvalidSampleBitWidth); @@ -204,6 +209,7 @@ impl Adc { // right after changing VREF value self.adc.swtrig().modify(|_, w| w.start().set_bit()); self.sync(); + self.adc.intflag().write(|w| w.resrdy().set_bit()); // Clear RESRDY self.adc.swtrig().modify(|_, w| w.start().set_bit()); } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 3e02291305ee..9d2d1f35e494 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -281,18 +281,18 @@ impl Adc { fn read_blocking_channel(&mut self, ch: u8) -> u16 { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); - self.disable_interrupts(Flags::all()); + self.disable_freerunning(); + self.sync(); self.mux(ch); - - // Discard any potentially old measurements still lingering in the buffer - let _discard = self.conversion_result(); - + self.power_up(); self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - self.conversion_result() + let res = self.conversion_result(); + self.power_down(); + res } /// Read into a buffer from the provided ADC pin, in a blocking fashion @@ -314,9 +314,6 @@ impl Adc { self.disable_interrupts(Flags::all()); self.mux(ch); - // Discard any potentially old measurements still lingering in the buffer - let _discard = self.conversion_result(); - self.enable_freerunning(); self.start_conversion(); @@ -405,17 +402,17 @@ where async fn read_channel(&mut self, ch: u8) -> u16 { // Clear overrun errors that might've occured before we try to read anything let _ = self.check_and_clear_flags(self.read_flags()); - + self.disable_freerunning(); self.mux(ch); - - // Discard any potentially old measurements still lingering in the buffer - let _discard = self.conversion_result(); - + self.power_up(); self.start_conversion(); // Here we explicitly ignore the result, because we know that // overrun errors are impossible since the ADC is configured in one-shot mode. let _ = self.wait_flags(Flags::RESRDY).await; - self.conversion_result() + let res = self.conversion_result(); + self.power_down(); + self.sync(); + res } /// Read into a buffer from the provided ADC pin @@ -436,9 +433,6 @@ where self.mux(ch); - // Discard any potentially old measurements still lingering in the buffer - let _discard = self.conversion_result(); - self.enable_freerunning(); self.start_conversion(); From 5c5cfff6c1aba64305246350bd26192de3cfe479 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 11 Feb 2025 14:34:29 -0500 Subject: [PATCH 59/65] Fix fmt and clippy warns --- hal/src/peripherals/adc/d11/mod.rs | 2 -- hal/src/peripherals/adc/mod.rs | 19 ++++++++++--------- hal/src/peripherals/calibration/d11.rs | 1 + 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 7efd08eaf017..a26e162e1d52 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -5,8 +5,6 @@ use super::{ }; use crate::{calibration, pac}; -use pac::adc::avgctrl::Samplenumselect; -use pac::adc::ctrlb::Resselselect; use pac::adc::inputctrl::Gainselect; use pac::Peripherals; pub mod pin; diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 9d2d1f35e494..73a0df0c4d67 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -156,8 +156,8 @@ impl Adc { /// ## Important /// /// This function will return `Err` if the clock source provided - /// is faster than 100 MHz, since this is the maximum frequency for GCLK_ADCx - /// as per the datasheet. + /// is faster than 100 MHz, since this is the maximum frequency for + /// GCLK_ADCx as per the datasheet. /// /// The [`new`](Self::new) function currently takes an `&` reference to a /// [`Pclk`](crate::clock::v2::pclk::Pclk). In the future this will likely @@ -174,13 +174,14 @@ impl Adc { clk: crate::clock::v2::apb::ApbClk, pclk: &crate::clock::v2::pclk::Pclk, ) -> Result { - // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. However, since - // clock::v2 is not implemented for all chips yet, the generics for the Adc type would be - // different between chip families, leading to massive and unnecessary code duplication. In - // the meantime, we use a "lite" variation of the typelevel guarantees laid out by the - // clock::v2 module, meaning that we can guarantee that the clocks are enabled at the time - // of creation of the Adc struct; however we can't guarantee that the clock will stay - // enabled for the duration of its lifetime. + // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. + // However, since clock::v2 is not implemented for all chips yet, the + // generics for the Adc type would be different between chip families, + // leading to massive and unnecessary code duplication. In the meantime, + // we use a "lite" variation of the typelevel guarantees laid out by the + // clock::v2 module, meaning that we can guarantee that the clocks are enabled + // at the time of creation of the Adc struct; however we can't guarantee + // that the clock will stay enabled for the duration of its lifetime. if pclk.freq() > fugit::HertzU32::from_raw(100_000_000) { // Clock source is too fast diff --git a/hal/src/peripherals/calibration/d11.rs b/hal/src/peripherals/calibration/d11.rs index b442147bace5..3e8e2d6a573e 100644 --- a/hal/src/peripherals/calibration/d11.rs +++ b/hal/src/peripherals/calibration/d11.rs @@ -31,6 +31,7 @@ fn cal_with_errata( } /// ADC Linearity Calibration. Should be written to ADC CALIB register. +#[allow(clippy::unusual_byte_groupings)] pub fn adc_linearity_cal() -> u8 { // Value in flash is bits 34:27, which spans a 32b boundary From a6d87c6865be881e816855b9c870888e0a2a86c1 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Wed, 19 Feb 2025 19:39:53 -0500 Subject: [PATCH 60/65] Start fixing pygamer examples --- boards/pygamer/examples/neopixel_button.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/boards/pygamer/examples/neopixel_button.rs b/boards/pygamer/examples/neopixel_button.rs index ce897e23bd3b..617e35dad90c 100644 --- a/boards/pygamer/examples/neopixel_button.rs +++ b/boards/pygamer/examples/neopixel_button.rs @@ -10,12 +10,13 @@ #![no_std] #![no_main] +use atsamd_hal::adc::Config; #[cfg(not(feature = "panic_led"))] use panic_halt as _; use pygamer::{self as bsp, entry, hal, pac, pins::Keys, Pins}; use bsp::util::map_from; -use hal::adc::Adc; +use hal::adc::{Accumulation, Adc, Config, Prescaler, Resolution}; use hal::{clock::GenericClockController, delay::Delay}; use pac::gclk::pchctrl::Genselect::Gclk11; @@ -47,7 +48,13 @@ fn main() -> ! { let mut buttons = pins.buttons.init(); - let mut adc1 = Adc::adc1(peripherals.adc1, &mut peripherals.mclk, &mut clocks, Gclk11); + let adc1_settings = Config::new() + .clock_cycles_per_sample(5) + .clock_divider(Prescaler::Div32) + .sample_resolution(Resolution::_12bit) + .accumulation_method(Accumulation::Single); + + let mut adc1 = Adc::adc(peripherals.adc1, &mut peripherals.mclk, &mut clocks, Gclk11); let mut joystick = pins.joystick.init(); // neopixels From 371d802319aca98b99b1012e0e408253b80ab031 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Mar 2025 18:03:06 -0500 Subject: [PATCH 61/65] Fix overrun errors on thumbv6 targets --- boards/feather_m0/examples/adc.rs | 2 +- boards/feather_m0/examples/async_adc.rs | 2 +- boards/samd11_bare/examples/adc.rs | 2 +- hal/src/peripherals/adc/d5x/mod.rs | 2 +- hal/src/peripherals/adc/mod.rs | 14 ++++++++++++-- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index e3b6116eb265..ac20d1a331bc 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -39,7 +39,7 @@ fn main() -> ! { let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div32) + .clock_divider(Prescaler::Div64) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); diff --git a/boards/feather_m0/examples/async_adc.rs b/boards/feather_m0/examples/async_adc.rs index b355daa440f8..3e0e2a78ff06 100644 --- a/boards/feather_m0/examples/async_adc.rs +++ b/boards/feather_m0/examples/async_adc.rs @@ -42,7 +42,7 @@ async fn main(_s: embassy_executor::Spawner) -> ! { let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div32) + .clock_divider(Prescaler::Div64) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); diff --git a/boards/samd11_bare/examples/adc.rs b/boards/samd11_bare/examples/adc.rs index 53bdf6fc8aa0..fc5a1560ab6e 100644 --- a/boards/samd11_bare/examples/adc.rs +++ b/boards/samd11_bare/examples/adc.rs @@ -39,7 +39,7 @@ fn main() -> ! { let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div32) + .clock_divider(Prescaler::Div64) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 6a0a4240617c..8a5479c26a06 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -221,7 +221,7 @@ impl Adc { #[inline] pub(super) fn disable_freerunning(&mut self) { - self.adc.ctrlb().modify(|_, w| w.freerun().set_bit()); + self.adc.ctrlb().modify(|_, w| w.freerun().clear_bit()); self.sync(); } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 73a0df0c4d67..edf2e7fe3c3d 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -316,21 +316,26 @@ impl Adc { self.mux(ch); self.enable_freerunning(); + self.power_up(); self.start_conversion(); for result in dst.iter_mut() { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - *result = self.conversion_result(); if let Err(e) = self.check_and_clear_flags(self.read_flags()) { + self.power_down(); self.disable_freerunning(); + return Err(e); } - } + *result = self.conversion_result(); + } + self.power_down(); self.disable_freerunning(); + Ok(()) } @@ -435,17 +440,22 @@ where self.mux(ch); self.enable_freerunning(); + self.power_up(); self.start_conversion(); for result in dst.iter_mut() { if let Err(e) = self.wait_flags(Flags::RESRDY).await { + self.power_down(); self.disable_freerunning(); + return Err(e); } *result = self.conversion_result(); } + self.power_down(); self.disable_freerunning(); + Ok(()) } } From 6feda91e2ac465afa576add6b3bd10d2be19647d Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Tue, 4 Mar 2025 18:07:51 -0500 Subject: [PATCH 62/65] Fix dividers in examples --- boards/feather_m0/examples/adc.rs | 3 ++- boards/feather_m0/examples/async_adc.rs | 3 ++- boards/samd11_bare/examples/adc.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index ac20d1a331bc..529562498731 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -39,7 +39,8 @@ fn main() -> ! { let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div64) + // Overruns if clock divider < 128 in debug mode + .clock_divider(Prescaler::Div128) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); diff --git a/boards/feather_m0/examples/async_adc.rs b/boards/feather_m0/examples/async_adc.rs index 3e0e2a78ff06..6c3e91becfe3 100644 --- a/boards/feather_m0/examples/async_adc.rs +++ b/boards/feather_m0/examples/async_adc.rs @@ -42,7 +42,8 @@ async fn main(_s: embassy_executor::Spawner) -> ! { let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div64) + // Overruns if clock divider < 128 in debug mode + .clock_divider(Prescaler::Div128) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); diff --git a/boards/samd11_bare/examples/adc.rs b/boards/samd11_bare/examples/adc.rs index fc5a1560ab6e..ebb4cc885182 100644 --- a/boards/samd11_bare/examples/adc.rs +++ b/boards/samd11_bare/examples/adc.rs @@ -39,7 +39,8 @@ fn main() -> ! { let adc_settings = Config::new() .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div64) + // Overruns if clock divider < 128 in debug mode + .clock_divider(Prescaler::Div128) .sample_resolution(Resolution::_12bit) .accumulation_method(Accumulation::Single); From 1ef46e0c248aa6097bf768fdc90421904cfe84d8 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 Mar 2025 21:08:44 +0000 Subject: [PATCH 63/65] Remove pygamer neopixel examples --- boards/pygamer/Cargo.toml | 20 --- .../pygamer/examples/neopixel_adc_battery.rs | 89 ------------- boards/pygamer/examples/neopixel_adc_light.rs | 88 ------------- boards/pygamer/examples/neopixel_button.rs | 122 ------------------ boards/pygamer/examples/neopixel_easing.rs | 102 --------------- boards/pygamer/examples/neopixel_rainbow.rs | 84 ------------ 6 files changed, 505 deletions(-) delete mode 100644 boards/pygamer/examples/neopixel_adc_battery.rs delete mode 100644 boards/pygamer/examples/neopixel_adc_light.rs delete mode 100644 boards/pygamer/examples/neopixel_button.rs delete mode 100644 boards/pygamer/examples/neopixel_easing.rs delete mode 100644 boards/pygamer/examples/neopixel_rainbow.rs diff --git a/boards/pygamer/Cargo.toml b/boards/pygamer/Cargo.toml index 3b2db575f0bf..3d83cce51f61 100644 --- a/boards/pygamer/Cargo.toml +++ b/boards/pygamer/Cargo.toml @@ -83,26 +83,6 @@ name = "clock_out" [[example]] name = "ferris_img" -[[example]] -name = "neopixel_adc_battery" -required-features = ["neopixel-spi"] - -[[example]] -name = "neopixel_adc_light" -required-features = ["neopixel-spi"] - -[[example]] -name = "neopixel_button" -required-features = ["neopixel-spi"] - -[[example]] -name = "neopixel_easing" -required-features = ["neopixel-spi"] - -[[example]] -name = "neopixel_rainbow" -required-features = ["neopixel-spi"] - [[example]] name = "pwm_tc4" diff --git a/boards/pygamer/examples/neopixel_adc_battery.rs b/boards/pygamer/examples/neopixel_adc_battery.rs deleted file mode 100644 index 0a048aa5f0e4..000000000000 --- a/boards/pygamer/examples/neopixel_adc_battery.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Display battery percentage on the neopixels. -//! -//! Note leds may appear white during debug. Either build for release or add -//! opt-level = 2 to profile.dev in Cargo.toml - -#![no_std] -#![no_main] - -#[cfg(not(feature = "panic_led"))] -use panic_halt as _; -use pygamer::{entry, hal, pac, Pins}; - -use hal::adc::Adc; -use hal::{clock::GenericClockController, delay::Delay}; - -use pac::gclk::pchctrl::Genselect::Gclk11; - -use hal::ehal::delay::DelayNs; - -use pac::{CorePeripherals, Peripherals}; -use smart_leds::{brightness, SmartLedsWrite, RGB8}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl, - ); - let pins = Pins::new(peripherals.port).split(); - - let mut adc0 = Adc::adc0(peripherals.adc0, &mut peripherals.mclk, &mut clocks, Gclk11); - let mut battery = pins.battery.init(); - - // neopixels - let mut neopixel = pins.neopixel.init_spi( - &mut clocks, - // Unfortunately, the SPI driver requires a clock pin, even though it's not used by the - // neopixels. - pins.i2c.scl, - peripherals.sercom2, - &mut peripherals.mclk, - ); - - let mut delay = Delay::new(core.SYST, &mut clocks); - - //todo put this on a .. 10minute, 30min, update timer - loop { - let battery_data = battery.read(&mut adc0); - - let mut colors = [ - RGB8::default(), - RGB8::default(), - RGB8::default(), - RGB8::default(), - RGB8::default(), - ]; - - if battery_data < 3.6 { - enable_leds(1, &mut colors); - } else if (3.6..3.8).contains(&battery_data) { - enable_leds(2, &mut colors); - } else if (3.8..3.9).contains(&battery_data) { - enable_leds(3, &mut colors); - } else if (3.9..4.0).contains(&battery_data) { - enable_leds(4, &mut colors); - } else { - enable_leds(5, &mut colors); - }; - - neopixel - .write(brightness(colors.iter().cloned(), 1)) - .unwrap(); - - // Reset the LEDs - delay.delay_ms(10); - } -} - -/// Turn on the specified number of LEDs and set the color to red. -fn enable_leds(num_leds: usize, colors: &mut [RGB8]) { - for color in colors.iter_mut().take(num_leds) { - *color = RGB8::from((255, 0, 0)); - } -} diff --git a/boards/pygamer/examples/neopixel_adc_light.rs b/boards/pygamer/examples/neopixel_adc_light.rs deleted file mode 100644 index 4ed277709b2e..000000000000 --- a/boards/pygamer/examples/neopixel_adc_light.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Display light sensor reading on the neopixels. -//! -//! Note leds may appear white during debug. Either build for release or add -//! opt-level = 2 to profile.dev in Cargo.toml - -#![no_std] -#![no_main] - -#[cfg(not(feature = "panic_led"))] -use panic_halt as _; -use pygamer::{entry, hal, pac, Pins}; - -use hal::adc::Adc; -use hal::prelude::*; -use hal::{clock::GenericClockController, delay::Delay}; -use pac::gclk::pchctrl::Genselect::Gclk11; -use pac::{CorePeripherals, Peripherals}; -use smart_leds::SmartLedsWrite; -use smart_leds::{ - hsv::{hsv2rgb, Hsv}, - RGB8, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl, - ); - let pins = Pins::new(peripherals.port).split(); - - let mut adc1 = Adc::adc1(peripherals.adc1, &mut peripherals.mclk, &mut clocks, Gclk11); - let mut light = pins.light_pin.into_alternate(); - - // neopixels - let mut neopixel = pins.neopixel.init_spi( - &mut clocks, - // Unfortunately, the SPI driver requires a clock pin, even though it's not used by the - // neopixels. - pins.i2c.scl, - peripherals.sercom2, - &mut peripherals.mclk, - ); - - let mut delay = Delay::new(core.SYST, &mut clocks); - - const NUM_LEDS: usize = 5; - let mut j: u8 = 0; - - loop { - let light_data: u16 = adc1.read(&mut light).unwrap(); - - let pos: usize = if light_data < 100 { - 0 - } else if (147..1048).contains(&light_data) { - 1 - } else if (1048..3048).contains(&light_data) { - 2 - } else if (3048..3948).contains(&light_data) { - 3 - } else { - 4 - }; - - //finally paint the one led wherever the position is - let _ = neopixel.write((0..NUM_LEDS).map(|i| { - if i == pos { - hsv2rgb(Hsv { - hue: j, - sat: 255, - val: 32, - }) - } else { - RGB8::default() - } - })); - - //incremement the hue easing - j = j.wrapping_add(1); - - delay.delay_ms(10u8); - } -} diff --git a/boards/pygamer/examples/neopixel_button.rs b/boards/pygamer/examples/neopixel_button.rs deleted file mode 100644 index 617e35dad90c..000000000000 --- a/boards/pygamer/examples/neopixel_button.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! Joystick y controls the color of a neopixel while Joystick x moves it -//! left and right around the center neopixel -//! Select and Start control a second neopixel left and right while it is -//! automatically rotating through the color wheel -//! When they overlap, joystick takes precedence -//! -//! Note leds may appear white during debug. Either build for release or add -//! opt-level = 2 to profile.dev in Cargo.toml - -#![no_std] -#![no_main] - -use atsamd_hal::adc::Config; -#[cfg(not(feature = "panic_led"))] -use panic_halt as _; -use pygamer::{self as bsp, entry, hal, pac, pins::Keys, Pins}; - -use bsp::util::map_from; -use hal::adc::{Accumulation, Adc, Config, Prescaler, Resolution}; -use hal::{clock::GenericClockController, delay::Delay}; - -use pac::gclk::pchctrl::Genselect::Gclk11; -use pac::{CorePeripherals, Peripherals}; - -use hal::ehal::delay::DelayNs; - -use smart_leds::SmartLedsWrite; -use smart_leds::{ - hsv::{hsv2rgb, Hsv}, - RGB8, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let core_peripherals = CorePeripherals::take().unwrap(); - - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl, - ); - - let mut delay = Delay::new(core_peripherals.SYST, &mut clocks); - let pins = Pins::new(peripherals.port).split(); - - let mut buttons = pins.buttons.init(); - - let adc1_settings = Config::new() - .clock_cycles_per_sample(5) - .clock_divider(Prescaler::Div32) - .sample_resolution(Resolution::_12bit) - .accumulation_method(Accumulation::Single); - - let mut adc1 = Adc::adc(peripherals.adc1, &mut peripherals.mclk, &mut clocks, Gclk11); - let mut joystick = pins.joystick.init(); - - // neopixels - let mut neopixel = pins.neopixel.init_spi( - &mut clocks, - // Unfortunately, the SPI driver requires a clock pin, even though it's not used by the - // neopixels. - pins.i2c.scl, - peripherals.sercom2, - &mut peripherals.mclk, - ); - - const NUM_LEDS: usize = 5; - let mut pos_button: usize = 2; - let mut color_button: u8 = 0; - loop { - let (x, y) = joystick.read(&mut adc1); - - // map up/down to control rainbow color 0-255 - let color_joy = map_from(y as i16, (0, 4095), (0, 255)) as u8; - - // map left/right to neopixel position 0-4 - // joystick is not quite linear, rests at second pixel - // shifting up by 500 seems to help - let pos_joy = map_from(x as i16 + 500, (0, 4595), (0, 4)) as usize; - - for event in buttons.events() { - match event { - Keys::SelectDown => { - pos_button = pos_button.saturating_sub(1); - } - Keys::StartDown => { - if pos_button < 4 { - pos_button += 1; - } - } - _ => {} - } - } - - //finally paint the two leds at position, accel priority - let _ = neopixel.write((0..NUM_LEDS).map(|i| { - if i == pos_joy { - hsv2rgb(Hsv { - hue: color_joy, - sat: 255, - val: 32, - }) - } else if i == pos_button { - hsv2rgb(Hsv { - hue: color_button, - sat: 255, - val: 32, - }) - } else { - RGB8::default() - } - })); - - //incremement the hue easing - color_button = color_button.wrapping_add(1); - - delay.delay_ms(5); - } -} diff --git a/boards/pygamer/examples/neopixel_easing.rs b/boards/pygamer/examples/neopixel_easing.rs deleted file mode 100644 index 43982c80b83c..000000000000 --- a/boards/pygamer/examples/neopixel_easing.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Randomly choose and led and color to breath in and out -//! -//! Note leds may appear white during debug. Either build for release or add -//! opt-level = 2 to profile.dev in Cargo.toml - -#![no_std] -#![no_main] - -#[cfg(not(feature = "panic_led"))] -use panic_halt as _; -use pygamer::{entry, hal, pac, Pins}; - -use core::f32::consts::FRAC_PI_2; - -use hal::clock::GenericClockController; -use hal::delay::Delay; -use hal::trng::Trng; - -use pac::{CorePeripherals, Peripherals}; - -use hal::ehal::delay::DelayNs; - -use micromath::F32Ext; -use smart_leds::SmartLedsWrite; -use smart_leds::{ - hsv::{hsv2rgb, Hsv}, - RGB8, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl, - ); - - let pins = Pins::new(peripherals.port).split(); - - // neopixels - let mut neopixel = pins.neopixel.init_spi( - &mut clocks, - // Unfortunately, the SPI driver requires a clock pin, even though it's not used by the - // neopixels. - pins.i2c.scl, - peripherals.sercom2, - &mut peripherals.mclk, - ); - - let mut delay = Delay::new(core.SYST, &mut clocks); - - let trng = Trng::new(&mut peripherals.mclk, peripherals.trng); - - const NUM_LEDS: usize = 5; - - loop { - let rand = trng.random_u8(); - let pos: usize = rand.wrapping_rem(5) as usize; //random led - - //slowly enable led - for j in 0..255u8 { - let _ = neopixel.write((0..NUM_LEDS).map(|i| { - if i == pos { - hsv2rgb(Hsv { - hue: rand, - sat: 255, - val: sine_ease_in(j as f32, 0.0, 32.0, 255.0) as u8, - }) - } else { - RGB8::default() - } - })); - delay.delay_ms(5); - } - - //slowly disable led - note the reverse .rev() - for j in (0..255u8).rev() { - let _ = neopixel.write((0..NUM_LEDS).map(|i| { - if i == pos { - hsv2rgb(Hsv { - hue: rand, - sat: 255, - val: sine_ease_in(j as f32, 0.0, 32.0, 255.0) as u8, - }) - } else { - RGB8::default() - } - })); - delay.delay_ms(5); - } - } -} - -#[inline] -// current step, where oputput starts, where output ends, last step -fn sine_ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 { - -c * (t / d * FRAC_PI_2).cos() + c + b -} diff --git a/boards/pygamer/examples/neopixel_rainbow.rs b/boards/pygamer/examples/neopixel_rainbow.rs deleted file mode 100644 index d31d40d222ad..000000000000 --- a/boards/pygamer/examples/neopixel_rainbow.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Rotate all neopixel leds through a rainbow. Uses a luckily placed set of SPI -//! pins as a timer source. -//! -//! Note leds may appear white during debug. Either build for release or add -//! opt-level = 2 to profile.dev in Cargo.toml - -#![no_std] -#![no_main] - -#[cfg(not(feature = "panic_led"))] -use panic_halt as _; -use pygamer::{entry, hal, pac, Pins}; - -use hal::{clock::GenericClockController, delay::Delay}; - -use pac::{CorePeripherals, Peripherals}; - -use hal::ehal::delay::DelayNs; - -use smart_leds::hsv::{hsv2rgb, Hsv}; -use smart_leds::SmartLedsWrite; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl, - ); - let pins = Pins::new(peripherals.port).split(); - - // neopixels - let mut neopixel = pins.neopixel.init_spi( - &mut clocks, - // Unfortunately, the SPI driver requires a clock pin, even though it's not used by the - // neopixels. - pins.i2c.scl, - peripherals.sercom2, - &mut peripherals.mclk, - ); - - let mut delay = Delay::new(core.SYST, &mut clocks); - - loop { - for j in 0..255u8 { - let colors = [ - // split the color changes across all 5 leds evenly, 255/5=51 - // and have them safely wrap over when they go above 255 - hsv2rgb(Hsv { - hue: j, - sat: 255, - val: 32, - }), - hsv2rgb(Hsv { - hue: j.wrapping_add(51), - sat: 255, - val: 32, - }), - hsv2rgb(Hsv { - hue: j.wrapping_add(102), - sat: 255, - val: 32, - }), - hsv2rgb(Hsv { - hue: j.wrapping_add(153), - sat: 255, - val: 32, - }), - hsv2rgb(Hsv { - hue: j.wrapping_add(204), - sat: 255, - val: 32, - }), - ]; - - neopixel.write(colors.iter().cloned()).unwrap(); - delay.delay_ms(5); - } - } -} From d4117e7a36b423d87f92462749e29ceb082e998b Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 Mar 2025 21:14:24 +0000 Subject: [PATCH 64/65] Fix Samd21g build --- hal/src/peripherals/calibration/d11.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/src/peripherals/calibration/d11.rs b/hal/src/peripherals/calibration/d11.rs index 3e8e2d6a573e..e39eb52ebfb6 100644 --- a/hal/src/peripherals/calibration/d11.rs +++ b/hal/src/peripherals/calibration/d11.rs @@ -41,7 +41,7 @@ pub fn adc_linearity_cal() -> u8 { // bits 7:5 let high = cal(1, 0, 0x7) as u8; - high << 5 | low + (high << 5) | low } /// ADC Bias Calibration. Should be written to ADC CALIB register. From 34fc57167b9818cd253d161dfc30044f86a33727 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sun, 30 Mar 2025 21:05:17 +0100 Subject: [PATCH 65/65] First round of ADC Doc fixes --- boards/feather_m0/examples/adc.rs | 1 + hal/src/peripherals/adc/config.rs | 75 ++++++++++++++++++------------ hal/src/peripherals/adc/d5x/mod.rs | 1 + hal/src/peripherals/adc/mod.rs | 11 ++++- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 529562498731..11799c1fd96a 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -54,6 +54,7 @@ fn main() -> ! { let mut adc_pin = pins.a0.into_alternate(); loop { + #[allow(dead_code)] let mut buffer = [0; 16]; let res = adc.read_buffer_blocking(&mut adc_pin, &mut buffer).unwrap(); #[cfg(feature = "use_semihosting")] diff --git a/hal/src/peripherals/adc/config.rs b/hal/src/peripherals/adc/config.rs index 87ccbb5913b7..2a1ceb979b11 100644 --- a/hal/src/peripherals/adc/config.rs +++ b/hal/src/peripherals/adc/config.rs @@ -21,13 +21,19 @@ pub use adc0::refctrl::Refselselect as Reference; /// Result accumulation strategy for the ADC #[derive(Copy, Clone, PartialEq, Eq)] pub enum Accumulation { - /// The ADC will read once and then the result is ready + /// The ADC will read once and then the result is ready. + /// + /// The result will be in the users chosen bitwidth Single, - /// The ADC will read [AdcSampleCount] samples, average them out - /// into a 16 bit wide value, and then the result is ready + /// The ADC will read [SampleCount] samples, average them out + /// into a 16 bit wide value, and then the result is ready. + /// + /// The result will be in the range of 0-65535 (16bit) Average(SampleCount), - /// The ADC will read [AdcSampleCount] samples, sum them - /// into a 16 bit wide value, and then the result is ready + /// The ADC will read [SampleCount] samples, sum them + /// into a 16 bit wide value, and then the result is ready. + /// + /// The result will be in the range of 0-65535 (16bit) Summed(SampleCount), } @@ -38,15 +44,15 @@ pub enum Accumulation { /// the sample rate of the ADC /// /// To begin with, the ADC Clock is driven by the peripheral clock divided with -/// a divider ([AdcDivider]). +/// a divider (see [Config::clock_divider]). /// /// Each sample is read by the ADC over -/// [AdcSettingsBuilder::sample_clock_cycles] clock cycles, and then transmitted -/// to the ADC register over [AdcSettingsBuilder::bit_width] clock cycles (1 +/// [Config::sample_clock_cycles] clock cycles, and then transmitted +/// to the ADC register over [Config::bit_width] clock cycles (1 /// clock cycle per bit) /// /// The ADC can also be configured to combine multiple simultaneous readings in -/// either an average or summed mode (See [AdcAccumulation]), this also affects +/// either an average or summed mode (See [Accumulation]), this also affects /// the overall sample rate of the ADC as the ADC has to do multiple /// samples before a result is ready. /// @@ -71,16 +77,13 @@ pub struct Config { } impl Config { - /// - /// Configure the ADC to sample at 250_000 SPS (Assuming the clock source is + /// Configure the ADC to sample at 250_000 SPS (Assuming the ADC Gclk source is /// 48MHz) using the following settings: /// * clock divider factor of 32 /// * 6 clock cycles per sample - /// * 12bit sampling - /// * Single accumulation (No averaging or summing) - /// - /// ## Additional reading settings by default - /// * Use VDDANA as reference voltage for a full 0.0-3.3V reading + /// * 12bit ADC result resolution + /// * ADC will not perform any averaging or summation of multiple readings + /// * Use Intvcc1 (Analog reference voltage) as reference voltage for a full 0.0-3.3V reading pub fn new() -> Self { Self { clk_divider: Prescaler::Div32, @@ -91,7 +94,6 @@ impl Config { } } - /// /// This setting adjusts the ADC clock frequency by dividing the input clock /// for the ADC. /// @@ -102,12 +104,17 @@ impl Config { self } - /// This setting adjusts the bit width of each ADC sample + /// Sets the ADC output resolution + /// + /// ## Application Notes + /// [Resolution::_16bit] can only be used if the ADC is in summation or averaging mode. See [Config::accumulation_method] for + /// more detail pub fn sample_resolution(mut self, bit_width: Resolution) -> Self { self.bit_width = bit_width; self } + /// Sets the ADC reference voltage source pub fn with_vref(mut self, reference: Reference) -> Self { self.vref = reference; self @@ -119,35 +126,41 @@ impl Config { /// The default is single (ADC will return a sample as soon as it is /// measured) /// - /// Setting [AdcAccumulation::Summed] will make the ADC take 'n' samples, + /// Setting [Accumulation::Summed] will make the ADC take 'n' samples, /// and sum the total before returning it /// - /// Setting [AdcAccumulation::Average] will make the ADC take 'n' samples, + /// Setting [Accumulation::Average] will make the ADC take 'n' samples, /// and average the total before returning it /// - /// NOTE: Selecting [AdcAccumulation::Summed] or [AdcAccumulation::Average] - /// will reduce the overall ADC sample rate by a factor of 1/n, and the - /// returned value will be 16bits long no matter what the sample Bit - /// width was selected as + /// ## Application notes + /// + /// * Selecting [Accumulation::Summed] or [Accumulation::Average] + /// will reduce the overall ADC sample rate by a factor of 1/n, and + /// the ADC resolution will be set to [Resolution::_16bit] which is required + /// in these modes. pub fn accumulation_method(mut self, method: Accumulation) -> Self { self.accumulation = method; + if Accumulation::Single != self.accumulation { + // Auto set 16bit + self.bit_width = Resolution::_16bit; + } self } - /// This adjusts the number of ADC clock cycles taken to sample a single + /// Sets the number of ADC clock cycles taken to sample a single /// sample. The higher this number, the longer it will take the ADC to - /// sample each sample. + /// sample each sample. Smaller values will make the ADC perform more samples per second, + /// but there may be more noise in each sample leading to irratic values. /// /// ## Safety - /// Internally, this function will clamp the minimum input value to 1 to - /// avoid 0 + /// * This function will clamp input value between 1 and 63, to conform to the ADC registers + /// min and max values. pub fn clock_cycles_per_sample(mut self, num: u8) -> Self { - self.sample_clock_cycles = 1.max(num); // Prevent 0 + self.sample_clock_cycles = num.clamp(1, 63); // Clamp in range self } - /// - /// Returns a calculated sample rate of the ADC with these settings + /// Returns a calculated sample rate based on the settings used pub fn calculate_sps(&self, clock_freq: u32) -> u32 { let div = self.clk_divider as u32; let adc_clk_freq = clock_freq / div; diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 8a5479c26a06..c29e6487d428 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -141,6 +141,7 @@ impl Adc { impl Adc { #[inline] + /// As per the datasheet fn tp_tc_to_temp(&self, tp: f32, tc: f32) -> f32 { let tl = calibration::tl(); let th = calibration::th(); diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index edf2e7fe3c3d..777e837c8365 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -202,7 +202,7 @@ impl Adc { /// /// ## Important /// - /// This function will return `Err` if the clock source provided + /// This function will return [Error::ClockTooFast] if the clock source provided /// is faster than 48 MHz, since this is the maximum frequency for the /// ADC as per the datasheet. #[hal_cfg(any("adc-d11", "adc-d21"))] @@ -228,6 +228,15 @@ impl Adc { Ok(new_adc) } + /// Use the [`Adc`] in async mode. You are required to provide the + /// struct created by the + /// [`bind_interrupts`](crate::bind_interrupts) macro to prove + /// that the interrupt sources have been correctly configured. This function + /// will automatically enable the relevant NVIC interrupt sources. However, + /// you are required to configure the desired interrupt priorities prior to + /// calling this method. Consult [`crate::async_hal::interrupts`] + /// module-level documentation for more information. + /// [`bind_interrupts`](crate::bind_interrupts). #[cfg(feature = "async")] #[atsamd_hal_macros::hal_macro_helper] #[inline]