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