aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/adc/ringbuffered.rs
blob: 5437866d383ce3c5d44f5c2da1735d0af18cc928 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use core::marker::PhantomData;
use core::sync::atomic::{Ordering, compiler_fence};

#[allow(unused_imports)]
use embassy_hal_internal::Peri;

use crate::adc::AnyInstance;
#[allow(unused_imports)]
use crate::adc::{Instance, RxDma};
#[allow(unused_imports)]
use crate::dma::{ReadableRingBuffer, TransferOptions};
use crate::rcc;

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct OverrunError;

pub struct RingBufferedAdc<'d, T: Instance> {
    _phantom: PhantomData<T>,
    ring_buf: ReadableRingBuffer<'d, u16>,
}

impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
    pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self {
        //dma side setup
        let opts = TransferOptions {
            half_transfer_ir: true,
            circular: true,
            ..Default::default()
        };

        // Safety: we forget the struct before this function returns.
        let request = dma.request();

        let ring_buf =
            unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) };

        Self {
            _phantom: PhantomData,
            ring_buf,
        }
    }

    /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.
    pub fn start(&mut self) {
        compiler_fence(Ordering::SeqCst);
        self.ring_buf.start();

        T::start();
    }

    pub fn stop(&mut self) {
        self.ring_buf.request_pause();

        compiler_fence(Ordering::SeqCst);
    }

    pub fn clear(&mut self) {
        self.ring_buf.clear();
    }

    /// Reads measurements from the DMA ring buffer.
    ///
    /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
    /// 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.
    ///
    /// Each call to `read` will populate the `measurements` array in the same order as the channels
    /// defined with `sequence`. 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. For example if 2
    /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`.
    ///
    /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly
    /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that
    /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size.
    /// Example:
    /// ```rust,ignore
    /// const DMA_BUF_LEN: usize = 120;
    /// use embassy_stm32::adc::{Adc, AdcChannel}
    ///
    /// let mut adc = Adc::new(p.ADC1);
    /// let mut adc_pin0 = p.PA0.degrade_adc();
    /// let mut adc_pin1 = p.PA1.degrade_adc();
    /// let adc_dma_buf = [0u16; DMA_BUF_LEN];
    ///
    /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(
    ///     p.DMA2_CH0,
    ///      adc_dma_buf, [
    ///         (&mut *adc_pin0, SampleTime::CYCLES160_5),
    ///         (&mut *adc_pin1, SampleTime::CYCLES160_5),
    ///     ].into_iter());
    ///
    ///
    /// let mut measurements = [0u16; DMA_BUF_LEN / 2];
    /// loop {
    ///     match ring_buffered_adc.read(&mut measurements).await {
    ///         Ok(_) => {
    ///             defmt::info!("adc1: {}", measurements);
    ///         }
    ///         Err(e) => {
    ///             defmt::warn!("Error: {:?}", e);
    ///         }
    ///     }
    /// }
    /// ```
    ///
    ///
    /// [`teardown_adc`]: #method.teardown_adc
    /// [`start_continuous_sampling`]: #method.start_continuous_sampling
    pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> {
        assert_eq!(
            self.ring_buf.capacity() / 2,
            measurements.len(),
            "Buffer size must be half the size of the ring buffer"
        );

        if !self.ring_buf.is_running() {
            self.start();
        }

        #[cfg(adc_v2)]
        {
            // Clear overrun flag if set.
            if T::regs().sr().read().ovr() {
                self.stop();

                return Err(OverrunError);
            }
        }

        self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError)
    }

    /// Read bytes that are readily available in the ring buffer.
    /// If no bytes are currently available in the buffer the call waits until the some
    /// bytes are available (at least one byte and at most half the buffer size)
    ///
    /// Background receive is started if `start_continuous_sampling()` has not been previously called.
    ///
    /// Receive in the background is terminated if an error is returned.
    /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`.
    pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> {
        if !self.ring_buf.is_running() {
            self.start();
        }

        #[cfg(adc_v2)]
        {
            // Clear overrun flag if set.
            if T::regs().sr().read().ovr() {
                self.stop();

                return Err(OverrunError);
            }
        }
        loop {
            match self.ring_buf.read(buf) {
                Ok((0, _)) => {}
                Ok((len, _)) => {
                    return Ok(len);
                }
                Err(_) => {
                    self.ring_buf.request_pause();

                    return Err(OverrunError);
                }
            }
        }
    }
}

impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> {
    fn drop(&mut self) {
        T::stop();

        compiler_fence(Ordering::SeqCst);

        self.ring_buf.request_pause();
        rcc::disable::<T>();
    }
}