diff options
| author | Per Rosengren <[email protected]> | 2025-09-13 19:21:44 +0200 |
|---|---|---|
| committer | Per Rosengren <[email protected]> | 2025-09-16 18:11:24 +0200 |
| commit | 575b27f9f4412cf4d0aa18d77895117092e8c3b6 (patch) | |
| tree | a6f99cd37964272027d3aa6a75a40349603bb468 | |
| parent | 25e0ebf5206fa2e2906f5826c0b1587739f628d8 (diff) | |
ADC v3: Migrate to stm32-data g0 with enums
Also allow separate sample times in read()
| -rw-r--r-- | embassy-stm32/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v3.rs | 179 |
2 files changed, 113 insertions, 71 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9c2ba1f53..6cd8ed262 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -174,7 +174,7 @@ futures-util = { version = "0.3.30", default-features = false } | |||
| 174 | sdio-host = "0.9.0" | 174 | sdio-host = "0.9.0" |
| 175 | critical-section = "1.1" | 175 | critical-section = "1.1" |
| 176 | #stm32-metapac = { version = "18" } | 176 | #stm32-metapac = { version = "18" } |
| 177 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d8432edd0406495adec19d31923584e80b8e03cb" } | 177 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7" } |
| 178 | 178 | ||
| 179 | vcell = "0.1.3" | 179 | vcell = "0.1.3" |
| 180 | nb = "1.0.0" | 180 | nb = "1.0.0" |
| @@ -192,6 +192,7 @@ bitflags = "2.4.2" | |||
| 192 | 192 | ||
| 193 | block-device-driver = { version = "0.2" } | 193 | block-device-driver = { version = "0.2" } |
| 194 | aligned = "0.4.1" | 194 | aligned = "0.4.1" |
| 195 | heapless = "0.9.1" | ||
| 195 | 196 | ||
| 196 | [dev-dependencies] | 197 | [dev-dependencies] |
| 197 | critical-section = { version = "1.1", features = ["std"] } | 198 | critical-section = { version = "1.1", features = ["std"] } |
| @@ -203,7 +204,7 @@ proc-macro2 = "1.0.36" | |||
| 203 | quote = "1.0.15" | 204 | quote = "1.0.15" |
| 204 | 205 | ||
| 205 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} |
| 206 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d8432edd0406495adec19d31923584e80b8e03cb", default-features = false, features = ["metadata"] } | 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7", default-features = false, features = ["metadata"] } |
| 207 | 208 | ||
| 208 | [features] | 209 | [features] |
| 209 | default = ["rt"] | 210 | default = ["rt"] |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 77f24c87f..eb8b53495 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,8 +1,13 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | use pac::adc::vals::Dmacfg; | 2 | use pac::adc::vals::Dmacfg; |
| 3 | #[cfg(adc_g0)] | ||
| 4 | use pac::adc::vals::{Ckmode, Ovsr, Ovss, Presc, Smpsel}; | ||
| 3 | #[cfg(adc_v3)] | 5 | #[cfg(adc_v3)] |
| 4 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | 6 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; |
| 5 | 7 | ||
| 8 | #[cfg(adc_g0)] | ||
| 9 | use heapless::Vec; | ||
| 10 | |||
| 6 | use super::{ | 11 | use super::{ |
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 12 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, |
| 8 | }; | 13 | }; |
| @@ -14,6 +19,11 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 14 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 19 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 15 | pub const VREF_CALIB_MV: u32 = 3000; | 20 | pub const VREF_CALIB_MV: u32 = 3000; |
| 16 | 21 | ||
| 22 | #[cfg(adc_g0)] | ||
| 23 | /// The number of variants in Smpsel | ||
| 24 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable | ||
| 25 | const SAMPLE_TIMES_CAPACITY: usize = 2; | ||
| 26 | |||
| 17 | pub struct VrefInt; | 27 | pub struct VrefInt; |
| 18 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 28 | impl<T: Instance> AdcChannel<T> for VrefInt {} |
| 19 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | 29 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { |
| @@ -111,30 +121,10 @@ pub enum Averaging { | |||
| 111 | cfg_if! { if #[cfg(adc_g0)] { | 121 | cfg_if! { if #[cfg(adc_g0)] { |
| 112 | 122 | ||
| 113 | /// Synchronous PCLK prescaler | 123 | /// Synchronous PCLK prescaler |
| 114 | /// * ADC_CFGR2:CKMODE in STM32WL5x | ||
| 115 | #[repr(u8)] | ||
| 116 | pub enum CkModePclk { | 124 | pub enum CkModePclk { |
| 117 | DIV1 = 3, | ||
| 118 | DIV2 = 1, | ||
| 119 | DIV4 = 2, | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Asynchronous ADCCLK prescaler | ||
| 123 | /// * ADC_CCR:PRESC in STM32WL5x | ||
| 124 | #[repr(u8)] | ||
| 125 | pub enum Presc { | ||
| 126 | DIV1, | 125 | DIV1, |
| 127 | DIV2, | 126 | DIV2, |
| 128 | DIV4, | 127 | DIV4, |
| 129 | DIV6, | ||
| 130 | DIV8, | ||
| 131 | DIV10, | ||
| 132 | DIV12, | ||
| 133 | DIV16, | ||
| 134 | DIV32, | ||
| 135 | DIV64, | ||
| 136 | DIV128, | ||
| 137 | DIV256, | ||
| 138 | } | 128 | } |
| 139 | 129 | ||
| 140 | /// The analog clock is either the synchronous prescaled PCLK or | 130 | /// The analog clock is either the synchronous prescaled PCLK or |
| @@ -213,8 +203,14 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 213 | } | 203 | } |
| 214 | } | 204 | } |
| 215 | match clock { | 205 | match clock { |
| 216 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div as u8)), | 206 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), |
| 217 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| reg.set_ckmode(div as u8)), | 207 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { |
| 208 | reg.set_ckmode(match div { | ||
| 209 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 210 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 211 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 212 | }) | ||
| 213 | }), | ||
| 218 | } | 214 | } |
| 219 | 215 | ||
| 220 | Self::init_calibrate(); | 216 | Self::init_calibrate(); |
| @@ -434,53 +430,78 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 434 | w.set_l(sequence.len() as u8 - 1); | 430 | w.set_l(sequence.len() as u8 - 1); |
| 435 | }); | 431 | }); |
| 436 | 432 | ||
| 437 | #[cfg(any(adc_g0, adc_u0))] | 433 | #[cfg(adc_g0)] |
| 438 | let mut channel_mask = 0; | 434 | { |
| 439 | 435 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | |
| 440 | // Configure channels and ranks | 436 | |
| 441 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 437 | T::regs().chselr().write(|chselr| { |
| 442 | Self::configure_channel(channel, sample_time); | 438 | T::regs().smpr().write(|smpr| { |
| 443 | 439 | for (channel, sample_time) in sequence { | |
| 444 | // Each channel is sampled according to sequence | 440 | chselr.set_chsel(channel.channel.into(), true); |
| 445 | #[cfg(not(any(adc_g0, adc_u0)))] | 441 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 446 | match _i { | 442 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); |
| 447 | 0..=3 => { | 443 | } else { |
| 448 | T::regs().sqr1().modify(|w| { | 444 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 449 | w.set_sq(_i, channel.channel()); | 445 | if let Err(_) = sample_times.push(sample_time) { |
| 450 | }); | 446 | panic!( |
| 451 | } | 447 | "Implementation is limited to {} unique sample times among all channels.", |
| 452 | 4..=8 => { | 448 | SAMPLE_TIMES_CAPACITY |
| 453 | T::regs().sqr2().modify(|w| { | 449 | ); |
| 454 | w.set_sq(_i - 4, channel.channel()); | 450 | } |
| 455 | }); | 451 | } |
| 456 | } | 452 | } |
| 457 | 9..=13 => { | 453 | }) |
| 458 | T::regs().sqr3().modify(|w| { | 454 | }); |
| 459 | w.set_sq(_i - 9, channel.channel()); | 455 | } |
| 460 | }); | 456 | #[cfg(not(adc_g0))] |
| 457 | { | ||
| 458 | #[cfg(adc_u0)] | ||
| 459 | let mut channel_mask = 0; | ||
| 460 | |||
| 461 | // Configure channels and ranks | ||
| 462 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 463 | Self::configure_channel(channel, sample_time); | ||
| 464 | |||
| 465 | // Each channel is sampled according to sequence | ||
| 466 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 467 | match _i { | ||
| 468 | 0..=3 => { | ||
| 469 | T::regs().sqr1().modify(|w| { | ||
| 470 | w.set_sq(_i, channel.channel()); | ||
| 471 | }); | ||
| 472 | } | ||
| 473 | 4..=8 => { | ||
| 474 | T::regs().sqr2().modify(|w| { | ||
| 475 | w.set_sq(_i - 4, channel.channel()); | ||
| 476 | }); | ||
| 477 | } | ||
| 478 | 9..=13 => { | ||
| 479 | T::regs().sqr3().modify(|w| { | ||
| 480 | w.set_sq(_i - 9, channel.channel()); | ||
| 481 | }); | ||
| 482 | } | ||
| 483 | 14..=15 => { | ||
| 484 | T::regs().sqr4().modify(|w| { | ||
| 485 | w.set_sq(_i - 14, channel.channel()); | ||
| 486 | }); | ||
| 487 | } | ||
| 488 | _ => unreachable!(), | ||
| 461 | } | 489 | } |
| 462 | 14..=15 => { | 490 | |
| 463 | T::regs().sqr4().modify(|w| { | 491 | #[cfg(adc_u0)] |
| 464 | w.set_sq(_i - 14, channel.channel()); | 492 | { |
| 465 | }); | 493 | channel_mask |= 1 << channel.channel(); |
| 466 | } | 494 | } |
| 467 | _ => unreachable!(), | ||
| 468 | } | 495 | } |
| 469 | 496 | ||
| 470 | #[cfg(any(adc_g0, adc_u0))] | 497 | // On G0 and U0 enabled channels are sampled from 0 to last channel. |
| 471 | { | 498 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. |
| 472 | channel_mask |= 1 << channel.channel(); | 499 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. |
| 473 | } | 500 | #[cfg(adc_u0)] |
| 501 | T::regs().chselr().modify(|reg| { | ||
| 502 | reg.set_chsel(channel_mask); | ||
| 503 | }); | ||
| 474 | } | 504 | } |
| 475 | |||
| 476 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | ||
| 477 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | ||
| 478 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | ||
| 479 | #[cfg(any(adc_g0, adc_u0))] | ||
| 480 | T::regs().chselr().modify(|reg| { | ||
| 481 | reg.set_chsel(channel_mask); | ||
| 482 | }); | ||
| 483 | |||
| 484 | // Set continuous mode with oneshot dma. | 505 | // Set continuous mode with oneshot dma. |
| 485 | // Clear overrun flag before starting transfer. | 506 | // Clear overrun flag before starting transfer. |
| 486 | T::regs().isr().modify(|reg| { | 507 | T::regs().isr().modify(|reg| { |
| @@ -535,6 +556,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 535 | }); | 556 | }); |
| 536 | } | 557 | } |
| 537 | 558 | ||
| 559 | #[cfg(not(adc_g0))] | ||
| 538 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 560 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 539 | // RM0492, RM0481, etc. | 561 | // RM0492, RM0481, etc. |
| 540 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 562 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." |
| @@ -549,13 +571,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 549 | 571 | ||
| 550 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 572 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { |
| 551 | self.enable(); | 573 | self.enable(); |
| 574 | #[cfg(not(adc_g0))] | ||
| 552 | Self::configure_channel(channel, self.sample_time); | 575 | Self::configure_channel(channel, self.sample_time); |
| 553 | 576 | #[cfg(adc_g0)] | |
| 577 | T::regs().smpr().write(|reg| { | ||
| 578 | reg.set_sample_time(0, self.sample_time); | ||
| 579 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | ||
| 580 | }); | ||
| 554 | // Select channel | 581 | // Select channel |
| 555 | #[cfg(not(any(adc_g0, adc_u0)))] | 582 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 556 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 583 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); |
| 557 | #[cfg(any(adc_g0, adc_u0))] | 584 | #[cfg(any(adc_g0, adc_u0))] |
| 558 | T::regs().chselr().write(|reg| reg.set_chsel(1 << channel.channel())); | 585 | T::regs().chselr().write(|reg| { |
| 586 | #[cfg(adc_g0)] | ||
| 587 | reg.set_chsel(channel.channel().into(), true); | ||
| 588 | #[cfg(adc_u0)] | ||
| 589 | reg.set_chsel(1 << channel.channel()); | ||
| 590 | }); | ||
| 559 | 591 | ||
| 560 | // Some models are affected by an erratum: | 592 | // Some models are affected by an erratum: |
| 561 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 593 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| @@ -579,12 +611,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 579 | val | 611 | val |
| 580 | } | 612 | } |
| 581 | 613 | ||
| 582 | #[cfg(any(adc_g0, adc_u0))] | 614 | #[cfg(adc_g0)] |
| 615 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 616 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 617 | } | ||
| 618 | #[cfg(adc_u0)] | ||
| 583 | pub fn set_oversampling_shift(&mut self, shift: u8) { | 619 | pub fn set_oversampling_shift(&mut self, shift: u8) { |
| 584 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 620 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 585 | } | 621 | } |
| 586 | 622 | ||
| 587 | #[cfg(any(adc_g0, adc_u0))] | 623 | #[cfg(adc_g0)] |
| 624 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 625 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 626 | } | ||
| 627 | #[cfg(adc_u0)] | ||
| 588 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | 628 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { |
| 589 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 629 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 590 | } | 630 | } |
| @@ -611,9 +651,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 611 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 651 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 612 | } | 652 | } |
| 613 | 653 | ||
| 654 | #[cfg(not(adc_g0))] | ||
| 614 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | 655 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { |
| 615 | cfg_if! { | 656 | cfg_if! { |
| 616 | if #[cfg(any(adc_g0, adc_u0))] { | 657 | if #[cfg(adc_u0)] { |
| 617 | // On G0 and U6 all channels use the same sampling time. | 658 | // On G0 and U6 all channels use the same sampling time. |
| 618 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 659 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); |
| 619 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 660 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
