Example #1
0
/***************************************************************************//**
 * @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));

#ifdef PCNT0
  if (PCNT0 == pcnt)
  {
    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > val);
  }
#endif

#ifdef PCNT1
  if (PCNT1 == pcnt)
  {
    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > val);
  }
#endif

#ifdef PCNT2
  if (PCNT2 == pcnt)
  {
    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > val);
  }
#endif

  /* 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;
}
Example #2
0
/***************************************************************************//**
 * @brief
 *   Set filter configuration.
 *
 * @details
 *   This function will configure the PCNT input filter, when the PCNT mode is
 *   configured to take an LFA-derived clock as input clock.
 *
 * @param[in] pcnt
 *   Pointer to PCNT peripheral register block.
 *
 * @param[in] config
 *   Pointer to configuration structure to be applied.
 *
 * @param[in] enable
 *   Whether to enable or disable filtering
 ******************************************************************************/
void PCNT_FilterConfiguration(PCNT_TypeDef *pcnt, const PCNT_Filter_TypeDef *config, bool enable) {
  uint32_t ovscfg = 0;

  EFM_ASSERT(PCNT_REF_VALID(pcnt));

  /* Construct new filter setting value */
  ovscfg  = ((config->filtLen & _PCNT_OVSCFG_FILTLEN_MASK) << _PCNT_OVSCFG_FILTLEN_SHIFT)
            | ((config->flutterrm & 0x1) << _PCNT_OVSCFG_FLUTTERRM_SHIFT);

  /* Set new configuration. LF register requires sync check before writing. */
  PCNT_Sync(pcnt, PCNT_SYNCBUSY_OVSCFG);
  pcnt->OVSCFG = ovscfg;


  /* Set new state of filter. LF register requires sync check before writing. */
  PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
  if(enable)
  {
    pcnt->CTRL |= PCNT_CTRL_FILT;
  }
  else
  {
    pcnt->CTRL &= ~PCNT_CTRL_FILT;
  }
}
Example #3
0
/***************************************************************************//**
 * @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;
}
Example #4
0
/***************************************************************************//**
 * @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 */
}
Example #5
0
/***************************************************************************//**
 * @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;
}
Example #6
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;
  }
}
Example #7
0
/***************************************************************************//**
 * @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;
}
Example #8
0
/***************************************************************************//**
 * @brief
 *   Set Triggered Compare and Clear configuration.
 *
 * @details
 *   This function will configure the PCNT TCC (Triggered Compare and Clear)
 *   module. This module can, upon a configurable trigger source, compare the
 *   current counter value with the configured TOP value. Upon match, the counter
 *   will be reset, and the TCC PRS output and TCC interrupt flag will be set.
 *
 *   Since there is a comparison with the TOP value, the counter will not stop
 *   counting nor wrap when hitting the TOP value, but it will keep on counting
 *   until its maximum value. Then, it will not wrap, but instead stop counting
 *   and set the overflow flag.
 *
 * @param[in] pcnt
 *   Pointer to PCNT peripheral register block.
 *
 * @param[in] config
 *   Pointer to configuration structure to be applied.
 ******************************************************************************/
void PCNT_TCCConfiguration(PCNT_TypeDef *pcnt, const PCNT_TCC_TypeDef *config){
  uint32_t ctrl = 0;
  uint32_t mask = _PCNT_CTRL_TCCMODE_MASK
                  | _PCNT_CTRL_TCCPRESC_MASK
                  | _PCNT_CTRL_TCCCOMP_MASK
                  | _PCNT_CTRL_PRSGATEEN_MASK
                  | _PCNT_CTRL_TCCPRSPOL_MASK
                  | _PCNT_CTRL_TCCPRSSEL_MASK;

  EFM_ASSERT(PCNT_REF_VALID(pcnt));

  /* construct TCC part of configuration register */
  ctrl |= (config->mode          << _PCNT_CTRL_TCCMODE_SHIFT   ) & _PCNT_CTRL_TCCMODE_MASK;
  ctrl |= (config->prescaler     << _PCNT_CTRL_TCCPRESC_SHIFT  ) & _PCNT_CTRL_TCCPRESC_MASK;
  ctrl |= (config->compare       << _PCNT_CTRL_TCCCOMP_SHIFT   ) & _PCNT_CTRL_TCCCOMP_MASK;
  ctrl |= (config->tccPRS        << _PCNT_CTRL_TCCPRSSEL_SHIFT ) & _PCNT_CTRL_TCCPRSSEL_MASK;
  ctrl |= (config->prsPolarity   << _PCNT_CTRL_TCCPRSPOL_SHIFT ) & _PCNT_CTRL_TCCPRSPOL_MASK;
  ctrl |= (config->prsGateEnable << _PCNT_CTRL_PRSGATEEN_SHIFT ) & _PCNT_CTRL_PRSGATEEN_MASK;

  /* Load new TCC config to PCNT. LF register requires sync check before write. */
  PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
  pcnt->CTRL = (pcnt->CTRL & (~mask)) | ctrl;
}
Example #9
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));

