Skip to main content

rp2040_hal/dma/
double_buffer.rs

1//! Double-buffered DMA Transfers
2
3use core::sync::atomic::{compiler_fence, Ordering};
4
5use super::{
6    single_channel::ChannelConfig, single_channel::SingleChannel, EndlessReadTarget,
7    EndlessWriteTarget, Pace, ReadTarget, WriteTarget,
8};
9
10/// Configuration for double-buffered DMA transfer
11pub struct Config<CH1: SingleChannel, CH2: SingleChannel, FROM: ReadTarget, TO: WriteTarget> {
12    ch: (CH1, CH2),
13    from: FROM,
14    to: TO,
15    bswap: bool,
16    pace: Pace,
17}
18
19impl<CH1, CH2, FROM, TO, WORD> Config<CH1, CH2, FROM, TO>
20where
21    CH1: SingleChannel,
22    CH2: SingleChannel,
23    FROM: ReadTarget<ReceivedWord = WORD>,
24    TO: WriteTarget<TransmittedWord = WORD>,
25{
26    /// Create a new configuration for double-buffered DMA transfer
27    pub fn new(ch: (CH1, CH2), from: FROM, to: TO) -> Config<CH1, CH2, FROM, TO> {
28        Config {
29            ch,
30            from,
31            to,
32            bswap: false,
33            pace: Pace::PreferSource,
34        }
35    }
36
37    /// Sets the (preferred) pace for the DMA transfers.
38    ///
39    /// Usually, the code will automatically configure the correct pace, but
40    /// peripheral-to-peripheral transfers require the user to manually select whether the source
41    /// or the sink shall be queried for the pace signal.
42    pub fn pace(&mut self, pace: Pace) {
43        self.pace = pace;
44    }
45
46    /// Enable/disable byteswapping for the DMA transfers, default value is false.
47    ///
48    /// For byte data, this has no effect. For halfword data, the two bytes of
49    /// each halfword are swapped. For word data, the four bytes of each word
50    /// are swapped to reverse order.
51    ///
52    /// This is a convenient way to change the (half-)words' byte endianness on the fly.
53    pub fn bswap(&mut self, bswap: bool) {
54        self.bswap = bswap;
55    }
56
57    /// Start the DMA transfer
58    pub fn start(mut self) -> Transfer<CH1, CH2, FROM, TO, ()> {
59        // TODO: Do we want to call any callbacks to configure source/sink?
60
61        // Make sure that memory contents reflect what the user intended.
62        // TODO: How much of the following is necessary?
63        cortex_m::asm::dsb();
64        compiler_fence(Ordering::SeqCst);
65
66        // Configure the DMA channel and start it.
67        self.ch
68            .0
69            .config(&self.from, &mut self.to, self.pace, self.bswap, None, true);
70
71        Transfer {
72            ch: self.ch,
73            from: self.from,
74            to: self.to,
75            pace: self.pace,
76            bswap: self.bswap,
77            state: (),
78            second_ch: false,
79        }
80    }
81}
82
83/// State for a double-buffered read
84pub struct ReadNext<BUF: ReadTarget>(BUF);
85/// State for a double-buffered write
86pub struct WriteNext<BUF: WriteTarget>(BUF);
87
88/// Instance of a double-buffered DMA transfer
89pub struct Transfer<CH1, CH2, FROM, TO, STATE>
90where
91    CH1: SingleChannel,
92    CH2: SingleChannel,
93    FROM: ReadTarget,
94    TO: WriteTarget,
95{
96    ch: (CH1, CH2),
97    from: FROM,
98    to: TO,
99    pace: Pace,
100    bswap: bool,
101    state: STATE,
102    second_ch: bool,
103}
104
105impl<CH1, CH2, FROM, TO, WORD, STATE> Transfer<CH1, CH2, FROM, TO, STATE>
106where
107    CH1: SingleChannel,
108    CH2: SingleChannel,
109    FROM: ReadTarget<ReceivedWord = WORD>,
110    TO: WriteTarget<TransmittedWord = WORD>,
111{
112    /// Check if an interrupt is pending for the active channel and clear the corresponding pending bit
113    pub fn check_irq0(&mut self) -> bool {
114        if self.second_ch {
115            self.ch.1.check_irq0()
116        } else {
117            self.ch.0.check_irq0()
118        }
119    }
120
121    /// Check if an interrupt is pending for the active channel and clear the corresponding pending bit
122    pub fn check_irq1(&mut self) -> bool {
123        if self.second_ch {
124            self.ch.1.check_irq1()
125        } else {
126            self.ch.0.check_irq1()
127        }
128    }
129
130    /// Check if the transfer is completed
131    pub fn is_done(&self) -> bool {
132        if self.second_ch {
133            !self.ch.1.ch().ch_ctrl_trig().read().busy().bit_is_set()
134        } else {
135            !self.ch.0.ch().ch_ctrl_trig().read().busy().bit_is_set()
136        }
137    }
138}
139
140impl<CH1, CH2, FROM, TO, WORD> Transfer<CH1, CH2, FROM, TO, ()>
141where
142    CH1: SingleChannel,
143    CH2: SingleChannel,
144    FROM: ReadTarget<ReceivedWord = WORD>,
145    TO: WriteTarget<TransmittedWord = WORD> + EndlessWriteTarget,
146{
147    /// Block until transfer completed
148    pub fn wait(self) -> (CH1, CH2, FROM, TO) {
149        while !self.is_done() {}
150
151        // Make sure that memory contents reflect what the user intended.
152        cortex_m::asm::dsb();
153        compiler_fence(Ordering::SeqCst);
154
155        // TODO: Use a tuple type?
156        (self.ch.0, self.ch.1, self.from, self.to)
157    }
158}
159
160impl<CH1, CH2, FROM, TO, WORD> Transfer<CH1, CH2, FROM, TO, ()>
161where
162    CH1: SingleChannel,
163    CH2: SingleChannel,
164    FROM: ReadTarget<ReceivedWord = WORD>,
165    TO: WriteTarget<TransmittedWord = WORD> + EndlessWriteTarget,
166{
167    /// Perform the next read of a double-buffered sequence
168    pub fn read_next<BUF: ReadTarget<ReceivedWord = WORD>>(
169        mut self,
170        buf: BUF,
171    ) -> Transfer<CH1, CH2, FROM, TO, ReadNext<BUF>> {
172        // Make sure that memory contents reflect what the user intended.
173        // TODO: How much of the following is necessary?
174        cortex_m::asm::dsb();
175        compiler_fence(Ordering::SeqCst);
176
177        // Configure the _other_ DMA channel, but do not start it yet.
178        if self.second_ch {
179            self.ch
180                .0
181                .config(&buf, &mut self.to, self.pace, self.bswap, None, false);
182        } else {
183            self.ch
184                .1
185                .config(&buf, &mut self.to, self.pace, self.bswap, None, false);
186        }
187
188        // Chain the first channel to the second.
189        if self.second_ch {
190            self.ch.1.set_chain_to_enabled(&mut self.ch.0);
191        } else {
192            self.ch.0.set_chain_to_enabled(&mut self.ch.1);
193        }
194
195        Transfer {
196            ch: self.ch,
197            from: self.from,
198            to: self.to,
199            pace: self.pace,
200            bswap: self.bswap,
201            state: ReadNext(buf),
202            second_ch: self.second_ch,
203        }
204    }
205}
206
207impl<CH1, CH2, FROM, TO, WORD> Transfer<CH1, CH2, FROM, TO, ()>
208where
209    CH1: SingleChannel,
210    CH2: SingleChannel,
211    FROM: ReadTarget<ReceivedWord = WORD> + EndlessReadTarget,
212    TO: WriteTarget<TransmittedWord = WORD>,
213{
214    /// Perform the next write of a double-buffered sequence
215    pub fn write_next<BUF: WriteTarget<TransmittedWord = WORD>>(
216        mut self,
217        mut buf: BUF,
218    ) -> Transfer<CH1, CH2, FROM, TO, WriteNext<BUF>> {
219        // Make sure that memory contents reflect what the user intended.
220        // TODO: How much of the following is necessary?
221        cortex_m::asm::dsb();
222        compiler_fence(Ordering::SeqCst);
223
224        // Configure the _other_ DMA channel, but do not start it yet.
225        if self.second_ch {
226            self.ch
227                .0
228                .config(&self.from, &mut buf, self.pace, self.bswap, None, false);
229        } else {
230            self.ch
231                .1
232                .config(&self.from, &mut buf, self.pace, self.bswap, None, false);
233        }
234
235        // Chain the first channel to the second.
236        if self.second_ch {
237            self.ch.1.set_chain_to_enabled(&mut self.ch.0);
238        } else {
239            self.ch.0.set_chain_to_enabled(&mut self.ch.1);
240        }
241
242        Transfer {
243            ch: self.ch,
244            from: self.from,
245            to: self.to,
246            pace: self.pace,
247            bswap: self.bswap,
248            state: WriteNext(buf),
249            second_ch: self.second_ch,
250        }
251    }
252}
253
254impl<CH1, CH2, FROM, TO, NEXT, WORD> Transfer<CH1, CH2, FROM, TO, ReadNext<NEXT>>
255where
256    CH1: SingleChannel,
257    CH2: SingleChannel,
258    FROM: ReadTarget<ReceivedWord = WORD>,
259    TO: WriteTarget<TransmittedWord = WORD> + EndlessWriteTarget,
260    NEXT: ReadTarget<ReceivedWord = WORD>,
261{
262    /// Block until the the transfer is complete
263    pub fn wait(self) -> (FROM, Transfer<CH1, CH2, NEXT, TO, ()>) {
264        while !self.is_done() {}
265
266        // Make sure that memory contents reflect what the user intended.
267        cortex_m::asm::dsb();
268        compiler_fence(Ordering::SeqCst);
269
270        // Invert second_ch as now the other channel is the "active" channel.
271        (
272            self.from,
273            Transfer {
274                ch: self.ch,
275                from: self.state.0,
276                to: self.to,
277                pace: self.pace,
278                bswap: self.bswap,
279                state: (),
280                second_ch: !self.second_ch,
281            },
282        )
283    }
284}
285
286impl<CH1, CH2, FROM, TO, NEXT, WORD> Transfer<CH1, CH2, FROM, TO, WriteNext<NEXT>>
287where
288    CH1: SingleChannel,
289    CH2: SingleChannel,
290    FROM: ReadTarget<ReceivedWord = WORD> + EndlessReadTarget,
291    TO: WriteTarget<TransmittedWord = WORD>,
292    NEXT: WriteTarget<TransmittedWord = WORD>,
293{
294    /// Block until transfer is complete
295    pub fn wait(self) -> (TO, Transfer<CH1, CH2, FROM, NEXT, ()>) {
296        while !self.is_done() {}
297
298        // Make sure that memory contents reflect what the user intended.
299        cortex_m::asm::dsb();
300        compiler_fence(Ordering::SeqCst);
301
302        // Invert second_ch as now the other channel is the "active" channel.
303        (
304            self.to,
305            Transfer {
306                ch: self.ch,
307                from: self.from,
308                to: self.state.0,
309                pace: self.pace,
310                bswap: self.bswap,
311                state: (),
312                second_ch: !self.second_ch,
313            },
314        )
315    }
316}