em_pcnt.c

Go to the documentation of this file.
00001 /***************************************************************************/
00034 #include "em_pcnt.h"
00035 #if defined(PCNT_COUNT) && (PCNT_COUNT > 0)
00036 
00037 #include "em_cmu.h"
00038 #include "em_assert.h"
00039 #include "em_bitband.h"
00040 
00041 /***************************************************************************/
00046 /***************************************************************************/
00052 /*******************************************************************************
00053  *******************************   DEFINES   ***********************************
00054  ******************************************************************************/
00055 
00060 #if (PCNT_COUNT == 1)
00061 #define PCNT_REF_VALID(ref)    ((ref) == PCNT0)
00062 #elif (PCNT_COUNT == 2)
00063 #define PCNT_REF_VALID(ref)    (((ref) == PCNT0) || ((ref) == PCNT1))
00064 #elif (PCNT_COUNT == 3)
00065 #define PCNT_REF_VALID(ref)    (((ref) == PCNT0) || ((ref) == PCNT1) || \
00066                                 ((ref) == PCNT2))
00067 #else
00068 #error Undefined number of pulse counters (PCNT).
00069 #endif
00070 
00074 /*******************************************************************************
00075  **************************   LOCAL FUNCTIONS   ********************************
00076  ******************************************************************************/
00077 
00080 /***************************************************************************/
00090 __STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt)
00091 {
00092   return(((uint32_t)pcnt - PCNT0_BASE) / 0x400);
00093 }
00094 
00095 
00096 /***************************************************************************/
00107 __STATIC_INLINE void PCNT_Sync(PCNT_TypeDef *pcnt, uint32_t mask)
00108 {
00109   /* Avoid deadlock if modifying the same register twice when freeze mode is
00110    * activated. */
00111   if (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE)
00112   {
00113     return;
00114   }
00115 
00116   /* Wait for any pending previous write operation to have been completed in low
00117    * frequency domain. */
00118   while (pcnt->SYNCBUSY & mask)
00119     ;
00120 }
00121 
00124 /*******************************************************************************
00125  **************************   GLOBAL FUNCTIONS   *******************************
00126  ******************************************************************************/
00127 
00128 /***************************************************************************/
00142 void PCNT_CounterReset(PCNT_TypeDef *pcnt)
00143 {
00144   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00145 
00146   /* Enable reset of CNT and TOP register */
00147   BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
00148 
00149   /* Disable reset of CNT and TOP register */
00150   BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
00151 }
00152 
00153 
00154 /***************************************************************************/
00178 void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top)
00179 {
00180   uint32_t ctrl;
00181 
00182   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00183 
00184 #ifdef PCNT0
00185   if (PCNT0 == pcnt)
00186   {
00187     EFM_ASSERT((1<<PCNT0_CNT_SIZE) > count);
00188     EFM_ASSERT((1<<PCNT0_CNT_SIZE) > top);
00189   }
00190 #endif
00191 
00192 #ifdef PCNT1
00193   if (PCNT1 == pcnt)
00194   {
00195     EFM_ASSERT((1<<PCNT1_CNT_SIZE) > count);
00196     EFM_ASSERT((1<<PCNT1_CNT_SIZE) > top);
00197   }
00198 #endif
00199 
00200 #ifdef PCNT2
00201   if (PCNT2 == pcnt)
00202   {
00203     EFM_ASSERT((1<<PCNT2_CNT_SIZE) > count);
00204     EFM_ASSERT((1<<PCNT2_CNT_SIZE) > top);
00205   }
00206 #endif
00207 
00208   /* Keep current control setting, must be restored */
00209   ctrl = pcnt->CTRL;
00210 
00211   /* If enabled, disable pulse counter before changing values */
00212   if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
00213   {
00214     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00215     pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE;
00216   }
00217 
00218   /* Load into TOPB */
00219   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
00220   pcnt->TOPB = count;
00221 
00222   /* Load TOPB value into TOP */
00223   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
00224 
00225   /* This bit has no effect on rev. C and onwards parts - for compatibility */
00226   pcnt->CMD = PCNT_CMD_LTOPBIM;
00227   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
00228 
00229   /* Load TOP into CNT */
00230   pcnt->CMD = PCNT_CMD_LCNTIM;
00231 
00232   /* Restore TOP? ('count' setting has been loaded into pcnt->TOP, better
00233    * to use 'top' than pcnt->TOP in compare, since latter may in theory not
00234    * be visible yet.) */
00235   if (top != count)
00236   {
00237     /* Wait for command to sync LCNTIM before setting TOPB */
00238     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
00239 
00240     /* Load into TOPB, we don't need to check for TOPB sync complete here,
00241      * it has been ensured above. */
00242     pcnt->TOPB = top;
00243 
00244     /* Load TOPB value into TOP */
00245     PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
00246     pcnt->CMD = PCNT_CMD_LTOPBIM;
00247   }
00248 
00249   /* Reenable if it was enabled */
00250   if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
00251   {
00252     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD);
00253     pcnt->CTRL = ctrl;
00254   }
00255 }
00256 
00257 
00258 /***************************************************************************/
00280 void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode)
00281 {
00282   uint32_t tmp;
00283 
00284   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00285 
00286   /* Set as specified */
00287   tmp  = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK;
00288   tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT;
00289 
00290   /* LF register about to be modified require sync. busy check */
00291   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00292   pcnt->CTRL = tmp;
00293 }
00294 
00295 #if defined( _PCNT_INPUT_MASK )
00296 /***************************************************************************/
00312 void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
00313                          PCNT_PRSInput_TypeDef prsInput,
00314                          bool enable)
00315 {
00316   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00317 
00318   /* Enable/disable the selected PRS input on the selected PCNT module. */
00319   switch (prsInput)
00320   {
00321   /* Enable/disable PRS input S0. */
00322   case pcntPRSInputS0:
00323   {
00324     BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, (uint32_t)enable);
00325   }
00326   break;
00327 
00328   /* Enable/disable PRS input S1. */
00329   case pcntPRSInputS1:
00330   {
00331     BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, (uint32_t)enable);
00332   }
00333   break;
00334 
00335   /* Invalid parameter, asserted. */
00336   default:
00337   {
00338     EFM_ASSERT(0);
00339   }
00340   break;
00341   }
00342 }
00343 #endif
00344 
00345 
00346 /***************************************************************************/
00373 void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable)
00374 {
00375   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00376 
00377   if (enable)
00378   {
00379     /* Wait for any ongoing LF synchronization to complete. This is just to
00380      * protect against the rare case when a user:
00381      * - modifies a register requiring LF sync
00382      * - then enables freeze before LF sync completed
00383      * - then modifies the same register again
00384      * since modifying a register while it is in sync progress should be
00385      * avoided. */
00386     while (pcnt->SYNCBUSY)
00387       ;
00388 
00389     pcnt->FREEZE = PCNT_FREEZE_REGFREEZE;
00390   }
00391   else
00392   {
00393     pcnt->FREEZE = 0;
00394   }
00395 }
00396 
00397 
00398 /***************************************************************************/
00437 void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init)
00438 {
00439   unsigned int inst;
00440   uint32_t     tmp;
00441 
00442   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00443 
00444 #ifdef PCNT0
00445   if (PCNT0 == pcnt)
00446   {
00447     EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->counter);
00448     EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->top);
00449   }
00450 #endif
00451   
00452 #ifdef PCNT1
00453   if (PCNT1 == pcnt)
00454   {
00455     EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->counter);
00456     EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->top);
00457   }
00458 #endif
00459   
00460 #ifdef PCNT2
00461   if (PCNT2 == pcnt)
00462   {
00463     EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->counter);
00464     EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->top);
00465   }
00466 #endif
00467   
00468   /* Map pointer to instance */
00469   inst = PCNT_Map(pcnt);
00470 
00471 #if defined( _PCNT_INPUT_MASK )
00472   /* Selecting the PRS channels for the PRS input sources of the PCNT. These are
00473    * written with a Read-Modify-Write sequence in order to keep the value of the
00474    * input enable bits which can be modified using PCNT_PRSInputEnable(). */
00475   tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK);
00476   tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT) |
00477          ((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT);
00478   pcnt->INPUT = tmp;
00479 #endif
00480 
00481   /* Build CTRL setting, except for mode */
00482   tmp = 0;
00483   if (init->negEdge)
00484   {
00485     tmp |= PCNT_CTRL_EDGE_NEG;
00486   }
00487 
00488   if (init->countDown)
00489   {
00490     tmp |= PCNT_CTRL_CNTDIR_DOWN;
00491   }
00492 
00493   if (init->filter)
00494   {
00495     tmp |= PCNT_CTRL_FILT;
00496   }
00497 
00498 #if defined( PCNT_CTRL_HYST )
00499   if (init->hyst)
00500   {
00501     tmp |= PCNT_CTRL_HYST;
00502   }
00503 #endif
00504 
00505 #if defined( PCNT_CTRL_S1CDIR )
00506   if (init->s1CntDir)
00507   {
00508     tmp |= PCNT_CTRL_S1CDIR;
00509   }
00510 #endif
00511 
00512   /* Configure counter events for regular and auxiliary counter. */
00513 #if defined( _PCNT_CTRL_CNTEV_SHIFT )
00514   tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT;
00515 #endif
00516 
00517 #if defined( _PCNT_CTRL_AUXCNTEV_SHIFT )
00518   {
00519     /* Modify the auxCntEvent value before writing to the AUXCNTEV field in
00520        the CTRL register because the AUXCNTEV field values are different from
00521        the CNTEV field values, and cntEvent and auxCntEvent are of the same type
00522        PCNT_CntEvent_TypeDef.
00523     */
00524     uint32_t auxCntEventField = 0; /* Get rid of compiler warning. */
00525     switch (init->auxCntEvent)
00526     {
00527     case pcntCntEventBoth:
00528       auxCntEventField = pcntCntEventNone;
00529       break;
00530     case pcntCntEventNone:
00531       auxCntEventField = pcntCntEventBoth;
00532       break;
00533     case pcntCntEventUp:
00534     case pcntCntEventDown:
00535       auxCntEventField = init->auxCntEvent;
00536       break;
00537     default:
00538       /* Invalid parameter, asserted. */
00539       EFM_ASSERT(0);
00540     }
00541     tmp |= auxCntEventField << _PCNT_CTRL_AUXCNTEV_SHIFT;
00542   }
00543 #endif
00544 
00545   /* Reset pulse counter while changing clock source. The reset bit */
00546   /* is asynchronous, we don't have to check for SYNCBUSY. */
00547   BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
00548 
00549   /* Select LFACLK to clock in control setting */
00550   CMU_PCNTClockExternalSet(inst, false);
00551 
00552   /* Handling depends on whether using external clock or not. */
00553   switch (init->mode)
00554   {
00555   case pcntModeExtSingle:
00556   case pcntModeExtQuad:
00557     tmp |= init->mode << _PCNT_CTRL_MODE_SHIFT;
00558 
00559     /* In most cases, the SYNCBUSY bit is set due to reset bit set, and waiting
00560      * for asynchronous reset bit is strictly not necessary.
00561      * But in theory, other operations on CTRL register may have been done
00562      * outside this function, so wait. */
00563     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00564 
00565     /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
00566      * the clock source to an external clock */
00567     pcnt->CTRL = PCNT_CTRL_RSTEN;
00568 
00569     /* Wait until CTRL write synchronized into LF domain. */
00570     PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00571 
00572     /* Change to external clock BEFORE disabling reset */
00573     CMU_PCNTClockExternalSet(inst, true);
00574 
00575     /* Write to TOPB. If using external clock TOPB will sync to TOP at the same
00576      * time as the mode. This will insure that if the user chooses to count
00577      * down, the first "countable" pulse will make CNT go to TOP and not 0xFF
00578      * (default TOP value). */
00579     pcnt->TOPB = init->top;
00580 
00581     /* This bit has no effect on rev. C and onwards parts - for compatibility */
00582     pcnt->CMD = PCNT_CMD_LTOPBIM;
00583 
00584     /* Write the CTRL register with the configurations.
00585      * This should be written after TOPB in the eventuality of a pulse between
00586      * these two writes that would cause the CTRL register to be synced one
00587      * clock cycle earlier than the TOPB. */
00588     pcnt->CTRL = tmp;
00589 
00590     /* There are no syncs for TOP, CMD or CTRL because the clock rate is unknown
00591      * and the program could stall
00592      * These will be synced within 3 clock cycles of the external clock  /
00593      * For the same reason CNT cannot be written here. */
00594     break;
00595 
00596   /* pcntModeDisable */
00597   /* pcntModeOvsSingle */
00598   default:
00599     /* No need to set disabled mode if already disabled. */
00600     if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
00601     {
00602       /* Set control to disabled mode, leave reset on until ensured disabled.
00603        * We don't need to wait for CTRL SYNCBUSY completion here, it was
00604        * triggered by reset bit above, which is asynchronous. */
00605       pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN;
00606 
00607       /* Wait until CTRL write synchronized into LF domain before proceeding
00608        * to disable reset. */
00609       PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00610     }
00611 
00612     /* Disable reset bit, counter should now be in disabled mode. */
00613     BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
00614 
00615     /* Set counter and top values as specified. */
00616     PCNT_CounterTopSet(pcnt, init->counter, init->top);
00617 
00618     /* Enter oversampling mode if selected. */
00619     if (init->mode == pcntModeOvsSingle)
00620     {
00621       PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00622       pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT);
00623     }
00624     break;
00625   }
00626 }
00627 
00628 
00629 /***************************************************************************/
00645 void PCNT_Reset(PCNT_TypeDef *pcnt)
00646 {
00647   unsigned int inst;
00648 
00649   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00650 
00651   /* Map pointer to instance and clock info */
00652   inst = PCNT_Map(pcnt);
00653 
00654   pcnt->IEN = _PCNT_IEN_RESETVALUE;
00655 
00656   /* Notice that special SYNCBUSY handling is not applicable for the RSTEN
00657    * bit of the control register, so we don't need to wait for it when only
00658    * modifying RSTEN. The SYNCBUSY bit will be set, leading to a
00659    * synchronization in the LF domain, with in reality no changes to LF domain.
00660    * Enable reset of CNT and TOP register. */
00661   BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
00662 
00663   /* Select LFACLK as default */
00664   CMU_PCNTClockExternalSet(inst, false);
00665 
00666   PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE);
00667 
00668   /* Reset CTRL leaving RSTEN set */
00669   pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN;
00670 
00671   /* Disable reset after CTRL reg has been synchronized */
00672   PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
00673   BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
00674 
00675   /* Clear pending interrupts */
00676   pcnt->IFC = _PCNT_IFC_MASK;
00677 
00678   /* Do not reset route register, setting should be done independently */
00679 }
00680 
00681 
00682 /***************************************************************************/
00698 void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val)
00699 {
00700   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00701 
00702   /* LF register about to be modified require sync. busy check */
00703   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
00704   pcnt->TOPB = val;
00705 }
00706 
00707 
00708 /***************************************************************************/
00724 void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val)
00725 {
00726   EFM_ASSERT(PCNT_REF_VALID(pcnt));
00727 
00728 #ifdef PCNT0
00729   if (PCNT0 == pcnt)
00730   {
00731     EFM_ASSERT((1<<PCNT0_CNT_SIZE) > val);
00732   }
00733 #endif
00734 
00735 #ifdef PCNT1
00736   if (PCNT1 == pcnt)
00737   {
00738     EFM_ASSERT((1<<PCNT1_CNT_SIZE) > val);
00739   }
00740 #endif
00741 
00742 #ifdef PCNT2
00743   if (PCNT2 == pcnt)
00744   {
00745     EFM_ASSERT((1<<PCNT2_CNT_SIZE) > val);
00746   }
00747 #endif
00748 
00749   /* LF register about to be modified require sync. busy check */
00750 
00751   /* Load into TOPB */
00752   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
00753   pcnt->TOPB = val;
00754 
00755   /* Load TOPB value into TOP */
00756   PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
00757   pcnt->CMD = PCNT_CMD_LTOPBIM;
00758 }
00759 
00760 
00763 #endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */