stm32f1_hal/common/dma/
circular_buffer_rx.rs

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