aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-14 14:53:46 +0000
committerGitHub <[email protected]>2025-11-14 14:53:46 +0000
commita6d392b24c5f010a8b5b2a00326c04b05a4ab0f0 (patch)
tree3d2b276da21d7802659fbd581dbdd94a8ac5f61f
parentdb8641740c1e4653ba3fad79744ca6f8a0a139ae (diff)
parent945ed1200957d9a4265cc5ac811ee39dce132317 (diff)
Merge pull request #4884 from xoviat/adc
Fix async adc reads on h5 and others
-rw-r--r--embassy-stm32/src/adc/c0.rs122
-rw-r--r--embassy-stm32/src/adc/mod.rs16
-rw-r--r--embassy-stm32/src/adc/v3.rs47
-rw-r--r--examples/stm32h5/src/bin/adc_dma.rs9
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)]
2use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; 2use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr};
3use pac::adccommon::vals::Presc; 3use pac::adccommon::vals::Presc;
4use stm32_metapac::adc::vals::Scandir; 4use stm32_metapac::adc::vals::{SampleTime, Scandir};
5 5
6use super::{Adc, Instance, Resolution, blocking_delay_us}; 6use super::{Adc, Instance, Resolution, blocking_delay_us};
7use crate::adc::{AnyInstance, ConversionMode}; 7use crate::adc::{AnyInstance, ConversionMode};
@@ -17,7 +17,6 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25);
17 17
18const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; 18const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20;
19 19
20const NUM_HW_CHANNELS: u8 = 22;
21const CHSELR_SQ_SIZE: usize = 8; 20const CHSELR_SQ_SIZE: usize = 8;
22const CHSELR_SQ_MAX_CHANNEL: u8 = 14; 21const CHSELR_SQ_MAX_CHANNEL: u8 = 14;
23const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; 22const 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;
6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; 6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime};
7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; 7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3};
8use embassy_stm32::{Config, Peri}; 8use embassy_stm32::{Config, Peri};
9use embassy_time::Instant; 9use embassy_time::{Duration, Instant, Ticker};
10use {defmt_rtt as _, panic_probe as _}; 10use {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}