660 lines
22 KiB
C
660 lines
22 KiB
C
/***************************************************************************//**
|
|
* @file
|
|
* @brief Pulse Counter (PCNT) 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_pcnt.h"
|
|
#include "em_cmu.h"
|
|
#include "em_assert.h"
|
|
#include "em_bitband.h"
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup EM_Library
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup PCNT
|
|
* @brief Pulse Counter (PCNT) Peripheral API
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
******************************* DEFINES ***********************************
|
|
******************************************************************************/
|
|
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
|
|
|
|
/** Validation of PCNT register block pointer reference for assert statements. */
|
|
#if (PCNT_COUNT == 1)
|
|
#define PCNT_REF_VALID(ref) ((ref) == PCNT0)
|
|
#elif (PCNT_COUNT == 2)
|
|
#define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1))
|
|
#elif (PCNT_COUNT == 3)
|
|
#define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1) || \
|
|
((ref) == PCNT2))
|
|
#else
|
|
#error Undefined number of pulse counters (PCNT).
|
|
#endif
|
|
|
|
/** @endcond */
|
|
|
|
|
|
/*******************************************************************************
|
|
************************** LOCAL FUNCTIONS ********************************
|
|
******************************************************************************/
|
|
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Map PCNT structure into instance number.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block
|
|
*
|
|
* @return
|
|
* Instance number.
|
|
******************************************************************************/
|
|
__STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt)
|
|
{
|
|
return(((uint32_t)pcnt - PCNT0_BASE) / 0x400);
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Wait for ongoing sync of register(s) to low frequency domain to complete.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block
|
|
*
|
|
* @param[in] mask
|
|
* Bitmask corresponding to SYNCBUSY register defined bits, indicating
|
|
* registers that must complete any ongoing synchronization.
|
|
******************************************************************************/
|
|
__STATIC_INLINE void PCNT_Sync(PCNT_TypeDef *pcnt, uint32_t mask)
|
|
{
|
|
/* Avoid deadlock if modifying the same register twice when freeze mode is
|
|
* activated. */
|
|
if (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Wait for any pending previous write operation to have been completed in low
|
|
* frequency domain. */
|
|
while (pcnt->SYNCBUSY & mask)
|
|
;
|
|
}
|
|
|
|
/** @endcond */
|
|
|
|
/*******************************************************************************
|
|
************************** GLOBAL FUNCTIONS *******************************
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Reset PCNT counters and TOP register.
|
|
*
|
|
* @note
|
|
* Notice that special SYNCBUSY handling is not applicable for the RSTEN
|
|
* bit of the control register, so we don't need to wait for it when only
|
|
* modifying RSTEN. (It would mean undefined wait time if clocked by external
|
|
* clock.) The SYNCBUSY bit will however be set, leading to a synchronization
|
|
* in the LF domain, with in reality no changes.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
******************************************************************************/
|
|
void PCNT_CounterReset(PCNT_TypeDef *pcnt)
|
|
{
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* Enable reset of CNT and TOP register */
|
|
BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
|
|
|
|
/* Disable reset of CNT and TOP register */
|
|
BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set counter and top values.
|
|
*
|
|
* @details
|
|
* The pulse counter is disabled while changing these values, and reenabled
|
|
* (if originally enabled) when values have been set.
|
|
*
|
|
* @note
|
|
* This function will stall until synchronization to low frequency domain is
|
|
* completed. For that reason, it should normally not be used when using
|
|
* an external clock to clock the PCNT module, since stall time may be
|
|
* undefined in that case. The counter should normally only be set when
|
|
* operating in (or about to enable) #pcntModeOvsSingle mode.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] count
|
|
* Value to set in counter register.
|
|
*
|
|
* @param[in] top
|
|
* Value to set in top register.
|
|
******************************************************************************/
|
|
void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top)
|
|
{
|
|
uint32_t ctrl;
|
|
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* Keep current control setting, must be restored */
|
|
ctrl = pcnt->CTRL;
|
|
|
|
/* If enabled, disable pulse counter before changing values */
|
|
if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
|
|
{
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE;
|
|
}
|
|
|
|
/* Load into TOPB */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
|
|
pcnt->TOPB = count;
|
|
|
|
/* Load TOPB value into TOP */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
|
|
|
|
/* This bit has no effect on rev. C and onwards parts - for compatibility */
|
|
pcnt->CMD = PCNT_CMD_LTOPBIM;
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
|
|
|
|
/* Load TOP into CNT */
|
|
pcnt->CMD = PCNT_CMD_LCNTIM;
|
|
|
|
/* Restore TOP? ('count' setting has been loaded into pcnt->TOP, better
|
|
* to use 'top' than pcnt->TOP in compare, since latter may in theory not
|
|
* be visible yet.) */
|
|
if (top != count)
|
|
{
|
|
/* Wait for command to sync LCNTIM before setting TOPB */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
|
|
|
|
/* Load into TOPB, we don't need to check for TOPB sync complete here,
|
|
* it has been ensured above. */
|
|
pcnt->TOPB = top;
|
|
|
|
/* Load TOPB value into TOP */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
|
|
pcnt->CMD = PCNT_CMD_LTOPBIM;
|
|
}
|
|
|
|
/* Reenable if it was enabled */
|
|
if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
|
|
{
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD);
|
|
pcnt->CTRL = ctrl;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set PCNT operational mode.
|
|
*
|
|
* @details
|
|
* Notice that this function does not do any configuration. Setting operational
|
|
* mode is normally only required after initialization is done, and if not
|
|
* done as part of initialization. Or if requiring to disable/reenable pulse
|
|
* counter.
|
|
*
|
|
* @note
|
|
* This function may stall until synchronization to low frequency domain is
|
|
* completed. For that reason, it should normally not be used when using
|
|
* an external clock to clock the PCNT module, since stall time may be
|
|
* undefined in that case.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] mode
|
|
* Operational mode to use for PCNT.
|
|
******************************************************************************/
|
|
void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* Set as specified */
|
|
tmp = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK;
|
|
tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT;
|
|
|
|
/* LF register about to be modified require sync. busy check */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
pcnt->CTRL = tmp;
|
|
}
|
|
|
|
#if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Enable/disable the selected PRS input of PCNT.
|
|
*
|
|
* @details
|
|
* Notice that this function does not do any configuration.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] prsInput
|
|
* PRS input (S0 or S1) of the selected PCNT module.
|
|
*
|
|
* @param[in] enable
|
|
* Set to true to enable, false to disable the selected PRS input.
|
|
******************************************************************************/
|
|
void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
|
|
PCNT_PRSInput_TypeDef prsInput,
|
|
bool enable)
|
|
{
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* Enable/disable the selected PRS input on the selected PCNT module. */
|
|
switch (prsInput)
|
|
{
|
|
/* Enable/disable PRS input S0. */
|
|
case pcntPRSInputS0:
|
|
{
|
|
BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, (uint32_t)enable);
|
|
}
|
|
break;
|
|
|
|
/* Enable/disable PRS input S1. */
|
|
case pcntPRSInputS1:
|
|
{
|
|
BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, (uint32_t)enable);
|
|
}
|
|
break;
|
|
|
|
/* Invalid parameter, asserted. */
|
|
default:
|
|
{
|
|
EFM_ASSERT(0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* PCNT register synchronization freeze control.
|
|
*
|
|
* @details
|
|
* Some PCNT registers require synchronization into the low frequency (LF)
|
|
* domain. The freeze feature allows for several such registers to be
|
|
* modified before passing them to the LF domain simultaneously (which
|
|
* takes place when the freeze mode is disabled).
|
|
*
|
|
* @note
|
|
* When enabling freeze mode, this function will wait for all current
|
|
* ongoing PCNT synchronization to LF domain to complete (Normally
|
|
* synchronization will not be in progress.) However for this reason, when
|
|
* using freeze mode, modifications of registers requiring LF synchronization
|
|
* should be done within one freeze enable/disable block to avoid unecessary
|
|
* stalling.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] enable
|
|
* @li true - enable freeze, modified registers are not propagated to the
|
|
* LF domain
|
|
* @li false - disables freeze, modified registers are propagated to LF
|
|
* domain
|
|
******************************************************************************/
|
|
void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable)
|
|
{
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
if (enable)
|
|
{
|
|
/* Wait for any ongoing LF synchronization to complete. This is just to
|
|
* protect against the rare case when a user:
|
|
* - modifies a register requiring LF sync
|
|
* - then enables freeze before LF sync completed
|
|
* - then modifies the same register again
|
|
* since modifying a register while it is in sync progress should be
|
|
* avoided. */
|
|
while (pcnt->SYNCBUSY)
|
|
;
|
|
|
|
pcnt->FREEZE = PCNT_FREEZE_REGFREEZE;
|
|
}
|
|
else
|
|
{
|
|
pcnt->FREEZE = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Init pulse counter.
|
|
*
|
|
* @details
|
|
* This function will configure the pulse counter. The clock selection is
|
|
* configured as follows, depending on operational mode:
|
|
*
|
|
* @li #pcntModeOvsSingle - Use LFACLK.
|
|
* @li #pcntModeExtSingle - Use external PCNTn_S0 pin.
|
|
* @li #pcntModeExtQuad - Use external PCNTn_S0 pin.
|
|
*
|
|
* Notice that the LFACLK must be enabled in all modes, since some basic setup
|
|
* is done with this clock even if external pin clock usage mode is chosen.
|
|
* The pulse counter clock for the selected instance must also be enabled
|
|
* prior to init.
|
|
*
|
|
* Notice that pins used by the PCNT module must be properly configured
|
|
* by the user explicitly through setting the ROUTE register, in order for
|
|
* the PCNT to work as intended.
|
|
*
|
|
* Writing to CNT will not occur in external clock modes (EXTCLKQUAD and
|
|
* EXTCLKSINGLE) because the external clock rate is unknown. The user should
|
|
* handle it manually depending on the application
|
|
*
|
|
* TOPB is written for all modes but in external clock mode it will take
|
|
* 3 external clock cycles to sync to TOP
|
|
*
|
|
*
|
|
* @note
|
|
* Initializing requires synchronization into the low frequency domain. This
|
|
* may cause some delay.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] init
|
|
* Pointer to initialization structure used to initialize.
|
|
******************************************************************************/
|
|
void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init)
|
|
{
|
|
unsigned int inst;
|
|
uint32_t tmp;
|
|
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* Map pointer to instance */
|
|
inst = PCNT_Map(pcnt);
|
|
|
|
#if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
|
|
/* Selecting the PRS channels for the PRS input sources of the PCNT. These are
|
|
* written with a Read-Modify-Write sequence in order to keep the value of the
|
|
* input enable bits which can be modified using PCNT_PRSInputEnable(). */
|
|
tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK);
|
|
tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT) |
|
|
((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT);
|
|
pcnt->INPUT = tmp;
|
|
#endif
|
|
|
|
/* Build CTRL setting, except for mode */
|
|
tmp = 0;
|
|
if (init->negEdge)
|
|
{
|
|
tmp |= PCNT_CTRL_EDGE_NEG;
|
|
}
|
|
|
|
if (init->countDown)
|
|
{
|
|
tmp |= PCNT_CTRL_CNTDIR_DOWN;
|
|
}
|
|
|
|
if (init->filter)
|
|
{
|
|
tmp |= PCNT_CTRL_FILT;
|
|
}
|
|
|
|
#if (defined (_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY))
|
|
if (init->hyst)
|
|
{
|
|
tmp |= PCNT_CTRL_HYST;
|
|
}
|
|
|
|
if (init->s1CntDir)
|
|
{
|
|
tmp |= PCNT_CTRL_S1CDIR;
|
|
}
|
|
|
|
/* Configure counter events for regular and auxiliary counter. */
|
|
tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT;
|
|
tmp |= init->auxCntEvent << _PCNT_CTRL_AUXCNTEV_SHIFT;
|
|
#endif
|
|
|
|
/* Reset pulse counter while changing clock source. The reset bit */
|
|
/* is asynchronous, we don't have to check for SYNCBUSY. */
|
|
BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
|
|
|
|
/* Select LFACLK to clock in control setting */
|
|
CMU_PCNTClockExternalSet(inst, false);
|
|
|
|
/* Handling depends on whether using external clock or not. */
|
|
switch (init->mode)
|
|
{
|
|
case pcntModeExtSingle:
|
|
case pcntModeExtQuad:
|
|
tmp |= init->mode << _PCNT_CTRL_MODE_SHIFT;
|
|
|
|
/* In most cases, the SYNCBUSY bit is set due to reset bit set, and waiting
|
|
* for asynchronous reset bit is strictly not necessary.
|
|
* But in theory, other operations on CTRL register may have been done
|
|
* outside this function, so wait. */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
|
|
/* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
|
|
* the clock source to an external clock */
|
|
pcnt->CTRL = PCNT_CTRL_RSTEN;
|
|
|
|
/* Wait until CTRL write synchronized into LF domain. */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
|
|
/* Change to external clock BEFORE disabling reset */
|
|
CMU_PCNTClockExternalSet(inst, true);
|
|
|
|
/* Write to TOPB. If using external clock TOPB will sync to TOP at the same
|
|
* time as the mode. This will insure that if the user chooses to count
|
|
* down, the first "countable" pulse will make CNT go to TOP and not 0xFF
|
|
* (default TOP value). */
|
|
pcnt->TOPB = init->top;
|
|
|
|
/* This bit has no effect on rev. C and onwards parts - for compatibility */
|
|
pcnt->CMD = PCNT_CMD_LTOPBIM;
|
|
|
|
/* Write the CTRL register with the configurations.
|
|
* This should be written after TOPB in the eventuality of a pulse between
|
|
* these two writes that would cause the CTRL register to be synced one
|
|
* clock cycle earlier than the TOPB. */
|
|
pcnt->CTRL = tmp;
|
|
|
|
/* There are no syncs for TOP, CMD or CTRL because the clock rate is unknown
|
|
* and the program could stall
|
|
* These will be synced within 3 clock cycles of the external clock /
|
|
* For the same reason CNT cannot be written here. */
|
|
break;
|
|
|
|
/* pcntModeDisable */
|
|
/* pcntModeOvsSingle */
|
|
default:
|
|
/* No need to set disabled mode if already disabled. */
|
|
if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
|
|
{
|
|
/* Set control to disabled mode, leave reset on until ensured disabled.
|
|
* We don't need to wait for CTRL SYNCBUSY completion here, it was
|
|
* triggered by reset bit above, which is asynchronous. */
|
|
pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN;
|
|
|
|
/* Wait until CTRL write synchronized into LF domain before proceeding
|
|
* to disable reset. */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
}
|
|
|
|
/* Disable reset bit, counter should now be in disabled mode. */
|
|
BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
|
|
|
|
/* Set counter and top values as specified. */
|
|
PCNT_CounterTopSet(pcnt, init->counter, init->top);
|
|
|
|
/* Enter oversampling mode if selected. */
|
|
if (init->mode == pcntModeOvsSingle)
|
|
{
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Reset PCNT to same state as after a HW reset.
|
|
*
|
|
* @details
|
|
* Notice the LFACLK must be enabled, since some basic reset is done with
|
|
* this clock. The pulse counter clock for the selected instance must also
|
|
* be enabled prior to init.
|
|
*
|
|
* @note
|
|
* The ROUTE register is NOT reset by this function, in order to allow for
|
|
* centralized setup of this feature.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
******************************************************************************/
|
|
void PCNT_Reset(PCNT_TypeDef *pcnt)
|
|
{
|
|
unsigned int inst;
|
|
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* Map pointer to instance and clock info */
|
|
inst = PCNT_Map(pcnt);
|
|
|
|
pcnt->IEN = _PCNT_IEN_RESETVALUE;
|
|
|
|
/* Notice that special SYNCBUSY handling is not applicable for the RSTEN
|
|
* bit of the control register, so we don't need to wait for it when only
|
|
* modifying RSTEN. The SYNCBUSY bit will be set, leading to a
|
|
* synchronization in the LF domain, with in reality no changes to LF domain.
|
|
* Enable reset of CNT and TOP register. */
|
|
BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
|
|
|
|
/* Select LFACLK as default */
|
|
CMU_PCNTClockExternalSet(inst, false);
|
|
|
|
PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE);
|
|
|
|
/* Reset CTRL leaving RSTEN set */
|
|
pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN;
|
|
|
|
/* Disable reset after CTRL reg has been synchronized */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
|
|
BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
|
|
|
|
/* Clear pending interrupts */
|
|
pcnt->IFC = _PCNT_IFC_MASK;
|
|
|
|
/* Do not reset route register, setting should be done independently */
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set top buffer value.
|
|
*
|
|
* @note
|
|
* This function may stall until synchronization to low frequency domain is
|
|
* completed. For that reason, it should normally not be used when using
|
|
* an external clock to clock the PCNT module, since stall time may be
|
|
* undefined in that case.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] val
|
|
* Value to set in top buffer register.
|
|
******************************************************************************/
|
|
void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val)
|
|
{
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* LF register about to be modified require sync. busy check */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
|
|
pcnt->TOPB = val;
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set top value.
|
|
*
|
|
* @note
|
|
* This function will stall until synchronization to low frequency domain is
|
|
* completed. For that reason, it should normally not be used when using
|
|
* an external clock to clock the PCNT module, since stall time may be
|
|
* undefined in that case.
|
|
*
|
|
* @param[in] pcnt
|
|
* Pointer to PCNT peripheral register block.
|
|
*
|
|
* @param[in] val
|
|
* Value to set in top register.
|
|
******************************************************************************/
|
|
void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val)
|
|
{
|
|
EFM_ASSERT(PCNT_REF_VALID(pcnt));
|
|
|
|
/* LF register about to be modified require sync. busy check */
|
|
|
|
/* Load into TOPB */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
|
|
pcnt->TOPB = val;
|
|
|
|
/* Load TOPB value into TOP */
|
|
PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
|
|
pcnt->CMD = PCNT_CMD_LTOPBIM;
|
|
}
|
|
|
|
|
|
/** @} (end addtogroup PCNT) */
|
|
/** @} (end addtogroup EM_Library) */
|