diff options
| author | James Munns <[email protected]> | 2025-12-10 14:11:26 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-10 14:11:26 +0000 |
| commit | e932dc131e94f30db32b3fc9fbce8cd929555820 (patch) | |
| tree | 74fcf10c814babbbd3ea395624a741af868f3180 | |
| parent | 28a72e08265aea090e3968c0b564bb929f2a81ed (diff) | |
| parent | 62966d6fac20a7af7257e0a1a9015e1b7093b5ba (diff) | |
Merge pull request #5012 from MathisDeroo/adc_driver_v2
[MCXA]: Adc driver v2
| -rw-r--r-- | embassy-mcxa/Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy-mcxa/src/adc.rs | 553 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 3 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/periph_helpers.rs | 1 | ||||
| -rw-r--r-- | embassy-mcxa/src/interrupt.rs | 41 | ||||
| -rw-r--r-- | embassy-mcxa/src/lib.rs | 6 | ||||
| -rw-r--r-- | embassy-mcxa/src/pins.rs | 33 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/adc_interrupt.rs | 49 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/adc_polling.rs | 32 | ||||
| -rw-r--r-- | examples/mcxa/src/lib.rs | 16 |
10 files changed, 495 insertions, 241 deletions
diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml index 8ed842aec..cb985a2e9 100644 --- a/embassy-mcxa/Cargo.toml +++ b/embassy-mcxa/Cargo.toml | |||
| @@ -39,7 +39,7 @@ embedded-hal-nb = { version = "1.0" } | |||
| 39 | embedded-io = "0.6" | 39 | embedded-io = "0.6" |
| 40 | embedded-io-async = { version = "0.6.1" } | 40 | embedded-io-async = { version = "0.6.1" } |
| 41 | heapless = "0.8" | 41 | heapless = "0.8" |
| 42 | mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "e7dfed8740b449b6ac646bab8ac6776a3c099267" } | 42 | mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "02bd04a21ef8f8f67f88239ff5df765bb7e60fd8" } |
| 43 | nb = "1.1.0" | 43 | nb = "1.1.0" |
| 44 | paste = "1.0.15" | 44 | paste = "1.0.15" |
| 45 | maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] } | 45 | maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] } |
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs index 7475299ba..d7d17cf5f 100644 --- a/embassy-mcxa/src/adc.rs +++ b/embassy-mcxa/src/adc.rs | |||
| @@ -1,42 +1,24 @@ | |||
| 1 | //! ADC driver | 1 | //! ADC driver |
| 2 | use core::sync::atomic::{AtomicBool, Ordering}; | 2 | use core::marker::PhantomData; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | 4 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 5 | use maitake_sync::WaitCell; | ||
| 6 | use paste::paste; | ||
| 5 | 7 | ||
| 6 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | 8 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; |
| 7 | use crate::clocks::{Gate, PoweredClock, enable_and_reset}; | 9 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; |
| 10 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 11 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 8 | use crate::pac; | 12 | use crate::pac; |
| 9 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | 13 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; |
| 10 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | 14 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; |
| 11 | use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode}; | 15 | use crate::pac::adc1::cmdl1::{Adch, Mode}; |
| 12 | use crate::pac::adc1::ctrl::CalAvgs; | 16 | use crate::pac::adc1::ctrl::CalAvgs; |
| 13 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; | 17 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; |
| 14 | 18 | ||
| 15 | type Regs = pac::adc1::RegisterBlock; | 19 | const G_LPADC_RESULT_SHIFT: u32 = 0; |
| 16 | |||
| 17 | static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); | ||
| 18 | // Token-based instance pattern like embassy-imxrt | ||
| 19 | pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType { | ||
| 20 | fn ptr() -> *const Regs; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Token for ADC1 | ||
| 24 | pub type Adc1 = crate::peripherals::ADC1; | ||
| 25 | impl Instance for crate::peripherals::ADC1 { | ||
| 26 | #[inline(always)] | ||
| 27 | fn ptr() -> *const Regs { | ||
| 28 | pac::Adc1::ptr() | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | // Also implement Instance for the Peri wrapper type | ||
| 33 | // impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { | ||
| 34 | // #[inline(always)] | ||
| 35 | // fn ptr() -> *const Regs { | ||
| 36 | // pac::Adc1::ptr() | ||
| 37 | // } | ||
| 38 | // } | ||
| 39 | 20 | ||
| 21 | /// Trigger priority policy for ADC conversions. | ||
| 40 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 41 | #[repr(u8)] | 23 | #[repr(u8)] |
| 42 | pub enum TriggerPriorityPolicy { | 24 | pub enum TriggerPriorityPolicy { |
| @@ -52,20 +34,40 @@ pub enum TriggerPriorityPolicy { | |||
| 52 | TriggerPriorityExceptionDisabled = 16, | 34 | TriggerPriorityExceptionDisabled = 16, |
| 53 | } | 35 | } |
| 54 | 36 | ||
| 37 | /// Configuration for the LPADC peripheral. | ||
| 55 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 38 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 56 | pub struct LpadcConfig { | 39 | pub struct LpadcConfig { |
| 40 | /// Control system transition to Stop and Wait power modes while ADC is converting. | ||
| 41 | /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. | ||
| 42 | /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. | ||
| 57 | pub enable_in_doze_mode: bool, | 43 | pub enable_in_doze_mode: bool, |
| 44 | /// Auto-Calibration Averages. | ||
| 58 | pub conversion_average_mode: CalAvgs, | 45 | pub conversion_average_mode: CalAvgs, |
| 46 | /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption). | ||
| 59 | pub enable_analog_preliminary: bool, | 47 | pub enable_analog_preliminary: bool, |
| 48 | /// Power-up delay value (in ADC clock cycles) | ||
| 60 | pub power_up_delay: u8, | 49 | pub power_up_delay: u8, |
| 50 | /// Reference voltage source selection | ||
| 61 | pub reference_voltage_source: Refsel, | 51 | pub reference_voltage_source: Refsel, |
| 52 | /// Power configuration selection. | ||
| 62 | pub power_level_mode: Pwrsel, | 53 | pub power_level_mode: Pwrsel, |
| 54 | /// Trigger priority policy for handling multiple triggers | ||
| 63 | pub trigger_priority_policy: TriggerPriorityPolicy, | 55 | pub trigger_priority_policy: TriggerPriorityPolicy, |
| 56 | /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, | ||
| 57 | /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. | ||
| 64 | pub enable_conv_pause: bool, | 58 | pub enable_conv_pause: bool, |
| 59 | /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. | ||
| 60 | /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. | ||
| 65 | pub conv_pause_delay: u16, | 61 | pub conv_pause_delay: u16, |
| 62 | /// FIFO watermark level for interrupt generation. | ||
| 63 | /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field, | ||
| 64 | /// the ready flag would be asserted to indicate stored data has reached the programmable threshold. | ||
| 66 | pub fifo_watermark: u8, | 65 | pub fifo_watermark: u8, |
| 66 | /// Power configuration (normal/deep sleep behavior) | ||
| 67 | pub power: PoweredClock, | 67 | pub power: PoweredClock, |
| 68 | /// ADC clock source selection | ||
| 68 | pub source: AdcClockSel, | 69 | pub source: AdcClockSel, |
| 70 | /// Clock divider for ADC clock | ||
| 69 | pub div: Div4, | 71 | pub div: Div4, |
| 70 | } | 72 | } |
| 71 | 73 | ||
| @@ -89,9 +91,11 @@ impl Default for LpadcConfig { | |||
| 89 | } | 91 | } |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 94 | /// Configuration for a conversion command. | ||
| 95 | /// | ||
| 96 | /// Defines the parameters for a single ADC conversion operation. | ||
| 92 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 97 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 93 | pub struct ConvCommandConfig { | 98 | pub struct ConvCommandConfig { |
| 94 | pub sample_channel_mode: Ctype, | ||
| 95 | pub channel_number: Adch, | 99 | pub channel_number: Adch, |
| 96 | pub chained_next_command_number: Next, | 100 | pub chained_next_command_number: Next, |
| 97 | pub enable_auto_channel_increment: bool, | 101 | pub enable_auto_channel_increment: bool, |
| @@ -105,6 +109,9 @@ pub struct ConvCommandConfig { | |||
| 105 | pub enable_wait_trigger: bool, | 109 | pub enable_wait_trigger: bool, |
| 106 | } | 110 | } |
| 107 | 111 | ||
| 112 | /// Configuration for a conversion trigger. | ||
| 113 | /// | ||
| 114 | /// Defines how a trigger initiates ADC conversions. | ||
| 108 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 115 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 109 | pub struct ConvTriggerConfig { | 116 | pub struct ConvTriggerConfig { |
| 110 | pub target_command_id: Tcmd, | 117 | pub target_command_id: Tcmd, |
| @@ -113,32 +120,117 @@ pub struct ConvTriggerConfig { | |||
| 113 | pub enable_hardware_trigger: bool, | 120 | pub enable_hardware_trigger: bool, |
| 114 | } | 121 | } |
| 115 | 122 | ||
| 123 | /// Shorthand for `Result<T>`. | ||
| 124 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 125 | |||
| 126 | /// ADC Error types | ||
| 127 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 128 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 129 | pub enum Error { | ||
| 130 | /// FIFO is empty, no conversion result available | ||
| 131 | FifoEmpty, | ||
| 132 | /// Invalid configuration | ||
| 133 | InvalidConfig, | ||
| 134 | /// Clock configuration error. | ||
| 135 | ClockSetup(ClockError), | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Result of an ADC conversion. | ||
| 139 | /// | ||
| 140 | /// Contains the conversion value and metadata about the conversion. | ||
| 116 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 141 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 117 | pub struct ConvResult { | 142 | pub struct ConvResult { |
| 118 | pub command_id_source: u32, | 143 | pub command_id_source: u8, |
| 119 | pub loop_count_index: u32, | 144 | pub loop_count_index: u8, |
| 120 | pub trigger_id_source: u32, | 145 | pub trigger_id_source: u8, |
| 121 | pub conv_value: u16, | 146 | pub conv_value: u16, |
| 122 | } | 147 | } |
| 123 | 148 | ||
| 124 | pub struct Adc<'a, I: Instance> { | 149 | /// ADC interrupt handler. |
| 125 | _inst: core::marker::PhantomData<&'a mut I>, | 150 | pub struct InterruptHandler<I: Instance> { |
| 151 | _phantom: PhantomData<I>, | ||
| 152 | } | ||
| 153 | |||
| 154 | /// ADC driver instance. | ||
| 155 | pub struct Adc<'a, I: Instance, M: ModeAdc> { | ||
| 156 | _inst: PhantomData<&'a mut I>, | ||
| 157 | _phantom: PhantomData<M>, | ||
| 158 | } | ||
| 159 | |||
| 160 | impl<'a, I: Instance> Adc<'a, I, Blocking> { | ||
| 161 | /// Create a new blocking instance of the ADC driver. | ||
| 162 | /// # Arguments | ||
| 163 | /// * `_inst` - ADC peripheral instance | ||
| 164 | /// * `pin` - GPIO pin to use for ADC | ||
| 165 | /// * `config` - ADC configuration | ||
| 166 | pub fn new_blocking(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin<I>>, config: LpadcConfig) -> Result<Self> { | ||
| 167 | Self::new_inner(_inst, pin, config) | ||
| 168 | } | ||
| 126 | } | 169 | } |
| 127 | 170 | ||
| 128 | impl<'a, I: Instance> Adc<'a, I> { | 171 | impl<'a, I: Instance> Adc<'a, I, Async> { |
| 129 | /// initialize ADC | 172 | /// Initialize ADC with interrupt support. |
| 130 | pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self { | 173 | /// |
| 131 | let adc = unsafe { &*I::ptr() }; | 174 | /// # Arguments |
| 175 | /// * `_inst` - ADC peripheral instance | ||
| 176 | /// * `pin` - GPIO pin to use for ADC | ||
| 177 | /// * `_irq` - Interrupt binding for this ADC instance | ||
| 178 | /// * `config` - ADC configuration | ||
| 179 | pub fn new_async( | ||
| 180 | _inst: Peri<'a, I>, | ||
| 181 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 182 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | ||
| 183 | config: LpadcConfig, | ||
| 184 | ) -> Result<Self> { | ||
| 185 | let adc = Self::new_inner(_inst, pin, config); | ||
| 186 | |||
| 187 | I::Interrupt::unpend(); | ||
| 188 | unsafe { I::Interrupt::enable() }; | ||
| 189 | |||
| 190 | adc | ||
| 191 | } | ||
| 132 | 192 | ||
| 133 | let _clock_freq = unsafe { | 193 | /// Read ADC value asynchronously. |
| 194 | /// | ||
| 195 | /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. | ||
| 196 | /// | ||
| 197 | /// The function: | ||
| 198 | /// 1. Enables the FIFO watermark interrupt | ||
| 199 | /// 2. Triggers a software conversion on trigger 0 | ||
| 200 | /// 3. Waits for the conversion to complete | ||
| 201 | /// 4. Returns the conversion result | ||
| 202 | /// | ||
| 203 | /// # Returns | ||
| 204 | /// 16-bit ADC conversion value | ||
| 205 | pub async fn read(&mut self) -> Result<u16> { | ||
| 206 | let wait = I::wait_cell().subscribe().await; | ||
| 207 | |||
| 208 | Adc::<'a, I, Async>::enable_interrupt(self, 0x1); | ||
| 209 | Adc::<'a, I, Async>::do_software_trigger(self, 1); | ||
| 210 | |||
| 211 | let _ = wait.await; | ||
| 212 | |||
| 213 | let result = Adc::<'a, I, Async>::get_conv_result(self).unwrap().conv_value >> G_LPADC_RESULT_SHIFT; | ||
| 214 | Ok(result) | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> { | ||
| 219 | /// Internal initialization function shared by `new_async` and `new_blocking`. | ||
| 220 | fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin<I>>, config: LpadcConfig) -> Result<Self> { | ||
| 221 | let adc = I::ptr(); | ||
| 222 | |||
| 223 | _ = unsafe { | ||
| 134 | enable_and_reset::<I>(&AdcConfig { | 224 | enable_and_reset::<I>(&AdcConfig { |
| 135 | power: config.power, | 225 | power: config.power, |
| 136 | source: config.source, | 226 | source: config.source, |
| 137 | div: config.div, | 227 | div: config.div, |
| 138 | }) | 228 | }) |
| 139 | .expect("Adc Init should not fail") | 229 | .map_err(Error::ClockSetup)? |
| 140 | }; | 230 | }; |
| 141 | 231 | ||
| 232 | pin.mux(); | ||
| 233 | |||
| 142 | /* Reset the module. */ | 234 | /* Reset the module. */ |
| 143 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); | 235 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); |
| 144 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); | 236 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); |
| @@ -220,18 +312,22 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 220 | // Enable ADC | 312 | // Enable ADC |
| 221 | adc.ctrl().modify(|_, w| w.adcen().enabled()); | 313 | adc.ctrl().modify(|_, w| w.adcen().enabled()); |
| 222 | 314 | ||
| 223 | Self { | 315 | Ok(Self { |
| 224 | _inst: core::marker::PhantomData, | 316 | _inst: PhantomData, |
| 225 | } | 317 | _phantom: PhantomData, |
| 318 | }) | ||
| 226 | } | 319 | } |
| 227 | 320 | ||
| 321 | /// Deinitialize the ADC peripheral. | ||
| 228 | pub fn deinit(&self) { | 322 | pub fn deinit(&self) { |
| 229 | let adc = unsafe { &*I::ptr() }; | 323 | let adc = I::ptr(); |
| 230 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | 324 | adc.ctrl().modify(|_, w| w.adcen().disabled()); |
| 231 | } | 325 | } |
| 232 | 326 | ||
| 327 | /// Perform offset calibration. | ||
| 328 | /// Waits for calibration to complete before returning. | ||
| 233 | pub fn do_offset_calibration(&self) { | 329 | pub fn do_offset_calibration(&self) { |
| 234 | let adc = unsafe { &*I::ptr() }; | 330 | let adc = I::ptr(); |
| 235 | // Enable calibration mode | 331 | // Enable calibration mode |
| 236 | adc.ctrl() | 332 | adc.ctrl() |
| 237 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); | 333 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); |
| @@ -240,6 +336,13 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 240 | while adc.stat().read().cal_rdy().is_not_set() {} | 336 | while adc.stat().read().cal_rdy().is_not_set() {} |
| 241 | } | 337 | } |
| 242 | 338 | ||
| 339 | /// Calculate gain conversion result from gain adjustment factor. | ||
| 340 | /// | ||
| 341 | /// # Arguments | ||
| 342 | /// * `gain_adjustment` - Gain adjustment factor | ||
| 343 | /// | ||
| 344 | /// # Returns | ||
| 345 | /// Gain calibration register value | ||
| 243 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { | 346 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { |
| 244 | let mut gcra_array = [0u32; 17]; | 347 | let mut gcra_array = [0u32; 17]; |
| 245 | let mut gcalr: u32 = 0; | 348 | let mut gcalr: u32 = 0; |
| @@ -258,8 +361,9 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 258 | gcalr | 361 | gcalr |
| 259 | } | 362 | } |
| 260 | 363 | ||
| 364 | /// Perform automatic gain calibration. | ||
| 261 | pub fn do_auto_calibration(&self) { | 365 | pub fn do_auto_calibration(&self) { |
| 262 | let adc = unsafe { &*I::ptr() }; | 366 | let adc = I::ptr(); |
| 263 | adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); | 367 | adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending()); |
| 264 | 368 | ||
| 265 | while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} | 369 | while adc.gcc0().read().rdy().is_gain_cal_not_valid() {} |
| @@ -280,14 +384,23 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 280 | while adc.stat().read().cal_rdy().is_not_set() {} | 384 | while adc.stat().read().cal_rdy().is_not_set() {} |
| 281 | } | 385 | } |
| 282 | 386 | ||
| 387 | /// Trigger ADC conversion(s) via software. | ||
| 388 | /// | ||
| 389 | /// Initiates conversion(s) for the trigger(s) specified in the bitmask. | ||
| 390 | /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). | ||
| 391 | /// | ||
| 392 | /// # Arguments | ||
| 393 | /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) | ||
| 283 | pub fn do_software_trigger(&self, trigger_id_mask: u32) { | 394 | pub fn do_software_trigger(&self, trigger_id_mask: u32) { |
| 284 | let adc = unsafe { &*I::ptr() }; | 395 | let adc = I::ptr(); |
| 285 | adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); | 396 | adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) }); |
| 286 | } | 397 | } |
| 287 | 398 | ||
| 399 | /// Get default conversion command configuration. | ||
| 400 | /// # Returns | ||
| 401 | /// Default conversion command configuration | ||
| 288 | pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { | 402 | pub fn get_default_conv_command_config(&self) -> ConvCommandConfig { |
| 289 | ConvCommandConfig { | 403 | ConvCommandConfig { |
| 290 | sample_channel_mode: Ctype::SingleEndedASideChannel, | ||
| 291 | channel_number: Adch::SelectCh0, | 404 | channel_number: Adch::SelectCh0, |
| 292 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | 405 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, |
| 293 | enable_auto_channel_increment: false, | 406 | enable_auto_channel_increment: false, |
| @@ -302,42 +415,72 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 302 | } | 415 | } |
| 303 | } | 416 | } |
| 304 | 417 | ||
| 305 | //TBD Need to add cmdlx and cmdhx with x {2..7} | 418 | /// Set conversion command configuration. |
| 306 | pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) { | 419 | /// |
| 307 | let adc = unsafe { &*I::ptr() }; | 420 | /// Configures a conversion command slot with the specified parameters. |
| 421 | /// Commands define how conversions are performed (channel, resolution, etc.). | ||
| 422 | /// | ||
| 423 | /// # Arguments | ||
| 424 | /// * `index` - Command index | ||
| 425 | /// * `config` - Command configuration | ||
| 426 | /// | ||
| 427 | /// # Returns | ||
| 428 | /// * `Ok(())` if the command was configured successfully | ||
| 429 | /// * `Err(Error::InvalidConfig)` if the index is out of range | ||
| 430 | pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) -> Result<()> { | ||
| 431 | let adc = I::ptr(); | ||
| 432 | |||
| 433 | if index < 1 || index > 7 { | ||
| 434 | return Err(Error::InvalidConfig); | ||
| 435 | } | ||
| 436 | |||
| 437 | macro_rules! write_cmd { | ||
| 438 | ($idx:expr) => {{ | ||
| 439 | paste! { | ||
| 440 | adc.[<cmdl $idx>]().write(|w| { | ||
| 441 | w.adch() | ||
| 442 | .variant(config.channel_number) | ||
| 443 | .mode() | ||
| 444 | .variant(config.conversion_resolution_mode) | ||
| 445 | }); | ||
| 446 | adc.[<cmdh $idx>]().write(|w| unsafe { | ||
| 447 | w.next() | ||
| 448 | .variant(config.chained_next_command_number) | ||
| 449 | .loop_() | ||
| 450 | .bits(config.loop_count) | ||
| 451 | .avgs() | ||
| 452 | .variant(config.hardware_average_mode) | ||
| 453 | .sts() | ||
| 454 | .variant(config.sample_time_mode) | ||
| 455 | .cmpen() | ||
| 456 | .variant(config.hardware_compare_mode) | ||
| 457 | .wait_trig() | ||
| 458 | .bit(config.enable_wait_trigger) | ||
| 459 | .lwi() | ||
| 460 | .bit(config.enable_auto_channel_increment) | ||
| 461 | }); | ||
| 462 | } | ||
| 463 | }}; | ||
| 464 | } | ||
| 308 | 465 | ||
| 309 | match index { | 466 | match index { |
| 310 | 1 => { | 467 | 1 => write_cmd!(1), |
| 311 | adc.cmdl1().write(|w| { | 468 | 2 => write_cmd!(2), |
| 312 | w.adch() | 469 | 3 => write_cmd!(3), |
| 313 | .variant(config.channel_number) | 470 | 4 => write_cmd!(4), |
| 314 | .mode() | 471 | 5 => write_cmd!(5), |
| 315 | .variant(config.conversion_resolution_mode) | 472 | 6 => write_cmd!(6), |
| 316 | }); | 473 | 7 => write_cmd!(7), |
| 317 | adc.cmdh1().write(|w| unsafe { | 474 | _ => unreachable!(), |
| 318 | w.next() | ||
| 319 | .variant(config.chained_next_command_number) | ||
| 320 | .loop_() | ||
| 321 | .bits(config.loop_count) | ||
| 322 | .avgs() | ||
| 323 | .variant(config.hardware_average_mode) | ||
| 324 | .sts() | ||
| 325 | .variant(config.sample_time_mode) | ||
| 326 | .cmpen() | ||
| 327 | .variant(config.hardware_compare_mode); | ||
| 328 | if config.enable_wait_trigger { | ||
| 329 | w.wait_trig().enabled(); | ||
| 330 | } | ||
| 331 | if config.enable_auto_channel_increment { | ||
| 332 | w.lwi().enabled(); | ||
| 333 | } | ||
| 334 | w | ||
| 335 | }); | ||
| 336 | } | ||
| 337 | _ => panic!("Invalid command index: must be between 1 and 7"), | ||
| 338 | } | 475 | } |
| 476 | |||
| 477 | Ok(()) | ||
| 339 | } | 478 | } |
| 340 | 479 | ||
| 480 | /// Get default conversion trigger configuration. | ||
| 481 | /// | ||
| 482 | /// # Returns | ||
| 483 | /// Default conversion trigger configuration | ||
| 341 | pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { | 484 | pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig { |
| 342 | ConvTriggerConfig { | 485 | ConvTriggerConfig { |
| 343 | target_command_id: Tcmd::NotValid, | 486 | target_command_id: Tcmd::NotValid, |
| @@ -347,8 +490,16 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 347 | } | 490 | } |
| 348 | } | 491 | } |
| 349 | 492 | ||
| 493 | /// Set conversion trigger configuration. | ||
| 494 | /// | ||
| 495 | /// Configures a trigger to initiate conversions. Triggers can be | ||
| 496 | /// activated by software or hardware signals. | ||
| 497 | /// | ||
| 498 | /// # Arguments | ||
| 499 | /// * `trigger_id` - Trigger index (0-15) | ||
| 500 | /// * `config` - Trigger configuration | ||
| 350 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { | 501 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) { |
| 351 | let adc = unsafe { &*I::ptr() }; | 502 | let adc = I::ptr(); |
| 352 | let tctrl = &adc.tctrl(trigger_id); | 503 | let tctrl = &adc.tctrl(trigger_id); |
| 353 | 504 | ||
| 354 | tctrl.write(|w| unsafe { | 505 | tctrl.write(|w| unsafe { |
| @@ -363,47 +514,239 @@ impl<'a, I: Instance> Adc<'a, I> { | |||
| 363 | }); | 514 | }); |
| 364 | } | 515 | } |
| 365 | 516 | ||
| 517 | /// Reset the FIFO buffer. | ||
| 518 | /// | ||
| 519 | /// Clears all pending conversion results from the FIFO. | ||
| 366 | pub fn do_reset_fifo(&self) { | 520 | pub fn do_reset_fifo(&self) { |
| 367 | let adc = unsafe { &*I::ptr() }; | 521 | let adc = I::ptr(); |
| 368 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | 522 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); |
| 369 | } | 523 | } |
| 370 | 524 | ||
| 525 | /// Enable ADC interrupts. | ||
| 526 | /// | ||
| 527 | /// Enables the interrupt sources specified in the bitmask. | ||
| 528 | /// | ||
| 529 | /// # Arguments | ||
| 530 | /// * `mask` - Bitmask of interrupt sources to enable | ||
| 371 | pub fn enable_interrupt(&self, mask: u32) { | 531 | pub fn enable_interrupt(&self, mask: u32) { |
| 372 | let adc = unsafe { &*I::ptr() }; | 532 | let adc = I::ptr(); |
| 373 | adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | 533 | adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); |
| 374 | INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst); | ||
| 375 | } | 534 | } |
| 376 | 535 | ||
| 377 | pub fn is_interrupt_triggered(&self) -> bool { | 536 | /// Disable ADC interrupts. |
| 378 | INTERRUPT_TRIGGERED.load(Ordering::Relaxed) | 537 | /// |
| 538 | /// Disables the interrupt sources specified in the bitmask. | ||
| 539 | /// | ||
| 540 | /// # Arguments | ||
| 541 | /// * `mask` - Bitmask of interrupt sources to disable | ||
| 542 | pub fn disable_interrupt(&self, mask: u32) { | ||
| 543 | let adc = I::ptr(); | ||
| 544 | adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); | ||
| 545 | } | ||
| 546 | |||
| 547 | /// Get conversion result from FIFO. | ||
| 548 | /// | ||
| 549 | /// Reads and returns the next conversion result from the FIFO. | ||
| 550 | /// Returns `None` if the FIFO is empty. | ||
| 551 | /// | ||
| 552 | /// # Returns | ||
| 553 | /// - `Some(ConvResult)` if a result is available | ||
| 554 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 555 | pub fn get_conv_result(&self) -> Result<ConvResult> { | ||
| 556 | let adc = I::ptr(); | ||
| 557 | let fifo = adc.resfifo0().read(); | ||
| 558 | if !fifo.valid().is_valid() { | ||
| 559 | return Err(Error::FifoEmpty); | ||
| 560 | } | ||
| 561 | |||
| 562 | Ok(ConvResult { | ||
| 563 | command_id_source: fifo.cmdsrc().bits(), | ||
| 564 | loop_count_index: fifo.loopcnt().bits(), | ||
| 565 | trigger_id_source: fifo.tsrc().bits(), | ||
| 566 | conv_value: fifo.d().bits(), | ||
| 567 | }) | ||
| 379 | } | 568 | } |
| 380 | } | 569 | } |
| 381 | 570 | ||
| 382 | pub fn get_conv_result() -> Option<ConvResult> { | 571 | impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> { |
| 383 | let adc = unsafe { &*pac::Adc1::ptr() }; | 572 | unsafe fn on_interrupt() { |
| 384 | let fifo = adc.resfifo0().read().bits(); | 573 | T::ptr().ie().modify(|r, w| w.bits(r.bits() & !0x1)); |
| 385 | const VALID_MASK: u32 = 1 << 31; | 574 | T::wait_cell().wake(); |
| 386 | if fifo & VALID_MASK == 0 { | ||
| 387 | return None; | ||
| 388 | } | 575 | } |
| 576 | } | ||
| 389 | 577 | ||
| 390 | Some(ConvResult { | 578 | mod sealed { |
| 391 | command_id_source: (fifo >> 24) & 0x0F, | 579 | /// Seal a trait |
| 392 | loop_count_index: (fifo >> 20) & 0x0F, | 580 | pub trait Sealed {} |
| 393 | trigger_id_source: (fifo >> 16) & 0x0F, | ||
| 394 | conv_value: (fifo & 0xFFFF) as u16, | ||
| 395 | }) | ||
| 396 | } | 581 | } |
| 397 | 582 | ||
| 398 | pub fn on_interrupt() { | 583 | impl<I: GpioPin> sealed::Sealed for I {} |
| 399 | if get_conv_result().is_some() { | 584 | |
| 400 | INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst); | 585 | trait SealedInstance { |
| 401 | } | 586 | fn ptr() -> &'static pac::adc0::RegisterBlock; |
| 587 | fn wait_cell() -> &'static WaitCell; | ||
| 402 | } | 588 | } |
| 403 | 589 | ||
| 404 | pub struct AdcHandler; | 590 | /// ADC Instance |
| 405 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler { | 591 | #[allow(private_bounds)] |
| 406 | unsafe fn on_interrupt() { | 592 | pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> { |
| 407 | on_interrupt(); | 593 | /// Interrupt for this ADC instance. |
| 408 | } | 594 | type Interrupt: Interrupt; |
| 409 | } | 595 | } |
| 596 | |||
| 597 | macro_rules! impl_instance { | ||
| 598 | ($($n:expr),*) => { | ||
| 599 | $( | ||
| 600 | paste!{ | ||
| 601 | impl SealedInstance for crate::peripherals::[<ADC $n>] { | ||
| 602 | fn ptr() -> &'static pac::adc0::RegisterBlock { | ||
| 603 | unsafe { &*pac::[<Adc $n>]::ptr() } | ||
| 604 | } | ||
| 605 | |||
| 606 | fn wait_cell() -> &'static WaitCell { | ||
| 607 | static WAIT_CELL: WaitCell = WaitCell::new(); | ||
| 608 | &WAIT_CELL | ||
| 609 | } | ||
| 610 | |||
| 611 | } | ||
| 612 | |||
| 613 | impl Instance for crate::peripherals::[<ADC $n>] { | ||
| 614 | type Interrupt = crate::interrupt::typelevel::[<ADC $n>]; | ||
| 615 | } | ||
| 616 | } | ||
| 617 | )* | ||
| 618 | }; | ||
| 619 | } | ||
| 620 | |||
| 621 | impl_instance!(0, 1, 2, 3); | ||
| 622 | |||
| 623 | pub trait AdcPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 624 | /// Set the given pin to the correct muxing state | ||
| 625 | fn mux(&self); | ||
| 626 | } | ||
| 627 | |||
| 628 | /// Driver mode. | ||
| 629 | #[allow(private_bounds)] | ||
| 630 | pub trait ModeAdc: sealed::Sealed {} | ||
| 631 | |||
| 632 | /// Blocking mode. | ||
| 633 | pub struct Blocking; | ||
| 634 | impl sealed::Sealed for Blocking {} | ||
| 635 | impl ModeAdc for Blocking {} | ||
| 636 | |||
| 637 | /// Async mode. | ||
| 638 | pub struct Async; | ||
| 639 | impl sealed::Sealed for Async {} | ||
| 640 | impl ModeAdc for Async {} | ||
| 641 | |||
| 642 | macro_rules! impl_pin { | ||
| 643 | ($pin:ident, $peri:ident, $func:ident, $trait:ident) => { | ||
| 644 | impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin { | ||
| 645 | fn mux(&self) { | ||
| 646 | self.set_pull(crate::gpio::Pull::Disabled); | ||
| 647 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 648 | self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); | ||
| 649 | self.set_function(crate::pac::port0::pcr0::Mux::$func); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | }; | ||
| 653 | } | ||
| 654 | |||
| 655 | impl_pin!(P2_0, ADC0, Mux0, AdcPin); | ||
| 656 | impl_pin!(P2_4, ADC0, Mux0, AdcPin); | ||
| 657 | impl_pin!(P2_15, ADC0, Mux0, AdcPin); | ||
| 658 | impl_pin!(P2_3, ADC0, Mux0, AdcPin); | ||
| 659 | impl_pin!(P2_2, ADC0, Mux0, AdcPin); | ||
| 660 | impl_pin!(P2_12, ADC0, Mux0, AdcPin); | ||
| 661 | impl_pin!(P2_16, ADC0, Mux0, AdcPin); | ||
| 662 | impl_pin!(P2_7, ADC0, Mux0, AdcPin); | ||
| 663 | impl_pin!(P0_18, ADC0, Mux0, AdcPin); | ||
| 664 | impl_pin!(P0_19, ADC0, Mux0, AdcPin); | ||
| 665 | impl_pin!(P0_20, ADC0, Mux0, AdcPin); | ||
| 666 | impl_pin!(P0_21, ADC0, Mux0, AdcPin); | ||
| 667 | impl_pin!(P0_22, ADC0, Mux0, AdcPin); | ||
| 668 | impl_pin!(P0_23, ADC0, Mux0, AdcPin); | ||
| 669 | impl_pin!(P0_3, ADC0, Mux0, AdcPin); | ||
| 670 | impl_pin!(P0_6, ADC0, Mux0, AdcPin); | ||
| 671 | impl_pin!(P1_0, ADC0, Mux0, AdcPin); | ||
| 672 | impl_pin!(P1_1, ADC0, Mux0, AdcPin); | ||
| 673 | impl_pin!(P1_2, ADC0, Mux0, AdcPin); | ||
| 674 | impl_pin!(P1_3, ADC0, Mux0, AdcPin); | ||
| 675 | impl_pin!(P1_4, ADC0, Mux0, AdcPin); | ||
| 676 | impl_pin!(P1_5, ADC0, Mux0, AdcPin); | ||
| 677 | impl_pin!(P1_6, ADC0, Mux0, AdcPin); | ||
| 678 | impl_pin!(P1_7, ADC0, Mux0, AdcPin); | ||
| 679 | impl_pin!(P1_10, ADC0, Mux0, AdcPin); | ||
| 680 | |||
| 681 | impl_pin!(P2_1, ADC1, Mux0, AdcPin); | ||
| 682 | impl_pin!(P2_5, ADC1, Mux0, AdcPin); | ||
| 683 | impl_pin!(P2_19, ADC1, Mux0, AdcPin); | ||
| 684 | impl_pin!(P2_6, ADC1, Mux0, AdcPin); | ||
| 685 | impl_pin!(P2_3, ADC1, Mux0, AdcPin); | ||
| 686 | impl_pin!(P2_13, ADC1, Mux0, AdcPin); | ||
| 687 | impl_pin!(P2_17, ADC1, Mux0, AdcPin); | ||
| 688 | impl_pin!(P2_7, ADC1, Mux0, AdcPin); | ||
| 689 | impl_pin!(P1_10, ADC1, Mux0, AdcPin); | ||
| 690 | impl_pin!(P1_11, ADC1, Mux0, AdcPin); | ||
| 691 | impl_pin!(P1_12, ADC1, Mux0, AdcPin); | ||
| 692 | impl_pin!(P1_13, ADC1, Mux0, AdcPin); | ||
| 693 | impl_pin!(P1_14, ADC1, Mux0, AdcPin); | ||
| 694 | impl_pin!(P1_15, ADC1, Mux0, AdcPin); | ||
| 695 | impl_pin!(P1_16, ADC1, Mux0, AdcPin); | ||
| 696 | impl_pin!(P1_17, ADC1, Mux0, AdcPin); | ||
| 697 | impl_pin!(P1_18, ADC1, Mux0, AdcPin); | ||
| 698 | impl_pin!(P1_19, ADC1, Mux0, AdcPin); | ||
| 699 | impl_pin!(P3_31, ADC1, Mux0, AdcPin); | ||
| 700 | impl_pin!(P3_30, ADC1, Mux0, AdcPin); | ||
| 701 | impl_pin!(P3_29, ADC1, Mux0, AdcPin); | ||
| 702 | |||
| 703 | impl_pin!(P2_4, ADC2, Mux0, AdcPin); | ||
| 704 | impl_pin!(P2_10, ADC2, Mux0, AdcPin); | ||
| 705 | impl_pin!(P4_4, ADC2, Mux0, AdcPin); | ||
| 706 | impl_pin!(P2_24, ADC2, Mux0, AdcPin); | ||
| 707 | impl_pin!(P2_16, ADC2, Mux0, AdcPin); | ||
| 708 | impl_pin!(P2_12, ADC2, Mux0, AdcPin); | ||
| 709 | impl_pin!(P2_20, ADC2, Mux0, AdcPin); | ||
| 710 | impl_pin!(P2_7, ADC2, Mux0, AdcPin); | ||
| 711 | impl_pin!(P0_2, ADC2, Mux0, AdcPin); | ||
| 712 | impl_pin!(P0_4, ADC2, Mux0, AdcPin); | ||
| 713 | impl_pin!(P0_5, ADC2, Mux0, AdcPin); | ||
| 714 | impl_pin!(P0_6, ADC2, Mux0, AdcPin); | ||
| 715 | impl_pin!(P0_7, ADC2, Mux0, AdcPin); | ||
| 716 | impl_pin!(P0_12, ADC2, Mux0, AdcPin); | ||
| 717 | impl_pin!(P0_13, ADC2, Mux0, AdcPin); | ||
| 718 | impl_pin!(P0_14, ADC2, Mux0, AdcPin); | ||
| 719 | impl_pin!(P0_15, ADC2, Mux0, AdcPin); | ||
| 720 | impl_pin!(P4_0, ADC2, Mux0, AdcPin); | ||
| 721 | impl_pin!(P4_1, ADC2, Mux0, AdcPin); | ||
| 722 | impl_pin!(P4_2, ADC2, Mux0, AdcPin); | ||
| 723 | impl_pin!(P4_3, ADC2, Mux0, AdcPin); | ||
| 724 | //impl_pin!(P4_4, ADC2, Mux0, AdcPin); // Conflit with ADC2_A3 and ADC2_A20 using the same pin | ||
| 725 | impl_pin!(P4_5, ADC2, Mux0, AdcPin); | ||
| 726 | impl_pin!(P4_6, ADC2, Mux0, AdcPin); | ||
| 727 | impl_pin!(P4_7, ADC2, Mux0, AdcPin); | ||
| 728 | |||
| 729 | impl_pin!(P2_5, ADC3, Mux0, AdcPin); | ||
| 730 | impl_pin!(P2_11, ADC3, Mux0, AdcPin); | ||
| 731 | impl_pin!(P2_23, ADC3, Mux0, AdcPin); | ||
| 732 | impl_pin!(P2_25, ADC3, Mux0, AdcPin); | ||
| 733 | impl_pin!(P2_17, ADC3, Mux0, AdcPin); | ||
| 734 | impl_pin!(P2_13, ADC3, Mux0, AdcPin); | ||
| 735 | impl_pin!(P2_21, ADC3, Mux0, AdcPin); | ||
| 736 | impl_pin!(P2_7, ADC3, Mux0, AdcPin); | ||
| 737 | impl_pin!(P3_2, ADC3, Mux0, AdcPin); | ||
| 738 | impl_pin!(P3_3, ADC3, Mux0, AdcPin); | ||
| 739 | impl_pin!(P3_4, ADC3, Mux0, AdcPin); | ||
| 740 | impl_pin!(P3_5, ADC3, Mux0, AdcPin); | ||
| 741 | impl_pin!(P3_6, ADC3, Mux0, AdcPin); | ||
| 742 | impl_pin!(P3_7, ADC3, Mux0, AdcPin); | ||
| 743 | impl_pin!(P3_12, ADC3, Mux0, AdcPin); | ||
| 744 | impl_pin!(P3_13, ADC3, Mux0, AdcPin); | ||
| 745 | impl_pin!(P3_14, ADC3, Mux0, AdcPin); | ||
| 746 | impl_pin!(P3_15, ADC3, Mux0, AdcPin); | ||
| 747 | impl_pin!(P3_20, ADC3, Mux0, AdcPin); | ||
| 748 | impl_pin!(P3_21, ADC3, Mux0, AdcPin); | ||
| 749 | impl_pin!(P3_22, ADC3, Mux0, AdcPin); | ||
| 750 | impl_pin!(P3_23, ADC3, Mux0, AdcPin); | ||
| 751 | impl_pin!(P3_24, ADC3, Mux0, AdcPin); | ||
| 752 | impl_pin!(P3_25, ADC3, Mux0, AdcPin); | ||
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 014a12519..667d79536 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs | |||
| @@ -945,7 +945,10 @@ pub(crate) mod gate { | |||
| 945 | impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); | 945 | impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); |
| 946 | impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); | 946 | impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); |
| 947 | impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); | 947 | impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); |
| 948 | impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig); | ||
| 948 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); | 949 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); |
| 950 | impl_cc_gate!(ADC2, mrcc_glb_cc1, mrcc_glb_rst1, adc2, AdcConfig); | ||
| 951 | impl_cc_gate!(ADC3, mrcc_glb_cc1, mrcc_glb_rst1, adc3, AdcConfig); | ||
| 949 | 952 | ||
| 950 | // DMA0 peripheral - uses NoConfig since it has no selectable clock source | 953 | // DMA0 peripheral - uses NoConfig since it has no selectable clock source |
| 951 | impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig); | 954 | impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig); |
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs index fed5e558e..f2f51c60c 100644 --- a/embassy-mcxa/src/clocks/periph_helpers.rs +++ b/embassy-mcxa/src/clocks/periph_helpers.rs | |||
| @@ -427,6 +427,7 @@ impl SPConfHelper for OsTimerConfig { | |||
| 427 | 427 | ||
| 428 | /// Selectable clocks for the ADC peripheral | 428 | /// Selectable clocks for the ADC peripheral |
| 429 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 429 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| 430 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 430 | pub enum AdcClockSel { | 431 | pub enum AdcClockSel { |
| 431 | /// Divided `fro_lf`/`clk_12m`/FRO12M source | 432 | /// Divided `fro_lf`/`clk_12m`/FRO12M source |
| 432 | FroLfDiv, | 433 | FroLfDiv, |
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs index c960af7a2..725f8d499 100644 --- a/embassy-mcxa/src/interrupt.rs +++ b/embassy-mcxa/src/interrupt.rs | |||
| @@ -9,7 +9,10 @@ | |||
| 9 | mod generated { | 9 | mod generated { |
| 10 | #[rustfmt::skip] | 10 | #[rustfmt::skip] |
| 11 | embassy_hal_internal::interrupt_mod!( | 11 | embassy_hal_internal::interrupt_mod!( |
| 12 | ADC0, | ||
| 12 | ADC1, | 13 | ADC1, |
| 14 | ADC2, | ||
| 15 | ADC3, | ||
| 13 | DMA_CH0, | 16 | DMA_CH0, |
| 14 | DMA_CH1, | 17 | DMA_CH1, |
| 15 | DMA_CH2, | 18 | DMA_CH2, |
| @@ -280,44 +283,6 @@ impl InterruptExt for Rtc { | |||
| 280 | } | 283 | } |
| 281 | } | 284 | } |
| 282 | 285 | ||
| 283 | pub struct Adc; | ||
| 284 | pub const ADC1: Adc = Adc; | ||
| 285 | |||
| 286 | impl InterruptExt for Adc { | ||
| 287 | /// Clear any pending ADC1 in NVIC. | ||
| 288 | #[inline] | ||
| 289 | fn unpend(&self) { | ||
| 290 | cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1); | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Set NVIC priority for ADC1. | ||
| 294 | #[inline] | ||
| 295 | fn set_priority(&self, priority: Priority) { | ||
| 296 | unsafe { | ||
| 297 | let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC; | ||
| 298 | nvic.set_priority(Interrupt::ADC1, u8::from(priority)); | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Enable ADC1 in NVIC. | ||
| 303 | #[inline] | ||
| 304 | unsafe fn enable(&self) { | ||
| 305 | cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1); | ||
| 306 | } | ||
| 307 | |||
| 308 | /// Disable ADC1 in NVIC. | ||
| 309 | #[inline] | ||
| 310 | unsafe fn disable(&self) { | ||
| 311 | cortex_m::peripheral::NVIC::mask(Interrupt::ADC1); | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Check if ADC1 is pending in NVIC. | ||
| 315 | #[inline] | ||
| 316 | fn is_pending(&self) -> bool { | ||
| 317 | cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1) | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | pub struct Gpio0; | 286 | pub struct Gpio0; |
| 322 | pub const GPIO0: Gpio0 = Gpio0; | 287 | pub const GPIO0: Gpio0 = Gpio0; |
| 323 | 288 | ||
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index 1bbdffa06..be279e509 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | pub mod clocks; // still provide clock helpers | 8 | pub mod clocks; // still provide clock helpers |
| 9 | pub mod dma; | 9 | pub mod dma; |
| 10 | pub mod gpio; | 10 | pub mod gpio; |
| 11 | pub mod pins; // pin mux helpers | ||
| 12 | 11 | ||
| 13 | pub mod adc; | 12 | pub mod adc; |
| 14 | pub mod clkout; | 13 | pub mod clkout; |
| @@ -26,6 +25,8 @@ pub use crate::pac::NVIC_PRIO_BITS; | |||
| 26 | embassy_hal_internal::peripherals!( | 25 | embassy_hal_internal::peripherals!( |
| 27 | ADC0, | 26 | ADC0, |
| 28 | ADC1, | 27 | ADC1, |
| 28 | ADC2, | ||
| 29 | ADC3, | ||
| 29 | 30 | ||
| 30 | AOI0, | 31 | AOI0, |
| 31 | AOI1, | 32 | AOI1, |
| @@ -336,7 +337,6 @@ embassy_hal_internal::peripherals!( | |||
| 336 | // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. | 337 | // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. |
| 337 | 338 | ||
| 338 | // Re-export interrupt traits and types | 339 | // Re-export interrupt traits and types |
| 339 | pub use adc::Adc1 as Adc1Token; | ||
| 340 | pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; | 340 | pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; |
| 341 | pub use interrupt::InterruptExt; | 341 | pub use interrupt::InterruptExt; |
| 342 | #[cfg(feature = "unstable-pac")] | 342 | #[cfg(feature = "unstable-pac")] |
| @@ -354,8 +354,6 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { | |||
| 354 | // Apply user-configured priority early; enabling is left to examples/apps | 354 | // Apply user-configured priority early; enabling is left to examples/apps |
| 355 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); | 355 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); |
| 356 | // Apply user-configured priority early; enabling is left to examples/apps | 356 | // Apply user-configured priority early; enabling is left to examples/apps |
| 357 | crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); | ||
| 358 | // Apply user-configured priority early; enabling is left to examples/apps | ||
| 359 | crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); | 357 | crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority); |
| 360 | // Apply user-configured priority early; enabling is left to examples/apps | 358 | // Apply user-configured priority early; enabling is left to examples/apps |
| 361 | crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); | 359 | crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority); |
diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs deleted file mode 100644 index 9adbe64c8..000000000 --- a/embassy-mcxa/src/pins.rs +++ /dev/null | |||
| @@ -1,33 +0,0 @@ | |||
| 1 | //! Pin configuration helpers (separate from peripheral drivers). | ||
| 2 | use crate::pac; | ||
| 3 | |||
| 4 | /// Configure pins for ADC usage. | ||
| 5 | /// | ||
| 6 | /// # Safety | ||
| 7 | /// | ||
| 8 | /// Must be called after PORT clocks are enabled. | ||
| 9 | pub unsafe fn configure_adc_pins() { | ||
| 10 | // P1_10 = ADC1_A8 | ||
| 11 | let port1 = &*pac::Port1::ptr(); | ||
| 12 | port1.pcr10().write(|w| { | ||
| 13 | w.ps() | ||
| 14 | .ps0() | ||
| 15 | .pe() | ||
| 16 | .pe0() | ||
| 17 | .sre() | ||
| 18 | .sre0() | ||
| 19 | .ode() | ||
| 20 | .ode0() | ||
| 21 | .dse() | ||
| 22 | .dse0() | ||
| 23 | .mux() | ||
| 24 | .mux0() | ||
| 25 | .ibe() | ||
| 26 | .ibe0() | ||
| 27 | .inv() | ||
| 28 | .inv0() | ||
| 29 | .lk() | ||
| 30 | .lk0() | ||
| 31 | }); | ||
| 32 | core::arch::asm!("dsb sy; isb sy"); | ||
| 33 | } | ||
diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index c88b1fe8d..5876923a1 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs | |||
| @@ -2,34 +2,31 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::init_adc_pins; | 5 | use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; |
| 6 | use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; | 6 | use hal::bind_interrupts; |
| 7 | use hal::clocks::PoweredClock; | 7 | use hal::clocks::PoweredClock; |
| 8 | use hal::clocks::config::Div8; | ||
| 8 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | 9 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; |
| 10 | use hal::config::Config; | ||
| 9 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | 11 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; |
| 10 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | 12 | use hal::pac::adc1::cmdl1::{Adch, Mode}; |
| 11 | use hal::pac::adc1::ctrl::CalAvgs; | 13 | use hal::pac::adc1::ctrl::CalAvgs; |
| 12 | use hal::pac::adc1::tctrl::Tcmd; | 14 | use hal::pac::adc1::tctrl::Tcmd; |
| 13 | use hal::{InterruptExt, bind_interrupts}; | 15 | use hal::peripherals::ADC1; |
| 14 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 15 | 17 | ||
| 16 | bind_interrupts!(struct Irqs { | 18 | bind_interrupts!(struct Irqs { |
| 17 | ADC1 => hal::adc::AdcHandler; | 19 | ADC1 => InterruptHandler<ADC1>; |
| 18 | }); | 20 | }); |
| 19 | 21 | ||
| 20 | #[used] | ||
| 21 | #[no_mangle] | ||
| 22 | static KEEP_ADC: unsafe extern "C" fn() = ADC1; | ||
| 23 | |||
| 24 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 25 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 26 | let p = hal::init(hal::config::Config::default()); | 24 | let mut config = Config::default(); |
| 25 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 27 | 26 | ||
| 28 | defmt::info!("ADC interrupt Example"); | 27 | let p = hal::init(config); |
| 29 | 28 | ||
| 30 | unsafe { | 29 | defmt::info!("ADC interrupt Example"); |
| 31 | init_adc_pins(); | ||
| 32 | } | ||
| 33 | 30 | ||
| 34 | let adc_config = LpadcConfig { | 31 | let adc_config = LpadcConfig { |
| 35 | enable_in_doze_mode: true, | 32 | enable_in_doze_mode: true, |
| @@ -46,7 +43,7 @@ async fn main(_spawner: Spawner) { | |||
| 46 | source: AdcClockSel::FroLfDiv, | 43 | source: AdcClockSel::FroLfDiv, |
| 47 | div: Div4::no_div(), | 44 | div: Div4::no_div(), |
| 48 | }; | 45 | }; |
| 49 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); | 46 | let mut adc = Adc::new_async(p.ADC1, p.P1_10, Irqs, adc_config).unwrap(); |
| 50 | 47 | ||
| 51 | adc.do_offset_calibration(); | 48 | adc.do_offset_calibration(); |
| 52 | adc.do_auto_calibration(); | 49 | adc.do_auto_calibration(); |
| @@ -54,7 +51,7 @@ async fn main(_spawner: Spawner) { | |||
| 54 | let mut conv_command_config = adc.get_default_conv_command_config(); | 51 | let mut conv_command_config = adc.get_default_conv_command_config(); |
| 55 | conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; | 52 | conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; |
| 56 | conv_command_config.conversion_resolution_mode = Mode::Data16Bits; | 53 | conv_command_config.conversion_resolution_mode = Mode::Data16Bits; |
| 57 | adc.set_conv_command_config(1, &conv_command_config); | 54 | adc.set_conv_command_config(1, &conv_command_config).unwrap(); |
| 58 | 55 | ||
| 59 | let mut conv_trigger_config = adc.get_default_conv_trigger_config(); | 56 | let mut conv_trigger_config = adc.get_default_conv_trigger_config(); |
| 60 | conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; | 57 | conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; |
| @@ -63,22 +60,14 @@ async fn main(_spawner: Spawner) { | |||
| 63 | 60 | ||
| 64 | defmt::info!("ADC configuration done..."); | 61 | defmt::info!("ADC configuration done..."); |
| 65 | 62 | ||
| 66 | adc.enable_interrupt(0x1); | ||
| 67 | |||
| 68 | unsafe { | ||
| 69 | hal::interrupt::ADC1.enable(); | ||
| 70 | } | ||
| 71 | |||
| 72 | unsafe { | ||
| 73 | cortex_m::interrupt::enable(); | ||
| 74 | } | ||
| 75 | |||
| 76 | loop { | 63 | loop { |
| 77 | adc.do_software_trigger(1); | 64 | match adc.read().await { |
| 78 | while !adc.is_interrupt_triggered() { | 65 | Ok(value) => { |
| 79 | // Wait until the interrupt is triggered | 66 | defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value); |
| 67 | } | ||
| 68 | Err(e) => { | ||
| 69 | defmt::error!("ADC read error: {:?}", e); | ||
| 70 | } | ||
| 80 | } | 71 | } |
| 81 | defmt::info!("*** ADC interrupt TRIGGERED! ***"); | ||
| 82 | //TBD need to print the value | ||
| 83 | } | 72 | } |
| 84 | } | 73 | } |
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 07c50f224..d048bb56f 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs | |||
| @@ -2,10 +2,11 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::init_adc_pins; | 5 | use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; |
| 6 | use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; | ||
| 7 | use hal::clocks::PoweredClock; | 6 | use hal::clocks::PoweredClock; |
| 7 | use hal::clocks::config::Div8; | ||
| 8 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | 8 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; |
| 9 | use hal::config::Config; | ||
| 9 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | 10 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; |
| 10 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | 11 | use hal::pac::adc1::cmdl1::{Adch, Mode}; |
| 11 | use hal::pac::adc1::ctrl::CalAvgs; | 12 | use hal::pac::adc1::ctrl::CalAvgs; |
| @@ -16,11 +17,10 @@ const G_LPADC_RESULT_SHIFT: u32 = 0; | |||
| 16 | 17 | ||
| 17 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
| 18 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 19 | let p = hal::init(hal::config::Config::default()); | 20 | let mut config = Config::default(); |
| 21 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 20 | 22 | ||
| 21 | unsafe { | 23 | let p = hal::init(config); |
| 22 | init_adc_pins(); | ||
| 23 | } | ||
| 24 | 24 | ||
| 25 | defmt::info!("=== ADC polling Example ==="); | 25 | defmt::info!("=== ADC polling Example ==="); |
| 26 | 26 | ||
| @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { | |||
| 39 | source: AdcClockSel::FroLfDiv, | 39 | source: AdcClockSel::FroLfDiv, |
| 40 | div: Div4::no_div(), | 40 | div: Div4::no_div(), |
| 41 | }; | 41 | }; |
| 42 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); | 42 | let adc = Adc::new_blocking(p.ADC1, p.P1_10, adc_config).unwrap(); |
| 43 | 43 | ||
| 44 | adc.do_offset_calibration(); | 44 | adc.do_offset_calibration(); |
| 45 | adc.do_auto_calibration(); | 45 | adc.do_auto_calibration(); |
| @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { | |||
| 47 | let mut conv_command_config = adc.get_default_conv_command_config(); | 47 | let mut conv_command_config = adc.get_default_conv_command_config(); |
| 48 | conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; | 48 | conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; |
| 49 | conv_command_config.conversion_resolution_mode = Mode::Data16Bits; | 49 | conv_command_config.conversion_resolution_mode = Mode::Data16Bits; |
| 50 | adc.set_conv_command_config(1, &conv_command_config); | 50 | adc.set_conv_command_config(1, &conv_command_config).unwrap(); |
| 51 | 51 | ||
| 52 | let mut conv_trigger_config = adc.get_default_conv_trigger_config(); | 52 | let mut conv_trigger_config = adc.get_default_conv_trigger_config(); |
| 53 | conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; | 53 | conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; |
| @@ -58,11 +58,15 @@ async fn main(_spawner: Spawner) { | |||
| 58 | 58 | ||
| 59 | loop { | 59 | loop { |
| 60 | adc.do_software_trigger(1); | 60 | adc.do_software_trigger(1); |
| 61 | let mut result: Option<ConvResult> = None; | 61 | let result = loop { |
| 62 | while result.is_none() { | 62 | match adc.get_conv_result() { |
| 63 | result = hal::adc::get_conv_result(); | 63 | Ok(res) => break res, |
| 64 | } | 64 | Err(_) => { |
| 65 | let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; | 65 | // Conversion not ready, continue polling |
| 66 | defmt::info!("value: {=u16}", value); | 66 | } |
| 67 | } | ||
| 68 | }; | ||
| 69 | let value = result.conv_value >> G_LPADC_RESULT_SHIFT; | ||
| 70 | defmt::info!("ADC value: {=u16}", value); | ||
| 67 | } | 71 | } |
| 68 | } | 72 | } |
diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs deleted file mode 100644 index 2573a6adc..000000000 --- a/examples/mcxa/src/lib.rs +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![allow(clippy::missing_safety_doc)] | ||
| 3 | |||
| 4 | //! Shared board-specific helpers for the FRDM-MCXA276 examples. | ||
| 5 | //! These live with the examples so the HAL stays generic. | ||
| 6 | |||
| 7 | use hal::{clocks, pins}; | ||
| 8 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 9 | |||
| 10 | /// Initialize clocks and pin muxing for ADC. | ||
| 11 | pub unsafe fn init_adc_pins() { | ||
| 12 | // NOTE: Lpuart has been updated to properly enable + reset its own clocks. | ||
| 13 | // GPIO has not. | ||
| 14 | _ = clocks::enable_and_reset::<hal::peripherals::PORT1>(&clocks::periph_helpers::NoConfig); | ||
| 15 | pins::configure_adc_pins(); | ||
| 16 | } | ||
