/***************************************************************************//**
* @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 ********************************************************