123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173 |
- /***************************************************************************//**
- * @file
- * @brief Direct memory access (DMA) module peripheral API
- * @author Energy Micro AS
- * @version 3.0.0
- *******************************************************************************
- * @section License
- * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
- *******************************************************************************
- *
- * 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.
- * 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.
- *
- * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
- * obligation to support this Software. Energy Micro AS is providing the
- * Software "AS IS", with no express or implied warranties of any kind,
- * including, but not limited to, any implied warranties of merchantability
- * or fitness for any particular purpose or warranties against infringement
- * of any proprietary rights of a third party.
- *
- * Energy Micro AS will not be liable for any consequential, incidental, or
- * special damages, or any other relief, or for any claim by any third party,
- * arising from your use of this Software.
- *
- ******************************************************************************/
- #include "em_dma.h"
- #include "em_cmu.h"
- #include "em_assert.h"
- #include "em_bitband.h"
- /***************************************************************************//**
- * @addtogroup EM_Library
- * @{
- ******************************************************************************/
- /***************************************************************************//**
- * @addtogroup DMA
- * @brief Direct Memory Access (DMA) Peripheral API
- * @details
- * These DMA access functions provide basic support for the following
- * types of DMA cycles:
- *
- * @li @b Basic, used for transferring data between memory and peripherals.
- * @li @b Auto-request, used for transferring data between memory locations.
- * @li @b Ping-pong, used for for continuous transfer of data between memory
- * and peripherals, automatically toggling between primary and alternate
- * descriptors.
- * @li @b Memory @b scatter-gather, used for transferring a number of buffers
- * between memory locations.
- * @li @b Peripheral @b scatter-gather, used for transferring a number of
- * buffers between memory and peripherals.
- *
- * A basic understanding of the DMA controller is assumed. Please refer to
- * the EFM32 reference manual for further details.
- *
- * The term 'descriptor' is used as a synonym to the 'channel control data
- * structure' term.
- *
- * In order to use the DMA controller, the initialization function must have
- * been executed once (normally during system init):
- * @verbatim
- * DMA_Init();
- * @endverbatim
- *
- * Then, normally a user of a DMA channel configures the channel:
- * @verbatim
- * DMA_CfgChannel();
- * @endverbatim
- *
- * The channel configuration only has to be done once, if reusing the channel
- * for the same purpose later.
- *
- * In order to set up a DMA cycle, the primary and/or alternate descriptor
- * has to be set up as indicated below.
- *
- * For basic or auto-request cycles, use once on either primary or alternate
- * descriptor:
- * @verbatim
- * DMA_CfgDescr();
- * @endverbatim
- *
- * For ping-pong cycles, configure both primary or alternate descriptors:
- * @verbatim
- * DMA_CfgDescr(); // Primary descriptor config
- * DMA_CfgDescr(); // Alternate descriptor config
- * @endverbatim
- *
- * For scatter-gather cycles, the alternate descriptor array must be programmed:
- * @verbatim
- * // 'n' is the number of scattered buffers
- * // 'descr' points to the start of the alternate descriptor array
- *
- * // Fill in 'cfg'
- * DMA_CfgDescrScatterGather(descr, 0, cfg);
- * // Fill in 'cfg'
- * DMA_CfgDescrScatterGather(descr, 1, cfg);
- * :
- * // Fill in 'cfg'
- * DMA_CfgDescrScatterGather(descr, n - 1, cfg);
- * @endverbatim
- *
- * In many cases, the descriptor configuration only has to be done once, if
- * re-using the channel for the same type of DMA cycles later.
- *
- * In order to activate the DMA cycle, use the respective DMA_Activate...()
- * function.
- *
- * For ping-pong DMA cycles, use DMA_RefreshPingPong() from the callback to
- * prepare the completed descriptor for reuse. Notice that the refresh must
- * be done prior to the other active descriptor completes, otherwise the
- * ping-pong DMA cycle will halt.
- * @{
- ******************************************************************************/
- /*******************************************************************************
- ************************** LOCAL FUNCTIONS ********************************
- ******************************************************************************/
- /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
- /***************************************************************************//**
- * @brief
- * Prepare descriptor for DMA cycle.
- *
- * @details
- * This function prepares the last pieces of configuration required to start a
- * DMA cycle. Since the DMA controller itself modifies some parts of the
- * descriptor during use, those parts need to be refreshed if reusing a
- * descriptor configuration.
- *
- * @note
- * If using this function on a descriptor already activated and in use by the
- * DMA controller, the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to prepare for DMA cycle.
- *
- * @param[in] cycleCtrl
- * DMA cycle type to prepare for.
- *
- * @param[in] primary
- * @li true - prepare primary descriptor
- * @li false - prepare alternate descriptor
- *
- * @param[in] useBurst
- * The burst feature is only used on peripherals supporting DMA bursts.
- * Bursts must not be used if the total length (as given by nMinus1) is
- * less than the arbitration rate configured for the descriptor. Please
- * refer to the reference manual for further details on burst usage.
- *
- * @param[in] dst
- * Address to start location to transfer data to. If NULL, leave setting in
- * descriptor as is.
- *
- * @param[in] src
- * Address to start location to transfer data from. If NULL, leave setting in
- * descriptor as is.
- *
- * @param[in] nMinus1
- * Number of elements (minus 1) to transfer (<= 1023).
- ******************************************************************************/
- static void DMA_Prepare(unsigned int channel,
- DMA_CycleCtrl_TypeDef cycleCtrl,
- bool primary,
- bool useBurst,
- void *dst,
- void *src,
- unsigned int nMinus1)
- {
- DMA_DESCRIPTOR_TypeDef *descr;
- DMA_DESCRIPTOR_TypeDef *primDescr;
- DMA_CB_TypeDef *cb;
- uint32_t inc;
- uint32_t chBit;
- uint32_t tmp;
- primDescr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE)) + channel;
- /* Find descriptor to configure */
- if (primary)
- {
- descr = primDescr;
- }
- else
- {
- descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE)) + channel;
- }
- /* If callback defined, update info on whether callback is issued */
- /* for primary or alternate descriptor. Mainly needed for ping-pong */
- /* cycles. */
- cb = (DMA_CB_TypeDef *)(primDescr->USER);
- if (cb)
- {
- cb->primary = (uint8_t)primary;
- }
- if (src)
- {
- inc = (descr->CTRL & _DMA_CTRL_SRC_INC_MASK) >> _DMA_CTRL_SRC_INC_SHIFT;
- if (inc == _DMA_CTRL_SRC_INC_NONE)
- {
- descr->SRCEND = src;
- }
- else
- {
- descr->SRCEND = (void *)((uint32_t)src + (nMinus1 << inc));
- }
- }
- if (dst)
- {
- inc = (descr->CTRL & _DMA_CTRL_DST_INC_MASK) >> _DMA_CTRL_DST_INC_SHIFT;
- if (inc == _DMA_CTRL_DST_INC_NONE)
- {
- descr->DSTEND = dst;
- }
- else
- {
- descr->DSTEND = (void *)((uint32_t)dst + (nMinus1 << inc));
- }
- }
- chBit = 1 << channel;
- if (useBurst)
- {
- DMA->CHUSEBURSTS = chBit;
- }
- else
- {
- DMA->CHUSEBURSTC = chBit;
- }
- if (primary)
- {
- DMA->CHALTC = chBit;
- }
- else
- {
- DMA->CHALTS = chBit;
- }
- /* Set cycle control */
- tmp = descr->CTRL & ~(_DMA_CTRL_CYCLE_CTRL_MASK | _DMA_CTRL_N_MINUS_1_MASK);
- tmp |= nMinus1 << _DMA_CTRL_N_MINUS_1_SHIFT;
- tmp |= (uint32_t)cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT;
- descr->CTRL = tmp;
- }
- /** @endcond */
- /*******************************************************************************
- ************************ INTERRUPT FUNCTIONS ******************************
- ******************************************************************************/
- /***************************************************************************//**
- * @brief
- * Interrupt handler for DMA cycle completion handling.
- *
- * @details
- * Clears any pending flags and calls registered callback (if any).
- *
- * If using the default interrupt vector table setup provided, this function
- * is automatically placed in the IRQ table due to weak linking. If taking
- * control over the interrupt vector table in some other way, this interrupt
- * handler must be installed in order to be able to support callback actions.
- ******************************************************************************/
- void DMA_IRQHandler(void)
- {
- DMA_DESCRIPTOR_TypeDef *descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE);
- int channel;
- DMA_CB_TypeDef *cb;
- uint32_t pending;
- uint32_t pendingPrio;
- uint32_t prio;
- uint32_t primaryCpy;
- int i;
- /* Get all pending interrupts */
- pending = DMA->IF;
- /* Check for bus error */
- if (pending & DMA_IF_ERR)
- {
- /* Loop here to enable the debugger to see what has happened */
- while (1)
- ;
- }
- /* Process all pending channel interrupts. First process channels */
- /* defined with high priority, then those with default priority. */
- prio = DMA->CHPRIS;
- pendingPrio = pending & prio;
- for (i = 0; i < 2; i++)
- {
- channel = 0;
- /* Process pending interrupts within high/default priority group */
- /* honouring priority within group. */
- while (pendingPrio)
- {
- if (pendingPrio & 1)
- {
- /* Clear pending interrupt prior to invoking callback, in case it */
- /* sets up another DMA cycle. */
- DMA->IFC = 1 << channel;
- /* Normally, no point in enabling interrupt without callback, but */
- /* check if callback is defined anyway. Callback info is always */
- /* located in primary descriptor. */
- cb = (DMA_CB_TypeDef *)(descr[channel].USER);
- if (cb)
- {
- /* Toggle next-descriptor indicator always prior to invoking */
- /* callback (in case callback reconfigurs something) */
- primaryCpy = cb->primary;
- cb->primary ^= 1;
- if (cb->cbFunc)
- {
- cb->cbFunc(channel, (bool)primaryCpy, cb->userPtr);
- }
- }
- }
- pendingPrio >>= 1;
- channel++;
- }
- /* On second iteration, process default priority channels */
- pendingPrio = pending & ~prio;
- }
- }
- /*******************************************************************************
- ************************** GLOBAL FUNCTIONS *******************************
- ******************************************************************************/
- /***************************************************************************//**
- * @brief
- * Activate DMA auto-request cycle (used for memory-memory transfers).
- *
- * @details
- * Prior to activating the DMA cycle, the channel and descriptor to be used
- * must have been properly configured.
- *
- * @note
- * If using this function on a channel already activated and in use by the
- * DMA controller, the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to activate DMA cycle for.
- *
- * @param[in] primary
- * @li true - activate using primary descriptor
- * @li false - activate using alternate descriptor
- *
- * @param[in] dst
- * Address to start location to transfer data to. If NULL, leave setting in
- * descriptor as is from a previous activation.
- *
- * @param[in] src
- * Address to start location to transfer data from. If NULL, leave setting in
- * descriptor as is from a previous activation.
- *
- * @param[in] nMinus1
- * Number of DMA transfer elements (minus 1) to transfer (<= 1023). The
- * size of the DMA transfer element (1, 2 or 4 bytes) is configured with
- * DMA_CfgDescr().
- ******************************************************************************/
- void DMA_ActivateAuto(unsigned int channel,
- bool primary,
- void *dst,
- void *src,
- unsigned int nMinus1)
- {
- uint32_t chBit;
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
- DMA_Prepare(channel,
- dmaCycleCtrlAuto,
- primary,
- false,
- dst,
- src,
- nMinus1);
- chBit = 1 << channel;
- DMA->CHENS = chBit; /* Enable channel */
- DMA->CHSWREQ = chBit; /* Activate with SW request */
- }
- /***************************************************************************//**
- * @brief
- * Activate DMA basic cycle (used for memory-peripheral transfers).
- *
- * @details
- * Prior to activating the DMA cycle, the channel and descriptor to be used
- * must have been properly configured.
- *
- * @note
- * If using this function on a channel already activated and in use by the
- * DMA controller, the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to activate DMA cycle for.
- *
- * @param[in] primary
- * @li true - activate using primary descriptor
- * @li false - activate using alternate descriptor
- *
- * @param[in] useBurst
- * The burst feature is only used on peripherals supporting DMA bursts.
- * Bursts must not be used if the total length (as given by nMinus1) is
- * less than the arbitration rate configured for the descriptor. Please
- * refer to the reference manual for further details on burst usage.
- *
- * @param[in] dst
- * Address to start location to transfer data to. If NULL, leave setting in
- * descriptor as is from a previous activation.
- *
- * @param[in] src
- * Address to start location to transfer data from. If NULL, leave setting in
- * descriptor as is from a previous activation.
- *
- * @param[in] nMinus1
- * Number of DMA transfer elements (minus 1) to transfer (<= 1023). The
- * size of the DMA transfer element (1, 2 or 4 bytes) is configured with
- * DMA_CfgDescr().
- ******************************************************************************/
- void DMA_ActivateBasic(unsigned int channel,
- bool primary,
- bool useBurst,
- void *dst,
- void *src,
- unsigned int nMinus1)
- {
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
- DMA_Prepare(channel,
- dmaCycleCtrlBasic,
- primary,
- useBurst,
- dst,
- src,
- nMinus1);
- /* Enable channel, request signal is provided by peripheral device */
- DMA->CHENS = 1 << channel;
- }
- /***************************************************************************//**
- * @brief
- * Activate DMA ping-pong cycle (used for memory-peripheral transfers).
- *
- * @details
- * Prior to activating the DMA cycle, the channel and both descriptors must
- * have been properly configured. The primary descriptor is always the first
- * descriptor to be used by the DMA controller.
- *
- * @note
- * If using this function on a channel already activated and in use by the
- * DMA controller, the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to activate DMA cycle for.
- *
- * @param[in] useBurst
- * The burst feature is only used on peripherals supporting DMA bursts.
- * Bursts must not be used if the total length (as given by nMinus1) is
- * less than the arbitration rate configured for the descriptors. Please
- * refer to the reference manual for further details on burst usage. Notice
- * that this setting is used for both the primary and alternate descriptors.
- *
- * @param[in] primDst
- * Address to start location to transfer data to, for primary descriptor.
- * If NULL, leave setting in descriptor as is from a previous activation.
- *
- * @param[in] primSrc
- * Address to start location to transfer data from, for primary descriptor.
- * If NULL, leave setting in descriptor as is from a previous activation.
- *
- * @param[in] primNMinus1
- * Number of DMA transfer elements (minus 1) to transfer (<= 1023), for
- * primary descriptor. The size of the DMA transfer element (1, 2 or 4 bytes)
- * is configured with DMA_CfgDescr().
- *
- * @param[in] altDst
- * Address to start location to transfer data to, for alternate descriptor.
- * If NULL, leave setting in descriptor as is from a previous activation.
- *
- * @param[in] altSrc
- * Address to start location to transfer data from, for alternate descriptor.
- * If NULL, leave setting in descriptor as is from a previous activation.
- *
- * @param[in] altNMinus1
- * Number of DMA transfer elements (minus 1) to transfer (<= 1023), for
- * alternate descriptor. The size of the DMA transfer element (1, 2 or 4 bytes)
- * is configured with DMA_CfgDescr().
- ******************************************************************************/
- void DMA_ActivatePingPong(unsigned int channel,
- bool useBurst,
- void *primDst,
- void *primSrc,
- unsigned int primNMinus1,
- void *altDst,
- void *altSrc,
- unsigned int altNMinus1)
- {
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(primNMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
- EFM_ASSERT(altNMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
- /* Prepare alternate descriptor first */
- DMA_Prepare(channel,
- dmaCycleCtrlPingPong,
- false,
- useBurst,
- altDst,
- altSrc,
- altNMinus1);
- /* Prepare primary descriptor last in order to start cycle using it */
- DMA_Prepare(channel,
- dmaCycleCtrlPingPong,
- true,
- useBurst,
- primDst,
- primSrc,
- primNMinus1);
- /* Enable channel, request signal is provided by peripheral device */
- DMA->CHENS = 1 << channel;
- }
- /***************************************************************************//**
- * @brief
- * Activate DMA scatter-gather cycle (used for either memory-peripheral
- * or memory-memory transfers).
- *
- * @details
- * Prior to activating the DMA cycle, the array with alternate descriptors
- * must have been properly configured. This function can be reused without
- * reconfiguring the alternate descriptors, as long as @p count is the same.
- *
- * @note
- * If using this function on a channel already activated and in use by the
- * DMA controller, the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to activate DMA cycle for.
- *
- * @param[in] useBurst
- * The burst feature is only used on peripherals supporting DMA bursts
- * (and thus this parameter is ignored for memory scatter-gather cycles).
- * This parameter determines if bursts should be enabled during DMA transfers
- * using the alternate descriptors. Bursts must not be used if the total
- * length (as given by nMinus1 for the alternate descriptor) is
- * less than the arbitration rate configured for the descriptor. Please
- * refer to the reference manual for further details on burst usage.
- *
- * @param[in,out] altDescr
- * Pointer to start of array with prepared alternate descriptors. The last
- * descriptor will have its cycle control type reprogrammed to basic type.
- *
- * @param[in] count
- * Number of alternate descriptors in @p altDescr array. Maximum number of
- * alternate descriptors is 256.
- ******************************************************************************/
- void DMA_ActivateScatterGather(unsigned int channel,
- bool useBurst,
- DMA_DESCRIPTOR_TypeDef *altDescr,
- unsigned int count)
- {
- DMA_DESCRIPTOR_TypeDef *descr;
- DMA_CB_TypeDef *cb;
- uint32_t cycleCtrl;
- uint32_t chBit;
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(altDescr);
- EFM_ASSERT(count && (count <= 256));
- /* We have to configure the primary descriptor properly in order to */
- /* transfer one complete alternate descriptor from the alternate */
- /* descriptor table into the actual alternate descriptor. */
- descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE) + channel;
- /* Set source end address to point to alternate descriptor array */
- descr->SRCEND = (uint32_t *)altDescr + (count * 4) - 1;
- /* The destination end address in the primary descriptor MUST point */
- /* to the corresponding alternate descriptor in scatter-gather mode. */
- descr->DSTEND = (uint32_t *)((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE) +
- channel + 1) - 1;
- /* The user field of the descriptor is used for callback configuration, */
- /* and already configured when channel is configured. Do not modify it. */
- /* Determine from alternate configuration whether this is a memory or */
- /* peripheral scatter-gather, by looking at the first alternate descriptor. */
- cycleCtrl = altDescr->CTRL & _DMA_CTRL_CYCLE_CTRL_MASK;
- cycleCtrl &= ~(1 << _DMA_CTRL_CYCLE_CTRL_SHIFT);
- EFM_ASSERT((cycleCtrl == dmaCycleCtrlMemScatterGather) ||
- (cycleCtrl == dmaCycleCtrlPerScatterGather));
- /* Set last alternate descriptor to basic or auto-request cycle type in */
- /* order to have dma_done signal asserted when complete. Otherwise interrupt */
- /* will not be triggered when done. */
- altDescr[count - 1].CTRL &= ~_DMA_CTRL_CYCLE_CTRL_MASK;
- if (cycleCtrl == dmaCycleCtrlMemScatterGather)
- {
- altDescr[count - 1].CTRL |= (uint32_t)dmaCycleCtrlAuto << _DMA_CTRL_CYCLE_CTRL_SHIFT;
- }
- else
- {
- altDescr[count - 1].CTRL |= (uint32_t)dmaCycleCtrlBasic << _DMA_CTRL_CYCLE_CTRL_SHIFT;
- }
- /* If callback defined, update info on whether callback is issued for */
- /* primary or alternate descriptor. Not really useful for scatter-gather, */
- /* but do for consistency. Always set to alternate, since that is the last */
- /* descriptor actually used. */
- cb = (DMA_CB_TypeDef *)(descr->USER);
- if (cb)
- {
- cb->primary = false;
- }
- /* Configure primary descriptor control word */
- descr->CTRL =
- ((uint32_t)dmaDataInc4 << _DMA_CTRL_DST_INC_SHIFT) |
- ((uint32_t)dmaDataSize4 << _DMA_CTRL_DST_SIZE_SHIFT) |
- ((uint32_t)dmaDataInc4 << _DMA_CTRL_SRC_INC_SHIFT) |
- ((uint32_t)dmaDataSize4 << _DMA_CTRL_SRC_SIZE_SHIFT) |
- /* Use same protection scheme as for alternate descriptors */
- (altDescr->CTRL & _DMA_CTRL_SRC_PROT_CTRL_MASK) |
- ((uint32_t)dmaArbitrate4 << _DMA_CTRL_R_POWER_SHIFT) |
- (((count * 4) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT) |
- (((uint32_t)useBurst & 1) << _DMA_CTRL_NEXT_USEBURST_SHIFT) |
- cycleCtrl;
- chBit = 1 << channel;
- /* Start with primary descriptor */
- DMA->CHALTC = chBit;
- /* Enable channel */
- DMA->CHENS = chBit;
- /* Send request if memory scatter-gather, otherwise request signal is */
- /* provided by peripheral. */
- if (cycleCtrl == dmaCycleCtrlMemScatterGather)
- {
- DMA->CHSWREQ = chBit;
- }
- }
- /***************************************************************************//**
- * @brief
- * Configure a DMA channel.
- *
- * @details
- * Configure miscellaneous issues for a DMA channel. This function is typically
- * used once to setup a channel for a certain type of use.
- *
- * @note
- * If using this function on a channel already in use by the DMA controller,
- * the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to configure.
- *
- * @param[in] cfg
- * Configuration to use.
- ******************************************************************************/
- void DMA_CfgChannel(unsigned int channel, DMA_CfgChannel_TypeDef *cfg)
- {
- DMA_DESCRIPTOR_TypeDef *descr;
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(cfg);
- /* Always keep callback configuration reference in primary descriptor */
- descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE);
- descr[channel].USER = (uint32_t)(cfg->cb);
- /* Set to specified priority for channel */
- if (cfg->highPri)
- {
- DMA->CHPRIS = 1 << channel;
- }
- else
- {
- DMA->CHPRIC = 1 << channel;
- }
- /* Set DMA signal source select */
- DMA->CH[channel].CTRL = cfg->select;
- /* Enable/disable interrupt as specified */
- if (cfg->enableInt)
- {
- DMA->IFC = (1 << channel);
- BITBAND_Peripheral(&(DMA->IEN), channel, 1);
- }
- else
- {
- BITBAND_Peripheral(&(DMA->IEN), channel, 0);
- }
- }
- /***************************************************************************//**
- * @brief
- * Configure DMA descriptor for auto-request, basic or ping-pong DMA cycles.
- *
- * @details
- * This function is used for configuration of a descriptor for the following
- * DMA cycle types:
- *
- * @li auto-request - used for memory/memory transfer
- * @li basic - used for a peripheral/memory transfer
- * @li ping-pong - used for a ping-pong based peripheral/memory transfer
- * style providing time to refresh one descriptor while the other is
- * in use.
- *
- * The DMA cycle is not activated, please see DMA_ActivateAuto(),
- * DMA_ActivateBasic() or DMA_ActivatePingPong() to activate the DMA cycle.
- * In many cases, the configuration only has to be done once, and all
- * subsequent cycles may be activated with the activate function.
- *
- * For ping-pong DMA cycles, this function must be used both on the primary
- * and the alternate descriptor prior to activating the DMA cycle.
- *
- * Notice that the DMA channel must also be configured, see DMA_CfgChannel().
- *
- * @note
- * If using this function on a descriptor already activated and in use by
- * the DMA controller, the behaviour is undefined.
- *
- * @param[in] channel
- * DMA channel to configure for.
- *
- * @param[in] primary
- * @li true - configure primary descriptor
- * @li false - configure alternate descriptor
- *
- * @param[in] cfg
- * Configuration to use.
- ******************************************************************************/
- void DMA_CfgDescr(unsigned int channel,
- bool primary,
- DMA_CfgDescr_TypeDef *cfg)
- {
- DMA_DESCRIPTOR_TypeDef *descr;
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(cfg);
- /* Find descriptor to configure */
- if (primary)
- {
- descr = (DMA_DESCRIPTOR_TypeDef *)DMA->CTRLBASE;
- }
- else
- {
- descr = (DMA_DESCRIPTOR_TypeDef *)DMA->ALTCTRLBASE;
- }
- descr += channel;
- /* Prepare the descriptor */
- /* Source/destination end addresses set when started */
- descr->CTRL =
- (cfg->dstInc << _DMA_CTRL_DST_INC_SHIFT) |
- (cfg->size << _DMA_CTRL_DST_SIZE_SHIFT) |
- (cfg->srcInc << _DMA_CTRL_SRC_INC_SHIFT) |
- (cfg->size << _DMA_CTRL_SRC_SIZE_SHIFT) |
- ((uint32_t)(cfg->hprot) << _DMA_CTRL_SRC_PROT_CTRL_SHIFT) |
- (cfg->arbRate << _DMA_CTRL_R_POWER_SHIFT) |
- (0 << _DMA_CTRL_N_MINUS_1_SHIFT) | /* Set when activated */
- (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) | /* Set when activated */
- DMA_CTRL_CYCLE_CTRL_INVALID; /* Set when activated */
- }
- #if defined(_EFM32_GIANT_FAMILY)
- /***************************************************************************//**
- * @brief Configure DMA channel for Loop mode or 2D transfer.
- *
- * @details
- * For 2D transfer, set cfg->enable to "false", and only configure nMinus1
- * to same width as channel descriptor.
- *
- * @param[in] channel
- * DMA channel to configure for.
- *
- * @param[in] cfg
- * Configuration to use.
- ******************************************************************************/
- void DMA_CfgLoop(unsigned int channel, DMA_CfgLoop_TypeDef *cfg)
- {
- EFM_ASSERT(channel <= 1);
- EFM_ASSERT(cfg->nMinus1 <= 1023);
- /* Configure LOOP setting */
- switch( channel )
- {
- case 0:
- DMA->LOOP0 = (cfg->enable << _DMA_LOOP0_EN_SHIFT|
- cfg->nMinus1 << _DMA_LOOP0_WIDTH_SHIFT);
- break;
- case 1:
- DMA->LOOP1 = (cfg->enable << _DMA_LOOP1_EN_SHIFT|
- cfg->nMinus1 << _DMA_LOOP1_WIDTH_SHIFT);
- break;
- }
- }
- /***************************************************************************//**
- * @brief Configure DMA channel 2D transfer properties.
- *
- * @param[in] channel
- * DMA channel to configure for.
- *
- * @param[in] cfg
- * Configuration to use.
- ******************************************************************************/
- void DMA_CfgRect(unsigned int channel, DMA_CfgRect_TypeDef *cfg)
- {
- EFM_ASSERT(channel == 0);
- EFM_ASSERT(cfg->dstStride <= 2047);
- EFM_ASSERT(cfg->srcStride <= 2047);
- EFM_ASSERT(cfg->height <= 1023);
- /* Configure rectangular/2D copy */
- DMA->RECT0 = (cfg->dstStride << _DMA_RECT0_DSTSTRIDE_SHIFT|
- cfg->srcStride << _DMA_RECT0_SRCSTRIDE_SHIFT|
- cfg->height << _DMA_RECT0_HEIGHT_SHIFT);
- }
- #endif
- /***************************************************************************//**
- * @brief
- * Configure an alternate DMA descriptor for use with scatter-gather DMA
- * cycles.
- *
- * @details
- * In scatter-gather mode, the alternate descriptors are located in one
- * contiguous memory area. Each of the alternate descriptor must be fully
- * configured prior to starting the scatter-gather DMA cycle.
- *
- * The DMA cycle is not activated by this function, please see
- * DMA_ActivateScatterGather() to activate the DMA cycle. In some cases, the
- * alternate configuration only has to be done once, and all subsequent
- * transfers may be activated with the activate function.
- *
- * Notice that the DMA channel must also be configured, see DMA_CfgChannel().
- *
- * @param[in] descr
- * Points to start of memory area holding the alternate descriptors.
- *
- * @param[in] indx
- * Alternate descriptor index number to configure (numbered from 0).
- *
- * @param[in] cfg
- * Configuration to use.
- ******************************************************************************/
- void DMA_CfgDescrScatterGather(DMA_DESCRIPTOR_TypeDef *descr,
- unsigned int indx,
- DMA_CfgDescrSGAlt_TypeDef *cfg)
- {
- uint32_t cycleCtrl;
- EFM_ASSERT(descr);
- EFM_ASSERT(cfg);
- /* Point to selected entry in alternate descriptor table */
- descr += indx;
- if (cfg->srcInc == dmaDataIncNone)
- {
- descr->SRCEND = cfg->src;
- }
- else
- {
- descr->SRCEND = (void *)((uint32_t)(cfg->src) + ((uint32_t)(cfg->nMinus1) << cfg->srcInc));
- }
- if (cfg->dstInc == dmaDataIncNone)
- {
- descr->DSTEND = cfg->dst;
- }
- else
- {
- descr->DSTEND = (void *)((uint32_t)(cfg->dst) + ((uint32_t)(cfg->nMinus1) << cfg->dstInc));
- }
- /* User definable part not used */
- descr->USER = 0;
- if (cfg->peripheral)
- {
- cycleCtrl = (uint32_t)dmaCycleCtrlPerScatterGather + 1;
- }
- else
- {
- cycleCtrl = (uint32_t)dmaCycleCtrlMemScatterGather + 1;
- }
- descr->CTRL =
- (cfg->dstInc << _DMA_CTRL_DST_INC_SHIFT) |
- (cfg->size << _DMA_CTRL_DST_SIZE_SHIFT) |
- (cfg->srcInc << _DMA_CTRL_SRC_INC_SHIFT) |
- (cfg->size << _DMA_CTRL_SRC_SIZE_SHIFT) |
- ((uint32_t)(cfg->hprot) << _DMA_CTRL_SRC_PROT_CTRL_SHIFT) |
- (cfg->arbRate << _DMA_CTRL_R_POWER_SHIFT) |
- ((uint32_t)(cfg->nMinus1) << _DMA_CTRL_N_MINUS_1_SHIFT) |
- /* Never set next useburst bit, since the descriptor used after the */
- /* alternate descriptor is the primary descriptor which operates on */
- /* memory. If the alternate descriptors need to have useBurst set, this */
- /* done when setting up the primary descriptor, ie when activating. */
- (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) |
- (cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT);
- }
- /***************************************************************************//**
- * @brief
- * Check if DMA channel is enabled.
- *
- * @details
- * The DMA channel is disabled when the DMA controller has finished a DMA
- * cycle.
- *
- * @param[in] channel
- * DMA channel to check.
- *
- * @return
- * true if channel is enabled, false if not.
- ******************************************************************************/
- bool DMA_ChannelEnabled(unsigned int channel)
- {
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- return (bool)((DMA->CHENS >> channel) & 1);
- }
- /***************************************************************************//**
- * @brief
- * Initializes DMA controller.
- *
- * @details
- * This function will reset and prepare the DMA controller for use. Although
- * it may be used several times, it is normally only used during system
- * init. If reused during normal operation, notice that any ongoing DMA
- * transfers will be aborted. When completed, the DMA controller is in
- * an enabled state.
- *
- * @note
- * Must be invoked before using the DMA controller.
- *
- * @param[in] init
- * Pointer to a structure containing DMA init information.
- ******************************************************************************/
- void DMA_Init(DMA_Init_TypeDef *init)
- {
- EFM_ASSERT(init);
- /* Make sure control block is properly aligned */
- EFM_ASSERT(!((uint32_t)(init->controlBlock) & (256 - 1)));
- /* Make sure DMA clock is enabled prior to accessing DMA module */
- CMU_ClockEnable(cmuClock_DMA, true);
- /* Make sure DMA controller is set to a known reset state */
- DMA_Reset();
- /* Clear/enable DMA interrupts */
- NVIC_ClearPendingIRQ(DMA_IRQn);
- NVIC_EnableIRQ(DMA_IRQn);
- /* Enable bus error interrupt */
- DMA->IEN = DMA_IEN_ERR;
- /* Set pointer to control block, notice that this ptr must have been */
- /* properly aligned, according to requirements defined in the reference */
- /* manual. */
- DMA->CTRLBASE = (uint32_t)(init->controlBlock);
- /* Configure and enable the DMA controller */
- DMA->CONFIG = ((uint32_t)(init->hprot) << _DMA_CONFIG_CHPROT_SHIFT) | DMA_CONFIG_EN;
- }
- /***************************************************************************//**
- * @brief
- * Refresh a descriptor used in a DMA ping-pong cycle.
- *
- * @details
- * During a ping-pong DMA cycle, the DMA controller automatically alternates
- * between primary and alternate descriptors, when completing use of a
- * descriptor. While the other descriptor is in use by the DMA controller,
- * the SW should refresh the completed descriptor. This is typically done from
- * the callback defined for the ping-pong cycle.
- *
- * @param[in] channel
- * DMA channel to refresh ping-pong descriptor for.
- *
- * @param[in] primary
- * @li true - refresh primary descriptor
- * @li false - refresh alternate descriptor
- *
- * @param[in] useBurst
- * The burst feature is only used on peripherals supporting DMA bursts.
- * Bursts must not be used if the total length (as given by nMinus1) is
- * less than the arbitration rate configured for the descriptor. Please
- * refer to the reference manual for further details on burst usage.
- *
- * @param[in] dst
- * Address to start location to transfer data to. If NULL, leave setting in
- * descriptor as is.
- *
- * @param[in] src
- * Address to start location to transfer data from. If NULL, leave setting in
- * descriptor as is.
- *
- * @param[in] nMinus1
- * Number of DMA transfer elements (minus 1) to transfer (<= 1023). The
- * size of the DMA transfer element (1, 2 or 4 bytes) is configured with
- * DMA_CfgDescr().
- *
- * @param[in] stop
- * Indicate that the DMA ping-pong cycle shall stop @b after completing use
- * of this descriptor.
- ******************************************************************************/
- void DMA_RefreshPingPong(unsigned int channel,
- bool primary,
- bool useBurst,
- void *dst,
- void *src,
- unsigned int nMinus1,
- bool stop)
- {
- DMA_CycleCtrl_TypeDef cycleCtrl;
- DMA_DESCRIPTOR_TypeDef *descr;
- uint32_t inc;
- uint32_t chBit;
- uint32_t tmp;
- EFM_ASSERT(channel < DMA_CHAN_COUNT);
- EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
- /* The ping-pong DMA cycle may be stopped by issuing a basic cycle type */
- if (stop)
- {
- cycleCtrl = dmaCycleCtrlBasic;
- }
- else
- {
- cycleCtrl = dmaCycleCtrlPingPong;
- }
- /* Find descriptor to configure */
- if (primary)
- {
- descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE)) + channel;
- }
- else
- {
- descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE)) + channel;
- }
- if (src)
- {
- inc = (descr->CTRL & _DMA_CTRL_SRC_INC_MASK) >> _DMA_CTRL_SRC_INC_SHIFT;
- if (inc == _DMA_CTRL_SRC_INC_NONE)
- {
- descr->SRCEND = src;
- }
- else
- {
- descr->SRCEND = (void *)((uint32_t)src + (nMinus1 << inc));
- }
- }
- if (dst)
- {
- inc = (descr->CTRL & _DMA_CTRL_DST_INC_MASK) >> _DMA_CTRL_DST_INC_SHIFT;
- if (inc == _DMA_CTRL_DST_INC_NONE)
- {
- descr->DSTEND = dst;
- }
- else
- {
- descr->DSTEND = (void *)((uint32_t)dst + (nMinus1 << inc));
- }
- }
- chBit = 1 << channel;
- if (useBurst)
- {
- DMA->CHUSEBURSTS = chBit;
- }
- else
- {
- DMA->CHUSEBURSTC = chBit;
- }
- /* Set cycle control */
- tmp = descr->CTRL & ~(_DMA_CTRL_CYCLE_CTRL_MASK | _DMA_CTRL_N_MINUS_1_MASK);
- tmp |= nMinus1 << _DMA_CTRL_N_MINUS_1_SHIFT;
- tmp |= cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT;
- descr->CTRL = tmp;
- }
- /***************************************************************************//**
- * @brief
- * Reset the DMA controller.
- *
- * @details
- * This functions will disable the DMA controller and set it to a reset
- * state.
- *
- * @note
- * Notice that any ongoing transfers will be aborted.
- ******************************************************************************/
- void DMA_Reset(void)
- {
- int i;
- /* Disable DMA interrupts */
- NVIC_DisableIRQ(DMA_IRQn);
- /* Put the DMA controller into a known state, first disabling it. */
- DMA->CONFIG = _DMA_CONFIG_RESETVALUE;
- DMA->CHUSEBURSTC = _DMA_CHUSEBURSTC_MASK;
- DMA->CHREQMASKC = _DMA_CHREQMASKC_MASK;
- DMA->CHENC = _DMA_CHENC_MASK;
- DMA->CHALTC = _DMA_CHALTC_MASK;
- DMA->CHPRIC = _DMA_CHPRIC_MASK;
- DMA->ERRORC = DMA_ERRORC_ERRORC;
- DMA->IEN = _DMA_IEN_RESETVALUE;
- DMA->IFC = _DMA_IFC_MASK;
- /* Clear channel control flags */
- for (i = 0; i < DMA_CHAN_COUNT; i++)
- {
- DMA->CH[i].CTRL = _DMA_CH_CTRL_RESETVALUE;
- }
- }
- /** @} (end addtogroup DMA) */
- /** @} (end addtogroup EM_Library) */
|