Module imxrt_hal::dma [−][src]
Direct Memory Access (DMA)
We support
- DMA from memory to a peripheral. Transfers may be uni- or bi-directional.
See the
Peripheral
for details. - DMA memory copy, or memory-to-memory transfers. See
Memcpy
for details.
DMA types support either Linear
or Circular
memory buffers. Either may be used as a DMA transfer source or destination. Both are backed
by statically-allocated Buffer
s. A user will create a memory buffer,
then pass ownership to a DMA type that defines a transfer. When the transfer completes, the
DMA type will release ownership back to the user.
Terms
- Source is a location in memory that provides data. A source may be a buffer of data, or a peripheral register.
- Destination is a location in memory that will receive data. A destination may be a buffer of data, or a peripheral register.
- Transfer is an overloaded term, meaning either a DMA transfer, or the movement of data out of software, through a peripheral, to an external device.
- DMA Transfer is an operation achieved by the DMA controller to move data from a source to a destination.
- Receive means that we’re moving data into software, through a peripheral, from an external device.
Example: Full-Duplex SPI Peripheral
In this example, we prepare a SPI peripheral (SPI4) with two DMA channels. One channel will send data; the other will receive data. The example assumes that the user has registered a DMA interrupt handler, since we’re enabling an interrupt when the receive completes.
use imxrt_hal::dma::{Circular, Buffer, Linear, Peripheral, bidirectional_u16}; // Circular buffers have alignment requirements #[repr(align(512))] struct Align(Buffer<[u16; 256]>); // Two buffers that can support maximum receive and transfer sizes of 256 elements static RX_BUFFER: Buffer<[u16; 256]> = Buffer::new([0; 256]); static TX_BUFFER: Align = Align(Buffer::new([0; 256])); let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); // // SPI setup... // let (_, _, _, spi4_builder) = peripherals.spi.clock( &mut peripherals.ccm.handle, imxrt_hal::ccm::spi::ClockSelect::Pll2, imxrt_hal::ccm::spi::PrescalarSelect::LPSPI_PODF_5, ); let mut spi4 = spi4_builder.build( peripherals.iomuxc.b0.p02, peripherals.iomuxc.b0.p01, peripherals.iomuxc.b0.p03, ); spi4.enable_chip_select_0(peripherals.iomuxc.b0.p00); // Set the SPI clock speed, if desired... // // DMA setup // let mut dma_channels = peripherals.dma.clock(&mut peripherals.ccm.handle); // i.MX RT DMA interrupt handlers manage pairs of DMA channels. There's one // interrupt for DMA channel 9 and channel 25. By selecting these two // DMA channels, we can register one interrupt to handle both DMA channel // completion. let tx_channel = dma_channels[9].take().unwrap(); let mut rx_channel = dma_channels[25].take().unwrap(); // We only want to interrupt when the receive completes. When // the receive completes, we know that we're also done transferring // data. rx_channel.set_interrupt_on_completion(true); // The peripheral will transfer and receive u16 elements. // It takes ownership of the SPI object, and the two DMA channels. let mut peripheral = bidirectional_u16( spi4, tx_channel, rx_channel, ); // Create DMA memory adapters over the statically-allocated DMA memory. // These adapters will 'own' the statically-allocated memory. See the // Linear and Circular docs for more information. let mut tx_buffer = Circular::new(&TX_BUFFER.0).unwrap(); let mut rx_buffer = Linear::new(&RX_BUFFER).unwrap(); // Send 6 elements, and expect to receive 6 elements for v in 1..=6 { tx_buffer.push(v); } rx_buffer.set_transfer_len(6); // Start the DMA transfers peripheral.start_receive(rx_buffer).unwrap(); peripheral.start_transfer(tx_buffer).unwrap(); // At this point, the DMA controller is transferring data from // the SPI peripheral, and receiving data from the SPI peripheral. // Received data appears in RX_BUFFER. The DMA controller will trigger // an interrupt for DMA channel 25 when it has transferred 6 u16s. // // Your ISR should clear the interrupt and complete the transfers, // as depicted below: while peripheral.is_receive_interrupt() { peripheral.receive_clear_interrupt(); } let mut rx_buffer = None; if peripheral.is_receive_complete() { // Recover the receive buffer rx_buffer = peripheral.receive_complete(); } let mut tx_buffer = None; if peripheral.is_transfer_complete() { // Recover the transfer buffer tx_buffer = peripheral.transfer_complete(); }
Example: memcpy
See the Memcpy
documentation for an example of DMA-powered memcpy.
Notes on Data Cache
If your i.MX RT system is using a data cache (DCache), you’re responsible for issuing memory barriers, and flushing any cached buffers, for the DMA controller. More generally, you’re responsible for placing your DMA buffers into memory regions that the DMA controller can use.
TODO
- Channel arbitration modes
- Channel grouping
- Channel priority, and channel priority swapping
- Channel chaining
Structs
Buffer | A dedicated DMA memory buffer for transfers and receives |
Channel | A DMA channel |
Circular | A circular DMA buffer |
Drain | An iterator that will drain the contents from a circular DMA buffer |
ErrorStatus | A wrapper around a DMA error status value |
Linear | A linear DMA buffer |
Memcpy | A type that can peform memory-to-memory DMA transfers |
Peripheral | A DMA-capable peripheral |
ReadHalf | Exposes the read-half of the circular buffer |
Unclocked | Unclocked, uninitialized DMA channels |
WriteHalf | Exposes the write-half of the circular buffer |
Enums
BandwidthControl | Throttles the amount of bus bandwidth consumed by the eDMA |
CircularError | Possible errors when creating a circular buffer |
Error | An error when preparing a transfer |
Traits
Element | Describes a transferrable DMA element; basically, an unsigned integer of any size. |
Functions
bidirectional_u8 | Create a peripheral that can accept |
bidirectional_u16 | Create a peripheral that can accept |
receive_u8 | Create a peripheral that can suppy |
receive_u16 | Create a peripheral that can supply |
transfer_u8 | Create a peripheral that can accept |
transfer_u16 | Create a peripheral that can accept |