482 lines
18 KiB
C
482 lines
18 KiB
C
/*
|
|
* The Clear BSD License
|
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
|
* Copyright 2016-2017 NXP
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted (subject to the limitations in the disclaimer below) provided
|
|
* that the following conditions are met:
|
|
*
|
|
* o Redistributions of source code must retain the above copyright notice, this list
|
|
* of conditions and the following disclaimer.
|
|
*
|
|
* o Redistributions in binary form must reproduce the above copyright notice, this
|
|
* list of conditions and the following disclaimer in the documentation and/or
|
|
* other materials provided with the distribution.
|
|
*
|
|
* o Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fsl_dma.h"
|
|
|
|
/*******************************************************************************
|
|
* Definitions
|
|
******************************************************************************/
|
|
|
|
/* Component ID definition, used by tools. */
|
|
#ifndef FSL_COMPONENT_ID
|
|
#define FSL_COMPONENT_ID "platform.drivers.lpc_dma"
|
|
#endif
|
|
|
|
|
|
/*******************************************************************************
|
|
* Prototypes
|
|
******************************************************************************/
|
|
|
|
/*!
|
|
* @brief Get instance number for DMA.
|
|
*
|
|
* @param base DMA peripheral base address.
|
|
*/
|
|
static uint32_t DMA_GetInstance(DMA_Type *base);
|
|
|
|
/*!
|
|
* @brief Get virtual channel number.
|
|
*
|
|
* @param base DMA peripheral base address.
|
|
*/
|
|
static uint32_t DMA_GetVirtualStartChannel(DMA_Type *base);
|
|
|
|
/*******************************************************************************
|
|
* Variables
|
|
******************************************************************************/
|
|
|
|
/*! @brief Array to map DMA instance number to base pointer. */
|
|
static DMA_Type *const s_dmaBases[] = DMA_BASE_PTRS;
|
|
|
|
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
|
|
/*! @brief Array to map DMA instance number to clock name. */
|
|
static const clock_ip_name_t s_dmaClockName[] = DMA_CLOCKS;
|
|
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
|
|
|
|
/*! @brief Array to map DMA instance number to IRQ number. */
|
|
static const IRQn_Type s_dmaIRQNumber[] = DMA_IRQS;
|
|
|
|
/*! @brief Pointers to transfer handle for each DMA channel. */
|
|
static dma_handle_t *s_DMAHandle[FSL_FEATURE_DMA_ALL_CHANNELS];
|
|
|
|
/*! @brief Static table of descriptors */
|
|
#if defined(__ICCARM__)
|
|
#pragma data_alignment = FSL_FEATURE_DMA_DESCRIPTOR_ALIGN_SIZE
|
|
static dma_descriptor_t s_dma_descriptor_table[FSL_FEATURE_SOC_DMA_COUNT][FSL_FEATURE_DMA_MAX_CHANNELS] = {0};
|
|
#elif defined(__CC_ARM)
|
|
__attribute__((aligned(FSL_FEATURE_DMA_DESCRIPTOR_ALIGN_SIZE)))
|
|
static dma_descriptor_t s_dma_descriptor_table[FSL_FEATURE_SOC_DMA_COUNT][FSL_FEATURE_DMA_MAX_CHANNELS] = {0};
|
|
#elif defined(__GNUC__)
|
|
__attribute__((aligned(FSL_FEATURE_DMA_DESCRIPTOR_ALIGN_SIZE)))
|
|
static dma_descriptor_t s_dma_descriptor_table[FSL_FEATURE_SOC_DMA_COUNT][FSL_FEATURE_DMA_MAX_CHANNELS] = {0};
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
|
|
static uint32_t DMA_GetInstance(DMA_Type *base)
|
|
{
|
|
int32_t instance;
|
|
/* Find the instance index from base address mappings. */
|
|
for (instance = 0; instance < ARRAY_SIZE(s_dmaBases); instance++)
|
|
{
|
|
if (s_dmaBases[instance] == base)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
assert(instance < ARRAY_SIZE(s_dmaBases));
|
|
|
|
return instance;
|
|
}
|
|
|
|
static uint32_t DMA_GetVirtualStartChannel(DMA_Type *base)
|
|
{
|
|
uint32_t startChannel = 0, instance = 0;
|
|
uint32_t i = 0;
|
|
|
|
instance = DMA_GetInstance(base);
|
|
|
|
/* Compute start channel */
|
|
for (i = 0; i < instance; i++)
|
|
{
|
|
startChannel += FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(s_dmaBases[i]);
|
|
}
|
|
|
|
return startChannel;
|
|
}
|
|
|
|
void DMA_Init(DMA_Type *base)
|
|
{
|
|
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
|
|
/* enable dma clock gate */
|
|
CLOCK_EnableClock(s_dmaClockName[DMA_GetInstance(base)]);
|
|
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
|
|
/* set descriptor table */
|
|
base->SRAMBASE = (uint32_t)s_dma_descriptor_table;
|
|
/* enable dma peripheral */
|
|
base->CTRL |= DMA_CTRL_ENABLE_MASK;
|
|
}
|
|
|
|
void DMA_Deinit(DMA_Type *base)
|
|
{
|
|
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
|
|
CLOCK_DisableClock(s_dmaClockName[DMA_GetInstance(base)]);
|
|
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
|
|
/* Disable DMA peripheral */
|
|
base->CTRL &= ~(DMA_CTRL_ENABLE_MASK);
|
|
}
|
|
|
|
void DMA_ConfigureChannelTrigger(DMA_Type *base, uint32_t channel, dma_channel_trigger_t *trigger)
|
|
{
|
|
assert((channel < FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(base)) && (NULL != trigger));
|
|
|
|
uint32_t tmp = (DMA_CHANNEL_CFG_HWTRIGEN_MASK | DMA_CHANNEL_CFG_TRIGPOL_MASK | DMA_CHANNEL_CFG_TRIGTYPE_MASK |
|
|
DMA_CHANNEL_CFG_TRIGBURST_MASK | DMA_CHANNEL_CFG_BURSTPOWER_MASK |
|
|
DMA_CHANNEL_CFG_SRCBURSTWRAP_MASK | DMA_CHANNEL_CFG_DSTBURSTWRAP_MASK);
|
|
tmp = base->CHANNEL[channel].CFG & (~tmp);
|
|
tmp |= (uint32_t)(trigger->type) | (uint32_t)(trigger->burst) | (uint32_t)(trigger->wrap);
|
|
base->CHANNEL[channel].CFG = tmp;
|
|
}
|
|
|
|
/*!
|
|
* @brief Gets the remaining bytes of the current DMA descriptor transfer.
|
|
*
|
|
* @param base DMA peripheral base address.
|
|
* @param channel DMA channel number.
|
|
* @return The number of bytes which have not been transferred yet.
|
|
*/
|
|
uint32_t DMA_GetRemainingBytes(DMA_Type *base, uint32_t channel)
|
|
{
|
|
assert(channel < FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(base));
|
|
|
|
/* NOTE: when descriptors are chained, ACTIVE bit is set for whole chain. It makes
|
|
* impossible to distinguish between:
|
|
* - transfer finishes (represented by value '0x3FF')
|
|
* - and remaining 1024 bytes to transfer (value 0x3FF)
|
|
* for all descriptor in chain, except the last one.
|
|
* If you decide to use this function, please use 1023 transfers as maximal value */
|
|
|
|
/* Channel not active (transfer finished) and value is 0x3FF - nothing to transfer */
|
|
if ((!DMA_ChannelIsActive(base, channel)) &&
|
|
(0x3FF == ((base->CHANNEL[channel].XFERCFG & DMA_CHANNEL_XFERCFG_XFERCOUNT_MASK) >>
|
|
DMA_CHANNEL_XFERCFG_XFERCOUNT_SHIFT)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return ((base->CHANNEL[channel].XFERCFG & DMA_CHANNEL_XFERCFG_XFERCOUNT_MASK) >>
|
|
DMA_CHANNEL_XFERCFG_XFERCOUNT_SHIFT) +
|
|
1;
|
|
}
|
|
|
|
static void DMA_SetupDescriptor(
|
|
dma_descriptor_t *desc, uint32_t xfercfg, void *srcEndAddr, void *dstEndAddr, void *nextDesc)
|
|
{
|
|
desc->xfercfg = xfercfg;
|
|
desc->srcEndAddr = srcEndAddr;
|
|
desc->dstEndAddr = dstEndAddr;
|
|
desc->linkToNextDesc = nextDesc;
|
|
}
|
|
|
|
/* Verify and convert dma_xfercfg_t to XFERCFG register */
|
|
static void DMA_SetupXferCFG(dma_xfercfg_t *xfercfg, uint32_t *xfercfg_addr)
|
|
{
|
|
assert(xfercfg != NULL);
|
|
/* check source increment */
|
|
assert((xfercfg->srcInc == 0) || (xfercfg->srcInc == 1) || (xfercfg->srcInc == 2) || (xfercfg->srcInc == 4));
|
|
/* check destination increment */
|
|
assert((xfercfg->dstInc == 0) || (xfercfg->dstInc == 1) || (xfercfg->dstInc == 2) || (xfercfg->dstInc == 4));
|
|
/* check data width */
|
|
assert((xfercfg->byteWidth == 1) || (xfercfg->byteWidth == 2) || (xfercfg->byteWidth == 4));
|
|
/* check transfer count */
|
|
assert(xfercfg->transferCount <= DMA_MAX_TRANSFER_COUNT);
|
|
|
|
uint32_t xfer = 0, tmp;
|
|
/* set valid flag - descriptor is ready now */
|
|
xfer |= DMA_CHANNEL_XFERCFG_CFGVALID(xfercfg->valid ? 1 : 0);
|
|
/* set reload - allow link to next descriptor */
|
|
xfer |= DMA_CHANNEL_XFERCFG_RELOAD(xfercfg->reload ? 1 : 0);
|
|
/* set swtrig flag - start transfer */
|
|
xfer |= DMA_CHANNEL_XFERCFG_SWTRIG(xfercfg->swtrig ? 1 : 0);
|
|
/* set transfer count */
|
|
xfer |= DMA_CHANNEL_XFERCFG_CLRTRIG(xfercfg->clrtrig ? 1 : 0);
|
|
/* set INTA */
|
|
xfer |= DMA_CHANNEL_XFERCFG_SETINTA(xfercfg->intA ? 1 : 0);
|
|
/* set INTB */
|
|
xfer |= DMA_CHANNEL_XFERCFG_SETINTB(xfercfg->intB ? 1 : 0);
|
|
/* set data width */
|
|
tmp = xfercfg->byteWidth == 4 ? 2 : xfercfg->byteWidth - 1;
|
|
xfer |= DMA_CHANNEL_XFERCFG_WIDTH(tmp);
|
|
/* set source increment value */
|
|
tmp = xfercfg->srcInc == 4 ? 3 : xfercfg->srcInc;
|
|
xfer |= DMA_CHANNEL_XFERCFG_SRCINC(tmp);
|
|
/* set destination increment value */
|
|
tmp = xfercfg->dstInc == 4 ? 3 : xfercfg->dstInc;
|
|
xfer |= DMA_CHANNEL_XFERCFG_DSTINC(tmp);
|
|
/* set transfer count */
|
|
xfer |= DMA_CHANNEL_XFERCFG_XFERCOUNT(xfercfg->transferCount - 1);
|
|
|
|
/* store xferCFG */
|
|
*xfercfg_addr = xfer;
|
|
}
|
|
|
|
void DMA_CreateDescriptor(dma_descriptor_t *desc, dma_xfercfg_t *xfercfg, void *srcAddr, void *dstAddr, void *nextDesc)
|
|
{
|
|
uint32_t xfercfg_reg = 0;
|
|
|
|
assert((NULL != desc) && (0 == (uint32_t)desc % 16) && (NULL != xfercfg));
|
|
assert((NULL != srcAddr) && (0 == (uint32_t)srcAddr % xfercfg->byteWidth));
|
|
assert((NULL != dstAddr) && (0 == (uint32_t)dstAddr % xfercfg->byteWidth));
|
|
assert((NULL == nextDesc) || (0 == (uint32_t)nextDesc % 16));
|
|
|
|
/* Setup channel configuration */
|
|
DMA_SetupXferCFG(xfercfg, &xfercfg_reg);
|
|
|
|
/* Set descriptor structure */
|
|
DMA_SetupDescriptor(
|
|
desc, xfercfg_reg, (uint8_t *)srcAddr + (xfercfg->srcInc * xfercfg->byteWidth * (xfercfg->transferCount - 1)),
|
|
(uint8_t *)dstAddr + (xfercfg->dstInc * xfercfg->byteWidth * (xfercfg->transferCount - 1)), nextDesc);
|
|
}
|
|
|
|
void DMA_AbortTransfer(dma_handle_t *handle)
|
|
{
|
|
assert(NULL != handle);
|
|
|
|
DMA_DisableChannel(handle->base, handle->channel);
|
|
while (DMA_COMMON_CONST_REG_GET(handle->base, handle->channel, BUSY) & (1U << DMA_CHANNEL_INDEX(handle->channel)))
|
|
{
|
|
}
|
|
DMA_COMMON_REG_GET(handle->base, handle->channel, ABORT) |= 1U << DMA_CHANNEL_INDEX(handle->channel);
|
|
DMA_EnableChannel(handle->base, handle->channel);
|
|
}
|
|
|
|
void DMA_CreateHandle(dma_handle_t *handle, DMA_Type *base, uint32_t channel)
|
|
{
|
|
assert((NULL != handle) && (channel < FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(base)));
|
|
|
|
int32_t dmaInstance;
|
|
uint32_t startChannel = 0;
|
|
/* base address is invalid DMA instance */
|
|
dmaInstance = DMA_GetInstance(base);
|
|
startChannel = DMA_GetVirtualStartChannel(base);
|
|
|
|
memset(handle, 0, sizeof(*handle));
|
|
handle->base = base;
|
|
handle->channel = channel;
|
|
s_DMAHandle[startChannel + channel] = handle;
|
|
/* Enable NVIC interrupt */
|
|
EnableIRQ(s_dmaIRQNumber[dmaInstance]);
|
|
}
|
|
|
|
void DMA_SetCallback(dma_handle_t *handle, dma_callback callback, void *userData)
|
|
{
|
|
assert(handle != NULL);
|
|
|
|
handle->callback = callback;
|
|
handle->userData = userData;
|
|
}
|
|
|
|
void DMA_PrepareTransfer(dma_transfer_config_t *config,
|
|
void *srcAddr,
|
|
void *dstAddr,
|
|
uint32_t byteWidth,
|
|
uint32_t transferBytes,
|
|
dma_transfer_type_t type,
|
|
void *nextDesc)
|
|
{
|
|
uint32_t xfer_count;
|
|
assert((NULL != config) && (NULL != srcAddr) && (NULL != dstAddr));
|
|
assert((byteWidth == 1) || (byteWidth == 2) || (byteWidth == 4));
|
|
|
|
/* check max */
|
|
xfer_count = transferBytes / byteWidth;
|
|
assert((xfer_count <= DMA_MAX_TRANSFER_COUNT) && (0 == transferBytes % byteWidth));
|
|
|
|
memset(config, 0, sizeof(*config));
|
|
switch (type)
|
|
{
|
|
case kDMA_MemoryToMemory:
|
|
config->xfercfg.srcInc = 1;
|
|
config->xfercfg.dstInc = 1;
|
|
config->isPeriph = false;
|
|
break;
|
|
case kDMA_PeripheralToMemory:
|
|
/* Peripheral register - source doesn't increment */
|
|
config->xfercfg.srcInc = 0;
|
|
config->xfercfg.dstInc = 1;
|
|
config->isPeriph = true;
|
|
break;
|
|
case kDMA_MemoryToPeripheral:
|
|
/* Peripheral register - destination doesn't increment */
|
|
config->xfercfg.srcInc = 1;
|
|
config->xfercfg.dstInc = 0;
|
|
config->isPeriph = true;
|
|
break;
|
|
case kDMA_StaticToStatic:
|
|
config->xfercfg.srcInc = 0;
|
|
config->xfercfg.dstInc = 0;
|
|
config->isPeriph = true;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
config->dstAddr = (uint8_t *)dstAddr;
|
|
config->srcAddr = (uint8_t *)srcAddr;
|
|
config->nextDesc = (uint8_t *)nextDesc;
|
|
config->xfercfg.transferCount = xfer_count;
|
|
config->xfercfg.byteWidth = byteWidth;
|
|
config->xfercfg.intA = true;
|
|
config->xfercfg.reload = nextDesc != NULL;
|
|
config->xfercfg.valid = true;
|
|
}
|
|
|
|
status_t DMA_SubmitTransfer(dma_handle_t *handle, dma_transfer_config_t *config)
|
|
{
|
|
assert((NULL != handle) && (NULL != config));
|
|
uint32_t instance = DMA_GetInstance(handle->base);
|
|
|
|
/* Previous transfer has not finished */
|
|
if (DMA_ChannelIsActive(handle->base, handle->channel))
|
|
{
|
|
return kStatus_DMA_Busy;
|
|
}
|
|
|
|
/* enable/disable peripheral request */
|
|
if (config->isPeriph)
|
|
{
|
|
DMA_EnableChannelPeriphRq(handle->base, handle->channel);
|
|
}
|
|
else
|
|
{
|
|
DMA_DisableChannelPeriphRq(handle->base, handle->channel);
|
|
}
|
|
|
|
DMA_CreateDescriptor(&(s_dma_descriptor_table[instance][handle->channel]), &config->xfercfg, config->srcAddr,
|
|
config->dstAddr, config->nextDesc);
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
void DMA_StartTransfer(dma_handle_t *handle)
|
|
{
|
|
assert(NULL != handle);
|
|
|
|
uint32_t instance = DMA_GetInstance(handle->base);
|
|
|
|
/* Enable channel interrupt */
|
|
DMA_EnableChannelInterrupts(handle->base, handle->channel);
|
|
|
|
/* If HW trigger is enabled - disable SW trigger */
|
|
if (handle->base->CHANNEL[handle->channel].CFG & DMA_CHANNEL_CFG_HWTRIGEN_MASK)
|
|
{
|
|
s_dma_descriptor_table[instance][handle->channel].xfercfg &= ~(DMA_CHANNEL_XFERCFG_SWTRIG_MASK);
|
|
}
|
|
/* Otherwise enable SW trigger */
|
|
else
|
|
{
|
|
s_dma_descriptor_table[instance][handle->channel].xfercfg |= DMA_CHANNEL_XFERCFG_SWTRIG_MASK;
|
|
}
|
|
|
|
/* Set channel XFERCFG register according first channel descriptor. */
|
|
handle->base->CHANNEL[handle->channel].XFERCFG = s_dma_descriptor_table[instance][handle->channel].xfercfg;
|
|
/* At this moment, the channel ACTIVE bit is set and application cannot modify
|
|
* or start another transfer using this channel. Channel ACTIVE bit is cleared by
|
|
* 'AbortTransfer' function or when the transfer finishes */
|
|
}
|
|
|
|
void DMA_IRQHandle(DMA_Type *base)
|
|
{
|
|
dma_handle_t *handle;
|
|
int32_t channel_index;
|
|
uint32_t startChannel = DMA_GetVirtualStartChannel(base);
|
|
uint32_t i = 0;
|
|
|
|
/* Find channels that have completed transfer */
|
|
for (i = 0; i < FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(base); i++)
|
|
{
|
|
handle = s_DMAHandle[i + startChannel];
|
|
/* Handle is not present */
|
|
if (NULL == handle)
|
|
{
|
|
continue;
|
|
}
|
|
channel_index = DMA_CHANNEL_INDEX(handle->channel);
|
|
/* Channel uses INTA flag */
|
|
if (DMA_COMMON_REG_GET(handle->base, handle->channel, INTA) & (1U << channel_index))
|
|
{
|
|
/* Clear INTA flag */
|
|
DMA_COMMON_REG_SET(handle->base, handle->channel, INTA, (1U << channel_index));
|
|
if (handle->callback)
|
|
{
|
|
(handle->callback)(handle, handle->userData, true, kDMA_IntA);
|
|
}
|
|
}
|
|
/* Channel uses INTB flag */
|
|
if (DMA_COMMON_REG_GET(handle->base, handle->channel, INTB) & (1U << channel_index))
|
|
{
|
|
/* Clear INTB flag */
|
|
DMA_COMMON_REG_SET(handle->base, handle->channel, INTB, (1U << channel_index));
|
|
if (handle->callback)
|
|
{
|
|
(handle->callback)(handle, handle->userData, true, kDMA_IntB);
|
|
}
|
|
}
|
|
/* Error flag */
|
|
if (DMA_COMMON_REG_GET(handle->base, handle->channel, ERRINT) & (1U << channel_index))
|
|
{
|
|
/* Clear error flag */
|
|
DMA_COMMON_REG_SET(handle->base, handle->channel, ERRINT, (1U << channel_index));
|
|
if (handle->callback)
|
|
{
|
|
(handle->callback)(handle, handle->userData, false, kDMA_IntError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DMA0_DriverIRQHandler(void)
|
|
{
|
|
DMA_IRQHandle(DMA0);
|
|
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
|
|
exception return operation might vector to incorrect interrupt */
|
|
#if defined __CORTEX_M && (__CORTEX_M == 4U)
|
|
__DSB();
|
|
#endif
|
|
}
|
|
|
|
#if defined(DMA1)
|
|
void DMA1_DriverIRQHandler(void)
|
|
{
|
|
DMA_IRQHandle(DMA1);
|
|
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
|
|
exception return operation might vector to incorrect interrupt */
|
|
#if defined __CORTEX_M && (__CORTEX_M == 4U)
|
|
__DSB();
|
|
#endif
|
|
}
|
|
#endif
|