stm32f1_hal/common/dma/
circular_buffer_rx.rs

1use super::*;
2#[cfg(not(feature = "std"))]
3use crate::common::os::*;
4
5/// A buffer used for DMA cyclic data reception, continuously read by the user.
6pub struct DmaCircularBufferRx<T: Sized, CH> {
7    ch: CH,
8    buf: CircularBuffer<T>,
9}
10
11impl<T, CH> DmaCircularBufferRx<T, CH>
12where
13    T: Sized + Copy,
14    CH: DmaChannel,
15{
16    pub fn new(mut ch: CH, peripheral_addr: usize, buf_size: usize) -> Self {
17        let buf = CircularBuffer::<T>::new(buf_size);
18        ch.stop();
19        ch.set_memory_buf_for_peripheral(buf.as_slice());
20        ch.set_peripheral_address::<T>(peripheral_addr, false, false, true);
21        ch.start();
22        Self { ch, buf }
23    }
24
25    #[inline]
26    pub fn pop_slice(&mut self, max: usize) -> Option<&[T]> {
27        self.buf.pop_slice(self.ch.get_unprocessed_len(), max)
28    }
29}
30
31pub struct CircularBuffer<T> {
32    recv_buf: Vec<T>,
33    read_idx: usize,
34}
35
36impl<T: Sized + Copy> CircularBuffer<T> {
37    fn new(buf_size: usize) -> Self {
38        let mut recv_buf = Vec::<T>::with_capacity(buf_size);
39        #[allow(clippy::uninit_vec)]
40        unsafe {
41            recv_buf.set_len(buf_size)
42        }
43
44        Self {
45            recv_buf,
46            read_idx: 0,
47        }
48    }
49
50    fn pop_slice(&mut self, unprocessed_len: usize, max: usize) -> Option<&[T]> {
51        let dma_recv_idx = if unprocessed_len == 0 {
52            0
53        } else {
54            self.recv_buf.len() - unprocessed_len
55        };
56
57        if self.read_idx == dma_recv_idx {
58            return None;
59        }
60
61        let ret;
62        if dma_recv_idx < self.read_idx {
63            if max > self.recv_buf.len() - self.read_idx {
64                ret = Some(&self.recv_buf[self.read_idx..]);
65                self.read_idx = 0;
66            } else {
67                let end = self.read_idx + max;
68                ret = Some(&self.recv_buf[self.read_idx..end]);
69                self.read_idx = end;
70            }
71        } else if max > dma_recv_idx - self.read_idx {
72            ret = Some(&self.recv_buf[self.read_idx..dma_recv_idx]);
73            self.read_idx = dma_recv_idx;
74        } else {
75            let end = self.read_idx + max;
76            ret = Some(&self.recv_buf[self.read_idx..end]);
77            self.read_idx = end;
78        }
79        ret
80    }
81
82    fn as_slice(&self) -> &[T] {
83        self.recv_buf.as_slice()
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn circular_buffer() {
93        let buf_size = 13;
94        let mut buf = CircularBuffer::new(buf_size);
95        assert_eq!(buf.recv_buf.len(), buf_size);
96
97        for i in 0..buf_size {
98            buf.recv_buf[i] = i as u8;
99        }
100
101        assert_eq!(
102            buf.pop_slice(5, usize::MAX),
103            Some([0u8, 1, 2, 3, 4, 5, 6, 7].as_slice())
104        );
105        assert_eq!(buf.pop_slice(5, usize::MAX), None);
106        // Single wraparound
107        assert_eq!(
108            buf.pop_slice(0, usize::MAX),
109            Some([8u8, 9, 10, 11, 12].as_slice())
110        );
111        assert_eq!(buf.pop_slice(0, usize::MAX), None);
112        assert_eq!(buf.pop_slice(buf_size, usize::MAX), None);
113        // small max
114        assert_eq!(buf.pop_slice(5, 5), Some([0u8, 1, 2, 3, 4].as_slice()));
115        assert_eq!(buf.pop_slice(5, 5), Some([5u8, 6, 7].as_slice()));
116        assert_eq!(buf.pop_slice(5, 5), None);
117        assert_eq!(
118            buf.pop_slice(0, usize::MAX),
119            Some([8u8, 9, 10, 11, 12].as_slice())
120        );
121        // Multiple wraparounds
122        assert_eq!(
123            buf.pop_slice(5, usize::MAX),
124            Some([0u8, 1, 2, 3, 4, 5, 6, 7].as_slice())
125        );
126        assert_eq!(
127            buf.pop_slice(10, usize::MAX),
128            Some([8u8, 9, 10, 11, 12].as_slice())
129        );
130        assert_eq!(buf.pop_slice(10, usize::MAX), Some([0u8, 1, 2].as_slice()));
131        assert_eq!(buf.pop_slice(10, usize::MAX), None);
132    }
133}