/***************************************************************************//** * @file * @brief DMADRV API implementation. ******************************************************************************* * # License * Copyright 2018 Silicon Laboratories Inc. www.silabs.com ******************************************************************************* * * SPDX-License-Identifier: Zlib * * The licensor of this software is Silicon Laboratories Inc. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * ******************************************************************************/ #include #include #include "em_device.h" #include "sl_core.h" #include "dmadrv.h" #if defined(EMDRV_DMADRV_LDMA_S3) #include "sl_clock_manager.h" #endif /// @cond DO_NOT_INCLUDE_WITH_DOXYGEN #if !defined(EMDRV_DMADRV_DMA_CH_COUNT) \ || (EMDRV_DMADRV_DMA_CH_COUNT > DMA_CHAN_COUNT) #if defined(_SILICON_LABS_32B_SERIES_3) #define EMDRV_DMADRV_DMA_CH_COUNT DMA_CHAN_COUNT(0) #else #define EMDRV_DMADRV_DMA_CH_COUNT DMA_CHAN_COUNT #endif #endif typedef enum { dmaDirectionMemToPeripheral, dmaDirectionPeripheralToMem } DmaDirection_t; typedef enum { dmaModeBasic, dmaModePingPong } DmaMode_t; typedef struct { DMADRV_Callback_t callback; void *userParam; unsigned int callbackCount; #if defined(EMDRV_DMADRV_UDMA) int length; #endif bool allocated; #if defined(EMDRV_DMADRV_LDMA) || defined(EMDRV_DMADRV_LDMA_S3) DmaMode_t mode; #endif } ChTable_t; static bool initialized = false; static ChTable_t chTable[EMDRV_DMADRV_DMA_CH_COUNT]; #if defined(EMDRV_DMADRV_UDMA) static DMA_CB_TypeDef dmaCallBack[EMDRV_DMADRV_DMA_CH_COUNT]; #endif #if defined(EMDRV_DMADRV_LDMA) || defined(EMDRV_DMADRV_LDMA_S3) #if defined(EMDRV_DMADRV_LDMA) const LDMA_TransferCfg_t xferCfgPeripheral = LDMA_TRANSFER_CFG_PERIPHERAL(0); const LDMA_Descriptor_t m2p = LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(NULL, NULL, 1UL); const LDMA_Descriptor_t p2m = LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(NULL, NULL, 1UL); typedef struct { LDMA_Descriptor_t desc[2]; } DmaXfer_t; #else const sl_hal_ldma_transfer_config_t xferCfgPeripheral = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(0); const sl_hal_ldma_descriptor_t m2p = SL_HAL_LDMA_DESCRIPTOR_SINGLE_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, NULL, NULL, 1UL); const sl_hal_ldma_descriptor_t p2m = SL_HAL_LDMA_DESCRIPTOR_SINGLE_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, NULL, NULL, 1UL); typedef struct { sl_hal_ldma_descriptor_t desc[2]; } DmaXfer_t; #endif static DmaXfer_t dmaXfer[EMDRV_DMADRV_DMA_CH_COUNT]; #endif static Ecode_t StartTransfer(DmaMode_t mode, DmaDirection_t direction, unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *buf0, void *buf1, void *buf2, bool bufInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam); #if defined(EMDRV_DMADRV_LDMA_S3) static void LDMA_IRQHandlerDefault(uint8_t chnum); #endif /// @endcond /***************************************************************************//** * @brief * Allocate (reserve) a DMA channel. * * @param[out] channelId * The channel ID assigned by DMADRV. * * @param[in] capabilities * Not used. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_AllocateChannel(unsigned int *channelId, void *capabilities) { unsigned int i; (void)capabilities; CORE_DECLARE_IRQ_STATE; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId == NULL ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } CORE_ENTER_ATOMIC(); for ( i = 0U; i < (unsigned int)EMDRV_DMADRV_DMA_CH_COUNT; i++ ) { if ( !chTable[i].allocated ) { *channelId = i; chTable[i].allocated = true; chTable[i].callback = NULL; CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_OK; } } CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_CHANNELS_EXHAUSTED; } /***************************************************************************//** * @brief * Allocate (reserve) the given DMA channel if he is free. * * @param[out] channelId * The channel ID to be assigned by DMADRV. * * @param[in] capabilities * Not used. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_AllocateChannelById(unsigned int channelId, void *capabilities) { (void)capabilities; CORE_DECLARE_IRQ_STATE; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } CORE_ENTER_ATOMIC(); if ( !chTable[channelId].allocated ) { chTable[channelId].allocated = true; chTable[channelId].callback = NULL; CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_OK; } CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_IN_USE; } /***************************************************************************//** * @brief * Deinitialize DMADRV. * * @details * If DMA channels are not currently allocated, it will disable DMA hardware * and mask associated interrupts. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_DeInit(void) { int i; bool inUse; CORE_DECLARE_IRQ_STATE; inUse = false; CORE_ENTER_ATOMIC(); for ( i = 0; i < (int)EMDRV_DMADRV_DMA_CH_COUNT; i++ ) { if ( chTable[i].allocated ) { inUse = true; break; } } if ( !inUse ) { #if defined(EMDRV_DMADRV_LDMA) LDMA_DeInit(); #elif defined(EMDRV_DMADRV_LDMA_S3) NVIC_DisableIRQ(LDMA0_CHNL0_IRQn); NVIC_DisableIRQ(LDMA0_CHNL1_IRQn); NVIC_DisableIRQ(LDMA0_CHNL2_IRQn); NVIC_DisableIRQ(LDMA0_CHNL3_IRQn); NVIC_DisableIRQ(LDMA0_CHNL4_IRQn); NVIC_DisableIRQ(LDMA0_CHNL5_IRQn); NVIC_DisableIRQ(LDMA0_CHNL6_IRQn); NVIC_DisableIRQ(LDMA0_CHNL7_IRQn); sl_hal_ldma_reset(LDMA0); sl_clock_manager_disable_bus_clock(SL_BUS_CLOCK_LDMA0); sl_clock_manager_disable_bus_clock(SL_BUS_CLOCK_LDMAXBAR0); #endif initialized = false; CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_OK; } CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_IN_USE; } /***************************************************************************//** * @brief * Free an allocated (reserved) DMA channel. * * @param[in] channelId * The channel ID to free. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_FreeChannel(unsigned int channelId) { CORE_DECLARE_IRQ_STATE; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } CORE_ENTER_ATOMIC(); if ( chTable[channelId].allocated ) { chTable[channelId].allocated = false; CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_OK; } CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_ALREADY_FREED; } /***************************************************************************//** * @brief * Initialize DMADRV. * * @details * The DMA hardware is initialized. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_Init(void) { int i; CORE_DECLARE_IRQ_STATE; #if defined(EMDRV_DMADRV_UDMA) DMA_Init_TypeDef dmaInit; #elif defined(EMDRV_DMADRV_LDMA) LDMA_Init_t dmaInit = LDMA_INIT_DEFAULT; dmaInit.ldmaInitCtrlNumFixed = EMDRV_DMADRV_DMA_CH_PRIORITY; #elif defined(EMDRV_DMADRV_LDMA_S3) sl_hal_ldma_config_t dmaInit = SL_HAL_LDMA_INIT_DEFAULT; dmaInit.num_fixed_priority = EMDRV_DMADRV_DMA_CH_PRIORITY; #endif CORE_ENTER_ATOMIC(); if ( initialized ) { CORE_EXIT_ATOMIC(); return ECODE_EMDRV_DMADRV_ALREADY_INITIALIZED; } initialized = true; CORE_EXIT_ATOMIC(); if ( EMDRV_DMADRV_DMA_IRQ_PRIORITY >= (1 << __NVIC_PRIO_BITS) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } for ( i = 0; i < (int)EMDRV_DMADRV_DMA_CH_COUNT; i++ ) { chTable[i].allocated = false; } #if defined(EMDRV_DMADRV_UDMA) NVIC_SetPriority(DMA_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); dmaInit.hprot = 0; dmaInit.controlBlock = dmaControlBlock; DMA_Init(&dmaInit); #elif defined(EMDRV_DMADRV_LDMA) dmaInit.ldmaInitIrqPriority = EMDRV_DMADRV_DMA_IRQ_PRIORITY; LDMA_Init(&dmaInit); #elif defined(EMDRV_DMADRV_LDMA_S3) sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_LDMA0); sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_LDMAXBAR0); sl_hal_ldma_init(LDMA0, &dmaInit); NVIC_ClearPendingIRQ(LDMA0_CHNL0_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL1_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL2_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL3_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL4_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL5_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL6_IRQn); NVIC_ClearPendingIRQ(LDMA0_CHNL7_IRQn); NVIC_SetPriority(LDMA0_CHNL0_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL1_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL2_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL3_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL4_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL5_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL6_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_SetPriority(LDMA0_CHNL7_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY); NVIC_EnableIRQ(LDMA0_CHNL0_IRQn); NVIC_EnableIRQ(LDMA0_CHNL1_IRQn); NVIC_EnableIRQ(LDMA0_CHNL2_IRQn); NVIC_EnableIRQ(LDMA0_CHNL3_IRQn); NVIC_EnableIRQ(LDMA0_CHNL4_IRQn); NVIC_EnableIRQ(LDMA0_CHNL5_IRQn); NVIC_EnableIRQ(LDMA0_CHNL6_IRQn); NVIC_EnableIRQ(LDMA0_CHNL7_IRQn); sl_hal_ldma_enable(LDMA0); #endif return ECODE_EMDRV_DMADRV_OK; } #if defined(EMDRV_DMADRV_LDMA) || defined(DOXYGEN) /***************************************************************************//** * @brief * Start an LDMA transfer. * * @details * This function is similar to the emlib LDMA function. * * @param[in] channelId * The channel ID to use. * * @param[in] transfer * A DMA transfer configuration data structure. * * @param[in] descriptor * A DMA transfer descriptor, can be an array of descriptors linked together. * * @param[in] callback * An optional callback function for signalling completion. May be NULL if not * needed. * * @param[in] cbUserParam * An optional user parameter to feed to the callback function. May be NULL if * not needed. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_LdmaStartTransfer(int channelId, LDMA_TransferCfg_t *transfer, LDMA_Descriptor_t *descriptor, DMADRV_Callback_t callback, void *cbUserParam) { ChTable_t *ch; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= (int)EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } ch = &chTable[channelId]; if ( ch->allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } ch->callback = callback; ch->userParam = cbUserParam; ch->callbackCount = 0; LDMA_StartTransfer(channelId, transfer, descriptor); return ECODE_EMDRV_DMADRV_OK; } #elif defined(EMDRV_DMADRV_LDMA_S3) Ecode_t DMADRV_LdmaStartTransfer(int channelId, sl_hal_ldma_transfer_config_t *transfer, sl_hal_ldma_descriptor_t *descriptor, DMADRV_Callback_t callback, void *cbUserParam) { ChTable_t *ch; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= (int)EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } ch = &chTable[channelId]; if ( ch->allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } ch->callback = callback; ch->userParam = cbUserParam; ch->callbackCount = 0; sl_hal_ldma_init_transfer(LDMA0, channelId, transfer, descriptor); sl_hal_ldma_enable_interrupts(LDMA0, (1 << channelId)); sl_hal_ldma_start_transfer(LDMA0, channelId); return ECODE_EMDRV_DMADRV_OK; } #endif /***************************************************************************//** * @brief * Start a memory to a peripheral DMA transfer. * * @param[in] channelId * The channel ID to use for the transfer. * * @param[in] peripheralSignal * Selects which peripheral/peripheralsignal to use. * * @param[in] dst * A destination (peripheral register) memory address. * * @param[in] src * A source memory address. * * @param[in] srcInc * Set to true to enable source address increment (increments according to * @a size parameter). * * @param[in] len * A number of items (of @a size size) to transfer. * * @param[in] size * An item size, byte, halfword or word. * * @param[in] callback * A function to call on DMA completion, use NULL if not needed. * * @param[in] cbUserParam * An optional user parameter to feed to the callback function. Use NULL if * not needed. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_MemoryPeripheral(unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *dst, void *src, bool srcInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { return StartTransfer(dmaModeBasic, dmaDirectionMemToPeripheral, channelId, peripheralSignal, dst, src, NULL, srcInc, len, size, callback, cbUserParam); } /***************************************************************************//** * @brief * Start a memory to a peripheral ping-pong DMA transfer. * * @param[in] channelId * The channel ID to use for the transfer. * * @param[in] peripheralSignal * Selects which peripheral/peripheralsignal to use. * * @param[in] dst * A destination (peripheral register) memory address. * * @param[in] src0 * A source memory address of the first (ping) buffer. * * @param[in] src1 * A source memory address of the second (pong) buffer. * * @param[in] srcInc * Set to true to enable source address increment (increments according to * @a size parameter). * * @param[in] len * A number of items (of @a size size) to transfer. * * @param[in] size * An item size, byte, halfword or word. * * @param[in] callback * A function to call on DMA completion, use NULL if not needed. * * @param[in] cbUserParam * An optional user parameter to feed to the callback function. Use NULL if * not needed. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_MemoryPeripheralPingPong( unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *dst, void *src0, void *src1, bool srcInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { return StartTransfer(dmaModePingPong, dmaDirectionMemToPeripheral, channelId, peripheralSignal, dst, src0, src1, srcInc, len, size, callback, cbUserParam); } /***************************************************************************//** * @brief * Start a peripheral to memory DMA transfer. * * @param[in] channelId * The channel ID to use for the transfer. * * @param[in] peripheralSignal * Selects which peripheral/peripheralsignal to use. * * @param[in] dst * A destination memory address. * * @param[in] src * A source memory (peripheral register) address. * * @param[in] dstInc * Set to true to enable destination address increment (increments according * to @a size parameter). * * @param[in] len * A number of items (of @a size size) to transfer. * * @param[in] size * An item size, byte, halfword or word. * * @param[in] callback * A function to call on DMA completion, use NULL if not needed. * * @param[in] cbUserParam * An optional user parameter to feed to the callback function. Use NULL if * not needed. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_PeripheralMemory(unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *dst, void *src, bool dstInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { return StartTransfer(dmaModeBasic, dmaDirectionPeripheralToMem, channelId, peripheralSignal, dst, src, NULL, dstInc, len, size, callback, cbUserParam); } /***************************************************************************//** * @brief * Start a peripheral to memory ping-pong DMA transfer. * * @param[in] channelId * The channel ID to use for the transfer. * * @param[in] peripheralSignal * Selects which peripheral/peripheralsignal to use. * * @param[in] dst0 * A destination memory address of the first (ping) buffer. * * @param[in] dst1 * A destination memory address of the second (pong) buffer. * * @param[in] src * A source memory (peripheral register) address. * * @param[in] dstInc * Set to true to enable destination address increment (increments according * to @a size parameter). * * @param[in] len * A number of items (of @a size size) to transfer. * * @param[in] size * An item size, byte, halfword or word. * * @param[in] callback * A function to call on DMA completion, use NULL if not needed. * * @param[in] cbUserParam * An optional user parameter to feed to the callback function. Use NULL if * not needed. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_PeripheralMemoryPingPong( unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *dst0, void *dst1, void *src, bool dstInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { return StartTransfer(dmaModePingPong, dmaDirectionPeripheralToMem, channelId, peripheralSignal, dst0, dst1, src, dstInc, len, size, callback, cbUserParam); } /***************************************************************************//** * @brief * Pause an ongoing DMA transfer. * * @param[in] channelId * The channel ID of the transfer to pause. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_PauseTransfer(unsigned int channelId) { if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) DMA_ChannelRequestEnable(channelId, false); #elif defined(EMDRV_DMADRV_LDMA) LDMA_EnableChannelRequest(channelId, false); #elif defined(EMDRV_DMADRV_LDMA_S3) sl_hal_ldma_disable_channel_request(LDMA0, channelId); #endif return ECODE_EMDRV_DMADRV_OK; } /***************************************************************************//** * @brief * Resume an ongoing DMA transfer. * * @param[in] channelId * The channel ID of the transfer to resume. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_ResumeTransfer(unsigned int channelId) { if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) DMA_ChannelRequestEnable(channelId, true); #elif defined(EMDRV_DMADRV_LDMA) LDMA_EnableChannelRequest(channelId, true); #elif defined(EMDRV_DMADRV_LDMA_S3) sl_hal_ldma_enable_channel_request(LDMA0, channelId); #endif return ECODE_EMDRV_DMADRV_OK; } /***************************************************************************//** * @brief * Stop an ongoing DMA transfer. * * @param[in] channelId * The channel ID of the transfer to stop. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_StopTransfer(unsigned int channelId) { if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) DMA_ChannelEnable(channelId, false); #elif defined(EMDRV_DMADRV_LDMA) LDMA_StopTransfer(channelId); #elif defined(EMDRV_DMADRV_LDMA_S3) sl_hal_ldma_stop_transfer(LDMA0, channelId); #endif return ECODE_EMDRV_DMADRV_OK; } /***************************************************************************//** * @brief * Check if a transfer is running. * * @param[in] channelId * The channel ID of the transfer to check. * * @param[out] active * True if transfer is running, false otherwise. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_TransferActive(unsigned int channelId, bool *active) { if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (active == NULL) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) if ( DMA_ChannelEnabled(channelId) ) #elif defined(EMDRV_DMADRV_LDMA) if ( LDMA_ChannelEnabled(channelId) ) #elif defined(EMDRV_DMADRV_LDMA_S3) if ( sl_hal_ldma_channel_is_enabled(LDMA0, channelId) ) #endif { *active = true; } else { *active = false; } return ECODE_EMDRV_DMADRV_OK; } /***************************************************************************//** * @brief * Check if a transfer complete is pending. * * @details * Will check the channel interrupt flag. This assumes that the DMA is configured * to give a completion interrupt. * * @param[in] channelId * The channel ID of the transfer to check. * * @param[out] pending * True if a transfer complete is pending, false otherwise. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_TransferCompletePending(unsigned int channelId, bool *pending) { if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (pending == NULL) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) if ( DMA->IF & (1 << channelId) ) #elif defined(EMDRV_DMADRV_LDMA) if ( LDMA->IF & (1 << channelId) ) #elif defined(EMDRV_DMADRV_LDMA_S3) if ( sl_hal_ldma_get_pending_interrupts(LDMA0) & (1 << channelId) ) #endif { *pending = true; } else { *pending = false; } return ECODE_EMDRV_DMADRV_OK; } /***************************************************************************//** * @brief * Check if a transfer has completed. * * @note * This function should be used in a polled environment. * Will only work reliably for transfers NOT using the completion interrupt. * On UDMA, it will only work on basic transfers on the primary channel. * * @param[in] channelId * The channel ID of the transfer to check. * * @param[out] done * True if a transfer has completed, false otherwise. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_TransferDone(unsigned int channelId, bool *done) { #if defined(EMDRV_DMADRV_UDMA) uint32_t remaining, iflag; #endif if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (done == NULL) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) CORE_ATOMIC_SECTION( /* This works for primary channel only ! */ remaining = (dmaControlBlock[channelId].CTRL & _DMA_CTRL_N_MINUS_1_MASK) >> _DMA_CTRL_N_MINUS_1_SHIFT; iflag = DMA->IF; ) if ( (remaining == 0) && (iflag & (1 << channelId)) ) { *done = true; } else { *done = false; } #elif defined(EMDRV_DMADRV_LDMA) *done = LDMA_TransferDone(channelId); #elif defined(EMDRV_DMADRV_LDMA_S3) *done = sl_hal_ldma_transfer_is_done(LDMA0, channelId); #endif return ECODE_EMDRV_DMADRV_OK; } /***************************************************************************//** * @brief * Get number of items remaining in a transfer. * * @note * This function does not take into account that a DMA transfer with * a chain of linked transfers might be ongoing. It will only check the * count for the current transfer. * On UDMA, it will only work on the primary channel. * * @param[in] channelId * The channel ID of the transfer to check. * * @param[out] remaining * A number of items remaining in the transfer. * * @return * @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate * DMADRV @ref Ecode_t is returned. ******************************************************************************/ Ecode_t DMADRV_TransferRemainingCount(unsigned int channelId, int *remaining) { #if defined(EMDRV_DMADRV_UDMA) uint32_t remain, iflag; #endif if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (remaining == NULL) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } if ( chTable[channelId].allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } #if defined(EMDRV_DMADRV_UDMA) CORE_ATOMIC_SECTION( /* This works for the primary channel only ! */ remain = (dmaControlBlock[channelId].CTRL & _DMA_CTRL_N_MINUS_1_MASK) >> _DMA_CTRL_N_MINUS_1_SHIFT; iflag = DMA->IF; ) if ( (remain == 0) && (iflag & (1 << channelId)) ) { *remaining = 0; } else { *remaining = 1 + remain; } #elif defined(EMDRV_DMADRV_LDMA) *remaining = LDMA_TransferRemainingCount(channelId); #elif defined(EMDRV_DMADRV_LDMA_S3) *remaining = sl_hal_ldma_transfer_remaining_count(LDMA0, channelId); #endif return ECODE_EMDRV_DMADRV_OK; } /// @cond DO_NOT_INCLUDE_WITH_DOXYGEN #if defined(EMDRV_DMADRV_LDMA) /***************************************************************************//** * @brief * An interrupt handler for LDMA. ******************************************************************************/ void LDMA_IRQHandler(void) { bool stop; ChTable_t *ch; uint32_t pending, chnum, chmask; /* Get all pending and enabled interrupts. */ pending = LDMA->IF; pending &= LDMA->IEN; /* Check for LDMA error. */ if ( pending & LDMA_IF_ERROR ) { /* Loop to enable debugger to see what has happened. */ while (true) { /* Wait forever. */ } } /* Iterate over all LDMA channels. */ for ( chnum = 0, chmask = 1; chnum < EMDRV_DMADRV_DMA_CH_COUNT; chnum++, chmask <<= 1 ) { if ( pending & chmask ) { /* Clear the interrupt flag. */ #if defined (LDMA_HAS_SET_CLEAR) LDMA->IF_CLR = chmask; #else LDMA->IFC = chmask; #endif ch = &chTable[chnum]; if ( ch->callback != NULL ) { ch->callbackCount++; stop = !ch->callback(chnum, ch->callbackCount, ch->userParam); if ( (ch->mode == dmaModePingPong) && stop ) { dmaXfer[chnum].desc[0].xfer.link = 0; dmaXfer[chnum].desc[1].xfer.link = 0; } } } } } #endif /* defined( EMDRV_DMADRV_LDMA ) */ #if defined(EMDRV_DMADRV_LDMA_S3) /***************************************************************************//** * @brief * Default interrupt handler for LDMA common to all interrupt channel lines. * * @param[in] chnum * The channel ID responsible for the interrupt signal trigger. ******************************************************************************/ static void LDMA_IRQHandlerDefault(uint8_t chnum) { bool stop; ChTable_t *ch; uint32_t pending; uint32_t chmask; /* Get all pending and enabled interrupts. */ pending = sl_hal_ldma_get_enabled_pending_interrupts(LDMA0); /* Check for LDMA error. */ if ( pending & (LDMA_IF_ERROR0 << chnum) ) { /* Loop to enable debugger to see what has happened. */ while (true) { /* Wait forever. */ } } chmask = 1 << chnum; if ( pending & chmask ) { /* Clear the interrupt flag. */ sl_hal_ldma_clear_interrupts(LDMA0, chmask); /* Callback called if it was provided for the given channel. */ ch = &chTable[chnum]; if ( ch->callback != NULL ) { ch->callbackCount++; stop = !ch->callback(chnum, ch->callbackCount, ch->userParam); /* Continue or not a ping-pong transfer. */ if ( (ch->mode == dmaModePingPong) && stop ) { dmaXfer[chnum].desc[0].xfer.link = 0; dmaXfer[chnum].desc[1].xfer.link = 0; } } } } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 0. ******************************************************************************/ void LDMA0_CHNL0_IRQHandler(void) { LDMA_IRQHandlerDefault(0); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 1. ******************************************************************************/ void LDMA0_CHNL1_IRQHandler(void) { LDMA_IRQHandlerDefault(1); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 2. ******************************************************************************/ void LDMA0_CHNL2_IRQHandler(void) { LDMA_IRQHandlerDefault(2); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 3. ******************************************************************************/ void LDMA0_CHNL3_IRQHandler(void) { LDMA_IRQHandlerDefault(3); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 4. ******************************************************************************/ void LDMA0_CHNL4_IRQHandler(void) { LDMA_IRQHandlerDefault(4); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 5. ******************************************************************************/ void LDMA0_CHNL5_IRQHandler(void) { LDMA_IRQHandlerDefault(5); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 6. ******************************************************************************/ void LDMA0_CHNL6_IRQHandler(void) { LDMA_IRQHandlerDefault(6); } /***************************************************************************//** * @brief * Root interrupt handler for LDMA channel 7. ******************************************************************************/ void LDMA0_CHNL7_IRQHandler(void) { LDMA_IRQHandlerDefault(7); } #endif /* defined( EMDRV_DMADRV_LDMA_S3 ) */ #if defined(EMDRV_DMADRV_UDMA) /***************************************************************************//** * @brief * A callback function for UDMA basic transfers. ******************************************************************************/ static void DmaBasicCallback(unsigned int channel, bool primary, void *user) { ChTable_t *ch = &chTable[channel]; (void)user; (void)primary; if ( ch->callback != NULL ) { ch->callbackCount++; ch->callback(channel, ch->callbackCount, ch->userParam); } } #endif #if defined(EMDRV_DMADRV_UDMA) /***************************************************************************//** * @brief * A callback function for UDMA ping-pong transfers. ******************************************************************************/ static void DmaPingPongCallback(unsigned int channel, bool primary, void *user) { bool stop = true; ChTable_t *ch = &chTable[channel]; (void)user; if ( ch->callback != NULL ) { ch->callbackCount++; stop = !ch->callback(channel, ch->callbackCount, ch->userParam); } DMA_RefreshPingPong(channel, primary, false, NULL, NULL, ch->length - 1, stop); } #endif #if defined(EMDRV_DMADRV_UDMA) /***************************************************************************//** * @brief * Start a UDMA transfer. ******************************************************************************/ static Ecode_t StartTransfer(DmaMode_t mode, DmaDirection_t direction, unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *buf0, void *buf1, void *buf2, bool bufInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { ChTable_t *ch; DMA_CfgChannel_TypeDef chCfg; DMA_CfgDescr_TypeDef descrCfg; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (buf0 == NULL) || (buf1 == NULL) || (len > DMADRV_MAX_XFER_COUNT) || ((mode == dmaModePingPong) && (buf2 == NULL)) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } ch = &chTable[channelId]; if ( ch->allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } /* Se tup the interrupt callback routine. */ if ( mode == dmaModeBasic ) { dmaCallBack[channelId].cbFunc = DmaBasicCallback; } else { dmaCallBack[channelId].cbFunc = DmaPingPongCallback; } dmaCallBack[channelId].userPtr = NULL; /* Set up the channel */ chCfg.highPri = false; /* Can't use hi pri with peripherals. */ /* Whether the interrupt is needed. */ if ( (callback != NULL) || (mode == dmaModePingPong) ) { chCfg.enableInt = true; } else { chCfg.enableInt = false; } chCfg.select = peripheralSignal; chCfg.cb = &dmaCallBack[channelId]; DMA_CfgChannel(channelId, &chCfg); /* Set up the channel descriptor. */ if ( direction == dmaDirectionMemToPeripheral ) { if ( bufInc ) { if ( size == dmadrvDataSize1 ) { descrCfg.srcInc = dmaDataInc1; } else if ( size == dmadrvDataSize2 ) { descrCfg.srcInc = dmaDataInc2; } else { /* dmadrvDataSize4 */ descrCfg.srcInc = dmaDataInc4; } } else { descrCfg.srcInc = dmaDataIncNone; } descrCfg.dstInc = dmaDataIncNone; } else { if ( bufInc ) { if ( size == dmadrvDataSize1 ) { descrCfg.dstInc = dmaDataInc1; } else if ( size == dmadrvDataSize2 ) { descrCfg.dstInc = dmaDataInc2; } else { /* dmadrvDataSize4 */ descrCfg.dstInc = dmaDataInc4; } } else { descrCfg.dstInc = dmaDataIncNone; } descrCfg.srcInc = dmaDataIncNone; } descrCfg.size = (DMA_DataSize_TypeDef)size; descrCfg.arbRate = dmaArbitrate1; descrCfg.hprot = 0; DMA_CfgDescr(channelId, true, &descrCfg); if ( mode == dmaModePingPong ) { DMA_CfgDescr(channelId, false, &descrCfg); } ch->callback = callback; ch->userParam = cbUserParam; ch->callbackCount = 0; ch->length = len; DMA->IFC = 1 << channelId; /* Start the DMA cycle. */ if ( mode == dmaModeBasic ) { DMA_ActivateBasic(channelId, true, false, buf0, buf1, len - 1); } else { if ( direction == dmaDirectionMemToPeripheral ) { DMA_ActivatePingPong(channelId, false, buf0, /* dest */ buf1, /* src */ len - 1, buf0, /* dest */ buf2, /* src */ len - 1); } else { DMA_ActivatePingPong(channelId, false, buf0, /* dest */ buf2, /* src */ len - 1, buf1, /* dest */ buf2, /* src */ len - 1); } } return ECODE_EMDRV_DMADRV_OK; } #endif /* defined( EMDRV_DMADRV_UDMA ) */ #if defined(EMDRV_DMADRV_LDMA) /***************************************************************************//** * @brief * Start an LDMA transfer. ******************************************************************************/ static Ecode_t StartTransfer(DmaMode_t mode, DmaDirection_t direction, unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *buf0, void *buf1, void *buf2, bool bufInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { ChTable_t *ch; LDMA_TransferCfg_t xfer; LDMA_Descriptor_t *desc; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (buf0 == NULL) || (buf1 == NULL) || (len > DMADRV_MAX_XFER_COUNT) || ((mode == dmaModePingPong) && (buf2 == NULL)) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } ch = &chTable[channelId]; if ( ch->allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } xfer = xferCfgPeripheral; desc = &dmaXfer[channelId].desc[0]; if ( direction == dmaDirectionMemToPeripheral ) { *desc = m2p; if ( !bufInc ) { desc->xfer.srcInc = ldmaCtrlSrcIncNone; } } else { *desc = p2m; if ( !bufInc ) { desc->xfer.dstInc = ldmaCtrlDstIncNone; } } xfer.ldmaReqSel = peripheralSignal; desc->xfer.xferCnt = len - 1; desc->xfer.dstAddr = (uint32_t)(uint8_t *)buf0; desc->xfer.srcAddr = (uint32_t)(uint8_t *)buf1; desc->xfer.size = size; if ( mode == dmaModePingPong ) { desc->xfer.linkMode = ldmaLinkModeRel; desc->xfer.link = 1; desc->xfer.linkAddr = 4; /* Refer to the "pong" descriptor. */ /* Set the "pong" descriptor equal to the "ping" descriptor. */ dmaXfer[channelId].desc[1] = *desc; /* Refer to the "ping" descriptor. */ dmaXfer[channelId].desc[1].xfer.linkAddr = -4; dmaXfer[channelId].desc[1].xfer.srcAddr = (uint32_t)(uint8_t *)buf2; if ( direction == dmaDirectionPeripheralToMem ) { dmaXfer[channelId].desc[1].xfer.dstAddr = (uint32_t)(uint8_t *)buf1; desc->xfer.srcAddr = (uint32_t)(uint8_t *)buf2; } } /* Whether an interrupt is needed. */ if ( (callback == NULL) && (mode == dmaModeBasic) ) { desc->xfer.doneIfs = 0; } ch->callback = callback; ch->userParam = cbUserParam; ch->callbackCount = 0; ch->mode = mode; LDMA_StartTransfer(channelId, &xfer, desc); return ECODE_EMDRV_DMADRV_OK; } #endif /* defined( EMDRV_DMADRV_LDMA ) */ #if defined(EMDRV_DMADRV_LDMA_S3) /***************************************************************************//** * @brief * Start an LDMA transfer. ******************************************************************************/ static Ecode_t StartTransfer(DmaMode_t mode, DmaDirection_t direction, unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *buf0, void *buf1, void *buf2, bool bufInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam) { ChTable_t *ch; sl_hal_ldma_transfer_config_t xfer; sl_hal_ldma_descriptor_t *desc; if ( !initialized ) { return ECODE_EMDRV_DMADRV_NOT_INITIALIZED; } if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT) || (buf0 == NULL) || (buf1 == NULL) || (len > DMADRV_MAX_XFER_COUNT) || ((mode == dmaModePingPong) && (buf2 == NULL)) ) { return ECODE_EMDRV_DMADRV_PARAM_ERROR; } ch = &chTable[channelId]; if ( ch->allocated == false ) { return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED; } xfer = xferCfgPeripheral; desc = &dmaXfer[channelId].desc[0]; if ( direction == dmaDirectionMemToPeripheral ) { *desc = m2p; if ( !bufInc ) { desc->xfer.src_inc = SL_HAL_LDMA_CTRL_SRC_INC_NONE; } } else { *desc = p2m; if ( !bufInc ) { desc->xfer.dst_inc = SL_HAL_LDMA_CTRL_DST_INC_NONE; } } xfer.request_sel = peripheralSignal; desc->xfer.xfer_count = len - 1; desc->xfer.dst_addr = (uint32_t)(uint8_t *)buf0; desc->xfer.src_addr = (uint32_t)(uint8_t *)buf1; desc->xfer.size = size; if ( mode == dmaModePingPong ) { desc->xfer.link_mode = SL_HAL_LDMA_LINK_MODE_REL; desc->xfer.link = 1; desc->xfer.link_addr = 4; /* Refer to the "pong" descriptor. */ /* Set the "pong" descriptor equal to the "ping" descriptor. */ dmaXfer[channelId].desc[1] = *desc; /* Refer to the "ping" descriptor. */ dmaXfer[channelId].desc[1].xfer.link_addr = -4; dmaXfer[channelId].desc[1].xfer.src_addr = (uint32_t)(uint8_t *)buf2; if ( direction == dmaDirectionPeripheralToMem ) { dmaXfer[channelId].desc[1].xfer.dst_addr = (uint32_t)(uint8_t *)buf1; desc->xfer.src_addr = (uint32_t)(uint8_t *)buf2; } } /* Whether an interrupt is needed. */ if ( (callback == NULL) && (mode == dmaModeBasic) ) { desc->xfer.done_ifs = 0; } ch->callback = callback; ch->userParam = cbUserParam; ch->callbackCount = 0; ch->mode = mode; sl_hal_ldma_init_transfer(LDMA0, channelId, &xfer, desc); sl_hal_ldma_start_transfer(LDMA0, channelId); sl_hal_ldma_enable_interrupts(LDMA0, (0x1UL << channelId)); return ECODE_EMDRV_DMADRV_OK; } #endif /* defined( EMDRV_DMADRV_LDMA_S3 ) */ /// @endcond // ******** THE REST OF THE FILE IS DOCUMENTATION ONLY !*********************** /// @addtogroup dmadrv DMADRV - DMA Driver /// @brief Direct Memory Access Driver /// @{ /// /// @details /// /// /// @n @section dmadrv_intro Introduction /// /// The DMADRV driver supports writing code using DMA which will work /// regardless of the type of the DMA controller on the underlying microcontroller. /// Additionally, DMA can be used in several modules that are /// completely unaware of each other. /// The driver does not preclude use of the native emlib or peripheral API of the /// underlying DMA controller. On the contrary, it will often result in more efficient /// code and is necessary for complex DMA operations. The housekeeping /// functions of this driver are valuable even in this use-case. /// /// The dmadrv.c and dmadrv.h source files are in the /// emdrv/dmadrv folder. /// /// @note DMA transfer completion callback functions are called from within the /// DMA interrupt handler. On versions of the DMA controller with one interrupt per /// channel, the callback function is called from its respective channel interrupt /// handler. /// /// @n @section dmadrv_conf Configuration Options /// /// Some properties of the DMADRV driver are compile-time configurable. These /// properties are stored in a file named dmadrv_config.h. A template for this /// file, containing default values, is in the emdrv/config folder. IC specific /// versions of dmadrv_config.h files are available in config/sx_xch directories. /// Currently the configuration options are as follows: /// @li The interrupt priority of the DMA peripheral. /// @li A number of DMA channels to support. /// @li Use the native emlib/peripheral API belonging to the underlying DMA hardware in /// combination with the DMADRV API. /// /// Both configuration options will help reduce the driver's RAM footprint. /// /// To configure DMADRV, provide a custom configuration file. This is an /// example dmadrv_config.h file: /// @code{.c} /// #ifndef __SILICON_LABS_DMADRV_CONFIG_H__ /// #define __SILICON_LABS_DMADRV_CONFIG_H__ /// /// // DMADRV DMA interrupt priority configuration option. /// // Set DMA interrupt priority. Range is 0..7, 0 is the highest priority. /// #define EMDRV_DMADRV_DMA_IRQ_PRIORITY 4 /// /// // DMADRV channel count configuration option. /// // A number of DMA channels to support. A lower DMA channel count will reduce /// // RAM footprint. /// #define EMDRV_DMADRV_DMA_CH_COUNT 4 /// /// #endif /// @endcode /// /// @n @section dmadrv_api The API /// /// This section contains brief descriptions of the API functions. /// For more information about input and output parameters and return values, /// click on the hyperlinked function names. Most functions return an error /// code, @ref ECODE_EMDRV_DMADRV_OK is returned on success, /// see @ref ecode and @ref dmadrv_error_codes for other error codes. /// /// The application code must include @em dmadrv.h header file. /// /// @ref DMADRV_Init(), @ref DMADRV_DeInit() @n /// These functions initialize or deinitialize the DMADRV driver. Typically, /// DMADRV_Init() is called once in the startup code. /// /// @ref DMADRV_AllocateChannel(), @ref DMADRV_FreeChannel() @n /// DMA channel reserve and release functions. It is recommended that /// application code check that DMADRV_AllocateChannel() /// returns ECODE_EMDRV_DMADRV_OK before starting a DMA /// transfer. /// /// @ref DMADRV_MemoryPeripheral() @n /// Start a DMA transfer from memory to a peripheral. /// /// @ref DMADRV_PeripheralMemory() @n /// Start a DMA transfer from a peripheral to memory. /// /// @ref DMADRV_MemoryPeripheralPingPong() @n /// Start a DMA ping-pong transfer from memory to a peripheral. /// /// @ref DMADRV_PeripheralMemoryPingPong() @n /// Start a DMA ping-pong transfer from a peripheral to memory. /// /// @ref DMADRV_LdmaStartTransfer() @n /// Start a DMA transfer on an LDMA controller. /// /// @ref DMADRV_PauseTransfer() @n /// Pause an ongoing DMA transfer. /// /// @ref DMADRV_ResumeTransfer() @n /// Resume paused DMA transfer. /// /// @ref DMADRV_StopTransfer() @n /// Stop an ongoing DMA transfer. /// /// @ref DMADRV_TransferActive() @n /// Check if a transfer is ongoing. /// /// @ref DMADRV_TransferCompletePending() @n /// Check if a transfer completion is pending. /// /// @ref DMADRV_TransferDone() @n /// Check if a transfer has completed. /// /// @ref DMADRV_TransferRemainingCount() @n /// Get number of items remaining in a transfer. /// /// @n @section dmadrv_example Example /// Transfer a text string to USART1. /// @code{.c} /// #include "dmadrv.h" /// /// char str[] = "Hello DMA !"; /// unsigned int channel; /// /// int main( void ) /// { /// // Initialize DMA. /// DMADRV_Init(); /// /// // Request a DMA channel. /// DMADRV_AllocateChannel( &channel, NULL ); /// /// // Start the DMA transfer. /// DMADRV_MemoryPeripheral( channel, /// dmadrvPeripheralSignal_USART1_TXBL, /// (void*)&(USART1->TXDATA), /// str, /// true, /// sizeof( str ), /// dmadrvDataSize1, /// NULL, /// NULL ); /// /// return 0; /// } /// @endcode /// /// @} end group dmadrv ********************************************************