added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
Diff: targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_letimer.c
- Revision:
- 144:ef7eb2e8f9f7
- Parent:
- 50:a417edff4437
diff -r 423e1876dc07 -r ef7eb2e8f9f7 targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_letimer.c --- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_letimer.c Tue Aug 02 14:07:36 2016 +0000 +++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_letimer.c Fri Sep 02 15:07:44 2016 +0100 @@ -1,544 +1,544 @@ -/***************************************************************************//** - * @file em_letimer.c - * @brief Low Energy Timer (LETIMER) Peripheral API - * @version 4.2.1 - ******************************************************************************* - * @section License - * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b> - ******************************************************************************* - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no - * obligation to support this Software. Silicon Labs is providing the - * Software "AS IS", with no express or implied warranties of any kind, - * including, but not limited to, any implied warranties of merchantability - * or fitness for any particular purpose or warranties against infringement - * of any proprietary rights of a third party. - * - * Silicon Labs will not be liable for any consequential, incidental, or - * special damages, or any other relief, or for any claim by any third party, - * arising from your use of this Software. - * - ******************************************************************************/ - -#include "em_letimer.h" -#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) -#include "em_cmu.h" -#include "em_assert.h" - -/***************************************************************************//** - * @addtogroup EM_Library - * @{ - ******************************************************************************/ - -/***************************************************************************//** - * @addtogroup LETIMER - * @brief Low Energy Timer (LETIMER) Peripheral API - * @{ - ******************************************************************************/ - -/******************************************************************************* - ******************************* DEFINES *********************************** - ******************************************************************************/ - -/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ - -/** Validation of valid comparator register for assert statements. */ -#define LETIMER_COMP_REG_VALID(reg) (((reg) <= 1)) - -/** Validation of LETIMER register block pointer reference for assert statements. */ -#define LETIMER_REF_VALID(ref) ((ref) == LETIMER0) - -/** Validation of valid repeat counter register for assert statements. */ -#define LETIMER_REP_REG_VALID(reg) (((reg) <= 1)) - -/** @endcond */ - - -/******************************************************************************* - ************************** LOCAL FUNCTIONS ******************************** - ******************************************************************************/ - -/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ - -#if defined(_EFM32_GECKO_FAMILY) -/***************************************************************************//** - * @brief - * Wait for ongoing sync of register(s) to low frequency domain to complete. - * - * @note - * This only applies to the Gecko Family, see the reference manual - * chapter about Access to Low Energy Peripherals (Asynchronos Registers) - * for details. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block - * - * @param[in] mask - * Bitmask corresponding to SYNCBUSY register defined bits, indicating - * registers that must complete any ongoing synchronization. - ******************************************************************************/ -__STATIC_INLINE void regSync(LETIMER_TypeDef *letimer, uint32_t mask) -{ -#if defined(_LETIMER_FREEZE_MASK) - /* Avoid deadlock if modifying the same register twice when freeze mode is */ - /* activated. */ - if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE) - return; -#endif - - /* Wait for any pending previous write operation to have been completed */ - /* in low frequency domain, only required for Gecko Family of devices */ - while (letimer->SYNCBUSY & mask) - ; -} -#endif - -/** @endcond */ - -/******************************************************************************* - ************************** GLOBAL FUNCTIONS ******************************* - ******************************************************************************/ - -/***************************************************************************//** - * @brief - * Get LETIMER compare register value. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block - * - * @param[in] comp - * Compare register to get, either 0 or 1 - * - * @return - * Compare register value, 0 if invalid register selected. - ******************************************************************************/ -uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp) -{ - uint32_t ret; - - EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_COMP_REG_VALID(comp)); - - /* Initialize selected compare value */ - switch (comp) - { - case 0: - ret = letimer->COMP0; - break; - - case 1: - ret = letimer->COMP1; - break; - - default: - /* Unknown compare register selected */ - ret = 0; - break; - } - - return(ret); -} - - -/***************************************************************************//** - * @brief - * Set LETIMER compare register value. - * - * @note - * The setting of a compare register requires synchronization into the - * low frequency domain. If the same register is modified before a previous - * update has completed, this function will stall until the previous - * synchronization has completed. This only applies to the Gecko Family, see - * comment in the LETIMER_Sync() internal function call. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block - * - * @param[in] comp - * Compare register to set, either 0 or 1 - * - * @param[in] value - * Initialization value (<= 0x0000ffff) - ******************************************************************************/ -void LETIMER_CompareSet(LETIMER_TypeDef *letimer, - unsigned int comp, - uint32_t value) -{ - volatile uint32_t *compReg; - - EFM_ASSERT(LETIMER_REF_VALID(letimer) - && LETIMER_COMP_REG_VALID(comp) - && ((value & ~(_LETIMER_COMP0_COMP0_MASK - >> _LETIMER_COMP0_COMP0_SHIFT)) - == 0)); - - /* Initialize selected compare value */ - switch (comp) - { - case 0: - compReg = &(letimer->COMP0); - break; - - case 1: - compReg = &(letimer->COMP1); - break; - - default: - /* Unknown compare register selected, abort */ - return; - } - -#if defined(_EFM32_GECKO_FAMILY) - /* LF register about to be modified require sync. busy check */ - regSync(letimer, comp ? LETIMER_SYNCBUSY_COMP1 : LETIMER_SYNCBUSY_COMP0); -#endif - - *compReg = value; -} - - -/***************************************************************************//** - * @brief - * Start/stop LETIMER. - * - * @note - * The enabling/disabling of the LETIMER modifies the LETIMER CMD register - * which requires synchronization into the low frequency domain. If this - * register is modified before a previous update to the same register has - * completed, this function will stall until the previous synchronization has - * completed. This only applies to the Gecko Family, see comment in the - * LETIMER_Sync() internal function call. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block. - * - * @param[in] enable - * true to enable counting, false to disable. - ******************************************************************************/ -void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable) -{ - EFM_ASSERT(LETIMER_REF_VALID(letimer)); - -#if defined(_EFM32_GECKO_FAMILY) - /* LF register about to be modified require sync. busy check */ - regSync(letimer, LETIMER_SYNCBUSY_CMD); -#endif - - if (enable) - { - letimer->CMD = LETIMER_CMD_START; - } - else - { - letimer->CMD = LETIMER_CMD_STOP; - } -} - -#if defined(_LETIMER_FREEZE_MASK) -/***************************************************************************//** - * @brief - * LETIMER register synchronization freeze control. - * - * @details - * Some LETIMER registers require synchronization into the low frequency (LF) - * domain. The freeze feature allows for several such registers to be - * modified before passing them to the LF domain simultaneously (which - * takes place when the freeze mode is disabled). - * - * @note - * When enabling freeze mode, this function will wait for all current - * ongoing LETIMER synchronization to LF domain to complete (Normally - * synchronization will not be in progress.) However for this reason, when - * using freeze mode, modifications of registers requiring LF synchronization - * should be done within one freeze enable/disable block to avoid unecessary - * stalling. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block. - * - * @param[in] enable - * @li true - enable freeze, modified registers are not propagated to the - * LF domain - * @li false - disables freeze, modified registers are propagated to LF - * domain - ******************************************************************************/ -void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable) -{ - if (enable) - { - /* - * Wait for any ongoing LF synchronization to complete. This is just to - * protect against the rare case when a user - * - modifies a register requiring LF sync - * - then enables freeze before LF sync completed - * - then modifies the same register again - * since modifying a register while it is in sync progress should be - * avoided. - */ - while (letimer->SYNCBUSY) - ; - - letimer->FREEZE = LETIMER_FREEZE_REGFREEZE; - } - else - { - letimer->FREEZE = 0; - } -} -#endif /* defined(_LETIMER_FREEZE_MASK) */ - -/***************************************************************************//** - * @brief - * Initialize LETIMER. - * - * @details - * Note that the compare/repeat values must be set separately with - * LETIMER_CompareSet() and LETIMER_RepeatSet(). That should probably be done - * prior to the use of this function if configuring the LETIMER to start when - * initialization is completed. - * - * @note - * The initialization of the LETIMER modifies the LETIMER CTRL/CMD registers - * which require synchronization into the low frequency domain. If any of those - * registers are modified before a previous update to the same register has - * completed, this function will stall until the previous synchronization has - * completed. This only applies to the Gecko Family, see comment in the - * LETIMER_Sync() internal function call. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block. - * - * @param[in] init - * Pointer to LETIMER initialization structure. - ******************************************************************************/ -void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init) -{ - uint32_t tmp = 0; - - EFM_ASSERT(LETIMER_REF_VALID(letimer)); - - /* Stop timer if specified to be disabled and running */ - if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING)) - { -#if defined(_EFM32_GECKO_FAMILY) - /* LF register about to be modified require sync. busy check */ - regSync(letimer, LETIMER_SYNCBUSY_CMD); -#endif - letimer->CMD = LETIMER_CMD_STOP; - } - - /* Configure DEBUGRUN flag, sets whether or not counter should be - * updated when debugger is active */ - if (init->debugRun) - { - tmp |= LETIMER_CTRL_DEBUGRUN; - } - -#if defined(LETIMER_CTRL_RTCC0TEN) - if (init->rtcComp0Enable) - { - tmp |= LETIMER_CTRL_RTCC0TEN; - } - - if (init->rtcComp1Enable) - { - tmp |= LETIMER_CTRL_RTCC1TEN; - } -#endif - - if (init->comp0Top) - { - tmp |= LETIMER_CTRL_COMP0TOP; - } - - if (init->bufTop) - { - tmp |= LETIMER_CTRL_BUFTOP; - } - - if (init->out0Pol) - { - tmp |= LETIMER_CTRL_OPOL0; - } - - if (init->out1Pol) - { - tmp |= LETIMER_CTRL_OPOL1; - } - - tmp |= init->ufoa0 << _LETIMER_CTRL_UFOA0_SHIFT; - tmp |= init->ufoa1 << _LETIMER_CTRL_UFOA1_SHIFT; - tmp |= init->repMode << _LETIMER_CTRL_REPMODE_SHIFT; - -#if defined(_EFM32_GECKO_FAMILY) - /* LF register about to be modified require sync. busy check */ - regSync(letimer, LETIMER_SYNCBUSY_CTRL); -#endif - letimer->CTRL = tmp; - - /* Start timer if specified to be enabled and not already running */ - if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING)) - { -#if defined(_EFM32_GECKO_FAMILY) - /* LF register about to be modified require sync. busy check */ - regSync(letimer, LETIMER_SYNCBUSY_CMD); -#endif - letimer->CMD = LETIMER_CMD_START; - } -} - - -/***************************************************************************//** - * @brief - * Get LETIMER repeat register value. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block - * - * @param[in] rep - * Repeat register to get, either 0 or 1 - * - * @return - * Repeat register value, 0 if invalid register selected. - ******************************************************************************/ -uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep) -{ - uint32_t ret; - - EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_REP_REG_VALID(rep)); - - /* Initialize selected compare value */ - switch (rep) - { - case 0: - ret = letimer->REP0; - break; - - case 1: - ret = letimer->REP1; - break; - - default: - /* Unknown compare register selected */ - ret = 0; - break; - } - - return(ret); -} - - -/***************************************************************************//** - * @brief - * Set LETIMER repeat counter register value. - * - * @note - * The setting of a repeat counter register requires synchronization into the - * low frequency domain. If the same register is modified before a previous - * update has completed, this function will stall until the previous - * synchronization has completed. This only applies to the Gecko Family, see - * comment in the LETIMER_Sync() internal function call. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block - * - * @param[in] rep - * Repeat counter register to set, either 0 or 1 - * - * @param[in] value - * Initialization value (<= 0x0000ffff) - ******************************************************************************/ -void LETIMER_RepeatSet(LETIMER_TypeDef *letimer, - unsigned int rep, - uint32_t value) -{ - volatile uint32_t *repReg; -#if defined(_EFM32_GECKO_FAMILY) - uint32_t syncbusy; -#endif - EFM_ASSERT(LETIMER_REF_VALID(letimer) - && LETIMER_REP_REG_VALID(rep) - && ((value & ~(_LETIMER_REP0_REP0_MASK - >> _LETIMER_REP0_REP0_SHIFT)) - == 0)); - - /* Initialize selected compare value */ - switch (rep) - { - case 0: - repReg = &(letimer->REP0); -#if defined(_EFM32_GECKO_FAMILY) - syncbusy = LETIMER_SYNCBUSY_REP0; -#endif - break; - - case 1: - repReg = &(letimer->REP1); -#if defined(_EFM32_GECKO_FAMILY) - syncbusy = LETIMER_SYNCBUSY_REP1; -#endif - break; - - default: - /* Unknown compare register selected, abort */ - return; - } - -#if defined(_EFM32_GECKO_FAMILY) - /* LF register about to be modified require sync. busy check */ - regSync(letimer, syncbusy); -#endif - - *repReg = value; -} - - -/***************************************************************************//** - * @brief - * Reset LETIMER to same state as after a HW reset. - * - * @note - * The ROUTE register is NOT reset by this function, in order to allow for - * centralized setup of this feature. - * - * @param[in] letimer - * Pointer to LETIMER peripheral register block. - ******************************************************************************/ -void LETIMER_Reset(LETIMER_TypeDef *letimer) -{ -#if defined(_LETIMER_FREEZE_MASK) - /* Freeze registers to avoid stalling for LF synchronization */ - LETIMER_FreezeEnable(letimer, true); -#endif - - /* Make sure disabled first, before resetting other registers */ - letimer->CMD = LETIMER_CMD_STOP | LETIMER_CMD_CLEAR - | LETIMER_CMD_CTO0 | LETIMER_CMD_CTO1; - letimer->CTRL = _LETIMER_CTRL_RESETVALUE; - letimer->COMP0 = _LETIMER_COMP0_RESETVALUE; - letimer->COMP1 = _LETIMER_COMP1_RESETVALUE; - letimer->REP0 = _LETIMER_REP0_RESETVALUE; - letimer->REP1 = _LETIMER_REP1_RESETVALUE; - letimer->IEN = _LETIMER_IEN_RESETVALUE; - letimer->IFC = _LETIMER_IFC_MASK; - /* Do not reset route register, setting should be done independently */ - -#if defined(_LETIMER_FREEZE_MASK) - /* Unfreeze registers, pass new settings on to LETIMER */ - LETIMER_FreezeEnable(letimer, false); -#endif -} - - -/** @} (end addtogroup LETIMER) */ -/** @} (end addtogroup EM_Library) */ -#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */ +/***************************************************************************//** + * @file em_letimer.c + * @brief Low Energy Timer (LETIMER) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_letimer.h" +#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) +#include "em_cmu.h" +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LETIMER + * @brief Low Energy Timer (LETIMER) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of valid comparator register for assert statements. */ +#define LETIMER_COMP_REG_VALID(reg) (((reg) <= 1)) + +/** Validation of LETIMER register block pointer reference for assert statements. */ +#define LETIMER_REF_VALID(ref) ((ref) == LETIMER0) + +/** Validation of valid repeat counter register for assert statements. */ +#define LETIMER_REP_REG_VALID(reg) (((reg) <= 1)) + +/** @endcond */ + + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#if defined(_EFM32_GECKO_FAMILY) +/***************************************************************************//** + * @brief + * Wait for ongoing sync of register(s) to low frequency domain to complete. + * + * @note + * This only applies to the Gecko Family, see the reference manual + * chapter about Access to Low Energy Peripherals (Asynchronos Registers) + * for details. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] mask + * Bitmask corresponding to SYNCBUSY register defined bits, indicating + * registers that must complete any ongoing synchronization. + ******************************************************************************/ +__STATIC_INLINE void regSync(LETIMER_TypeDef *letimer, uint32_t mask) +{ +#if defined(_LETIMER_FREEZE_MASK) + /* Avoid deadlock if modifying the same register twice when freeze mode is */ + /* activated. */ + if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE) + return; +#endif + + /* Wait for any pending previous write operation to have been completed */ + /* in low frequency domain, only required for Gecko Family of devices */ + while (letimer->SYNCBUSY & mask) + ; +} +#endif + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get LETIMER compare register value. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] comp + * Compare register to get, either 0 or 1 + * + * @return + * Compare register value, 0 if invalid register selected. + ******************************************************************************/ +uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp) +{ + uint32_t ret; + + EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_COMP_REG_VALID(comp)); + + /* Initialize selected compare value */ + switch (comp) + { + case 0: + ret = letimer->COMP0; + break; + + case 1: + ret = letimer->COMP1; + break; + + default: + /* Unknown compare register selected */ + ret = 0; + break; + } + + return(ret); +} + + +/***************************************************************************//** + * @brief + * Set LETIMER compare register value. + * + * @note + * The setting of a compare register requires synchronization into the + * low frequency domain. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. This only applies to the Gecko Family, see + * comment in the LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] comp + * Compare register to set, either 0 or 1 + * + * @param[in] value + * Initialization value (<= 0x0000ffff) + ******************************************************************************/ +void LETIMER_CompareSet(LETIMER_TypeDef *letimer, + unsigned int comp, + uint32_t value) +{ + volatile uint32_t *compReg; + + EFM_ASSERT(LETIMER_REF_VALID(letimer) + && LETIMER_COMP_REG_VALID(comp) + && ((value & ~(_LETIMER_COMP0_COMP0_MASK + >> _LETIMER_COMP0_COMP0_SHIFT)) + == 0)); + + /* Initialize selected compare value */ + switch (comp) + { + case 0: + compReg = &(letimer->COMP0); + break; + + case 1: + compReg = &(letimer->COMP1); + break; + + default: + /* Unknown compare register selected, abort */ + return; + } + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, comp ? LETIMER_SYNCBUSY_COMP1 : LETIMER_SYNCBUSY_COMP0); +#endif + + *compReg = value; +} + + +/***************************************************************************//** + * @brief + * Start/stop LETIMER. + * + * @note + * The enabling/disabling of the LETIMER modifies the LETIMER CMD register + * which requires synchronization into the low frequency domain. If this + * register is modified before a previous update to the same register has + * completed, this function will stall until the previous synchronization has + * completed. This only applies to the Gecko Family, see comment in the + * LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] enable + * true to enable counting, false to disable. + ******************************************************************************/ +void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable) +{ + EFM_ASSERT(LETIMER_REF_VALID(letimer)); + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CMD); +#endif + + if (enable) + { + letimer->CMD = LETIMER_CMD_START; + } + else + { + letimer->CMD = LETIMER_CMD_STOP; + } +} + +#if defined(_LETIMER_FREEZE_MASK) +/***************************************************************************//** + * @brief + * LETIMER register synchronization freeze control. + * + * @details + * Some LETIMER registers require synchronization into the low frequency (LF) + * domain. The freeze feature allows for several such registers to be + * modified before passing them to the LF domain simultaneously (which + * takes place when the freeze mode is disabled). + * + * @note + * When enabling freeze mode, this function will wait for all current + * ongoing LETIMER synchronization to LF domain to complete (Normally + * synchronization will not be in progress.) However for this reason, when + * using freeze mode, modifications of registers requiring LF synchronization + * should be done within one freeze enable/disable block to avoid unecessary + * stalling. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] enable + * @li true - enable freeze, modified registers are not propagated to the + * LF domain + * @li false - disables freeze, modified registers are propagated to LF + * domain + ******************************************************************************/ +void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable) +{ + if (enable) + { + /* + * Wait for any ongoing LF synchronization to complete. This is just to + * protect against the rare case when a user + * - modifies a register requiring LF sync + * - then enables freeze before LF sync completed + * - then modifies the same register again + * since modifying a register while it is in sync progress should be + * avoided. + */ + while (letimer->SYNCBUSY) + ; + + letimer->FREEZE = LETIMER_FREEZE_REGFREEZE; + } + else + { + letimer->FREEZE = 0; + } +} +#endif /* defined(_LETIMER_FREEZE_MASK) */ + +/***************************************************************************//** + * @brief + * Initialize LETIMER. + * + * @details + * Note that the compare/repeat values must be set separately with + * LETIMER_CompareSet() and LETIMER_RepeatSet(). That should probably be done + * prior to the use of this function if configuring the LETIMER to start when + * initialization is completed. + * + * @note + * The initialization of the LETIMER modifies the LETIMER CTRL/CMD registers + * which require synchronization into the low frequency domain. If any of those + * registers are modified before a previous update to the same register has + * completed, this function will stall until the previous synchronization has + * completed. This only applies to the Gecko Family, see comment in the + * LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] init + * Pointer to LETIMER initialization structure. + ******************************************************************************/ +void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init) +{ + uint32_t tmp = 0; + + EFM_ASSERT(LETIMER_REF_VALID(letimer)); + + /* Stop timer if specified to be disabled and running */ + if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING)) + { +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CMD); +#endif + letimer->CMD = LETIMER_CMD_STOP; + } + + /* Configure DEBUGRUN flag, sets whether or not counter should be + * updated when debugger is active */ + if (init->debugRun) + { + tmp |= LETIMER_CTRL_DEBUGRUN; + } + +#if defined(LETIMER_CTRL_RTCC0TEN) + if (init->rtcComp0Enable) + { + tmp |= LETIMER_CTRL_RTCC0TEN; + } + + if (init->rtcComp1Enable) + { + tmp |= LETIMER_CTRL_RTCC1TEN; + } +#endif + + if (init->comp0Top) + { + tmp |= LETIMER_CTRL_COMP0TOP; + } + + if (init->bufTop) + { + tmp |= LETIMER_CTRL_BUFTOP; + } + + if (init->out0Pol) + { + tmp |= LETIMER_CTRL_OPOL0; + } + + if (init->out1Pol) + { + tmp |= LETIMER_CTRL_OPOL1; + } + + tmp |= init->ufoa0 << _LETIMER_CTRL_UFOA0_SHIFT; + tmp |= init->ufoa1 << _LETIMER_CTRL_UFOA1_SHIFT; + tmp |= init->repMode << _LETIMER_CTRL_REPMODE_SHIFT; + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CTRL); +#endif + letimer->CTRL = tmp; + + /* Start timer if specified to be enabled and not already running */ + if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING)) + { +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CMD); +#endif + letimer->CMD = LETIMER_CMD_START; + } +} + + +/***************************************************************************//** + * @brief + * Get LETIMER repeat register value. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] rep + * Repeat register to get, either 0 or 1 + * + * @return + * Repeat register value, 0 if invalid register selected. + ******************************************************************************/ +uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep) +{ + uint32_t ret; + + EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_REP_REG_VALID(rep)); + + /* Initialize selected compare value */ + switch (rep) + { + case 0: + ret = letimer->REP0; + break; + + case 1: + ret = letimer->REP1; + break; + + default: + /* Unknown compare register selected */ + ret = 0; + break; + } + + return(ret); +} + + +/***************************************************************************//** + * @brief + * Set LETIMER repeat counter register value. + * + * @note + * The setting of a repeat counter register requires synchronization into the + * low frequency domain. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. This only applies to the Gecko Family, see + * comment in the LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] rep + * Repeat counter register to set, either 0 or 1 + * + * @param[in] value + * Initialization value (<= 0x0000ffff) + ******************************************************************************/ +void LETIMER_RepeatSet(LETIMER_TypeDef *letimer, + unsigned int rep, + uint32_t value) +{ + volatile uint32_t *repReg; +#if defined(_EFM32_GECKO_FAMILY) + uint32_t syncbusy; +#endif + EFM_ASSERT(LETIMER_REF_VALID(letimer) + && LETIMER_REP_REG_VALID(rep) + && ((value & ~(_LETIMER_REP0_REP0_MASK + >> _LETIMER_REP0_REP0_SHIFT)) + == 0)); + + /* Initialize selected compare value */ + switch (rep) + { + case 0: + repReg = &(letimer->REP0); +#if defined(_EFM32_GECKO_FAMILY) + syncbusy = LETIMER_SYNCBUSY_REP0; +#endif + break; + + case 1: + repReg = &(letimer->REP1); +#if defined(_EFM32_GECKO_FAMILY) + syncbusy = LETIMER_SYNCBUSY_REP1; +#endif + break; + + default: + /* Unknown compare register selected, abort */ + return; + } + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, syncbusy); +#endif + + *repReg = value; +} + + +/***************************************************************************//** + * @brief + * Reset LETIMER to same state as after a HW reset. + * + * @note + * The ROUTE register is NOT reset by this function, in order to allow for + * centralized setup of this feature. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + ******************************************************************************/ +void LETIMER_Reset(LETIMER_TypeDef *letimer) +{ +#if defined(_LETIMER_FREEZE_MASK) + /* Freeze registers to avoid stalling for LF synchronization */ + LETIMER_FreezeEnable(letimer, true); +#endif + + /* Make sure disabled first, before resetting other registers */ + letimer->CMD = LETIMER_CMD_STOP | LETIMER_CMD_CLEAR + | LETIMER_CMD_CTO0 | LETIMER_CMD_CTO1; + letimer->CTRL = _LETIMER_CTRL_RESETVALUE; + letimer->COMP0 = _LETIMER_COMP0_RESETVALUE; + letimer->COMP1 = _LETIMER_COMP1_RESETVALUE; + letimer->REP0 = _LETIMER_REP0_RESETVALUE; + letimer->REP1 = _LETIMER_REP1_RESETVALUE; + letimer->IEN = _LETIMER_IEN_RESETVALUE; + letimer->IFC = _LETIMER_IFC_MASK; + /* Do not reset route register, setting should be done independently */ + +#if defined(_LETIMER_FREEZE_MASK) + /* Unfreeze registers, pass new settings on to LETIMER */ + LETIMER_FreezeEnable(letimer, false); +#endif +} + + +/** @} (end addtogroup LETIMER) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */