diff options
Diffstat (limited to 'embassy-mcxa/src/adc.rs')
| -rw-r--r-- | embassy-mcxa/src/adc.rs | 849 |
1 files changed, 849 insertions, 0 deletions
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs new file mode 100644 index 000000000..f88bb6b37 --- /dev/null +++ b/embassy-mcxa/src/adc.rs | |||
| @@ -0,0 +1,849 @@ | |||
| 1 | //! ADC driver | ||
| 2 | use core::future::Future; | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 6 | use maitake_sync::WaitCell; | ||
| 7 | use paste::paste; | ||
| 8 | |||
| 9 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | ||
| 10 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; | ||
| 11 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 12 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 13 | use crate::pac; | ||
| 14 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | ||
| 15 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | ||
| 16 | use crate::pac::adc1::cmdl1::Mode; | ||
| 17 | use crate::pac::adc1::ctrl::CalAvgs; | ||
| 18 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; | ||
| 19 | |||
| 20 | /// Trigger priority policy for ADC conversions. | ||
| 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 22 | #[repr(u8)] | ||
| 23 | pub enum TriggerPriorityPolicy { | ||
| 24 | ConvPreemptImmediatelyNotAutoResumed = 0, | ||
| 25 | ConvPreemptSoftlyNotAutoResumed = 1, | ||
| 26 | ConvPreemptImmediatelyAutoRestarted = 4, | ||
| 27 | ConvPreemptSoftlyAutoRestarted = 5, | ||
| 28 | ConvPreemptImmediatelyAutoResumed = 12, | ||
| 29 | ConvPreemptSoftlyAutoResumed = 13, | ||
| 30 | ConvPreemptSubsequentlyNotAutoResumed = 2, | ||
| 31 | ConvPreemptSubsequentlyAutoRestarted = 6, | ||
| 32 | ConvPreemptSubsequentlyAutoResumed = 14, | ||
| 33 | TriggerPriorityExceptionDisabled = 16, | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Configuration for the LPADC peripheral. | ||
| 37 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 38 | pub struct LpadcConfig { | ||
| 39 | /// Control system transition to Stop and Wait power modes while ADC is converting. | ||
| 40 | /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. | ||
| 41 | /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. | ||
| 42 | pub enable_in_doze_mode: bool, | ||
| 43 | /// Auto-Calibration Averages. | ||
| 44 | pub conversion_average_mode: CalAvgs, | ||
| 45 | /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption). | ||
| 46 | pub enable_analog_preliminary: bool, | ||
| 47 | /// Power-up delay value (in ADC clock cycles) | ||
| 48 | pub power_up_delay: u8, | ||
| 49 | /// Reference voltage source selection | ||
| 50 | pub reference_voltage_source: Refsel, | ||
| 51 | /// Power configuration selection. | ||
| 52 | pub power_level_mode: Pwrsel, | ||
| 53 | /// Trigger priority policy for handling multiple triggers | ||
| 54 | pub trigger_priority_policy: TriggerPriorityPolicy, | ||
| 55 | /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, | ||
| 56 | /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. | ||
| 57 | pub enable_conv_pause: bool, | ||
| 58 | /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. | ||
| 59 | /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. | ||
| 60 | pub conv_pause_delay: u16, | ||
| 61 | /// Power configuration (normal/deep sleep behavior) | ||
| 62 | pub power: PoweredClock, | ||
| 63 | /// ADC clock source selection | ||
| 64 | pub source: AdcClockSel, | ||
| 65 | /// Clock divider for ADC clock | ||
| 66 | pub div: Div4, | ||
| 67 | } | ||
| 68 | |||
| 69 | impl Default for LpadcConfig { | ||
| 70 | fn default() -> Self { | ||
| 71 | LpadcConfig { | ||
| 72 | enable_in_doze_mode: true, | ||
| 73 | conversion_average_mode: CalAvgs::NoAverage, | ||
| 74 | enable_analog_preliminary: false, | ||
| 75 | power_up_delay: 0x80, | ||
| 76 | reference_voltage_source: Refsel::Option1, | ||
| 77 | power_level_mode: Pwrsel::Lowest, | ||
| 78 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 79 | enable_conv_pause: false, | ||
| 80 | conv_pause_delay: 0, | ||
| 81 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 82 | source: AdcClockSel::FroLfDiv, | ||
| 83 | div: Div4::no_div(), | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Configuration for a conversion command. | ||
| 89 | /// | ||
| 90 | /// Defines the parameters for a single ADC conversion operation. | ||
| 91 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 92 | pub struct ConvCommandConfig { | ||
| 93 | pub chained_next_command_number: Next, | ||
| 94 | pub enable_auto_channel_increment: bool, | ||
| 95 | pub loop_count: u8, | ||
| 96 | pub hardware_average_mode: Avgs, | ||
| 97 | pub sample_time_mode: Sts, | ||
| 98 | pub hardware_compare_mode: Cmpen, | ||
| 99 | pub hardware_compare_value_high: u32, | ||
| 100 | pub hardware_compare_value_low: u32, | ||
| 101 | pub conversion_resolution_mode: Mode, | ||
| 102 | pub enable_wait_trigger: bool, | ||
| 103 | } | ||
| 104 | |||
| 105 | impl Default for ConvCommandConfig { | ||
| 106 | fn default() -> Self { | ||
| 107 | ConvCommandConfig { | ||
| 108 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 109 | enable_auto_channel_increment: false, | ||
| 110 | loop_count: 0, | ||
| 111 | hardware_average_mode: Avgs::NoAverage, | ||
| 112 | sample_time_mode: Sts::Sample3p5, | ||
| 113 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 114 | hardware_compare_value_high: 0, | ||
| 115 | hardware_compare_value_low: 0, | ||
| 116 | conversion_resolution_mode: Mode::Data12Bits, | ||
| 117 | enable_wait_trigger: false, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Configuration for a conversion trigger. | ||
| 123 | /// | ||
| 124 | /// Defines how a trigger initiates ADC conversions. | ||
| 125 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 126 | pub struct ConvTriggerConfig { | ||
| 127 | pub target_command_id: Tcmd, | ||
| 128 | pub delay_power: u8, | ||
| 129 | pub priority: Tpri, | ||
| 130 | pub enable_hardware_trigger: bool, | ||
| 131 | } | ||
| 132 | |||
| 133 | impl Default for ConvTriggerConfig { | ||
| 134 | fn default() -> Self { | ||
| 135 | ConvTriggerConfig { | ||
| 136 | target_command_id: Tcmd::NotValid, | ||
| 137 | delay_power: 0, | ||
| 138 | priority: Tpri::HighestPriority, | ||
| 139 | enable_hardware_trigger: false, | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Shorthand for `Result<T>`. | ||
| 145 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 146 | |||
| 147 | /// ADC Error types | ||
| 148 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 149 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 150 | pub enum Error { | ||
| 151 | /// FIFO is empty, no conversion result available | ||
| 152 | FifoEmpty, | ||
| 153 | /// Invalid configuration | ||
| 154 | InvalidConfig, | ||
| 155 | /// Clock configuration error. | ||
| 156 | ClockSetup(ClockError), | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Result of an ADC conversion. | ||
| 160 | /// | ||
| 161 | /// Contains the conversion value and metadata about the conversion. | ||
| 162 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 163 | pub struct ConvResult { | ||
| 164 | pub command_id_source: u8, | ||
| 165 | pub loop_count_index: u8, | ||
| 166 | pub trigger_id_source: u8, | ||
| 167 | pub conv_value: u16, | ||
| 168 | } | ||
| 169 | |||
| 170 | /// ADC interrupt handler. | ||
| 171 | pub struct InterruptHandler<I: Instance> { | ||
| 172 | _phantom: PhantomData<I>, | ||
| 173 | } | ||
| 174 | |||
| 175 | /// ADC driver instance. | ||
| 176 | pub struct Adc<'a, M: ModeAdc> { | ||
| 177 | _inst: PhantomData<&'a mut ()>, | ||
| 178 | mode: M, | ||
| 179 | |||
| 180 | // The channel index of the pin used to create our ADC instance | ||
| 181 | channel_idx: u8, | ||
| 182 | |||
| 183 | // The register block of the ADC instance | ||
| 184 | info: &'static pac::adc0::RegisterBlock, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl<'a> Adc<'a, Blocking> { | ||
| 188 | /// Create a new blocking instance of the ADC driver. | ||
| 189 | /// # Arguments | ||
| 190 | /// * `_inst` - ADC peripheral instance | ||
| 191 | /// * `pin` - GPIO pin to use for ADC | ||
| 192 | /// * `config` - ADC configuration | ||
| 193 | pub fn new_blocking<I: Instance>( | ||
| 194 | _inst: Peri<'a, I>, | ||
| 195 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 196 | config: LpadcConfig, | ||
| 197 | ) -> Result<Self> { | ||
| 198 | Self::new_inner(_inst, pin, config, Blocking) | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Enable ADC interrupts. | ||
| 202 | /// | ||
| 203 | /// Enables the interrupt sources specified in the bitmask. | ||
| 204 | /// | ||
| 205 | /// # Arguments | ||
| 206 | /// * `mask` - Bitmask of interrupt sources to enable | ||
| 207 | pub fn enable_interrupt(&mut self, mask: u32) { | ||
| 208 | self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Disable ADC interrupts. | ||
| 212 | /// | ||
| 213 | /// Disables the interrupt sources specified in the bitmask. | ||
| 214 | /// | ||
| 215 | /// # Arguments | ||
| 216 | /// * `mask` - Bitmask of interrupt sources to disable | ||
| 217 | pub fn disable_interrupt(&mut self, mask: u32) { | ||
| 218 | self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); | ||
| 219 | } | ||
| 220 | |||
| 221 | pub fn set_fifo_watermark(&mut self, watermark: u8) -> Result<()> { | ||
| 222 | if watermark > 0b111 { | ||
| 223 | return Err(Error::InvalidConfig); | ||
| 224 | } | ||
| 225 | self.info.fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) }); | ||
| 226 | Ok(()) | ||
| 227 | } | ||
| 228 | |||
| 229 | /// Trigger ADC conversion(s) via software. | ||
| 230 | /// | ||
| 231 | /// Initiates conversion(s) for the trigger(s) specified in the bitmask. | ||
| 232 | /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). | ||
| 233 | /// | ||
| 234 | /// # Arguments | ||
| 235 | /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) | ||
| 236 | /// | ||
| 237 | /// # Returns | ||
| 238 | /// * `Ok(())` if the triger mask was valid | ||
| 239 | /// * `Err(Error::InvalidConfig)` if the mask was greater than `0b1111` | ||
| 240 | pub fn do_software_trigger(&self, trigger_id_mask: u8) -> Result<()> { | ||
| 241 | if trigger_id_mask > 0b1111 { | ||
| 242 | return Err(Error::InvalidConfig); | ||
| 243 | } | ||
| 244 | self.info.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) }); | ||
| 245 | Ok(()) | ||
| 246 | } | ||
| 247 | |||
| 248 | /// Set conversion command configuration. | ||
| 249 | /// | ||
| 250 | /// Configures a conversion command slot with the specified parameters. | ||
| 251 | /// Commands define how conversions are performed (channel, resolution, etc.). | ||
| 252 | /// | ||
| 253 | /// # Arguments | ||
| 254 | /// * `index` - Command index (Must be in range 1..=7) | ||
| 255 | /// * `config` - Command configuration | ||
| 256 | /// | ||
| 257 | /// # Returns | ||
| 258 | /// * `Ok(())` if the command was configured successfully | ||
| 259 | /// * `Err(Error::InvalidConfig)` if the index is out of range | ||
| 260 | pub fn set_conv_command_config(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { | ||
| 261 | self.set_conv_command_config_inner(index, config) | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Set conversion trigger configuration. | ||
| 265 | /// | ||
| 266 | /// Configures a trigger to initiate conversions. Triggers can be | ||
| 267 | /// activated by software or hardware signals. | ||
| 268 | /// | ||
| 269 | /// # Arguments | ||
| 270 | /// * `trigger_id` - Trigger index (0..=3) | ||
| 271 | /// * `config` - Trigger configuration | ||
| 272 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { | ||
| 273 | self.set_conv_trigger_config_inner(trigger_id, config) | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Reset the FIFO buffer. | ||
| 277 | /// | ||
| 278 | /// Clears all pending conversion results from the FIFO. | ||
| 279 | pub fn do_reset_fifo(&self) { | ||
| 280 | self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Get conversion result from FIFO. | ||
| 284 | /// | ||
| 285 | /// Reads and returns the next conversion result from the FIFO. | ||
| 286 | /// Returns `None` if the FIFO is empty. | ||
| 287 | /// | ||
| 288 | /// # Returns | ||
| 289 | /// - `Some(ConvResult)` if a result is available | ||
| 290 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 291 | pub fn get_conv_result(&self) -> Result<ConvResult> { | ||
| 292 | self.get_conv_result_inner() | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | impl<'a> Adc<'a, Async> { | ||
| 297 | /// Initialize ADC with interrupt support. | ||
| 298 | /// | ||
| 299 | /// # Arguments | ||
| 300 | /// * `_inst` - ADC peripheral instance | ||
| 301 | /// * `pin` - GPIO pin to use for ADC | ||
| 302 | /// * `_irq` - Interrupt binding for this ADC instance | ||
| 303 | /// * `config` - ADC configuration | ||
| 304 | pub fn new_async<I: Instance>( | ||
| 305 | _inst: Peri<'a, I>, | ||
| 306 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 307 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | ||
| 308 | config: LpadcConfig, | ||
| 309 | ) -> Result<Self> { | ||
| 310 | let adc = Self::new_inner(_inst, pin, config, Async { waiter: I::wait_cell() })?; | ||
| 311 | |||
| 312 | I::Interrupt::unpend(); | ||
| 313 | unsafe { I::Interrupt::enable() }; | ||
| 314 | |||
| 315 | let cfg = ConvCommandConfig { | ||
| 316 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 317 | enable_auto_channel_increment: false, | ||
| 318 | loop_count: 0, | ||
| 319 | hardware_average_mode: Avgs::NoAverage, | ||
| 320 | sample_time_mode: Sts::Sample3p5, | ||
| 321 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 322 | hardware_compare_value_high: 0, | ||
| 323 | hardware_compare_value_low: 0, | ||
| 324 | conversion_resolution_mode: Mode::Data16Bits, | ||
| 325 | enable_wait_trigger: false, | ||
| 326 | }; | ||
| 327 | |||
| 328 | // We always use command 1, so this cannot fail | ||
| 329 | _ = adc.set_conv_command_config_inner(1, &cfg); | ||
| 330 | |||
| 331 | let cfg = ConvTriggerConfig { | ||
| 332 | target_command_id: Tcmd::ExecuteCmd1, | ||
| 333 | delay_power: 0, | ||
| 334 | priority: Tpri::HighestPriority, | ||
| 335 | enable_hardware_trigger: false, | ||
| 336 | }; | ||
| 337 | |||
| 338 | // We always use trigger 0, so this cannot fail | ||
| 339 | _ = adc.set_conv_trigger_config_inner(0, &cfg); | ||
| 340 | |||
| 341 | // We always set the watermark to 0 (trigger when 1 is available) | ||
| 342 | I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(0) }); | ||
| 343 | |||
| 344 | Ok(adc) | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Set the number of averages | ||
| 348 | pub fn set_averages(&mut self, avgs: Avgs) { | ||
| 349 | // TODO: we should probably return a result or wait for idle? | ||
| 350 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 351 | self.info.cmdh1().modify(|_r, w| w.avgs().variant(avgs)); | ||
| 352 | } | ||
| 353 | |||
| 354 | /// Set the sample time | ||
| 355 | pub fn set_sample_time(&mut self, st: Sts) { | ||
| 356 | // TODO: we should probably return a result or wait for idle? | ||
| 357 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 358 | self.info.cmdh1().modify(|_r, w| w.sts().variant(st)); | ||
| 359 | } | ||
| 360 | |||
| 361 | pub fn set_resolution(&mut self, mode: Mode) { | ||
| 362 | // TODO: we should probably return a result or wait for idle? | ||
| 363 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 364 | self.info.cmdl1().modify(|_r, w| w.mode().variant(mode)); | ||
| 365 | } | ||
| 366 | |||
| 367 | fn wait_idle(&mut self) -> impl Future<Output = core::result::Result<(), maitake_sync::Closed>> + use<'_> { | ||
| 368 | self.mode | ||
| 369 | .waiter | ||
| 370 | .wait_for(|| self.info.ie().read().fwmie0().bit_is_clear()) | ||
| 371 | } | ||
| 372 | |||
| 373 | /// Read ADC value asynchronously. | ||
| 374 | /// | ||
| 375 | /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. | ||
| 376 | /// | ||
| 377 | /// The function: | ||
| 378 | /// 1. Enables the FIFO watermark interrupt | ||
| 379 | /// 2. Triggers a software conversion on trigger 0 | ||
| 380 | /// 3. Waits for the conversion to complete | ||
| 381 | /// 4. Returns the conversion result | ||
| 382 | /// | ||
| 383 | /// # Returns | ||
| 384 | /// 16-bit ADC conversion value | ||
| 385 | pub async fn read(&mut self) -> Result<u16> { | ||
| 386 | // If we cancelled a previous read, we might still be busy, wait | ||
| 387 | // until the interrupt is cleared (done by the interrupt) | ||
| 388 | _ = self.wait_idle().await; | ||
| 389 | |||
| 390 | // Clear the fifo | ||
| 391 | self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 392 | |||
| 393 | // Trigger a new conversion | ||
| 394 | self.info.ie().modify(|_r, w| w.fwmie0().set_bit()); | ||
| 395 | self.info.swtrig().write(|w| w.swt0().set_bit()); | ||
| 396 | |||
| 397 | // Wait for completion | ||
| 398 | _ = self.wait_idle().await; | ||
| 399 | |||
| 400 | self.get_conv_result_inner().map(|r| r.conv_value) | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | impl<'a, M: ModeAdc> Adc<'a, M> { | ||
| 405 | /// Internal initialization function shared by `new_async` and `new_blocking`. | ||
| 406 | fn new_inner<I: Instance, P: AdcPin<I>>( | ||
| 407 | _inst: Peri<'a, I>, | ||
| 408 | pin: Peri<'a, P>, | ||
| 409 | config: LpadcConfig, | ||
| 410 | mode: M, | ||
| 411 | ) -> Result<Self> { | ||
| 412 | let adc = I::ptr(); | ||
| 413 | |||
| 414 | _ = unsafe { | ||
| 415 | enable_and_reset::<I>(&AdcConfig { | ||
| 416 | power: config.power, | ||
| 417 | source: config.source, | ||
| 418 | div: config.div, | ||
| 419 | }) | ||
| 420 | .map_err(Error::ClockSetup)? | ||
| 421 | }; | ||
| 422 | |||
| 423 | pin.mux(); | ||
| 424 | |||
| 425 | /* Reset the module. */ | ||
| 426 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); | ||
| 427 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); | ||
| 428 | |||
| 429 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 430 | |||
| 431 | /* Disable the module before setting configuration. */ | ||
| 432 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | ||
| 433 | |||
| 434 | /* Configure the module generally. */ | ||
| 435 | adc.ctrl().modify(|_, w| w.dozen().bit(config.enable_in_doze_mode)); | ||
| 436 | |||
| 437 | /* Set calibration average mode. */ | ||
| 438 | adc.ctrl() | ||
| 439 | .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); | ||
| 440 | |||
| 441 | adc.cfg().write(|w| unsafe { | ||
| 442 | w.pwren().bit(config.enable_analog_preliminary); | ||
| 443 | |||
| 444 | w.pudly() | ||
| 445 | .bits(config.power_up_delay) | ||
| 446 | .refsel() | ||
| 447 | .variant(config.reference_voltage_source) | ||
| 448 | .pwrsel() | ||
| 449 | .variant(config.power_level_mode) | ||
| 450 | .tprictrl() | ||
| 451 | .variant(match config.trigger_priority_policy { | ||
| 452 | TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed | ||
| 453 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted | ||
| 454 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority, | ||
| 455 | TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed | ||
| 456 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted | ||
| 457 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority, | ||
| 458 | _ => Tprictrl::AbortCurrentOnPriority, | ||
| 459 | }) | ||
| 460 | .tres() | ||
| 461 | .variant(match config.trigger_priority_policy { | ||
| 462 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted | ||
| 463 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted | ||
| 464 | | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed | ||
| 465 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed | ||
| 466 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted | ||
| 467 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled, | ||
| 468 | _ => Tres::Disabled, | ||
| 469 | }) | ||
| 470 | .tcmdres() | ||
| 471 | .variant(match config.trigger_priority_policy { | ||
| 472 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed | ||
| 473 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed | ||
| 474 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed | ||
| 475 | | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled, | ||
| 476 | _ => Tcmdres::Disabled, | ||
| 477 | }) | ||
| 478 | .hpt_exdi() | ||
| 479 | .variant(match config.trigger_priority_policy { | ||
| 480 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled, | ||
| 481 | _ => HptExdi::Enabled, | ||
| 482 | }) | ||
| 483 | }); | ||
| 484 | |||
| 485 | if config.enable_conv_pause { | ||
| 486 | adc.pause() | ||
| 487 | .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) }); | ||
| 488 | } else { | ||
| 489 | adc.pause().write(|w| unsafe { w.bits(0) }); | ||
| 490 | } | ||
| 491 | |||
| 492 | adc.fctrl0().write(|w| unsafe { w.fwmark().bits(0) }); | ||
| 493 | |||
| 494 | // Enable ADC | ||
| 495 | adc.ctrl().modify(|_, w| w.adcen().enabled()); | ||
| 496 | |||
| 497 | Ok(Self { | ||
| 498 | _inst: PhantomData, | ||
| 499 | mode, | ||
| 500 | channel_idx: P::CHANNEL, | ||
| 501 | info: adc, | ||
| 502 | }) | ||
| 503 | } | ||
| 504 | |||
| 505 | /// Perform offset calibration. | ||
| 506 | /// Waits for calibration to complete before returning. | ||
| 507 | pub fn do_offset_calibration(&self) { | ||
| 508 | // Enable calibration mode | ||
| 509 | self.info | ||
| 510 | .ctrl() | ||
| 511 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); | ||
| 512 | |||
| 513 | // Wait for calibration to complete (polling status register) | ||
| 514 | while self.info.stat().read().cal_rdy().is_not_set() {} | ||
| 515 | } | ||
| 516 | |||
| 517 | /// Calculate gain conversion result from gain adjustment factor. | ||
| 518 | /// | ||
| 519 | /// # Arguments | ||
| 520 | /// * `gain_adjustment` - Gain adjustment factor | ||
| 521 | /// | ||
| 522 | /// # Returns | ||
| 523 | /// Gain calibration register value | ||
| 524 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { | ||
| 525 | let mut gcra_array = [0u32; 17]; | ||
| 526 | let mut gcalr: u32 = 0; | ||
| 527 | |||
| 528 | for i in (1..=17).rev() { | ||
| 529 | let shift = 16 - (i - 1); | ||
| 530 | let step = 1.0 / (1u32 << shift) as f32; | ||
| 531 | let tmp = (gain_adjustment / step) as u32; | ||
| 532 | gcra_array[i - 1] = tmp; | ||
| 533 | gain_adjustment -= tmp as f32 * step; | ||
| 534 | } | ||
| 535 | |||
| 536 | for i in (1..=17).rev() { | ||
| 537 | gcalr += gcra_array[i - 1] << (i - 1); | ||
| 538 | } | ||
| 539 | gcalr | ||
| 540 | } | ||
| 541 | |||
| 542 | /// Perform automatic gain calibration. | ||
| 543 | pub fn do_auto_calibration(&self) { | ||
| 544 | self.info | ||
| 545 | .ctrl() | ||
| 546 | .modify(|_, w| w.cal_req().calibration_request_pending()); | ||
| 547 | |||
| 548 | while self.info.gcc0().read().rdy().is_gain_cal_not_valid() {} | ||
| 549 | |||
| 550 | let mut gcca = self.info.gcc0().read().gain_cal().bits() as u32; | ||
| 551 | if gcca & 0x8000 != 0 { | ||
| 552 | gcca |= !0xFFFF; | ||
| 553 | } | ||
| 554 | |||
| 555 | let gcra = 131072.0 / (131072.0 - gcca as f32); | ||
| 556 | |||
| 557 | // Write to GCR0 | ||
| 558 | self.info | ||
| 559 | .gcr0() | ||
| 560 | .write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); | ||
| 561 | |||
| 562 | self.info.gcr0().modify(|_, w| w.rdy().set_bit()); | ||
| 563 | |||
| 564 | // Wait for calibration to complete (polling status register) | ||
| 565 | while self.info.stat().read().cal_rdy().is_not_set() {} | ||
| 566 | } | ||
| 567 | |||
| 568 | fn set_conv_command_config_inner(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { | ||
| 569 | let (cmdl, cmdh) = match index { | ||
| 570 | 1 => (self.info.cmdl1(), self.info.cmdh1()), | ||
| 571 | 2 => (self.info.cmdl2(), self.info.cmdh2()), | ||
| 572 | 3 => (self.info.cmdl3(), self.info.cmdh3()), | ||
| 573 | 4 => (self.info.cmdl4(), self.info.cmdh4()), | ||
| 574 | 5 => (self.info.cmdl5(), self.info.cmdh5()), | ||
| 575 | 6 => (self.info.cmdl6(), self.info.cmdh6()), | ||
| 576 | 7 => (self.info.cmdl7(), self.info.cmdh7()), | ||
| 577 | _ => return Err(Error::InvalidConfig), | ||
| 578 | }; | ||
| 579 | |||
| 580 | cmdl.write(|w| { | ||
| 581 | unsafe { | ||
| 582 | w.adch().bits(self.channel_idx); | ||
| 583 | } | ||
| 584 | w.mode().variant(config.conversion_resolution_mode) | ||
| 585 | }); | ||
| 586 | |||
| 587 | cmdh.write(|w| { | ||
| 588 | w.next().variant(config.chained_next_command_number); | ||
| 589 | unsafe { | ||
| 590 | w.loop_().bits(config.loop_count); | ||
| 591 | } | ||
| 592 | w.avgs().variant(config.hardware_average_mode); | ||
| 593 | w.sts().variant(config.sample_time_mode); | ||
| 594 | w.cmpen().variant(config.hardware_compare_mode); | ||
| 595 | w.wait_trig().bit(config.enable_wait_trigger); | ||
| 596 | w.lwi().bit(config.enable_auto_channel_increment); | ||
| 597 | w | ||
| 598 | }); | ||
| 599 | |||
| 600 | Ok(()) | ||
| 601 | } | ||
| 602 | |||
| 603 | fn set_conv_trigger_config_inner(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { | ||
| 604 | // 0..4 are valid | ||
| 605 | if trigger_id >= 4 { | ||
| 606 | return Err(Error::InvalidConfig); | ||
| 607 | } | ||
| 608 | |||
| 609 | let tctrl = &self.info.tctrl(trigger_id); | ||
| 610 | |||
| 611 | tctrl.write(|w| { | ||
| 612 | w.tcmd().variant(config.target_command_id); | ||
| 613 | unsafe { | ||
| 614 | w.tdly().bits(config.delay_power); | ||
| 615 | } | ||
| 616 | w.tpri().variant(config.priority); | ||
| 617 | if config.enable_hardware_trigger { | ||
| 618 | w.hten().enabled() | ||
| 619 | } else { | ||
| 620 | w | ||
| 621 | } | ||
| 622 | }); | ||
| 623 | |||
| 624 | Ok(()) | ||
| 625 | } | ||
| 626 | |||
| 627 | /// Get conversion result from FIFO. | ||
| 628 | /// | ||
| 629 | /// Reads and returns the next conversion result from the FIFO. | ||
| 630 | /// Returns `None` if the FIFO is empty. | ||
| 631 | /// | ||
| 632 | /// # Returns | ||
| 633 | /// - `Some(ConvResult)` if a result is available | ||
| 634 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 635 | fn get_conv_result_inner(&self) -> Result<ConvResult> { | ||
| 636 | let fifo = self.info.resfifo0().read(); | ||
| 637 | if !fifo.valid().is_valid() { | ||
| 638 | return Err(Error::FifoEmpty); | ||
| 639 | } | ||
| 640 | |||
| 641 | Ok(ConvResult { | ||
| 642 | command_id_source: fifo.cmdsrc().bits(), | ||
| 643 | loop_count_index: fifo.loopcnt().bits(), | ||
| 644 | trigger_id_source: fifo.tsrc().bits(), | ||
| 645 | conv_value: fifo.d().bits(), | ||
| 646 | }) | ||
| 647 | } | ||
| 648 | } | ||
| 649 | |||
| 650 | impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 651 | unsafe fn on_interrupt() { | ||
| 652 | T::ptr().ie().modify(|_r, w| w.fwmie0().clear_bit()); | ||
| 653 | T::wait_cell().wake(); | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | mod sealed { | ||
| 658 | /// Seal a trait | ||
| 659 | pub trait Sealed {} | ||
| 660 | } | ||
| 661 | |||
| 662 | impl<I: GpioPin> sealed::Sealed for I {} | ||
| 663 | |||
| 664 | trait SealedInstance { | ||
| 665 | fn ptr() -> &'static pac::adc0::RegisterBlock; | ||
| 666 | fn wait_cell() -> &'static WaitCell; | ||
| 667 | } | ||
| 668 | |||
| 669 | /// ADC Instance | ||
| 670 | #[allow(private_bounds)] | ||
| 671 | pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> { | ||
| 672 | /// Interrupt for this ADC instance. | ||
| 673 | type Interrupt: Interrupt; | ||
| 674 | } | ||
| 675 | |||
| 676 | macro_rules! impl_instance { | ||
| 677 | ($($n:expr),*) => { | ||
| 678 | $( | ||
| 679 | paste!{ | ||
| 680 | impl SealedInstance for crate::peripherals::[<ADC $n>] { | ||
| 681 | fn ptr() -> &'static pac::adc0::RegisterBlock { | ||
| 682 | unsafe { &*pac::[<Adc $n>]::ptr() } | ||
| 683 | } | ||
| 684 | |||
| 685 | fn wait_cell() -> &'static WaitCell { | ||
| 686 | static WAIT_CELL: WaitCell = WaitCell::new(); | ||
| 687 | &WAIT_CELL | ||
| 688 | } | ||
| 689 | |||
| 690 | } | ||
| 691 | |||
| 692 | impl Instance for crate::peripherals::[<ADC $n>] { | ||
| 693 | type Interrupt = crate::interrupt::typelevel::[<ADC $n>]; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | )* | ||
| 697 | }; | ||
| 698 | } | ||
| 699 | |||
| 700 | impl_instance!(0, 1, 2, 3); | ||
| 701 | |||
| 702 | pub trait AdcPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 703 | const CHANNEL: u8; | ||
| 704 | |||
| 705 | /// Set the given pin to the correct muxing state | ||
| 706 | fn mux(&self); | ||
| 707 | } | ||
| 708 | |||
| 709 | /// Driver mode. | ||
| 710 | #[allow(private_bounds)] | ||
| 711 | pub trait ModeAdc: sealed::Sealed {} | ||
| 712 | |||
| 713 | /// Blocking mode. | ||
| 714 | pub struct Blocking; | ||
| 715 | impl sealed::Sealed for Blocking {} | ||
| 716 | impl ModeAdc for Blocking {} | ||
| 717 | |||
| 718 | /// Async mode. | ||
| 719 | pub struct Async { | ||
| 720 | waiter: &'static WaitCell, | ||
| 721 | } | ||
| 722 | impl sealed::Sealed for Async {} | ||
| 723 | impl ModeAdc for Async {} | ||
| 724 | |||
| 725 | macro_rules! impl_pin { | ||
| 726 | ($pin:ident, $peri:ident, $func:ident, $channel:literal) => { | ||
| 727 | impl AdcPin<crate::peripherals::$peri> for crate::peripherals::$pin { | ||
| 728 | const CHANNEL: u8 = $channel; | ||
| 729 | |||
| 730 | fn mux(&self) { | ||
| 731 | self.set_pull(crate::gpio::Pull::Disabled); | ||
| 732 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 733 | self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); | ||
| 734 | self.set_function(crate::pac::port0::pcr0::Mux::$func); | ||
| 735 | } | ||
| 736 | } | ||
| 737 | }; | ||
| 738 | } | ||
| 739 | |||
| 740 | impl_pin!(P2_0, ADC0, Mux0, 0); | ||
| 741 | impl_pin!(P2_4, ADC0, Mux0, 1); | ||
| 742 | impl_pin!(P2_15, ADC0, Mux0, 2); | ||
| 743 | impl_pin!(P2_3, ADC0, Mux0, 3); | ||
| 744 | impl_pin!(P2_2, ADC0, Mux0, 4); | ||
| 745 | impl_pin!(P2_12, ADC0, Mux0, 5); | ||
| 746 | impl_pin!(P2_16, ADC0, Mux0, 6); | ||
| 747 | impl_pin!(P2_7, ADC0, Mux0, 7); | ||
| 748 | impl_pin!(P0_18, ADC0, Mux0, 8); | ||
| 749 | impl_pin!(P0_19, ADC0, Mux0, 9); | ||
| 750 | impl_pin!(P0_20, ADC0, Mux0, 10); | ||
| 751 | impl_pin!(P0_21, ADC0, Mux0, 11); | ||
| 752 | impl_pin!(P0_22, ADC0, Mux0, 12); | ||
| 753 | impl_pin!(P0_23, ADC0, Mux0, 13); | ||
| 754 | impl_pin!(P0_3, ADC0, Mux0, 14); | ||
| 755 | impl_pin!(P0_6, ADC0, Mux0, 15); | ||
| 756 | impl_pin!(P1_0, ADC0, Mux0, 16); | ||
| 757 | impl_pin!(P1_1, ADC0, Mux0, 17); | ||
| 758 | impl_pin!(P1_2, ADC0, Mux0, 18); | ||
| 759 | impl_pin!(P1_3, ADC0, Mux0, 19); | ||
| 760 | impl_pin!(P1_4, ADC0, Mux0, 20); | ||
| 761 | impl_pin!(P1_5, ADC0, Mux0, 21); | ||
| 762 | impl_pin!(P1_6, ADC0, Mux0, 22); | ||
| 763 | impl_pin!(P1_7, ADC0, Mux0, 23); | ||
| 764 | |||
| 765 | // ??? | ||
| 766 | // impl_pin!(P1_10, ADC0, Mux0, 255); | ||
| 767 | |||
| 768 | impl_pin!(P2_1, ADC1, Mux0, 0); | ||
| 769 | impl_pin!(P2_5, ADC1, Mux0, 1); | ||
| 770 | impl_pin!(P2_19, ADC1, Mux0, 2); | ||
| 771 | impl_pin!(P2_6, ADC1, Mux0, 3); | ||
| 772 | impl_pin!(P2_3, ADC1, Mux0, 4); | ||
| 773 | impl_pin!(P2_13, ADC1, Mux0, 5); | ||
| 774 | impl_pin!(P2_17, ADC1, Mux0, 6); | ||
| 775 | impl_pin!(P2_7, ADC1, Mux0, 7); | ||
| 776 | impl_pin!(P1_10, ADC1, Mux0, 8); | ||
| 777 | impl_pin!(P1_11, ADC1, Mux0, 9); | ||
| 778 | impl_pin!(P1_12, ADC1, Mux0, 10); | ||
| 779 | impl_pin!(P1_13, ADC1, Mux0, 11); | ||
| 780 | impl_pin!(P1_14, ADC1, Mux0, 12); | ||
| 781 | impl_pin!(P1_15, ADC1, Mux0, 13); | ||
| 782 | // ??? | ||
| 783 | // impl_pin!(P1_16, ADC1, Mux0, 255); | ||
| 784 | // impl_pin!(P1_17, ADC1, Mux0, 255); | ||
| 785 | // impl_pin!(P1_18, ADC1, Mux0, 255); | ||
| 786 | // impl_pin!(P1_19, ADC1, Mux0, 255); | ||
| 787 | // ??? | ||
| 788 | impl_pin!(P3_31, ADC1, Mux0, 20); | ||
| 789 | impl_pin!(P3_30, ADC1, Mux0, 21); | ||
| 790 | impl_pin!(P3_29, ADC1, Mux0, 22); | ||
| 791 | |||
| 792 | impl_pin!(P2_4, ADC2, Mux0, 0); | ||
| 793 | impl_pin!(P2_10, ADC2, Mux0, 1); | ||
| 794 | impl_pin!(P4_4, ADC2, Mux0, 2); | ||
| 795 | // impl_pin!(P2_24, ADC2, Mux0, 255); ??? | ||
| 796 | impl_pin!(P2_16, ADC2, Mux0, 4); | ||
| 797 | impl_pin!(P2_12, ADC2, Mux0, 5); | ||
| 798 | impl_pin!(P2_20, ADC2, Mux0, 6); | ||
| 799 | impl_pin!(P2_7, ADC2, Mux0, 7); | ||
| 800 | impl_pin!(P0_2, ADC2, Mux0, 8); | ||
| 801 | // ??? | ||
| 802 | // impl_pin!(P0_4, ADC2, Mux0, 255); | ||
| 803 | // impl_pin!(P0_5, ADC2, Mux0, 255); | ||
| 804 | // impl_pin!(P0_6, ADC2, Mux0, 255); | ||
| 805 | // impl_pin!(P0_7, ADC2, Mux0, 255); | ||
| 806 | // impl_pin!(P0_12, ADC2, Mux0, 255); | ||
| 807 | // impl_pin!(P0_13, ADC2, Mux0, 255); | ||
| 808 | // ??? | ||
| 809 | impl_pin!(P0_14, ADC2, Mux0, 14); | ||
| 810 | impl_pin!(P0_15, ADC2, Mux0, 15); | ||
| 811 | // ??? | ||
| 812 | // impl_pin!(P4_0, ADC2, Mux0, 255); | ||
| 813 | // impl_pin!(P4_1, ADC2, Mux0, 255); | ||
| 814 | // ??? | ||
| 815 | impl_pin!(P4_2, ADC2, Mux0, 18); | ||
| 816 | impl_pin!(P4_3, ADC2, Mux0, 19); | ||
| 817 | //impl_pin!(P4_4, ADC2, Mux0, 20); // Conflit with ADC2_A3 and ADC2_A20 using the same pin | ||
| 818 | impl_pin!(P4_5, ADC2, Mux0, 21); | ||
| 819 | impl_pin!(P4_6, ADC2, Mux0, 22); | ||
| 820 | impl_pin!(P4_7, ADC2, Mux0, 23); | ||
| 821 | |||
| 822 | impl_pin!(P2_5, ADC3, Mux0, 0); | ||
| 823 | impl_pin!(P2_11, ADC3, Mux0, 1); | ||
| 824 | impl_pin!(P2_23, ADC3, Mux0, 2); | ||
| 825 | // impl_pin!(P2_25, ADC3, Mux0, 255); // ??? | ||
| 826 | impl_pin!(P2_17, ADC3, Mux0, 4); | ||
| 827 | impl_pin!(P2_13, ADC3, Mux0, 5); | ||
| 828 | impl_pin!(P2_21, ADC3, Mux0, 6); | ||
| 829 | impl_pin!(P2_7, ADC3, Mux0, 7); | ||
| 830 | // ??? | ||
| 831 | // impl_pin!(P3_2, ADC3, Mux0, 255); | ||
| 832 | // impl_pin!(P3_3, ADC3, Mux0, 255); | ||
| 833 | // impl_pin!(P3_4, ADC3, Mux0, 255); | ||
| 834 | // impl_pin!(P3_5, ADC3, Mux0, 255); | ||
| 835 | // ??? | ||
| 836 | impl_pin!(P3_6, ADC3, Mux0, 12); | ||
| 837 | impl_pin!(P3_7, ADC3, Mux0, 13); | ||
| 838 | impl_pin!(P3_12, ADC3, Mux0, 14); | ||
| 839 | impl_pin!(P3_13, ADC3, Mux0, 15); | ||
| 840 | impl_pin!(P3_14, ADC3, Mux0, 16); | ||
| 841 | impl_pin!(P3_15, ADC3, Mux0, 17); | ||
| 842 | impl_pin!(P3_20, ADC3, Mux0, 18); | ||
| 843 | impl_pin!(P3_21, ADC3, Mux0, 19); | ||
| 844 | impl_pin!(P3_22, ADC3, Mux0, 20); | ||
| 845 | // ??? | ||
| 846 | // impl_pin!(P3_23, ADC3, Mux0, 255); | ||
| 847 | // impl_pin!(P3_24, ADC3, Mux0, 255); | ||
| 848 | // impl_pin!(P3_25, ADC3, Mux0, 255); | ||
| 849 | // ??? | ||
