lpc8xx_hal/dma/
transfer.rs

1//! APIs related to DMA transfers
2
3use core::{
4    fmt,
5    sync::atomic::{compiler_fence, Ordering},
6};
7
8use crate::{
9    init_state::Enabled,
10    pac::dma0::channel::xfercfg::{DSTINC_A, SRCINC_A},
11};
12
13use super::{
14    channels::{Instance, SharedRegisters},
15    Channel,
16};
17
18/// A DMA transfer
19///
20/// A `Transfer` instance is used to represent a DMA transfer that uses a
21/// specific [`Channel`]. Instances of this can be acquired by calling a
22/// `write_all` or `read_all` method of the peripheral that should be involved
23/// in the transfer.
24///
25/// # Limitations
26///
27/// Currently, memory-to-memory transfers are not supported. If you need this
28/// features, feel free to [comment on the respective GitHub issue].
29///
30/// [`Channel`]: ../struct.Channel.html
31/// [comment on the respective GitHub issue]: https://github.com/lpc-rs/lpc8xx-hal/issues/125
32pub struct Transfer<State, C, S, D>
33where
34    C: Instance,
35{
36    _state: State,
37    payload: Payload<C, S, D>,
38}
39
40impl<C, S, D> Transfer<state::Ready, C, S, D>
41where
42    C: Instance,
43    S: Source,
44    D: Dest,
45{
46    /// Create a new DMA transfer
47    ///
48    /// # Panics
49    ///
50    /// Panics, if the length of any buffer passed to this function is 0 or
51    /// larger than 1024.
52    ///
53    /// # Limitations
54    ///
55    /// The caller must make sure to call this method only for the correct
56    /// combination of channel and target.
57    pub(crate) fn new(
58        channel: Channel<C, Enabled>,
59        source: S,
60        mut dest: D,
61    ) -> Self {
62        assert!(!source.is_empty());
63        assert!(!dest.is_full());
64        assert!(source.is_valid());
65        assert!(dest.is_valid());
66
67        compiler_fence(Ordering::SeqCst);
68
69        // Currently we don't support memory-to-memory transfers, which means
70        // exactly one participant is providing the transfer count.
71        let source_count = source.transfer_count();
72        let dest_count = dest.transfer_count();
73        let transfer_count = match (source_count, dest_count) {
74            (Some(transfer_count), None) => transfer_count,
75            (None, Some(transfer_count)) => transfer_count,
76            _ => {
77                panic!("Unsupported transfer type");
78            }
79        };
80
81        // Configure channel
82        // See user manual, section 12.6.16.
83        channel.cfg.write(|w| {
84            w.periphreqen().enabled();
85            w.hwtrigen().disabled();
86            unsafe { w.chpriority().bits(0) }
87        });
88
89        // Set channel transfer configuration
90        // See user manual, section 12.6.18.
91        channel.xfercfg.write(|w| {
92            w.cfgvalid().valid();
93            w.reload().disabled();
94            w.swtrig().not_set();
95            w.clrtrig().cleared();
96            w.setinta().no_effect();
97            w.setintb().no_effect();
98            w.width().bit_8();
99            w.srcinc().variant(source.increment());
100            w.dstinc().variant(dest.increment());
101            unsafe { w.xfercount().bits(transfer_count) }
102        });
103
104        // Configure channel descriptor
105        // See user manual, sections 12.5.2 and 12.5.3.
106        channel.descriptor.source_end = source.end_addr();
107        channel.descriptor.dest_end = dest.end_addr();
108
109        Self {
110            _state: state::Ready,
111            payload: Payload {
112                channel,
113                source,
114                dest,
115            },
116        }
117    }
118
119    /// Set INTA flag when this transfer is complete
120    ///
121    /// By default, the flag is not set. This method can be used to overwrite
122    /// that setting. Setting the flag can be used to trigger an interrupt.
123    ///
124    /// This method is only available, if the `Transfer` is in the [`Ready`]
125    /// state. Code attempting to call this method when this is not the case
126    /// will not compile.
127    ///
128    /// [`Ready`]: state/struct.Ready.html
129    pub fn set_a_when_complete(&mut self) {
130        self.payload
131            .channel
132            .xfercfg
133            .modify(|_, w| w.setinta().set())
134    }
135
136    /// Set INTB flag when this transfer is complete
137    ///
138    /// By default, the flag is not set. This method can be used to overwrite
139    /// that setting. Setting the flag can be used to trigger an interrupt.
140    ///
141    /// This method is only available, if the `Transfer` is in the [`Ready`]
142    /// state. Code attempting to call this method when this is not the case
143    /// will not compile.
144    ///
145    /// [`Ready`]: state/struct.Ready.html
146    pub fn set_b_when_complete(&mut self) {
147        self.payload
148            .channel
149            .xfercfg
150            .modify(|_, w| w.setintb().set())
151    }
152
153    /// Start the DMA transfer
154    ///
155    /// This method is only available, if the `Transfer` is in the [`Ready`]
156    /// state. Code attempting to call this method when this is not the case
157    /// will not compile.
158    ///
159    /// Consumes this `Transfer` instance and returns another one with its
160    /// `State` parameter set to [`Started`].
161    ///
162    /// [`Ready`]: state/struct.Ready.html
163    /// [`Started`]: state/struct.Started.html
164    pub fn start(self) -> Transfer<state::Started, C, S, D> {
165        let registers = SharedRegisters::<C>::new();
166
167        // Reset all flags to make sure we don't still have one set from a
168        // previous transfer.
169        registers.reset_flags();
170
171        // Enable channel
172        // See user manual, section 12.6.4.
173        registers.enable();
174
175        // Trigger transfer
176        registers.trigger();
177
178        Transfer {
179            _state: state::Started,
180            payload: self.payload,
181        }
182    }
183}
184
185impl<C, S, D> Transfer<state::Started, C, S, D>
186where
187    C: Instance,
188    S: Source,
189    D: Dest,
190{
191    /// Indicates whether transfer is currently active
192    ///
193    /// Corresponds to the channel's flag in the ACTIVE0 register.
194    ///
195    /// This method is only available, if the `Transfer` is in the [`Started`]
196    /// state. Code attempting to call this method when this is not the case
197    /// will not compile.
198    ///
199    /// [`Started`]: state/struct.Started.html
200    pub fn is_active(&self) -> bool {
201        let registers = SharedRegisters::<C>::new();
202        registers.is_active()
203    }
204
205    /// Indicates whether transfer is currently busy
206    ///
207    /// Corresponds to the channel's flag in the BUSY0 register.
208    ///
209    /// This method is only available, if the `Transfer` is in the [`Started`]
210    /// state. Code attempting to call this method when this is not the case
211    /// will not compile.
212    ///
213    /// [`Started`]: state/struct.Started.html
214    pub fn is_busy(&self) -> bool {
215        let registers = SharedRegisters::<C>::new();
216        registers.is_busy()
217    }
218
219    /// Indicates whether the error interrupt fired
220    ///
221    /// Corresponds to the channel's flag in the ERRINT0 register.
222    ///
223    /// This method is only available, if the `Transfer` is in the [`Started`]
224    /// state. Code attempting to call this method when this is not the case
225    /// will not compile.
226    ///
227    /// [`Started`]: state/struct.Started.html
228    pub fn error_interrupt_fired(&self) -> bool {
229        let registers = SharedRegisters::<C>::new();
230        registers.error_interrupt_fired()
231    }
232
233    /// Indicates whether interrupt A fired
234    ///
235    /// Corresponds to the channel's flag in the INTA0 register.
236    ///
237    /// This method is only available, if the `Transfer` is in the [`Started`]
238    /// state. Code attempting to call this method when this is not the case
239    /// will not compile.
240    ///
241    /// [`Started`]: state/struct.Started.html
242    pub fn a_interrupt_fired(&self) -> bool {
243        let registers = SharedRegisters::<C>::new();
244        registers.a_interrupt_fired()
245    }
246
247    /// Indicates whether interrupt B fired
248    ///
249    /// Corresponds to the channel's flag in the INTB0 register.
250    ///
251    /// This method is only available, if the `Transfer` is in the [`Started`]
252    /// state. Code attempting to call this method when this is not the case
253    /// will not compile.
254    ///
255    /// [`Started`]: state/struct.Started.html
256    pub fn b_interrupt_fired(&self) -> bool {
257        let registers = SharedRegisters::<C>::new();
258        registers.b_interrupt_fired()
259    }
260
261    /// Waits for the transfer to finish
262    ///
263    /// This method will block until the transfer is finished. If this is not
264    /// acceptable, you can enable an interrupt for the channel, and/or check
265    /// the channel state with the [`is_active`] method.
266    ///
267    /// This method is only available, if the `Transfer` is in the [`Started`]
268    /// state. Code attempting to call this method when this is not the case
269    /// will not compile.
270    ///
271    /// Consumes this instance of `Transfer` and returns the transfer payload,
272    /// which contains all resources that were held by this transfer.
273    ///
274    /// [`is_active`]: #method.is_active
275    /// [`Started`]: state/struct.Started.html
276    pub fn wait(
277        mut self,
278    ) -> Result<Payload<C, S, D>, (Error<S::Error, D::Error>, Payload<C, S, D>)>
279    {
280        // There's an error interrupt status register. Maybe we should check
281        // this here, but I have no idea whether that actually makes sense:
282        // 1. As of this writing, we're not enabling any interrupts. I don't
283        //    know if the flag would still be set in that case.
284        // 2. The documentation is quiet about what could cause an error in the
285        //    first place.
286        //
287        // This needs some further looking into.
288
289        let registers = SharedRegisters::<C>::new();
290
291        while registers.is_active() {}
292
293        loop {
294            match self.payload.source.finish() {
295                Err(nb::Error::WouldBlock) => continue,
296                Ok(()) => break,
297
298                Err(nb::Error::Other(error)) => {
299                    compiler_fence(Ordering::SeqCst);
300                    return Err((Error::Source(error), self.payload));
301                }
302            }
303        }
304        loop {
305            match self.payload.dest.finish() {
306                Err(nb::Error::WouldBlock) => continue,
307                Ok(()) => break,
308
309                Err(nb::Error::Other(error)) => {
310                    compiler_fence(Ordering::SeqCst);
311                    return Err((Error::Dest(error), self.payload));
312                }
313            }
314        }
315
316        compiler_fence(Ordering::SeqCst);
317
318        Ok(self.payload)
319    }
320}
321
322/// Error that can occur while waiting for the DMA transfer to finish
323#[derive(Debug)]
324pub enum Error<S, D> {
325    /// An error occured while finishing the transfer at the source
326    Source(S),
327
328    /// An error occured while finishing the transfer at the destination
329    Dest(D),
330}
331
332/// The payload of a [`Transfer`]
333///
334/// These are resources that must be moved into a [`Transfer`] while it is going
335/// on, and will be returned to the user once it has finished.
336///
337/// [`Transfer`]: struct.Transfer.html
338pub struct Payload<C, S, D>
339where
340    C: Instance,
341{
342    /// The channel used for this transfer
343    pub channel: Channel<C, Enabled>,
344
345    /// The source of the transfer
346    ///
347    /// Can be a peripheral or a buffer.
348    pub source: S,
349
350    /// The destination of the transfer
351    ///
352    /// Can be a peripheral or a buffer.
353    pub dest: D,
354}
355
356impl<C, S, D> fmt::Debug for Payload<C, S, D>
357where
358    C: Instance,
359{
360    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
361        // Placeholder implementation. Trying to do this properly runs into many
362        // hurdles in many places, mainly because `Debug` isn't available for
363        // many svd2rust-generated types.
364        write!(f, "Payload")
365    }
366}
367
368/// The source of a DMA transfer
369///
370/// This trait's methods are intended for internal use only. It is implemented
371/// for immutable static buffers and peripherals that support being read from
372/// using DMA.
373pub trait Source: crate::private::Sealed {
374    /// The error that can occur while finishing the transfer
375    type Error;
376
377    /// Indicates whether the source is valid
378    ///
379    /// Buffers are valid, if they have a length of 1024 or less. Peripherals
380    /// are always valid.
381    fn is_valid(&self) -> bool;
382
383    /// Indicates whether the source is empty
384    ///
385    /// Buffers are empty, if they have a length of 0. Peripherals are never
386    /// empty.
387    fn is_empty(&self) -> bool;
388
389    /// The address increment during the transfer
390    ///
391    /// Buffers will return the word size here. Peripherals will indicate no
392    /// increment.
393    fn increment(&self) -> SRCINC_A;
394
395    /// The transfer count, as defined by XFERCFG.XFERCOUNT
396    ///
397    /// Only buffers will return a value here, and only if `is_empty` returns
398    /// false. Peripherals will always return `None`.
399    fn transfer_count(&self) -> Option<u16>;
400
401    /// The end address
402    ///
403    /// This is not the actual end of the buffer, but the starting address plus
404    /// `transfer_count` times address increment. See LPC845 user manual,
405    /// section 16.5.2, for example.
406    fn end_addr(&self) -> *const u8;
407
408    /// Tell the source to finish the transfer
409    fn finish(&mut self) -> nb::Result<(), Self::Error>;
410}
411
412/// A destination for a DMA transfer
413///
414/// This trait's methods are intended for internal use only. It is implemented
415/// for mutable static buffers and peripherals that support being written to
416/// using DMA.
417pub trait Dest: crate::private::Sealed {
418    /// The error that can occur while finishing the transfer
419    type Error;
420
421    /// Indicates whether the destination is valid
422    ///
423    /// Buffers are valid if they have a length of 1024 or less. Peripherals are
424    /// always valid.
425    fn is_valid(&self) -> bool;
426
427    /// Indicates whether the destination is full
428    ///
429    /// Buffers are empty, if they have a length of 0. Peripherals are never
430    /// empty.
431    fn is_full(&self) -> bool;
432
433    /// The address increment during the transfer
434    ///
435    /// Buffers will return the word size here. Peripherals will indicate no
436    /// increment.
437    fn increment(&self) -> DSTINC_A;
438
439    /// The transfer count, as defined by XFERCFG.XFERCOUNT
440    ///
441    /// Only buffers will return a value here, and only if `if_full` returns
442    /// `false`. Peripherals will always return `None`.
443    fn transfer_count(&self) -> Option<u16>;
444
445    /// The end address
446    ///
447    /// This is not the actual end of the buffer, but the starting address plus
448    /// `transfer_count` times address increment. See LPC845 user manual,
449    /// section 16.5.2, for example.
450    fn end_addr(&mut self) -> *mut u8;
451
452    /// Tell the destination to finish the transfer
453    fn finish(&mut self) -> nb::Result<(), Self::Error>;
454}
455
456/// Types representing the states of a DMA transfer
457pub mod state {
458    /// Indicates that a transfer is ready to be started
459    ///
460    /// Used for the `State` type parameter of [`Transfer`].
461    ///
462    /// [`Transfer`]: ../struct.Transfer.html
463    pub struct Ready;
464
465    /// Indicates that a transfer has been started
466    ///
467    /// Used for the `State` type parameter of [`Transfer`].
468    ///
469    /// [`Transfer`]: ../struct.Transfer.html
470    pub struct Started;
471}