Skip to main content

rp2040_hal/dma/
single_buffer.rs

1//! Single-buffered or peripheral-peripheral DMA Transfers
2
3use core::sync::atomic::{compiler_fence, Ordering};
4
5use super::{
6    single_channel::ChannelConfig, single_channel::SingleChannel, Pace, ReadTarget, WriteTarget,
7};
8
9/// Configuration for single-buffered DMA transfer
10pub struct Config<CH: SingleChannel, FROM: ReadTarget, TO: WriteTarget> {
11    ch: CH,
12    from: FROM,
13    to: TO,
14    pace: Pace,
15    bswap: bool,
16}
17
18impl<CH, FROM, TO, WORD> Config<CH, FROM, TO>
19where
20    CH: SingleChannel,
21    FROM: ReadTarget<ReceivedWord = WORD>,
22    TO: WriteTarget<TransmittedWord = WORD>,
23{
24    /// Create a new configuration for single-buffered DMA transfer
25    pub fn new(ch: CH, from: FROM, to: TO) -> Config<CH, FROM, TO> {
26        Config {
27            ch,
28            from,
29            to,
30            pace: Pace::PreferSource,
31            bswap: false,
32        }
33    }
34
35    /// Sets the (preferred) pace for the DMA transfers.
36    ///
37    /// Usually, the code will automatically configure the correct pace, but
38    /// peripheral-to-peripheral transfers require the user to manually select whether the source
39    /// or the sink shall be queried for the pace signal.
40    pub fn pace(&mut self, pace: Pace) {
41        self.pace = pace;
42    }
43
44    /// Enable/disable byteswapping for the DMA transfers, default value is false.
45    ///
46    /// For byte data, this has no effect. For halfword data, the two bytes of
47    /// each halfword are swapped. For word data, the four bytes of each word
48    /// are swapped to reverse order.
49    ///
50    /// This is a convenient way to change the (half-)words' byte endianness on the fly.
51    pub fn bswap(&mut self, bswap: bool) {
52        self.bswap = bswap;
53    }
54
55    /// Start the DMA transfer
56    pub fn start(mut self) -> Transfer<CH, FROM, TO> {
57        // TODO: Do we want to call any callbacks to configure source/sink?
58
59        // Make sure that memory contents reflect what the user intended.
60        // TODO: How much of the following is necessary?
61        cortex_m::asm::dsb();
62        compiler_fence(Ordering::SeqCst);
63
64        // Configure the DMA channel and start it.
65        self.ch
66            .config(&self.from, &mut self.to, self.pace, self.bswap, None, true);
67
68        Transfer {
69            ch: self.ch,
70            from: self.from,
71            to: self.to,
72        }
73    }
74}
75
76// TODO: Drop for most of these structs
77/// Instance of a single-buffered DMA transfer
78pub struct Transfer<CH: SingleChannel, FROM: ReadTarget, TO: WriteTarget> {
79    ch: CH,
80    from: FROM,
81    to: TO,
82}
83
84impl<CH, FROM, TO, WORD> Transfer<CH, FROM, TO>
85where
86    CH: SingleChannel,
87    FROM: ReadTarget<ReceivedWord = WORD>,
88    TO: WriteTarget<TransmittedWord = WORD>,
89{
90    /// Check if an interrupt is pending for this channel and clear the corresponding pending bit
91    pub fn check_irq0(&mut self) -> bool {
92        self.ch.check_irq0()
93    }
94
95    /// Check if an interrupt is pending for this channel and clear the corresponding pending bit
96    pub fn check_irq1(&mut self) -> bool {
97        self.ch.check_irq1()
98    }
99
100    /// Check if the transfer has completed.
101    pub fn is_done(&self) -> bool {
102        !self.ch.ch().ch_ctrl_trig().read().busy().bit_is_set()
103    }
104
105    /// Block until the transfer is complete, returning the channel and targets
106    pub fn wait(self) -> (CH, FROM, TO) {
107        while !self.is_done() {}
108
109        // Make sure that memory contents reflect what the user intended.
110        cortex_m::asm::dsb();
111        compiler_fence(Ordering::SeqCst);
112
113        (self.ch, self.from, self.to)
114    }
115
116    /// Aborts the current transfer, returning the channel and targets
117    pub fn abort(mut self) -> (CH, FROM, TO) {
118        let irq0_was_enabled = self.ch.is_enabled_irq0();
119        let irq1_was_enabled = self.ch.is_enabled_irq1();
120        self.ch.disable_irq0();
121        self.ch.disable_irq1();
122
123        let chan_abort = unsafe { &*crate::pac::DMA::ptr() }.chan_abort();
124        let abort_mask = (1 << self.ch.id()) as u16;
125
126        chan_abort.write(|w| unsafe { w.chan_abort().bits(abort_mask) });
127
128        while chan_abort.read().chan_abort().bits() != 0 {}
129
130        while !self.is_done() {}
131
132        self.ch.check_irq0();
133        self.ch.check_irq1();
134
135        if irq0_was_enabled {
136            self.ch.enable_irq0();
137        }
138
139        if irq1_was_enabled {
140            self.ch.enable_irq1();
141        }
142
143        // Make sure that memory contents reflect what the user intended.
144        cortex_m::asm::dsb();
145        compiler_fence(Ordering::SeqCst);
146
147        (self.ch, self.from, self.to)
148    }
149}