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}