atsam4_hal/
pdc.rs

1//! PDC Traits for use with PDC-enabled peripherals
2//!
3//! Common interface to use with PDC enabled peripherals
4
5use core::marker::PhantomData;
6use core::sync::atomic::{self, compiler_fence, Ordering};
7use core::{mem, ptr};
8use embedded_dma::{ReadBuffer, WriteBuffer};
9
10/// Read transfer
11pub struct R;
12
13/// Write transfer
14pub struct W;
15
16/// DMA Receiver
17pub struct RxDma<PAYLOAD> {
18    pub(crate) payload: PAYLOAD,
19}
20
21/// DMA Transmitter
22pub struct TxDma<PAYLOAD> {
23    pub(crate) payload: PAYLOAD,
24}
25
26/// DMA Receiver/Transmitter
27pub struct RxTxDma<PAYLOAD> {
28    pub(crate) payload: PAYLOAD,
29}
30
31pub trait Receive {
32    type TransmittedWord;
33}
34
35pub trait Transmit {
36    type ReceivedWord;
37}
38
39/// Trait for DMA readings from peripheral to memory.
40pub trait ReadDma<B, RS>: Receive
41where
42    B: WriteBuffer<Word = RS>,
43    Self: core::marker::Sized + TransferPayload,
44{
45    fn read(self, buffer: B) -> Transfer<W, B, Self>;
46}
47
48/// Trait for DMA readings from peripheral to memory, start paused.
49pub trait ReadDmaPaused<B, RS>: Receive
50where
51    B: WriteBuffer<Word = RS>,
52    Self: core::marker::Sized + TransferPayload,
53{
54    fn read_paused(self, buffer: B) -> Transfer<W, B, Self>;
55}
56
57/// Trait for DMA writing from memory to peripheral.
58pub trait WriteDma<B, TS>: Transmit
59where
60    B: ReadBuffer<Word = TS>,
61    Self: core::marker::Sized + TransferPayload,
62{
63    fn write(self, buffer: B) -> Transfer<R, B, Self>;
64}
65
66/// Trait for DMA simultaneously reading and writing within one synchronous operation. Panics if both buffers are not of equal length.
67pub trait ReadWriteDma<RXB, TXB, TS>: Transmit + Receive
68where
69    RXB: WriteBuffer<Word = TS>,
70    TXB: ReadBuffer<Word = TS>,
71    Self: core::marker::Sized + TransferPayload,
72{
73    fn read_write(self, rx_buffer: RXB, tx_buffer: TXB) -> Transfer<W, (RXB, TXB), Self>;
74}
75
76/// Trait for manually specifying the DMA length used, even if the buffer is larger
77/// Panics if the buffer(s) are too small
78pub trait ReadWriteDmaLen<RXB, TXB, TS>: Transmit + Receive
79where
80    RXB: WriteBuffer<Word = TS>,
81    TXB: ReadBuffer<Word = TS>,
82    Self: core::marker::Sized + TransferPayload,
83{
84    fn read_write_len(
85        self,
86        rx_buffer: RXB,
87        rx_buf_len: usize,
88        tx_buffer: TXB,
89        tx_buf_len: usize,
90    ) -> Transfer<W, (RXB, TXB), Self>;
91}
92
93pub trait TransferPayload {
94    fn start(&mut self);
95    fn stop(&mut self);
96    fn in_progress(&self) -> bool;
97}
98
99pub struct Transfer<MODE, BUFFER, PAYLOAD>
100where
101    PAYLOAD: TransferPayload,
102{
103    _mode: PhantomData<MODE>,
104    buffer: BUFFER,
105    payload: PAYLOAD,
106}
107
108impl<BUFFER, PAYLOAD> Transfer<R, BUFFER, PAYLOAD>
109where
110    PAYLOAD: TransferPayload,
111{
112    pub(crate) fn r(buffer: BUFFER, payload: PAYLOAD) -> Self {
113        Transfer {
114            _mode: PhantomData,
115            buffer,
116            payload,
117        }
118    }
119}
120
121impl<BUFFER, PAYLOAD> Transfer<W, BUFFER, PAYLOAD>
122where
123    PAYLOAD: TransferPayload,
124{
125    pub(crate) fn w(buffer: BUFFER, payload: PAYLOAD) -> Self {
126        Transfer {
127            _mode: PhantomData,
128            buffer,
129            payload,
130        }
131    }
132}
133
134impl<MODE, BUFFER, PAYLOAD> Drop for Transfer<MODE, BUFFER, PAYLOAD>
135where
136    PAYLOAD: TransferPayload,
137{
138    fn drop(&mut self) {
139        self.payload.stop();
140        compiler_fence(Ordering::SeqCst);
141    }
142}
143
144macro_rules! pdc_transfer {
145    (
146        $DmaType:ident
147    ) => {
148        impl<BUFFER, PAYLOAD, MODE> Transfer<MODE, BUFFER, $DmaType<PAYLOAD>>
149        where
150            $DmaType<PAYLOAD>: TransferPayload,
151        {
152            pub fn is_done(&self) -> bool {
153                !self.payload.in_progress()
154            }
155
156            pub fn wait(mut self) -> (BUFFER, $DmaType<PAYLOAD>) {
157                while !self.is_done() {}
158
159                atomic::compiler_fence(Ordering::Acquire);
160
161                self.payload.stop();
162
163                // we need a read here to make the Acquire fence effective
164                // we do *not* need this if `dma.stop` does a RMW operation
165                unsafe {
166                    ptr::read_volatile(&0);
167                }
168
169                // we need a fence here for the same reason we need one in `Transfer.wait`
170                atomic::compiler_fence(Ordering::Acquire);
171
172                // `Transfer` needs to have a `Drop` implementation, because we accept
173                // managed buffers that can free their memory on drop. Because of that
174                // we can't move out of the `Transfer`'s fields, so we use `ptr::read`
175                // and `mem::forget`.
176                //
177                // NOTE(unsafe) There is no panic branch between getting the resources
178                // and forgetting `self`.
179                unsafe {
180                    let buffer = ptr::read(&self.buffer);
181                    let payload = ptr::read(&self.payload);
182                    mem::forget(self);
183                    (buffer, payload)
184                }
185            }
186
187            pub fn pause(&mut self) {
188                self.payload.stop();
189            }
190
191            pub fn resume(&mut self) {
192                self.payload.start();
193            }
194        }
195    };
196}
197
198pdc_transfer!(RxDma);
199pdc_transfer!(TxDma);
200pdc_transfer!(RxTxDma);
201
202macro_rules! pdc_rx {
203    (
204        $Periph:ident: $periph:ident, $isr:ident
205    ) => {
206        impl $Periph {
207            /// Sets the PDC receive address pointer
208            pub fn set_receive_address(&mut self, address: u32) {
209                self.$periph
210                    .rpr
211                    .write(|w| unsafe { w.rxptr().bits(address) });
212            }
213
214            /// Sets the receive increment counter
215            /// Will increment by the count * size of the peripheral data
216            pub fn set_receive_counter(&mut self, count: u16) {
217                self.$periph.rcr.write(|w| unsafe { w.rxctr().bits(count) });
218            }
219
220            /// Sets the PDC receive next address pointer
221            pub fn set_receive_next_address(&mut self, address: u32) {
222                self.$periph
223                    .rnpr
224                    .write(|w| unsafe { w.rxnptr().bits(address) });
225            }
226
227            /// Sets the receive next increment counter
228            /// Will increment by the count * size of the peripheral data
229            pub fn set_receive_next_counter(&mut self, count: u16) {
230                self.$periph
231                    .rncr
232                    .write(|w| unsafe { w.rxnctr().bits(count) });
233            }
234
235            /// Starts the PDC transfer
236            pub fn start_rx_pdc(&mut self) {
237                unsafe { self.$periph.ptcr.write_with_zero(|w| w.rxten().set_bit()) };
238            }
239
240            /// Stops the PDC transfer
241            pub fn stop_rx_pdc(&mut self) {
242                unsafe { self.$periph.ptcr.write_with_zero(|w| w.rxtdis().set_bit()) };
243            }
244
245            /// Returns `true` if the PDC is active and may be receiving data
246            pub fn active_rx_pdc(&self) -> bool {
247                self.$periph.ptsr.read().rxten().bit()
248            }
249
250            /// Returns `true` if DMA is still in progress
251            /// Uses rxbuff, which checks both receive and receive next counters to see if they are 0
252            pub fn rx_in_progress(&self) -> bool {
253                !self.$periph.$isr.read().rxbuff().bit()
254            }
255
256            /// Enable ENDRX (End of Receive) interrupt
257            /// Triggered when RCR reaches 0
258            pub fn enable_endrx_interrupt(&mut self) {
259                unsafe { self.$periph.ier.write_with_zero(|w| w.endrx().set_bit()) };
260            }
261
262            /// Disable ENDRX (End of Receive) interrupt
263            pub fn disable_endrx_interrupt(&mut self) {
264                unsafe { self.$periph.idr.write_with_zero(|w| w.endrx().set_bit()) };
265            }
266
267            /// Enable RXBUFF (Receive Buffer Full) interrupt
268            /// Triggered when RCR and RNCR reach 0
269            pub fn enable_rxbuff_interrupt(&mut self) {
270                unsafe { self.$periph.ier.write_with_zero(|w| w.rxbuff().set_bit()) };
271            }
272
273            /// Disable RXBUFF (Receive Buffer Full) interrupt
274            pub fn disable_rxbuff_interrupt(&mut self) {
275                unsafe { self.$periph.idr.write_with_zero(|w| w.rxbuff().set_bit()) };
276            }
277        }
278    };
279}
280pub(crate) use pdc_rx;
281
282macro_rules! pdc_tx {
283    (
284        $Periph:ident: $periph:ident, $isr:ident
285    ) => {
286        impl $Periph {
287            /// Sets the PDC transmit address pointer
288            pub fn set_transmit_address(&mut self, address: u32) {
289                self.$periph
290                    .tpr
291                    .write(|w| unsafe { w.txptr().bits(address) });
292            }
293
294            /// Sets the transmit increment counter
295            /// Will increment by the count * size of the peripheral data
296            pub fn set_transmit_counter(&mut self, count: u16) {
297                self.$periph.tcr.write(|w| unsafe { w.txctr().bits(count) });
298            }
299
300            /// Sets the PDC transmit next address pointer
301            pub fn set_transmit_next_address(&mut self, address: u32) {
302                self.$periph
303                    .tnpr
304                    .write(|w| unsafe { w.txnptr().bits(address) });
305            }
306
307            /// Sets the transmit next increment counter
308            /// Will increment by the count * size of the peripheral data
309            pub fn set_transmit_next_counter(&mut self, count: u16) {
310                self.$periph
311                    .tncr
312                    .write(|w| unsafe { w.txnctr().bits(count) });
313            }
314
315            /// Starts the PDC transfer
316            pub fn start_tx_pdc(&mut self) {
317                unsafe {
318                    self.$periph.ptcr.write_with_zero(|w| w.txten().set_bit());
319                }
320            }
321
322            /// Stops the PDC transfer
323            pub fn stop_tx_pdc(&mut self) {
324                unsafe {
325                    self.$periph.ptcr.write_with_zero(|w| w.txtdis().set_bit());
326                }
327            }
328
329            /// Returns `true` if the PDC is active and may be receiving data
330            pub fn active_tx_pdc(&self) -> bool {
331                self.$periph.ptsr.read().txten().bit()
332            }
333
334            /// Returns `true` if DMA is still in progress
335            /// Uses rxbuff, which checks both transmit and transmit next counters to see if they are 0
336            pub fn tx_in_progress(&self) -> bool {
337                !self.$periph.$isr.read().txbufe().bit()
338            }
339
340            /// Enable ENDRX (End of Transmit) interrupt
341            /// Triggered when RCR reaches 0
342            pub fn enable_endtx_interrupt(&mut self) {
343                unsafe {
344                    self.$periph.ier.write_with_zero(|w| w.endtx().set_bit());
345                }
346            }
347
348            /// Disable ENDRX (End of Transmit) interrupt
349            pub fn disable_endtx_interrupt(&mut self) {
350                unsafe {
351                    self.$periph.idr.write_with_zero(|w| w.endtx().set_bit());
352                }
353            }
354
355            /// Enable RXBUFF (Transmit Buffer Full) interrupt
356            /// Triggered when RCR and RNCR reach 0
357            pub fn enable_txbufe_interrupt(&mut self) {
358                unsafe {
359                    self.$periph.ier.write_with_zero(|w| w.txbufe().set_bit());
360                }
361            }
362
363            /// Disable RXBUFF (Transmit Buffer Full) interrupt
364            pub fn disable_txbufe_interrupt(&mut self) {
365                unsafe {
366                    self.$periph.idr.write_with_zero(|w| w.txbufe().set_bit());
367                }
368            }
369        }
370    };
371}
372pub(crate) use pdc_tx;
373
374macro_rules! pdc_rxtx {
375    (
376        $Periph:ident: $periph:ident
377    ) => {
378        impl $Periph {
379            /// Starts the PDC transfer (rx+tx)
380            pub fn start_rxtx_pdc(&mut self) {
381                unsafe {
382                    self.$periph
383                        .ptcr
384                        .write_with_zero(|w| w.txten().set_bit().rxten().set_bit());
385                }
386            }
387
388            /// Stops the PDC transfer (rx+tx)
389            pub fn stop_rxtx_pdc(&mut self) {
390                unsafe {
391                    self.$periph
392                        .ptcr
393                        .write_with_zero(|w| w.txtdis().set_bit().rxtdis().set_bit());
394                }
395            }
396        }
397    };
398}
399pub(crate) use pdc_rxtx;