diff options
| author | xoviat <[email protected]> | 2025-11-13 12:02:38 -0600 |
|---|---|---|
| committer | xoviat <[email protected]> | 2025-11-13 12:02:38 -0600 |
| commit | 32408f4a031dff11c1c3c8c4aeb2044f1a7e8f42 (patch) | |
| tree | 83db411fc79c4f544f77a84973370bfd6f7ada2a | |
| parent | a8de2ccb8c0721284281715ce6eda28271db3950 (diff) | |
adc: extract c0
| -rw-r--r-- | embassy-stm32/src/adc/c0.rs | 488 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 53 | ||||
| -rw-r--r-- | examples/stm32c0/src/bin/adc.rs | 18 |
3 files changed, 214 insertions, 345 deletions
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 8992d6e6e..983e7c10d 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,104 +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() {} | ||
| 182 | } | ||
| 183 | |||
| 184 | fn convert() -> u16 { | ||
| 185 | // Set single conversion mode. | ||
| 186 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 187 | |||
| 188 | // Start conversion | ||
| 189 | T::regs().cr().modify(|reg| { | ||
| 190 | reg.set_adstart(true); | ||
| 191 | }); | ||
| 192 | |||
| 193 | // Waiting for End Of Conversion (EOC). | ||
| 194 | while !T::regs().isr().read().eoc() {} | ||
| 195 | |||
| 196 | T::regs().dr().read().data() as u16 | ||
| 119 | } | 197 | } |
| 120 | } | 198 | } |
| 121 | 199 | ||
| 122 | impl<'d, T: Instance> Adc<'d, T> { | 200 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 123 | /// Create a new ADC driver. | 201 | /// Create a new ADC driver. |
| 124 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { | 202 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { |
| 125 | rcc::enable_and_reset::<T>(); | 203 | rcc::enable_and_reset::<T>(); |
| 126 | 204 | ||
| 127 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 205 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| 128 | 206 | ||
| 129 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 207 | let prescaler = from_ker_ck(T::frequency()); |
| 130 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 208 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 131 | 209 | ||
| 132 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 210 | let frequency = T::frequency() / prescaler; |
| 133 | debug!("ADC frequency set to {}", frequency); | 211 | debug!("ADC frequency set to {}", frequency); |
| 134 | 212 | ||
| 135 | if frequency > MAX_ADC_CLK_FREQ { | 213 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -139,22 +217,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 139 | ); | 217 | ); |
| 140 | } | 218 | } |
| 141 | 219 | ||
| 142 | let mut s = Self { adc }; | ||
| 143 | |||
| 144 | s.power_up(); | ||
| 145 | |||
| 146 | s.set_resolution(resolution); | ||
| 147 | |||
| 148 | s.calibrate(); | ||
| 149 | |||
| 150 | s.enable(); | ||
| 151 | |||
| 152 | s.configure_default(); | ||
| 153 | |||
| 154 | s | ||
| 155 | } | ||
| 156 | |||
| 157 | fn power_up(&mut self) { | ||
| 158 | T::regs().cr().modify(|reg| { | 220 | T::regs().cr().modify(|reg| { |
| 159 | reg.set_advregen(true); | 221 | reg.set_advregen(true); |
| 160 | }); | 222 | }); |
| @@ -162,9 +224,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 162 | // "The software must wait for the ADC voltage regulator startup time." | 224 | // "The software must wait for the ADC voltage regulator startup time." |
| 163 | // See datasheet for the value. | 225 | // See datasheet for the value. |
| 164 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 226 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); |
| 165 | } | ||
| 166 | 227 | ||
| 167 | fn calibrate(&mut self) { | 228 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); |
| 229 | |||
| 168 | // 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. |
| 169 | let autoff_value = T::regs().cfgr1().read().autoff(); | 231 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 170 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 232 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -178,22 +240,17 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 178 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 240 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 179 | 241 | ||
| 180 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 242 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 181 | } | ||
| 182 | 243 | ||
| 183 | fn enable(&mut self) { | 244 | T::enable(); |
| 184 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 185 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 186 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 187 | while !T::regs().isr().read().adrdy() {} | ||
| 188 | } | ||
| 189 | 245 | ||
| 190 | fn configure_default(&mut self) { | ||
| 191 | // single conversion mode, software trigger | 246 | // single conversion mode, software trigger |
| 192 | T::regs().cfgr1().modify(|w| { | 247 | T::regs().cfgr1().modify(|w| { |
| 193 | w.set_cont(false); | 248 | w.set_cont(false); |
| 194 | w.set_exten(Exten::DISABLED); | 249 | w.set_exten(Exten::DISABLED); |
| 195 | w.set_align(Align::RIGHT); | 250 | w.set_align(Align::RIGHT); |
| 196 | }); | 251 | }); |
| 252 | |||
| 253 | Self { adc } | ||
| 197 | } | 254 | } |
| 198 | 255 | ||
| 199 | /// Enable reading the voltage reference internal channel. | 256 | /// Enable reading the voltage reference internal channel. |
| @@ -214,219 +271,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 214 | 271 | ||
| 215 | super::Temperature {} | 272 | super::Temperature {} |
| 216 | } | 273 | } |
| 217 | |||
| 218 | /// Set the ADC sample time. | ||
| 219 | /// Shall only be called when ADC is not converting. | ||
| 220 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 221 | // Set all channels to use SMP1 field as source. | ||
| 222 | T::regs().smpr().modify(|w| { | ||
| 223 | w.smpsel(0); | ||
| 224 | w.set_smp1(sample_time); | ||
| 225 | }); | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Set the ADC resolution. | ||
| 229 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 230 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Perform a single conversion. | ||
| 234 | fn convert(&mut self) -> u16 { | ||
| 235 | // Set single conversion mode. | ||
| 236 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 237 | |||
| 238 | // Start conversion | ||
| 239 | T::regs().cr().modify(|reg| { | ||
| 240 | reg.set_adstart(true); | ||
| 241 | }); | ||
| 242 | |||
| 243 | // Waiting for End Of Conversion (EOC). | ||
| 244 | while !T::regs().isr().read().eoc() {} | ||
| 245 | |||
| 246 | T::regs().dr().read().data() as u16 | ||
| 247 | } | ||
| 248 | |||
| 249 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 250 | self.set_sample_time_all_channels(sample_time); | ||
| 251 | |||
| 252 | Self::configure_channel(channel); | ||
| 253 | T::regs().cfgr1().write(|reg| { | ||
| 254 | reg.set_chselrmod(false); | ||
| 255 | reg.set_align(Align::RIGHT); | ||
| 256 | }); | ||
| 257 | self.convert() | ||
| 258 | } | ||
| 259 | |||
| 260 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 261 | assert!( | ||
| 262 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 263 | "Seqenced read set cannot be more than {} in size.", | ||
| 264 | CHSELR_SQ_SIZE | ||
| 265 | ); | ||
| 266 | let mut last_sq_set: usize = 0; | ||
| 267 | T::regs().chselr_sq().write(|w| { | ||
| 268 | for (i, channel) in channel_sequence.enumerate() { | ||
| 269 | assert!( | ||
| 270 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 271 | "Sequencer only support HW channels smaller than {}.", | ||
| 272 | CHSELR_SQ_MAX_CHANNEL | ||
| 273 | ); | ||
| 274 | w.set_sq(i, channel.channel()); | ||
| 275 | last_sq_set = i; | ||
| 276 | } | ||
| 277 | |||
| 278 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 279 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 280 | } | ||
| 281 | }); | ||
| 282 | |||
| 283 | Self::apply_channel_conf() | ||
| 284 | } | ||
| 285 | |||
| 286 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 287 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 288 | // previous DR values is read. | ||
| 289 | T::regs().isr().modify(|reg| { | ||
| 290 | reg.set_ovr(true); | ||
| 291 | }); | ||
| 292 | |||
| 293 | // Set continuous mode with oneshot dma. | ||
| 294 | T::regs().cfgr1().modify(|reg| { | ||
| 295 | reg.set_discen(false); | ||
| 296 | reg.set_cont(true); | ||
| 297 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 298 | reg.set_dmaen(true); | ||
| 299 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 300 | }); | ||
| 301 | |||
| 302 | let request = rx_dma.request(); | ||
| 303 | let transfer = unsafe { | ||
| 304 | Transfer::new_read( | ||
| 305 | rx_dma, | ||
| 306 | request, | ||
| 307 | T::regs().dr().as_ptr() as *mut u16, | ||
| 308 | readings, | ||
| 309 | Default::default(), | ||
| 310 | ) | ||
| 311 | }; | ||
| 312 | |||
| 313 | // Start conversion. | ||
| 314 | T::regs().cr().modify(|reg| { | ||
| 315 | reg.set_adstart(true); | ||
| 316 | }); | ||
| 317 | |||
| 318 | // Wait for conversion sequence to finish. | ||
| 319 | transfer.await; | ||
| 320 | |||
| 321 | // Ensure conversions are finished. | ||
| 322 | Self::cancel_conversions(); | ||
| 323 | |||
| 324 | // Reset configuration. | ||
| 325 | T::regs().cfgr1().modify(|reg| { | ||
| 326 | reg.set_cont(false); | ||
| 327 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 328 | reg.set_dmaen(false); | ||
| 329 | }); | ||
| 330 | } | ||
| 331 | |||
| 332 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 333 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 334 | /// Readings won't be in the same order as in the `set`! | ||
| 335 | /// | ||
| 336 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 337 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 338 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 339 | pub async fn read_in_hw_order( | ||
| 340 | &mut self, | ||
| 341 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 342 | hw_channel_selection: u32, | ||
| 343 | scandir: Scandir, | ||
| 344 | readings: &mut [u16], | ||
| 345 | ) { | ||
| 346 | assert!( | ||
| 347 | hw_channel_selection != 0, | ||
| 348 | "Some bits in `hw_channel_selection` shall be set." | ||
| 349 | ); | ||
| 350 | assert!( | ||
| 351 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 352 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 353 | NUM_HW_CHANNELS | ||
| 354 | ); | ||
| 355 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 356 | // which is either slow or memory consuming. | ||
| 357 | // Since we have limited resources, we don't do it here. | ||
| 358 | // Not doing this have a great potential for a bug through. | ||
| 359 | |||
| 360 | // Ensure no conversions are ongoing. | ||
| 361 | Self::cancel_conversions(); | ||
| 362 | |||
| 363 | T::regs().cfgr1().modify(|reg| { | ||
| 364 | reg.set_chselrmod(false); | ||
| 365 | reg.set_scandir(scandir); | ||
| 366 | reg.set_align(Align::RIGHT); | ||
| 367 | }); | ||
| 368 | |||
| 369 | // Set required channels for multi-convert. | ||
| 370 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 371 | |||
| 372 | Self::apply_channel_conf(); | ||
| 373 | |||
| 374 | self.dma_convert(rx_dma, readings).await | ||
| 375 | } | ||
| 376 | |||
| 377 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 378 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 379 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 380 | pub async fn read( | ||
| 381 | &mut self, | ||
| 382 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 383 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 384 | readings: &mut [u16], | ||
| 385 | ) { | ||
| 386 | assert!( | ||
| 387 | channel_sequence.len() != 0, | ||
| 388 | "Asynchronous read channel sequence cannot be empty." | ||
| 389 | ); | ||
| 390 | assert!( | ||
| 391 | channel_sequence.len() == readings.len(), | ||
| 392 | "Channel sequence length must be equal to readings length." | ||
| 393 | ); | ||
| 394 | |||
| 395 | // Ensure no conversions are ongoing. | ||
| 396 | Self::cancel_conversions(); | ||
| 397 | |||
| 398 | T::regs().cfgr1().modify(|reg| { | ||
| 399 | reg.set_chselrmod(true); | ||
| 400 | reg.set_align(Align::RIGHT); | ||
| 401 | }); | ||
| 402 | |||
| 403 | Self::setup_channel_sequencer(channel_sequence); | ||
| 404 | |||
| 405 | self.dma_convert(rx_dma, readings).await | ||
| 406 | } | ||
| 407 | |||
| 408 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 409 | channel.setup(); | ||
| 410 | // write() because we want all other bits to be set to 0. | ||
| 411 | T::regs() | ||
| 412 | .chselr() | ||
| 413 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 414 | |||
| 415 | Self::apply_channel_conf(); | ||
| 416 | } | ||
| 417 | |||
| 418 | fn apply_channel_conf() { | ||
| 419 | // Trigger and wait for the channel selection procedure to complete. | ||
| 420 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 421 | while !T::regs().isr().read().ccrdy() {} | ||
| 422 | } | ||
| 423 | |||
| 424 | fn cancel_conversions() { | ||
| 425 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 426 | T::regs().cr().modify(|reg| { | ||
| 427 | reg.set_adstp(Adstp::STOP); | ||
| 428 | }); | ||
| 429 | while T::regs().cr().read().adstart() {} | ||
| 430 | } | ||
| 431 | } | ||
| 432 | } | 274 | } |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index fd5b94224..549f2f5a5 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -88,30 +88,37 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | // Temporary patch for ADCs that have not implemented the standard iface yet | 90 | // Temporary patch for ADCs that have not implemented the standard iface yet |
| 91 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1, adc_c0))] | 91 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] |
| 92 | trait_set::trait_set! { | 92 | trait_set::trait_set! { |
| 93 | pub trait AnyInstance = Instance; | 93 | pub trait AnyInstance = Instance; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 96 | #[cfg(any( |
| 97 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 98 | ))] | ||
| 97 | pub trait BasicAnyInstance { | 99 | pub trait BasicAnyInstance { |
| 98 | type SampleTime; | 100 | type SampleTime; |
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 103 | #[cfg(any( |
| 104 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 105 | ))] | ||
| 102 | pub(self) trait SealedAnyInstance: BasicAnyInstance { | 106 | pub(self) trait SealedAnyInstance: BasicAnyInstance { |
| 103 | fn enable(); | 107 | fn enable(); |
| 104 | fn start(); | 108 | fn start(); |
| 105 | fn stop(); | 109 | fn stop(); |
| 106 | fn convert() -> u16; | 110 | fn convert() -> u16; |
| 107 | fn configure_dma(conversion_mode: ConversionMode); | 111 | fn configure_dma(conversion_mode: ConversionMode); |
| 112 | #[cfg(not(adc_c0))] | ||
| 108 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | 113 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); |
| 114 | #[cfg(adc_c0)] | ||
| 115 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>, blocking: bool); | ||
| 109 | #[allow(dead_code)] | 116 | #[allow(dead_code)] |
| 110 | fn dr() -> *mut u16; | 117 | fn dr() -> *mut u16; |
| 111 | } | 118 | } |
| 112 | 119 | ||
| 113 | // On chips without ADC4, AnyInstance is an Instance | 120 | // On chips without ADC4, AnyInstance is an Instance |
| 114 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4))] | 121 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))] |
| 115 | #[allow(private_bounds)] | 122 | #[allow(private_bounds)] |
| 116 | pub trait AnyInstance: SealedAnyInstance + Instance {} | 123 | pub trait AnyInstance: SealedAnyInstance + Instance {} |
| 117 | 124 | ||
| @@ -121,12 +128,16 @@ pub trait AnyInstance: SealedAnyInstance + Instance {} | |||
| 121 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} | 128 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} |
| 122 | 129 | ||
| 123 | // Implement AnyInstance automatically for SealedAnyInstance | 130 | // Implement AnyInstance automatically for SealedAnyInstance |
| 124 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 131 | #[cfg(any( |
| 132 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 133 | ))] | ||
| 125 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { | 134 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { |
| 126 | type SampleTime = SampleTime; | 135 | type SampleTime = SampleTime; |
| 127 | } | 136 | } |
| 128 | 137 | ||
| 129 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] | 138 | #[cfg(any( |
| 139 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 140 | ))] | ||
| 130 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} | 141 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} |
| 131 | 142 | ||
| 132 | /// Performs a busy-wait delay for a specified number of microseconds. | 143 | /// Performs a busy-wait delay for a specified number of microseconds. |
| @@ -167,10 +178,12 @@ pub enum Averaging { | |||
| 167 | Samples1024, | 178 | Samples1024, |
| 168 | } | 179 | } |
| 169 | 180 | ||
| 170 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 181 | #[cfg(any( |
| 182 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 | ||
| 183 | ))] | ||
| 171 | pub(crate) enum ConversionMode { | 184 | pub(crate) enum ConversionMode { |
| 172 | // Should match the cfg on "read" below | 185 | // Should match the cfg on "read" below |
| 173 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 186 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] |
| 174 | Singular, | 187 | Singular, |
| 175 | // Should match the cfg on "into_ring_buffered" below | 188 | // Should match the cfg on "into_ring_buffered" below |
| 176 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | 189 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] |
| @@ -190,7 +203,9 @@ pub enum RegularConversionMode { | |||
| 190 | } | 203 | } |
| 191 | 204 | ||
| 192 | impl<'d, T: AnyInstance> Adc<'d, T> { | 205 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 193 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba))] | 206 | #[cfg(any( |
| 207 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 | ||
| 208 | ))] | ||
| 194 | /// Read an ADC pin. | 209 | /// Read an ADC pin. |
| 195 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { | 210 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { |
| 196 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | 211 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] |
| @@ -198,12 +213,18 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 198 | 213 | ||
| 199 | #[cfg(not(adc_v4))] | 214 | #[cfg(not(adc_v4))] |
| 200 | T::enable(); | 215 | T::enable(); |
| 216 | #[cfg(not(adc_c0))] | ||
| 201 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | 217 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); |
| 218 | #[cfg(adc_c0)] | ||
| 219 | T::configure_sequence( | ||
| 220 | [((channel.channel(), channel.is_differential()), sample_time)].into_iter(), | ||
| 221 | true, | ||
| 222 | ); | ||
| 202 | 223 | ||
| 203 | T::convert() | 224 | T::convert() |
| 204 | } | 225 | } |
| 205 | 226 | ||
| 206 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] | 227 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] |
| 207 | /// Read one or multiple ADC regular channels using DMA. | 228 | /// Read one or multiple ADC regular channels using DMA. |
| 208 | /// | 229 | /// |
| 209 | /// `sequence` iterator and `readings` must have the same length. | 230 | /// `sequence` iterator and `readings` must have the same length. |
| @@ -232,6 +253,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 232 | /// | 253 | /// |
| 233 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | 254 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use |
| 234 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | 255 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` |
| 256 | /// | ||
| 257 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 258 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 259 | /// | ||
| 260 | /// In addtion, on STM320, this method will panic if the channels are not passed in order | ||
| 235 | pub async fn read( | 261 | pub async fn read( |
| 236 | &mut self, | 262 | &mut self, |
| 237 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | 263 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, |
| @@ -252,8 +278,15 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 252 | T::stop(); | 278 | T::stop(); |
| 253 | T::enable(); | 279 | T::enable(); |
| 254 | 280 | ||
| 281 | #[cfg(not(adc_c0))] | ||
| 282 | T::configure_sequence( | ||
| 283 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 284 | ); | ||
| 285 | |||
| 286 | #[cfg(adc_c0)] | ||
| 255 | T::configure_sequence( | 287 | T::configure_sequence( |
| 256 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | 288 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), |
| 289 | false, | ||
| 257 | ); | 290 | ); |
| 258 | 291 | ||
| 259 | T::configure_dma(ConversionMode::Singular); | 292 | T::configure_dma(ConversionMode::Singular); |
diff --git a/examples/stm32c0/src/bin/adc.rs b/examples/stm32c0/src/bin/adc.rs index b52c9e7f8..ad597b63c 100644 --- a/examples/stm32c0/src/bin/adc.rs +++ b/examples/stm32c0/src/bin/adc.rs | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::vals::Scandir; | ||
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; |
| 8 | use embassy_stm32::peripherals::ADC1; | 7 | use embassy_stm32::peripherals::ADC1; |
| 9 | use embassy_time::Timer; | 8 | use embassy_time::Timer; |
| @@ -35,8 +34,12 @@ async fn main(_spawner: Spawner) { | |||
| 35 | blocking_vref, blocking_temp, blocing_pin0 | 34 | blocking_vref, blocking_temp, blocing_pin0 |
| 36 | ); | 35 | ); |
| 37 | 36 | ||
| 38 | let channels_seqence: [&mut AnyAdcChannel<ADC1>; 3] = [&mut vref, &mut temp, &mut pin0]; | 37 | let channels_sequence: [(&mut AnyAdcChannel<ADC1>, SampleTime); 3] = [ |
| 39 | adc.read(dma.reborrow(), channels_seqence.into_iter(), &mut read_buffer) | 38 | (&mut vref, SampleTime::CYCLES12_5), |
| 39 | (&mut temp, SampleTime::CYCLES12_5), | ||
| 40 | (&mut pin0, SampleTime::CYCLES12_5), | ||
| 41 | ]; | ||
| 42 | adc.read(dma.reborrow(), channels_sequence.into_iter(), &mut read_buffer) | ||
| 40 | .await; | 43 | .await; |
| 41 | // Values are ordered according to hardware ADC channel number! | 44 | // Values are ordered according to hardware ADC channel number! |
| 42 | info!( | 45 | info!( |
| @@ -44,15 +47,6 @@ async fn main(_spawner: Spawner) { | |||
| 44 | read_buffer[0], read_buffer[1], read_buffer[2] | 47 | read_buffer[0], read_buffer[1], read_buffer[2] |
| 45 | ); | 48 | ); |
| 46 | 49 | ||
| 47 | let hw_channel_selection: u32 = | ||
| 48 | (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); | ||
| 49 | adc.read_in_hw_order(dma.reborrow(), hw_channel_selection, Scandir::UP, &mut read_buffer) | ||
| 50 | .await; | ||
| 51 | info!( | ||
| 52 | "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", | ||
| 53 | read_buffer[2], read_buffer[1], read_buffer[0] | ||
| 54 | ); | ||
| 55 | |||
| 56 | Timer::after_millis(2000).await; | 50 | Timer::after_millis(2000).await; |
| 57 | } | 51 | } |
| 58 | } | 52 | } |
