added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_pcnt.c@0:9b334a45a8ff, 2015-10-01 (annotated)
- Committer:
- bogdanm
- Date:
- Thu Oct 01 15:25:22 2015 +0300
- Revision:
- 0:9b334a45a8ff
- Child:
- 50:a417edff4437
Initial commit on mbed-dev
Replaces mbed-src (now inactive)
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
bogdanm | 0:9b334a45a8ff | 1 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 2 | * @file em_pcnt.c |
bogdanm | 0:9b334a45a8ff | 3 | * @brief Pulse Counter (PCNT) peripheral API |
bogdanm | 0:9b334a45a8ff | 4 | * @version 3.20.12 |
bogdanm | 0:9b334a45a8ff | 5 | ******************************************************************************* |
bogdanm | 0:9b334a45a8ff | 6 | * @section License |
bogdanm | 0:9b334a45a8ff | 7 | * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> |
bogdanm | 0:9b334a45a8ff | 8 | ******************************************************************************* |
bogdanm | 0:9b334a45a8ff | 9 | * |
bogdanm | 0:9b334a45a8ff | 10 | * Permission is granted to anyone to use this software for any purpose, |
bogdanm | 0:9b334a45a8ff | 11 | * including commercial applications, and to alter it and redistribute it |
bogdanm | 0:9b334a45a8ff | 12 | * freely, subject to the following restrictions: |
bogdanm | 0:9b334a45a8ff | 13 | * |
bogdanm | 0:9b334a45a8ff | 14 | * 1. The origin of this software must not be misrepresented; you must not |
bogdanm | 0:9b334a45a8ff | 15 | * claim that you wrote the original software. |
bogdanm | 0:9b334a45a8ff | 16 | * 2. Altered source versions must be plainly marked as such, and must not be |
bogdanm | 0:9b334a45a8ff | 17 | * misrepresented as being the original software. |
bogdanm | 0:9b334a45a8ff | 18 | * 3. This notice may not be removed or altered from any source distribution. |
bogdanm | 0:9b334a45a8ff | 19 | * |
bogdanm | 0:9b334a45a8ff | 20 | * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no |
bogdanm | 0:9b334a45a8ff | 21 | * obligation to support this Software. Silicon Labs is providing the |
bogdanm | 0:9b334a45a8ff | 22 | * Software "AS IS", with no express or implied warranties of any kind, |
bogdanm | 0:9b334a45a8ff | 23 | * including, but not limited to, any implied warranties of merchantability |
bogdanm | 0:9b334a45a8ff | 24 | * or fitness for any particular purpose or warranties against infringement |
bogdanm | 0:9b334a45a8ff | 25 | * of any proprietary rights of a third party. |
bogdanm | 0:9b334a45a8ff | 26 | * |
bogdanm | 0:9b334a45a8ff | 27 | * Silicon Labs will not be liable for any consequential, incidental, or |
bogdanm | 0:9b334a45a8ff | 28 | * special damages, or any other relief, or for any claim by any third party, |
bogdanm | 0:9b334a45a8ff | 29 | * arising from your use of this Software. |
bogdanm | 0:9b334a45a8ff | 30 | * |
bogdanm | 0:9b334a45a8ff | 31 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 32 | |
bogdanm | 0:9b334a45a8ff | 33 | |
bogdanm | 0:9b334a45a8ff | 34 | #include "em_pcnt.h" |
bogdanm | 0:9b334a45a8ff | 35 | #if defined(PCNT_COUNT) && (PCNT_COUNT > 0) |
bogdanm | 0:9b334a45a8ff | 36 | |
bogdanm | 0:9b334a45a8ff | 37 | #include "em_cmu.h" |
bogdanm | 0:9b334a45a8ff | 38 | #include "em_assert.h" |
bogdanm | 0:9b334a45a8ff | 39 | #include "em_bitband.h" |
bogdanm | 0:9b334a45a8ff | 40 | |
bogdanm | 0:9b334a45a8ff | 41 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 42 | * @addtogroup EM_Library |
bogdanm | 0:9b334a45a8ff | 43 | * @{ |
bogdanm | 0:9b334a45a8ff | 44 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 45 | |
bogdanm | 0:9b334a45a8ff | 46 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 47 | * @addtogroup PCNT |
bogdanm | 0:9b334a45a8ff | 48 | * @brief Pulse Counter (PCNT) Peripheral API |
bogdanm | 0:9b334a45a8ff | 49 | * @{ |
bogdanm | 0:9b334a45a8ff | 50 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 51 | |
bogdanm | 0:9b334a45a8ff | 52 | /******************************************************************************* |
bogdanm | 0:9b334a45a8ff | 53 | ******************************* DEFINES *********************************** |
bogdanm | 0:9b334a45a8ff | 54 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 55 | |
bogdanm | 0:9b334a45a8ff | 56 | /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ |
bogdanm | 0:9b334a45a8ff | 57 | |
bogdanm | 0:9b334a45a8ff | 58 | |
bogdanm | 0:9b334a45a8ff | 59 | /** Validation of PCNT register block pointer reference for assert statements. */ |
bogdanm | 0:9b334a45a8ff | 60 | #if (PCNT_COUNT == 1) |
bogdanm | 0:9b334a45a8ff | 61 | #define PCNT_REF_VALID(ref) ((ref) == PCNT0) |
bogdanm | 0:9b334a45a8ff | 62 | #elif (PCNT_COUNT == 2) |
bogdanm | 0:9b334a45a8ff | 63 | #define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1)) |
bogdanm | 0:9b334a45a8ff | 64 | #elif (PCNT_COUNT == 3) |
bogdanm | 0:9b334a45a8ff | 65 | #define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1) || \ |
bogdanm | 0:9b334a45a8ff | 66 | ((ref) == PCNT2)) |
bogdanm | 0:9b334a45a8ff | 67 | #else |
bogdanm | 0:9b334a45a8ff | 68 | #error Undefined number of pulse counters (PCNT). |
bogdanm | 0:9b334a45a8ff | 69 | #endif |
bogdanm | 0:9b334a45a8ff | 70 | |
bogdanm | 0:9b334a45a8ff | 71 | /** @endcond */ |
bogdanm | 0:9b334a45a8ff | 72 | |
bogdanm | 0:9b334a45a8ff | 73 | |
bogdanm | 0:9b334a45a8ff | 74 | /******************************************************************************* |
bogdanm | 0:9b334a45a8ff | 75 | ************************** LOCAL FUNCTIONS ******************************** |
bogdanm | 0:9b334a45a8ff | 76 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 77 | |
bogdanm | 0:9b334a45a8ff | 78 | /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ |
bogdanm | 0:9b334a45a8ff | 79 | |
bogdanm | 0:9b334a45a8ff | 80 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 81 | * @brief |
bogdanm | 0:9b334a45a8ff | 82 | * Map PCNT structure into instance number. |
bogdanm | 0:9b334a45a8ff | 83 | * |
bogdanm | 0:9b334a45a8ff | 84 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 85 | * Pointer to PCNT peripheral register block |
bogdanm | 0:9b334a45a8ff | 86 | * |
bogdanm | 0:9b334a45a8ff | 87 | * @return |
bogdanm | 0:9b334a45a8ff | 88 | * Instance number. |
bogdanm | 0:9b334a45a8ff | 89 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 90 | __STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt) |
bogdanm | 0:9b334a45a8ff | 91 | { |
bogdanm | 0:9b334a45a8ff | 92 | return(((uint32_t)pcnt - PCNT0_BASE) / 0x400); |
bogdanm | 0:9b334a45a8ff | 93 | } |
bogdanm | 0:9b334a45a8ff | 94 | |
bogdanm | 0:9b334a45a8ff | 95 | |
bogdanm | 0:9b334a45a8ff | 96 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 97 | * @brief |
bogdanm | 0:9b334a45a8ff | 98 | * Wait for ongoing sync of register(s) to low frequency domain to complete. |
bogdanm | 0:9b334a45a8ff | 99 | * |
bogdanm | 0:9b334a45a8ff | 100 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 101 | * Pointer to PCNT peripheral register block |
bogdanm | 0:9b334a45a8ff | 102 | * |
bogdanm | 0:9b334a45a8ff | 103 | * @param[in] mask |
bogdanm | 0:9b334a45a8ff | 104 | * Bitmask corresponding to SYNCBUSY register defined bits, indicating |
bogdanm | 0:9b334a45a8ff | 105 | * registers that must complete any ongoing synchronization. |
bogdanm | 0:9b334a45a8ff | 106 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 107 | __STATIC_INLINE void PCNT_Sync(PCNT_TypeDef *pcnt, uint32_t mask) |
bogdanm | 0:9b334a45a8ff | 108 | { |
bogdanm | 0:9b334a45a8ff | 109 | /* Avoid deadlock if modifying the same register twice when freeze mode is |
bogdanm | 0:9b334a45a8ff | 110 | * activated. */ |
bogdanm | 0:9b334a45a8ff | 111 | if (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE) |
bogdanm | 0:9b334a45a8ff | 112 | { |
bogdanm | 0:9b334a45a8ff | 113 | return; |
bogdanm | 0:9b334a45a8ff | 114 | } |
bogdanm | 0:9b334a45a8ff | 115 | |
bogdanm | 0:9b334a45a8ff | 116 | /* Wait for any pending previous write operation to have been completed in low |
bogdanm | 0:9b334a45a8ff | 117 | * frequency domain. */ |
bogdanm | 0:9b334a45a8ff | 118 | while (pcnt->SYNCBUSY & mask) |
bogdanm | 0:9b334a45a8ff | 119 | ; |
bogdanm | 0:9b334a45a8ff | 120 | } |
bogdanm | 0:9b334a45a8ff | 121 | |
bogdanm | 0:9b334a45a8ff | 122 | /** @endcond */ |
bogdanm | 0:9b334a45a8ff | 123 | |
bogdanm | 0:9b334a45a8ff | 124 | /******************************************************************************* |
bogdanm | 0:9b334a45a8ff | 125 | ************************** GLOBAL FUNCTIONS ******************************* |
bogdanm | 0:9b334a45a8ff | 126 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 127 | |
bogdanm | 0:9b334a45a8ff | 128 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 129 | * @brief |
bogdanm | 0:9b334a45a8ff | 130 | * Reset PCNT counters and TOP register. |
bogdanm | 0:9b334a45a8ff | 131 | * |
bogdanm | 0:9b334a45a8ff | 132 | * @note |
bogdanm | 0:9b334a45a8ff | 133 | * Notice that special SYNCBUSY handling is not applicable for the RSTEN |
bogdanm | 0:9b334a45a8ff | 134 | * bit of the control register, so we don't need to wait for it when only |
bogdanm | 0:9b334a45a8ff | 135 | * modifying RSTEN. (It would mean undefined wait time if clocked by external |
bogdanm | 0:9b334a45a8ff | 136 | * clock.) The SYNCBUSY bit will however be set, leading to a synchronization |
bogdanm | 0:9b334a45a8ff | 137 | * in the LF domain, with in reality no changes. |
bogdanm | 0:9b334a45a8ff | 138 | * |
bogdanm | 0:9b334a45a8ff | 139 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 140 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 141 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 142 | void PCNT_CounterReset(PCNT_TypeDef *pcnt) |
bogdanm | 0:9b334a45a8ff | 143 | { |
bogdanm | 0:9b334a45a8ff | 144 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 145 | |
bogdanm | 0:9b334a45a8ff | 146 | /* Enable reset of CNT and TOP register */ |
bogdanm | 0:9b334a45a8ff | 147 | BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1); |
bogdanm | 0:9b334a45a8ff | 148 | |
bogdanm | 0:9b334a45a8ff | 149 | /* Disable reset of CNT and TOP register */ |
bogdanm | 0:9b334a45a8ff | 150 | BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0); |
bogdanm | 0:9b334a45a8ff | 151 | } |
bogdanm | 0:9b334a45a8ff | 152 | |
bogdanm | 0:9b334a45a8ff | 153 | |
bogdanm | 0:9b334a45a8ff | 154 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 155 | * @brief |
bogdanm | 0:9b334a45a8ff | 156 | * Set counter and top values. |
bogdanm | 0:9b334a45a8ff | 157 | * |
bogdanm | 0:9b334a45a8ff | 158 | * @details |
bogdanm | 0:9b334a45a8ff | 159 | * The pulse counter is disabled while changing these values, and reenabled |
bogdanm | 0:9b334a45a8ff | 160 | * (if originally enabled) when values have been set. |
bogdanm | 0:9b334a45a8ff | 161 | * |
bogdanm | 0:9b334a45a8ff | 162 | * @note |
bogdanm | 0:9b334a45a8ff | 163 | * This function will stall until synchronization to low frequency domain is |
bogdanm | 0:9b334a45a8ff | 164 | * completed. For that reason, it should normally not be used when using |
bogdanm | 0:9b334a45a8ff | 165 | * an external clock to clock the PCNT module, since stall time may be |
bogdanm | 0:9b334a45a8ff | 166 | * undefined in that case. The counter should normally only be set when |
bogdanm | 0:9b334a45a8ff | 167 | * operating in (or about to enable) #pcntModeOvsSingle mode. |
bogdanm | 0:9b334a45a8ff | 168 | * |
bogdanm | 0:9b334a45a8ff | 169 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 170 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 171 | * |
bogdanm | 0:9b334a45a8ff | 172 | * @param[in] count |
bogdanm | 0:9b334a45a8ff | 173 | * Value to set in counter register. |
bogdanm | 0:9b334a45a8ff | 174 | * |
bogdanm | 0:9b334a45a8ff | 175 | * @param[in] top |
bogdanm | 0:9b334a45a8ff | 176 | * Value to set in top register. |
bogdanm | 0:9b334a45a8ff | 177 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 178 | void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top) |
bogdanm | 0:9b334a45a8ff | 179 | { |
bogdanm | 0:9b334a45a8ff | 180 | uint32_t ctrl; |
bogdanm | 0:9b334a45a8ff | 181 | |
bogdanm | 0:9b334a45a8ff | 182 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 183 | |
bogdanm | 0:9b334a45a8ff | 184 | #ifdef PCNT0 |
bogdanm | 0:9b334a45a8ff | 185 | if (PCNT0 == pcnt) |
bogdanm | 0:9b334a45a8ff | 186 | { |
bogdanm | 0:9b334a45a8ff | 187 | EFM_ASSERT((1<<PCNT0_CNT_SIZE) > count); |
bogdanm | 0:9b334a45a8ff | 188 | EFM_ASSERT((1<<PCNT0_CNT_SIZE) > top); |
bogdanm | 0:9b334a45a8ff | 189 | } |
bogdanm | 0:9b334a45a8ff | 190 | #endif |
bogdanm | 0:9b334a45a8ff | 191 | |
bogdanm | 0:9b334a45a8ff | 192 | #ifdef PCNT1 |
bogdanm | 0:9b334a45a8ff | 193 | if (PCNT1 == pcnt) |
bogdanm | 0:9b334a45a8ff | 194 | { |
bogdanm | 0:9b334a45a8ff | 195 | EFM_ASSERT((1<<PCNT1_CNT_SIZE) > count); |
bogdanm | 0:9b334a45a8ff | 196 | EFM_ASSERT((1<<PCNT1_CNT_SIZE) > top); |
bogdanm | 0:9b334a45a8ff | 197 | } |
bogdanm | 0:9b334a45a8ff | 198 | #endif |
bogdanm | 0:9b334a45a8ff | 199 | |
bogdanm | 0:9b334a45a8ff | 200 | #ifdef PCNT2 |
bogdanm | 0:9b334a45a8ff | 201 | if (PCNT2 == pcnt) |
bogdanm | 0:9b334a45a8ff | 202 | { |
bogdanm | 0:9b334a45a8ff | 203 | EFM_ASSERT((1<<PCNT2_CNT_SIZE) > count); |
bogdanm | 0:9b334a45a8ff | 204 | EFM_ASSERT((1<<PCNT2_CNT_SIZE) > top); |
bogdanm | 0:9b334a45a8ff | 205 | } |
bogdanm | 0:9b334a45a8ff | 206 | #endif |
bogdanm | 0:9b334a45a8ff | 207 | |
bogdanm | 0:9b334a45a8ff | 208 | /* Keep current control setting, must be restored */ |
bogdanm | 0:9b334a45a8ff | 209 | ctrl = pcnt->CTRL; |
bogdanm | 0:9b334a45a8ff | 210 | |
bogdanm | 0:9b334a45a8ff | 211 | /* If enabled, disable pulse counter before changing values */ |
bogdanm | 0:9b334a45a8ff | 212 | if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE) |
bogdanm | 0:9b334a45a8ff | 213 | { |
bogdanm | 0:9b334a45a8ff | 214 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 215 | pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE; |
bogdanm | 0:9b334a45a8ff | 216 | } |
bogdanm | 0:9b334a45a8ff | 217 | |
bogdanm | 0:9b334a45a8ff | 218 | /* Load into TOPB */ |
bogdanm | 0:9b334a45a8ff | 219 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB); |
bogdanm | 0:9b334a45a8ff | 220 | pcnt->TOPB = count; |
bogdanm | 0:9b334a45a8ff | 221 | |
bogdanm | 0:9b334a45a8ff | 222 | /* Load TOPB value into TOP */ |
bogdanm | 0:9b334a45a8ff | 223 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD); |
bogdanm | 0:9b334a45a8ff | 224 | |
bogdanm | 0:9b334a45a8ff | 225 | /* This bit has no effect on rev. C and onwards parts - for compatibility */ |
bogdanm | 0:9b334a45a8ff | 226 | pcnt->CMD = PCNT_CMD_LTOPBIM; |
bogdanm | 0:9b334a45a8ff | 227 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD); |
bogdanm | 0:9b334a45a8ff | 228 | |
bogdanm | 0:9b334a45a8ff | 229 | /* Load TOP into CNT */ |
bogdanm | 0:9b334a45a8ff | 230 | pcnt->CMD = PCNT_CMD_LCNTIM; |
bogdanm | 0:9b334a45a8ff | 231 | |
bogdanm | 0:9b334a45a8ff | 232 | /* Restore TOP? ('count' setting has been loaded into pcnt->TOP, better |
bogdanm | 0:9b334a45a8ff | 233 | * to use 'top' than pcnt->TOP in compare, since latter may in theory not |
bogdanm | 0:9b334a45a8ff | 234 | * be visible yet.) */ |
bogdanm | 0:9b334a45a8ff | 235 | if (top != count) |
bogdanm | 0:9b334a45a8ff | 236 | { |
bogdanm | 0:9b334a45a8ff | 237 | /* Wait for command to sync LCNTIM before setting TOPB */ |
bogdanm | 0:9b334a45a8ff | 238 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD); |
bogdanm | 0:9b334a45a8ff | 239 | |
bogdanm | 0:9b334a45a8ff | 240 | /* Load into TOPB, we don't need to check for TOPB sync complete here, |
bogdanm | 0:9b334a45a8ff | 241 | * it has been ensured above. */ |
bogdanm | 0:9b334a45a8ff | 242 | pcnt->TOPB = top; |
bogdanm | 0:9b334a45a8ff | 243 | |
bogdanm | 0:9b334a45a8ff | 244 | /* Load TOPB value into TOP */ |
bogdanm | 0:9b334a45a8ff | 245 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD); |
bogdanm | 0:9b334a45a8ff | 246 | pcnt->CMD = PCNT_CMD_LTOPBIM; |
bogdanm | 0:9b334a45a8ff | 247 | } |
bogdanm | 0:9b334a45a8ff | 248 | |
bogdanm | 0:9b334a45a8ff | 249 | /* Reenable if it was enabled */ |
bogdanm | 0:9b334a45a8ff | 250 | if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE) |
bogdanm | 0:9b334a45a8ff | 251 | { |
bogdanm | 0:9b334a45a8ff | 252 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD); |
bogdanm | 0:9b334a45a8ff | 253 | pcnt->CTRL = ctrl; |
bogdanm | 0:9b334a45a8ff | 254 | } |
bogdanm | 0:9b334a45a8ff | 255 | } |
bogdanm | 0:9b334a45a8ff | 256 | |
bogdanm | 0:9b334a45a8ff | 257 | |
bogdanm | 0:9b334a45a8ff | 258 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 259 | * @brief |
bogdanm | 0:9b334a45a8ff | 260 | * Set PCNT operational mode. |
bogdanm | 0:9b334a45a8ff | 261 | * |
bogdanm | 0:9b334a45a8ff | 262 | * @details |
bogdanm | 0:9b334a45a8ff | 263 | * Notice that this function does not do any configuration. Setting operational |
bogdanm | 0:9b334a45a8ff | 264 | * mode is normally only required after initialization is done, and if not |
bogdanm | 0:9b334a45a8ff | 265 | * done as part of initialization. Or if requiring to disable/reenable pulse |
bogdanm | 0:9b334a45a8ff | 266 | * counter. |
bogdanm | 0:9b334a45a8ff | 267 | * |
bogdanm | 0:9b334a45a8ff | 268 | * @note |
bogdanm | 0:9b334a45a8ff | 269 | * This function may stall until synchronization to low frequency domain is |
bogdanm | 0:9b334a45a8ff | 270 | * completed. For that reason, it should normally not be used when using |
bogdanm | 0:9b334a45a8ff | 271 | * an external clock to clock the PCNT module, since stall time may be |
bogdanm | 0:9b334a45a8ff | 272 | * undefined in that case. |
bogdanm | 0:9b334a45a8ff | 273 | * |
bogdanm | 0:9b334a45a8ff | 274 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 275 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 276 | * |
bogdanm | 0:9b334a45a8ff | 277 | * @param[in] mode |
bogdanm | 0:9b334a45a8ff | 278 | * Operational mode to use for PCNT. |
bogdanm | 0:9b334a45a8ff | 279 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 280 | void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode) |
bogdanm | 0:9b334a45a8ff | 281 | { |
bogdanm | 0:9b334a45a8ff | 282 | uint32_t tmp; |
bogdanm | 0:9b334a45a8ff | 283 | |
bogdanm | 0:9b334a45a8ff | 284 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 285 | |
bogdanm | 0:9b334a45a8ff | 286 | /* Set as specified */ |
bogdanm | 0:9b334a45a8ff | 287 | tmp = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK; |
bogdanm | 0:9b334a45a8ff | 288 | tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT; |
bogdanm | 0:9b334a45a8ff | 289 | |
bogdanm | 0:9b334a45a8ff | 290 | /* LF register about to be modified require sync. busy check */ |
bogdanm | 0:9b334a45a8ff | 291 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 292 | pcnt->CTRL = tmp; |
bogdanm | 0:9b334a45a8ff | 293 | } |
bogdanm | 0:9b334a45a8ff | 294 | |
bogdanm | 0:9b334a45a8ff | 295 | #if defined( _PCNT_INPUT_MASK ) |
bogdanm | 0:9b334a45a8ff | 296 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 297 | * @brief |
bogdanm | 0:9b334a45a8ff | 298 | * Enable/disable the selected PRS input of PCNT. |
bogdanm | 0:9b334a45a8ff | 299 | * |
bogdanm | 0:9b334a45a8ff | 300 | * @details |
bogdanm | 0:9b334a45a8ff | 301 | * Notice that this function does not do any configuration. |
bogdanm | 0:9b334a45a8ff | 302 | * |
bogdanm | 0:9b334a45a8ff | 303 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 304 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 305 | * |
bogdanm | 0:9b334a45a8ff | 306 | * @param[in] prsInput |
bogdanm | 0:9b334a45a8ff | 307 | * PRS input (S0 or S1) of the selected PCNT module. |
bogdanm | 0:9b334a45a8ff | 308 | * |
bogdanm | 0:9b334a45a8ff | 309 | * @param[in] enable |
bogdanm | 0:9b334a45a8ff | 310 | * Set to true to enable, false to disable the selected PRS input. |
bogdanm | 0:9b334a45a8ff | 311 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 312 | void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt, |
bogdanm | 0:9b334a45a8ff | 313 | PCNT_PRSInput_TypeDef prsInput, |
bogdanm | 0:9b334a45a8ff | 314 | bool enable) |
bogdanm | 0:9b334a45a8ff | 315 | { |
bogdanm | 0:9b334a45a8ff | 316 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 317 | |
bogdanm | 0:9b334a45a8ff | 318 | /* Enable/disable the selected PRS input on the selected PCNT module. */ |
bogdanm | 0:9b334a45a8ff | 319 | switch (prsInput) |
bogdanm | 0:9b334a45a8ff | 320 | { |
bogdanm | 0:9b334a45a8ff | 321 | /* Enable/disable PRS input S0. */ |
bogdanm | 0:9b334a45a8ff | 322 | case pcntPRSInputS0: |
bogdanm | 0:9b334a45a8ff | 323 | { |
bogdanm | 0:9b334a45a8ff | 324 | BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, (uint32_t)enable); |
bogdanm | 0:9b334a45a8ff | 325 | } |
bogdanm | 0:9b334a45a8ff | 326 | break; |
bogdanm | 0:9b334a45a8ff | 327 | |
bogdanm | 0:9b334a45a8ff | 328 | /* Enable/disable PRS input S1. */ |
bogdanm | 0:9b334a45a8ff | 329 | case pcntPRSInputS1: |
bogdanm | 0:9b334a45a8ff | 330 | { |
bogdanm | 0:9b334a45a8ff | 331 | BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, (uint32_t)enable); |
bogdanm | 0:9b334a45a8ff | 332 | } |
bogdanm | 0:9b334a45a8ff | 333 | break; |
bogdanm | 0:9b334a45a8ff | 334 | |
bogdanm | 0:9b334a45a8ff | 335 | /* Invalid parameter, asserted. */ |
bogdanm | 0:9b334a45a8ff | 336 | default: |
bogdanm | 0:9b334a45a8ff | 337 | { |
bogdanm | 0:9b334a45a8ff | 338 | EFM_ASSERT(0); |
bogdanm | 0:9b334a45a8ff | 339 | } |
bogdanm | 0:9b334a45a8ff | 340 | break; |
bogdanm | 0:9b334a45a8ff | 341 | } |
bogdanm | 0:9b334a45a8ff | 342 | } |
bogdanm | 0:9b334a45a8ff | 343 | #endif |
bogdanm | 0:9b334a45a8ff | 344 | |
bogdanm | 0:9b334a45a8ff | 345 | |
bogdanm | 0:9b334a45a8ff | 346 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 347 | * @brief |
bogdanm | 0:9b334a45a8ff | 348 | * PCNT register synchronization freeze control. |
bogdanm | 0:9b334a45a8ff | 349 | * |
bogdanm | 0:9b334a45a8ff | 350 | * @details |
bogdanm | 0:9b334a45a8ff | 351 | * Some PCNT registers require synchronization into the low frequency (LF) |
bogdanm | 0:9b334a45a8ff | 352 | * domain. The freeze feature allows for several such registers to be |
bogdanm | 0:9b334a45a8ff | 353 | * modified before passing them to the LF domain simultaneously (which |
bogdanm | 0:9b334a45a8ff | 354 | * takes place when the freeze mode is disabled). |
bogdanm | 0:9b334a45a8ff | 355 | * |
bogdanm | 0:9b334a45a8ff | 356 | * @note |
bogdanm | 0:9b334a45a8ff | 357 | * When enabling freeze mode, this function will wait for all current |
bogdanm | 0:9b334a45a8ff | 358 | * ongoing PCNT synchronization to LF domain to complete (Normally |
bogdanm | 0:9b334a45a8ff | 359 | * synchronization will not be in progress.) However for this reason, when |
bogdanm | 0:9b334a45a8ff | 360 | * using freeze mode, modifications of registers requiring LF synchronization |
bogdanm | 0:9b334a45a8ff | 361 | * should be done within one freeze enable/disable block to avoid unecessary |
bogdanm | 0:9b334a45a8ff | 362 | * stalling. |
bogdanm | 0:9b334a45a8ff | 363 | * |
bogdanm | 0:9b334a45a8ff | 364 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 365 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 366 | * |
bogdanm | 0:9b334a45a8ff | 367 | * @param[in] enable |
bogdanm | 0:9b334a45a8ff | 368 | * @li true - enable freeze, modified registers are not propagated to the |
bogdanm | 0:9b334a45a8ff | 369 | * LF domain |
bogdanm | 0:9b334a45a8ff | 370 | * @li false - disables freeze, modified registers are propagated to LF |
bogdanm | 0:9b334a45a8ff | 371 | * domain |
bogdanm | 0:9b334a45a8ff | 372 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 373 | void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable) |
bogdanm | 0:9b334a45a8ff | 374 | { |
bogdanm | 0:9b334a45a8ff | 375 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 376 | |
bogdanm | 0:9b334a45a8ff | 377 | if (enable) |
bogdanm | 0:9b334a45a8ff | 378 | { |
bogdanm | 0:9b334a45a8ff | 379 | /* Wait for any ongoing LF synchronization to complete. This is just to |
bogdanm | 0:9b334a45a8ff | 380 | * protect against the rare case when a user: |
bogdanm | 0:9b334a45a8ff | 381 | * - modifies a register requiring LF sync |
bogdanm | 0:9b334a45a8ff | 382 | * - then enables freeze before LF sync completed |
bogdanm | 0:9b334a45a8ff | 383 | * - then modifies the same register again |
bogdanm | 0:9b334a45a8ff | 384 | * since modifying a register while it is in sync progress should be |
bogdanm | 0:9b334a45a8ff | 385 | * avoided. */ |
bogdanm | 0:9b334a45a8ff | 386 | while (pcnt->SYNCBUSY) |
bogdanm | 0:9b334a45a8ff | 387 | ; |
bogdanm | 0:9b334a45a8ff | 388 | |
bogdanm | 0:9b334a45a8ff | 389 | pcnt->FREEZE = PCNT_FREEZE_REGFREEZE; |
bogdanm | 0:9b334a45a8ff | 390 | } |
bogdanm | 0:9b334a45a8ff | 391 | else |
bogdanm | 0:9b334a45a8ff | 392 | { |
bogdanm | 0:9b334a45a8ff | 393 | pcnt->FREEZE = 0; |
bogdanm | 0:9b334a45a8ff | 394 | } |
bogdanm | 0:9b334a45a8ff | 395 | } |
bogdanm | 0:9b334a45a8ff | 396 | |
bogdanm | 0:9b334a45a8ff | 397 | |
bogdanm | 0:9b334a45a8ff | 398 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 399 | * @brief |
bogdanm | 0:9b334a45a8ff | 400 | * Init pulse counter. |
bogdanm | 0:9b334a45a8ff | 401 | * |
bogdanm | 0:9b334a45a8ff | 402 | * @details |
bogdanm | 0:9b334a45a8ff | 403 | * This function will configure the pulse counter. The clock selection is |
bogdanm | 0:9b334a45a8ff | 404 | * configured as follows, depending on operational mode: |
bogdanm | 0:9b334a45a8ff | 405 | * |
bogdanm | 0:9b334a45a8ff | 406 | * @li #pcntModeOvsSingle - Use LFACLK. |
bogdanm | 0:9b334a45a8ff | 407 | * @li #pcntModeExtSingle - Use external PCNTn_S0 pin. |
bogdanm | 0:9b334a45a8ff | 408 | * @li #pcntModeExtQuad - Use external PCNTn_S0 pin. |
bogdanm | 0:9b334a45a8ff | 409 | * |
bogdanm | 0:9b334a45a8ff | 410 | * Notice that the LFACLK must be enabled in all modes, since some basic setup |
bogdanm | 0:9b334a45a8ff | 411 | * is done with this clock even if external pin clock usage mode is chosen. |
bogdanm | 0:9b334a45a8ff | 412 | * The pulse counter clock for the selected instance must also be enabled |
bogdanm | 0:9b334a45a8ff | 413 | * prior to init. |
bogdanm | 0:9b334a45a8ff | 414 | * |
bogdanm | 0:9b334a45a8ff | 415 | * Notice that pins used by the PCNT module must be properly configured |
bogdanm | 0:9b334a45a8ff | 416 | * by the user explicitly through setting the ROUTE register, in order for |
bogdanm | 0:9b334a45a8ff | 417 | * the PCNT to work as intended. |
bogdanm | 0:9b334a45a8ff | 418 | * |
bogdanm | 0:9b334a45a8ff | 419 | * Writing to CNT will not occur in external clock modes (EXTCLKQUAD and |
bogdanm | 0:9b334a45a8ff | 420 | * EXTCLKSINGLE) because the external clock rate is unknown. The user should |
bogdanm | 0:9b334a45a8ff | 421 | * handle it manually depending on the application |
bogdanm | 0:9b334a45a8ff | 422 | * |
bogdanm | 0:9b334a45a8ff | 423 | * TOPB is written for all modes but in external clock mode it will take |
bogdanm | 0:9b334a45a8ff | 424 | * 3 external clock cycles to sync to TOP |
bogdanm | 0:9b334a45a8ff | 425 | * |
bogdanm | 0:9b334a45a8ff | 426 | * |
bogdanm | 0:9b334a45a8ff | 427 | * @note |
bogdanm | 0:9b334a45a8ff | 428 | * Initializing requires synchronization into the low frequency domain. This |
bogdanm | 0:9b334a45a8ff | 429 | * may cause some delay. |
bogdanm | 0:9b334a45a8ff | 430 | * |
bogdanm | 0:9b334a45a8ff | 431 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 432 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 433 | * |
bogdanm | 0:9b334a45a8ff | 434 | * @param[in] init |
bogdanm | 0:9b334a45a8ff | 435 | * Pointer to initialization structure used to initialize. |
bogdanm | 0:9b334a45a8ff | 436 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 437 | void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init) |
bogdanm | 0:9b334a45a8ff | 438 | { |
bogdanm | 0:9b334a45a8ff | 439 | unsigned int inst; |
bogdanm | 0:9b334a45a8ff | 440 | uint32_t tmp; |
bogdanm | 0:9b334a45a8ff | 441 | |
bogdanm | 0:9b334a45a8ff | 442 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 443 | |
bogdanm | 0:9b334a45a8ff | 444 | #ifdef PCNT0 |
bogdanm | 0:9b334a45a8ff | 445 | if (PCNT0 == pcnt) |
bogdanm | 0:9b334a45a8ff | 446 | { |
bogdanm | 0:9b334a45a8ff | 447 | EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->counter); |
bogdanm | 0:9b334a45a8ff | 448 | EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->top); |
bogdanm | 0:9b334a45a8ff | 449 | } |
bogdanm | 0:9b334a45a8ff | 450 | #endif |
bogdanm | 0:9b334a45a8ff | 451 | |
bogdanm | 0:9b334a45a8ff | 452 | #ifdef PCNT1 |
bogdanm | 0:9b334a45a8ff | 453 | if (PCNT1 == pcnt) |
bogdanm | 0:9b334a45a8ff | 454 | { |
bogdanm | 0:9b334a45a8ff | 455 | EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->counter); |
bogdanm | 0:9b334a45a8ff | 456 | EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->top); |
bogdanm | 0:9b334a45a8ff | 457 | } |
bogdanm | 0:9b334a45a8ff | 458 | #endif |
bogdanm | 0:9b334a45a8ff | 459 | |
bogdanm | 0:9b334a45a8ff | 460 | #ifdef PCNT2 |
bogdanm | 0:9b334a45a8ff | 461 | if (PCNT2 == pcnt) |
bogdanm | 0:9b334a45a8ff | 462 | { |
bogdanm | 0:9b334a45a8ff | 463 | EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->counter); |
bogdanm | 0:9b334a45a8ff | 464 | EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->top); |
bogdanm | 0:9b334a45a8ff | 465 | } |
bogdanm | 0:9b334a45a8ff | 466 | #endif |
bogdanm | 0:9b334a45a8ff | 467 | |
bogdanm | 0:9b334a45a8ff | 468 | /* Map pointer to instance */ |
bogdanm | 0:9b334a45a8ff | 469 | inst = PCNT_Map(pcnt); |
bogdanm | 0:9b334a45a8ff | 470 | |
bogdanm | 0:9b334a45a8ff | 471 | #if defined( _PCNT_INPUT_MASK ) |
bogdanm | 0:9b334a45a8ff | 472 | /* Selecting the PRS channels for the PRS input sources of the PCNT. These are |
bogdanm | 0:9b334a45a8ff | 473 | * written with a Read-Modify-Write sequence in order to keep the value of the |
bogdanm | 0:9b334a45a8ff | 474 | * input enable bits which can be modified using PCNT_PRSInputEnable(). */ |
bogdanm | 0:9b334a45a8ff | 475 | tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK); |
bogdanm | 0:9b334a45a8ff | 476 | tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT) | |
bogdanm | 0:9b334a45a8ff | 477 | ((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT); |
bogdanm | 0:9b334a45a8ff | 478 | pcnt->INPUT = tmp; |
bogdanm | 0:9b334a45a8ff | 479 | #endif |
bogdanm | 0:9b334a45a8ff | 480 | |
bogdanm | 0:9b334a45a8ff | 481 | /* Build CTRL setting, except for mode */ |
bogdanm | 0:9b334a45a8ff | 482 | tmp = 0; |
bogdanm | 0:9b334a45a8ff | 483 | if (init->negEdge) |
bogdanm | 0:9b334a45a8ff | 484 | { |
bogdanm | 0:9b334a45a8ff | 485 | tmp |= PCNT_CTRL_EDGE_NEG; |
bogdanm | 0:9b334a45a8ff | 486 | } |
bogdanm | 0:9b334a45a8ff | 487 | |
bogdanm | 0:9b334a45a8ff | 488 | if (init->countDown) |
bogdanm | 0:9b334a45a8ff | 489 | { |
bogdanm | 0:9b334a45a8ff | 490 | tmp |= PCNT_CTRL_CNTDIR_DOWN; |
bogdanm | 0:9b334a45a8ff | 491 | } |
bogdanm | 0:9b334a45a8ff | 492 | |
bogdanm | 0:9b334a45a8ff | 493 | if (init->filter) |
bogdanm | 0:9b334a45a8ff | 494 | { |
bogdanm | 0:9b334a45a8ff | 495 | tmp |= PCNT_CTRL_FILT; |
bogdanm | 0:9b334a45a8ff | 496 | } |
bogdanm | 0:9b334a45a8ff | 497 | |
bogdanm | 0:9b334a45a8ff | 498 | #if defined( PCNT_CTRL_HYST ) |
bogdanm | 0:9b334a45a8ff | 499 | if (init->hyst) |
bogdanm | 0:9b334a45a8ff | 500 | { |
bogdanm | 0:9b334a45a8ff | 501 | tmp |= PCNT_CTRL_HYST; |
bogdanm | 0:9b334a45a8ff | 502 | } |
bogdanm | 0:9b334a45a8ff | 503 | #endif |
bogdanm | 0:9b334a45a8ff | 504 | |
bogdanm | 0:9b334a45a8ff | 505 | #if defined( PCNT_CTRL_S1CDIR ) |
bogdanm | 0:9b334a45a8ff | 506 | if (init->s1CntDir) |
bogdanm | 0:9b334a45a8ff | 507 | { |
bogdanm | 0:9b334a45a8ff | 508 | tmp |= PCNT_CTRL_S1CDIR; |
bogdanm | 0:9b334a45a8ff | 509 | } |
bogdanm | 0:9b334a45a8ff | 510 | #endif |
bogdanm | 0:9b334a45a8ff | 511 | |
bogdanm | 0:9b334a45a8ff | 512 | /* Configure counter events for regular and auxiliary counter. */ |
bogdanm | 0:9b334a45a8ff | 513 | #if defined( _PCNT_CTRL_CNTEV_SHIFT ) |
bogdanm | 0:9b334a45a8ff | 514 | tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT; |
bogdanm | 0:9b334a45a8ff | 515 | #endif |
bogdanm | 0:9b334a45a8ff | 516 | |
bogdanm | 0:9b334a45a8ff | 517 | #if defined( _PCNT_CTRL_AUXCNTEV_SHIFT ) |
bogdanm | 0:9b334a45a8ff | 518 | { |
bogdanm | 0:9b334a45a8ff | 519 | /* Modify the auxCntEvent value before writing to the AUXCNTEV field in |
bogdanm | 0:9b334a45a8ff | 520 | the CTRL register because the AUXCNTEV field values are different from |
bogdanm | 0:9b334a45a8ff | 521 | the CNTEV field values, and cntEvent and auxCntEvent are of the same type |
bogdanm | 0:9b334a45a8ff | 522 | PCNT_CntEvent_TypeDef. |
bogdanm | 0:9b334a45a8ff | 523 | */ |
bogdanm | 0:9b334a45a8ff | 524 | uint32_t auxCntEventField = 0; /* Get rid of compiler warning. */ |
bogdanm | 0:9b334a45a8ff | 525 | switch (init->auxCntEvent) |
bogdanm | 0:9b334a45a8ff | 526 | { |
bogdanm | 0:9b334a45a8ff | 527 | case pcntCntEventBoth: |
bogdanm | 0:9b334a45a8ff | 528 | auxCntEventField = pcntCntEventNone; |
bogdanm | 0:9b334a45a8ff | 529 | break; |
bogdanm | 0:9b334a45a8ff | 530 | case pcntCntEventNone: |
bogdanm | 0:9b334a45a8ff | 531 | auxCntEventField = pcntCntEventBoth; |
bogdanm | 0:9b334a45a8ff | 532 | break; |
bogdanm | 0:9b334a45a8ff | 533 | case pcntCntEventUp: |
bogdanm | 0:9b334a45a8ff | 534 | case pcntCntEventDown: |
bogdanm | 0:9b334a45a8ff | 535 | auxCntEventField = init->auxCntEvent; |
bogdanm | 0:9b334a45a8ff | 536 | break; |
bogdanm | 0:9b334a45a8ff | 537 | default: |
bogdanm | 0:9b334a45a8ff | 538 | /* Invalid parameter, asserted. */ |
bogdanm | 0:9b334a45a8ff | 539 | EFM_ASSERT(0); |
bogdanm | 0:9b334a45a8ff | 540 | } |
bogdanm | 0:9b334a45a8ff | 541 | tmp |= auxCntEventField << _PCNT_CTRL_AUXCNTEV_SHIFT; |
bogdanm | 0:9b334a45a8ff | 542 | } |
bogdanm | 0:9b334a45a8ff | 543 | #endif |
bogdanm | 0:9b334a45a8ff | 544 | |
bogdanm | 0:9b334a45a8ff | 545 | /* Reset pulse counter while changing clock source. The reset bit */ |
bogdanm | 0:9b334a45a8ff | 546 | /* is asynchronous, we don't have to check for SYNCBUSY. */ |
bogdanm | 0:9b334a45a8ff | 547 | BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1); |
bogdanm | 0:9b334a45a8ff | 548 | |
bogdanm | 0:9b334a45a8ff | 549 | /* Select LFACLK to clock in control setting */ |
bogdanm | 0:9b334a45a8ff | 550 | CMU_PCNTClockExternalSet(inst, false); |
bogdanm | 0:9b334a45a8ff | 551 | |
bogdanm | 0:9b334a45a8ff | 552 | /* Handling depends on whether using external clock or not. */ |
bogdanm | 0:9b334a45a8ff | 553 | switch (init->mode) |
bogdanm | 0:9b334a45a8ff | 554 | { |
bogdanm | 0:9b334a45a8ff | 555 | case pcntModeExtSingle: |
bogdanm | 0:9b334a45a8ff | 556 | case pcntModeExtQuad: |
bogdanm | 0:9b334a45a8ff | 557 | tmp |= init->mode << _PCNT_CTRL_MODE_SHIFT; |
bogdanm | 0:9b334a45a8ff | 558 | |
bogdanm | 0:9b334a45a8ff | 559 | /* In most cases, the SYNCBUSY bit is set due to reset bit set, and waiting |
bogdanm | 0:9b334a45a8ff | 560 | * for asynchronous reset bit is strictly not necessary. |
bogdanm | 0:9b334a45a8ff | 561 | * But in theory, other operations on CTRL register may have been done |
bogdanm | 0:9b334a45a8ff | 562 | * outside this function, so wait. */ |
bogdanm | 0:9b334a45a8ff | 563 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 564 | |
bogdanm | 0:9b334a45a8ff | 565 | /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing |
bogdanm | 0:9b334a45a8ff | 566 | * the clock source to an external clock */ |
bogdanm | 0:9b334a45a8ff | 567 | pcnt->CTRL = PCNT_CTRL_RSTEN; |
bogdanm | 0:9b334a45a8ff | 568 | |
bogdanm | 0:9b334a45a8ff | 569 | /* Wait until CTRL write synchronized into LF domain. */ |
bogdanm | 0:9b334a45a8ff | 570 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 571 | |
bogdanm | 0:9b334a45a8ff | 572 | /* Change to external clock BEFORE disabling reset */ |
bogdanm | 0:9b334a45a8ff | 573 | CMU_PCNTClockExternalSet(inst, true); |
bogdanm | 0:9b334a45a8ff | 574 | |
bogdanm | 0:9b334a45a8ff | 575 | /* Write to TOPB. If using external clock TOPB will sync to TOP at the same |
bogdanm | 0:9b334a45a8ff | 576 | * time as the mode. This will insure that if the user chooses to count |
bogdanm | 0:9b334a45a8ff | 577 | * down, the first "countable" pulse will make CNT go to TOP and not 0xFF |
bogdanm | 0:9b334a45a8ff | 578 | * (default TOP value). */ |
bogdanm | 0:9b334a45a8ff | 579 | pcnt->TOPB = init->top; |
bogdanm | 0:9b334a45a8ff | 580 | |
bogdanm | 0:9b334a45a8ff | 581 | /* This bit has no effect on rev. C and onwards parts - for compatibility */ |
bogdanm | 0:9b334a45a8ff | 582 | pcnt->CMD = PCNT_CMD_LTOPBIM; |
bogdanm | 0:9b334a45a8ff | 583 | |
bogdanm | 0:9b334a45a8ff | 584 | /* Write the CTRL register with the configurations. |
bogdanm | 0:9b334a45a8ff | 585 | * This should be written after TOPB in the eventuality of a pulse between |
bogdanm | 0:9b334a45a8ff | 586 | * these two writes that would cause the CTRL register to be synced one |
bogdanm | 0:9b334a45a8ff | 587 | * clock cycle earlier than the TOPB. */ |
bogdanm | 0:9b334a45a8ff | 588 | pcnt->CTRL = tmp; |
bogdanm | 0:9b334a45a8ff | 589 | |
bogdanm | 0:9b334a45a8ff | 590 | /* There are no syncs for TOP, CMD or CTRL because the clock rate is unknown |
bogdanm | 0:9b334a45a8ff | 591 | * and the program could stall |
bogdanm | 0:9b334a45a8ff | 592 | * These will be synced within 3 clock cycles of the external clock / |
bogdanm | 0:9b334a45a8ff | 593 | * For the same reason CNT cannot be written here. */ |
bogdanm | 0:9b334a45a8ff | 594 | break; |
bogdanm | 0:9b334a45a8ff | 595 | |
bogdanm | 0:9b334a45a8ff | 596 | /* pcntModeDisable */ |
bogdanm | 0:9b334a45a8ff | 597 | /* pcntModeOvsSingle */ |
bogdanm | 0:9b334a45a8ff | 598 | default: |
bogdanm | 0:9b334a45a8ff | 599 | /* No need to set disabled mode if already disabled. */ |
bogdanm | 0:9b334a45a8ff | 600 | if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE) |
bogdanm | 0:9b334a45a8ff | 601 | { |
bogdanm | 0:9b334a45a8ff | 602 | /* Set control to disabled mode, leave reset on until ensured disabled. |
bogdanm | 0:9b334a45a8ff | 603 | * We don't need to wait for CTRL SYNCBUSY completion here, it was |
bogdanm | 0:9b334a45a8ff | 604 | * triggered by reset bit above, which is asynchronous. */ |
bogdanm | 0:9b334a45a8ff | 605 | pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN; |
bogdanm | 0:9b334a45a8ff | 606 | |
bogdanm | 0:9b334a45a8ff | 607 | /* Wait until CTRL write synchronized into LF domain before proceeding |
bogdanm | 0:9b334a45a8ff | 608 | * to disable reset. */ |
bogdanm | 0:9b334a45a8ff | 609 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 610 | } |
bogdanm | 0:9b334a45a8ff | 611 | |
bogdanm | 0:9b334a45a8ff | 612 | /* Disable reset bit, counter should now be in disabled mode. */ |
bogdanm | 0:9b334a45a8ff | 613 | BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0); |
bogdanm | 0:9b334a45a8ff | 614 | |
bogdanm | 0:9b334a45a8ff | 615 | /* Set counter and top values as specified. */ |
bogdanm | 0:9b334a45a8ff | 616 | PCNT_CounterTopSet(pcnt, init->counter, init->top); |
bogdanm | 0:9b334a45a8ff | 617 | |
bogdanm | 0:9b334a45a8ff | 618 | /* Enter oversampling mode if selected. */ |
bogdanm | 0:9b334a45a8ff | 619 | if (init->mode == pcntModeOvsSingle) |
bogdanm | 0:9b334a45a8ff | 620 | { |
bogdanm | 0:9b334a45a8ff | 621 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 622 | pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT); |
bogdanm | 0:9b334a45a8ff | 623 | } |
bogdanm | 0:9b334a45a8ff | 624 | break; |
bogdanm | 0:9b334a45a8ff | 625 | } |
bogdanm | 0:9b334a45a8ff | 626 | } |
bogdanm | 0:9b334a45a8ff | 627 | |
bogdanm | 0:9b334a45a8ff | 628 | |
bogdanm | 0:9b334a45a8ff | 629 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 630 | * @brief |
bogdanm | 0:9b334a45a8ff | 631 | * Reset PCNT to same state as after a HW reset. |
bogdanm | 0:9b334a45a8ff | 632 | * |
bogdanm | 0:9b334a45a8ff | 633 | * @details |
bogdanm | 0:9b334a45a8ff | 634 | * Notice the LFACLK must be enabled, since some basic reset is done with |
bogdanm | 0:9b334a45a8ff | 635 | * this clock. The pulse counter clock for the selected instance must also |
bogdanm | 0:9b334a45a8ff | 636 | * be enabled prior to init. |
bogdanm | 0:9b334a45a8ff | 637 | * |
bogdanm | 0:9b334a45a8ff | 638 | * @note |
bogdanm | 0:9b334a45a8ff | 639 | * The ROUTE register is NOT reset by this function, in order to allow for |
bogdanm | 0:9b334a45a8ff | 640 | * centralized setup of this feature. |
bogdanm | 0:9b334a45a8ff | 641 | * |
bogdanm | 0:9b334a45a8ff | 642 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 643 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 644 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 645 | void PCNT_Reset(PCNT_TypeDef *pcnt) |
bogdanm | 0:9b334a45a8ff | 646 | { |
bogdanm | 0:9b334a45a8ff | 647 | unsigned int inst; |
bogdanm | 0:9b334a45a8ff | 648 | |
bogdanm | 0:9b334a45a8ff | 649 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 650 | |
bogdanm | 0:9b334a45a8ff | 651 | /* Map pointer to instance and clock info */ |
bogdanm | 0:9b334a45a8ff | 652 | inst = PCNT_Map(pcnt); |
bogdanm | 0:9b334a45a8ff | 653 | |
bogdanm | 0:9b334a45a8ff | 654 | pcnt->IEN = _PCNT_IEN_RESETVALUE; |
bogdanm | 0:9b334a45a8ff | 655 | |
bogdanm | 0:9b334a45a8ff | 656 | /* Notice that special SYNCBUSY handling is not applicable for the RSTEN |
bogdanm | 0:9b334a45a8ff | 657 | * bit of the control register, so we don't need to wait for it when only |
bogdanm | 0:9b334a45a8ff | 658 | * modifying RSTEN. The SYNCBUSY bit will be set, leading to a |
bogdanm | 0:9b334a45a8ff | 659 | * synchronization in the LF domain, with in reality no changes to LF domain. |
bogdanm | 0:9b334a45a8ff | 660 | * Enable reset of CNT and TOP register. */ |
bogdanm | 0:9b334a45a8ff | 661 | BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1); |
bogdanm | 0:9b334a45a8ff | 662 | |
bogdanm | 0:9b334a45a8ff | 663 | /* Select LFACLK as default */ |
bogdanm | 0:9b334a45a8ff | 664 | CMU_PCNTClockExternalSet(inst, false); |
bogdanm | 0:9b334a45a8ff | 665 | |
bogdanm | 0:9b334a45a8ff | 666 | PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE); |
bogdanm | 0:9b334a45a8ff | 667 | |
bogdanm | 0:9b334a45a8ff | 668 | /* Reset CTRL leaving RSTEN set */ |
bogdanm | 0:9b334a45a8ff | 669 | pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN; |
bogdanm | 0:9b334a45a8ff | 670 | |
bogdanm | 0:9b334a45a8ff | 671 | /* Disable reset after CTRL reg has been synchronized */ |
bogdanm | 0:9b334a45a8ff | 672 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); |
bogdanm | 0:9b334a45a8ff | 673 | BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0); |
bogdanm | 0:9b334a45a8ff | 674 | |
bogdanm | 0:9b334a45a8ff | 675 | /* Clear pending interrupts */ |
bogdanm | 0:9b334a45a8ff | 676 | pcnt->IFC = _PCNT_IFC_MASK; |
bogdanm | 0:9b334a45a8ff | 677 | |
bogdanm | 0:9b334a45a8ff | 678 | /* Do not reset route register, setting should be done independently */ |
bogdanm | 0:9b334a45a8ff | 679 | } |
bogdanm | 0:9b334a45a8ff | 680 | |
bogdanm | 0:9b334a45a8ff | 681 | |
bogdanm | 0:9b334a45a8ff | 682 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 683 | * @brief |
bogdanm | 0:9b334a45a8ff | 684 | * Set top buffer value. |
bogdanm | 0:9b334a45a8ff | 685 | * |
bogdanm | 0:9b334a45a8ff | 686 | * @note |
bogdanm | 0:9b334a45a8ff | 687 | * This function may stall until synchronization to low frequency domain is |
bogdanm | 0:9b334a45a8ff | 688 | * completed. For that reason, it should normally not be used when using |
bogdanm | 0:9b334a45a8ff | 689 | * an external clock to clock the PCNT module, since stall time may be |
bogdanm | 0:9b334a45a8ff | 690 | * undefined in that case. |
bogdanm | 0:9b334a45a8ff | 691 | * |
bogdanm | 0:9b334a45a8ff | 692 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 693 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 694 | * |
bogdanm | 0:9b334a45a8ff | 695 | * @param[in] val |
bogdanm | 0:9b334a45a8ff | 696 | * Value to set in top buffer register. |
bogdanm | 0:9b334a45a8ff | 697 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 698 | void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val) |
bogdanm | 0:9b334a45a8ff | 699 | { |
bogdanm | 0:9b334a45a8ff | 700 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 701 | |
bogdanm | 0:9b334a45a8ff | 702 | /* LF register about to be modified require sync. busy check */ |
bogdanm | 0:9b334a45a8ff | 703 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB); |
bogdanm | 0:9b334a45a8ff | 704 | pcnt->TOPB = val; |
bogdanm | 0:9b334a45a8ff | 705 | } |
bogdanm | 0:9b334a45a8ff | 706 | |
bogdanm | 0:9b334a45a8ff | 707 | |
bogdanm | 0:9b334a45a8ff | 708 | /***************************************************************************//** |
bogdanm | 0:9b334a45a8ff | 709 | * @brief |
bogdanm | 0:9b334a45a8ff | 710 | * Set top value. |
bogdanm | 0:9b334a45a8ff | 711 | * |
bogdanm | 0:9b334a45a8ff | 712 | * @note |
bogdanm | 0:9b334a45a8ff | 713 | * This function will stall until synchronization to low frequency domain is |
bogdanm | 0:9b334a45a8ff | 714 | * completed. For that reason, it should normally not be used when using |
bogdanm | 0:9b334a45a8ff | 715 | * an external clock to clock the PCNT module, since stall time may be |
bogdanm | 0:9b334a45a8ff | 716 | * undefined in that case. |
bogdanm | 0:9b334a45a8ff | 717 | * |
bogdanm | 0:9b334a45a8ff | 718 | * @param[in] pcnt |
bogdanm | 0:9b334a45a8ff | 719 | * Pointer to PCNT peripheral register block. |
bogdanm | 0:9b334a45a8ff | 720 | * |
bogdanm | 0:9b334a45a8ff | 721 | * @param[in] val |
bogdanm | 0:9b334a45a8ff | 722 | * Value to set in top register. |
bogdanm | 0:9b334a45a8ff | 723 | ******************************************************************************/ |
bogdanm | 0:9b334a45a8ff | 724 | void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val) |
bogdanm | 0:9b334a45a8ff | 725 | { |
bogdanm | 0:9b334a45a8ff | 726 | EFM_ASSERT(PCNT_REF_VALID(pcnt)); |
bogdanm | 0:9b334a45a8ff | 727 | |
bogdanm | 0:9b334a45a8ff | 728 | #ifdef PCNT0 |
bogdanm | 0:9b334a45a8ff | 729 | if (PCNT0 == pcnt) |
bogdanm | 0:9b334a45a8ff | 730 | { |
bogdanm | 0:9b334a45a8ff | 731 | EFM_ASSERT((1<<PCNT0_CNT_SIZE) > val); |
bogdanm | 0:9b334a45a8ff | 732 | } |
bogdanm | 0:9b334a45a8ff | 733 | #endif |
bogdanm | 0:9b334a45a8ff | 734 | |
bogdanm | 0:9b334a45a8ff | 735 | #ifdef PCNT1 |
bogdanm | 0:9b334a45a8ff | 736 | if (PCNT1 == pcnt) |
bogdanm | 0:9b334a45a8ff | 737 | { |
bogdanm | 0:9b334a45a8ff | 738 | EFM_ASSERT((1<<PCNT1_CNT_SIZE) > val); |
bogdanm | 0:9b334a45a8ff | 739 | } |
bogdanm | 0:9b334a45a8ff | 740 | #endif |
bogdanm | 0:9b334a45a8ff | 741 | |
bogdanm | 0:9b334a45a8ff | 742 | #ifdef PCNT2 |
bogdanm | 0:9b334a45a8ff | 743 | if (PCNT2 == pcnt) |
bogdanm | 0:9b334a45a8ff | 744 | { |
bogdanm | 0:9b334a45a8ff | 745 | EFM_ASSERT((1<<PCNT2_CNT_SIZE) > val); |
bogdanm | 0:9b334a45a8ff | 746 | } |
bogdanm | 0:9b334a45a8ff | 747 | #endif |
bogdanm | 0:9b334a45a8ff | 748 | |
bogdanm | 0:9b334a45a8ff | 749 | /* LF register about to be modified require sync. busy check */ |
bogdanm | 0:9b334a45a8ff | 750 | |
bogdanm | 0:9b334a45a8ff | 751 | /* Load into TOPB */ |
bogdanm | 0:9b334a45a8ff | 752 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB); |
bogdanm | 0:9b334a45a8ff | 753 | pcnt->TOPB = val; |
bogdanm | 0:9b334a45a8ff | 754 | |
bogdanm | 0:9b334a45a8ff | 755 | /* Load TOPB value into TOP */ |
bogdanm | 0:9b334a45a8ff | 756 | PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD); |
bogdanm | 0:9b334a45a8ff | 757 | pcnt->CMD = PCNT_CMD_LTOPBIM; |
bogdanm | 0:9b334a45a8ff | 758 | } |
bogdanm | 0:9b334a45a8ff | 759 | |
bogdanm | 0:9b334a45a8ff | 760 | |
bogdanm | 0:9b334a45a8ff | 761 | /** @} (end addtogroup PCNT) */ |
bogdanm | 0:9b334a45a8ff | 762 | /** @} (end addtogroup EM_Library) */ |
bogdanm | 0:9b334a45a8ff | 763 | #endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */ |