blob: adcfb1a3f53da271b48352bbdbbe8ce80e42ac06 [file] [log] [blame]
/***************************************************************************//**
* @file em_timer.c
* @brief Timer/counter (TIMER) Peripheral API
* @version 5.6.0
*******************************************************************************
* # License
* <b>Copyright 2016 Silicon Laboratories, Inc. 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_timer.h"
#if defined(TIMER_COUNT) && (TIMER_COUNT > 0)
#include "em_assert.h"
/***************************************************************************//**
* @addtogroup emlib
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup TIMER
* @brief Timer/Counter (TIMER) Peripheral API
* @details
* The timer module consists of three main parts:
* @li General timer configuration and enable control.
* @li Compare/capture control.
* @li Dead time insertion control (may not be available for all timers).
* @{
******************************************************************************/
/*******************************************************************************
************************** LOCAL FUNCTIONS ********************************
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#if defined(_PRS_CONSUMER_TIMER0_CC0_MASK)
/** Map TIMER reference to index of device. */
#define TIMER_DEVICE_ID(timer) ( \
(timer) == TIMER0 ? 0 \
: (timer) == TIMER1 ? 1 \
: (timer) == TIMER2 ? 2 \
: (timer) == TIMER3 ? 3 \
: -1)
#define TIMER_INPUT_CHANNEL_DTI 3UL
#define TIMER_INPUT_CHANNEL_DTIFS1 4UL
#define TIMER_INPUT_CHANNEL_DTIFS2 5UL
/**
* TIMER PRS registers are moved into the PRS register space on series 2 devices.
* The PRS Consumer registers for a timer consist of 6 registers.
*
* [0] - CC0 PRS Consumer
* [1] - CC1 PRS Consumer
* [2] - CC2 PRS Consumer
* [3] - DTI PRS Consumer
* [4] - DTIFS1 PRS Consumer
* [5] - DTIFS2 PRS Consumer
*/
typedef struct {
__IOM uint32_t CONSUMER_CH[6]; /**< TIMER PRS consumers. */
} PRS_TIMERn_Consumer_TypeDef;
typedef struct {
PRS_TIMERn_Consumer_TypeDef TIMER_CONSUMER[TIMER_COUNT];
} PRS_TIMERn_TypeDef;
/**
* @brief Configure a timer capture/compare channel to use a PRS channel as input.
*
* @param[in] timer
*
* @param[in] cc
* Timer input channel. Valid input is 0-5.
* 0 - CC0
* 1 - CC1
* 2 - CC2
* 3 - DTI
* 4 - DTIFS1
* 5 - DTIFS2
*
* @param[in] prsCh
* PRS channel number.
*
* @param[in] async
* true for asynchronous PRS channel, false for synchronous PRS channel.
*/
static void timerPrsConfig(TIMER_TypeDef * timer, unsigned int cc, unsigned int prsCh, bool async)
{
int i = TIMER_DEVICE_ID(timer);
PRS_TIMERn_TypeDef * base = (PRS_TIMERn_TypeDef *) &PRS->CONSUMER_TIMER0_CC0;
EFM_ASSERT(i != -1);
if (async) {
base->TIMER_CONSUMER[i].CONSUMER_CH[cc] = prsCh << _PRS_CONSUMER_TIMER0_CC0_PRSSEL_SHIFT;
} else {
base->TIMER_CONSUMER[i].CONSUMER_CH[cc] = prsCh << _PRS_CONSUMER_TIMER0_CC0_SPRSSEL_SHIFT;
}
}
#endif
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Initialize TIMER.
*
* @details
* Notice that the counter top must be configured separately with, for instance
* TIMER_TopSet(). In addition, compare/capture and dead-time insertion
* initialization must be initialized separately if used, which should probably
* be done prior to using this function if configuring the TIMER to
* start when initialization is completed.
*
* @param[in] timer
* A pointer to the TIMER peripheral register block.
*
* @param[in] init
* A pointer to the TIMER initialization structure.
******************************************************************************/
void TIMER_Init(TIMER_TypeDef *timer, const TIMER_Init_TypeDef *init)
{
EFM_ASSERT(TIMER_REF_VALID(timer));
uint32_t ctrlRegVal = 0;
#if defined (_TIMER_CFG_PRESC_SHIFT)
timer->EN_CLR = TIMER_EN_EN;
timer->CFG = ((uint32_t)init->prescale << _TIMER_CFG_PRESC_SHIFT)
| ((uint32_t)init->clkSel << _TIMER_CFG_CLKSEL_SHIFT)
| ((uint32_t)init->mode << _TIMER_CFG_MODE_SHIFT)
| (init->debugRun ? TIMER_CFG_DEBUGRUN : 0)
| (init->dmaClrAct ? TIMER_CFG_DMACLRACT : 0)
| (init->quadModeX4 ? TIMER_CFG_QDM_X4 : 0)
| (init->oneShot ? TIMER_CFG_OSMEN : 0)
| (init->sync ? TIMER_CFG_SYNC : 0)
| (init->ati ? TIMER_CFG_ATI : 0);
timer->EN_SET = TIMER_EN_EN;
#endif
/* Stop the timer if specified to be disabled (doesn't hurt if already stopped). */
if (!(init->enable)) {
timer->CMD = TIMER_CMD_STOP;
}
/* Reset the counter. */
timer->CNT = _TIMER_CNT_RESETVALUE;
#if defined(_SILICON_LABS_32B_SERIES_2)
ctrlRegVal = ((uint32_t)init->fallAction << _TIMER_CTRL_FALLA_SHIFT)
| ((uint32_t)init->riseAction << _TIMER_CTRL_RISEA_SHIFT)
| (init->count2x ? TIMER_CTRL_X2CNT : 0);
#else
ctrlRegVal = ((uint32_t)init->prescale << _TIMER_CTRL_PRESC_SHIFT)
| ((uint32_t)init->clkSel << _TIMER_CTRL_CLKSEL_SHIFT)
| ((uint32_t)init->fallAction << _TIMER_CTRL_FALLA_SHIFT)
| ((uint32_t)init->riseAction << _TIMER_CTRL_RISEA_SHIFT)
| ((uint32_t)init->mode << _TIMER_CTRL_MODE_SHIFT)
| (init->debugRun ? TIMER_CTRL_DEBUGRUN : 0)
| (init->dmaClrAct ? TIMER_CTRL_DMACLRACT : 0)
| (init->quadModeX4 ? TIMER_CTRL_QDM_X4 : 0)
| (init->oneShot ? TIMER_CTRL_OSMEN : 0)
| (init->sync ? TIMER_CTRL_SYNC : 0);
#if defined(TIMER_CTRL_X2CNT) && defined(TIMER_CTRL_ATI)
ctrlRegVal |= (init->count2x ? TIMER_CTRL_X2CNT : 0)
| (init->ati ? TIMER_CTRL_ATI : 0);
#endif
#endif
timer->CTRL = ctrlRegVal;
/* Start the timer if specified to be enabled (doesn't hurt if already started). */
if (init->enable) {
timer->CMD = TIMER_CMD_START;
}
}
/***************************************************************************//**
* @brief
* Initialize the TIMER compare/capture channel.
*
* @details
* Notice that if operating the channel in compare mode, the CCV and CCVB register
* must be set separately, as required.
*
* @param[in] timer
* A pointer to the TIMER peripheral register block.
*
* @param[in] ch
* A compare/capture channel to initialize for.
*
* @param[in] init
* A pointer to the TIMER initialization structure.
******************************************************************************/
void TIMER_InitCC(TIMER_TypeDef *timer,
unsigned int ch,
const TIMER_InitCC_TypeDef *init)
{
EFM_ASSERT(TIMER_REF_VALID(timer));
EFM_ASSERT(TIMER_CH_VALID(ch));
#if defined (_TIMER_CC_CFG_MASK)
timer->EN_CLR = TIMER_EN_EN;
timer->CC[ch].CFG =
((uint32_t)init->mode << _TIMER_CC_CFG_MODE_SHIFT)
| (init->filter ? TIMER_CC_CFG_FILT_ENABLE : 0)
| (init->coist ? TIMER_CC_CFG_COIST : 0)
| ((uint32_t)init->prsOutput << _TIMER_CC_CFG_PRSCONF_SHIFT);
if (init->prsInput) {
timer->CC[ch].CFG |= (uint32_t)init->prsInputType << _TIMER_CC_CFG_INSEL_SHIFT;
bool async = (init->prsInputType != timerPrsInputSync);
timerPrsConfig(timer, ch, init->prsSel, async);
} else {
timer->CC[ch].CFG |= TIMER_CC_CFG_INSEL_PIN;
}
timer->EN_SET = TIMER_EN_EN;
timer->CC[ch].CTRL =
((uint32_t)init->eventCtrl << _TIMER_CC_CTRL_ICEVCTRL_SHIFT)
| ((uint32_t)init->edge << _TIMER_CC_CTRL_ICEDGE_SHIFT)
| ((uint32_t)init->cufoa << _TIMER_CC_CTRL_CUFOA_SHIFT)
| ((uint32_t)init->cofoa << _TIMER_CC_CTRL_COFOA_SHIFT)
| ((uint32_t)init->cmoa << _TIMER_CC_CTRL_CMOA_SHIFT)
| (init->outInvert ? TIMER_CC_CTRL_OUTINV : 0);
#else
timer->CC[ch].CTRL =
((uint32_t)init->eventCtrl << _TIMER_CC_CTRL_ICEVCTRL_SHIFT)
| ((uint32_t)init->edge << _TIMER_CC_CTRL_ICEDGE_SHIFT)
| ((uint32_t)init->prsSel << _TIMER_CC_CTRL_PRSSEL_SHIFT)
| ((uint32_t)init->cufoa << _TIMER_CC_CTRL_CUFOA_SHIFT)
| ((uint32_t)init->cofoa << _TIMER_CC_CTRL_COFOA_SHIFT)
| ((uint32_t)init->cmoa << _TIMER_CC_CTRL_CMOA_SHIFT)
| ((uint32_t)init->mode << _TIMER_CC_CTRL_MODE_SHIFT)
| (init->filter ? TIMER_CC_CTRL_FILT_ENABLE : 0)
| (init->prsInput ? TIMER_CC_CTRL_INSEL_PRS : 0)
| (init->coist ? TIMER_CC_CTRL_COIST : 0)
| (init->outInvert ? TIMER_CC_CTRL_OUTINV : 0)
#if defined(_TIMER_CC_CTRL_PRSCONF_MASK)
| ((uint32_t)init->prsOutput << _TIMER_CC_CTRL_PRSCONF_SHIFT)
#endif
;
#endif
}
#if defined(_TIMER_DTCTRL_MASK)
/***************************************************************************//**
* @brief
* Initialize the TIMER DTI unit.
*
* @param[in] timer
* A pointer to the TIMER peripheral register block.
*
* @param[in] init
* A pointer to the TIMER DTI initialization structure.
******************************************************************************/
void TIMER_InitDTI(TIMER_TypeDef *timer, const TIMER_InitDTI_TypeDef *init)
{
EFM_ASSERT(TIMER0 == timer);
/* Make sure the DTI unit is disabled while initializing. */
TIMER_EnableDTI(timer, false);
#if defined (_TIMER_DTCFG_MASK)
timer->EN_CLR = TIMER_EN_EN;
timer->DTCFG = (init->autoRestart ? TIMER_DTCFG_DTDAS : 0)
| (init->enablePrsSource ? TIMER_DTCFG_DTPRSEN : 0);
if (init->enablePrsSource) {
timerPrsConfig(timer, TIMER_INPUT_CHANNEL_DTI, init->prsSel, true);
}
timer->DTTIMECFG =
((uint32_t)init->prescale << _TIMER_DTTIMECFG_DTPRESC_SHIFT)
| ((uint32_t)init->riseTime << _TIMER_DTTIMECFG_DTRISET_SHIFT)
| ((uint32_t)init->fallTime << _TIMER_DTTIMECFG_DTFALLT_SHIFT);
timer->DTFCFG =
(init->enableFaultSourceCoreLockup ? TIMER_DTFCFG_DTLOCKUPFEN : 0)
| (init->enableFaultSourceDebugger ? TIMER_DTFCFG_DTDBGFEN : 0)
| (init->enableFaultSourcePrsSel0 ? TIMER_DTFCFG_DTPRS0FEN : 0)
| (init->enableFaultSourcePrsSel1 ? TIMER_DTFCFG_DTPRS1FEN : 0)
| ((uint32_t)(init->faultAction) << _TIMER_DTFCFG_DTFA_SHIFT);
if (init->enableFaultSourcePrsSel0) {
timerPrsConfig(timer, TIMER_INPUT_CHANNEL_DTIFS1, init->faultSourcePrsSel0, true);
}
if (init->enableFaultSourcePrsSel1) {
timerPrsConfig(timer, TIMER_INPUT_CHANNEL_DTIFS2, init->faultSourcePrsSel1, true);
}
timer->EN_SET = TIMER_EN_EN;
#endif
#if defined(TIMER_DTCTRL_DTDAS)
/* Set up the DTCTRL register.
The enable bit will be set at the end of the function if specified. */
timer->DTCTRL =
(init->autoRestart ? TIMER_DTCTRL_DTDAS : 0)
| (init->activeLowOut ? TIMER_DTCTRL_DTIPOL : 0)
| (init->invertComplementaryOut ? TIMER_DTCTRL_DTCINV : 0)
| (init->enablePrsSource ? TIMER_DTCTRL_DTPRSEN : 0)
| ((uint32_t)(init->prsSel) << _TIMER_DTCTRL_DTPRSSEL_SHIFT);
#endif
#if defined (TIMER_DTCFG_DTDAS)
timer->DTCTRL = (init->activeLowOut ? TIMER_DTCTRL_DTIPOL : 0)
| (init->invertComplementaryOut ? TIMER_DTCTRL_DTCINV : 0);
#endif
#if defined (_TIMER_DTTIME_DTPRESC_SHIFT)
/* Set up the DTTIME register. */
timer->DTTIME = ((uint32_t)init->prescale << _TIMER_DTTIME_DTPRESC_SHIFT)
| ((uint32_t)init->riseTime << _TIMER_DTTIME_DTRISET_SHIFT)
| ((uint32_t)init->fallTime << _TIMER_DTTIME_DTFALLT_SHIFT);
#endif
#if defined (TIMER_DTFC_DTLOCKUPFEN)
/* Set up the DTFC register. */
timer->DTFC =
(init->enableFaultSourceCoreLockup ? TIMER_DTFC_DTLOCKUPFEN : 0)
| (init->enableFaultSourceDebugger ? TIMER_DTFC_DTDBGFEN : 0)
| (init->enableFaultSourcePrsSel0 ? TIMER_DTFC_DTPRS0FEN : 0)
| (init->enableFaultSourcePrsSel1 ? TIMER_DTFC_DTPRS1FEN : 0)
| ((uint32_t)init->faultAction << _TIMER_DTFC_DTFA_SHIFT)
| ((uint32_t)init->faultSourcePrsSel0 << _TIMER_DTFC_DTPRS0FSEL_SHIFT)
| ((uint32_t)init->faultSourcePrsSel1 << _TIMER_DTFC_DTPRS1FSEL_SHIFT);
#endif
/* Set up the DTOGEN register. */
timer->DTOGEN = init->outputsEnableMask;
/* Clear any previous DTI faults. */
TIMER_ClearDTIFault(timer, TIMER_GetDTIFault(timer));
/* Enable/disable before returning. */
TIMER_EnableDTI(timer, init->enable);
}
#endif
/***************************************************************************//**
* @brief
* Reset the TIMER to the same state that it was in after a hardware reset.
*
* @note
* The ROUTE register is NOT reset by this function to allow for
* a centralized setup of this feature.
*
* @param[in] timer
* A pointer to the TIMER peripheral register block.
******************************************************************************/
void TIMER_Reset(TIMER_TypeDef *timer)
{
int i;
EFM_ASSERT(TIMER_REF_VALID(timer));
/* Make sure disabled first, before resetting other registers. */
timer->CMD = TIMER_CMD_STOP;
timer->CTRL = _TIMER_CTRL_RESETVALUE;
timer->IEN = _TIMER_IEN_RESETVALUE;
#if defined (TIMER_HAS_SET_CLEAR)
timer->IF_CLR = _TIMER_IF_MASK;
#else
timer->IFC = _TIMER_IFC_MASK;
#endif
timer->TOPB = _TIMER_TOPB_RESETVALUE;
/* Write TOP after TOPB to invalidate TOPB (clear TIMER_STATUS_TOPBV). */
timer->TOP = _TIMER_TOP_RESETVALUE;
timer->CNT = _TIMER_CNT_RESETVALUE;
/* Do not reset the route register, setting should be done independently. */
/* Note: The ROUTE register may be locked by the DTLOCK register. */
for (i = 0; TIMER_CH_VALID(i); i++) {
timer->CC[i].CTRL = _TIMER_CC_CTRL_RESETVALUE;
#if defined (_TIMER_CC_CCV_RESETVALUE) && defined (_TIMER_CC_CCVB_RESETVALUE)
timer->CC[i].CCV = _TIMER_CC_CCV_RESETVALUE;
timer->CC[i].CCVB = _TIMER_CC_CCVB_RESETVALUE;
#endif
#if defined (_TIMER_CC_OC_RESETVALUE) && defined (_TIMER_CC_OCB_RESETVALUE) \
&& defined (_TIMER_CC_ICF_RESETVALUE) && defined (_TIMER_CC_ICOF_RESETVALUE)
timer->CC[i].OC = _TIMER_CC_OC_RESETVALUE;
timer->CC[i].OCB = _TIMER_CC_OCB_RESETVALUE;
#endif
}
/* Reset dead time insertion module, which has no effect on timers without DTI. */
#if defined(_TIMER_DTCFG_MASK)
timer->DTLOCK = TIMER_DTLOCK_DTILOCKKEY_UNLOCK;
timer->DTCTRL = _TIMER_DTCTRL_RESETVALUE;
timer->DTOGEN = _TIMER_DTOGEN_RESETVALUE;
timer->DTFAULTC = _TIMER_DTFAULTC_MASK;
#elif defined(TIMER_DTLOCK_LOCKKEY_UNLOCK)
/* Unlock DTI registers first if locked. */
timer->DTLOCK = TIMER_DTLOCK_LOCKKEY_UNLOCK;
timer->DTCTRL = _TIMER_DTCTRL_RESETVALUE;
timer->DTTIME = _TIMER_DTTIME_RESETVALUE;
timer->DTFC = _TIMER_DTFC_RESETVALUE;
timer->DTOGEN = _TIMER_DTOGEN_RESETVALUE;
timer->DTFAULTC = _TIMER_DTFAULTC_MASK;
#endif
#if defined(_TIMER_CFG_MASK)
/* CFG registers must be reset after the timer is disabled */
timer->EN_CLR = TIMER_EN_EN;
timer->CFG = _TIMER_CFG_RESETVALUE;
for (i = 0; TIMER_CH_VALID(i); i++) {
timer->CC[i].CFG = _TIMER_CC_CFG_RESETVALUE;
}
timer->DTCFG = _TIMER_DTCFG_RESETVALUE;
timer->DTFCFG = _TIMER_DTFCFG_RESETVALUE;
timer->DTTIMECFG = _TIMER_DTTIMECFG_RESETVALUE;
#endif
}
/** @} (end addtogroup TIMER) */
/** @} (end addtogroup emlib) */
#endif /* defined(TIMER_COUNT) && (TIMER_COUNT > 0) */