diff options
| author | xoviat <[email protected]> | 2025-11-14 14:53:46 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-14 14:53:46 +0000 |
| commit | a6d392b24c5f010a8b5b2a00326c04b05a4ab0f0 (patch) | |
| tree | 3d2b276da21d7802659fbd581dbdd94a8ac5f61f | |
| parent | db8641740c1e4653ba3fad79744ca6f8a0a139ae (diff) | |
| parent | 945ed1200957d9a4265cc5ac811ee39dce132317 (diff) | |
Merge pull request #4884 from xoviat/adc
Fix async adc reads on h5 and others
| -rw-r--r-- | embassy-stm32/src/adc/c0.rs | 122 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 16 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v3.rs | 47 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/adc_dma.rs | 9 |
4 files changed, 80 insertions, 114 deletions
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 3bdca7edb..d87bd1ed4 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #[allow(unused)] | 1 | #[allow(unused)] |
| 2 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; | 2 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; |
| 3 | use pac::adccommon::vals::Presc; | 3 | use pac::adccommon::vals::Presc; |
| 4 | use stm32_metapac::adc::vals::Scandir; | 4 | use stm32_metapac::adc::vals::{SampleTime, Scandir}; |
| 5 | 5 | ||
| 6 | use super::{Adc, Instance, Resolution, blocking_delay_us}; | 6 | use super::{Adc, Instance, Resolution, blocking_delay_us}; |
| 7 | use crate::adc::{AnyInstance, ConversionMode}; | 7 | use crate::adc::{AnyInstance, ConversionMode}; |
| @@ -17,7 +17,6 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); | |||
| 17 | 17 | ||
| 18 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; | 18 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; |
| 19 | 19 | ||
| 20 | const NUM_HW_CHANNELS: u8 = 22; | ||
| 21 | const CHSELR_SQ_SIZE: usize = 8; | 20 | const CHSELR_SQ_SIZE: usize = 8; |
| 22 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | 21 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; |
| 23 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | 22 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; |
| @@ -100,82 +99,67 @@ impl<T: Instance> super::SealedAnyInstance for T { | |||
| 100 | } | 99 | } |
| 101 | } | 100 | } |
| 102 | 101 | ||
| 103 | fn configure_sequence(mut sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>, blocking: bool) { | 102 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) { |
| 104 | T::regs().cfgr1().modify(|reg| { | 103 | let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; |
| 105 | reg.set_chselrmod(!blocking); | 104 | let mut is_ordered_up = true; |
| 106 | reg.set_align(Align::RIGHT); | 105 | let mut is_ordered_down = true; |
| 107 | }); | ||
| 108 | 106 | ||
| 109 | assert!(!blocking || sequence.len() == 1, "Sequence len must be 1 for blocking."); | 107 | let sequence_len = sequence.len(); |
| 110 | if blocking { | 108 | let mut hw_channel_selection: u32 = 0; |
| 111 | let ((ch, _), sample_time) = sequence.next().unwrap(); | 109 | let mut last_channel: u8 = 0; |
| 112 | // Set all channels to use SMP1 field as source. | 110 | let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; |
| 113 | T::regs().smpr().modify(|w| { | 111 | |
| 114 | w.smpsel(0); | 112 | T::regs().chselr_sq().write(|w| { |
| 115 | w.set_smp1(sample_time); | 113 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { |
| 116 | }); | 114 | assert!( |
| 115 | sample_time == _sample_time || i == 0, | ||
| 116 | "C0 only supports one sample time for the sequence." | ||
| 117 | ); | ||
| 117 | 118 | ||
| 118 | // write() because we want all other bits to be set to 0. | 119 | sample_time = _sample_time; |
| 119 | T::regs().chselr().write(|w| w.set_chsel(ch.into(), true)); | 120 | needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; |
| 120 | } else { | 121 | is_ordered_up = is_ordered_up && (channel > last_channel || i == 0); |
| 121 | let mut hw_channel_selection: u32 = 0; | 122 | is_ordered_down = is_ordered_down && (channel < last_channel || i == 0); |
| 122 | let mut is_ordered_up = true; | 123 | hw_channel_selection += 1 << channel; |
| 123 | let mut is_ordered_down = true; | 124 | last_channel = channel; |
| 124 | let mut needs_hw = false; | ||
| 125 | 125 | ||
| 126 | if !needs_hw { | ||
| 127 | w.set_sq(i, channel); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | for i in sequence_len..CHSELR_SQ_SIZE { | ||
| 132 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 133 | } | ||
| 134 | }); | ||
| 135 | |||
| 136 | if needs_hw { | ||
| 126 | assert!( | 137 | assert!( |
| 127 | sequence.len() <= CHSELR_SQ_SIZE, | 138 | sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, |
| 128 | "Sequence read set cannot be more than {} in size.", | 139 | "Sequencer is required because of unordered channels, but read set cannot be more than {} in size.", |
| 129 | CHSELR_SQ_SIZE | 140 | CHSELR_SQ_SIZE |
| 130 | ); | 141 | ); |
| 131 | let mut last_sq_set: usize = 0; | 142 | assert!( |
| 132 | let mut last_channel: u8 = 0; | 143 | sequence_len > CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, |
| 133 | T::regs().chselr_sq().write(|w| { | 144 | "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", |
| 134 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { | 145 | CHSELR_SQ_MAX_CHANNEL |
| 135 | needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; | 146 | ); |
| 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 | 147 | ||
| 153 | if needs_hw { | 148 | // Set required channels for multi-convert. |
| 154 | assert!( | 149 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } |
| 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 | }); | ||
| 177 | } | 150 | } |
| 178 | 151 | ||
| 152 | T::regs().smpr().modify(|w| { | ||
| 153 | w.smpsel(0); | ||
| 154 | w.set_smp1(sample_time); | ||
| 155 | }); | ||
| 156 | |||
| 157 | T::regs().cfgr1().modify(|reg| { | ||
| 158 | reg.set_chselrmod(!needs_hw); | ||
| 159 | reg.set_align(Align::RIGHT); | ||
| 160 | reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK }); | ||
| 161 | }); | ||
| 162 | |||
| 179 | // Trigger and wait for the channel selection procedure to complete. | 163 | // Trigger and wait for the channel selection procedure to complete. |
| 180 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | 164 | T::regs().isr().modify(|w| w.set_ccrdy(false)); |
| 181 | while !T::regs().isr().read().ccrdy() {} | 165 | while !T::regs().isr().read().ccrdy() {} |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 5ec08a22d..13f8a1544 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -111,10 +111,7 @@ pub(self) trait SealedAnyInstance: BasicAnyInstance { | |||
| 111 | fn stop(); | 111 | fn stop(); |
| 112 | fn convert() -> u16; | 112 | fn convert() -> u16; |
| 113 | fn configure_dma(conversion_mode: ConversionMode); | 113 | fn configure_dma(conversion_mode: ConversionMode); |
| 114 | #[cfg(not(adc_c0))] | ||
| 115 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | 114 | 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)] | 115 | #[allow(dead_code)] |
| 119 | fn dr() -> *mut u16; | 116 | fn dr() -> *mut u16; |
| 120 | } | 117 | } |
| @@ -197,13 +194,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 197 | 194 | ||
| 198 | #[cfg(not(adc_v4))] | 195 | #[cfg(not(adc_v4))] |
| 199 | T::enable(); | 196 | T::enable(); |
| 200 | #[cfg(not(adc_c0))] | ||
| 201 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | 197 | 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 | ); | ||
| 207 | 198 | ||
| 208 | T::convert() | 199 | T::convert() |
| 209 | } | 200 | } |
| @@ -262,15 +253,8 @@ impl<'d, T: AnyInstance> Adc<'d, T> { | |||
| 262 | T::stop(); | 253 | T::stop(); |
| 263 | T::enable(); | 254 | T::enable(); |
| 264 | 255 | ||
| 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)] | ||
| 271 | T::configure_sequence( | 256 | T::configure_sequence( |
| 272 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | 257 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), |
| 273 | false, | ||
| 274 | ); | 258 | ); |
| 275 | 259 | ||
| 276 | T::configure_dma(ConversionMode::Singular); | 260 | T::configure_dma(ConversionMode::Singular); |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 78b497727..288bd77ce 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -174,38 +174,31 @@ impl<T: Instance> super::SealedAnyInstance for T { | |||
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | fn start() { | 176 | fn start() { |
| 177 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 177 | T::regs().cr().modify(|reg| { |
| 178 | { | 178 | reg.set_adstart(true); |
| 179 | // Start adc conversion | 179 | }); |
| 180 | T::regs().cr().modify(|reg| { | ||
| 181 | reg.set_adstart(true); | ||
| 182 | }); | ||
| 183 | } | ||
| 184 | } | 180 | } |
| 185 | 181 | ||
| 186 | fn stop() { | 182 | fn stop() { |
| 187 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 183 | // Ensure conversions are finished. |
| 188 | { | 184 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 189 | // Ensure conversions are finished. | 185 | T::regs().cr().modify(|reg| { |
| 190 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 186 | reg.set_adstp(true); |
| 191 | T::regs().cr().modify(|reg| { | ||
| 192 | reg.set_adstp(true); | ||
| 193 | }); | ||
| 194 | while T::regs().cr().read().adstart() {} | ||
| 195 | } | ||
| 196 | |||
| 197 | // Reset configuration. | ||
| 198 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 199 | T::regs().cfgr().modify(|reg| { | ||
| 200 | reg.set_cont(false); | ||
| 201 | reg.set_dmaen(false); | ||
| 202 | }); | ||
| 203 | #[cfg(any(adc_g0, adc_u0))] | ||
| 204 | T::regs().cfgr1().modify(|reg| { | ||
| 205 | reg.set_cont(false); | ||
| 206 | reg.set_dmaen(false); | ||
| 207 | }); | 187 | }); |
| 188 | while T::regs().cr().read().adstart() {} | ||
| 208 | } | 189 | } |
| 190 | |||
| 191 | // Reset configuration. | ||
| 192 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 193 | T::regs().cfgr().modify(|reg| { | ||
| 194 | reg.set_cont(false); | ||
| 195 | reg.set_dmaen(false); | ||
| 196 | }); | ||
| 197 | #[cfg(any(adc_g0, adc_u0))] | ||
| 198 | T::regs().cfgr1().modify(|reg| { | ||
| 199 | reg.set_cont(false); | ||
| 200 | reg.set_dmaen(false); | ||
| 201 | }); | ||
| 209 | } | 202 | } |
| 210 | 203 | ||
| 211 | /// Perform a single conversion. | 204 | /// Perform a single conversion. |
diff --git a/examples/stm32h5/src/bin/adc_dma.rs b/examples/stm32h5/src/bin/adc_dma.rs index fb9fcbc5c..2138257f7 100644 --- a/examples/stm32h5/src/bin/adc_dma.rs +++ b/examples/stm32h5/src/bin/adc_dma.rs | |||
| @@ -6,7 +6,7 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; | 6 | use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; |
| 7 | use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; | 7 | use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; |
| 8 | use embassy_stm32::{Config, Peri}; | 8 | use embassy_stm32::{Config, Peri}; |
| 9 | use embassy_time::Instant; | 9 | use embassy_time::{Duration, Instant, Ticker}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | #[embassy_executor::main] | 12 | #[embassy_executor::main] |
| @@ -76,6 +76,9 @@ async fn adc_task<'a, T: adc::Instance>( | |||
| 76 | let mut pin1 = pin1.degrade_adc(); | 76 | let mut pin1 = pin1.degrade_adc(); |
| 77 | let mut pin2 = pin2.degrade_adc(); | 77 | let mut pin2 = pin2.degrade_adc(); |
| 78 | 78 | ||
| 79 | info!("adc init"); | ||
| 80 | |||
| 81 | let mut ticker = Ticker::every(Duration::from_millis(500)); | ||
| 79 | let mut tic = Instant::now(); | 82 | let mut tic = Instant::now(); |
| 80 | let mut buffer = [0u16; 512]; | 83 | let mut buffer = [0u16; 512]; |
| 81 | loop { | 84 | loop { |
| @@ -84,11 +87,13 @@ async fn adc_task<'a, T: adc::Instance>( | |||
| 84 | adc.read( | 87 | adc.read( |
| 85 | dma.reborrow(), | 88 | dma.reborrow(), |
| 86 | [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(), | 89 | [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(), |
| 87 | &mut buffer, | 90 | &mut buffer[0..2], |
| 88 | ) | 91 | ) |
| 89 | .await; | 92 | .await; |
| 90 | let toc = Instant::now(); | 93 | let toc = Instant::now(); |
| 91 | info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros()); | 94 | info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros()); |
| 92 | tic = toc; | 95 | tic = toc; |
| 96 | |||
| 97 | ticker.next().await; | ||
| 93 | } | 98 | } |
| 94 | } | 99 | } |