#ifdef PCNT0
  if (PCNT0 == pcnt)
  {
    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->counter);
    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->top);
  }
#endif
  
#ifdef PCNT1
  if (PCNT1 == pcnt)
  {
    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->counter);
    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->top);
  }
#endif
  
#ifdef PCNT2
  if (PCNT2 == pcnt)
  {
    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->counter);
    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->top);
  }
#endif
  
  /* Map pointer to instance */
  inst = PCNT_Map(pcnt);

#if defined( _PCNT_INPUT_MASK )
  /* 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( PCNT_CTRL_HYST )
  if (init->hyst)
  {
    tmp |= PCNT_CTRL_HYST;
  }
#endif

#if defined( PCNT_CTRL_S1CDIR )
  if (init->s1CntDir)
  {
    tmp |= PCNT_CTRL_S1CDIR;
  }
#endif

  /* Configure counter events for regular and auxiliary counter. */
#if defined( _PCNT_CTRL_CNTEV_SHIFT )
  tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT;
#endif

#if defined( _PCNT_CTRL_AUXCNTEV_SHIFT )
  {
    /* Modify the auxCntEvent value before writing to the AUXCNTEV field in
       the CTRL register because the AUXCNTEV field values are different from
       the CNTEV field values, and cntEvent and auxCntEvent are of the same type
       PCNT_CntEvent_TypeDef.
    */
    uint32_t auxCntEventField = 0; /* Get rid of compiler warning. */
    switch (init->auxCntEvent)
    {
    case pcntCntEventBoth:
      auxCntEventField = pcntCntEventNone;
      break;
    case pcntCntEventNone:
      auxCntEventField = pcntCntEventBoth;
      break;
    case pcntCntEventUp:
    case pcntCntEventDown:
      auxCntEventField = init->auxCntEvent;
      break;
    default:
      /* Invalid parameter, asserted. */
      EFM_ASSERT(0);
    }
    tmp |= auxCntEventField << _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;
  }
}
Example #10
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.
 *
 * @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, PCNT_Init_TypeDef *init)
{
  unsigned int inst;
  uint32_t     tmp;

  EFM_ASSERT(PCNT_REF_VALID(pcnt));

  /* Map pointer to instance */
  inst = PCNT_Map(pcnt);

  /* 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;
  }

  /* 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 reg may have been done */
    /* outside this func, so wait. */
    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
    pcnt->CTRL = tmp | PCNT_CTRL_RSTEN;

    /* Wait until CTRL write synchronized into LF domain. */
    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);

    /* Do dummy write to ensure fully activated in LF domain before */
    /* switching to external clock, may require one additional clock. */
    pcnt->CTRL = tmp | PCNT_CTRL_RSTEN;
    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);

    /* Change to external clock BEFORE disabling reset */
    CMU_PCNTClockExternalSet(inst, true);

    /* Disable reset bit, it can be done without waiting for SYNCBUSY bit. */
    /* (Notice that this will set SYNCBUSY bit for CTRL register even if */
    /* bit is asynchronous. The SYNCBUSY bit will remain set until external */
    /* pulses completes synchronization and clears bit.) */
    BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);

    /* TOP and CNT registers have been reset, but we should not set them */
    /* since external clock rate is unknown. Handling depends on application. */
    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;
  }
}