Content including 1) peripheral features, work logic and work method; 2) driver design logic and use method; 3) typical use case.
More...
Peripheral features
The DMA controller module enables fast transfers of data, providing an efficient way to move blocks of data with minimal processor interaction.
- Four independently programmable DMA controller channels
- Dual-address transfers via 32-bit master connection to the system bus
- Data transfers in 8-, 16-, or 32-bit blocks
- Continuous-mode or cycle-steal transfers from software or peripheral initiation
- One programmable input selected from 16 possible peripheral requests per channel
- Automatic hardware acknowledge/done indicator from each channel
- Independent source and destination address registers
- Optional modulo addressing and automatic updates of source and destination addresses
- Independent transfer sizes for source and destination
- Optional auto-alignment feature for source or destination accesses
- Optional automatic single or double channel linking
- Programming model accessed via 32-bit slave peripheral bus
- Channel arbitration on transfer boundaries using fixed priority scheme
How this peripheral works
As soon as a channel has been initialized, it may be started by setting DCRn[START] or a properly-selected peripheral DMA request, depending on the status of DCRn[ERQ]. Each channel can be programmed to select one peripheral request from a set of 16 possible request inputs. The DMA controller supports dual-address transfers using its bus master connection to the system bus. The DMA channels support transfers up to 32 data bits in size and have the same memory map addressibility as the processor. Any operation involving a DMA channel follows the same three steps:
- Channel initialization The transfer control descriptor, contained in the channel registers, is loaded with address pointers, a byte-transfer count, and control information using accesses from the slave peripheral bus.
- Data transfer The DMA accepts requests for data transfers. Upon receipt of a request, it provides address and bus control for the transfers via its master connection to the system bus and temporary storage for the read data. The channel performs one or more source read and destination write data transfers.
- Channel termination Occurs after the operation is finished successfully or due to an error. The channel indicates the operation status in the channel's DSR, described in the definitions of the DMA Status Registers (DSRn) and Byte Count Registers (BCRn).
How this driver is designed to make this peripheral works
The DMA peripheral is powerful with complex transfer features supported. To satisfy different user getting different demands for the feature sets/optimization level/flexibility/abstraction level, 2 parallel layers are provided in this driver (DO NOT MIX THE USAGE OF THESE 2 LAYERS!!!):
- Functional Layer is provided with highly optimized implementation and highly flexible usage of the peripheral features. All hardware features are supported while requiring user get a decent understanding of the hardware detail of DMA so that user know how to organize these functional API together to meet the requirement of application.
- Transactional layer is provided with average optimization level, average flexibility and not all features are covered. It is for user who want to ramp up the usage of peripheral quickly without dig much into this specific peripheral with only common knowledge of DMA peripheral model. It achieve this goal by hiding the processing of interrupt handling.
Below introduce the detail data structure/API for the two layers.
- Functional Layer
- Transfer Configuration The most important data structure usage is to configure the transfer attributes. The driver provide dma_channel_transfer_config_t whose members expose all the transfer attributes supported by peripheral. However, it may still complex / fussy for user who need not such many features. A helper function DMA_GetChannelDefaultTransferConfig is provided to get a ready-to-use configuration with only basic information required. User can create the dma_channel_transfer_config_t himself/herself if he/she is comfortable with that way or use the helper function DMA_GetChannelDefaultTransferConfig to get initial data structure and then fine-tune needed members per requirement.
- Channel Identifier All channel is represented with a enumerator provided in dma_channel_t that user need not bother with whether channel 0 shall be 0 or 1 for register access.
- Functional layer provide multiple API function groups
- DMA initialization and De-initialization Interfaces
- The interfaces target for whole module configuration which includes:
- All channels specific transfer configuration.
- The interfaces provide helper function assist user to get a ready-to-use dma_config_t with easy-to-understand parameter filled to assist the usage of Init API.
- DMA channel functional interfaces
- The interfaces provide a api that support configure the full transfer parameters of a channel.
- The interfaces provide helper function assist user to get a ready-to-use dma_channel_transfer_config_t with easy-to-understand parameter filled to assist the usage of Init API.
- DMA Channel Status Interfaces
- The interfaces in the function group can be used to get/clear the channel status
- DMA Channel Interrupt Interfaces
- The interfaces in the function group can be used to Enable/Disable channel interrupt
Transactional Layer
To distinguish from Functional layer, all transactional API get Transfer in the naming of the API.
In parallel with the Functional layer, the driver provide the transactional layer remove user the effort below thus user can easily get DMA peripheral features enabled with least coding effort and much less knowledge requirement on this peripheral
- User need not spent effort on prepare the interrupt handler function from vector entry to the user code.
Comparing to functional layer,
- Transactional layer use the dma_handle_t to represent a channel and user need the DMA_CreateHandle function to initialize the handle. This handler will be stored inside driver thus channel state is retained in software level which is crucial in interrupt context.
- Transactional use same data structure dma_channel_transfer_config_t to configure the transfer to keep the consistent usage experience across this driver
One of the key feature in transactional layer comparing to functional layer is user need not setup interrupt related stuff(like enabling the system level interrupt enable/disable, prepare the vector entry function) but still get user's interrupt handling code executed once interrupt happens.
- Interrupt handling codes are automatically active when user get this driver files into application project. This is provided by the 'double weak' mechanism. You can find the detail in the general section of API Reference Manual. In brief, SDK place a weak function A in the vector entry for this peripheral. The default implementation of this function call another weak function B. By default, when a interrupt happen, the code will be executed as A (Weak) -> B (Weak). This driver provide the function B thus once user get the driver file into the application project, when interrupt happened, code will be executed as A (Weak) -> B (Non-Weak, in this driver) thus user is no longer need to do with the interrupt handling himself. If user get his/her own requirement of interrupt handling, define the A in the application and interrupt will be routed to his/her own interrupt function.
- Hidden interrupt handling doesn't mean user get no chance to insert his/her processing logic in the interrupt handling. User is requested to provide the callback function on DMA_TransferChannelCreateHandle and when interrupt happens, callback will be invoked immediately that user codes can be executed when interrupt happens.
Several function groups are provided for the transactional layer
- Transaction Initialization Interface
- Init the channel with a user provided handle memory data structure with callback requested for inserting user code on completion of transfer
- Transaction Transfer Configuration Interface
- Interfaces to submit transfer
- Transaction Start/Stop Interface
- Start/Stop/Abort the transfer
- Transaction Interrupt Handler
- Interrupt handling function which is called in the 'double weak' function. User is not necessary to understand it but can take it as reference code to understand how this driver work
How to use this driver
Functional Layer
- Configuration option on compiling
- if FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL is defined, so please make sure that the DMA clock gate is enabled before DMA driver interface being invoked if driver does not handle it.
- If interrupt mode is used, setup the interrupt handler entry to execute the application codes
- Driver Interface invoking Sequence
- Initialize the DMA peripheral Use the DMA Initialization and De-initialization Interfaces. DMA driver provides an interface DMA_Init which is target for whole module configurations.
- If the application would like to submit transfer configuration by DMA_Init, please reference steps Prepare transfer configuration and then skip to step Trigger the transfer.
- If the channel request is enabled in the transfer configuration, please skips steps Trigger the transfer also.
- Prepare transfer configuration
- Use the dma_channel_transfer_config_t to prepare the transfer attributes and use the DMA_GetChannelDefaultTransferConfig to assist the creation of the transfer configure. 3 typical modes are supported on getting the default configuration
- Memory To Memory: Transfer data from memory to memory which means tranSfer address of source/destination address is incremental.
- Memory To Peripheral: Transfer data from memory to peripheral where destination address is fixed. A typical case is sending a buffer by UART send register.
- Peripheral To Memory: Transfer data from peripheral to memory buffer where source address is fixed. A typical case is receiving data into memory buffer on UART RX.
- Submit the transfer
- Application can submit the transfer configurations by calling DMA_SetChannelTransferConfig.
- Trigger the transfer
- User can trigger transfer manually using DMA_TriggerChannelStart/DMA_EnableChannelPeripheralRequest
- Track the completion of transfer
- Use API DMA_GetChannelStatusFlags to know whether it is done
- De-initialize the DMA peripheral channel DMA driver provides an interface DMA_Deinit which will reset all the channel relate transfer control descriptor registers.
Transactional Layer
- Configuration option on compiling
- if FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL is defined, so please make sure that the DMA clock gate is enabled before DMA driver interface being invoked if driver does not handle it.
- Steps shall be covered in application codes rather than in driver
- Same as functional layer except that interrupt entry need not be setup by user.
- Driver Interface invoking Sequence
- Initialize the DMA peripheral Use the DMA Initialization and De-initialization Interfaces. DMA driver provides an interface DMA_Init which is target for whole module configurations, channel transfer configuration should not be submitted with DMA_Init while using transactional layer.
- Init the handle by calling DMA_TransferCreateHandle with optional callback provided.
- Configure the Transfer
- Call DMA_TransferSubmitTransfer, user can submit the transfer request any time.
- Start/Stop/Abort channel use the DMA_TransferChannelStart/DMA_TransferChannelStop/DMA_TransferChannelAbort
- Handle transfer completion
- User's code will be executed on completion of transfer or transfers where it is provided in callback function on DMA_TransferChannelCreateHandle.
Typical Use Cases
- single transfer (using dma transactional layer)
*
DMA_Init(DEMO_DMA_BASEADDR, &dmaConfig);
*
- Memory to peripheral (using kDmaCH0_IIC0_ipd_Req as peripheral)
*
DMA_Init(DEMO_DMA_BASEADDR, &dmaConfig);
*