aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/adc/ringbuffered_v3.rs180
-rw-r--r--embassy-stm32/src/adc/v3.rs138
-rw-r--r--examples/stm32l4/src/bin/adc_dma.rs51
4 files changed, 370 insertions, 0 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 4e55e1034..4d0559c0d 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51- chore: Updated stm32-metapac and stm32-data dependencies 51- chore: Updated stm32-metapac and stm32-data dependencies
52- feat: stm32/adc/v3: allow DMA reads to loop through enable channels 52- feat: stm32/adc/v3: allow DMA reads to loop through enable channels
53- fix: Fix XSPI not disabling alternate bytes when they were previously enabled 53- fix: Fix XSPI not disabling alternate bytes when they were previously enabled
54- feat: stm32/adc/v3: added support for Continuous DMA configuration
54- fix: Fix stm32h7rs init when using external flash via XSPI 55- fix: Fix stm32h7rs init when using external flash via XSPI
55- feat: Add Adc::new_with_clock() to configure analog clock 56- feat: Add Adc::new_with_clock() to configure analog clock
56- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) 57- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923))
diff --git a/embassy-stm32/src/adc/ringbuffered_v3.rs b/embassy-stm32/src/adc/ringbuffered_v3.rs
new file mode 100644
index 000000000..0aee309e3
--- /dev/null
+++ b/embassy-stm32/src/adc/ringbuffered_v3.rs
@@ -0,0 +1,180 @@
1use core::marker::PhantomData;
2use core::sync::atomic::{Ordering, compiler_fence};
3
4use embassy_hal_internal::Peri;
5
6use crate::adc::{Instance, RxDma};
7use crate::dma::{ReadableRingBuffer, TransferOptions};
8use crate::rcc;
9
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub struct OverrunError;
12
13pub struct RingBufferedAdc<'d, T: Instance> {
14 pub _phantom: PhantomData<T>,
15 pub ring_buf: ReadableRingBuffer<'d, u16>,
16}
17
18impl<'d, T: Instance> RingBufferedAdc<'d, T> {
19 pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self {
20 //dma side setup
21 let opts = TransferOptions {
22 half_transfer_ir: true,
23 circular: true,
24 ..Default::default()
25 };
26
27 // Safety: we forget the struct before this function returns.
28 let request = dma.request();
29
30 let ring_buf =
31 unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) };
32
33 Self {
34 _phantom: PhantomData,
35 ring_buf,
36 }
37 }
38
39 #[inline]
40 fn start_continuous_sampling(&mut self) {
41 // Start adc conversion
42 T::regs().cr().modify(|reg| {
43 reg.set_adstart(true);
44 });
45 self.ring_buf.start();
46 }
47
48 #[inline]
49 pub fn stop_continuous_sampling(&mut self) {
50 // Stop adc conversion
51 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
52 T::regs().cr().modify(|reg| {
53 reg.set_adstp(true);
54 });
55 while T::regs().cr().read().adstart() {}
56 }
57 }
58 pub fn disable_adc(&mut self) {
59 self.stop_continuous_sampling();
60 self.ring_buf.clear();
61 self.ring_buf.request_pause();
62 }
63
64 pub fn teardown_adc(&mut self) {
65 self.disable_adc();
66
67 //disable dma control
68 #[cfg(not(any(adc_g0, adc_u0)))]
69 T::regs().cfgr().modify(|reg| {
70 reg.set_dmaen(false);
71 });
72 #[cfg(any(adc_g0, adc_u0))]
73 T::regs().cfgr1().modify(|reg| {
74 reg.set_dmaen(false);
75 });
76
77 //TODO: do we need to cleanup the DMA request here?
78
79 compiler_fence(Ordering::SeqCst);
80 }
81
82 /// Reads measurements from the DMA ring buffer.
83 ///
84 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
85 /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
86 ///
87 /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `sequence`.
88 /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
89 /// For example if 2 channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`.
90 ///
91 /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly running tasks
92 /// Otherwise, you'll see constant Overrun errors occuring, this means that you're sampling too quickly for the task to handle, and you may need to increase the buffer size.
93 /// Example:
94 /// ```rust,ignore
95 /// const DMA_BUF_LEN: usize = 120;
96 /// use embassy_stm32::adc::{Adc, AdcChannel}
97 ///
98 /// let mut adc = Adc::new(p.ADC1);
99 /// let mut adc_pin0 = p.PA0.degrade_adc();
100 /// let mut adc_pin1 = p.PA1.degrade_adc();
101 /// let adc_dma_buf = [0u16; DMA_BUF_LEN];
102 ///
103 /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(
104 /// p.DMA2_CH0,
105 /// adc_dma_buf, [
106 /// (&mut *adc_pin0, SampleTime::CYCLES160_5),
107 /// (&mut *adc_pin1, SampleTime::CYCLES160_5),
108 /// ].into_iter());
109 ///
110 ///
111 /// let mut measurements = [0u16; DMA_BUF_LEN / 2];
112 /// loop {
113 /// match ring_buffered_adc.read(&mut measurements).await {
114 /// Ok(_) => {
115 /// defmt::info!("adc1: {}", measurements);
116 /// }
117 /// Err(e) => {
118 /// defmt::warn!("Error: {:?}", e);
119 /// }
120 /// }
121 /// }
122 /// ```
123 ///
124 ///
125 /// [`teardown_adc`]: #method.teardown_adc
126 /// [`start_continuous_sampling`]: #method.start_continuous_sampling
127 pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> {
128 assert_eq!(
129 self.ring_buf.capacity() / 2,
130 measurements.len(),
131 "Buffer size must be half the size of the ring buffer"
132 );
133
134 let r = T::regs();
135
136 // Start background receive if it was not already started
137 if !r.cr().read().adstart() {
138 self.start_continuous_sampling();
139 }
140
141 self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError)
142 }
143
144 /// Read bytes that are readily available in the ring buffer.
145 /// If no bytes are currently available in the buffer the call waits until the some
146 /// bytes are available (at least one byte and at most half the buffer size)
147 ///
148 /// Background receive is started if `start_continuous_sampling()` has not been previously called.
149 ///
150 /// Receive in the background is terminated if an error is returned.
151 /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`.
152 pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> {
153 let r = T::regs();
154
155 // Start background receive if it was not already started
156 if !r.cr().read().adstart() {
157 self.start_continuous_sampling();
158 }
159
160 loop {
161 match self.ring_buf.read(buf) {
162 Ok((0, _)) => {}
163 Ok((len, _)) => {
164 return Ok(len);
165 }
166 Err(_) => {
167 self.stop_continuous_sampling();
168 return Err(OverrunError);
169 }
170 }
171 }
172 }
173}
174
175impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
176 fn drop(&mut self) {
177 self.teardown_adc();
178 rcc::disable::<T>();
179 }
180}
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 47632263b..d9a3ce21d 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -12,6 +12,13 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc};
12use super::{ 12use super::{
13 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, 13 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us,
14}; 14};
15
16#[cfg(any(adc_v3, adc_g0, adc_u0))]
17mod ringbuffered_v3;
18
19#[cfg(any(adc_v3, adc_g0, adc_u0))]
20use ringbuffered_v3::RingBufferedAdc;
21
15use crate::dma::Transfer; 22use crate::dma::Transfer;
16use crate::{Peri, pac, rcc}; 23use crate::{Peri, pac, rcc};
17 24
@@ -559,6 +566,137 @@ impl<'d, T: Instance> Adc<'d, T> {
559 }); 566 });
560 } 567 }
561 568
569 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
570 ///
571 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
572 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
573 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
574 ///
575 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
576 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
577 ///
578 /// [`read`]: #method.read
579 #[cfg(any(adc_v3, adc_g0, adc_u0))]
580 pub fn into_ring_buffered<'a>(
581 &mut self,
582 dma: Peri<'a, impl RxDma<T>>,
583 dma_buf: &'a mut [u16],
584 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
585 ) -> RingBufferedAdc<'a, T> {
586 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
587 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
588 assert!(
589 sequence.len() <= 16,
590 "Asynchronous read sequence cannot be more than 16 in length"
591 );
592 // reset conversions and enable the adc
593 Self::cancel_conversions();
594 self.enable();
595
596 //adc side setup
597
598 // Set sequence length
599 #[cfg(not(any(adc_g0, adc_u0)))]
600 T::regs().sqr1().modify(|w| {
601 w.set_l(sequence.len() as u8 - 1);
602 });
603
604 #[cfg(adc_g0)]
605 {
606 let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new();
607
608 T::regs().chselr().write(|chselr| {
609 T::regs().smpr().write(|smpr| {
610 for (channel, sample_time) in sequence {
611 chselr.set_chsel(channel.channel.into(), true);
612 if let Some(i) = sample_times.iter().position(|&t| t == sample_time) {
613 smpr.set_smpsel(channel.channel.into(), (i as u8).into());
614 } else {
615 smpr.set_sample_time(sample_times.len(), sample_time);
616 if let Err(_) = sample_times.push(sample_time) {
617 panic!(
618 "Implementation is limited to {} unique sample times among all channels.",
619 SAMPLE_TIMES_CAPACITY
620 );
621 }
622 }
623 }
624 })
625 });
626 }
627 #[cfg(not(adc_g0))]
628 {
629 #[cfg(adc_u0)]
630 let mut channel_mask = 0;
631
632 // Configure channels and ranks
633 for (_i, (channel, sample_time)) in sequence.enumerate() {
634 Self::configure_channel(channel, sample_time);
635
636 // Each channel is sampled according to sequence
637 #[cfg(not(any(adc_g0, adc_u0)))]
638 match _i {
639 0..=3 => {
640 T::regs().sqr1().modify(|w| {
641 w.set_sq(_i, channel.channel());
642 });
643 }
644 4..=8 => {
645 T::regs().sqr2().modify(|w| {
646 w.set_sq(_i - 4, channel.channel());
647 });
648 }
649 9..=13 => {
650 T::regs().sqr3().modify(|w| {
651 w.set_sq(_i - 9, channel.channel());
652 });
653 }
654 14..=15 => {
655 T::regs().sqr4().modify(|w| {
656 w.set_sq(_i - 14, channel.channel());
657 });
658 }
659 _ => unreachable!(),
660 }
661
662 #[cfg(adc_u0)]
663 {
664 channel_mask |= 1 << channel.channel();
665 }
666 }
667
668 // On G0 and U0 enabled channels are sampled from 0 to last channel.
669 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
670 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
671 #[cfg(adc_u0)]
672 T::regs().chselr().modify(|reg| {
673 reg.set_chsel(channel_mask);
674 });
675 }
676 // Set continuous mode with Circular dma.
677 // Clear overrun flag before starting transfer.
678 T::regs().isr().modify(|reg| {
679 reg.set_ovr(true);
680 });
681
682 #[cfg(not(any(adc_g0, adc_u0)))]
683 T::regs().cfgr().modify(|reg| {
684 reg.set_discen(false);
685 reg.set_cont(true);
686 reg.set_dmacfg(Dmacfg::CIRCULAR);
687 reg.set_dmaen(true);
688 });
689 #[cfg(any(adc_g0, adc_u0))]
690 T::regs().cfgr1().modify(|reg| {
691 reg.set_discen(false);
692 reg.set_cont(true);
693 reg.set_dmacfg(Dmacfg::CIRCULAR);
694 reg.set_dmaen(true);
695 });
696
697 RingBufferedAdc::new(dma, dma_buf)
698 }
699
562 #[cfg(not(adc_g0))] 700 #[cfg(not(adc_g0))]
563 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { 701 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
564 // RM0492, RM0481, etc. 702 // RM0492, RM0481, etc.
diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs
new file mode 100644
index 000000000..7a9200edd
--- /dev/null
+++ b/examples/stm32l4/src/bin/adc_dma.rs
@@ -0,0 +1,51 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::adc::{Adc, AdcChannel, SampleTime};
8use {defmt_rtt as _, panic_probe as _};
9
10const DMA_BUF_LEN: usize = 512;
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 info!("Hello World!");
15
16 let mut config = Config::default();
17 {
18 use embassy_stm32::rcc::*;
19 config.rcc.mux.adcsel = mux::Adcsel::SYS;
20 }
21 let p = embassy_stm32::init(config);
22
23 let mut adc = Adc::new(p.ADC1);
24 let mut adc_pin0 = p.PA0.degrade_adc();
25 let mut adc_pin1 = p.PA1.degrade_adc();
26 let mut adc_dma_buf = [0u16; DMA_BUF_LEN];
27 let mut measurements = [0u16; DMA_BUF_LEN / 2];
28 let mut ring_buffered_adc = adc.into_ring_buffered(
29 p.DMA1_CH1,
30 &mut adc_dma_buf,
31 [
32 (&mut adc_pin0, SampleTime::CYCLES640_5),
33 (&mut adc_pin1, SampleTime::CYCLES640_5),
34 ]
35 .into_iter(),
36 );
37
38 info!("starting measurement loop");
39 loop {
40 match ring_buffered_adc.read(&mut measurements).await {
41 Ok(_) => {
42 //note: originally there was a print here showing all the samples,
43 //but even that takes too much time and would cause adc overruns
44 info!("adc1 first 10 samples: {}", measurements[0..10]);
45 }
46 Err(e) => {
47 warn!("Error: {:?}", e);
48 }
49 }
50 }
51}