diff options
| author | xoviat <[email protected]> | 2025-11-13 17:41:44 -0600 |
|---|---|---|
| committer | xoviat <[email protected]> | 2025-11-13 17:41:44 -0600 |
| commit | cc1aad2cc4d2f87a177495e8352dd312c1ac331c (patch) | |
| tree | 8bca5d0816882f151a87b88121e15c9e4f11f36a /embassy-stm32/src/adc | |
| parent | 4fb60b5991c4c98427ef23e6c011210341ba09e1 (diff) | |
| parent | 578679771eafe93ccc0e8de8fc3f97a5b991b02c (diff) | |
Merge branch 'main' of https://github.com/embassy-rs/embassy into adc
Diffstat (limited to 'embassy-stm32/src/adc')
| -rw-r--r-- | embassy-stm32/src/adc/adc4.rs | 83 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/c0.rs | 507 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/f1.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/f3.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/g4.rs | 82 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 101 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v2.rs | 51 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v3.rs | 18 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v4.rs | 102 |
9 files changed, 301 insertions, 649 deletions
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 52678d1b6..babdebfdb 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -76,71 +76,17 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 76 | } | 76 | } |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 79 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 80 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 80 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 81 | #[allow(unused)] | 81 | match raw_prescaler { |
| 82 | enum Prescaler { | 82 | 0 => Presc::DIV1, |
| 83 | NotDivided, | 83 | 1 => Presc::DIV2, |
| 84 | DividedBy2, | 84 | 2..=3 => Presc::DIV4, |
| 85 | DividedBy4, | 85 | 4..=5 => Presc::DIV6, |
| 86 | DividedBy6, | 86 | 6..=7 => Presc::DIV8, |
| 87 | DividedBy8, | 87 | 8..=9 => Presc::DIV10, |
| 88 | DividedBy10, | 88 | 10..=11 => Presc::DIV12, |
| 89 | DividedBy12, | 89 | _ => unimplemented!(), |
| 90 | DividedBy16, | ||
| 91 | DividedBy32, | ||
| 92 | DividedBy64, | ||
| 93 | DividedBy128, | ||
| 94 | DividedBy256, | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Prescaler { | ||
| 98 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 99 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 100 | match raw_prescaler { | ||
| 101 | 0 => Self::NotDivided, | ||
| 102 | 1 => Self::DividedBy2, | ||
| 103 | 2..=3 => Self::DividedBy4, | ||
| 104 | 4..=5 => Self::DividedBy6, | ||
| 105 | 6..=7 => Self::DividedBy8, | ||
| 106 | 8..=9 => Self::DividedBy10, | ||
| 107 | 10..=11 => Self::DividedBy12, | ||
| 108 | _ => unimplemented!(), | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | fn divisor(&self) -> u32 { | ||
| 113 | match self { | ||
| 114 | Prescaler::NotDivided => 1, | ||
| 115 | Prescaler::DividedBy2 => 2, | ||
| 116 | Prescaler::DividedBy4 => 4, | ||
| 117 | Prescaler::DividedBy6 => 6, | ||
| 118 | Prescaler::DividedBy8 => 8, | ||
| 119 | Prescaler::DividedBy10 => 10, | ||
| 120 | Prescaler::DividedBy12 => 12, | ||
| 121 | Prescaler::DividedBy16 => 16, | ||
| 122 | Prescaler::DividedBy32 => 32, | ||
| 123 | Prescaler::DividedBy64 => 64, | ||
| 124 | Prescaler::DividedBy128 => 128, | ||
| 125 | Prescaler::DividedBy256 => 256, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | fn presc(&self) -> Presc { | ||
| 130 | match self { | ||
| 131 | Prescaler::NotDivided => Presc::DIV1, | ||
| 132 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 133 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 134 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 135 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 136 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 137 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 138 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 139 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 140 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 141 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 142 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 143 | } | ||
| 144 | } | 90 | } |
| 145 | } | 91 | } |
| 146 | 92 | ||
| @@ -212,6 +158,7 @@ foreach_adc!( | |||
| 212 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | 158 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) |
| 213 | }); | 159 | }); |
| 214 | } | 160 | } |
| 161 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 215 | _ => unreachable!(), | 162 | _ => unreachable!(), |
| 216 | } | 163 | } |
| 217 | } | 164 | } |
| @@ -283,11 +230,11 @@ impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { | |||
| 283 | /// Create a new ADC driver. | 230 | /// Create a new ADC driver. |
| 284 | pub fn new_adc4(adc: Peri<'d, T>) -> Self { | 231 | pub fn new_adc4(adc: Peri<'d, T>) -> Self { |
| 285 | rcc::enable_and_reset::<T>(); | 232 | rcc::enable_and_reset::<T>(); |
| 286 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 233 | let prescaler = from_ker_ck(T::frequency()); |
| 287 | 234 | ||
| 288 | T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 235 | T::regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 289 | 236 | ||
| 290 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 237 | let frequency = T::frequency() / prescaler; |
| 291 | info!("ADC4 frequency set to {}", frequency); | 238 | info!("ADC4 frequency set to {}", frequency); |
| 292 | 239 | ||
| 293 | if frequency > MAX_ADC_CLK_FREQ { | 240 | if frequency > MAX_ADC_CLK_FREQ { |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index bc97a7c4b..3bdca7edb 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -1,12 +1,10 @@ | |||
| 1 | use pac::adc::vals::Scandir; | ||
| 2 | #[allow(unused)] | 1 | #[allow(unused)] |
| 3 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; | 2 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; |
| 4 | use pac::adccommon::vals::Presc; | 3 | use pac::adccommon::vals::Presc; |
| 4 | use stm32_metapac::adc::vals::Scandir; | ||
| 5 | 5 | ||
| 6 | use super::{ | 6 | use super::{Adc, Instance, Resolution, blocking_delay_us}; |
| 7 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, | 7 | use crate::adc::{AnyInstance, ConversionMode}; |
| 8 | }; | ||
| 9 | use crate::dma::Transfer; | ||
| 10 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 11 | use crate::{Peri, pac, rcc}; | 9 | use crate::{Peri, pac, rcc}; |
| 12 | 10 | ||
| @@ -32,123 +30,184 @@ impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { | |||
| 32 | const CHANNEL: u8 = 9; | 30 | const CHANNEL: u8 = 9; |
| 33 | } | 31 | } |
| 34 | 32 | ||
| 35 | #[derive(Copy, Clone, Debug)] | 33 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 36 | pub enum Prescaler { | 34 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 37 | NotDivided, | 35 | match raw_prescaler { |
| 38 | DividedBy2, | 36 | 0 => Presc::DIV1, |
| 39 | DividedBy4, | 37 | 1 => Presc::DIV2, |
| 40 | DividedBy6, | 38 | 2..=3 => Presc::DIV4, |
| 41 | DividedBy8, | 39 | 4..=5 => Presc::DIV6, |
| 42 | DividedBy10, | 40 | 6..=7 => Presc::DIV8, |
| 43 | DividedBy12, | 41 | 8..=9 => Presc::DIV10, |
| 44 | DividedBy16, | 42 | 10..=11 => Presc::DIV12, |
| 45 | DividedBy32, | 43 | _ => unimplemented!(), |
| 46 | DividedBy64, | 44 | } |
| 47 | DividedBy128, | ||
| 48 | DividedBy256, | ||
| 49 | } | 45 | } |
| 50 | 46 | ||
| 51 | impl Prescaler { | 47 | impl<T: Instance> super::SealedAnyInstance for T { |
| 52 | fn from_ker_ck(frequency: Hertz) -> Self { | 48 | fn dr() -> *mut u16 { |
| 53 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 49 | T::regs().dr().as_ptr() as *mut u16 |
| 54 | match raw_prescaler { | ||
| 55 | 0 => Self::NotDivided, | ||
| 56 | 1 => Self::DividedBy2, | ||
| 57 | 2..=3 => Self::DividedBy4, | ||
| 58 | 4..=5 => Self::DividedBy6, | ||
| 59 | 6..=7 => Self::DividedBy8, | ||
| 60 | 8..=9 => Self::DividedBy10, | ||
| 61 | 10..=11 => Self::DividedBy12, | ||
| 62 | _ => unimplemented!(), | ||
| 63 | } | ||
| 64 | } | 50 | } |
| 65 | 51 | ||
| 66 | #[allow(unused)] | 52 | fn enable() { |
| 67 | fn divisor(&self) -> u32 { | 53 | T::regs().isr().modify(|w| w.set_adrdy(true)); |
| 68 | match self { | 54 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 69 | Prescaler::NotDivided => 1, | 55 | // ADRDY is "ADC ready". Wait until it will be True. |
| 70 | Prescaler::DividedBy2 => 2, | 56 | while !T::regs().isr().read().adrdy() {} |
| 71 | Prescaler::DividedBy4 => 4, | 57 | } |
| 72 | Prescaler::DividedBy6 => 6, | 58 | |
| 73 | Prescaler::DividedBy8 => 8, | 59 | fn start() { |
| 74 | Prescaler::DividedBy10 => 10, | 60 | // Start conversion |
| 75 | Prescaler::DividedBy12 => 12, | 61 | T::regs().cr().modify(|reg| { |
| 76 | Prescaler::DividedBy16 => 16, | 62 | reg.set_adstart(true); |
| 77 | Prescaler::DividedBy32 => 32, | 63 | }); |
| 78 | Prescaler::DividedBy64 => 64, | 64 | } |
| 79 | Prescaler::DividedBy128 => 128, | 65 | |
| 80 | Prescaler::DividedBy256 => 256, | 66 | fn stop() { |
| 67 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 68 | T::regs().cr().modify(|reg| { | ||
| 69 | reg.set_adstp(Adstp::STOP); | ||
| 70 | }); | ||
| 71 | while T::regs().cr().read().adstart() {} | ||
| 81 | } | 72 | } |
| 73 | |||
| 74 | // Reset configuration. | ||
| 75 | T::regs().cfgr1().modify(|reg| { | ||
| 76 | reg.set_cont(false); | ||
| 77 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 78 | reg.set_dmaen(false); | ||
| 79 | }); | ||
| 82 | } | 80 | } |
| 83 | 81 | ||
| 84 | fn presc(&self) -> Presc { | 82 | fn configure_dma(conversion_mode: super::ConversionMode) { |
| 85 | match self { | 83 | match conversion_mode { |
| 86 | Prescaler::NotDivided => Presc::DIV1, | 84 | ConversionMode::Singular => { |
| 87 | Prescaler::DividedBy2 => Presc::DIV2, | 85 | // Enable overrun control, so no new DMA requests will be generated until |
| 88 | Prescaler::DividedBy4 => Presc::DIV4, | 86 | // previous DR values is read. |
| 89 | Prescaler::DividedBy6 => Presc::DIV6, | 87 | T::regs().isr().modify(|reg| { |
| 90 | Prescaler::DividedBy8 => Presc::DIV8, | 88 | reg.set_ovr(true); |
| 91 | Prescaler::DividedBy10 => Presc::DIV10, | 89 | }); |
| 92 | Prescaler::DividedBy12 => Presc::DIV12, | 90 | |
| 93 | Prescaler::DividedBy16 => Presc::DIV16, | 91 | // Set continuous mode with oneshot dma. |
| 94 | Prescaler::DividedBy32 => Presc::DIV32, | 92 | T::regs().cfgr1().modify(|reg| { |
| 95 | Prescaler::DividedBy64 => Presc::DIV64, | 93 | reg.set_discen(false); |
| 96 | Prescaler::DividedBy128 => Presc::DIV128, | 94 | reg.set_cont(true); |
| 97 | Prescaler::DividedBy256 => Presc::DIV256, | 95 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); |
| 96 | reg.set_dmaen(true); | ||
| 97 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 98 | }); | ||
| 99 | } | ||
| 98 | } | 100 | } |
| 99 | } | 101 | } |
| 100 | } | ||
| 101 | 102 | ||
| 102 | #[cfg(feature = "defmt")] | 103 | fn configure_sequence(mut sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>, blocking: bool) { |
| 103 | impl<'a> defmt::Format for Prescaler { | 104 | T::regs().cfgr1().modify(|reg| { |
| 104 | fn format(&self, fmt: defmt::Formatter) { | 105 | reg.set_chselrmod(!blocking); |
| 105 | match self { | 106 | reg.set_align(Align::RIGHT); |
| 106 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | 107 | }); |
| 107 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | 108 | |
| 108 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | 109 | assert!(!blocking || sequence.len() == 1, "Sequence len must be 1 for blocking."); |
| 109 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | 110 | if blocking { |
| 110 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | 111 | let ((ch, _), sample_time) = sequence.next().unwrap(); |
| 111 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | 112 | // Set all channels to use SMP1 field as source. |
| 112 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | 113 | T::regs().smpr().modify(|w| { |
| 113 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | 114 | w.smpsel(0); |
| 114 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | 115 | w.set_smp1(sample_time); |
| 115 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | 116 | }); |
| 116 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | 117 | |
| 117 | Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), | 118 | // write() because we want all other bits to be set to 0. |
| 119 | T::regs().chselr().write(|w| w.set_chsel(ch.into(), true)); | ||
| 120 | } else { | ||
| 121 | let mut hw_channel_selection: u32 = 0; | ||
| 122 | let mut is_ordered_up = true; | ||
| 123 | let mut is_ordered_down = true; | ||
| 124 | let mut needs_hw = false; | ||
| 125 | |||
| 126 | assert!( | ||
| 127 | sequence.len() <= CHSELR_SQ_SIZE, | ||
| 128 | "Sequence read set cannot be more than {} in size.", | ||
| 129 | CHSELR_SQ_SIZE | ||
| 130 | ); | ||
| 131 | let mut last_sq_set: usize = 0; | ||
| 132 | let mut last_channel: u8 = 0; | ||
| 133 | T::regs().chselr_sq().write(|w| { | ||
| 134 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { | ||
| 135 | needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; | ||
| 136 | last_sq_set = i; | ||
| 137 | is_ordered_up = is_ordered_up && channel > last_channel; | ||
| 138 | is_ordered_down = is_ordered_down && channel < last_channel; | ||
| 139 | hw_channel_selection += 1 << channel; | ||
| 140 | last_channel = channel; | ||
| 141 | |||
| 142 | if !needs_hw { | ||
| 143 | w.set_sq(i, channel); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | assert!( | ||
| 148 | !needs_hw || is_ordered_up || is_ordered_down, | ||
| 149 | "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", | ||
| 150 | CHSELR_SQ_MAX_CHANNEL | ||
| 151 | ); | ||
| 152 | |||
| 153 | if needs_hw { | ||
| 154 | assert!( | ||
| 155 | hw_channel_selection != 0, | ||
| 156 | "Some bits in `hw_channel_selection` shall be set." | ||
| 157 | ); | ||
| 158 | assert!( | ||
| 159 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 160 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 161 | NUM_HW_CHANNELS | ||
| 162 | ); | ||
| 163 | |||
| 164 | T::regs().cfgr1().modify(|reg| { | ||
| 165 | reg.set_chselrmod(false); | ||
| 166 | reg.set_scandir(if is_ordered_up { Scandir::UP} else { Scandir::BACK }); | ||
| 167 | }); | ||
| 168 | |||
| 169 | // Set required channels for multi-convert. | ||
| 170 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 171 | } else { | ||
| 172 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 173 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | }); | ||
| 118 | } | 177 | } |
| 178 | |||
| 179 | // Trigger and wait for the channel selection procedure to complete. | ||
| 180 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 181 | while !T::regs().isr().read().ccrdy() {} | ||
| 119 | } | 182 | } |
| 120 | } | ||
| 121 | 183 | ||
| 122 | /// Number of samples used for averaging. | 184 | fn convert() -> u16 { |
| 123 | /// TODO: Implement hardware averaging setting. | 185 | // Set single conversion mode. |
| 124 | #[allow(unused)] | 186 | T::regs().cfgr1().modify(|w| w.set_cont(false)); |
| 125 | #[derive(Copy, Clone, Debug)] | 187 | |
| 126 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 188 | // Start conversion |
| 127 | pub enum Averaging { | 189 | T::regs().cr().modify(|reg| { |
| 128 | Disabled, | 190 | reg.set_adstart(true); |
| 129 | Samples2, | 191 | }); |
| 130 | Samples4, | 192 | |
| 131 | Samples8, | 193 | // Waiting for End Of Conversion (EOC). |
| 132 | Samples16, | 194 | while !T::regs().isr().read().eoc() {} |
| 133 | Samples32, | 195 | |
| 134 | Samples64, | 196 | T::regs().dr().read().data() as u16 |
| 135 | Samples128, | 197 | } |
| 136 | Samples256, | ||
| 137 | Samples512, | ||
| 138 | Samples1024, | ||
| 139 | } | 198 | } |
| 140 | 199 | ||
| 141 | impl<'d, T: Instance> Adc<'d, T> { | 200 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 142 | /// Create a new ADC driver. | 201 | /// Create a new ADC driver. |
| 143 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { | 202 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { |
| 144 | rcc::enable_and_reset::<T>(); | 203 | rcc::enable_and_reset::<T>(); |
| 145 | 204 | ||
| 146 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 205 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| 147 | 206 | ||
| 148 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 207 | let prescaler = from_ker_ck(T::frequency()); |
| 149 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 208 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 150 | 209 | ||
| 151 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 210 | let frequency = T::frequency() / prescaler; |
| 152 | debug!("ADC frequency set to {}", frequency); | 211 | debug!("ADC frequency set to {}", frequency); |
| 153 | 212 | ||
| 154 | if frequency > MAX_ADC_CLK_FREQ { | 213 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -158,32 +217,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 158 | ); | 217 | ); |
| 159 | } | 218 | } |
| 160 | 219 | ||
| 161 | let mut s = Self { adc }; | ||
| 162 | |||
| 163 | s.power_up(); | ||
| 164 | |||
| 165 | s.set_resolution(resolution); | ||
| 166 | |||
| 167 | s.calibrate(); | ||
| 168 | |||
| 169 | s.enable(); | ||
| 170 | |||
| 171 | s.configure_default(); | ||
| 172 | |||
| 173 | s | ||
| 174 | } | ||
| 175 | |||
| 176 | fn power_up(&mut self) { | ||
| 177 | T::regs().cr().modify(|reg| { | 220 | T::regs().cr().modify(|reg| { |
| 178 | reg.set_advregen(true); | 221 | reg.set_advregen(true); |
| 179 | }); | 222 | }); |
| 180 | 223 | ||
| 181 | // "The software must wait for the ADC voltage regulator startup time." | 224 | // "The software must wait for the ADC voltage regulator startup time." |
| 182 | // See datasheet for the value. | 225 | // See datasheet for the value. |
| 183 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 226 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); |
| 184 | } | 227 | |
| 228 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 185 | 229 | ||
| 186 | fn calibrate(&mut self) { | ||
| 187 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. | 230 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. |
| 188 | let autoff_value = T::regs().cfgr1().read().autoff(); | 231 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 189 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 232 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -197,22 +240,17 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 197 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 240 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 198 | 241 | ||
| 199 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 242 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 200 | } | ||
| 201 | 243 | ||
| 202 | fn enable(&mut self) { | 244 | T::enable(); |
| 203 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 204 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 205 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 206 | while !T::regs().isr().read().adrdy() {} | ||
| 207 | } | ||
| 208 | 245 | ||
| 209 | fn configure_default(&mut self) { | ||
| 210 | // single conversion mode, software trigger | 246 | // single conversion mode, software trigger |
| 211 | T::regs().cfgr1().modify(|w| { | 247 | T::regs().cfgr1().modify(|w| { |
| 212 | w.set_cont(false); | 248 | w.set_cont(false); |
| 213 | w.set_exten(Exten::DISABLED); | 249 | w.set_exten(Exten::DISABLED); |
| 214 | w.set_align(Align::RIGHT); | 250 | w.set_align(Align::RIGHT); |
| 215 | }); | 251 | }); |
| 252 | |||
| 253 | Self { adc } | ||
| 216 | } | 254 | } |
| 217 | 255 | ||
| 218 | /// Enable reading the voltage reference internal channel. | 256 | /// Enable reading the voltage reference internal channel. |
| @@ -233,219 +271,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 233 | 271 | ||
| 234 | super::Temperature {} | 272 | super::Temperature {} |
| 235 | } | 273 | } |
| 236 | |||
| 237 | /// Set the ADC sample time. | ||
| 238 | /// Shall only be called when ADC is not converting. | ||
| 239 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 240 | // Set all channels to use SMP1 field as source. | ||
| 241 | T::regs().smpr().modify(|w| { | ||
| 242 | w.smpsel(0); | ||
| 243 | w.set_smp1(sample_time); | ||
| 244 | }); | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Set the ADC resolution. | ||
| 248 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 249 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Perform a single conversion. | ||
| 253 | fn convert(&mut self) -> u16 { | ||
| 254 | // Set single conversion mode. | ||
| 255 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 256 | |||
| 257 | // Start conversion | ||
| 258 | T::regs().cr().modify(|reg| { | ||
| 259 | reg.set_adstart(true); | ||
| 260 | }); | ||
| 261 | |||
| 262 | // Waiting for End Of Conversion (EOC). | ||
| 263 | while !T::regs().isr().read().eoc() {} | ||
| 264 | |||
| 265 | T::regs().dr().read().data() as u16 | ||
| 266 | } | ||
| 267 | |||
| 268 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 269 | self.set_sample_time_all_channels(sample_time); | ||
| 270 | |||
| 271 | Self::configure_channel(channel); | ||
| 272 | T::regs().cfgr1().write(|reg| { | ||
| 273 | reg.set_chselrmod(false); | ||
| 274 | reg.set_align(Align::RIGHT); | ||
| 275 | }); | ||
| 276 | self.convert() | ||
| 277 | } | ||
| 278 | |||
| 279 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 280 | assert!( | ||
| 281 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 282 | "Seqenced read set cannot be more than {} in size.", | ||
| 283 | CHSELR_SQ_SIZE | ||
| 284 | ); | ||
| 285 | let mut last_sq_set: usize = 0; | ||
| 286 | T::regs().chselr_sq().write(|w| { | ||
| 287 | for (i, channel) in channel_sequence.enumerate() { | ||
| 288 | assert!( | ||
| 289 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 290 | "Sequencer only support HW channels smaller than {}.", | ||
| 291 | CHSELR_SQ_MAX_CHANNEL | ||
| 292 | ); | ||
| 293 | w.set_sq(i, channel.channel()); | ||
| 294 | last_sq_set = i; | ||
| 295 | } | ||
| 296 | |||
| 297 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 298 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 299 | } | ||
| 300 | }); | ||
| 301 | |||
| 302 | Self::apply_channel_conf() | ||
| 303 | } | ||
| 304 | |||
| 305 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 306 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 307 | // previous DR values is read. | ||
| 308 | T::regs().isr().modify(|reg| { | ||
| 309 | reg.set_ovr(true); | ||
| 310 | }); | ||
| 311 | |||
| 312 | // Set continuous mode with oneshot dma. | ||
| 313 | T::regs().cfgr1().modify(|reg| { | ||
| 314 | reg.set_discen(false); | ||
| 315 | reg.set_cont(true); | ||
| 316 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 317 | reg.set_dmaen(true); | ||
| 318 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 319 | }); | ||
| 320 | |||
| 321 | let request = rx_dma.request(); | ||
| 322 | let transfer = unsafe { | ||
| 323 | Transfer::new_read( | ||
| 324 | rx_dma, | ||
| 325 | request, | ||
| 326 | T::regs().dr().as_ptr() as *mut u16, | ||
| 327 | readings, | ||
| 328 | Default::default(), | ||
| 329 | ) | ||
| 330 | }; | ||
| 331 | |||
| 332 | // Start conversion. | ||
| 333 | T::regs().cr().modify(|reg| { | ||
| 334 | reg.set_adstart(true); | ||
| 335 | }); | ||
| 336 | |||
| 337 | // Wait for conversion sequence to finish. | ||
| 338 | transfer.await; | ||
| 339 | |||
| 340 | // Ensure conversions are finished. | ||
| 341 | Self::cancel_conversions(); | ||
| 342 | |||
| 343 | // Reset configuration. | ||
| 344 | T::regs().cfgr1().modify(|reg| { | ||
| 345 | reg.set_cont(false); | ||
| 346 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 347 | reg.set_dmaen(false); | ||
| 348 | }); | ||
| 349 | } | ||
| 350 | |||
| 351 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 352 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 353 | /// Readings won't be in the same order as in the `set`! | ||
| 354 | /// | ||
| 355 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 356 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 357 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 358 | pub async fn read_in_hw_order( | ||
| 359 | &mut self, | ||
| 360 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 361 | hw_channel_selection: u32, | ||
| 362 | scandir: Scandir, | ||
| 363 | readings: &mut [u16], | ||
| 364 | ) { | ||
| 365 | assert!( | ||
| 366 | hw_channel_selection != 0, | ||
| 367 | "Some bits in `hw_channel_selection` shall be set." | ||
| 368 | ); | ||
| 369 | assert!( | ||
| 370 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 371 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 372 | NUM_HW_CHANNELS | ||
| 373 | ); | ||
| 374 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 375 | // which is either slow or memory consuming. | ||
| 376 | // Since we have limited resources, we don't do it here. | ||
| 377 | // Not doing this have a great potential for a bug through. | ||
| 378 | |||
| 379 | // Ensure no conversions are ongoing. | ||
| 380 | Self::cancel_conversions(); | ||
| 381 | |||
| 382 | T::regs().cfgr1().modify(|reg| { | ||
| 383 | reg.set_chselrmod(false); | ||
| 384 | reg.set_scandir(scandir); | ||
| 385 | reg.set_align(Align::RIGHT); | ||
| 386 | }); | ||
| 387 | |||
| 388 | // Set required channels for multi-convert. | ||
| 389 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 390 | |||
| 391 | Self::apply_channel_conf(); | ||
| 392 | |||
| 393 | self.dma_convert(rx_dma, readings).await | ||
| 394 | } | ||
| 395 | |||
| 396 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 397 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 398 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 399 | pub async fn read( | ||
| 400 | &mut self, | ||
| 401 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 402 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 403 | readings: &mut [u16], | ||
| 404 | ) { | ||
| 405 | assert!( | ||
| 406 | channel_sequence.len() != 0, | ||
| 407 | "Asynchronous read channel sequence cannot be empty." | ||
| 408 | ); | ||
| 409 | assert!( | ||
| 410 | channel_sequence.len() == readings.len(), | ||
| 411 | "Channel sequence length must be equal to readings length." | ||
| 412 | ); | ||
| 413 | |||
| 414 | // Ensure no conversions are ongoing. | ||
| 415 | Self::cancel_conversions(); | ||
| 416 | |||
| 417 | T::regs().cfgr1().modify(|reg| { | ||
| 418 | reg.set_chselrmod(true); | ||
| 419 | reg.set_align(Align::RIGHT); | ||
| 420 | }); | ||
| 421 | |||
| 422 | Self::setup_channel_sequencer(channel_sequence); | ||
| 423 | |||
| 424 | self.dma_convert(rx_dma, readings).await | ||
| 425 | } | ||
| 426 | |||
| 427 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 428 | channel.setup(); | ||
| 429 | // write() because we want all other bits to be set to 0. | ||
| 430 | T::regs() | ||
| 431 | .chselr() | ||
| 432 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 433 | |||
| 434 | Self::apply_channel_conf(); | ||
| 435 | } | ||
| 436 | |||
| 437 | fn apply_channel_conf() { | ||
| 438 | // Trigger and wait for the channel selection procedure to complete. | ||
| 439 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 440 | while !T::regs().isr().read().ccrdy() {} | ||
| 441 | } | ||
| 442 | |||
| 443 | fn cancel_conversions() { | ||
| 444 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 445 | T::regs().cr().modify(|reg| { | ||
| 446 | reg.set_adstp(Adstp::STOP); | ||
| 447 | }); | ||
| 448 | while T::regs().cr().read().adstart() {} | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | 274 | } |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index f6220de78..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -43,7 +43,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 43 | 43 | ||
| 44 | // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) | 44 | // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) |
| 45 | // for at least two ADC clock cycles. | 45 | // for at least two ADC clock cycles. |
| 46 | blocking_delay_us((1_000_000 * 2) / Self::freq().0 + 1); | 46 | blocking_delay_us((1_000_000 * 2) / Self::freq().0 as u64 + 1); |
| 47 | 47 | ||
| 48 | // Reset calibration | 48 | // Reset calibration |
| 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); | 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); |
| @@ -58,7 +58,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | // One cycle after calibration | 60 | // One cycle after calibration |
| 61 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); | 61 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 as u64 + 1); |
| 62 | 62 | ||
| 63 | T::Interrupt::unpend(); | 63 | T::Interrupt::unpend(); |
| 64 | unsafe { T::Interrupt::enable() }; | 64 | unsafe { T::Interrupt::enable() }; |
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 4a77f3c5b..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -62,7 +62,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 62 | while T::regs().cr().read().adcal() {} | 62 | while T::regs().cr().read().adcal() {} |
| 63 | 63 | ||
| 64 | // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). | 64 | // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). |
| 65 | blocking_delay_us((1_000_000 * 4) / Self::freq().0 + 1); | 65 | blocking_delay_us((1_000_000 * 4) / Self::freq().0 as u64 + 1); |
| 66 | 66 | ||
| 67 | // Enable the adc | 67 | // Enable the adc |
| 68 | T::regs().cr().modify(|w| w.set_aden(true)); | 68 | T::regs().cr().modify(|w| w.set_aden(true)); |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 71dc8acc0..514734017 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -32,71 +32,17 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | |||
| 32 | #[cfg(stm32h7)] | 32 | #[cfg(stm32h7)] |
| 33 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 33 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 34 | 34 | ||
| 35 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 35 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 36 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 36 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 37 | #[allow(unused)] | 37 | match raw_prescaler { |
| 38 | enum Prescaler { | 38 | 0 => Presc::DIV1, |
| 39 | NotDivided, | 39 | 1 => Presc::DIV2, |
| 40 | DividedBy2, | 40 | 2..=3 => Presc::DIV4, |
| 41 | DividedBy4, | 41 | 4..=5 => Presc::DIV6, |
| 42 | DividedBy6, | 42 | 6..=7 => Presc::DIV8, |
| 43 | DividedBy8, | 43 | 8..=9 => Presc::DIV10, |
| 44 | DividedBy10, | 44 | 10..=11 => Presc::DIV12, |
| 45 | DividedBy12, | 45 | _ => unimplemented!(), |
| 46 | DividedBy16, | ||
| 47 | DividedBy32, | ||
| 48 | DividedBy64, | ||
| 49 | DividedBy128, | ||
| 50 | DividedBy256, | ||
| 51 | } | ||
| 52 | |||
| 53 | impl Prescaler { | ||
| 54 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 55 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 56 | match raw_prescaler { | ||
| 57 | 0 => Self::NotDivided, | ||
| 58 | 1 => Self::DividedBy2, | ||
| 59 | 2..=3 => Self::DividedBy4, | ||
| 60 | 4..=5 => Self::DividedBy6, | ||
| 61 | 6..=7 => Self::DividedBy8, | ||
| 62 | 8..=9 => Self::DividedBy10, | ||
| 63 | 10..=11 => Self::DividedBy12, | ||
| 64 | _ => unimplemented!(), | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | fn divisor(&self) -> u32 { | ||
| 69 | match self { | ||
| 70 | Prescaler::NotDivided => 1, | ||
| 71 | Prescaler::DividedBy2 => 2, | ||
| 72 | Prescaler::DividedBy4 => 4, | ||
| 73 | Prescaler::DividedBy6 => 6, | ||
| 74 | Prescaler::DividedBy8 => 8, | ||
| 75 | Prescaler::DividedBy10 => 10, | ||
| 76 | Prescaler::DividedBy12 => 12, | ||
| 77 | Prescaler::DividedBy16 => 16, | ||
| 78 | Prescaler::DividedBy32 => 32, | ||
| 79 | Prescaler::DividedBy64 => 64, | ||
| 80 | Prescaler::DividedBy128 => 128, | ||
| 81 | Prescaler::DividedBy256 => 256, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | fn presc(&self) -> Presc { | ||
| 86 | match self { | ||
| 87 | Prescaler::NotDivided => Presc::DIV1, | ||
| 88 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 89 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 90 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 91 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 92 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 93 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 94 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 95 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 96 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 97 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 98 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 99 | } | ||
| 100 | } | 46 | } |
| 101 | } | 47 | } |
| 102 | 48 | ||
| @@ -292,11 +238,11 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { | |||
| 292 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { | 238 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 293 | rcc::enable_and_reset::<T>(); | 239 | rcc::enable_and_reset::<T>(); |
| 294 | 240 | ||
| 295 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 241 | let prescaler = from_ker_ck(T::frequency()); |
| 296 | 242 | ||
| 297 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 243 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 298 | 244 | ||
| 299 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 245 | let frequency = T::frequency() / prescaler; |
| 300 | trace!("ADC frequency set to {}", frequency); | 246 | trace!("ADC frequency set to {}", frequency); |
| 301 | 247 | ||
| 302 | if frequency > MAX_ADC_CLK_FREQ { | 248 | if frequency > MAX_ADC_CLK_FREQ { |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 856c2e61e..5ec08a22d 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -35,6 +35,8 @@ pub use ringbuffered::RingBufferedAdc; | |||
| 35 | #[path = "adc4.rs"] | 35 | #[path = "adc4.rs"] |
| 36 | pub mod adc4; | 36 | pub mod adc4; |
| 37 | 37 | ||
| 38 | #[allow(unused)] | ||
| 39 | pub(self) use crate::block_for_us as blocking_delay_us; | ||
| 38 | pub use crate::pac::adc::vals; | 40 | pub use crate::pac::adc::vals; |
| 39 | #[cfg(not(any(adc_f1, adc_f3v3)))] | 41 | #[cfg(not(any(adc_f1, adc_f3v3)))] |
| 40 | pub use crate::pac::adc::vals::Res as Resolution; | 42 | pub use crate::pac::adc::vals::Res as Resolution; |
| @@ -88,31 +90,37 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | // Temporary patch for ADCs that have not implemented the standard iface yet | 92 | // Temporary patch for ADCs that have not implemented the standard iface yet |
| 91 | #[cfg(not(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4)))] | 93 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] |
| 92 | trait_set::trait_set! { | 94 | trait_set::trait_set! { |
| 93 | pub trait AnyInstance = Instance; | 95 | pub trait AnyInstance = Instance; |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 98 | #[cfg(any( |
| 97 | #[allow(dead_code)] | 99 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 |
| 100 | ))] | ||
| 98 | pub trait BasicAnyInstance { | 101 | pub trait BasicAnyInstance { |
| 99 | type SampleTime; | 102 | type SampleTime; |
| 100 | } | 103 | } |
| 101 | 104 | ||
| 102 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 105 | #[cfg(any( |
| 103 | #[allow(dead_code)] | 106 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 |
| 107 | ))] | ||
| 104 | pub(self) trait SealedAnyInstance: BasicAnyInstance { | 108 | pub(self) trait SealedAnyInstance: BasicAnyInstance { |
| 105 | fn enable(); | 109 | fn enable(); |
| 106 | fn start(); | 110 | fn start(); |
| 107 | fn stop(); | 111 | fn stop(); |
| 108 | fn convert() -> u16; | 112 | fn convert() -> u16; |
| 109 | fn configure_dma(conversion_mode: ConversionMode); | 113 | fn configure_dma(conversion_mode: ConversionMode); |
| 114 | #[cfg(not(adc_c0))] | ||
| 110 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | 115 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); |
| 116 | #[cfg(adc_c0)] | ||
| 117 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>, blocking: bool); | ||
| 118 | #[allow(dead_code)] | ||
| 111 | fn dr() -> *mut u16; | 119 | fn dr() -> *mut u16; |
| 112 | } | 120 | } |
| 113 | 121 | ||
| 114 | // On chips without ADC4, AnyInstance is an Instance | 122 | // On chips without ADC4, AnyInstance is an Instance |
| 115 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4))] | 123 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))] |
| 116 | #[allow(private_bounds)] | 124 | #[allow(private_bounds)] |
| 117 | pub trait AnyInstance: SealedAnyInstance + Instance {} | 125 | pub trait AnyInstance: SealedAnyInstance + Instance {} |
| 118 | 126 | ||
| @@ -122,44 +130,53 @@ pub trait AnyInstance: SealedAnyInstance + Instance {} | |||
| 122 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} | 130 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} |
| 123 | 131 | ||
| 124 | // Implement AnyInstance automatically for SealedAnyInstance | 132 | // Implement AnyInstance automatically for SealedAnyInstance |
| 125 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 133 | #[cfg(any( |
| 134 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 135 | ))] | ||
| 126 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { | 136 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { |
| 127 | type SampleTime = SampleTime; | 137 | type SampleTime = SampleTime; |
| 128 | } | 138 | } |
| 129 | 139 | ||
| 130 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 140 | #[cfg(any( |
| 141 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 142 | ))] | ||
| 131 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} | 143 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} |
| 132 | 144 | ||
| 133 | /// Performs a busy-wait delay for a specified number of microseconds. | 145 | #[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] |
| 134 | #[allow(unused)] | 146 | /// Number of samples used for averaging. |
| 135 | pub(crate) fn blocking_delay_us(us: u32) { | 147 | #[derive(Copy, Clone, Debug)] |
| 136 | cfg_if::cfg_if! { | 148 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 137 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | 149 | pub enum Averaging { |
| 138 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | 150 | Disabled, |
| 139 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | 151 | Samples2, |
| 140 | let duration = embassy_time::Duration::from_micros(us as u64); | 152 | Samples4, |
| 141 | embassy_time::block_for(duration); | 153 | Samples8, |
| 142 | } else { | 154 | Samples16, |
| 143 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 155 | Samples32, |
| 144 | let us = us as u64; | 156 | Samples64, |
| 145 | let cycles = freq * us / 1_000_000; | 157 | Samples128, |
| 146 | cortex_m::asm::delay(cycles as u32); | 158 | Samples256, |
| 147 | } | 159 | #[cfg(any(adc_c0, adc_v4, adc_u5))] |
| 148 | } | 160 | Samples512, |
| 161 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 162 | Samples1024, | ||
| 149 | } | 163 | } |
| 150 | 164 | ||
| 151 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 165 | #[cfg(any( |
| 152 | #[allow(dead_code)] | 166 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 |
| 167 | ))] | ||
| 153 | pub(crate) enum ConversionMode { | 168 | pub(crate) enum ConversionMode { |
| 154 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 169 | // Should match the cfg on "read" below |
| 170 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 155 | Singular, | 171 | Singular, |
| 156 | #[allow(dead_code)] | 172 | // Should match the cfg on "into_ring_buffered" below |
| 173 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 157 | Repeated(RegularConversionMode), | 174 | Repeated(RegularConversionMode), |
| 158 | } | 175 | } |
| 159 | 176 | ||
| 160 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 177 | // Should match the cfg on "into_ring_buffered" below |
| 178 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 161 | // Conversion mode for regular ADC channels | 179 | // Conversion mode for regular ADC channels |
| 162 | #[allow(dead_code)] | ||
| 163 | #[derive(Copy, Clone)] | 180 | #[derive(Copy, Clone)] |
| 164 | pub enum RegularConversionMode { | 181 | pub enum RegularConversionMode { |
| 165 | // Samples as fast as possible | 182 | // Samples as fast as possible |
| @@ -170,7 +187,9 @@ pub enum RegularConversionMode { | |||
| 170 | } | 187 | } |
| 171 | 188 | ||
| 172 | impl<'d, T: AnyInstance> Adc<'d, T> { | 189 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 173 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba))] | 190 | #[cfg(any( |
| 191 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 | ||
| 192 | ))] | ||
| 174 | /// Read an ADC pin. | 193 | /// Read an ADC pin. |
| 175 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { | 194 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { |
| 176 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | 195 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] |
| @@ -178,12 +197,18 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 178 | 197 | ||
| 179 | #[cfg(not(adc_v4))] | 198 | #[cfg(not(adc_v4))] |
| 180 | T::enable(); | 199 | T::enable(); |
| 200 | #[cfg(not(adc_c0))] | ||
| 181 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | 201 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); |
| 202 | #[cfg(adc_c0)] | ||
| 203 | T::configure_sequence( | ||
| 204 | [((channel.channel(), channel.is_differential()), sample_time)].into_iter(), | ||
| 205 | true, | ||
| 206 | ); | ||
| 182 | 207 | ||
| 183 | T::convert() | 208 | T::convert() |
| 184 | } | 209 | } |
| 185 | 210 | ||
| 186 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 211 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] |
| 187 | /// Read one or multiple ADC regular channels using DMA. | 212 | /// Read one or multiple ADC regular channels using DMA. |
| 188 | /// | 213 | /// |
| 189 | /// `sequence` iterator and `readings` must have the same length. | 214 | /// `sequence` iterator and `readings` must have the same length. |
| @@ -212,6 +237,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 212 | /// | 237 | /// |
| 213 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | 238 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use |
| 214 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | 239 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` |
| 240 | /// | ||
| 241 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 242 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 243 | /// | ||
| 244 | /// In addtion, on STM320, this method will panic if the channels are not passed in order | ||
| 215 | pub async fn read( | 245 | pub async fn read( |
| 216 | &mut self, | 246 | &mut self, |
| 217 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | 247 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, |
| @@ -232,8 +262,15 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 232 | T::stop(); | 262 | T::stop(); |
| 233 | T::enable(); | 263 | T::enable(); |
| 234 | 264 | ||
| 265 | #[cfg(not(adc_c0))] | ||
| 266 | T::configure_sequence( | ||
| 267 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 268 | ); | ||
| 269 | |||
| 270 | #[cfg(adc_c0)] | ||
| 235 | T::configure_sequence( | 271 | T::configure_sequence( |
| 236 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | 272 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), |
| 273 | false, | ||
| 237 | ); | 274 | ); |
| 238 | 275 | ||
| 239 | T::configure_dma(ConversionMode::Singular); | 276 | T::configure_dma(ConversionMode::Singular); |
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 4065f89a7..07eaebf7c 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -3,6 +3,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; | |||
| 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; | 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; |
| 5 | use crate::pac::adc::vals; | 5 | use crate::pac::adc::vals; |
| 6 | pub use crate::pac::adccommon::vals::Adcpre; | ||
| 6 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 7 | use crate::{Peri, rcc}; | 8 | use crate::{Peri, rcc}; |
| 8 | 9 | ||
| @@ -50,38 +51,20 @@ impl Temperature { | |||
| 50 | } | 51 | } |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | enum Prescaler { | 54 | fn from_pclk2(freq: Hertz) -> Adcpre { |
| 54 | Div2, | 55 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). |
| 55 | Div4, | 56 | #[cfg(stm32f2)] |
| 56 | Div6, | 57 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); |
| 57 | Div8, | 58 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. |
| 58 | } | 59 | #[cfg(not(stm32f2))] |
| 59 | 60 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | |
| 60 | impl Prescaler { | 61 | let raw_div = freq.0 / MAX_FREQUENCY.0; |
| 61 | fn from_pclk2(freq: Hertz) -> Self { | 62 | match raw_div { |
| 62 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). | 63 | 0..=1 => Adcpre::DIV2, |
| 63 | #[cfg(stm32f2)] | 64 | 2..=3 => Adcpre::DIV4, |
| 64 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); | 65 | 4..=5 => Adcpre::DIV6, |
| 65 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. | 66 | 6..=7 => Adcpre::DIV8, |
| 66 | #[cfg(not(stm32f2))] | 67 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), |
| 67 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | ||
| 68 | let raw_div = freq.0 / MAX_FREQUENCY.0; | ||
| 69 | match raw_div { | ||
| 70 | 0..=1 => Self::Div2, | ||
| 71 | 2..=3 => Self::Div4, | ||
| 72 | 4..=5 => Self::Div6, | ||
| 73 | 6..=7 => Self::Div8, | ||
| 74 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { | ||
| 79 | match self { | ||
| 80 | Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, | ||
| 81 | Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, | ||
| 82 | Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, | ||
| 83 | Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, | ||
| 84 | } | ||
| 85 | } | 68 | } |
| 86 | } | 69 | } |
| 87 | 70 | ||
| @@ -224,8 +207,8 @@ where | |||
| 224 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | 207 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 225 | rcc::enable_and_reset::<T>(); | 208 | rcc::enable_and_reset::<T>(); |
| 226 | 209 | ||
| 227 | let presc = Prescaler::from_pclk2(T::frequency()); | 210 | let presc = from_pclk2(T::frequency()); |
| 228 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | 211 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); |
| 229 | T::regs().cr2().modify(|reg| { | 212 | T::regs().cr2().modify(|reg| { |
| 230 | reg.set_adon(true); | 213 | reg.set_adon(true); |
| 231 | }); | 214 | }); |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index ba1afbe05..288bd77ce 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -11,7 +11,7 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | |||
| 11 | 11 | ||
| 12 | #[allow(unused_imports)] | 12 | #[allow(unused_imports)] |
| 13 | use super::SealedAdcChannel; | 13 | use super::SealedAdcChannel; |
| 14 | use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; | 14 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | use crate::adc::ConversionMode; | 15 | use crate::adc::ConversionMode; |
| 16 | use crate::{Peri, pac, rcc}; | 16 | use crate::{Peri, pac, rcc}; |
| 17 | 17 | ||
| @@ -100,21 +100,6 @@ cfg_if! { | |||
| 100 | } | 100 | } |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | /// Number of samples used for averaging. | ||
| 104 | #[derive(Copy, Clone, Debug)] | ||
| 105 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 106 | pub enum Averaging { | ||
| 107 | Disabled, | ||
| 108 | Samples2, | ||
| 109 | Samples4, | ||
| 110 | Samples8, | ||
| 111 | Samples16, | ||
| 112 | Samples32, | ||
| 113 | Samples64, | ||
| 114 | Samples128, | ||
| 115 | Samples256, | ||
| 116 | } | ||
| 117 | |||
| 118 | cfg_if! { if #[cfg(adc_g0)] { | 103 | cfg_if! { if #[cfg(adc_g0)] { |
| 119 | 104 | ||
| 120 | /// Synchronous PCLK prescaler | 105 | /// Synchronous PCLK prescaler |
| @@ -267,6 +252,7 @@ impl<T: Instance> super::SealedAnyInstance for T { | |||
| 267 | reg.set_cont(true); | 252 | reg.set_cont(true); |
| 268 | reg.set_dmacfg(match conversion_mode { | 253 | reg.set_dmacfg(match conversion_mode { |
| 269 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | 254 | ConversionMode::Singular => Dmacfg::ONE_SHOT, |
| 255 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 270 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | 256 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, |
| 271 | }); | 257 | }); |
| 272 | reg.set_dmaen(true); | 258 | reg.set_dmaen(true); |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 43eb16fd5..804e63db6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,7 +4,7 @@ use pac::adc::vals::{Adcaldif, Boost}; | |||
| 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; | 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; |
| 5 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 6 | 6 | ||
| 7 | use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; | 7 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 8 | use crate::adc::ConversionMode; | 8 | use crate::adc::ConversionMode; |
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 10 | use crate::{Peri, pac, rcc}; | 10 | use crate::{Peri, pac, rcc}; |
| @@ -59,91 +59,20 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 59 | const CHANNEL: u8 = 18; | 59 | const CHANNEL: u8 = 18; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 62 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 63 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 63 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; |
| 64 | #[allow(unused)] | 64 | match raw_prescaler { |
| 65 | enum Prescaler { | 65 | 0 => Presc::DIV1, |
| 66 | NotDivided, | 66 | 1 => Presc::DIV2, |
| 67 | DividedBy2, | 67 | 2..=3 => Presc::DIV4, |
| 68 | DividedBy4, | 68 | 4..=5 => Presc::DIV6, |
| 69 | DividedBy6, | 69 | 6..=7 => Presc::DIV8, |
| 70 | DividedBy8, | 70 | 8..=9 => Presc::DIV10, |
| 71 | DividedBy10, | 71 | 10..=11 => Presc::DIV12, |
| 72 | DividedBy12, | 72 | _ => unimplemented!(), |
| 73 | DividedBy16, | ||
| 74 | DividedBy32, | ||
| 75 | DividedBy64, | ||
| 76 | DividedBy128, | ||
| 77 | DividedBy256, | ||
| 78 | } | ||
| 79 | |||
| 80 | impl Prescaler { | ||
| 81 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 82 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 83 | match raw_prescaler { | ||
| 84 | 0 => Self::NotDivided, | ||
| 85 | 1 => Self::DividedBy2, | ||
| 86 | 2..=3 => Self::DividedBy4, | ||
| 87 | 4..=5 => Self::DividedBy6, | ||
| 88 | 6..=7 => Self::DividedBy8, | ||
| 89 | 8..=9 => Self::DividedBy10, | ||
| 90 | 10..=11 => Self::DividedBy12, | ||
| 91 | _ => unimplemented!(), | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | fn divisor(&self) -> u32 { | ||
| 96 | match self { | ||
| 97 | Prescaler::NotDivided => 1, | ||
| 98 | Prescaler::DividedBy2 => 2, | ||
| 99 | Prescaler::DividedBy4 => 4, | ||
| 100 | Prescaler::DividedBy6 => 6, | ||
| 101 | Prescaler::DividedBy8 => 8, | ||
| 102 | Prescaler::DividedBy10 => 10, | ||
| 103 | Prescaler::DividedBy12 => 12, | ||
| 104 | Prescaler::DividedBy16 => 16, | ||
| 105 | Prescaler::DividedBy32 => 32, | ||
| 106 | Prescaler::DividedBy64 => 64, | ||
| 107 | Prescaler::DividedBy128 => 128, | ||
| 108 | Prescaler::DividedBy256 => 256, | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | fn presc(&self) -> Presc { | ||
| 113 | match self { | ||
| 114 | Prescaler::NotDivided => Presc::DIV1, | ||
| 115 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 116 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 117 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 118 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 119 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 120 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 121 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 122 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 123 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 124 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 125 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 126 | } | ||
| 127 | } | 73 | } |
| 128 | } | 74 | } |
| 129 | 75 | ||
| 130 | /// Number of samples used for averaging. | ||
| 131 | #[derive(Copy, Clone, Debug)] | ||
| 132 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 133 | pub enum Averaging { | ||
| 134 | Disabled, | ||
| 135 | Samples2, | ||
| 136 | Samples4, | ||
| 137 | Samples8, | ||
| 138 | Samples16, | ||
| 139 | Samples32, | ||
| 140 | Samples64, | ||
| 141 | Samples128, | ||
| 142 | Samples256, | ||
| 143 | Samples512, | ||
| 144 | Samples1024, | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Adc configuration | 76 | /// Adc configuration |
| 148 | #[derive(Default)] | 77 | #[derive(Default)] |
| 149 | pub struct AdcConfig { | 78 | pub struct AdcConfig { |
| @@ -214,6 +143,7 @@ impl<T: Instance> super::SealedAnyInstance for T { | |||
| 214 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | 143 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); |
| 215 | }); | 144 | }); |
| 216 | } | 145 | } |
| 146 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 217 | _ => unreachable!(), | 147 | _ => unreachable!(), |
| 218 | } | 148 | } |
| 219 | } | 149 | } |
| @@ -309,11 +239,11 @@ impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { | |||
| 309 | pub fn new(adc: Peri<'d, T>) -> Self { | 239 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 310 | rcc::enable_and_reset::<T>(); | 240 | rcc::enable_and_reset::<T>(); |
| 311 | 241 | ||
| 312 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 242 | let prescaler = from_ker_ck(T::frequency()); |
| 313 | 243 | ||
| 314 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 244 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 315 | 245 | ||
| 316 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 246 | let frequency = T::frequency() / prescaler; |
| 317 | info!("ADC frequency set to {}", frequency); | 247 | info!("ADC frequency set to {}", frequency); |
| 318 | 248 | ||
| 319 | if frequency > MAX_ADC_CLK_FREQ { | 249 | if frequency > MAX_ADC_CLK_FREQ { |
