Initial commit of firmware
This commit is contained in:
709
Libs/platform/emlib/src/em_acmp.c
Normal file
709
Libs/platform/emlib/src/em_acmp.c
Normal file
@@ -0,0 +1,709 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Analog Comparator (ACMP) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_acmp.h"
|
||||
#if defined(ACMP_COUNT) && (ACMP_COUNT > 0)
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "em_bus.h"
|
||||
#include "sl_assert.h"
|
||||
#include "em_gpio.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup acmp ACMP - Analog Comparator
|
||||
* @brief Analog comparator (ACMP) Peripheral API
|
||||
*
|
||||
* @details
|
||||
* The Analog Comparator is used to compare voltage of two analog inputs
|
||||
* with a digital output indicating which input voltage is higher. Inputs can
|
||||
* either be one of the selectable internal references or from external pins.
|
||||
* Response time and current consumption can be configured by
|
||||
* altering the current supply to the comparator.
|
||||
*
|
||||
* ACMP is available down to EM3 and is able to wake up the system when
|
||||
* input signals pass a certain threshold. Use @ref ACMP_IntEnable() to enable
|
||||
* an edge interrupt to use this functionality.
|
||||
*
|
||||
* This example shows how to use the em_acmp.h API for comparing an input
|
||||
* pin to an internal 2.5 V reference voltage.
|
||||
*
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* @include em_acmp_compare_s0.c
|
||||
* @endif
|
||||
*
|
||||
* @if DOXYDOC_P2_DEVICE
|
||||
* @include em_acmp_compare_s1.c
|
||||
* @endif
|
||||
*
|
||||
* @if DOXYDOC_S2_DEVICE
|
||||
* @include em_acmp_compare_s2.c
|
||||
* @endif
|
||||
*
|
||||
* @note
|
||||
* ACMP can also be used to compare two separate input pins.
|
||||
*
|
||||
* @details
|
||||
* ACMP also contains specialized hardware for capacitive sensing. This
|
||||
* module contains the @ref ACMP_CapsenseInit() function to initialize
|
||||
* ACMP for capacitive sensing and the @ref ACMP_CapsenseChannelSet() function
|
||||
* to select the current capsense channel.
|
||||
*
|
||||
* For applications that require capacitive sensing it is recommended to use a
|
||||
* library, such as cslib, which is provided by Silicon Labs.
|
||||
*
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** Validation of ACMP register block pointer reference
|
||||
* for assert statements. */
|
||||
#if (ACMP_COUNT == 1)
|
||||
#define ACMP_REF_VALID(ref) ((ref) == ACMP0)
|
||||
#elif (ACMP_COUNT == 2)
|
||||
#define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1))
|
||||
#elif (ACMP_COUNT == 3)
|
||||
#define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1) || ((ref) == ACMP2))
|
||||
#elif (ACMP_COUNT == 4)
|
||||
#define ACMP_REF_VALID(ref) (((ref) == ACMP0) \
|
||||
|| ((ref) == ACMP1) \
|
||||
|| ((ref) == ACMP2) \
|
||||
|| ((ref) == ACMP3))
|
||||
#else
|
||||
#error Undefined number of analog comparators (ACMP).
|
||||
#endif
|
||||
|
||||
/** The maximum value that can be inserted in the route location register
|
||||
* for the specific device. */
|
||||
#if defined(_ACMP_ROUTE_LOCATION_LOC3)
|
||||
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC3
|
||||
#elif defined(_ACMP_ROUTE_LOCATION_LOC2)
|
||||
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC2
|
||||
#elif defined(_ACMP_ROUTE_LOCATION_LOC1)
|
||||
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC1
|
||||
#elif defined(_ACMP_ROUTELOC0_OUTLOC_LOC31)
|
||||
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_LOC31
|
||||
#elif defined(_ACMP_ROUTELOC0_OUTLOC_MASK)
|
||||
#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_MASK
|
||||
#endif
|
||||
|
||||
/** Map ACMP reference to index of device. */
|
||||
#if (ACMP_COUNT == 1)
|
||||
#define ACMP_DEVICE_ID(acmp) ( \
|
||||
(acmp) == ACMP0 ? 0 \
|
||||
: 0)
|
||||
#elif (ACMP_COUNT == 2)
|
||||
#define ACMP_DEVICE_ID(acmp) ( \
|
||||
(acmp) == ACMP0 ? 0 \
|
||||
: (acmp) == ACMP1 ? 1 \
|
||||
: 0)
|
||||
#endif
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set up ACMP for use in capacitive sense applications.
|
||||
*
|
||||
* @details
|
||||
* This function sets up ACMP for use in capacitive sense applications.
|
||||
* To use the capacitive sense functionality in the ACMP, use
|
||||
* the PRS output of the ACMP module to count the number of oscillations
|
||||
* in the capacitive sense circuit (possibly using a TIMER).
|
||||
*
|
||||
* @note
|
||||
* A basic example of capacitive sensing can be found in the STK BSP
|
||||
* (capsense demo).
|
||||
*
|
||||
* @cond DOXYDOC_S2_DEVICE
|
||||
* @note
|
||||
* A call to ACMP_CapsenseInit will enable and disable the ACMP peripheral,
|
||||
* which can cause side effects if it was previously set up.
|
||||
* @endcond
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the initialization structure used to configure ACMP for capacitive
|
||||
* sensing operation.
|
||||
******************************************************************************/
|
||||
void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init)
|
||||
{
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
EFM_ASSERT(init->vrefDiv < 64);
|
||||
EFM_ASSERT(init->biasProg
|
||||
<= (_ACMP_CFG_BIAS_MASK >> _ACMP_CFG_BIAS_SHIFT));
|
||||
|
||||
ACMP_Disable(acmp);
|
||||
acmp->CFG = (init->biasProg << _ACMP_CFG_BIAS_SHIFT)
|
||||
| (init->hysteresisLevel << _ACMP_CFG_HYST_SHIFT);
|
||||
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
|
||||
ACMP_Enable(acmp);
|
||||
acmp->INPUTCTRL = (init->resistor << _ACMP_INPUTCTRL_CSRESSEL_SHIFT)
|
||||
| (init->vrefDiv << _ACMP_INPUTCTRL_VREFDIV_SHIFT)
|
||||
| (ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
|
||||
if (!init->enable) {
|
||||
ACMP_Disable(acmp);
|
||||
}
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
EFM_ASSERT(init->vddLevelLow < 64);
|
||||
EFM_ASSERT(init->vddLevelHigh < 64);
|
||||
EFM_ASSERT(init->biasProg
|
||||
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
|
||||
|
||||
/* Set the control register. No need to set interrupt modes. */
|
||||
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
|
||||
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
|
||||
| ACMP_CTRL_ACCURACY_HIGH;
|
||||
acmp->HYSTERESIS0 = (init->vddLevelHigh << _ACMP_HYSTERESIS0_DIVVA_SHIFT)
|
||||
| (init->hysteresisLevel_0 << _ACMP_HYSTERESIS0_HYST_SHIFT);
|
||||
acmp->HYSTERESIS1 = (init->vddLevelLow << _ACMP_HYSTERESIS1_DIVVA_SHIFT)
|
||||
| (init->hysteresisLevel_1 << _ACMP_HYSTERESIS1_HYST_SHIFT);
|
||||
/* Select capacitive sensing mode by selecting a resistor and enabling it. */
|
||||
acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
|
||||
| ACMP_INPUTSEL_CSRESEN
|
||||
| ACMP_INPUTSEL_VASEL_VDD
|
||||
| ACMP_INPUTSEL_NEGSEL_VADIV;
|
||||
BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, init->enable);
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_0)
|
||||
EFM_ASSERT(init->vddLevel < 64);
|
||||
EFM_ASSERT(init->biasProg
|
||||
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
|
||||
|
||||
/* Set the control register. No need to set interrupt modes. */
|
||||
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
|
||||
| (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
|
||||
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
|
||||
| (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
|
||||
| (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT);
|
||||
/* Select capacitive sensing mode by selecting a resistor and enabling it. */
|
||||
acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT)
|
||||
| ACMP_INPUTSEL_CSRESEN
|
||||
| (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
|
||||
| (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT)
|
||||
| ACMP_INPUTSEL_NEGSEL_CAPSENSE;
|
||||
BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, init->enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the ACMP channel used for capacitive sensing.
|
||||
*
|
||||
* @note
|
||||
* A basic example of capacitive sensing can be found in the STK BSP
|
||||
* (capsense demo).
|
||||
*
|
||||
* @cond DOXYDOC_S2_DEVICE
|
||||
* @note
|
||||
* Can only be called when the peripheral is enabled.
|
||||
* @endcond
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param[in] channel
|
||||
* The ACMP channel to use for capacitive sensing (Possel).
|
||||
******************************************************************************/
|
||||
void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip. */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
#if defined(_ACMP_INPUTSEL_POSSEL_CH7)
|
||||
/* Make sure that only external channels are used. */
|
||||
EFM_ASSERT(channel <= _ACMP_INPUTSEL_POSSEL_CH7);
|
||||
#elif defined(_ACMP_INPUTCTRL_POSSEL_PD15)
|
||||
EFM_ASSERT(channel != _ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
|
||||
EFM_ASSERT(_ACMP_INPUTCTRL_POSSEL_PA0 <= channel);
|
||||
EFM_ASSERT(channel <= _ACMP_INPUTCTRL_POSSEL_PD15);
|
||||
#endif
|
||||
|
||||
#if defined(_ACMP_INPUTCTRL_MASK)
|
||||
/* Make sure that the ACMP is enabled before changing INPUTCTRL. */
|
||||
EFM_ASSERT(acmp->EN & ACMP_EN_EN);
|
||||
|
||||
while (acmp->SYNCBUSY != 0U) {
|
||||
/* Wait for synchronization to finish */
|
||||
}
|
||||
/* Set channel as positive channel in ACMP */
|
||||
BUS_RegMaskedWrite(&acmp->INPUTCTRL, _ACMP_INPUTCTRL_POSSEL_MASK,
|
||||
channel << _ACMP_INPUTCTRL_POSSEL_SHIFT);
|
||||
#else
|
||||
/* Set channel as a positive channel in ACMP. */
|
||||
BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_POSSEL_MASK,
|
||||
channel << _ACMP_INPUTSEL_POSSEL_SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Disable ACMP.
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
******************************************************************************/
|
||||
void ACMP_Disable(ACMP_TypeDef *acmp)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip. */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
#if defined(_ACMP_EN_MASK)
|
||||
while ((acmp->EN != 0U) && (acmp->SYNCBUSY != 0U)) {
|
||||
/* Wait for synchronization to finish */
|
||||
}
|
||||
acmp->EN_CLR = ACMP_EN_EN;
|
||||
|
||||
#if defined(_ACMP_EN_DISABLING_MASK)
|
||||
while (acmp->EN & _ACMP_EN_DISABLING_MASK) {
|
||||
// Wait for disabling to finish
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
acmp->CTRL &= ~ACMP_CTRL_EN;
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable ACMP.
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
******************************************************************************/
|
||||
void ACMP_Enable(ACMP_TypeDef *acmp)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip. */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
#if defined(_ACMP_EN_MASK)
|
||||
acmp->EN_SET = ACMP_EN_EN;
|
||||
#else
|
||||
acmp->CTRL |= ACMP_CTRL_EN;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_ACMP_EXTIFCTRL_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Select and enable external input.
|
||||
*
|
||||
* @details
|
||||
* This is used when an external module needs to take control of the ACMP
|
||||
* POSSEL field to configure the APORT input for the ACMP. Modules,
|
||||
* such as LESENSE, use this to change the ACMP input during a scan sequence.
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param[in] aport
|
||||
* This parameter decides which APORT(s) the ACMP will use when it's
|
||||
* controlled by an external module.
|
||||
******************************************************************************/
|
||||
void ACMP_ExternalInputSelect(ACMP_TypeDef *acmp, ACMP_ExternalInput_Typedef aport)
|
||||
{
|
||||
acmp->EXTIFCTRL = (aport << _ACMP_EXTIFCTRL_APORTSEL_SHIFT)
|
||||
| ACMP_EXTIFCTRL_EN;
|
||||
while (!(acmp->STATUS & ACMP_STATUS_EXTIFACT)) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Reset ACMP to the same state that it was in after a hardware reset.
|
||||
*
|
||||
* @note
|
||||
* The GPIO ACMP ROUTE register is NOT reset by this function to allow for
|
||||
* centralized setup of this feature.
|
||||
*
|
||||
* @note
|
||||
* The peripheral may be enabled and disabled during reset.
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
******************************************************************************/
|
||||
void ACMP_Reset(ACMP_TypeDef *acmp)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
#if defined(ACMP_SWRST_SWRST)
|
||||
acmp->SWRST_SET = ACMP_SWRST_SWRST;
|
||||
while (acmp->SWRST & _ACMP_SWRST_RESETTING_MASK) {
|
||||
}
|
||||
#else
|
||||
acmp->IEN = _ACMP_IEN_RESETVALUE;
|
||||
ACMP_Enable(acmp);
|
||||
acmp->INPUTCTRL = _ACMP_INPUTCTRL_RESETVALUE;
|
||||
ACMP_Disable(acmp);
|
||||
acmp->CFG = PM5507_ACMP_CFG_RESETVALUE;
|
||||
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
|
||||
acmp->IF_CLR = _ACMP_IF_MASK;
|
||||
#endif
|
||||
#else // Series 0 and Series 1 devices
|
||||
acmp->IEN = _ACMP_IEN_RESETVALUE;
|
||||
acmp->CTRL = _ACMP_CTRL_RESETVALUE;
|
||||
acmp->INPUTSEL = _ACMP_INPUTSEL_RESETVALUE;
|
||||
#if defined(_ACMP_HYSTERESIS0_HYST_MASK)
|
||||
acmp->HYSTERESIS0 = _ACMP_HYSTERESIS0_RESETVALUE;
|
||||
acmp->HYSTERESIS1 = _ACMP_HYSTERESIS1_RESETVALUE;
|
||||
#endif
|
||||
acmp->IFC = _ACMP_IF_MASK;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_GPIO_ACMP_ROUTEEN_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Sets up GPIO output from the ACMP.
|
||||
*
|
||||
* @note
|
||||
* GPIO must be enabled in the CMU before this function call, i.e.
|
||||
* @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
|
||||
*
|
||||
* @param[in] acmp
|
||||
* Pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param port
|
||||
* The GPIO port to use.
|
||||
*
|
||||
* @param pin
|
||||
* The GPIO pin to use.
|
||||
*
|
||||
* @param enable
|
||||
* Enable or disable pin output.
|
||||
*
|
||||
* @param invert
|
||||
* Invert output.
|
||||
******************************************************************************/
|
||||
void ACMP_GPIOSetup(ACMP_TypeDef *acmp, GPIO_Port_TypeDef port,
|
||||
unsigned int pin, bool enable, bool invert)
|
||||
{
|
||||
int acmpIndex = ACMP_DEVICE_ID(acmp);
|
||||
|
||||
/* Make sure the module exists on the selected chip */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
/* Make sure that the port/pin combination is valid. */
|
||||
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
|
||||
|
||||
/* Set GPIO inversion */
|
||||
acmp->CTRL = (acmp->CTRL & _ACMP_CTRL_NOTRDYVAL_MASK)
|
||||
| (invert << _ACMP_CTRL_GPIOINV_SHIFT);
|
||||
|
||||
GPIO->ACMPROUTE[acmpIndex].ACMPOUTROUTE = (port << _GPIO_ACMP_ACMPOUTROUTE_PORT_SHIFT)
|
||||
| (pin << _GPIO_ACMP_ACMPOUTROUTE_PIN_SHIFT);
|
||||
GPIO->ACMPROUTE[acmpIndex].ROUTEEN = enable ? GPIO_ACMP_ROUTEEN_ACMPOUTPEN : 0;
|
||||
}
|
||||
#else
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set up GPIO output from ACMP.
|
||||
*
|
||||
* @note
|
||||
* GPIO must be enabled in the CMU before this function call, i.e.,
|
||||
* @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param location
|
||||
* The pin location to use. See the data sheet for location to pin mappings.
|
||||
*
|
||||
* @param enable
|
||||
* Enable or disable pin output.
|
||||
*
|
||||
* @param invert
|
||||
* Invert output.
|
||||
******************************************************************************/
|
||||
void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
/* Sanity checking of location */
|
||||
EFM_ASSERT(location <= _ACMP_ROUTE_LOCATION_MAX);
|
||||
|
||||
/* Set GPIO inversion */
|
||||
BUS_RegMaskedWrite(&acmp->CTRL, _ACMP_CTRL_GPIOINV_MASK,
|
||||
invert << _ACMP_CTRL_GPIOINV_SHIFT);
|
||||
|
||||
#if defined(_ACMP_ROUTE_MASK)
|
||||
acmp->ROUTE = (location << _ACMP_ROUTE_LOCATION_SHIFT)
|
||||
| (enable << _ACMP_ROUTE_ACMPPEN_SHIFT);
|
||||
#endif
|
||||
#if defined(_ACMP_ROUTELOC0_MASK)
|
||||
acmp->ROUTELOC0 = location << _ACMP_ROUTELOC0_OUTLOC_SHIFT;
|
||||
acmp->ROUTEPEN = enable ? ACMP_ROUTEPEN_OUTPEN : 0;
|
||||
#endif
|
||||
}
|
||||
#endif /* defined(_GPIO_ACMP_ROUTEEN_MASK) */
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set which channels should be used in ACMP comparisons.
|
||||
*
|
||||
* @cond DOXYDOC_S2_DEVICE
|
||||
* @note
|
||||
* Can only be called when the peripheral is enabled.
|
||||
*
|
||||
* @note
|
||||
* If GPIO is used for both posSel and negSel, they cannot both use even
|
||||
* or odd pins.
|
||||
* @endcond
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param negSel
|
||||
* A channel to use on the negative input to the ACMP.
|
||||
*
|
||||
* @param posSel
|
||||
* A channel to use on the positive input to the ACMP.
|
||||
******************************************************************************/
|
||||
void ACMP_ChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef negSel,
|
||||
ACMP_Channel_TypeDef posSel)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip. */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
/* Make sure that posSel and negSel channel selectors are valid. */
|
||||
#if defined(_ACMP_INPUTSEL_NEGSEL_DAC0CH1)
|
||||
EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_DAC0CH1);
|
||||
#elif defined(_ACMP_INPUTSEL_NEGSEL_CAPSENSE)
|
||||
EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_CAPSENSE);
|
||||
#endif
|
||||
|
||||
#if defined(_ACMP_INPUTSEL_POSSEL_CH7)
|
||||
EFM_ASSERT(posSel <= _ACMP_INPUTSEL_POSSEL_CH7);
|
||||
#endif
|
||||
|
||||
/* Make sure that posSel and negSel channel selectors are valid. */
|
||||
#if defined(_ACMP_INPUTCTRL_POSSEL_PD15)
|
||||
EFM_ASSERT(negSel <= _ACMP_INPUTCTRL_POSSEL_PD15);
|
||||
EFM_ASSERT(posSel <= _ACMP_INPUTCTRL_POSSEL_PD15);
|
||||
EFM_ASSERT(posSel != _ACMP_INPUTCTRL_NEGSEL_CAPSENSE);
|
||||
|
||||
/* Make sure that posSel and negSel channel selectors don't both
|
||||
* use odd or even pins. */
|
||||
#if (_SILICON_LABS_32B_SERIES_2_CONFIG > 2)
|
||||
EFM_ASSERT(!((((posSel >= _ACMP_INPUTCTRL_POSSEL_EXTPA)
|
||||
&& (posSel <= _ACMP_INPUTCTRL_POSSEL_EXTPD))
|
||||
|| (posSel >= _ACMP_INPUTCTRL_POSSEL_PA0))
|
||||
&& (negSel >= _ACMP_INPUTCTRL_NEGSEL_PA0)
|
||||
&& (posSel % 2 == negSel % 2)));
|
||||
#else
|
||||
EFM_ASSERT(!((posSel >= _ACMP_INPUTCTRL_POSSEL_PA0)
|
||||
&& (negSel >= _ACMP_INPUTCTRL_NEGSEL_PA0)
|
||||
&& (posSel % 2 == negSel % 2)));
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_ACMP_INPUTCTRL_MASK)
|
||||
/* Make sure that the ACMP is enabled before changing INPUTCTRL. */
|
||||
EFM_ASSERT(acmp->EN & ACMP_EN_EN);
|
||||
while (acmp->SYNCBUSY != 0U) {
|
||||
/* Wait for synchronization to finish */
|
||||
}
|
||||
acmp->INPUTCTRL = (acmp->INPUTCTRL & ~(_ACMP_INPUTCTRL_POSSEL_MASK
|
||||
| _ACMP_INPUTCTRL_NEGSEL_MASK))
|
||||
| (negSel << _ACMP_INPUTCTRL_NEGSEL_SHIFT)
|
||||
| (posSel << _ACMP_INPUTCTRL_POSSEL_SHIFT);
|
||||
#else
|
||||
acmp->INPUTSEL = (acmp->INPUTSEL & ~(_ACMP_INPUTSEL_POSSEL_MASK
|
||||
| _ACMP_INPUTSEL_NEGSEL_MASK))
|
||||
| (negSel << _ACMP_INPUTSEL_NEGSEL_SHIFT)
|
||||
| (posSel << _ACMP_INPUTSEL_POSSEL_SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize ACMP.
|
||||
*
|
||||
* @cond DOXYDOC_S2_DEVICE
|
||||
* @note
|
||||
* A call to ACMP_Init can cause side effects since it can enable/disable
|
||||
* the peripheral.
|
||||
* @endcond
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the initialization structure used to configure ACMP.
|
||||
******************************************************************************/
|
||||
void ACMP_Init(ACMP_TypeDef *acmp, const ACMP_Init_TypeDef *init)
|
||||
{
|
||||
/* Make sure the module exists on the selected chip. */
|
||||
EFM_ASSERT(ACMP_REF_VALID(acmp));
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
EFM_ASSERT(init->biasProg
|
||||
<= (_ACMP_CFG_BIAS_MASK >> _ACMP_CFG_BIAS_SHIFT));
|
||||
|
||||
// PM-5507: enforce that biasProg is a functional value
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
|
||||
EFM_ASSERT(init->biasProg >= 4);
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
|
||||
// Allow customer to use BIASPROG in [2; 3]
|
||||
EFM_ASSERT(init->biasProg >= 2);
|
||||
#else
|
||||
// Allow customer to use BIASPROG in [0; 3]
|
||||
// but the implementation of the wait operation would be their responsibility
|
||||
#endif
|
||||
|
||||
/* Make sure the ACMP is disabled since ACMP power source might be changed.*/
|
||||
ACMP_Disable(acmp);
|
||||
|
||||
acmp->CFG = (init->biasProg << _ACMP_CFG_BIAS_SHIFT)
|
||||
| (init->inputRange << _ACMP_CFG_INPUTRANGE_SHIFT)
|
||||
| (init->accuracy << _ACMP_CFG_ACCURACY_SHIFT)
|
||||
| (init->hysteresisLevel << _ACMP_CFG_HYST_SHIFT);
|
||||
acmp->CTRL = init->inactiveValue << _ACMP_CTRL_NOTRDYVAL_SHIFT;
|
||||
ACMP_Enable(acmp);
|
||||
BUS_RegMaskedWrite(&acmp->INPUTCTRL, _ACMP_INPUTCTRL_VREFDIV_MASK,
|
||||
init->vrefDiv << _ACMP_INPUTCTRL_VREFDIV_SHIFT);
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
EFM_ASSERT(init->biasProg
|
||||
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
|
||||
/* Make sure the ACMP is disabled since ACMP power source might be changed.*/
|
||||
ACMP_Disable(acmp);
|
||||
|
||||
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
|
||||
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
|
||||
| (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
|
||||
| (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
|
||||
| (init->inputRange << _ACMP_CTRL_INPUTRANGE_SHIFT)
|
||||
| (init->accuracy << _ACMP_CTRL_ACCURACY_SHIFT)
|
||||
| (init->powerSource << _ACMP_CTRL_PWRSEL_SHIFT)
|
||||
| (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
|
||||
acmp->INPUTSEL = init->vlpInput << _ACMP_INPUTSEL_VLPSEL_SHIFT;
|
||||
acmp->HYSTERESIS0 = init->hysteresisLevel_0;
|
||||
acmp->HYSTERESIS1 = init->hysteresisLevel_1;
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_0)
|
||||
EFM_ASSERT(init->biasProg
|
||||
<= (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT));
|
||||
/* Make sure the ACMP is disabled since ACMP power source might be changed.*/
|
||||
ACMP_Disable(acmp);
|
||||
|
||||
acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT)
|
||||
| (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT)
|
||||
| (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT)
|
||||
| (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT)
|
||||
| (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT)
|
||||
| (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT)
|
||||
| (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT)
|
||||
| (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT);
|
||||
acmp->INPUTSEL = (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT)
|
||||
| (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT);
|
||||
|
||||
#endif
|
||||
|
||||
if (init->enable) {
|
||||
ACMP_Enable(acmp);
|
||||
} else {
|
||||
ACMP_Disable(acmp);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_ACMP_INPUTSEL_VASEL_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set up the VA source.
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param[in] vaconfig
|
||||
* A pointer to the structure used to configure the VA source. This structure
|
||||
* contains the input source and the 2 divider values.
|
||||
******************************************************************************/
|
||||
void ACMP_VASetup(ACMP_TypeDef *acmp, const ACMP_VAConfig_TypeDef *vaconfig)
|
||||
{
|
||||
EFM_ASSERT(vaconfig->div0 < 64);
|
||||
EFM_ASSERT(vaconfig->div1 < 64);
|
||||
|
||||
BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VASEL_MASK,
|
||||
vaconfig->input << _ACMP_INPUTSEL_VASEL_SHIFT);
|
||||
BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVA_MASK,
|
||||
vaconfig->div0 << _ACMP_HYSTERESIS0_DIVVA_SHIFT);
|
||||
BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVA_MASK,
|
||||
vaconfig->div1 << _ACMP_HYSTERESIS1_DIVVA_SHIFT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_ACMP_INPUTSEL_VBSEL_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set up the VB Source.
|
||||
*
|
||||
* @param[in] acmp
|
||||
* A pointer to the ACMP peripheral register block.
|
||||
*
|
||||
* @param[in] vbconfig
|
||||
* A pointer to the structure used to configure the VB source. This structure
|
||||
* contains the input source and the 2 divider values.
|
||||
******************************************************************************/
|
||||
void ACMP_VBSetup(ACMP_TypeDef *acmp, const ACMP_VBConfig_TypeDef *vbconfig)
|
||||
{
|
||||
EFM_ASSERT(vbconfig->div0 < 64);
|
||||
EFM_ASSERT(vbconfig->div1 < 64);
|
||||
|
||||
BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VBSEL_MASK,
|
||||
vbconfig->input << _ACMP_INPUTSEL_VBSEL_SHIFT);
|
||||
BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVB_MASK,
|
||||
vbconfig->div0 << _ACMP_HYSTERESIS0_DIVVB_SHIFT);
|
||||
BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVB_MASK,
|
||||
vbconfig->div1 << _ACMP_HYSTERESIS1_DIVVB_SHIFT);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} (end addtogroup acmp) */
|
||||
#endif /* defined(ACMP_COUNT) && (ACMP_COUNT > 0) */
|
||||
433
Libs/platform/emlib/src/em_burtc.c
Normal file
433
Libs/platform/emlib/src/em_burtc.c
Normal file
@@ -0,0 +1,433 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Backup Real Time Counter (BURTC) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_burtc.h"
|
||||
#if defined(BURTC_PRESENT)
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup burtc BURTC - Backup RTC
|
||||
* @brief Backup Real Time Counter (BURTC) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the BURTC peripheral of Silicon
|
||||
* Labs 32-bit MCUs. The Backup Real Time Counter allows timekeeping in all
|
||||
* energy modes. The Backup RTC is also available when the system is in backup
|
||||
* mode.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
************************** LOCAL FUNCTIONS ********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
/***************************************************************************//**
|
||||
* @brief Convert dividend to a prescaler logarithmic value. Only works for even
|
||||
* numbers equal to 2^n.
|
||||
* @param[in] div Unscaled dividend,
|
||||
* @return Base 2 logarithm of input, as used by fixed prescalers.
|
||||
******************************************************************************/
|
||||
__STATIC_INLINE uint32_t divToLog2(uint32_t div)
|
||||
{
|
||||
uint32_t log2;
|
||||
|
||||
/* Prescaler accepts an argument of 128 or less, valid values being 2^n. */
|
||||
EFM_ASSERT((div > 0UL) && (div <= 32768UL));
|
||||
|
||||
/* Count leading zeroes and "reverse" result, Cortex-M3 intrinsic. */
|
||||
log2 = (31UL - __CLZ(div));
|
||||
|
||||
return log2;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Wait for an ongoing sync of register(s) to low frequency domain to complete.
|
||||
*
|
||||
* @param[in] mask
|
||||
* A bitmask corresponding to SYNCBUSY register defined bits, indicating
|
||||
* registers that must complete any ongoing synchronization.
|
||||
******************************************************************************/
|
||||
__STATIC_INLINE void regSync(uint32_t mask)
|
||||
{
|
||||
#if defined(_BURTC_FREEZE_MASK)
|
||||
/* Avoid deadlock if modifying the same register twice when freeze mode is
|
||||
activated or when a clock is not selected for the BURTC. If a clock is
|
||||
not selected, then the sync is done once the clock source is set. */
|
||||
if ((BURTC->FREEZE & BURTC_FREEZE_REGFREEZE)
|
||||
|| ((BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK) == BURTC_CTRL_CLKSEL_NONE)
|
||||
|| ((BURTC->CTRL & _BURTC_CTRL_RSTEN_MASK) == BURTC_CTRL_RSTEN)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Wait for any pending previous write operation to complete */
|
||||
/* in low frequency domain. This is only required for the Gecko Family. */
|
||||
while ((BURTC->SYNCBUSY & mask) != 0U) {
|
||||
}
|
||||
}
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief Initialize BURTC.
|
||||
*
|
||||
* @details
|
||||
* Configures the BURTC peripheral.
|
||||
*
|
||||
* @note
|
||||
* Before initialization, BURTC module must first be enabled by clearing the
|
||||
* reset bit in the RMU, i.e.,
|
||||
* @verbatim
|
||||
* RMU_ResetControl(rmuResetBU, rmuResetModeClear);
|
||||
* @endverbatim
|
||||
* Compare channel 0 must be configured outside this function, before
|
||||
* initialization if enable is set to true. The counter will always be reset.
|
||||
*
|
||||
* @param[in] burtcInit
|
||||
* A pointer to the BURTC initialization structure.
|
||||
******************************************************************************/
|
||||
void BURTC_Init(const BURTC_Init_TypeDef *burtcInit)
|
||||
{
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
uint32_t ctrl;
|
||||
uint32_t presc;
|
||||
|
||||
/* Check initializer structure integrity. */
|
||||
EFM_ASSERT(burtcInit != (BURTC_Init_TypeDef *) 0);
|
||||
/* Clock divider must be between 1 and 128, really on the form 2^n. */
|
||||
EFM_ASSERT((burtcInit->clkDiv >= 1) && (burtcInit->clkDiv <= 128));
|
||||
|
||||
/* Ignored compare bits during low power operation must be less than 7. */
|
||||
/* Note! Giant Gecko revision C errata, do NOT use LPCOMP=7. */
|
||||
EFM_ASSERT(burtcInit->lowPowerComp <= 6);
|
||||
/* You cannot enable the BURTC if mode is set to disabled. */
|
||||
EFM_ASSERT((burtcInit->enable == false)
|
||||
|| ((burtcInit->enable == true)
|
||||
&& (burtcInit->mode != burtcModeDisable)));
|
||||
/* Low power mode is only available with LFRCO or LFXO as clock source. */
|
||||
EFM_ASSERT((burtcInit->clkSel != burtcClkSelULFRCO)
|
||||
|| ((burtcInit->clkSel == burtcClkSelULFRCO)
|
||||
&& (burtcInit->lowPowerMode == burtcLPDisable)));
|
||||
|
||||
/* Calculate a prescaler value from the clock divider input. */
|
||||
/* Note! If clock select (clkSel) is ULFRCO, a clock divisor (clkDiv) of
|
||||
value 1 will select a 2 kHz ULFRCO clock, while any other value will
|
||||
select a 1 kHz ULFRCO clock source. */
|
||||
presc = divToLog2(burtcInit->clkDiv);
|
||||
|
||||
/* Make sure all registers are updated simultaneously. */
|
||||
if (burtcInit->enable) {
|
||||
BURTC_FreezeEnable(true);
|
||||
}
|
||||
|
||||
/* Modification of LPMODE register requires sync with potential ongoing
|
||||
* register updates in LF domain. */
|
||||
regSync(BURTC_SYNCBUSY_LPMODE);
|
||||
|
||||
/* Configure low power mode. */
|
||||
BURTC->LPMODE = (uint32_t) (burtcInit->lowPowerMode);
|
||||
|
||||
/* New configuration. */
|
||||
ctrl = (BURTC_CTRL_RSTEN
|
||||
| (burtcInit->mode)
|
||||
| (burtcInit->debugRun << _BURTC_CTRL_DEBUGRUN_SHIFT)
|
||||
| (burtcInit->compare0Top << _BURTC_CTRL_COMP0TOP_SHIFT)
|
||||
| (burtcInit->lowPowerComp << _BURTC_CTRL_LPCOMP_SHIFT)
|
||||
| (presc << _BURTC_CTRL_PRESC_SHIFT)
|
||||
| (burtcInit->clkSel)
|
||||
| (burtcInit->timeStamp << _BURTC_CTRL_BUMODETSEN_SHIFT));
|
||||
|
||||
/* Clear interrupts. */
|
||||
BURTC_IntClear(0xFFFFFFFF);
|
||||
|
||||
/* Set the new configuration. */
|
||||
BURTC->CTRL = ctrl;
|
||||
|
||||
/* Enable BURTC and counter. */
|
||||
if (burtcInit->enable) {
|
||||
/* To enable BURTC counter, disable reset. */
|
||||
BURTC_Enable(true);
|
||||
|
||||
/* Clear freeze. */
|
||||
BURTC_FreezeEnable(false);
|
||||
}
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
uint32_t presc;
|
||||
|
||||
presc = divToLog2(burtcInit->clkDiv);
|
||||
|
||||
if (BURTC->EN != 0U) {
|
||||
BURTC_SyncWait();
|
||||
}
|
||||
BURTC->EN_CLR = BURTC_EN_EN;
|
||||
#if defined(_BURTC_SYNCBUSY_EN_MASK)
|
||||
regSync(BURTC_SYNCBUSY_EN);
|
||||
#elif defined(_BURTC_EN_DISABLING_MASK)
|
||||
while (BURTC->EN & _BURTC_EN_DISABLING_MASK) {
|
||||
/* Wait for disabling to finish */
|
||||
}
|
||||
#endif
|
||||
|
||||
BURTC->CFG = (presc << _BURTC_CFG_CNTPRESC_SHIFT)
|
||||
| ((burtcInit->compare0Top ? 1UL : 0UL) << _BURTC_CFG_COMPTOP_SHIFT)
|
||||
| ((burtcInit->debugRun ? 1UL : 0UL) << _BURTC_CFG_DEBUGRUN_SHIFT);
|
||||
BURTC->EM4WUEN = ((burtcInit->em4comp ? 1UL : 0UL) << _BURTC_EM4WUEN_COMPEM4WUEN_SHIFT)
|
||||
| ((burtcInit->em4overflow ? 1UL : 0UL) << _BURTC_EM4WUEN_OFEM4WUEN_SHIFT);
|
||||
BURTC->EN_SET = BURTC_EN_EN;
|
||||
if (burtcInit->start) {
|
||||
BURTC_Start();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable or Disable BURTC peripheral.
|
||||
*
|
||||
* @param[in] enable
|
||||
* true to enable, false to disable.
|
||||
******************************************************************************/
|
||||
void BURTC_Enable(bool enable)
|
||||
{
|
||||
#if defined(_BURTC_SYNCBUSY_EN_MASK)
|
||||
regSync(BURTC_SYNCBUSY_EN);
|
||||
#endif
|
||||
|
||||
if ((BURTC->EN == 0U) && !enable) {
|
||||
/* Trying to disable BURTC when it's already disabled */
|
||||
return;
|
||||
}
|
||||
|
||||
if (BURTC->EN != 0U) {
|
||||
/* Modifying the enable bit while synchronization is active will BusFault */
|
||||
BURTC_SyncWait();
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
BURTC->EN_SET = BURTC_EN_EN;
|
||||
} else {
|
||||
BURTC_Stop();
|
||||
BURTC_SyncWait(); /* Wait for the stop to synchronize */
|
||||
BURTC->EN_CLR = BURTC_EN_EN;
|
||||
#if defined(_BURTC_SYNCBUSY_EN_MASK)
|
||||
regSync(BURTC_SYNCBUSY_EN);
|
||||
#elif defined(_BURTC_EN_DISABLING_MASK)
|
||||
while (BURTC->EN & _BURTC_EN_DISABLING_MASK) {
|
||||
/* Wait for disabling to finish */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_0)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable or Disable BURTC peripheral reset and start counter
|
||||
* @param[in] enable
|
||||
* If true; asserts reset to BURTC, halts counter, if false; deassert reset
|
||||
******************************************************************************/
|
||||
void BURTC_Enable(bool enable)
|
||||
{
|
||||
/* Note! If mode is disabled, BURTC counter will not start */
|
||||
EFM_ASSERT(((enable == true)
|
||||
&& ((BURTC->CTRL & _BURTC_CTRL_MODE_MASK)
|
||||
!= BURTC_CTRL_MODE_DISABLE))
|
||||
|| (enable == false));
|
||||
BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, (uint32_t) !enable);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief Set BURTC compare channel.
|
||||
*
|
||||
* @param[in] comp Compare the channel index, must be 0 for current devices.
|
||||
*
|
||||
* @param[in] value New compare value.
|
||||
******************************************************************************/
|
||||
void BURTC_CompareSet(unsigned int comp, uint32_t value)
|
||||
{
|
||||
(void) comp; /* Unused parameter when EFM_ASSERT is undefined. */
|
||||
|
||||
EFM_ASSERT(comp == 0U);
|
||||
|
||||
#if defined(_BURTC_COMP0_MASK)
|
||||
/* Modification of COMP0 register requires sync with potential ongoing
|
||||
* register updates in LF domain. */
|
||||
regSync(BURTC_SYNCBUSY_COMP0);
|
||||
|
||||
/* Configure compare channel 0/. */
|
||||
BURTC->COMP0 = value;
|
||||
#else
|
||||
/* Wait for last potential write to complete. */
|
||||
regSync(BURTC_SYNCBUSY_COMP);
|
||||
|
||||
/* Configure compare channel 0 */
|
||||
BURTC->COMP = value;
|
||||
regSync(BURTC_SYNCBUSY_COMP);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief Get the BURTC compare value.
|
||||
*
|
||||
* @param[in] comp Compare the channel index value, must be 0 for Giant/Leopard Gecko.
|
||||
*
|
||||
* @return The currently configured value for this compare channel.
|
||||
******************************************************************************/
|
||||
uint32_t BURTC_CompareGet(unsigned int comp)
|
||||
{
|
||||
(void) comp; /* Unused parameter when EFM_ASSERT is undefined. */
|
||||
|
||||
EFM_ASSERT(comp == 0U);
|
||||
#if defined(_BURTC_COMP0_MASK)
|
||||
return BURTC->COMP0;
|
||||
#else
|
||||
return BURTC->COMP;
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief Reset counter
|
||||
******************************************************************************/
|
||||
void BURTC_CounterReset(void)
|
||||
{
|
||||
#if defined(_BURTC_CTRL_MASK)
|
||||
/* Set and clear reset bit */
|
||||
BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 1U);
|
||||
BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 0U);
|
||||
#else
|
||||
BURTC_Stop();
|
||||
BURTC->CNT = 0U;
|
||||
BURTC_Start();
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Restore BURTC to reset state.
|
||||
* @note
|
||||
* Before accessing the BURTC, BURSTEN in RMU->CTRL must be cleared.
|
||||
* LOCK will not be reset to default value, as this will disable access
|
||||
* to core BURTC registers.
|
||||
******************************************************************************/
|
||||
void BURTC_Reset(void)
|
||||
{
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
bool buResetState;
|
||||
|
||||
/* Read reset state, set reset, and restore state. */
|
||||
buResetState = BUS_RegBitRead(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT);
|
||||
BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, 1);
|
||||
BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, buResetState);
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
if (BURTC->EN != 0U) {
|
||||
BURTC_SyncWait();
|
||||
}
|
||||
BURTC->EN_SET = BURTC_EN_EN;
|
||||
BURTC_Stop();
|
||||
BURTC->CNT = 0x0;
|
||||
BURTC->PRECNT = 0x0;
|
||||
BURTC->COMP = 0x0;
|
||||
BURTC->EM4WUEN = _BURTC_EM4WUEN_RESETVALUE;
|
||||
BURTC->IEN = _BURTC_IEN_RESETVALUE;
|
||||
BURTC->IF_CLR = _BURTC_IF_MASK;
|
||||
/* Wait for all values to synchronize. BusFaults can happen if we don't
|
||||
* do this before the enable bit is cleared. */
|
||||
BURTC_SyncWait();
|
||||
BURTC->EN_CLR = BURTC_EN_EN;
|
||||
#if defined(_BURTC_SYNCBUSY_EN_MASK)
|
||||
while (BURTC->SYNCBUSY != 0U) {
|
||||
// Wait for the EN=0 to synchronize
|
||||
}
|
||||
#elif defined(_BURTC_EN_DISABLING_MASK)
|
||||
while (BURTC->EN & _BURTC_EN_DISABLING_MASK) {
|
||||
/* Wait for disabling to finish */
|
||||
}
|
||||
#endif
|
||||
BURTC->CFG = _BURTC_CFG_RESETVALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_BURTC_CTRL_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the clock frequency of the BURTC.
|
||||
*
|
||||
* @return
|
||||
* The current frequency in Hz.
|
||||
******************************************************************************/
|
||||
uint32_t BURTC_ClockFreqGet(void)
|
||||
{
|
||||
uint32_t clkSel;
|
||||
uint32_t clkDiv;
|
||||
uint32_t frequency;
|
||||
|
||||
clkSel = BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK;
|
||||
clkDiv = (BURTC->CTRL & _BURTC_CTRL_PRESC_MASK) >> _BURTC_CTRL_PRESC_SHIFT;
|
||||
|
||||
switch (clkSel) {
|
||||
/** Ultra-low frequency (1 kHz) clock. */
|
||||
case BURTC_CTRL_CLKSEL_ULFRCO:
|
||||
if (_BURTC_CTRL_PRESC_DIV1 == clkDiv) {
|
||||
frequency = 2000; /* 2 kHz when clock divisor is 1. */
|
||||
} else {
|
||||
frequency = SystemULFRCOClockGet(); /* 1 kHz when divisor is different
|
||||
from 1. */
|
||||
}
|
||||
break;
|
||||
|
||||
/** Low-frequency RC oscillator. */
|
||||
case BURTC_CTRL_CLKSEL_LFRCO:
|
||||
frequency = SystemLFRCOClockGet() / (1 << clkDiv); /* freq=32768/2^clkDiv */
|
||||
break;
|
||||
|
||||
/** Low-frequency crystal oscillator. */
|
||||
case BURTC_CTRL_CLKSEL_LFXO:
|
||||
frequency = SystemLFXOClockGet() / (1 << clkDiv); /* freq=32768/2^clkDiv */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* No clock selected for BURTC. */
|
||||
frequency = 0;
|
||||
}
|
||||
return frequency;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} (end addtogroup burtc) */
|
||||
|
||||
#endif /* BURTC_PRESENT */
|
||||
11864
Libs/platform/emlib/src/em_cmu.c
Normal file
11864
Libs/platform/emlib/src/em_cmu.c
Normal file
File diff suppressed because it is too large
Load Diff
518
Libs/platform/emlib/src/em_core.c
Normal file
518
Libs/platform/emlib/src/em_core.c
Normal file
@@ -0,0 +1,518 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Core interrupt handling API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include "em_core.h"
|
||||
#include "sl_assert.h"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
// *****************************************************************************
|
||||
/// @addtogroup core CORE - Interrupt Handling
|
||||
///
|
||||
///
|
||||
/// @warning
|
||||
/// If you are looking for atomic and critical sections, they have been moved
|
||||
/// to platform/common in the Core API.
|
||||
///
|
||||
/// @section core_intro Introduction
|
||||
///
|
||||
/// This module provides support for NVIC sections. NVIC sections are like
|
||||
/// critical sections, except interrupts are masked on an individual IRQ basis.
|
||||
/// This module also provides an API to relocate the vector table in RAM, and
|
||||
/// register IRQ handlers in the RAM based interrupt vector table.
|
||||
///
|
||||
/// @li <b>NVIC mask section</b>: Mask interrupts (external interrupts) on an
|
||||
/// individual IRQ basis.
|
||||
///
|
||||
/// @section core_examples Examples
|
||||
///
|
||||
/// Implement an NVIC critical section:
|
||||
/// @code{.c}
|
||||
/// {
|
||||
/// CORE_DECLARE_NVIC_ZEROMASK(mask); // A zero initialized NVIC disable mask
|
||||
///
|
||||
/// // Set mask bits for IRQs to block in the NVIC critical section.
|
||||
/// // In many cases, you can create the disable mask once upon application
|
||||
/// // startup and use the mask globally throughout the application lifetime.
|
||||
/// CORE_NvicMaskSetIRQ(LEUART0_IRQn, &mask);
|
||||
/// CORE_NvicMaskSetIRQ(VCMP_IRQn, &mask);
|
||||
///
|
||||
/// // Enter NVIC critical section with the disable mask
|
||||
/// CORE_NVIC_SECTION(&mask,
|
||||
/// ...
|
||||
/// ... your code goes here ...
|
||||
/// ...
|
||||
/// )
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// @section core_vector_tables Interrupt vector tables
|
||||
///
|
||||
/// When using RAM based interrupt vector tables it is the user's responsibility
|
||||
/// to allocate the table space correctly. The tables must be aligned as specified
|
||||
/// in the CPU reference manual.
|
||||
///
|
||||
/// Use @ref CORE_InitNvicVectorTable() to initialize a RAM based vector table
|
||||
/// by copying table entries from a source vector table to a target table.
|
||||
/// VTOR is set to the address of the target vector table.
|
||||
///
|
||||
/// Use @ref CORE_GetNvicRamTableHandler() @ref CORE_SetNvicRamTableHandler()
|
||||
/// to get or set the interrupt handler for a specific IRQn. They both use
|
||||
/// the interrupt vector table defined by the current VTOR register value.
|
||||
///
|
||||
/// @{
|
||||
// *****************************************************************************
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enter a NVIC mask section.
|
||||
*
|
||||
* When a NVIC mask section is entered, specified NVIC interrupts
|
||||
* are disabled.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[out] nvicState
|
||||
* Return NVIC interrupts enable mask prior to section entry.
|
||||
*
|
||||
* @param[in] disable
|
||||
* A mask specifying which NVIC interrupts to disable within the section.
|
||||
******************************************************************************/
|
||||
void CORE_EnterNvicMask(CORE_nvicMask_t *nvicState,
|
||||
const CORE_nvicMask_t *disable)
|
||||
{
|
||||
CORE_CRITICAL_SECTION(
|
||||
*nvicState = *(CORE_nvicMask_t*)((uint32_t)&NVIC->ICER[0]);
|
||||
*(CORE_nvicMask_t*)((uint32_t)&NVIC->ICER[0]) = *disable;
|
||||
)
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Disable NVIC interrupts.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[in] disable
|
||||
* A mask specifying which NVIC interrupts to disable.
|
||||
******************************************************************************/
|
||||
void CORE_NvicDisableMask(const CORE_nvicMask_t *disable)
|
||||
{
|
||||
CORE_CRITICAL_SECTION(
|
||||
*(CORE_nvicMask_t*)((uint32_t)&NVIC->ICER[0]) = *disable;
|
||||
)
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set current NVIC interrupt enable mask.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[out] enable
|
||||
* A mask specifying which NVIC interrupts are currently enabled.
|
||||
******************************************************************************/
|
||||
void CORE_NvicEnableMask(const CORE_nvicMask_t *enable)
|
||||
{
|
||||
CORE_CRITICAL_SECTION(
|
||||
*(CORE_nvicMask_t*)((uint32_t)&NVIC->ISER[0]) = *enable;
|
||||
)
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Brief NVIC interrupt enable/disable sequence to allow handling of
|
||||
* pending interrupts.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[in] enable
|
||||
* A mask specifying which NVIC interrupts to briefly enable.
|
||||
*
|
||||
* @note
|
||||
* Usually used within an NVIC mask section.
|
||||
******************************************************************************/
|
||||
void CORE_YieldNvicMask(const CORE_nvicMask_t *enable)
|
||||
{
|
||||
CORE_nvicMask_t nvicMask;
|
||||
|
||||
// Get current NVIC enable mask.
|
||||
CORE_CRITICAL_SECTION(
|
||||
nvicMask = *(CORE_nvicMask_t*)((uint32_t)&NVIC->ISER[0]);
|
||||
)
|
||||
|
||||
// Make a mask with bits set for those interrupts that are currently
|
||||
// disabled but are set in the enable mask.
|
||||
#if (CORE_NVIC_REG_WORDS == 1)
|
||||
nvicMask.a[0] &= enable->a[0];
|
||||
nvicMask.a[0] = ~nvicMask.a[0] & enable->a[0];
|
||||
|
||||
if (nvicMask.a[0] != 0) {
|
||||
#elif (CORE_NVIC_REG_WORDS == 2)
|
||||
nvicMask.a[0] &= enable->a[0];
|
||||
nvicMask.a[1] &= enable->a[1];
|
||||
nvicMask.a[0] = ~nvicMask.a[0] & enable->a[0];
|
||||
nvicMask.a[1] = ~nvicMask.a[1] & enable->a[1];
|
||||
|
||||
if ((nvicMask.a[0] != 0U) || (nvicMask.a[1] != 0U)) {
|
||||
#elif (CORE_NVIC_REG_WORDS == 3)
|
||||
nvicMask.a[0] &= enable->a[0];
|
||||
nvicMask.a[1] &= enable->a[1];
|
||||
nvicMask.a[2] &= enable->a[2];
|
||||
nvicMask.a[0] = ~nvicMask.a[0] & enable->a[0];
|
||||
nvicMask.a[1] = ~nvicMask.a[1] & enable->a[1];
|
||||
nvicMask.a[2] = ~nvicMask.a[2] & enable->a[2];
|
||||
|
||||
if ((nvicMask.a[0] != 0U) || (nvicMask.a[1] != 0U) || (nvicMask.a[2] != 0U)) {
|
||||
#endif
|
||||
|
||||
// Enable previously disabled interrupts.
|
||||
*(CORE_nvicMask_t*)((uint32_t)&NVIC->ISER[0]) = nvicMask;
|
||||
|
||||
// Disable those interrupts again.
|
||||
*(CORE_nvicMask_t*)((uint32_t)&NVIC->ICER[0]) = nvicMask;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Utility function to set an IRQn bit in a NVIC enable/disable mask.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
* Use sl_interrupt_manager_enable from the interrupt_manager service to replace
|
||||
* the calls to this API. Note that each interrupts will need to be enabled
|
||||
* individually instead of using a mask.
|
||||
*
|
||||
* @param[in] irqN
|
||||
* The IRQn_Type enumerator for the interrupt.
|
||||
*
|
||||
* @param[in,out] mask
|
||||
* The mask to set the interrupt bit in.
|
||||
******************************************************************************/
|
||||
void CORE_NvicMaskSetIRQ(IRQn_Type irqN, CORE_nvicMask_t *mask)
|
||||
{
|
||||
EFM_ASSERT(((int)irqN >= 0) && ((int)irqN < EXT_IRQ_COUNT));
|
||||
mask->a[(unsigned)irqN >> 5] |= 1UL << ((unsigned)irqN & 0x1FUL);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Utility function to clear an IRQn bit in a NVIC enable/disable mask.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
* Use sl_interrupt_manager_enable from the interrupt_manager service to replace
|
||||
* the calls to this API. Note that each interrupts will need to be disabled
|
||||
* individually instead of using a mask.
|
||||
*
|
||||
* @param[in] irqN
|
||||
* The IRQn_Type enumerator for the interrupt.
|
||||
*
|
||||
* @param[in,out] mask
|
||||
* The mask to clear the interrupt bit in.
|
||||
******************************************************************************/
|
||||
void CORE_NvicMaskClearIRQ(IRQn_Type irqN, CORE_nvicMask_t *mask)
|
||||
{
|
||||
EFM_ASSERT(((int)irqN >= 0) && ((int)irqN < EXT_IRQ_COUNT));
|
||||
mask->a[(unsigned)irqN >> 5] &= ~(1UL << ((unsigned)irqN & 0x1FUL));
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the current NVIC enable mask state.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[out] mask
|
||||
* The current NVIC enable mask.
|
||||
******************************************************************************/
|
||||
void CORE_GetNvicEnabledMask(CORE_nvicMask_t *mask)
|
||||
{
|
||||
CORE_CRITICAL_SECTION(
|
||||
*mask = *(CORE_nvicMask_t*)((uint32_t)&NVIC->ISER[0]);
|
||||
)
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get NVIC disable state for a given mask.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[in] mask
|
||||
* An NVIC mask to check.
|
||||
*
|
||||
* @return
|
||||
* True if all NVIC interrupt mask bits are clear.
|
||||
******************************************************************************/
|
||||
bool CORE_GetNvicMaskDisableState(const CORE_nvicMask_t *mask)
|
||||
{
|
||||
CORE_nvicMask_t nvicMask;
|
||||
|
||||
CORE_CRITICAL_SECTION(
|
||||
nvicMask = *(CORE_nvicMask_t*)((uint32_t)&NVIC->ISER[0]);
|
||||
)
|
||||
|
||||
#if (CORE_NVIC_REG_WORDS == 1)
|
||||
return (mask->a[0] & nvicMask.a[0]) == 0U;
|
||||
|
||||
#elif (CORE_NVIC_REG_WORDS == 2)
|
||||
return ((mask->a[0] & nvicMask.a[0]) == 0U)
|
||||
&& ((mask->a[1] & nvicMask.a[1]) == 0U);
|
||||
|
||||
#elif (CORE_NVIC_REG_WORDS == 3)
|
||||
return ((mask->a[0] & nvicMask.a[0]) == 0U)
|
||||
&& ((mask->a[1] & nvicMask.a[1]) == 0U)
|
||||
&& ((mask->a[2] & nvicMask.a[2]) == 0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
/***************************************************************************//**
|
||||
* @brief Internal function to query the state an IRQ. Present for compatibility
|
||||
* during the deprecation process.
|
||||
*
|
||||
* @param[in] irqN IRQ number.
|
||||
*
|
||||
* @return True of False depending on if the IRQ is active in the NVIC or not.
|
||||
******************************************************************************/
|
||||
__STATIC_INLINE bool internal_NvicIRQDisabled(IRQn_Type irqN)
|
||||
{
|
||||
CORE_nvicMask_t *mask;
|
||||
|
||||
EFM_ASSERT(((int)irqN >= 0) && ((int)irqN < EXT_IRQ_COUNT));
|
||||
mask = (CORE_nvicMask_t*)((uint32_t)&NVIC->ISER[0]);
|
||||
return (mask->a[(unsigned)irqN >> 5U] & (1UL << ((unsigned)irqN & 0x1FUL)))
|
||||
== 0UL;
|
||||
}
|
||||
/** @endcond */
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Check if an NVIC interrupt is disabled.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK, please now use the
|
||||
* function sl_interrupt_manager_is_irq_disabled in the interrupt manager
|
||||
* service component.
|
||||
*
|
||||
* @param[in] irqN
|
||||
* The IRQn_Type enumerator for the interrupt to check.
|
||||
*
|
||||
* @return
|
||||
* True if the interrupt is disabled.
|
||||
******************************************************************************/
|
||||
bool CORE_NvicIRQDisabled(IRQn_Type irqN)
|
||||
{
|
||||
return internal_NvicIRQDisabled(irqN);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Utility function to get the handler for a specific interrupt.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK
|
||||
*
|
||||
* @param[in] irqN
|
||||
* The IRQn_Type enumerator for the interrupt.
|
||||
*
|
||||
* @return
|
||||
* The handler address.
|
||||
*
|
||||
* @note
|
||||
* Uses the interrupt vector table defined by the current VTOR register value.
|
||||
******************************************************************************/
|
||||
void *CORE_GetNvicRamTableHandler(IRQn_Type irqN)
|
||||
{
|
||||
EFM_ASSERT(((int)irqN >= -16) && ((int)irqN < EXT_IRQ_COUNT));
|
||||
return (void*)((uint32_t*)(((uint32_t*)SCB->VTOR)[(int)irqN + 16]));
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Utility function to set the handler for a specific interrupt.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK, please now use the
|
||||
* function sl_interrupt_manager_set_irq_handler in the interrupt manager
|
||||
* service component.
|
||||
*
|
||||
* @param[in] irqN
|
||||
* The IRQn_Type enumerator for the interrupt.
|
||||
*
|
||||
* @param[in] handler
|
||||
* The handler address.
|
||||
*
|
||||
* @note
|
||||
* Uses the interrupt vector table defined by the current VTOR register value.
|
||||
******************************************************************************/
|
||||
void CORE_SetNvicRamTableHandler(IRQn_Type irqN, void *handler)
|
||||
{
|
||||
EFM_ASSERT(((int)irqN >= -16) && ((int)irqN < EXT_IRQ_COUNT));
|
||||
((uint32_t*)SCB->VTOR)[(int)irqN + 16] = (uint32_t)((uint32_t*)handler);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize an interrupt vector table by copying table entries from a
|
||||
* source to a target table.
|
||||
*
|
||||
* @note This function will set a new VTOR register value.
|
||||
*
|
||||
* @param[in] sourceTable
|
||||
* The address of the source vector table.
|
||||
*
|
||||
* @param[in] sourceSize
|
||||
* A number of entries in the source vector table.
|
||||
*
|
||||
* @param[in] targetTable
|
||||
* The address of the target (new) vector table.
|
||||
*
|
||||
* @param[in] targetSize
|
||||
* A number of entries in the target vector table.
|
||||
*
|
||||
* @param[in] defaultHandler
|
||||
* An address of the interrupt handler used for target entries for which where there
|
||||
* is no corresponding source entry (i.e., the target table is larger than the source
|
||||
* table).
|
||||
*
|
||||
* @param[in] overwriteActive
|
||||
* When true, a target table entry is always overwritten with the
|
||||
* corresponding source entry. If false, a target table entry is only
|
||||
* overwritten if it is zero. This makes it possible for an application
|
||||
* to partly initialize a target table before passing it to this function.
|
||||
*
|
||||
******************************************************************************/
|
||||
void CORE_InitNvicVectorTable(uint32_t *sourceTable,
|
||||
uint32_t sourceSize,
|
||||
uint32_t *targetTable,
|
||||
uint32_t targetSize,
|
||||
void *defaultHandler,
|
||||
bool overwriteActive)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// ASSERT on non SRAM-based target table.
|
||||
EFM_ASSERT(((uint32_t)targetTable >= SRAM_BASE)
|
||||
&& ((uint32_t)targetTable < (SRAM_BASE + SRAM_SIZE)));
|
||||
|
||||
// ASSERT if misaligned with respect to the VTOR register implementation.
|
||||
#if defined(SCB_VTOR_TBLBASE_Msk)
|
||||
EFM_ASSERT(((uint32_t)targetTable & ~(SCB_VTOR_TBLOFF_Msk
|
||||
| SCB_VTOR_TBLBASE_Msk)) == 0U);
|
||||
#else
|
||||
EFM_ASSERT(((uint32_t)targetTable & ~SCB_VTOR_TBLOFF_Msk) == 0U);
|
||||
#endif
|
||||
|
||||
// ASSERT if misaligned with respect to the vector table size.
|
||||
// The vector table address must be aligned at its size rounded up to nearest 2^n.
|
||||
EFM_ASSERT(((uint32_t)targetTable
|
||||
& ((1UL << (32UL - __CLZ((targetSize * 4UL) - 1UL))) - 1UL))
|
||||
== 0UL);
|
||||
|
||||
for (i = 0; i < targetSize; i++) {
|
||||
if (overwriteActive) { // Overwrite target entries.
|
||||
if (i < sourceSize) { // targetSize <= sourceSize
|
||||
targetTable[i] = sourceTable[i];
|
||||
} else { // targetSize > sourceSize
|
||||
targetTable[i] = (uint32_t)((uint32_t*)defaultHandler);
|
||||
}
|
||||
} else { // Overwrite target entries which are 0.
|
||||
if (i < sourceSize) { // targetSize <= sourceSize
|
||||
if (targetTable[i] == 0U) {
|
||||
targetTable[i] = sourceTable[i];
|
||||
}
|
||||
} else { // targetSize > sourceSize
|
||||
if (targetTable[i] == 0U) {
|
||||
targetTable[i] = (uint32_t)((uint32_t*)defaultHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SCB->VTOR = (uint32_t)targetTable;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Check if a specific interrupt is disabled or blocked.
|
||||
*
|
||||
* @deprecated Will be removed from the Simplicity SDK, please now use the
|
||||
* function sl_interrupt_manager_is_irq_blocked in the interrupt manager
|
||||
* service component.
|
||||
*
|
||||
* @param[in] irqN
|
||||
* The IRQn_Type enumerator for the interrupt to check.
|
||||
*
|
||||
* @return
|
||||
* True if the interrupt is disabled or blocked.
|
||||
******************************************************************************/
|
||||
SL_WEAK bool CORE_IrqIsBlocked(IRQn_Type irqN)
|
||||
{
|
||||
uint32_t irqPri, activeIrq;
|
||||
|
||||
#if (__CORTEX_M >= 3)
|
||||
uint32_t basepri;
|
||||
|
||||
EFM_ASSERT((irqN >= MemoryManagement_IRQn)
|
||||
&& (irqN < (IRQn_Type)EXT_IRQ_COUNT));
|
||||
#else
|
||||
EFM_ASSERT((irqN >= SVCall_IRQn) && ((IRQn_Type)irqN < EXT_IRQ_COUNT));
|
||||
#endif
|
||||
|
||||
if ((__get_PRIMASK() & 1U) != 0U) {
|
||||
return true; // All IRQs are disabled.
|
||||
}
|
||||
|
||||
if (internal_NvicIRQDisabled(irqN)) {
|
||||
return true; // The IRQ in question is disabled.
|
||||
}
|
||||
|
||||
irqPri = NVIC_GetPriority(irqN);
|
||||
#if (__CORTEX_M >= 3)
|
||||
basepri = __get_BASEPRI();
|
||||
if ((basepri != 0U)
|
||||
&& (irqPri >= (basepri >> (8U - __NVIC_PRIO_BITS)))) {
|
||||
return true; // The IRQ in question has too low
|
||||
} // priority vs. BASEPRI.
|
||||
#endif
|
||||
|
||||
// Check if already in an interrupt handler. If so, an interrupt with a
|
||||
// higher priority (lower priority value) can preempt.
|
||||
activeIrq = (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||
if (activeIrq != 0U) {
|
||||
if (irqPri >= NVIC_GetPriority((IRQn_Type)(activeIrq - 16U))) {
|
||||
return true; // The IRQ in question has too low
|
||||
} // priority vs. current active IRQ
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @} (end addtogroup core) */
|
||||
132
Libs/platform/emlib/src/em_dbg.c
Normal file
132
Libs/platform/emlib/src/em_dbg.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Debug (DBG) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_dbg.h"
|
||||
|
||||
#if defined(CoreDebug_DHCSR_C_DEBUGEN_Msk)
|
||||
|
||||
#include "sl_assert.h"
|
||||
#include "em_cmu.h"
|
||||
#include "em_gpio.h"
|
||||
#include "em_msc.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup dbg DBG - Debug
|
||||
* @brief Debug (DBG) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the DBG peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. The Debug Interface is used to program and debug
|
||||
* Silicon Labs devices.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(GPIO_ROUTE_SWOPEN) || defined(GPIO_ROUTEPEN_SWVPEN) \
|
||||
|| defined(GPIO_TRACEROUTEPEN_SWVPEN)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable Serial Wire Output (SWO) pin.
|
||||
*
|
||||
* @details
|
||||
* The SWO pin (sometimes denoted SWV, serial wire viewer) allows for
|
||||
* miscellaneous output to be passed from the Cortex-M3 debug trace module to
|
||||
* an external debug probe. By default, the debug trace module and pin output
|
||||
* may be disabled.
|
||||
*
|
||||
* Since the SWO pin is only useful when using a debugger, a suggested use
|
||||
* of this function during startup may be:
|
||||
* @verbatim
|
||||
* if (DBG_Connected())
|
||||
* {
|
||||
* DBG_SWOEnable(1);
|
||||
* }
|
||||
* @endverbatim
|
||||
* By checking if the debugger is attached, a setup leading to a higher energy
|
||||
* consumption when the debugger is attached can be avoided when not using
|
||||
* a debugger.
|
||||
*
|
||||
* Another alternative may be to set the debugger tool chain to configure
|
||||
* the required setup (similar to the content of this function) by some
|
||||
* sort of toolchain scripting during its attach/reset procedure. In that
|
||||
* case, the above suggested code for enabling the SWO pin is not required
|
||||
* in the application.
|
||||
*
|
||||
* @param[in] location
|
||||
* A pin location used for SWO pin on the application in use.
|
||||
******************************************************************************/
|
||||
void DBG_SWOEnable(unsigned int location)
|
||||
{
|
||||
int port;
|
||||
int pin;
|
||||
|
||||
#if defined(GPIO_SWV_PORT)
|
||||
|
||||
port = GPIO_SWV_PORT;
|
||||
pin = GPIO_SWV_PIN;
|
||||
|
||||
#else
|
||||
EFM_ASSERT(location < AFCHANLOC_MAX);
|
||||
#if defined (AF_DBG_SWO_PORT)
|
||||
port = AF_DBG_SWO_PORT(location);
|
||||
pin = AF_DBG_SWO_PIN(location);
|
||||
#elif defined (AF_DBG_SWV_PORT)
|
||||
port = AF_DBG_SWV_PORT(location);
|
||||
pin = AF_DBG_SWV_PIN(location);
|
||||
|
||||
#else
|
||||
#warning "AF debug port is not defined."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Port/pin location not defined for the device. */
|
||||
if ((pin < 0) || (port < 0)) {
|
||||
EFM_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure that the auxiliary clock going to the Cortex debug trace module is enabled. */
|
||||
#if !defined(_SILICON_LABS_32B_SERIES_2)
|
||||
CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, false);
|
||||
#endif
|
||||
|
||||
/* Set the selected pin location for the SWO pin and enable it. */
|
||||
GPIO_DbgLocationSet(location);
|
||||
GPIO_DbgSWOEnable(true);
|
||||
|
||||
/* Configure the SWO pin for output. */
|
||||
GPIO_PinModeSet((GPIO_Port_TypeDef)port, pin, gpioModePushPull, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} (end addtogroup dbg) */
|
||||
#endif /* defined( CoreDebug_DHCSR_C_DEBUGEN_Msk ) */
|
||||
4319
Libs/platform/emlib/src/em_emu.c
Normal file
4319
Libs/platform/emlib/src/em_emu.c
Normal file
File diff suppressed because it is too large
Load Diff
1398
Libs/platform/emlib/src/em_eusart.c
Normal file
1398
Libs/platform/emlib/src/em_eusart.c
Normal file
File diff suppressed because it is too large
Load Diff
138
Libs/platform/emlib/src/em_gpcrc.c
Normal file
138
Libs/platform/emlib/src/em_gpcrc.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief General Purpose Cyclic Redundancy Check (GPCRC) API.
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "sl_common.h"
|
||||
#include "em_gpcrc.h"
|
||||
#include "sl_assert.h"
|
||||
|
||||
#if defined(GPCRC_PRESENT) && (GPCRC_COUNT > 0)
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup gpcrc
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
*************************** GLOBAL FUNCTIONS ******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize the General Purpose Cyclic Redundancy Check (GPCRC) module.
|
||||
*
|
||||
* @details
|
||||
* Use this function to configure the operational parameters of the GPCRC,
|
||||
* such as the polynomial to use and how the input should be preprocessed
|
||||
* before entering the CRC calculation.
|
||||
*
|
||||
* @note
|
||||
* This function will not copy the initialization value to the data register
|
||||
* to prepare for a new CRC calculation. Either call
|
||||
* @ref GPCRC_Start before each calculation or by use the
|
||||
* autoInit functionality.
|
||||
*
|
||||
* @param[in] gpcrc
|
||||
* A pointer to the GPCRC peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the initialization structure used to configure the GPCRC.
|
||||
******************************************************************************/
|
||||
void GPCRC_Init(GPCRC_TypeDef * gpcrc, const GPCRC_Init_TypeDef * init)
|
||||
{
|
||||
uint32_t polySelect;
|
||||
uint32_t revPoly = 0;
|
||||
|
||||
if (init->crcPoly == 0x04C11DB7) {
|
||||
polySelect = GPCRC_CTRL_POLYSEL_CRC32;
|
||||
} else {
|
||||
// If not using the fixed CRC-32 polynomial, use 16-bit.
|
||||
EFM_ASSERT((init->crcPoly & 0xFFFF0000UL) == 0U);
|
||||
#if defined(GPCRC_CTRL_POLYSEL_CRC16)
|
||||
polySelect = GPCRC_CTRL_POLYSEL_CRC16;
|
||||
#else
|
||||
polySelect = GPCRC_CTRL_POLYSEL_16;
|
||||
#endif
|
||||
revPoly = SL_RBIT16(init->crcPoly);
|
||||
}
|
||||
|
||||
#if defined(GPCRC_EN_EN)
|
||||
if (init->enable) {
|
||||
gpcrc->EN_SET = GPCRC_EN_EN;
|
||||
} else {
|
||||
gpcrc->EN_CLR = GPCRC_EN_EN;
|
||||
}
|
||||
|
||||
gpcrc->CTRL = (((uint32_t)init->autoInit << _GPCRC_CTRL_AUTOINIT_SHIFT)
|
||||
| ((uint32_t)init->reverseByteOrder << _GPCRC_CTRL_BYTEREVERSE_SHIFT)
|
||||
| ((uint32_t)init->reverseBits << _GPCRC_CTRL_BITREVERSE_SHIFT)
|
||||
| ((uint32_t)init->enableByteMode << _GPCRC_CTRL_BYTEMODE_SHIFT)
|
||||
| polySelect);
|
||||
#else
|
||||
gpcrc->CTRL = (((uint32_t)init->autoInit << _GPCRC_CTRL_AUTOINIT_SHIFT)
|
||||
| ((uint32_t)init->reverseByteOrder << _GPCRC_CTRL_BYTEREVERSE_SHIFT)
|
||||
| ((uint32_t)init->reverseBits << _GPCRC_CTRL_BITREVERSE_SHIFT)
|
||||
| ((uint32_t)init->enableByteMode << _GPCRC_CTRL_BYTEMODE_SHIFT)
|
||||
| polySelect
|
||||
| ((uint32_t)init->enable << _GPCRC_CTRL_EN_SHIFT));
|
||||
#endif
|
||||
|
||||
#if defined(GPCRC_CTRL_POLYSEL_CRC16)
|
||||
if (polySelect == GPCRC_CTRL_POLYSEL_CRC16) {
|
||||
#else
|
||||
if (polySelect == GPCRC_CTRL_POLYSEL_16) {
|
||||
#endif
|
||||
// Set the CRC polynomial value.
|
||||
gpcrc->POLY = revPoly & _GPCRC_POLY_POLY_MASK;
|
||||
}
|
||||
|
||||
// Load the CRC initialization value to GPCRC_INIT.
|
||||
gpcrc->INIT = init->initValue;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Reset GPCRC registers to the hardware reset state.
|
||||
*
|
||||
* @note
|
||||
* The data registers are not reset by this function.
|
||||
*
|
||||
* @param[in] gpcrc
|
||||
* A pointer to the GPCRC peripheral register block.
|
||||
******************************************************************************/
|
||||
void GPCRC_Reset(GPCRC_TypeDef * gpcrc)
|
||||
{
|
||||
gpcrc->CTRL = _GPCRC_CTRL_RESETVALUE;
|
||||
gpcrc->POLY = _GPCRC_POLY_RESETVALUE;
|
||||
gpcrc->INIT = _GPCRC_INIT_RESETVALUE;
|
||||
}
|
||||
|
||||
/** @} (end addtogroup gpcrc) */
|
||||
|
||||
#endif /* defined(GPCRC_COUNT) && (GPCRC_COUNT > 0) */
|
||||
452
Libs/platform/emlib/src/em_gpio.c
Normal file
452
Libs/platform/emlib/src/em_gpio.c
Normal file
@@ -0,0 +1,452 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief General Purpose IO (GPIO) peripheral API
|
||||
* devices.
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_gpio.h"
|
||||
|
||||
#if defined(GPIO_COUNT) && (GPIO_COUNT > 0)
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup gpio GPIO - General Purpose Input/Output
|
||||
* @brief General Purpose Input/Output (GPIO) API
|
||||
* @details
|
||||
* This module contains functions to control the GPIO peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. The GPIO peripheral is used for pin configuration
|
||||
* and direct pin manipulation and sensing as well as routing for peripheral
|
||||
* pin connections.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** Validation of the pin typically usable in assert statements. */
|
||||
#define GPIO_DRIVEMODE_VALID(mode) ((mode) <= 3)
|
||||
#define GPIO_STRENGTH_VALID(strength) (!((strength) \
|
||||
& ~(_GPIO_P_CTRL_DRIVESTRENGTH_MASK \
|
||||
| _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK)))
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Sets the pin location of the debug pins (Serial Wire interface).
|
||||
*
|
||||
* @note
|
||||
* Changing the pins used for debugging uncontrolled, may result in a lockout.
|
||||
*
|
||||
* @param[in] location
|
||||
* The debug pin location to use (0-3).
|
||||
******************************************************************************/
|
||||
void GPIO_DbgLocationSet(unsigned int location)
|
||||
{
|
||||
#if defined (_GPIO_ROUTE_SWLOCATION_MASK)
|
||||
EFM_ASSERT(location < AFCHANLOC_MAX);
|
||||
|
||||
GPIO->ROUTE = (GPIO->ROUTE & ~_GPIO_ROUTE_SWLOCATION_MASK)
|
||||
| (location << _GPIO_ROUTE_SWLOCATION_SHIFT);
|
||||
#elif defined (_GPIO_ROUTELOC0_SWVLOC_MASK)
|
||||
EFM_ASSERT(location < AFCHANLOC_MAX);
|
||||
|
||||
GPIO->ROUTELOC0 = (GPIO->ROUTELOC0 & ~_GPIO_ROUTELOC0_SWVLOC_MASK)
|
||||
| (location << _GPIO_ROUTELOC0_SWVLOC_SHIFT);
|
||||
#else
|
||||
(void)location;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (_GPIO_P_CTRL_DRIVEMODE_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Sets drive mode for a GPIO port.
|
||||
*
|
||||
* @param[in] port
|
||||
* The GPIO port to access.
|
||||
*
|
||||
* @param[in] mode
|
||||
* Drive mode to use for the port.
|
||||
******************************************************************************/
|
||||
void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode)
|
||||
{
|
||||
EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_DRIVEMODE_VALID(mode));
|
||||
|
||||
GPIO->P[port].CTRL = (GPIO->P[port].CTRL & ~(_GPIO_P_CTRL_DRIVEMODE_MASK))
|
||||
| (mode << _GPIO_P_CTRL_DRIVEMODE_SHIFT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (_GPIO_P_CTRL_DRIVESTRENGTH_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Sets the drive strength for a GPIO port.
|
||||
*
|
||||
* @param[in] port
|
||||
* The GPIO port to access.
|
||||
*
|
||||
* @param[in] strength
|
||||
* The drive strength to use for the port.
|
||||
******************************************************************************/
|
||||
void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port,
|
||||
GPIO_DriveStrength_TypeDef strength)
|
||||
{
|
||||
EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_STRENGTH_VALID(strength));
|
||||
BUS_RegMaskedWrite(&GPIO->P[port].CTRL,
|
||||
_GPIO_P_CTRL_DRIVESTRENGTH_MASK | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK,
|
||||
strength);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Configure the GPIO external pin interrupt.
|
||||
*
|
||||
* @details
|
||||
* It is recommended to disable interrupts before configuring the GPIO pin interrupt.
|
||||
* See @ref GPIO_IntDisable() for more information.
|
||||
*
|
||||
* The GPIO interrupt handler must be in place before enabling the
|
||||
* interrupt.
|
||||
*
|
||||
* Notice that any pending interrupt for the selected interrupt is cleared
|
||||
* by this function.
|
||||
*
|
||||
* @note
|
||||
* On series 0 devices, the pin number parameter is not used. The
|
||||
* pin number used on these devices is hardwired to the interrupt with the
|
||||
* same number. @n
|
||||
* On series 1 devices, the pin number can be selected freely within a group.
|
||||
* Interrupt numbers are divided into 4 groups (intNo / 4) and valid pin
|
||||
* number within the interrupt groups are:
|
||||
* 0: pins 0-3 (interrupt number 0-3)
|
||||
* 1: pins 4-7 (interrupt number 4-7)
|
||||
* 2: pins 8-11 (interrupt number 8-11)
|
||||
* 3: pins 12-15 (interrupt number 12-15)
|
||||
*
|
||||
* @param[in] port
|
||||
* The port to associate with the @p pin.
|
||||
*
|
||||
* @param[in] pin
|
||||
* The pin number on the port.
|
||||
*
|
||||
* @param[in] intNo
|
||||
* The interrupt number to trigger.
|
||||
*
|
||||
* @param[in] risingEdge
|
||||
* Set to true if the interrupt will be enabled on the rising edge. Otherwise, false.
|
||||
*
|
||||
* @param[in] fallingEdge
|
||||
* Set to true if the interrupt will be enabled on the falling edge. Otherwise, false.
|
||||
*
|
||||
* @param[in] enable
|
||||
* Set to true if the interrupt will be enabled after the configuration is complete.
|
||||
* False to leave disabled. See @ref GPIO_IntDisable() and @ref GPIO_IntEnable().
|
||||
******************************************************************************/
|
||||
void GPIO_ExtIntConfig(GPIO_Port_TypeDef port,
|
||||
unsigned int pin,
|
||||
unsigned int intNo,
|
||||
bool risingEdge,
|
||||
bool fallingEdge,
|
||||
bool enable)
|
||||
{
|
||||
#if defined (_GPIO_EXTIPSELH_MASK)
|
||||
uint32_t tmp = 0;
|
||||
#endif
|
||||
#if !defined(_GPIO_EXTIPINSELL_MASK)
|
||||
(void)pin;
|
||||
#endif
|
||||
|
||||
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
|
||||
#if defined(_GPIO_EXTIPINSELL_MASK)
|
||||
EFM_ASSERT(GPIO_INTNO_PIN_VALID(intNo, pin));
|
||||
#endif
|
||||
|
||||
/* The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls
|
||||
* pins 8-15 of the interrupt configuration. */
|
||||
if (intNo < 8) {
|
||||
BUS_RegMaskedWrite(&GPIO->EXTIPSELL,
|
||||
_GPIO_EXTIPSELL_EXTIPSEL0_MASK
|
||||
<< (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo),
|
||||
(uint32_t)port << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo));
|
||||
} else {
|
||||
#if defined(_GPIO_EXTIPSELH_MASK)
|
||||
tmp = intNo - 8;
|
||||
#if defined(_GPIO_EXTIPSELH_EXTIPSEL0_MASK)
|
||||
BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
|
||||
_GPIO_EXTIPSELH_EXTIPSEL0_MASK
|
||||
<< (_GPIO_EXTIPSELH_EXTIPSEL1_SHIFT * tmp),
|
||||
(uint32_t)port << (_GPIO_EXTIPSELH_EXTIPSEL1_SHIFT * tmp));
|
||||
#elif defined(_GPIO_EXTIPSELH_EXTIPSEL8_MASK)
|
||||
BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
|
||||
_GPIO_EXTIPSELH_EXTIPSEL8_MASK
|
||||
<< (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp),
|
||||
(uint32_t)port << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
|
||||
#else
|
||||
#error Invalid GPIO_EXTIPINSELH bit fields
|
||||
#endif
|
||||
#endif /* #if defined(_GPIO_EXTIPSELH_MASK) */
|
||||
}
|
||||
|
||||
#if defined(_GPIO_EXTIPINSELL_MASK)
|
||||
|
||||
/* The EXTIPINSELL register controls interrupt 0-7 and EXTIPINSELH controls
|
||||
* interrupt 8-15 of the interrupt/pin number mapping. */
|
||||
if (intNo < 8) {
|
||||
BUS_RegMaskedWrite(&GPIO->EXTIPINSELL,
|
||||
_GPIO_EXTIPINSELL_EXTIPINSEL0_MASK
|
||||
<< (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo),
|
||||
(uint32_t)((pin % 4) & _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK)
|
||||
<< (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo));
|
||||
} else {
|
||||
#if defined (_GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
|
||||
BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
|
||||
_GPIO_EXTIPINSELH_EXTIPINSEL8_MASK
|
||||
<< (_GPIO_EXTIPINSELH_EXTIPINSEL9_SHIFT * tmp),
|
||||
(uint32_t)((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
|
||||
<< (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
|
||||
#endif
|
||||
#if defined (_GPIO_EXTIPINSELH_EXTIPINSEL0_MASK)
|
||||
BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
|
||||
_GPIO_EXTIPINSELH_EXTIPINSEL0_MASK
|
||||
<< (_GPIO_EXTIPINSELH_EXTIPINSEL1_SHIFT * tmp),
|
||||
(uint32_t)((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL0_MASK)
|
||||
<< (_GPIO_EXTIPSELH_EXTIPSEL1_SHIFT * tmp));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Enable/disable the rising edge interrupt. */
|
||||
BUS_RegBitWrite(&(GPIO->EXTIRISE), intNo, risingEdge);
|
||||
|
||||
/* Enable/disable the falling edge interrupt. */
|
||||
BUS_RegBitWrite(&(GPIO->EXTIFALL), intNo, fallingEdge);
|
||||
|
||||
/* Clear any pending interrupt. */
|
||||
GPIO_IntClear(1 << intNo);
|
||||
|
||||
/* Finally enable/disable interrupt. */
|
||||
BUS_RegBitWrite(&(GPIO->IEN), intNo, enable);
|
||||
}
|
||||
|
||||
#if _SILICON_LABS_32B_SERIES > 0
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Configure EM4WU pins as external level-sensitive interrupts.
|
||||
*
|
||||
* @details
|
||||
* It is recommended to disable interrupts before configuring the GPIO pin interrupt.
|
||||
* See @ref GPIO_IntDisable() for more information.
|
||||
*
|
||||
* The GPIO interrupt handler must be in place before enabling the
|
||||
* interrupt.
|
||||
*
|
||||
* Notice that any pending interrupt for the selected interrupt is cleared
|
||||
* by this function.
|
||||
*
|
||||
* @note
|
||||
* The selected port/pin must be mapped to an existant EM4WU interrupt.
|
||||
* Each EM4WU signal is connected to a fixed pin.
|
||||
* Refer to the Alternate Function Table in the device Datasheet for the
|
||||
* location of each EM4WU signal. For example, on xG22 device, the interrupt
|
||||
* of EM4WU6 is fixed to pin PC00.
|
||||
*
|
||||
* @param[in] port
|
||||
* The port to associate with the @p pin.
|
||||
*
|
||||
* @param[in] pin
|
||||
* The pin number on the port.
|
||||
*
|
||||
* @param[in] intNo
|
||||
* The EM4WU interrupt number to trigger.
|
||||
*
|
||||
* @param[in] polarity
|
||||
* true = Active high level-sensitive interrupt.
|
||||
* false = Active low level-sensitive interrupt.
|
||||
*
|
||||
* @param[in] enable
|
||||
* Set to true if the interrupt will be enabled after the configuration is complete.
|
||||
* False to leave disabled. See @ref GPIO_IntDisable() and @ref GPIO_IntEnable().
|
||||
******************************************************************************/
|
||||
void GPIO_EM4WUExtIntConfig(GPIO_Port_TypeDef port,
|
||||
unsigned int pin,
|
||||
uint32_t intNo,
|
||||
bool polarity,
|
||||
bool enable)
|
||||
{
|
||||
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
|
||||
|
||||
// GPIO pin mode set.
|
||||
GPIO_PinModeSet(port, pin, gpioModeInputPullFilter, (unsigned int)!polarity);
|
||||
|
||||
// Enable EM4WU function and set polarity
|
||||
uint32_t polarityMask = (uint32_t)polarity << (intNo + _GPIO_EM4WUEN_EM4WUEN_SHIFT);
|
||||
uint32_t pinmask = 1UL << (intNo + _GPIO_EM4WUEN_EM4WUEN_SHIFT);
|
||||
|
||||
GPIO_EM4EnablePinWakeup(pinmask, polarityMask);
|
||||
|
||||
// Enable EM4WU interrupt
|
||||
#if defined(_SILICON_LABS_32B_SERIES_1)
|
||||
BUS_RegBitWrite(&(GPIO->IEN), intNo + _GPIO_IEN_EM4WU_SHIFT, enable);
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
|
||||
BUS_RegBitWrite(&(GPIO->IEN), intNo + _GPIO_IEN_EM4WUIEN_SHIFT, enable);
|
||||
#else
|
||||
BUS_RegBitWrite(&(GPIO->IEN), intNo + _GPIO_IEN_EM4WUIEN0_SHIFT, enable);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the mode for a GPIO pin.
|
||||
*
|
||||
* @param[in] port
|
||||
* The GPIO port to access.
|
||||
*
|
||||
* @param[in] pin
|
||||
* The pin number in the port.
|
||||
*
|
||||
* @param[in] mode
|
||||
* The desired pin mode.
|
||||
*
|
||||
* @param[in] out
|
||||
* A value to set for the pin in the DOUT register. The DOUT setting is important for
|
||||
* some input mode configurations to determine the pull-up/down direction.
|
||||
******************************************************************************/
|
||||
void GPIO_PinModeSet(GPIO_Port_TypeDef port,
|
||||
unsigned int pin,
|
||||
GPIO_Mode_TypeDef mode,
|
||||
unsigned int out)
|
||||
{
|
||||
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
|
||||
|
||||
/* If disabling a pin, do not modify DOUT to reduce the chance of */
|
||||
/* a glitch/spike (may not be sufficient precaution in all use cases). */
|
||||
if (mode != gpioModeDisabled) {
|
||||
if (out) {
|
||||
GPIO_PinOutSet(port, pin);
|
||||
} else {
|
||||
GPIO_PinOutClear(port, pin);
|
||||
}
|
||||
}
|
||||
|
||||
/* There are two registers controlling the pins for each port. The MODEL
|
||||
* register controls pins 0-7 and MODEH controls pins 8-15. */
|
||||
if (pin < 8) {
|
||||
// Cast parameter [mode] to 32 bits to fix C99 Undefined Behavior (see SEI CERT C INT34-C)
|
||||
// Compiler assigned 8 bits for enum. Same thing for other branch.
|
||||
BUS_RegMaskedWrite(&(GPIO->P[port].MODEL), 0xFu << (pin * 4), (uint32_t)mode << (pin * 4));
|
||||
} else {
|
||||
BUS_RegMaskedWrite(&(GPIO->P[port].MODEH), 0xFu << ((pin - 8) * 4), (uint32_t)mode << ((pin - 8) * 4));
|
||||
}
|
||||
|
||||
if (mode == gpioModeDisabled) {
|
||||
if (out) {
|
||||
GPIO_PinOutSet(port, pin);
|
||||
} else {
|
||||
GPIO_PinOutClear(port, pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the mode for a GPIO pin.
|
||||
*
|
||||
* @param[in] port
|
||||
* The GPIO port to access.
|
||||
*
|
||||
* @param[in] pin
|
||||
* The pin number in the port.
|
||||
*
|
||||
* @return
|
||||
* The pin mode.
|
||||
******************************************************************************/
|
||||
GPIO_Mode_TypeDef GPIO_PinModeGet(GPIO_Port_TypeDef port,
|
||||
unsigned int pin)
|
||||
{
|
||||
EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
|
||||
|
||||
if (pin < 8) {
|
||||
return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEL >> (pin * 4)) & 0xF);
|
||||
} else {
|
||||
return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEH >> ((pin - 8) * 4)) & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_GPIO_EM4WUEN_MASK)
|
||||
/**************************************************************************//**
|
||||
* @brief
|
||||
* Enable GPIO pin wake-up from EM4. When the function exits,
|
||||
* EM4 mode can be safely entered.
|
||||
*
|
||||
* @note
|
||||
* It is assumed that the GPIO pin modes are set correctly.
|
||||
* Valid modes are @ref gpioModeInput and @ref gpioModeInputPull.
|
||||
*
|
||||
* @param[in] pinmask
|
||||
* A bitmask containing the bitwise logic OR of which GPIO pin(s) to enable.
|
||||
* See Reference Manuals for a pinmask to the GPIO port/pin mapping.
|
||||
* @param[in] polaritymask
|
||||
* A bitmask containing the bitwise logic OR of GPIO pin(s) wake-up polarity.
|
||||
* See Reference Manuals for pinmask-to-GPIO port/pin mapping.
|
||||
*****************************************************************************/
|
||||
void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask)
|
||||
{
|
||||
EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0);
|
||||
|
||||
#if defined(_GPIO_EM4WUPOL_MASK)
|
||||
EFM_ASSERT((polaritymask & ~_GPIO_EM4WUPOL_MASK) == 0);
|
||||
GPIO->EM4WUPOL &= ~pinmask; /* Set the wakeup polarity. */
|
||||
GPIO->EM4WUPOL |= pinmask & polaritymask;
|
||||
#elif defined(_GPIO_EXTILEVEL_MASK)
|
||||
EFM_ASSERT((polaritymask & ~_GPIO_EXTILEVEL_MASK) == 0);
|
||||
GPIO->EXTILEVEL &= ~pinmask;
|
||||
GPIO->EXTILEVEL |= pinmask & polaritymask;
|
||||
#endif
|
||||
GPIO->EM4WUEN |= pinmask; /* Enable wakeup. */
|
||||
|
||||
GPIO_EM4SetPinRetention(true); /* Enable the pin retention. */
|
||||
|
||||
#if defined(_GPIO_CMD_EM4WUCLR_MASK)
|
||||
GPIO->CMD = GPIO_CMD_EM4WUCLR; /* Clear the wake-up logic. */
|
||||
#else
|
||||
GPIO_IntClear(pinmask);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} (end addtogroup gpio) */
|
||||
|
||||
#endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */
|
||||
940
Libs/platform/emlib/src/em_i2c.c
Normal file
940
Libs/platform/emlib/src/em_i2c.c
Normal file
@@ -0,0 +1,940 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Inter-integrated Circuit (I2C) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_i2c.h"
|
||||
#if defined(I2C_COUNT) && (I2C_COUNT > 0)
|
||||
|
||||
#include "em_cmu.h"
|
||||
#include "em_bus.h"
|
||||
#include "sl_assert.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup i2c I2C - Inter-Integrated Circuit
|
||||
* @brief Inter-integrated Circuit (I2C) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the I2C peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. The I2C interface allows communication on I2C
|
||||
* buses with the lowest energy consumption possible.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** Validation of the I2C register block pointer reference for assert statements. */
|
||||
#if (I2C_COUNT == 1)
|
||||
#define I2C_REF_VALID(ref) ((ref) == I2C0)
|
||||
#elif (I2C_COUNT == 2)
|
||||
#define I2C_REF_VALID(ref) (((ref) == I2C0) || ((ref) == I2C1))
|
||||
#elif (I2C_COUNT == 3)
|
||||
#define I2C_REF_VALID(ref) (((ref) == I2C0) || ((ref) == I2C1) || ((ref) == I2C2))
|
||||
#elif (I2C_COUNT == 4)
|
||||
#define I2C_REF_VALID(ref) (((ref) == I2C0) || ((ref) == I2C1) || ((ref) == I2C2) || ((ref) == I2C3))
|
||||
#endif
|
||||
|
||||
/** Error flags indicating that the I2C transfer has failed. */
|
||||
/* Notice that I2C_IF_TXOF (transmit overflow) is not really possible with */
|
||||
/* the software-supporting master mode. Likewise, for I2C_IF_RXUF (receive underflow) */
|
||||
/* RXUF is only likely to occur with the software if using a debugger peeking into */
|
||||
/* the RXDATA register. Therefore, those types of faults are ignored. */
|
||||
#define I2C_IF_ERRORS (I2C_IF_BUSERR | I2C_IF_ARBLOST)
|
||||
#define I2C_IEN_ERRORS (I2C_IEN_BUSERR | I2C_IEN_ARBLOST)
|
||||
|
||||
/* Maximum I2C transmission rate constant. */
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
#if defined(_EFM32_HAPPY_FAMILY) || defined(_EFM32_ZERO_FAMILY)
|
||||
#define I2C_CR_MAX 8
|
||||
#else
|
||||
#define I2C_CR_MAX 4
|
||||
#endif
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
#define I2C_CR_MAX 8
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
#define I2C_CR_MAX 8
|
||||
#else
|
||||
#warning "Max I2C transmission rate constant is not defined"
|
||||
#endif
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
******************************** ENUMS ************************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** Master mode transfer states. */
|
||||
typedef enum {
|
||||
i2cStateStartAddrSend, /**< Send start + (first part of) address. */
|
||||
i2cStateAddrWFAckNack, /**< Wait for ACK/NACK on (the first part of) address. */
|
||||
i2cStateAddrWF2ndAckNack, /**< Wait for ACK/NACK on the second part of a 10 bit address. */
|
||||
i2cStateRStartAddrSend, /**< Send a repeated start + (first part of) address. */
|
||||
i2cStateRAddrWFAckNack, /**< Wait for ACK/NACK on an address sent after a repeated start. */
|
||||
i2cStateDataSend, /**< Send data. */
|
||||
i2cStateDataWFAckNack, /**< Wait for ACK/NACK on data sent. */
|
||||
i2cStateWFData, /**< Wait for data. */
|
||||
i2cStateWFStopSent, /**< Wait for STOP to have been transmitted. */
|
||||
i2cStateDone /**< Transfer completed successfully. */
|
||||
} I2C_TransferState_TypeDef;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* STRUCTS ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** Structure used to store state information on an ongoing master mode transfer. */
|
||||
typedef struct {
|
||||
/** Current state. */
|
||||
I2C_TransferState_TypeDef state;
|
||||
|
||||
/** Result return code. */
|
||||
I2C_TransferReturn_TypeDef result;
|
||||
|
||||
/** Offset in the current sequence buffer. */
|
||||
uint16_t offset;
|
||||
|
||||
/* Index to the current sequence buffer in use. */
|
||||
uint8_t bufIndx;
|
||||
|
||||
/** Reference to the I2C transfer sequence definition provided by the user. */
|
||||
I2C_TransferSeq_TypeDef *seq;
|
||||
} I2C_Transfer_TypeDef;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
***************************** LOCAL DATA *******^**************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/**
|
||||
* Lookup table for Nlow + Nhigh setting defined by CLHR. Set the undefined
|
||||
* index (0x3) to reflect a default setting just in case.
|
||||
*/
|
||||
static const uint8_t i2cNSum[] = { 4 + 4, 6 + 3, 11 + 6, 4 + 4 };
|
||||
|
||||
/** A transfer state information for an ongoing master mode transfer. */
|
||||
static I2C_Transfer_TypeDef i2cTransfer[I2C_COUNT];
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** LOCAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Empty received data buffer.
|
||||
******************************************************************************/
|
||||
static void flushRx(I2C_TypeDef *i2c)
|
||||
{
|
||||
while (i2c->STATUS & I2C_STATUS_RXDATAV) {
|
||||
i2c->RXDATA;
|
||||
}
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
/* SW needs to clear RXDATAV IF on Series 2 devices.
|
||||
Flag is kept high by HW if buffer is not empty. */
|
||||
I2C_IntClear(i2c, I2C_IF_RXDATAV);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the current configured I2C bus frequency.
|
||||
*
|
||||
* @details
|
||||
* This frequency is only relevant when acting as master.
|
||||
*
|
||||
* @note
|
||||
* The actual frequency is a real number, this function returns a rounded
|
||||
* down (truncated) integer value.
|
||||
*
|
||||
* @param[in] i2c
|
||||
* A pointer to the I2C peripheral register block.
|
||||
*
|
||||
* @return
|
||||
* The current I2C frequency in Hz.
|
||||
******************************************************************************/
|
||||
uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c)
|
||||
{
|
||||
uint32_t freqHfper = 0;
|
||||
uint32_t n;
|
||||
|
||||
/* Maximum frequency is given by freqScl = freqHfper/((Nlow + Nhigh)(DIV + 1) + I2C_CR_MAX)
|
||||
* For more details, see the reference manual
|
||||
* I2C Clock Generation chapter. */
|
||||
if (i2c == I2C0) {
|
||||
freqHfper = CMU_ClockFreqGet(cmuClock_I2C0);
|
||||
#if defined(I2C1)
|
||||
} else if (i2c == I2C1) {
|
||||
freqHfper = CMU_ClockFreqGet(cmuClock_I2C1);
|
||||
#endif
|
||||
#if defined(I2C2)
|
||||
} else if (i2c == I2C2) {
|
||||
freqHfper = CMU_ClockFreqGet(cmuClock_I2C2);
|
||||
#endif
|
||||
} else {
|
||||
EFM_ASSERT(false);
|
||||
}
|
||||
|
||||
/* n = Nlow + Nhigh */
|
||||
n = (uint32_t)i2cNSum[(i2c->CTRL & _I2C_CTRL_CLHR_MASK)
|
||||
>> _I2C_CTRL_CLHR_SHIFT];
|
||||
return freqHfper / ((n * (i2c->CLKDIV + 1)) + I2C_CR_MAX);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the I2C bus frequency.
|
||||
*
|
||||
* @details
|
||||
* The bus frequency is only relevant when acting as master. The bus
|
||||
* frequency should not be set higher than the maximum frequency accepted by the
|
||||
* slowest device on the bus.
|
||||
*
|
||||
* Notice that, due to asymmetric requirements on low and high I2C clock
|
||||
* cycles in the I2C specification, the maximum frequency allowed
|
||||
* to comply with the specification may be somewhat lower than expected.
|
||||
*
|
||||
* See the reference manual, details on I2C clock generation,
|
||||
* for maximum allowed theoretical frequencies for different modes.
|
||||
*
|
||||
* @param[in] i2c
|
||||
* A pointer to the I2C peripheral register block.
|
||||
*
|
||||
* @param[in] freqRef
|
||||
* An I2C reference clock frequency in Hz that will be used. If set to 0,
|
||||
* HFPERCLK / HFPERCCLK clock is used. Setting it to a higher than actual
|
||||
* configured value has the consequence of reducing the real I2C frequency.
|
||||
*
|
||||
* @param[in] freqScl
|
||||
* A bus frequency to set (bus speed may be lower due to integer
|
||||
* prescaling). Safe (according to the I2C specification) maximum frequencies for
|
||||
* standard fast and fast+ modes are available using I2C_FREQ_ defines.
|
||||
* (Using I2C_FREQ_ defines requires corresponding setting of @p type.)
|
||||
* The slowest slave device on a bus must always be considered.
|
||||
*
|
||||
* @param[in] i2cMode
|
||||
* A clock low-to-high ratio type to use. If not using i2cClockHLRStandard,
|
||||
* make sure all devices on the bus support the specified mode. Using a
|
||||
* non-standard ratio is useful to achieve a higher bus clock in fast and
|
||||
* fast+ modes.
|
||||
******************************************************************************/
|
||||
void I2C_BusFreqSet(I2C_TypeDef *i2c,
|
||||
uint32_t freqRef,
|
||||
uint32_t freqScl,
|
||||
I2C_ClockHLR_TypeDef i2cMode)
|
||||
{
|
||||
uint32_t n, minFreq, denominator;
|
||||
int32_t div;
|
||||
|
||||
/* Avoid dividing by 0. */
|
||||
EFM_ASSERT(freqScl);
|
||||
if (!freqScl) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure mode is valid */
|
||||
i2cMode &= _I2C_CTRL_CLHR_MASK >> _I2C_CTRL_CLHR_SHIFT;
|
||||
|
||||
/* Set the CLHR (clock low-to-high ratio). */
|
||||
i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK;
|
||||
BUS_RegMaskedWrite(&i2c->CTRL,
|
||||
_I2C_CTRL_CLHR_MASK,
|
||||
i2cMode << _I2C_CTRL_CLHR_SHIFT);
|
||||
|
||||
if (freqRef == 0) {
|
||||
if (i2c == I2C0) {
|
||||
freqRef = CMU_ClockFreqGet(cmuClock_I2C0);
|
||||
#if defined(I2C1)
|
||||
} else if (i2c == I2C1) {
|
||||
freqRef = CMU_ClockFreqGet(cmuClock_I2C1);
|
||||
#endif
|
||||
#if defined(I2C2)
|
||||
} else if (i2c == I2C2) {
|
||||
freqRef = CMU_ClockFreqGet(cmuClock_I2C2);
|
||||
#endif
|
||||
} else {
|
||||
EFM_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the minumum HF peripheral clock. */
|
||||
minFreq = UINT_MAX;
|
||||
if (i2c->CTRL & I2C_CTRL_SLAVE) {
|
||||
switch (i2cMode) {
|
||||
case i2cClockHLRStandard:
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
minFreq = 4200000; break;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
minFreq = 2000000; break;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
minFreq = 2000000; break;
|
||||
#endif
|
||||
case i2cClockHLRAsymetric:
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
minFreq = 11000000; break;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
minFreq = 5000000; break;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
minFreq = 5000000; break;
|
||||
#endif
|
||||
case i2cClockHLRFast:
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
minFreq = 24400000; break;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
minFreq = 14000000; break;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
minFreq = 14000000; break;
|
||||
#endif
|
||||
default:
|
||||
/* MISRA requires the default case. */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* For master mode, platform 1 and 2 share the same
|
||||
minimum frequencies. */
|
||||
switch (i2cMode) {
|
||||
case i2cClockHLRStandard:
|
||||
minFreq = 2000000; break;
|
||||
case i2cClockHLRAsymetric:
|
||||
minFreq = 9000000; break;
|
||||
case i2cClockHLRFast:
|
||||
minFreq = 20000000; break;
|
||||
default:
|
||||
/* MISRA requires default case */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Frequency most be larger-than. */
|
||||
EFM_ASSERT(freqRef > minFreq);
|
||||
|
||||
/* SCL frequency is given by:
|
||||
* freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_CR_MAX)
|
||||
*
|
||||
* Therefore,
|
||||
* DIV = ((freqRef - (I2C_CR_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1
|
||||
*
|
||||
* For more details, see the reference manual
|
||||
* I2C Clock Generation chapter. */
|
||||
|
||||
/* n = Nlow + Nhigh */
|
||||
n = (uint32_t)i2cNSum[i2cMode];
|
||||
denominator = n * freqScl;
|
||||
|
||||
/* Explicitly ensure denominator is never zero. */
|
||||
if (denominator == 0) {
|
||||
EFM_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
/* Perform integer division so that div is rounded up. */
|
||||
div = (int32_t)(((freqRef - (I2C_CR_MAX * freqScl) + denominator - 1)
|
||||
/ denominator) - 1);
|
||||
EFM_ASSERT(div >= 0);
|
||||
EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK);
|
||||
|
||||
/* The clock divisor must be at least 1 in slave mode according to the reference */
|
||||
/* manual (in which case there is normally no need to set the bus frequency). */
|
||||
if ((i2c->CTRL & I2C_CTRL_SLAVE) && (div == 0)) {
|
||||
div = 1;
|
||||
}
|
||||
i2c->CLKDIV = (uint32_t)div;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable/disable I2C.
|
||||
*
|
||||
* @note
|
||||
* After enabling the I2C (from being disabled), the I2C is in BUSY state.
|
||||
*
|
||||
* @param[in] i2c
|
||||
* A pointer to the I2C peripheral register block.
|
||||
*
|
||||
* @param[in] enable
|
||||
* True to enable counting, false to disable.
|
||||
******************************************************************************/
|
||||
void I2C_Enable(I2C_TypeDef *i2c, bool enable)
|
||||
{
|
||||
EFM_ASSERT(I2C_REF_VALID(i2c));
|
||||
|
||||
#if defined (_I2C_EN_MASK)
|
||||
BUS_RegBitWrite(&(i2c->EN), _I2C_EN_EN_SHIFT, enable);
|
||||
#else
|
||||
BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize I2C.
|
||||
*
|
||||
* @param[in] i2c
|
||||
* A pointer to the I2C peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the I2C initialization structure.
|
||||
******************************************************************************/
|
||||
void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init)
|
||||
{
|
||||
EFM_ASSERT(I2C_REF_VALID(i2c));
|
||||
|
||||
i2c->IEN = 0;
|
||||
I2C_IntClear(i2c, _I2C_IF_MASK);
|
||||
|
||||
/* Set SLAVE select mode. */
|
||||
BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, init->master ? 0 : 1);
|
||||
|
||||
I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr);
|
||||
|
||||
I2C_Enable(i2c, init->enable);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Reset I2C 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
|
||||
* centralized setup of this feature.
|
||||
*
|
||||
* @param[in] i2c
|
||||
* A pointer to the I2C peripheral register block.
|
||||
******************************************************************************/
|
||||
void I2C_Reset(I2C_TypeDef *i2c)
|
||||
{
|
||||
// Cancel ongoing operations and clear TX buffer
|
||||
i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX | I2C_CMD_ABORT;
|
||||
i2c->CTRL = _I2C_CTRL_RESETVALUE;
|
||||
i2c->CLKDIV = _I2C_CLKDIV_RESETVALUE;
|
||||
i2c->SADDR = _I2C_SADDR_RESETVALUE;
|
||||
i2c->SADDRMASK = _I2C_SADDRMASK_RESETVALUE;
|
||||
i2c->IEN = _I2C_IEN_RESETVALUE;
|
||||
#if defined (_I2C_EN_EN_MASK)
|
||||
i2c->EN = _I2C_EN_RESETVALUE;
|
||||
#endif
|
||||
|
||||
// Empty received data buffer
|
||||
flushRx(i2c);
|
||||
I2C_IntClear(i2c, _I2C_IF_MASK);
|
||||
/* Do not reset the route register; setting should be done independently. */
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
/// @brief
|
||||
/// Continue an initiated I2C transfer (single master mode only).
|
||||
///
|
||||
/// @details
|
||||
/// This function is used repeatedly after a I2C_TransferInit() to
|
||||
/// complete a transfer. It may be used in polled mode as the below example
|
||||
/// shows:
|
||||
/// @code{.c}
|
||||
/// I2C_TransferReturn_TypeDef ret;
|
||||
///
|
||||
/// // Do a polled transfer
|
||||
/// ret = I2C_TransferInit(I2C0, seq);
|
||||
/// while (ret == i2cTransferInProgress)
|
||||
/// {
|
||||
/// ret = I2C_Transfer(I2C0);
|
||||
/// }
|
||||
/// @endcode
|
||||
/// It may also be used in interrupt driven mode, where this function is invoked
|
||||
/// from the interrupt handler. Notice that, if used in interrupt mode, NVIC
|
||||
/// interrupts must be configured and enabled for the I2C bus used. I2C
|
||||
/// peripheral specific interrupts are managed by this software.
|
||||
///
|
||||
/// @note
|
||||
/// Only single master mode is supported.
|
||||
///
|
||||
/// @param[in] i2c
|
||||
/// A pointer to the I2C peripheral register block.
|
||||
///
|
||||
/// @return
|
||||
/// Returns status for an ongoing transfer.
|
||||
/// @li #i2cTransferInProgress - indicates that transfer not finished.
|
||||
/// @li #i2cTransferDone - transfer completed successfully.
|
||||
/// @li otherwise some sort of error has occurred.
|
||||
///
|
||||
// *****************************************************************************
|
||||
I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c)
|
||||
{
|
||||
uint32_t tmp;
|
||||
uint32_t pending;
|
||||
I2C_Transfer_TypeDef *transfer;
|
||||
I2C_TransferSeq_TypeDef *seq;
|
||||
bool finished = false;
|
||||
|
||||
EFM_ASSERT(I2C_REF_VALID(i2c));
|
||||
|
||||
/* Support up to 2 I2C buses. */
|
||||
if (i2c == I2C0) {
|
||||
transfer = i2cTransfer;
|
||||
}
|
||||
#if (I2C_COUNT > 1)
|
||||
else if (i2c == I2C1) {
|
||||
transfer = i2cTransfer + 1;
|
||||
}
|
||||
#endif
|
||||
#if (I2C_COUNT > 2)
|
||||
else if (i2c == I2C2) {
|
||||
transfer = i2cTransfer + 2;
|
||||
}
|
||||
#endif
|
||||
#if (I2C_COUNT > 3)
|
||||
else if (i2c == I2C3) {
|
||||
transfer = i2cTransfer + 3;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
return i2cTransferUsageFault;
|
||||
}
|
||||
|
||||
seq = transfer->seq;
|
||||
while (!finished) {
|
||||
pending = i2c->IF;
|
||||
|
||||
/* If some sort of fault, abort transfer. */
|
||||
if (pending & I2C_IF_ERRORS) {
|
||||
if (pending & I2C_IF_ARBLOST) {
|
||||
/* If an arbitration fault, indicates either a slave device */
|
||||
/* not responding as expected, or other master which is not */
|
||||
/* supported by this software. */
|
||||
transfer->result = i2cTransferArbLost;
|
||||
} else if (pending & I2C_IF_BUSERR) {
|
||||
/* A bus error indicates a misplaced start or stop, which should */
|
||||
/* not occur in master mode controlled by this software. */
|
||||
transfer->result = i2cTransferBusErr;
|
||||
}
|
||||
|
||||
/* Ifan error occurs, it is difficult to know */
|
||||
/* an exact cause and how to resolve. It will be up to a wrapper */
|
||||
/* to determine how to handle a fault/recovery if possible. */
|
||||
transfer->state = i2cStateDone;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (transfer->state) {
|
||||
/***************************************************/
|
||||
/* Send the first start+address (first byte if 10 bit). */
|
||||
/***************************************************/
|
||||
case i2cStateStartAddrSend:
|
||||
if (seq->flags & I2C_FLAG_10BIT_ADDR) {
|
||||
tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0;
|
||||
|
||||
/* In 10 bit address mode, the address following the first */
|
||||
/* start always indicates write. */
|
||||
} else {
|
||||
tmp = (uint32_t)(seq->addr) & 0xfe;
|
||||
|
||||
if (seq->flags & I2C_FLAG_READ) {
|
||||
/* Indicate read request */
|
||||
tmp |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
transfer->state = i2cStateAddrWFAckNack;
|
||||
i2c->TXDATA = tmp;/* Data not transmitted until the START is sent. */
|
||||
i2c->CMD = I2C_CMD_START;
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/*******************************************************/
|
||||
/* Wait for ACK/NACK on the address (first byte if 10 bit). */
|
||||
/*******************************************************/
|
||||
case i2cStateAddrWFAckNack:
|
||||
if (pending & I2C_IF_NACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_NACK);
|
||||
transfer->result = i2cTransferNack;
|
||||
transfer->state = i2cStateWFStopSent;
|
||||
i2c->CMD = I2C_CMD_STOP;
|
||||
} else if (pending & I2C_IF_ACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_ACK);
|
||||
|
||||
/* If a 10 bit address, send the 2nd byte of the address. */
|
||||
if (seq->flags & I2C_FLAG_10BIT_ADDR) {
|
||||
transfer->state = i2cStateAddrWF2ndAckNack;
|
||||
i2c->TXDATA = (uint32_t)(seq->addr) & 0xff;
|
||||
} else {
|
||||
/* Determine whether receiving or sending data. */
|
||||
if (seq->flags & I2C_FLAG_READ) {
|
||||
transfer->state = i2cStateWFData;
|
||||
if (seq->buf[transfer->bufIndx].len == 1) {
|
||||
i2c->CMD = I2C_CMD_NACK;
|
||||
}
|
||||
} else {
|
||||
transfer->state = i2cStateDataSend;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/******************************************************/
|
||||
/* Wait for ACK/NACK on the second byte of a 10 bit address. */
|
||||
/******************************************************/
|
||||
case i2cStateAddrWF2ndAckNack:
|
||||
if (pending & I2C_IF_NACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_NACK);
|
||||
transfer->result = i2cTransferNack;
|
||||
transfer->state = i2cStateWFStopSent;
|
||||
i2c->CMD = I2C_CMD_STOP;
|
||||
} else if (pending & I2C_IF_ACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_ACK);
|
||||
|
||||
/* If using a plain read sequence with a 10 bit address, switch to send */
|
||||
/* a repeated start. */
|
||||
if (seq->flags & I2C_FLAG_READ) {
|
||||
transfer->state = i2cStateRStartAddrSend;
|
||||
}
|
||||
/* Otherwise, expected to write 0 or more bytes. */
|
||||
else {
|
||||
transfer->state = i2cStateDataSend;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/*******************************/
|
||||
/* Send a repeated start+address */
|
||||
/*******************************/
|
||||
case i2cStateRStartAddrSend:
|
||||
if (seq->flags & I2C_FLAG_10BIT_ADDR) {
|
||||
tmp = (uint32_t)((seq->addr >> 8) & 0x06) | 0xf0;
|
||||
} else {
|
||||
tmp = (uint32_t)(seq->addr & 0xfe);
|
||||
}
|
||||
|
||||
/* If this is a write+read combined sequence, read is about to start. */
|
||||
if (seq->flags & I2C_FLAG_WRITE_READ) {
|
||||
/* Indicate a read request. */
|
||||
tmp |= 1;
|
||||
/* If reading only one byte, prepare the NACK now before START command. */
|
||||
if (seq->buf[transfer->bufIndx].len == 1) {
|
||||
i2c->CMD = I2C_CMD_NACK;
|
||||
}
|
||||
}
|
||||
|
||||
transfer->state = i2cStateRAddrWFAckNack;
|
||||
/* The START command has to be written first since repeated start. Otherwise, */
|
||||
/* data would be sent first. */
|
||||
i2c->CMD = I2C_CMD_START;
|
||||
i2c->TXDATA = tmp;
|
||||
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/**********************************************************************/
|
||||
/* Wait for ACK/NACK on the repeated start+address (first byte if 10 bit) */
|
||||
/**********************************************************************/
|
||||
case i2cStateRAddrWFAckNack:
|
||||
if (pending & I2C_IF_NACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_NACK);
|
||||
transfer->result = i2cTransferNack;
|
||||
transfer->state = i2cStateWFStopSent;
|
||||
i2c->CMD = I2C_CMD_STOP;
|
||||
} else if (pending & I2C_IF_ACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_ACK);
|
||||
|
||||
/* Determine whether receiving or sending data. */
|
||||
if (seq->flags & I2C_FLAG_WRITE_READ) {
|
||||
transfer->state = i2cStateWFData;
|
||||
} else {
|
||||
transfer->state = i2cStateDataSend;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/*****************************/
|
||||
/* Send a data byte to the slave */
|
||||
/*****************************/
|
||||
case i2cStateDataSend:
|
||||
/* Reached end of data buffer. */
|
||||
if (transfer->offset >= seq->buf[transfer->bufIndx].len) {
|
||||
/* Move to the next message part. */
|
||||
transfer->offset = 0;
|
||||
transfer->bufIndx++;
|
||||
|
||||
/* Send a repeated start when switching to read mode on the 2nd buffer. */
|
||||
if (seq->flags & I2C_FLAG_WRITE_READ) {
|
||||
transfer->state = i2cStateRStartAddrSend;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only writing from one buffer or finished both buffers. */
|
||||
if ((seq->flags & I2C_FLAG_WRITE) || (transfer->bufIndx > 1)) {
|
||||
transfer->state = i2cStateWFStopSent;
|
||||
i2c->CMD = I2C_CMD_STOP;
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reprocess in case the next buffer is empty. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Send byte. */
|
||||
i2c->TXDATA = (uint32_t)(seq->buf[transfer->bufIndx].data[transfer->offset++]);
|
||||
transfer->state = i2cStateDataWFAckNack;
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/*********************************************************/
|
||||
/* Wait for ACK/NACK from the slave after sending data to it. */
|
||||
/*********************************************************/
|
||||
case i2cStateDataWFAckNack:
|
||||
if (pending & I2C_IF_NACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_NACK);
|
||||
transfer->result = i2cTransferNack;
|
||||
transfer->state = i2cStateWFStopSent;
|
||||
i2c->CMD = I2C_CMD_STOP;
|
||||
} else if (pending & I2C_IF_ACK) {
|
||||
I2C_IntClear(i2c, I2C_IF_ACK);
|
||||
transfer->state = i2cStateDataSend;
|
||||
continue;
|
||||
}
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/****************************/
|
||||
/* Wait for data from slave */
|
||||
/****************************/
|
||||
case i2cStateWFData:
|
||||
if (pending & I2C_IF_RXDATAV) {
|
||||
uint8_t data;
|
||||
unsigned int rxLen = seq->buf[transfer->bufIndx].len;
|
||||
|
||||
/* Must read out data not to block further progress. */
|
||||
data = (uint8_t)(i2c->RXDATA);
|
||||
|
||||
#if (defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1) \
|
||||
|| defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) \
|
||||
|| defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3))
|
||||
// Errata I2C_E303. I2C Fails to Indicate New Incoming Data.
|
||||
uint32_t status = i2c->STATUS;
|
||||
// look for invalid RXDATAV = 0 and RXFULL = 1 condition
|
||||
if (((status & I2C_IF_RXDATAV) == 0) & ((status & I2C_IF_RXFULL) != 0)) {
|
||||
// Performing a dummy read of the RXFIFO (I2C_RXDATA).
|
||||
// This restores the expected RXDATAV = 1 and RXFULL = 0 condition.
|
||||
(void)i2c->RXDATA;
|
||||
// The dummy read will also set the RXUFIF flag bit, which should be ignored and cleared.
|
||||
I2C_IntClear(i2c, I2C_IF_RXUF);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SW needs to clear RXDATAV IF on Series 2 devices.
|
||||
Flag is kept high by HW if buffer is not empty. */
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
I2C_IntClear(i2c, I2C_IF_RXDATAV);
|
||||
#endif
|
||||
|
||||
/* Make sure that there is no storing beyond the end of the buffer (just in case). */
|
||||
if (transfer->offset < rxLen) {
|
||||
seq->buf[transfer->bufIndx].data[transfer->offset++] = data;
|
||||
}
|
||||
|
||||
/* If all requested data is read, the sequence should end. */
|
||||
if (transfer->offset >= rxLen) {
|
||||
transfer->state = i2cStateWFStopSent;
|
||||
i2c->CMD = I2C_CMD_STOP;
|
||||
} else {
|
||||
/* Send ACK and wait for the next byte. */
|
||||
i2c->CMD = I2C_CMD_ACK;
|
||||
|
||||
if ( (1 < rxLen) && (transfer->offset == (rxLen - 1)) ) {
|
||||
/* If receiving more than one byte and this is the next
|
||||
to last byte, transmit the NACK now before receiving
|
||||
the last byte. */
|
||||
i2c->CMD = I2C_CMD_NACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/***********************************/
|
||||
/* Wait for STOP to have been sent */
|
||||
/***********************************/
|
||||
case i2cStateWFStopSent:
|
||||
if (pending & I2C_IF_MSTOP) {
|
||||
I2C_IntClear(i2c, I2C_IF_MSTOP);
|
||||
transfer->state = i2cStateDone;
|
||||
}
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
/******************************/
|
||||
/* An unexpected state, software fault */
|
||||
/******************************/
|
||||
default:
|
||||
transfer->result = i2cTransferSwFault;
|
||||
transfer->state = i2cStateDone;
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (transfer->state == i2cStateDone) {
|
||||
/* Disable interrupt sources when done. */
|
||||
i2c->IEN = 0;
|
||||
|
||||
/* Update the result unless a fault has already occurred. */
|
||||
if (transfer->result == i2cTransferInProgress) {
|
||||
transfer->result = i2cTransferDone;
|
||||
}
|
||||
}
|
||||
/* Until transfer is done, keep returning i2cTransferInProgress. */
|
||||
else {
|
||||
return i2cTransferInProgress;
|
||||
}
|
||||
|
||||
return transfer->result;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Prepare and start an I2C transfer (single master mode only).
|
||||
*
|
||||
* @details
|
||||
* This function must be invoked to start an I2C transfer
|
||||
* sequence. To complete the transfer, I2C_Transfer() must
|
||||
* be used either in polled mode or by adding a small driver wrapper using
|
||||
* interrupts.
|
||||
*
|
||||
* @note
|
||||
* Only single master mode is supported.
|
||||
*
|
||||
* @param[in] i2c
|
||||
* A pointer to the I2C peripheral register block.
|
||||
*
|
||||
* @param[in] seq
|
||||
* A pointer to the sequence structure defining the I2C transfer to take place. The
|
||||
* referenced structure must exist until the transfer has fully completed.
|
||||
*
|
||||
* @return
|
||||
* Returns the status for an ongoing transfer:
|
||||
* @li #i2cTransferInProgress - indicates that the transfer is not finished.
|
||||
* @li Otherwise, an error has occurred.
|
||||
******************************************************************************/
|
||||
I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
|
||||
I2C_TransferSeq_TypeDef *seq)
|
||||
{
|
||||
I2C_Transfer_TypeDef *transfer;
|
||||
|
||||
EFM_ASSERT(I2C_REF_VALID(i2c));
|
||||
EFM_ASSERT(seq);
|
||||
|
||||
/* Support up to 2 I2C buses. */
|
||||
if (i2c == I2C0) {
|
||||
transfer = i2cTransfer;
|
||||
}
|
||||
#if (I2C_COUNT > 1)
|
||||
else if (i2c == I2C1) {
|
||||
transfer = i2cTransfer + 1;
|
||||
}
|
||||
#endif
|
||||
#if (I2C_COUNT > 2)
|
||||
else if (i2c == I2C2) {
|
||||
transfer = i2cTransfer + 2;
|
||||
}
|
||||
#endif
|
||||
#if (I2C_COUNT > 3)
|
||||
else if (i2c == I2C3) {
|
||||
transfer = i2cTransfer + 3;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
return i2cTransferUsageFault;
|
||||
}
|
||||
|
||||
/* Check if in a busy state. Since this software assumes a single master, */
|
||||
/* issue an abort. The BUSY state is normal after a reset. */
|
||||
if (i2c->STATE & I2C_STATE_BUSY) {
|
||||
i2c->CMD = I2C_CMD_ABORT;
|
||||
}
|
||||
|
||||
/* Do not try to read 0 bytes. It is not */
|
||||
/* possible according to the I2C spec, since the slave will always start */
|
||||
/* sending the first byte ACK on an address. The read operation can */
|
||||
/* only be stopped by NACKing a received byte, i.e., minimum 1 byte. */
|
||||
if (((seq->flags & I2C_FLAG_READ) && !(seq->buf[0].len))
|
||||
|| ((seq->flags & I2C_FLAG_WRITE_READ) && !(seq->buf[1].len))
|
||||
) {
|
||||
return i2cTransferUsageFault;
|
||||
}
|
||||
|
||||
/* Prepare for a transfer. */
|
||||
transfer->state = i2cStateStartAddrSend;
|
||||
transfer->result = i2cTransferInProgress;
|
||||
transfer->offset = 0;
|
||||
transfer->bufIndx = 0;
|
||||
transfer->seq = seq;
|
||||
|
||||
/* Ensure buffers are empty. */
|
||||
i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
|
||||
flushRx(i2c);
|
||||
|
||||
/* Clear all pending interrupts prior to starting a transfer. */
|
||||
I2C_IntClear(i2c, _I2C_IF_MASK);
|
||||
|
||||
/* Enable relevant interrupts. */
|
||||
/* Notice that the I2C interrupt must also be enabled in the NVIC, but */
|
||||
/* that is left for an additional driver wrapper. */
|
||||
i2c->IEN |= I2C_IEN_NACK | I2C_IEN_ACK | I2C_IEN_MSTOP
|
||||
| I2C_IEN_RXDATAV | I2C_IEN_ERRORS;
|
||||
|
||||
/* Start a transfer. */
|
||||
return I2C_Transfer(i2c);
|
||||
}
|
||||
|
||||
/** @} (end addtogroup i2c) */
|
||||
#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */
|
||||
1178
Libs/platform/emlib/src/em_iadc.c
Normal file
1178
Libs/platform/emlib/src/em_iadc.c
Normal file
File diff suppressed because it is too large
Load Diff
461
Libs/platform/emlib/src/em_ldma.c
Normal file
461
Libs/platform/emlib/src/em_ldma.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Direct memory access (LDMA) module peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_ldma.h"
|
||||
|
||||
#if defined(LDMA_PRESENT) && (LDMA_COUNT == 1)
|
||||
|
||||
#include <stddef.h>
|
||||
#include "sl_assert.h"
|
||||
#include "em_bus.h"
|
||||
#include "em_cmu.h"
|
||||
#include "em_core.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup ldma
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(LDMA_IRQ_HANDLER_TEMPLATE)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* A template for an LDMA IRQ handler.
|
||||
******************************************************************************/
|
||||
void LDMA_IRQHandler(void)
|
||||
{
|
||||
uint32_t ch;
|
||||
/* Get all pending and enabled interrupts. */
|
||||
uint32_t pending = LDMA_IntGetEnabled();
|
||||
|
||||
/* Loop on an LDMA error to enable debugging. */
|
||||
while (pending & LDMA_IF_ERROR) {
|
||||
}
|
||||
|
||||
/* Iterate over all LDMA channels. */
|
||||
for (ch = 0; ch < DMA_CHAN_COUNT; ch++) {
|
||||
uint32_t mask = 0x1 << ch;
|
||||
if (pending & mask) {
|
||||
/* Clear the interrupt flag. */
|
||||
LDMA->IFC = mask;
|
||||
|
||||
/* Perform more actions here, execute callbacks, and so on. */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* De-initialize the LDMA controller.
|
||||
*
|
||||
* LDMA interrupts are disabled and the LDMA clock is stopped.
|
||||
******************************************************************************/
|
||||
void LDMA_DeInit(void)
|
||||
{
|
||||
NVIC_DisableIRQ(LDMA_IRQn);
|
||||
LDMA->IEN = 0;
|
||||
#if defined(_LDMA_CHDIS_MASK)
|
||||
LDMA->CHDIS = _LDMA_CHEN_MASK;
|
||||
#else
|
||||
LDMA->CHEN = 0;
|
||||
#endif
|
||||
#if defined(LDMA_EN_EN)
|
||||
LDMA->EN = 0;
|
||||
#if defined(LDMA_EN_DISABLING)
|
||||
while (LDMA->EN & _LDMA_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CMU_ClockEnable(cmuClock_LDMA, false);
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG) && (_SILICON_LABS_32B_SERIES_2_CONFIG > 1)
|
||||
CMU_ClockEnable(cmuClock_LDMAXBAR, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable or disable an LDMA channel request.
|
||||
*
|
||||
* @details
|
||||
* Use this function to enable or disable an LDMA channel request. This will
|
||||
* prevent the LDMA from proceeding after its current transaction if disabled.
|
||||
*
|
||||
* @param[in] ch
|
||||
* LDMA channel to enable or disable requests.
|
||||
*
|
||||
* @param[in] enable
|
||||
* If 'true', the request will be enabled. If 'false', the request will be disabled.
|
||||
******************************************************************************/
|
||||
void LDMA_EnableChannelRequest(int ch, bool enable)
|
||||
{
|
||||
EFM_ASSERT(ch < (int)DMA_CHAN_COUNT);
|
||||
|
||||
BUS_RegBitWrite(&LDMA->REQDIS, ch, !enable);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize the LDMA controller.
|
||||
*
|
||||
* @details
|
||||
* This function will disable all the LDMA channels and enable the LDMA bus
|
||||
* clock in the CMU. This function will also enable the LDMA IRQ in the NVIC
|
||||
* and set the LDMA IRQ priority to a user-configurable priority. The LDMA
|
||||
* interrupt priority is configured using the @ref LDMA_Init_t structure.
|
||||
*
|
||||
* @note
|
||||
* Since this function enables the LDMA IRQ, always add a custom
|
||||
* LDMA_IRQHandler to the application to handle any interrupts
|
||||
* from LDMA.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the initialization structure used to configure the LDMA.
|
||||
******************************************************************************/
|
||||
void LDMA_Init(const LDMA_Init_t *init)
|
||||
{
|
||||
uint32_t ldmaCtrlVal;
|
||||
EFM_ASSERT(init != NULL);
|
||||
EFM_ASSERT(!(((uint32_t)init->ldmaInitCtrlNumFixed << _LDMA_CTRL_NUMFIXED_SHIFT)
|
||||
& ~_LDMA_CTRL_NUMFIXED_MASK));
|
||||
|
||||
EFM_ASSERT(init->ldmaInitIrqPriority < (1 << __NVIC_PRIO_BITS));
|
||||
|
||||
CMU_ClockEnable(cmuClock_LDMA, true);
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG) && (_SILICON_LABS_32B_SERIES_2_CONFIG > 1)
|
||||
CMU_ClockEnable(cmuClock_LDMAXBAR, true);
|
||||
#endif
|
||||
|
||||
#if defined(LDMA_EN_EN)
|
||||
LDMA->EN = LDMA_EN_EN;
|
||||
#endif
|
||||
|
||||
ldmaCtrlVal = (uint32_t)init->ldmaInitCtrlNumFixed << _LDMA_CTRL_NUMFIXED_SHIFT;
|
||||
|
||||
#if defined(_LDMA_CTRL_SYNCPRSCLREN_SHIFT) && defined (_LDMA_CTRL_SYNCPRSSETEN_SHIFT)
|
||||
ldmaCtrlVal |= (init->ldmaInitCtrlSyncPrsClrEn << _LDMA_CTRL_SYNCPRSCLREN_SHIFT)
|
||||
| (init->ldmaInitCtrlSyncPrsSetEn << _LDMA_CTRL_SYNCPRSSETEN_SHIFT);
|
||||
#endif
|
||||
|
||||
LDMA->CTRL = ldmaCtrlVal;
|
||||
|
||||
#if defined(_LDMA_SYNCHWEN_SYNCCLREN_SHIFT) && defined (_LDMA_SYNCHWEN_SYNCSETEN_SHIFT)
|
||||
LDMA->SYNCHWEN = ((uint32_t)init->ldmaInitCtrlSyncPrsClrEn << _LDMA_SYNCHWEN_SYNCCLREN_SHIFT)
|
||||
| ((uint32_t)init->ldmaInitCtrlSyncPrsSetEn << _LDMA_SYNCHWEN_SYNCSETEN_SHIFT);
|
||||
#endif
|
||||
|
||||
#if defined(_LDMA_CHDIS_MASK)
|
||||
LDMA->CHDIS = _LDMA_CHEN_MASK;
|
||||
#else
|
||||
LDMA->CHEN = 0;
|
||||
#endif
|
||||
LDMA->DBGHALT = 0;
|
||||
LDMA->REQDIS = 0;
|
||||
|
||||
/* Enable the LDMA error interrupt. */
|
||||
LDMA->IEN = LDMA_IEN_ERROR;
|
||||
#if defined (LDMA_HAS_SET_CLEAR)
|
||||
LDMA->IF_CLR = 0xFFFFFFFFU;
|
||||
#else
|
||||
LDMA->IFC = 0xFFFFFFFFU;
|
||||
#endif
|
||||
NVIC_ClearPendingIRQ(LDMA_IRQn);
|
||||
|
||||
/* Range is 0-7, where 0 is the highest priority. */
|
||||
NVIC_SetPriority(LDMA_IRQn, init->ldmaInitIrqPriority);
|
||||
|
||||
NVIC_EnableIRQ(LDMA_IRQn);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Start a DMA transfer.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A DMA channel.
|
||||
*
|
||||
* @param[in] transfer
|
||||
* The initialization structure used to configure the transfer.
|
||||
*
|
||||
* @param[in] descriptor
|
||||
* The transfer descriptor, which can be an array of descriptors linked together.
|
||||
* Each descriptor's fields stored in RAM will be loaded into the certain
|
||||
* hardware registers at the proper time to perform the DMA transfer.
|
||||
******************************************************************************/
|
||||
void LDMA_StartTransfer(int ch,
|
||||
const LDMA_TransferCfg_t *transfer,
|
||||
const LDMA_Descriptor_t *descriptor)
|
||||
{
|
||||
#if !(defined (_LDMA_SYNCHWEN_SYNCCLREN_SHIFT) && defined (_LDMA_SYNCHWEN_SYNCSETEN_SHIFT))
|
||||
uint32_t tmp;
|
||||
#endif
|
||||
CORE_DECLARE_IRQ_STATE;
|
||||
uint32_t chMask = 1UL << (uint8_t)ch;
|
||||
|
||||
EFM_ASSERT(ch < (int)DMA_CHAN_COUNT);
|
||||
EFM_ASSERT(transfer != NULL);
|
||||
|
||||
#if defined (_LDMAXBAR_CH_REQSEL_MASK)
|
||||
EFM_ASSERT(!(transfer->ldmaReqSel & ~_LDMAXBAR_CH_REQSEL_MASK));
|
||||
#elif defined (_LDMA_CH_REQSEL_MASK)
|
||||
EFM_ASSERT(!(transfer->ldmaReqSel & ~_LDMA_CH_REQSEL_MASK));
|
||||
#endif
|
||||
|
||||
EFM_ASSERT(!(((uint32_t)transfer->ldmaCfgArbSlots << _LDMA_CH_CFG_ARBSLOTS_SHIFT)
|
||||
& ~_LDMA_CH_CFG_ARBSLOTS_MASK));
|
||||
EFM_ASSERT(!(((uint32_t)transfer->ldmaCfgSrcIncSign << _LDMA_CH_CFG_SRCINCSIGN_SHIFT)
|
||||
& ~_LDMA_CH_CFG_SRCINCSIGN_MASK));
|
||||
EFM_ASSERT(!(((uint32_t)transfer->ldmaCfgDstIncSign << _LDMA_CH_CFG_DSTINCSIGN_SHIFT)
|
||||
& ~_LDMA_CH_CFG_DSTINCSIGN_MASK));
|
||||
|
||||
/* Clear the pending channel interrupt. */
|
||||
#if defined (LDMA_HAS_SET_CLEAR)
|
||||
LDMA->IF_CLR = chMask;
|
||||
#else
|
||||
LDMA->IFC = chMask;
|
||||
#endif
|
||||
|
||||
#if defined(LDMAXBAR)
|
||||
LDMAXBAR->CH[ch].REQSEL = transfer->ldmaReqSel;
|
||||
#else
|
||||
LDMA->CH[ch].REQSEL = transfer->ldmaReqSel;
|
||||
#endif
|
||||
LDMA->CH[ch].LOOP = transfer->ldmaLoopCnt << _LDMA_CH_LOOP_LOOPCNT_SHIFT;
|
||||
LDMA->CH[ch].CFG = (transfer->ldmaCfgArbSlots << _LDMA_CH_CFG_ARBSLOTS_SHIFT)
|
||||
| (transfer->ldmaCfgSrcIncSign << _LDMA_CH_CFG_SRCINCSIGN_SHIFT)
|
||||
| (transfer->ldmaCfgDstIncSign << _LDMA_CH_CFG_DSTINCSIGN_SHIFT)
|
||||
#if defined(_LDMA_CH_CFG_SRCBUSPORT_MASK)
|
||||
| (transfer->ldmaCfgStructBusPort << _LDMA_CH_CFG_STRUCTBUSPORT_SHIFT)
|
||||
| (transfer->ldmaCfgSrcBusPort << _LDMA_CH_CFG_SRCBUSPORT_SHIFT)
|
||||
| (transfer->ldmaCfgDstBusPort << _LDMA_CH_CFG_DSTBUSPORT_SHIFT)
|
||||
#endif
|
||||
;
|
||||
|
||||
/* Set the descriptor address. */
|
||||
LDMA->CH[ch].LINK = (uint32_t)descriptor & _LDMA_CH_LINK_LINKADDR_MASK;
|
||||
|
||||
/* A critical region. */
|
||||
CORE_ENTER_ATOMIC();
|
||||
|
||||
/* Enable the channel interrupt. */
|
||||
BUS_RegMaskedSet(&LDMA->IEN, chMask);
|
||||
|
||||
if (transfer->ldmaReqDis) {
|
||||
LDMA->REQDIS |= chMask;
|
||||
}
|
||||
|
||||
if (transfer->ldmaDbgHalt) {
|
||||
LDMA->DBGHALT |= chMask;
|
||||
}
|
||||
|
||||
#if defined (_LDMA_SYNCHWEN_SYNCCLREN_SHIFT) && defined (_LDMA_SYNCHWEN_SYNCSETEN_SHIFT)
|
||||
|
||||
LDMA->SYNCHWEN_CLR =
|
||||
(((uint32_t)transfer->ldmaCtrlSyncPrsClrOff << _LDMA_SYNCHWEN_SYNCCLREN_SHIFT)
|
||||
| ((uint32_t)transfer->ldmaCtrlSyncPrsSetOff << _LDMA_SYNCHWEN_SYNCSETEN_SHIFT))
|
||||
& _LDMA_SYNCHWEN_MASK;
|
||||
|
||||
LDMA->SYNCHWEN_SET =
|
||||
(((uint32_t)transfer->ldmaCtrlSyncPrsClrOn << _LDMA_SYNCHWEN_SYNCCLREN_SHIFT)
|
||||
| ((uint32_t)transfer->ldmaCtrlSyncPrsSetOn << _LDMA_SYNCHWEN_SYNCSETEN_SHIFT))
|
||||
& _LDMA_SYNCHWEN_MASK;
|
||||
|
||||
#elif defined (_LDMA_CTRL_SYNCPRSCLREN_SHIFT) && defined (_LDMA_CTRL_SYNCPRSSETEN_SHIFT)
|
||||
|
||||
tmp = LDMA->CTRL;
|
||||
|
||||
if (transfer->ldmaCtrlSyncPrsClrOff) {
|
||||
tmp &= ~_LDMA_CTRL_SYNCPRSCLREN_MASK
|
||||
| (~transfer->ldmaCtrlSyncPrsClrOff << _LDMA_CTRL_SYNCPRSCLREN_SHIFT);
|
||||
}
|
||||
|
||||
if (transfer->ldmaCtrlSyncPrsClrOn) {
|
||||
tmp |= transfer->ldmaCtrlSyncPrsClrOn << _LDMA_CTRL_SYNCPRSCLREN_SHIFT;
|
||||
}
|
||||
|
||||
if (transfer->ldmaCtrlSyncPrsSetOff) {
|
||||
tmp &= ~_LDMA_CTRL_SYNCPRSSETEN_MASK
|
||||
| (~transfer->ldmaCtrlSyncPrsSetOff << _LDMA_CTRL_SYNCPRSSETEN_SHIFT);
|
||||
}
|
||||
|
||||
if (transfer->ldmaCtrlSyncPrsSetOn) {
|
||||
tmp |= transfer->ldmaCtrlSyncPrsSetOn << _LDMA_CTRL_SYNCPRSSETEN_SHIFT;
|
||||
}
|
||||
|
||||
LDMA->CTRL = tmp;
|
||||
|
||||
#else
|
||||
|
||||
#error "SYNC Set and SYNC Clear not defined"
|
||||
|
||||
#endif
|
||||
|
||||
BUS_RegMaskedClear(&LDMA->CHDONE, chMask); /* Clear the done flag. */
|
||||
LDMA->LINKLOAD = chMask; /* Start a transfer by loading the descriptor. */
|
||||
|
||||
/* A critical region end. */
|
||||
CORE_EXIT_ATOMIC();
|
||||
}
|
||||
|
||||
#if defined(_LDMA_CH_CTRL_EXTEND_MASK)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Start an extended DMA transfer.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A DMA channel.
|
||||
*
|
||||
* @param[in] transfer
|
||||
* The initialization structure used to configure the transfer.
|
||||
*
|
||||
* @param[in] descriptor_ext
|
||||
* The extended transfer descriptor, which can be an array of descriptors
|
||||
* linked together. Each descriptor's fields stored in RAM will be loaded
|
||||
* into the certain hardware registers at the proper time to perform the DMA
|
||||
* transfer.
|
||||
******************************************************************************/
|
||||
void LDMA_StartTransferExtend(int ch,
|
||||
const LDMA_TransferCfg_t *transfer,
|
||||
const LDMA_DescriptorExtend_t *descriptor_ext)
|
||||
{
|
||||
// Ensure destination interleaving supported for given channel.
|
||||
EFM_ASSERT(((1 << ch) & LDMA_ILCHNL));
|
||||
|
||||
LDMA_StartTransfer(ch,
|
||||
transfer,
|
||||
(const LDMA_Descriptor_t *)descriptor_ext);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Stop a DMA transfer.
|
||||
*
|
||||
* @note
|
||||
* The DMA will complete the current AHB burst transfer before stopping.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A DMA channel to stop.
|
||||
******************************************************************************/
|
||||
void LDMA_StopTransfer(int ch)
|
||||
{
|
||||
uint32_t chMask = 1UL << (uint8_t)ch;
|
||||
|
||||
EFM_ASSERT(ch < (int)DMA_CHAN_COUNT);
|
||||
|
||||
#if defined(_LDMA_CHDIS_MASK)
|
||||
CORE_ATOMIC_SECTION(
|
||||
LDMA->IEN &= ~chMask;
|
||||
LDMA->CHDIS = chMask;
|
||||
)
|
||||
#else
|
||||
CORE_ATOMIC_SECTION(
|
||||
LDMA->IEN &= ~chMask;
|
||||
BUS_RegMaskedClear(&LDMA->CHEN, chMask);
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Check if a DMA transfer has completed.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A DMA channel to check.
|
||||
*
|
||||
* @return
|
||||
* True if transfer has completed, false if not.
|
||||
******************************************************************************/
|
||||
bool LDMA_TransferDone(int ch)
|
||||
{
|
||||
bool retVal = false;
|
||||
uint32_t chMask = 1UL << (uint8_t)ch;
|
||||
|
||||
EFM_ASSERT(ch < (int)DMA_CHAN_COUNT);
|
||||
|
||||
#if defined(_LDMA_CHSTATUS_MASK)
|
||||
CORE_ATOMIC_SECTION(
|
||||
if (((LDMA->CHSTATUS & chMask) == 0) && ((LDMA->CHDONE & chMask) == chMask)) {
|
||||
retVal = true;
|
||||
}
|
||||
)
|
||||
#else
|
||||
CORE_ATOMIC_SECTION(
|
||||
if (((LDMA->CHEN & chMask) == 0) && ((LDMA->CHDONE & chMask) == chMask)) {
|
||||
retVal = true;
|
||||
}
|
||||
)
|
||||
#endif
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the number of items remaining in a transfer.
|
||||
*
|
||||
* @note
|
||||
* This function does not take into account that a DMA transfer with
|
||||
* a chain of linked transfers might be ongoing. It will only check the
|
||||
* count for the current transfer.
|
||||
*
|
||||
* @param[in] ch
|
||||
* The channel number of the transfer to check.
|
||||
*
|
||||
* @return
|
||||
* A number of items remaining in the transfer.
|
||||
******************************************************************************/
|
||||
uint32_t LDMA_TransferRemainingCount(int ch)
|
||||
{
|
||||
uint32_t remaining, done;
|
||||
uint32_t chMask = 1UL << (uint8_t)ch;
|
||||
|
||||
EFM_ASSERT(ch < (int)DMA_CHAN_COUNT);
|
||||
|
||||
CORE_ATOMIC_SECTION(
|
||||
done = LDMA->CHDONE;
|
||||
remaining = LDMA->CH[ch].CTRL;
|
||||
)
|
||||
|
||||
done &= chMask;
|
||||
|
||||
if (done) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
remaining = (remaining & _LDMA_CH_CTRL_XFERCNT_MASK)
|
||||
>> _LDMA_CH_CTRL_XFERCNT_SHIFT;
|
||||
|
||||
/* +1 because XFERCNT is 0-based. */
|
||||
return remaining + 1;
|
||||
}
|
||||
|
||||
/** @} (end addtogroup ldma) */
|
||||
#endif /* defined( LDMA_PRESENT ) && ( LDMA_COUNT == 1 ) */
|
||||
685
Libs/platform/emlib/src/em_letimer.c
Normal file
685
Libs/platform/emlib/src/em_letimer.c
Normal file
@@ -0,0 +1,685 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Low Energy Timer (LETIMER) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_letimer.h"
|
||||
#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0)
|
||||
#include "em_cmu.h"
|
||||
#include "sl_assert.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup letimer LETIMER - Low Energy Timer
|
||||
* @brief Low Energy Timer (LETIMER) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the LETIMER peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. The LETIMER is a down-counter that can keep track
|
||||
* of time and output configurable waveforms.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** A validation of the valid comparator register for assert statements. */
|
||||
#define LETIMER_COMP_REG_VALID(reg) (((reg) <= 1))
|
||||
|
||||
/** A validation of the LETIMER register block pointer reference for assert statements. */
|
||||
#if (LETIMER_COUNT == 1)
|
||||
#define LETIMER_REF_VALID(ref) ((ref) == LETIMER0)
|
||||
#elif (LETIMER_COUNT == 2)
|
||||
#define LETIMER_REF_VALID(ref) (((ref) == LETIMER0) || ((ref) == LETIMER1))
|
||||
#else
|
||||
#error Undefined number of analog comparators (ACMP).
|
||||
#endif
|
||||
|
||||
/** A validation of the valid repeat counter register for assert statements. */
|
||||
#define LETIMER_REP_REG_VALID(reg) (((reg) <= 1))
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** LOCAL FUNCTIONS ********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Wait for an ongoing sync of register(s) to the low-frequency domain to complete.
|
||||
*
|
||||
* @note
|
||||
* See the reference manual chapter about Access to Low Energy Peripherals
|
||||
* (Asynchronos Registers) for details.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] mask
|
||||
* A 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 a deadlock if modifying the same register twice when freeze mode is */
|
||||
/* activated. */
|
||||
if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Wait for any pending write operation to complete. */
|
||||
while (letimer->SYNCBUSY & mask) {
|
||||
}
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the LETIMER compare register value.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] comp
|
||||
* A compare register to get, either 0 or 1.
|
||||
*
|
||||
* @return
|
||||
* A 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 the selected compare value. */
|
||||
switch (comp) {
|
||||
case 0:
|
||||
#if defined(LETIMER_SYNCBUSY_COMP0)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_COMP0);
|
||||
#endif
|
||||
ret = letimer->COMP0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
#if defined(LETIMER_SYNCBUSY_COMP1)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_COMP1);
|
||||
#endif
|
||||
ret = letimer->COMP1;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* An unknown compare register selected. */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get LETIMER counter value.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* Pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @return
|
||||
* Current LETIMER counter value.
|
||||
******************************************************************************/
|
||||
uint32_t LETIMER_CounterGet(LETIMER_TypeDef *letimer)
|
||||
{
|
||||
#if defined(LETIMER_SYNCBUSY_CNT)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CNT);
|
||||
#endif
|
||||
return letimer->CNT;
|
||||
}
|
||||
|
||||
#if !defined(_EFM32_GECKO_FAMILY)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set LETIMER counter value.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* Pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] value
|
||||
* New counter value.
|
||||
******************************************************************************/
|
||||
void LETIMER_CounterSet(LETIMER_TypeDef *letimer, uint32_t value)
|
||||
{
|
||||
#if defined(LETIMER_SYNCBUSY_CNT)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CNT);
|
||||
#endif
|
||||
letimer->CNT = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the 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
|
||||
* comments in the LETIMER_Sync() internal function call.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] comp
|
||||
* A compare register to set, either 0 or 1.
|
||||
*
|
||||
* @param[in] value
|
||||
* An initialization value (<= 0x0000ffff).
|
||||
******************************************************************************/
|
||||
void LETIMER_CompareSet(LETIMER_TypeDef *letimer,
|
||||
unsigned int comp,
|
||||
uint32_t value)
|
||||
{
|
||||
EFM_ASSERT(LETIMER_REF_VALID(letimer)
|
||||
&& LETIMER_COMP_REG_VALID(comp)
|
||||
&& ((value & ~(_LETIMER_COMP0_COMP0_MASK
|
||||
>> _LETIMER_COMP0_COMP0_SHIFT))
|
||||
== 0));
|
||||
|
||||
/* Initialize the selected compare value. */
|
||||
switch (comp) {
|
||||
case 0:
|
||||
#if defined(LETIMER_SYNCBUSY_COMP0)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_COMP0);
|
||||
#endif
|
||||
letimer->COMP0 = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
#if defined(LETIMER_SYNCBUSY_COMP1)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_COMP1);
|
||||
#endif
|
||||
letimer->COMP1 = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* An unknown compare register selected, abort. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @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 comments in the
|
||||
* LETIMER_Sync() internal function call.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the 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(LETIMER_SYNCBUSY_CMD)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CMD);
|
||||
#elif defined (LETIMER_SYNCBUSY_START) && defined (LETIMER_SYNCBUSY_STOP)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_STOP | LETIMER_SYNCBUSY_START);
|
||||
#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 the LF domain to complete (Normally
|
||||
* synchronization will not be in progress.) However, for this reason, when
|
||||
* using freeze mode, modifications of registers requiring the LF synchronization
|
||||
* should be done within one freeze enable/disable block to avoid unecessary
|
||||
* stalling.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the 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 the LF
|
||||
* domain
|
||||
******************************************************************************/
|
||||
void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
/*
|
||||
* Wait for any ongoing LF synchronization to complete 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 using this function if configuring the LETIMER to start when
|
||||
* initialization is complete.
|
||||
*
|
||||
* @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 comments in the
|
||||
* LETIMER_Sync() internal function call.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the LETIMER initialization structure.
|
||||
******************************************************************************/
|
||||
void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init)
|
||||
{
|
||||
uint32_t tmp = 0;
|
||||
|
||||
EFM_ASSERT(LETIMER_REF_VALID(letimer));
|
||||
|
||||
#if defined (LETIMER_EN_EN)
|
||||
letimer->EN_SET = LETIMER_EN_EN;
|
||||
#endif
|
||||
|
||||
/* Stop the timer if specified to be disabled and running. */
|
||||
if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING)) {
|
||||
#if defined(LETIMER_SYNCBUSY_CMD)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CMD);
|
||||
#elif defined(LETIMER_SYNCBUSY_STOP)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_STOP);
|
||||
#endif
|
||||
letimer->CMD = LETIMER_CMD_STOP;
|
||||
}
|
||||
|
||||
/* Configure the DEBUGRUN flag, which sets whether or not the counter should be
|
||||
* updated when the 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) || (init->topValue != 0U)) {
|
||||
#if defined (LETIMER_CTRL_COMP0TOP)
|
||||
tmp |= LETIMER_CTRL_COMP0TOP;
|
||||
if (init->topValue != 0U) {
|
||||
letimer->COMP0 = init->topValue;
|
||||
}
|
||||
#elif defined (LETIMER_CTRL_CNTTOPEN)
|
||||
tmp |= LETIMER_CTRL_CNTTOPEN;
|
||||
if (init->topValue != 0U) {
|
||||
letimer->TOP = init->topValue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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(LETIMER_SYNCBUSY_CTRL)
|
||||
/* LF register about to be modified requires sync; busy check. */
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CTRL);
|
||||
#endif
|
||||
letimer->CTRL = tmp;
|
||||
|
||||
/* Start the timer if specified to be enabled and not already running. */
|
||||
if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING)) {
|
||||
#if defined(LETIMER_SYNCBUSY_CMD)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CMD);
|
||||
#elif defined(LETIMER_SYNCBUSY_START)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_START);
|
||||
#endif
|
||||
letimer->CMD = LETIMER_CMD_START;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the LETIMER repeat register value.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the 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 the selected compare value. */
|
||||
switch (rep) {
|
||||
case 0:
|
||||
#if defined(LETIMER_SYNCBUSY_REP0)
|
||||
/* Wait for sync to complete to read the potentially pending value. */
|
||||
regSync(letimer, LETIMER_SYNCBUSY_REP0);
|
||||
#endif
|
||||
ret = letimer->REP0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
#if defined(LETIMER_SYNCBUSY_REP1)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_REP1);
|
||||
#endif
|
||||
ret = letimer->REP1;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* An unknown compare register selected. */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the 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
|
||||
* comments in the LETIMER_Sync() internal function call.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] rep
|
||||
* Repeat counter register to set, either 0 or 1.
|
||||
*
|
||||
* @param[in] value
|
||||
* An initialization value (<= 0x0000ffff).
|
||||
******************************************************************************/
|
||||
void LETIMER_RepeatSet(LETIMER_TypeDef *letimer,
|
||||
unsigned int rep,
|
||||
uint32_t value)
|
||||
{
|
||||
EFM_ASSERT(LETIMER_REF_VALID(letimer)
|
||||
&& LETIMER_REP_REG_VALID(rep)
|
||||
&& ((value & ~(_LETIMER_REP0_REP0_MASK
|
||||
>> _LETIMER_REP0_REP0_SHIFT))
|
||||
== 0));
|
||||
|
||||
/* Initialize the selected compare value. */
|
||||
switch (rep) {
|
||||
case 0:
|
||||
#if defined(LETIMER_SYNCBUSY_REP0)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_REP0);
|
||||
#endif
|
||||
letimer->REP0 = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
#if defined(LETIMER_SYNCBUSY_REP1)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_REP1);
|
||||
#endif
|
||||
letimer->REP1 = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* An unknown compare register selected, abort. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Reset LETIMER 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] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
******************************************************************************/
|
||||
void LETIMER_Reset(LETIMER_TypeDef *letimer)
|
||||
{
|
||||
#if defined(LETIMER_EN_EN)
|
||||
letimer->EN_SET = LETIMER_EN_EN;
|
||||
#endif
|
||||
LETIMER_SyncWait(letimer);
|
||||
|
||||
#if defined(LETIMER_SWRST_SWRST)
|
||||
letimer->SWRST_SET = LETIMER_SWRST_SWRST;
|
||||
while (letimer->SWRST & _LETIMER_SWRST_RESETTING_MASK) {
|
||||
}
|
||||
#else
|
||||
|
||||
#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_IntClear(letimer, _LETIMER_IF_MASK);
|
||||
|
||||
#if defined(_LETIMER_FREEZE_MASK)
|
||||
/* Unfreeze registers and pass new settings to LETIMER. */
|
||||
LETIMER_FreezeEnable(letimer, false);
|
||||
#endif
|
||||
|
||||
LETIMER_SyncWait(letimer);
|
||||
|
||||
#if defined (LETIMER_EN_EN)
|
||||
letimer->EN_CLR = LETIMER_EN_EN;
|
||||
#if defined(_LETIMER_EN_DISABLING_MASK)
|
||||
/*
|
||||
* Currently, there are no chips without SWRST and with LETIMER_EN_DISABLING
|
||||
* so this code should never be reached, but that way the same pattern of
|
||||
* checking the disabling bit is spread across emlib, and code is slightly
|
||||
* more resilient to feature addition/removal.
|
||||
*/
|
||||
while (letimer->EN & _LETIMER_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Wait for the LETIMER to complete all synchronization of register changes
|
||||
* and commands.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
******************************************************************************/
|
||||
void LETIMER_SyncWait(LETIMER_TypeDef *letimer)
|
||||
{
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
while ((letimer->EN != 0U) && (letimer->SYNCBUSY != 0U)) {
|
||||
/* Wait for previous synchronization to finish */
|
||||
}
|
||||
#else
|
||||
while (letimer->SYNCBUSY != 0U) {
|
||||
/* Wait for previous synchronization to finish */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the LETIMER top value.
|
||||
*
|
||||
* @note
|
||||
* The LETIMER is a down-counter, so when the counter reaches 0 then the top
|
||||
* value will be loaded into the counter. This function can be used to set
|
||||
* the top value.
|
||||
*
|
||||
* If the LETIMER is not already configured to use a top value then this
|
||||
* function will enable that functionality for the user.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @param[in] value
|
||||
* The top value. This can be a 16 bit value on series-0 and series-1 devices
|
||||
* and a 24 bit value on series-2 devices.
|
||||
******************************************************************************/
|
||||
void LETIMER_TopSet(LETIMER_TypeDef *letimer, uint32_t value)
|
||||
{
|
||||
#if defined(LETIMER_SYNCBUSY_CTRL)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_CTRL);
|
||||
#elif defined(LETIMER_SYNCBUSY_TOP)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_TOP);
|
||||
#endif
|
||||
|
||||
#if defined(_LETIMER_TOP_MASK)
|
||||
/* Make sure TOP value is enabled. */
|
||||
if ((letimer->CTRL & LETIMER_CTRL_CNTTOPEN) == 0U) {
|
||||
letimer->CTRL_SET = LETIMER_CTRL_CNTTOPEN;
|
||||
}
|
||||
letimer->TOP = value;
|
||||
#else
|
||||
/* Make sure TOP value is enabled. */
|
||||
if ((letimer->CTRL & LETIMER_CTRL_COMP0TOP) == 0U) {
|
||||
letimer->CTRL |= LETIMER_CTRL_COMP0TOP;
|
||||
}
|
||||
LETIMER_CompareSet(letimer, 0, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the current LETIMER top value.
|
||||
*
|
||||
* @param[in] letimer
|
||||
* A pointer to the LETIMER peripheral register block.
|
||||
*
|
||||
* @return
|
||||
* The top value. This will be a 16 bit value on series-0 and series-1
|
||||
* devices and a 24 bit value on series-2 devices.
|
||||
******************************************************************************/
|
||||
uint32_t LETIMER_TopGet(LETIMER_TypeDef *letimer)
|
||||
{
|
||||
#if defined(_LETIMER_TOP_MASK)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_TOP);
|
||||
return letimer->TOP;
|
||||
#else
|
||||
#if defined(LETIMER_SYNCBUSY_COMP0)
|
||||
regSync(letimer, LETIMER_SYNCBUSY_COMP0);
|
||||
#endif
|
||||
return letimer->COMP0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} (end addtogroup letimer) */
|
||||
#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */
|
||||
2081
Libs/platform/emlib/src/em_msc.c
Normal file
2081
Libs/platform/emlib/src/em_msc.c
Normal file
File diff suppressed because it is too large
Load Diff
700
Libs/platform/emlib/src/em_opamp.c
Normal file
700
Libs/platform/emlib/src/em_opamp.c
Normal file
@@ -0,0 +1,700 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Operational Amplifier (OPAMP) peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_opamp.h"
|
||||
#if ((defined(_SILICON_LABS_32B_SERIES_0) && defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1)) \
|
||||
|| (defined(_SILICON_LABS_32B_SERIES_1) && defined(VDAC_PRESENT) && (VDAC_COUNT > 0)))
|
||||
|
||||
#include "em_system.h"
|
||||
#include "sl_assert.h"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
/***************************************************************************//**
|
||||
* @addtogroup opamp OPAMP - Operational Amplifier
|
||||
* @brief Operational Amplifier (OPAMP) peripheral API
|
||||
* @details
|
||||
* This module contains functions to:
|
||||
* @li OPAMP_Enable() Configure and enable OPAMP.
|
||||
* @li OPAMP_Disable() Disable OPAMP.
|
||||
*
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* All OPAMP functions assume that the DAC clock is running. If DAC is not
|
||||
* used, the clock can be turned off when OPAMPs are configured.
|
||||
* @elseif DOXYDOC_P2_DEVICE
|
||||
* All OPAMP functions assume that the VDAC clock is running. If VDAC is not
|
||||
* used, the clock can be turned off when the OPAMPs are configured.
|
||||
* @endif
|
||||
*
|
||||
* If the available gain values don't suit the application at hand, the resistor
|
||||
* ladders can be disabled and external gain programming resistors used.
|
||||
*
|
||||
* A number of predefined OPAMP setup macros are available for configuration
|
||||
* of the most common OPAMP topologies (see figures below).
|
||||
*
|
||||
* @note
|
||||
* <em>The terms POSPAD and NEGPAD in the figures are used to indicate that these
|
||||
* pads should be connected to a suitable signal ground.</em>
|
||||
*
|
||||
* \n<b>Unity gain voltage follower.</b>\n
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* Use predefined macros @ref OPA_INIT_UNITY_GAIN and
|
||||
* @ref OPA_INIT_UNITY_GAIN_OPA2.
|
||||
* @elseif DOXYDOC_P2_DEVICE
|
||||
* Use predefined macro @ref OPA_INIT_UNITY_GAIN.
|
||||
* @endif
|
||||
* @verbatim
|
||||
|
||||
|\
|
||||
___________|+\
|
||||
| \_______
|
||||
___|_ / |
|
||||
| | / |
|
||||
| |/ |
|
||||
|___________|
|
||||
@endverbatim
|
||||
*
|
||||
* \n<b>Non-inverting amplifier.</b>\n
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* Use predefined macros @ref OPA_INIT_NON_INVERTING and
|
||||
* @ref OPA_INIT_NON_INVERTING_OPA2.
|
||||
* @elseif DOXYDOC_P2_DEVICE
|
||||
* Use predefined macro @ref OPA_INIT_NON_INVERTING.
|
||||
* @endif
|
||||
* @verbatim
|
||||
|
||||
|\
|
||||
___________|+\
|
||||
| \_______
|
||||
___|_ / |
|
||||
| | / |
|
||||
| |/ |
|
||||
|_____R2____|
|
||||
|
|
||||
R1
|
||||
|
|
||||
NEGPAD @endverbatim
|
||||
*
|
||||
* \n<b>Inverting amplifier.</b>\n
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* Use predefined macros @ref OPA_INIT_INVERTING and
|
||||
* @ref OPA_INIT_INVERTING_OPA2.
|
||||
* @elseif DOXYDOC_P2_DEVICE
|
||||
* Use predefined macro @ref OPA_INIT_INVERTING.
|
||||
* @endif
|
||||
* @verbatim
|
||||
|
||||
_____R2____
|
||||
| |
|
||||
| |\ |
|
||||
____R1_|___|_\ |
|
||||
| \____|___
|
||||
___| /
|
||||
| |+/
|
||||
| |/
|
||||
|
|
||||
POSPAD @endverbatim
|
||||
*
|
||||
* \n<b>Cascaded non-inverting amplifiers.</b>\n
|
||||
* Use predefined macros @ref OPA_INIT_CASCADED_NON_INVERTING_OPA0,
|
||||
* @ref OPA_INIT_CASCADED_NON_INVERTING_OPA1 and
|
||||
* @ref OPA_INIT_CASCADED_NON_INVERTING_OPA2.
|
||||
* @verbatim
|
||||
|
||||
|\ |\ |\
|
||||
___________|+\ OPA0 ___________|+\ OPA1 ___________|+\ OPA2
|
||||
| \_________| | \_________| | \_______
|
||||
___|_ / | ___|_ / | ___|_ / |
|
||||
| | / | | | / | | | / |
|
||||
| |/ | | |/ | | |/ |
|
||||
|_____R2____| |_____R2____| |_____R2____|
|
||||
| | |
|
||||
R1 R1 R1
|
||||
| | |
|
||||
NEGPAD NEGPAD NEGPAD @endverbatim
|
||||
*
|
||||
* \n<b>Cascaded inverting amplifiers.</b>\n
|
||||
* Use predefined macros @ref OPA_INIT_CASCADED_INVERTING_OPA0,
|
||||
* @ref OPA_INIT_CASCADED_INVERTING_OPA1 and
|
||||
* @ref OPA_INIT_CASCADED_INVERTING_OPA2.
|
||||
* @verbatim
|
||||
|
||||
_____R2____ _____R2____ _____R2____
|
||||
| | | | | |
|
||||
| |\ | | |\ | | |\ |
|
||||
____R1_|___|_\ | ____R1_|___|_\ | ____R1_|___|_\ |
|
||||
| \____|____| | \____|___| | \____|__
|
||||
___| / ___| / ___| /
|
||||
| |+/ OPA0 | |+/ OPA1 | |+/ OPA2
|
||||
| |/ | |/ | |/
|
||||
| | |
|
||||
POSPAD POSPAD POSPAD @endverbatim
|
||||
*
|
||||
* \n<b>Differential driver with two opamp's.</b>\n
|
||||
* Use predefined macros @ref OPA_INIT_DIFF_DRIVER_OPA0 and
|
||||
* @ref OPA_INIT_DIFF_DRIVER_OPA1.
|
||||
* @verbatim
|
||||
|
||||
__________________________
|
||||
| +
|
||||
| _____R2____
|
||||
|\ | | |
|
||||
___________|+\ OPA0 | | |\ OPA1 |
|
||||
| \_________|____R1_|___|_\ | _
|
||||
___|_ / | | \____|______
|
||||
| | / | ___| /
|
||||
| |/ | | |+/
|
||||
|________________| | |/
|
||||
|
|
||||
POSPAD @endverbatim
|
||||
*
|
||||
* \n<b>Differential receiver with three opamp's.</b>\n
|
||||
* Use predefined macros @ref OPA_INIT_DIFF_RECEIVER_OPA0,
|
||||
* @ref OPA_INIT_DIFF_RECEIVER_OPA1 and @ref OPA_INIT_DIFF_RECEIVER_OPA2.
|
||||
* @verbatim
|
||||
|
||||
|\
|
||||
__________|+\ OPA1
|
||||
_ | \_________
|
||||
___|_ / | | _____R2____
|
||||
| | / | | | |
|
||||
| |/ | | | |\ |
|
||||
|___________| |____R1_|___|_\ |
|
||||
| \____|___
|
||||
|\ ____R1_ ___| /
|
||||
+__________|+\ OPA0 | | |+/ OPA2
|
||||
| \_________| | |/
|
||||
___|_ / | R2
|
||||
| | / | |
|
||||
| |/ | NEGPAD OPA0
|
||||
|___________|
|
||||
@endverbatim
|
||||
*
|
||||
* @if DOXYDOC_P2_DEVICE
|
||||
* \n<b>Instrumentation amplifier.</b>\n
|
||||
* Use predefined macros @ref OPA_INIT_INSTR_AMP_OPA0 and
|
||||
* @ref OPA_INIT_INSTR_AMP_OPA1.
|
||||
* @verbatim
|
||||
|
||||
|\
|
||||
__________|+\ OPA1
|
||||
| \______________
|
||||
___|_ / |
|
||||
| | / |
|
||||
| |/ R2
|
||||
|____________|
|
||||
|
|
||||
R1
|
||||
|
|
||||
R1
|
||||
____________|
|
||||
| |
|
||||
| R2
|
||||
| |\ |
|
||||
|___|+\ OPA0 |
|
||||
| \_____|________
|
||||
__________|_ /
|
||||
| /
|
||||
|/
|
||||
|
||||
@endverbatim
|
||||
* @endif
|
||||
*
|
||||
* @{
|
||||
******************************************************************************/
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Disable an Operational Amplifier.
|
||||
*
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* @param[in] dac
|
||||
* A pointer to the DAC peripheral register block.
|
||||
* @elseif DOXYDOC_P2_DEVICE
|
||||
* @param[in] dac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
* @endif
|
||||
*
|
||||
* @param[in] opa
|
||||
* Selects an OPA, valid values are OPA0, OPA1, and OPA2.
|
||||
******************************************************************************/
|
||||
void OPAMP_Disable(
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
DAC_TypeDef *dac,
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
VDAC_TypeDef *dac,
|
||||
#endif
|
||||
OPAMP_TypeDef opa)
|
||||
{
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
EFM_ASSERT(DAC_REF_VALID(dac));
|
||||
EFM_ASSERT(DAC_OPA_VALID(opa));
|
||||
|
||||
if (opa == OPA0) {
|
||||
dac->CH0CTRL &= ~DAC_CH0CTRL_EN;
|
||||
dac->OPACTRL &= ~DAC_OPACTRL_OPA0EN;
|
||||
} else if (opa == OPA1) {
|
||||
dac->CH1CTRL &= ~DAC_CH1CTRL_EN;
|
||||
dac->OPACTRL &= ~DAC_OPACTRL_OPA1EN;
|
||||
} else { /* OPA2 */
|
||||
dac->OPACTRL &= ~DAC_OPACTRL_OPA2EN;
|
||||
}
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
EFM_ASSERT(VDAC_REF_VALID(dac));
|
||||
EFM_ASSERT(VDAC_OPA_VALID(opa));
|
||||
|
||||
if (opa == OPA0) {
|
||||
#if defined(VDAC_STATUS_OPA0ENS)
|
||||
dac->CMD |= VDAC_CMD_OPA0DIS;
|
||||
while (dac->STATUS & VDAC_STATUS_OPA0ENS) {
|
||||
}
|
||||
#endif
|
||||
#if defined(VDAC_STATUS_OPA1ENS)
|
||||
} else if (opa == OPA1) {
|
||||
dac->CMD |= VDAC_CMD_OPA1DIS;
|
||||
while (dac->STATUS & VDAC_STATUS_OPA1ENS) {
|
||||
}
|
||||
#endif
|
||||
#if defined(VDAC_STATUS_OPA2ENS)
|
||||
} else if (opa == OPA2) {
|
||||
dac->CMD |= VDAC_CMD_OPA2DIS;
|
||||
while (dac->STATUS & VDAC_STATUS_OPA2ENS) {
|
||||
}
|
||||
#endif
|
||||
} else { /* OPA3 */
|
||||
#if defined(VDAC_STATUS_OPA3ENS)
|
||||
dac->CMD |= VDAC_CMD_OPA3DIS;
|
||||
while (dac->STATUS & VDAC_STATUS_OPA3ENS) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Configure and enable an Operational Amplifier.
|
||||
*
|
||||
* @if DOXYDOC_P1_DEVICE
|
||||
* @note
|
||||
* The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef
|
||||
* structure should consist of one or more of the
|
||||
* DAC_OPA[opa#]MUX_OUTPEN_OUT[output#] flags
|
||||
* (defined in \<part_name\>_dac.h) OR'ed together. @n @n
|
||||
* For OPA0:
|
||||
* @li DAC_OPA0MUX_OUTPEN_OUT0
|
||||
* @li DAC_OPA0MUX_OUTPEN_OUT1
|
||||
* @li DAC_OPA0MUX_OUTPEN_OUT2
|
||||
* @li DAC_OPA0MUX_OUTPEN_OUT3
|
||||
* @li DAC_OPA0MUX_OUTPEN_OUT4
|
||||
*
|
||||
* For OPA1:
|
||||
* @li DAC_OPA1MUX_OUTPEN_OUT0
|
||||
* @li DAC_OPA1MUX_OUTPEN_OUT1
|
||||
* @li DAC_OPA1MUX_OUTPEN_OUT2
|
||||
* @li DAC_OPA1MUX_OUTPEN_OUT3
|
||||
* @li DAC_OPA1MUX_OUTPEN_OUT4
|
||||
*
|
||||
* For OPA2:
|
||||
* @li DAC_OPA2MUX_OUTPEN_OUT0
|
||||
* @li DAC_OPA2MUX_OUTPEN_OUT1
|
||||
*
|
||||
* E.g: @n
|
||||
* init.outPen = DAC_OPA0MUX_OUTPEN_OUT0 | DAC_OPA0MUX_OUTPEN_OUT4;
|
||||
*
|
||||
* @param[in] dac
|
||||
* A pointer to the DAC peripheral register block.
|
||||
* @elseif DOXYDOC_P2_DEVICE
|
||||
* @note
|
||||
* The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef
|
||||
* structure should consist of one or more of the
|
||||
* VDAC_OPA_OUT_ALTOUTPADEN_OUT[output#] flags
|
||||
* (defined in \<part_name\>_vdac.h) OR'ed together. @n @n
|
||||
* @li VDAC_OPA_OUT_ALTOUTPADEN_OUT0
|
||||
* @li VDAC_OPA_OUT_ALTOUTPADEN_OUT1
|
||||
* @li VDAC_OPA_OUT_ALTOUTPADEN_OUT2
|
||||
* @li VDAC_OPA_OUT_ALTOUTPADEN_OUT3
|
||||
* @li VDAC_OPA_OUT_ALTOUTPADEN_OUT4
|
||||
*
|
||||
* For example: @n
|
||||
* init.outPen = VDAC_OPA_OUT_ALTOUTPADEN_OUT0 | VDAC_OPA_OUT_ALTOUTPADEN_OUT4;
|
||||
* @param[in] dac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
* @endif
|
||||
*
|
||||
* @param[in] opa
|
||||
* Selects an OPA, valid values are OPA0, OPA1, and OPA2.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to a structure containing OPAMP initialization information.
|
||||
******************************************************************************/
|
||||
void OPAMP_Enable(
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
DAC_TypeDef *dac,
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
VDAC_TypeDef *dac,
|
||||
#endif
|
||||
OPAMP_TypeDef opa,
|
||||
const OPAMP_Init_TypeDef *init)
|
||||
{
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
uint32_t gain;
|
||||
|
||||
EFM_ASSERT(DAC_REF_VALID(dac));
|
||||
EFM_ASSERT(DAC_OPA_VALID(opa));
|
||||
EFM_ASSERT(init->bias <= (_DAC_BIASPROG_BIASPROG_MASK
|
||||
>> _DAC_BIASPROG_BIASPROG_SHIFT));
|
||||
|
||||
if (opa == OPA0) {
|
||||
EFM_ASSERT((init->outPen & ~_DAC_OPA0MUX_OUTPEN_MASK) == 0);
|
||||
|
||||
dac->BIASPROG = (dac->BIASPROG
|
||||
& ~(_DAC_BIASPROG_BIASPROG_MASK
|
||||
| DAC_BIASPROG_HALFBIAS))
|
||||
| (init->bias << _DAC_BIASPROG_BIASPROG_SHIFT)
|
||||
| (init->halfBias ? DAC_BIASPROG_HALFBIAS : 0);
|
||||
|
||||
if (init->defaultOffset) {
|
||||
gain = dac->CAL & _DAC_CAL_GAIN_MASK;
|
||||
SYSTEM_GetCalibrationValue(&dac->CAL);
|
||||
dac->CAL = (dac->CAL & ~_DAC_CAL_GAIN_MASK) | gain;
|
||||
} else {
|
||||
EFM_ASSERT(init->offset <= (_DAC_CAL_CH0OFFSET_MASK
|
||||
>> _DAC_CAL_CH0OFFSET_SHIFT));
|
||||
|
||||
dac->CAL = (dac->CAL & ~_DAC_CAL_CH0OFFSET_MASK)
|
||||
| (init->offset << _DAC_CAL_CH0OFFSET_SHIFT);
|
||||
}
|
||||
|
||||
dac->OPA0MUX = (uint32_t)init->resSel
|
||||
| (uint32_t)init->outMode
|
||||
| init->outPen
|
||||
| (uint32_t)init->resInMux
|
||||
| (uint32_t)init->negSel
|
||||
| (uint32_t)init->posSel
|
||||
| (init->nextOut ? DAC_OPA0MUX_NEXTOUT : 0)
|
||||
| (init->npEn ? DAC_OPA0MUX_NPEN : 0)
|
||||
| (init->ppEn ? DAC_OPA0MUX_PPEN : 0);
|
||||
|
||||
dac->CH0CTRL |= DAC_CH0CTRL_EN;
|
||||
dac->OPACTRL = (dac->OPACTRL
|
||||
& ~(DAC_OPACTRL_OPA0SHORT
|
||||
| _DAC_OPACTRL_OPA0LPFDIS_MASK
|
||||
| DAC_OPACTRL_OPA0HCMDIS))
|
||||
| (init->shortInputs ? DAC_OPACTRL_OPA0SHORT : 0)
|
||||
| (init->lpfPosPadDisable
|
||||
? DAC_OPACTRL_OPA0LPFDIS_PLPFDIS : 0)
|
||||
| (init->lpfNegPadDisable
|
||||
? DAC_OPACTRL_OPA0LPFDIS_NLPFDIS : 0)
|
||||
| (init->hcmDisable ? DAC_OPACTRL_OPA0HCMDIS : 0)
|
||||
| DAC_OPACTRL_OPA0EN;
|
||||
} else if ( opa == OPA1 ) {
|
||||
EFM_ASSERT((init->outPen & ~_DAC_OPA1MUX_OUTPEN_MASK) == 0);
|
||||
|
||||
dac->BIASPROG = (dac->BIASPROG
|
||||
& ~(_DAC_BIASPROG_BIASPROG_MASK
|
||||
| DAC_BIASPROG_HALFBIAS))
|
||||
| (init->bias << _DAC_BIASPROG_BIASPROG_SHIFT)
|
||||
| (init->halfBias ? DAC_BIASPROG_HALFBIAS : 0);
|
||||
|
||||
if (init->defaultOffset) {
|
||||
gain = dac->CAL & _DAC_CAL_GAIN_MASK;
|
||||
SYSTEM_GetCalibrationValue(&dac->CAL);
|
||||
dac->CAL = (dac->CAL & ~_DAC_CAL_GAIN_MASK) | gain;
|
||||
} else {
|
||||
EFM_ASSERT(init->offset <= (_DAC_CAL_CH1OFFSET_MASK
|
||||
>> _DAC_CAL_CH1OFFSET_SHIFT));
|
||||
|
||||
dac->CAL = (dac->CAL & ~_DAC_CAL_CH1OFFSET_MASK)
|
||||
| (init->offset << _DAC_CAL_CH1OFFSET_SHIFT);
|
||||
}
|
||||
|
||||
dac->OPA1MUX = (uint32_t)init->resSel
|
||||
| (uint32_t)init->outMode
|
||||
| init->outPen
|
||||
| (uint32_t)init->resInMux
|
||||
| (uint32_t)init->negSel
|
||||
| (uint32_t)init->posSel
|
||||
| (init->nextOut ? DAC_OPA1MUX_NEXTOUT : 0)
|
||||
| (init->npEn ? DAC_OPA1MUX_NPEN : 0)
|
||||
| (init->ppEn ? DAC_OPA1MUX_PPEN : 0);
|
||||
|
||||
dac->CH1CTRL |= DAC_CH1CTRL_EN;
|
||||
dac->OPACTRL = (dac->OPACTRL
|
||||
& ~(DAC_OPACTRL_OPA1SHORT
|
||||
| _DAC_OPACTRL_OPA1LPFDIS_MASK
|
||||
| DAC_OPACTRL_OPA1HCMDIS))
|
||||
| (init->shortInputs ? DAC_OPACTRL_OPA1SHORT : 0)
|
||||
| (init->lpfPosPadDisable
|
||||
? DAC_OPACTRL_OPA1LPFDIS_PLPFDIS : 0)
|
||||
| (init->lpfNegPadDisable
|
||||
? DAC_OPACTRL_OPA1LPFDIS_NLPFDIS : 0)
|
||||
| (init->hcmDisable ? DAC_OPACTRL_OPA1HCMDIS : 0)
|
||||
| DAC_OPACTRL_OPA1EN;
|
||||
} else { /* OPA2 */
|
||||
EFM_ASSERT((init->posSel == DAC_OPA2MUX_POSSEL_DISABLE)
|
||||
|| (init->posSel == DAC_OPA2MUX_POSSEL_POSPAD)
|
||||
|| (init->posSel == DAC_OPA2MUX_POSSEL_OPA1INP)
|
||||
|| (init->posSel == DAC_OPA2MUX_POSSEL_OPATAP));
|
||||
|
||||
EFM_ASSERT((init->outMode & ~DAC_OPA2MUX_OUTMODE) == 0);
|
||||
|
||||
EFM_ASSERT((init->outPen & ~_DAC_OPA2MUX_OUTPEN_MASK) == 0);
|
||||
|
||||
dac->BIASPROG = (dac->BIASPROG
|
||||
& ~(_DAC_BIASPROG_OPA2BIASPROG_MASK
|
||||
| DAC_BIASPROG_OPA2HALFBIAS))
|
||||
| (init->bias << _DAC_BIASPROG_OPA2BIASPROG_SHIFT)
|
||||
| (init->halfBias ? DAC_BIASPROG_OPA2HALFBIAS : 0);
|
||||
|
||||
if (init->defaultOffset) {
|
||||
SYSTEM_GetCalibrationValue(&dac->OPAOFFSET);
|
||||
} else {
|
||||
EFM_ASSERT(init->offset <= (_DAC_OPAOFFSET_OPA2OFFSET_MASK
|
||||
>> _DAC_OPAOFFSET_OPA2OFFSET_SHIFT));
|
||||
dac->OPAOFFSET = (dac->OPAOFFSET & ~_DAC_OPAOFFSET_OPA2OFFSET_MASK)
|
||||
| (init->offset << _DAC_OPAOFFSET_OPA2OFFSET_SHIFT);
|
||||
}
|
||||
|
||||
dac->OPA2MUX = (uint32_t)init->resSel
|
||||
| (uint32_t)init->outMode
|
||||
| init->outPen
|
||||
| (uint32_t)init->resInMux
|
||||
| (uint32_t)init->negSel
|
||||
| (uint32_t)init->posSel
|
||||
| (init->nextOut ? DAC_OPA2MUX_NEXTOUT : 0)
|
||||
| (init->npEn ? DAC_OPA2MUX_NPEN : 0)
|
||||
| (init->ppEn ? DAC_OPA2MUX_PPEN : 0);
|
||||
|
||||
dac->OPACTRL = (dac->OPACTRL
|
||||
& ~(DAC_OPACTRL_OPA2SHORT
|
||||
| _DAC_OPACTRL_OPA2LPFDIS_MASK
|
||||
| DAC_OPACTRL_OPA2HCMDIS))
|
||||
| (init->shortInputs ? DAC_OPACTRL_OPA2SHORT : 0)
|
||||
| (init->lpfPosPadDisable
|
||||
? DAC_OPACTRL_OPA2LPFDIS_PLPFDIS : 0)
|
||||
| (init->lpfNegPadDisable
|
||||
? DAC_OPACTRL_OPA2LPFDIS_NLPFDIS : 0)
|
||||
| (init->hcmDisable ? DAC_OPACTRL_OPA2HCMDIS : 0)
|
||||
| DAC_OPACTRL_OPA2EN;
|
||||
}
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
uint32_t calData = 0;
|
||||
uint32_t warmupTime;
|
||||
|
||||
EFM_ASSERT(VDAC_REF_VALID(dac));
|
||||
EFM_ASSERT(VDAC_OPA_VALID(opa));
|
||||
EFM_ASSERT(init->settleTime <= (_VDAC_OPA_TIMER_SETTLETIME_MASK
|
||||
>> _VDAC_OPA_TIMER_SETTLETIME_SHIFT));
|
||||
EFM_ASSERT(init->startupDly <= (_VDAC_OPA_TIMER_STARTUPDLY_MASK
|
||||
>> _VDAC_OPA_TIMER_STARTUPDLY_SHIFT));
|
||||
EFM_ASSERT((init->outPen & ~_VDAC_OPA_OUT_ALTOUTPADEN_MASK) == 0);
|
||||
EFM_ASSERT((init->drvStr == opaDrvStrLowerAccLowStr)
|
||||
|| (init->drvStr == opaDrvStrLowAccLowStr)
|
||||
|| (init->drvStr == opaDrvStrHighAccHighStr)
|
||||
|| (init->drvStr == opaDrvStrHigherAccHighStr));
|
||||
|
||||
/* Disable OPAMP before writing to registers. */
|
||||
OPAMP_Disable(dac, opa);
|
||||
|
||||
/* Get the calibration value based on OPAMP, Drive Strength, and INCBW. */
|
||||
switch (opa) {
|
||||
#if defined(VDAC_STATUS_OPA0ENS)
|
||||
case OPA0:
|
||||
switch (init->drvStr) {
|
||||
case opaDrvStrLowerAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA0CAL0 : DEVINFO->OPA0CAL4);
|
||||
break;
|
||||
case opaDrvStrLowAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA0CAL1 : DEVINFO->OPA0CAL5);
|
||||
break;
|
||||
case opaDrvStrHighAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA0CAL2 : DEVINFO->OPA0CAL6);
|
||||
break;
|
||||
case opaDrvStrHigherAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA0CAL3 : DEVINFO->OPA0CAL7);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(VDAC_STATUS_OPA1ENS)
|
||||
case OPA1:
|
||||
switch (init->drvStr) {
|
||||
case opaDrvStrLowerAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA1CAL0 : DEVINFO->OPA1CAL4);
|
||||
break;
|
||||
case opaDrvStrLowAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA1CAL1 : DEVINFO->OPA1CAL5);
|
||||
break;
|
||||
case opaDrvStrHighAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA1CAL2 : DEVINFO->OPA1CAL6);
|
||||
break;
|
||||
case opaDrvStrHigherAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA1CAL3 : DEVINFO->OPA1CAL7);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(VDAC_STATUS_OPA2ENS)
|
||||
case OPA2:
|
||||
switch (init->drvStr) {
|
||||
case opaDrvStrLowerAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA2CAL0 : DEVINFO->OPA2CAL4);
|
||||
break;
|
||||
case opaDrvStrLowAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA2CAL1 : DEVINFO->OPA2CAL5);
|
||||
break;
|
||||
case opaDrvStrHighAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA2CAL2 : DEVINFO->OPA2CAL6);
|
||||
break;
|
||||
case opaDrvStrHigherAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA2CAL3 : DEVINFO->OPA2CAL7);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(VDAC_STATUS_OPA3ENS)
|
||||
case OPA3:
|
||||
switch (init->drvStr) {
|
||||
case opaDrvStrLowerAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA3CAL0 : DEVINFO->OPA3CAL4);
|
||||
break;
|
||||
case opaDrvStrLowAccLowStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA3CAL1 : DEVINFO->OPA3CAL5);
|
||||
break;
|
||||
case opaDrvStrHighAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA3CAL2 : DEVINFO->OPA3CAL6);
|
||||
break;
|
||||
case opaDrvStrHigherAccHighStr:
|
||||
calData = (init->ugBwScale ? DEVINFO->OPA3CAL3 : DEVINFO->OPA3CAL7);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
if (!init->defaultOffsetN) {
|
||||
EFM_ASSERT(init->offsetN <= (_VDAC_OPA_CAL_OFFSETN_MASK
|
||||
>> _VDAC_OPA_CAL_OFFSETN_SHIFT));
|
||||
calData = (calData & ~_VDAC_OPA_CAL_OFFSETN_MASK)
|
||||
| (init->offsetN << _VDAC_OPA_CAL_OFFSETN_SHIFT);
|
||||
}
|
||||
if (!init->defaultOffsetP) {
|
||||
EFM_ASSERT(init->offsetP <= (_VDAC_OPA_CAL_OFFSETP_MASK
|
||||
>> _VDAC_OPA_CAL_OFFSETP_SHIFT));
|
||||
calData = (calData & ~_VDAC_OPA_CAL_OFFSETP_MASK)
|
||||
| (init->offsetP << _VDAC_OPA_CAL_OFFSETP_SHIFT);
|
||||
}
|
||||
|
||||
dac->OPA[opa].CAL = (calData & _VDAC_OPA_CAL_MASK);
|
||||
|
||||
dac->OPA[opa].MUX = (uint32_t)init->resSel
|
||||
| (init->gain3xEn ? VDAC_OPA_MUX_GAIN3X : 0)
|
||||
| (uint32_t)init->resInMux
|
||||
| (uint32_t)init->negSel
|
||||
| (uint32_t)init->posSel;
|
||||
|
||||
dac->OPA[opa].OUT = (uint32_t)init->outMode
|
||||
| (uint32_t)init->outPen;
|
||||
|
||||
switch (init->drvStr) {
|
||||
case opaDrvStrHigherAccHighStr:
|
||||
warmupTime = 6;
|
||||
break;
|
||||
|
||||
case opaDrvStrHighAccHighStr:
|
||||
warmupTime = 8;
|
||||
break;
|
||||
|
||||
case opaDrvStrLowAccLowStr:
|
||||
warmupTime = 85;
|
||||
break;
|
||||
|
||||
case opaDrvStrLowerAccLowStr:
|
||||
default:
|
||||
warmupTime = 100;
|
||||
break;
|
||||
}
|
||||
|
||||
dac->OPA[opa].TIMER = (uint32_t)(init->settleTime
|
||||
<< _VDAC_OPA_TIMER_SETTLETIME_SHIFT)
|
||||
| (uint32_t)(warmupTime
|
||||
<< _VDAC_OPA_TIMER_WARMUPTIME_SHIFT)
|
||||
| (uint32_t)(init->startupDly
|
||||
<< _VDAC_OPA_TIMER_STARTUPDLY_SHIFT);
|
||||
|
||||
dac->OPA[opa].CTRL = (init->aportYMasterDisable
|
||||
? VDAC_OPA_CTRL_APORTYMASTERDIS : 0)
|
||||
| (init->aportXMasterDisable
|
||||
? VDAC_OPA_CTRL_APORTXMASTERDIS : 0)
|
||||
| (uint32_t)init->prsOutSel
|
||||
| (uint32_t)init->prsSel
|
||||
| (uint32_t)init->prsMode
|
||||
| (init->prsEn ? VDAC_OPA_CTRL_PRSEN : 0)
|
||||
| (init->halfDrvStr
|
||||
? VDAC_OPA_CTRL_OUTSCALE_HALF
|
||||
: VDAC_OPA_CTRL_OUTSCALE_FULL)
|
||||
| (init->hcmDisable ? VDAC_OPA_CTRL_HCMDIS : 0)
|
||||
| (init->ugBwScale ? VDAC_OPA_CTRL_INCBW : 0)
|
||||
| (uint32_t)init->drvStr;
|
||||
|
||||
if (opa == OPA0) {
|
||||
#if defined(VDAC_STATUS_OPA0ENS)
|
||||
dac->CMD |= VDAC_CMD_OPA0EN;
|
||||
#endif
|
||||
#if defined(VDAC_STATUS_OPA1ENS)
|
||||
} else if (opa == OPA1) {
|
||||
dac->CMD |= VDAC_CMD_OPA1EN;
|
||||
#endif
|
||||
#if defined(VDAC_STATUS_OPA2ENS)
|
||||
} else if (opa == OPA2) {
|
||||
dac->CMD |= VDAC_CMD_OPA2EN;
|
||||
#endif
|
||||
#if defined(VDAC_STATUS_OPA3ENS)
|
||||
} else { /* OPA3 */
|
||||
dac->CMD |= VDAC_CMD_OPA3EN;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} (end addtogroup opamp) */
|
||||
|
||||
#endif /* (defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1)
|
||||
|| defined(VDAC_PRESENT) && (VDAC_COUNT > 0) */
|
||||
1067
Libs/platform/emlib/src/em_pcnt.c
Normal file
1067
Libs/platform/emlib/src/em_pcnt.c
Normal file
File diff suppressed because it is too large
Load Diff
661
Libs/platform/emlib/src/em_prs.c
Normal file
661
Libs/platform/emlib/src/em_prs.c
Normal file
@@ -0,0 +1,661 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Peripheral Reflex System (PRS) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_prs.h"
|
||||
#if defined(PRS_COUNT) && (PRS_COUNT > 0)
|
||||
|
||||
#include "sl_assert.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup prs PRS - Peripheral Reflex System
|
||||
* @brief Peripheral Reflex System (PRS) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the PRS peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. The PRS allows configurable, fast, and autonomous
|
||||
* communication between peripherals on the MCU or SoC.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/* Generic defines for async and sync signals applying to all TIMER instances.
|
||||
* Those defines map to TIMER2 but it could be any TIMER instance number. */
|
||||
#define _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERUF _PRS_ASYNC_CH_CTRL_SIGSEL_TIMER2UF
|
||||
#define _PRS_ASYNC_CH_CTRL_SIGSEL_TIMEROF _PRS_ASYNC_CH_CTRL_SIGSEL_TIMER2OF
|
||||
#define _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERCC0 _PRS_ASYNC_CH_CTRL_SIGSEL_TIMER2CC0
|
||||
#define _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERCC1 _PRS_ASYNC_CH_CTRL_SIGSEL_TIMER2CC1
|
||||
#define _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERCC2 _PRS_ASYNC_CH_CTRL_SIGSEL_TIMER2CC2
|
||||
|
||||
#define _PRS_SYNC_CH_CTRL_SIGSEL_TIMERUF _PRS_SYNC_CH_CTRL_SIGSEL_TIMER2UF
|
||||
#define _PRS_SYNC_CH_CTRL_SIGSEL_TIMEROF _PRS_SYNC_CH_CTRL_SIGSEL_TIMER2OF
|
||||
#define _PRS_SYNC_CH_CTRL_SIGSEL_TIMERCC0 _PRS_SYNC_CH_CTRL_SIGSEL_TIMER2CC0
|
||||
#define _PRS_SYNC_CH_CTRL_SIGSEL_TIMERCC1 _PRS_SYNC_CH_CTRL_SIGSEL_TIMER2CC1
|
||||
#define _PRS_SYNC_CH_CTRL_SIGSEL_TIMERCC2 _PRS_SYNC_CH_CTRL_SIGSEL_TIMER2CC2
|
||||
|
||||
/*******************************************************************************
|
||||
************************** LOCAL FUNCTIONS ********************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get PRS source signal for a channel.
|
||||
*
|
||||
* @param[in] type
|
||||
* PRS channel type. This can be either @ref prsTypeAsync or
|
||||
* @ref prsTypeSync.
|
||||
*
|
||||
* @param[in] ch
|
||||
* channel number.
|
||||
*
|
||||
* @return
|
||||
* PRS signal assigned to the channel.
|
||||
******************************************************************************/
|
||||
static PRS_Signal_t getSignal(unsigned int ch, PRS_ChType_t type)
|
||||
{
|
||||
PRS_Signal_t signal;
|
||||
|
||||
#if defined(_PRS_ASYNC_CH_CTRL_SOURCESEL_MASK)
|
||||
if (type == prsTypeAsync) {
|
||||
signal = (PRS_Signal_t) (PRS->ASYNC_CH[ch].CTRL
|
||||
& (_PRS_ASYNC_CH_CTRL_SOURCESEL_MASK | _PRS_ASYNC_CH_CTRL_SIGSEL_MASK));
|
||||
} else {
|
||||
signal = (PRS_Signal_t) (PRS->SYNC_CH[ch].CTRL
|
||||
& (_PRS_SYNC_CH_CTRL_SOURCESEL_MASK | _PRS_SYNC_CH_CTRL_SIGSEL_MASK));
|
||||
}
|
||||
#else
|
||||
(void) type;
|
||||
signal = (PRS_Signal_t) (PRS->CH[ch].CTRL
|
||||
& (_PRS_CH_CTRL_SOURCESEL_MASK | _PRS_CH_CTRL_SIGSEL_MASK));
|
||||
#endif
|
||||
return signal;
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Convert an async PRS source to a sync source.
|
||||
*
|
||||
* @details
|
||||
* This conversion must be done because the id's of the same peripheral
|
||||
* source is different depending on if it's used as an asynchronous PRS source
|
||||
* or a synchronous PRS source.
|
||||
*
|
||||
* @param[in] asyncSource
|
||||
* The id of the asynchronous PRS source.
|
||||
*
|
||||
* @return
|
||||
* The id of the corresponding synchronous PRS source.
|
||||
******************************************************************************/
|
||||
uint32_t PRS_ConvertToSyncSource(uint32_t asyncSource)
|
||||
{
|
||||
uint32_t syncSource = 0;
|
||||
|
||||
switch (asyncSource) {
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_NONE:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_NONE;
|
||||
break;
|
||||
#if defined(IADC_PRESENT)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_IADC0:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_IADC0;
|
||||
break;
|
||||
#endif
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER0:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER0;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER1:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER1;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER2:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER2;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER3:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER3;
|
||||
break;
|
||||
#if defined(TIMER4)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER4:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER4;
|
||||
break;
|
||||
#endif
|
||||
#if defined(TIMER5)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER5:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER5;
|
||||
break;
|
||||
#endif
|
||||
#if defined(TIMER6)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER6:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER6;
|
||||
break;
|
||||
#endif
|
||||
#if defined(TIMER7)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER7:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_TIMER7;
|
||||
break;
|
||||
#endif
|
||||
#if defined(VDAC0)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_VDAC0L:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_VDAC0;
|
||||
break;
|
||||
#endif
|
||||
#if defined(VDAC1)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_VDAC1L:
|
||||
syncSource = _PRS_SYNC_CH_CTRL_SOURCESEL_VDAC1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
EFM_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return syncSource;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Convert an async PRS signal to a sync signal.
|
||||
*
|
||||
* @details
|
||||
* PRS values for some peripherals signals differ between asynchronous and
|
||||
* synchronous PRS channels. This function must be used to handle the
|
||||
* conversion.
|
||||
*
|
||||
* @param[in] asyncSource
|
||||
* The id of the asynchronous PRS source.
|
||||
*
|
||||
* @param[in] asyncSignal
|
||||
* The id of the asynchronous PRS signal.
|
||||
*
|
||||
* @return
|
||||
* The id of the corresponding synchronous PRS signal.
|
||||
******************************************************************************/
|
||||
uint32_t PRS_ConvertToSyncSignal(uint32_t asyncSource, uint32_t asyncSignal)
|
||||
{
|
||||
uint32_t syncSignal = asyncSignal;
|
||||
|
||||
switch (asyncSource) {
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER0:
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER1:
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER2:
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER3:
|
||||
#if defined(_PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER4)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER4:
|
||||
#endif
|
||||
#if defined(_PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER5)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER5:
|
||||
#endif
|
||||
#if defined(_PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER6)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER6:
|
||||
#endif
|
||||
#if defined(_PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER7)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_TIMER7:
|
||||
#endif
|
||||
/* Async and sync signal values are consistent across all timers instances.
|
||||
* Generic defines are used. */
|
||||
switch (asyncSignal) {
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERUF:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_TIMERUF;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_TIMEROF:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_TIMEROF;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERCC0:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_TIMERCC0;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERCC1:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_TIMERCC1;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_TIMERCC2:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_TIMERCC2;
|
||||
break;
|
||||
default:
|
||||
EFM_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#if defined(IADC0)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_IADC0:
|
||||
switch (asyncSignal) {
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_IADC0SCANENTRYDONE:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_IADC0SCANENTRYDONE;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_IADC0SCANTABLEDONE:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_IADC0SCANTABLEDONE;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_IADC0SINGLEDONE:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_IADC0SINGLEDONE;
|
||||
break;
|
||||
default:
|
||||
EFM_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if defined(VDAC0)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_VDAC0L:
|
||||
switch (asyncSignal) {
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_VDAC0LCH0DONEASYNC:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_VDAC0CH0DONESYNC;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_VDAC0LCH1DONEASYNC:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_VDAC0CH1DONESYNC;
|
||||
break;
|
||||
default:
|
||||
EFM_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if defined(VDAC1)
|
||||
case _PRS_ASYNC_CH_CTRL_SOURCESEL_VDAC1L:
|
||||
switch (asyncSignal) {
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_VDAC1LCH0DONEASYNC:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_VDAC1CH0DONESYNC;
|
||||
break;
|
||||
case _PRS_ASYNC_CH_CTRL_SIGSEL_VDAC1LCH1DONEASYNC:
|
||||
syncSignal = _PRS_SYNC_CH_CTRL_SIGSEL_VDAC1CH1DONESYNC;
|
||||
break;
|
||||
default:
|
||||
EFM_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
// No translation
|
||||
break;
|
||||
}
|
||||
return syncSignal;
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set a source and signal for a channel.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A channel to define the signal and source for.
|
||||
*
|
||||
* @param[in] source
|
||||
* A source to select for the channel. Use one of PRS_CH_CTRL_SOURCESEL_x defines.
|
||||
*
|
||||
* @param[in] signal
|
||||
* A signal (for selected @p source) to use. Use one of PRS_CH_CTRL_SIGSEL_x
|
||||
* defines.
|
||||
*
|
||||
* @param[in] edge
|
||||
* An edge (for selected source/signal) to generate the pulse for.
|
||||
******************************************************************************/
|
||||
void PRS_SourceSignalSet(unsigned int ch,
|
||||
uint32_t source,
|
||||
uint32_t signal,
|
||||
PRS_Edge_TypeDef edge)
|
||||
{
|
||||
#if defined(_PRS_SYNC_CH_CTRL_MASK)
|
||||
(void) edge;
|
||||
EFM_ASSERT(ch < PRS_SYNC_CHAN_COUNT);
|
||||
PRS->SYNC_CH[ch].CTRL = (source & _PRS_SYNC_CH_CTRL_SOURCESEL_MASK)
|
||||
| (signal & _PRS_SYNC_CH_CTRL_SIGSEL_MASK);
|
||||
#else
|
||||
EFM_ASSERT(ch < PRS_CHAN_COUNT);
|
||||
PRS->CH[ch].CTRL = (source & _PRS_CH_CTRL_SOURCESEL_MASK)
|
||||
| (signal & _PRS_CH_CTRL_SIGSEL_MASK)
|
||||
| (uint32_t)edge << _PRS_CH_CTRL_EDSEL_SHIFT;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(PRS_ASYNC_SUPPORTED)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the source and asynchronous signal for a channel.
|
||||
*
|
||||
* @details
|
||||
* Asynchronous reflexes are not clocked on HFPERCLK and can be used even in
|
||||
* EM2/EM3.
|
||||
* There is a limitation to reflexes operating in asynchronous mode in
|
||||
* that they can only be used by a subset of the reflex consumers. See
|
||||
* the PRS chapter in the reference manual for the complete list of
|
||||
* supported asynchronous signals and consumers.
|
||||
*
|
||||
* @note
|
||||
* This function is not supported on EFM32GxxxFyyy parts.
|
||||
* In asynchronous mode, the edge detector only works in EM0 and should
|
||||
* not be used. The EDSEL parameter in PRS_CHx_CTRL register is set to 0 (OFF)
|
||||
* by default.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A channel to define the source and asynchronous signal for.
|
||||
*
|
||||
* @param[in] source
|
||||
* A source to select for the channel. Use one of PRS_CH_CTRL_SOURCESEL_x defines.
|
||||
*
|
||||
* @param[in] signal
|
||||
* An asynchronous signal (for selected @p source) to use. Use one of the
|
||||
* PRS_CH_CTRL_SIGSEL_x defines that support asynchronous operation.
|
||||
******************************************************************************/
|
||||
SL_WEAK void PRS_SourceAsyncSignalSet(unsigned int ch,
|
||||
uint32_t source,
|
||||
uint32_t signal)
|
||||
{
|
||||
PRS_ConnectSignal(ch, prsTypeAsync, (PRS_Signal_t) (source | signal));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_PRS_ROUTELOC0_MASK) || (defined(_PRS_ROUTE_MASK) && (_PRS_ROUTE_MASK))
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Send the output of a PRS channel to a GPIO pin.
|
||||
*
|
||||
* @details
|
||||
* This function is used to send the output of a PRS channel to a GPIO pin.
|
||||
* Note that there are certain restrictions to where a PRS channel can be
|
||||
* routed. Consult the datasheet of the device to see if a channel can be
|
||||
* routed to the requested GPIO pin.
|
||||
*
|
||||
* @param[in] ch
|
||||
* PRS channel number.
|
||||
*
|
||||
* @param[in] location
|
||||
* PRS routing location.
|
||||
******************************************************************************/
|
||||
void PRS_GpioOutputLocation(unsigned int ch,
|
||||
unsigned int location)
|
||||
{
|
||||
EFM_ASSERT(ch < PRS_CHAN_COUNT);
|
||||
|
||||
#if defined(_PRS_ROUTE_MASK)
|
||||
PRS->ROUTE |= (location << _PRS_ROUTE_LOCATION_SHIFT)
|
||||
| (1 << ch);
|
||||
#else
|
||||
uint32_t shift = (ch % 4) * 8;
|
||||
uint32_t mask = location << shift;
|
||||
uint32_t locationGroup = ch / 4;
|
||||
/* Since all ROUTELOCx registers are in consecutive memory locations, treat them
|
||||
* as an array starting at ROUTELOC0 and use locationGroup to index into this array */
|
||||
volatile uint32_t * routeloc = &PRS->ROUTELOC0;
|
||||
routeloc[locationGroup] |= mask;
|
||||
PRS->ROUTEPEN |= 1 << ch;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Search for the first free PRS channel.
|
||||
*
|
||||
* @param[in] type
|
||||
* PRS channel type. This can be either @ref prsTypeAsync or
|
||||
* @ref prsTypeSync.
|
||||
*
|
||||
* @return
|
||||
* Channel number >= 0 if an unused PRS channel was found. If no free PRS
|
||||
* channel was found then -1 is returned.
|
||||
******************************************************************************/
|
||||
SL_WEAK int PRS_GetFreeChannel(PRS_ChType_t type)
|
||||
{
|
||||
int ch = -1;
|
||||
PRS_Signal_t signal;
|
||||
int max;
|
||||
|
||||
if (type == prsTypeAsync) {
|
||||
max = PRS_ASYNC_CHAN_COUNT;
|
||||
} else {
|
||||
max = PRS_SYNC_CHAN_COUNT;
|
||||
}
|
||||
|
||||
for (int i = 0; i < max; i++) {
|
||||
signal = getSignal(i, type);
|
||||
if (signal == prsSignalNone) {
|
||||
ch = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Reset all PRS channels
|
||||
*
|
||||
* @details
|
||||
* This function will reset all the PRS channel configuration.
|
||||
******************************************************************************/
|
||||
void PRS_Reset(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
PRS->ASYNC_SWLEVEL = 0;
|
||||
for (i = 0; i < PRS_ASYNC_CHAN_COUNT; i++) {
|
||||
PRS->ASYNC_CH[i].CTRL = _PRS_ASYNC_CH_CTRL_RESETVALUE;
|
||||
}
|
||||
for (i = 0; i < PRS_SYNC_CHAN_COUNT; i++) {
|
||||
PRS->SYNC_CH[i].CTRL = _PRS_SYNC_CH_CTRL_RESETVALUE;
|
||||
}
|
||||
#else
|
||||
PRS->SWLEVEL = 0x0;
|
||||
for (i = 0; i < PRS_CHAN_COUNT; i++) {
|
||||
PRS->CH[i].CTRL = _PRS_CH_CTRL_RESETVALUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Connect a PRS signal to a channel.
|
||||
*
|
||||
* @details
|
||||
* This function will make the PRS signal available on the specific channel.
|
||||
* Only a single PRS signal can be connected to any given channel.
|
||||
*
|
||||
* @param[in] ch
|
||||
* PRS channel number.
|
||||
*
|
||||
* @param[in] type
|
||||
* PRS channel type. This can be either @ref prsTypeAsync or
|
||||
* @ref prsTypeSync.
|
||||
*
|
||||
* @param[in] signal
|
||||
* This is the PRS signal that should be placed on the channel.
|
||||
******************************************************************************/
|
||||
void PRS_ConnectSignal(unsigned int ch, PRS_ChType_t type, PRS_Signal_t signal)
|
||||
{
|
||||
#if defined(_PRS_ASYNC_CH_CTRL_MASK)
|
||||
// Series 2 devices
|
||||
uint32_t sourceField = ((uint32_t)signal & _PRS_ASYNC_CH_CTRL_SOURCESEL_MASK)
|
||||
>> _PRS_ASYNC_CH_CTRL_SOURCESEL_SHIFT;
|
||||
uint32_t signalField = ((uint32_t)signal & _PRS_ASYNC_CH_CTRL_SIGSEL_MASK)
|
||||
>> _PRS_ASYNC_CH_CTRL_SIGSEL_SHIFT;
|
||||
if (type == prsTypeAsync) {
|
||||
EFM_ASSERT(ch < PRS_ASYNC_CHAN_COUNT);
|
||||
PRS->ASYNC_CH[ch].CTRL = PRS_ASYNC_CH_CTRL_FNSEL_A
|
||||
| (sourceField << _PRS_ASYNC_CH_CTRL_SOURCESEL_SHIFT)
|
||||
| (signalField << _PRS_ASYNC_CH_CTRL_SIGSEL_SHIFT);
|
||||
} else {
|
||||
EFM_ASSERT(ch < PRS_SYNC_CHAN_COUNT);
|
||||
signalField = PRS_ConvertToSyncSignal(sourceField, signalField);
|
||||
sourceField = PRS_ConvertToSyncSource(sourceField);
|
||||
PRS->SYNC_CH[ch].CTRL = (sourceField << _PRS_SYNC_CH_CTRL_SOURCESEL_SHIFT)
|
||||
| (signalField << _PRS_SYNC_CH_CTRL_SIGSEL_SHIFT);
|
||||
}
|
||||
#else
|
||||
// Series 0 and Series 1 devices
|
||||
uint32_t signalField = (uint32_t) signal & (_PRS_CH_CTRL_SOURCESEL_MASK
|
||||
| _PRS_CH_CTRL_SIGSEL_MASK);
|
||||
if (type == prsTypeAsync) {
|
||||
#if defined(PRS_ASYNC_SUPPORTED)
|
||||
EFM_ASSERT(ch < PRS_ASYNC_CHAN_COUNT);
|
||||
PRS->CH[ch].CTRL = PRS_CH_CTRL_EDSEL_OFF
|
||||
| PRS_CH_CTRL_ASYNC
|
||||
| signalField;
|
||||
#endif
|
||||
} else {
|
||||
EFM_ASSERT(ch < PRS_SYNC_CHAN_COUNT);
|
||||
PRS->CH[ch].CTRL = PRS_CH_CTRL_EDSEL_OFF
|
||||
| signalField;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Connect a peripheral consumer to a PRS channel.
|
||||
*
|
||||
* @details
|
||||
* Different peripherals can use PRS channels as their input. This function
|
||||
* can be used to connect a peripheral consumer to a PRS channel. Multiple
|
||||
* consumers can be connected to a single PRS channel.
|
||||
*
|
||||
* @param[in] ch
|
||||
* PRS channel number.
|
||||
*
|
||||
* @param[in] type
|
||||
* PRS channel type. This can be either @ref prsTypeAsync or
|
||||
* @ref prsTypeSync.
|
||||
*
|
||||
* @param[in] consumer
|
||||
* This is the PRS consumer.
|
||||
******************************************************************************/
|
||||
SL_WEAK void PRS_ConnectConsumer(unsigned int ch, PRS_ChType_t type, PRS_Consumer_t consumer)
|
||||
{
|
||||
EFM_ASSERT((uint32_t)consumer <= 0xFFF);
|
||||
volatile uint32_t * addr = (volatile uint32_t *) PRS;
|
||||
uint32_t offset = (uint32_t) consumer;
|
||||
addr = addr + offset / 4;
|
||||
|
||||
if (consumer != prsConsumerNone) {
|
||||
if (type == prsTypeAsync) {
|
||||
*addr = ch << _PRS_CONSUMER_TIMER0_CC0_PRSSEL_SHIFT;
|
||||
} else {
|
||||
*addr = ch << _PRS_CONSUMER_TIMER0_CC0_SPRSSEL_SHIFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Send the output of a PRS channel to a GPIO pin.
|
||||
*
|
||||
* @details
|
||||
* This function is used to send the output of a PRS channel to a GPIO pin.
|
||||
* Note that there are certain restrictions to where a PRS channel can be
|
||||
* routed. Consult the datasheet of the device to see if a channel can be
|
||||
* routed to the requested GPIO pin. Some devices for instance can only route
|
||||
* the async channels 0-5 on GPIO pins PAx and PBx while async channels 6-11
|
||||
* can only be routed to GPIO pins PCx and PDx
|
||||
*
|
||||
* @param[in] ch
|
||||
* PRS channel number.
|
||||
*
|
||||
* @param[in] type
|
||||
* PRS channel type. This can be either @ref prsTypeAsync or
|
||||
* @ref prsTypeSync.
|
||||
*
|
||||
* @param[in] port
|
||||
* GPIO port
|
||||
*
|
||||
* @param[in] pin
|
||||
* GPIO pin
|
||||
******************************************************************************/
|
||||
SL_WEAK void PRS_PinOutput(unsigned int ch, PRS_ChType_t type, GPIO_Port_TypeDef port, uint8_t pin)
|
||||
{
|
||||
volatile uint32_t * addr;
|
||||
if (type == prsTypeAsync) {
|
||||
addr = &GPIO->PRSROUTE[0].ASYNCH0ROUTE;
|
||||
} else {
|
||||
addr = &GPIO->PRSROUTE[0].SYNCH0ROUTE;
|
||||
}
|
||||
addr += ch;
|
||||
*addr = ((uint32_t)port << _GPIO_PRS_ASYNCH0ROUTE_PORT_SHIFT)
|
||||
| ((uint32_t)pin << _GPIO_PRS_ASYNCH0ROUTE_PIN_SHIFT);
|
||||
|
||||
if (type == prsTypeAsync) {
|
||||
GPIO->PRSROUTE[0].ROUTEEN |= 0x1 << (ch + _GPIO_PRS_ROUTEEN_ASYNCH0PEN_SHIFT);
|
||||
} else {
|
||||
GPIO->PRSROUTE[0].ROUTEEN |= 0x1 << (ch + _GPIO_PRS_ROUTEEN_SYNCH0PEN_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Combine two PRS channels using a logic function.
|
||||
*
|
||||
* @details
|
||||
* This function allows you to combine the output of one PRS channel with the
|
||||
* the signal of another PRS channel using various logic functions. Note that
|
||||
* for series 2, config 1 devices, the hardware only allows a PRS channel to
|
||||
* be combined with the previous channel. So for instance channel 5 can be
|
||||
* combined only with channel 4.
|
||||
*
|
||||
* The logic function operates on two PRS channels called A and B. The output
|
||||
* of PRS channel B is combined with the PRS source configured for channel A
|
||||
* to produce an output. This output is used as the output of channel A.
|
||||
*
|
||||
* @param[in] chA
|
||||
* PRS Channel for the A input.
|
||||
*
|
||||
* @param[in] chB
|
||||
* PRS Channel for the B input.
|
||||
*
|
||||
* @param[in] logic
|
||||
* The logic function to use when combining the Channel A and Channel B. The
|
||||
* output of the logic function is the output of Channel A. Function like
|
||||
* AND, OR, XOR, NOT and more are available.
|
||||
******************************************************************************/
|
||||
SL_WEAK void PRS_Combine(unsigned int chA, unsigned int chB, PRS_Logic_t logic)
|
||||
{
|
||||
EFM_ASSERT(chA < PRS_ASYNC_CHAN_COUNT);
|
||||
EFM_ASSERT(chB < PRS_ASYNC_CHAN_COUNT);
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
|
||||
EFM_ASSERT(chA == ((chB + 1) % PRS_ASYNC_CHAN_COUNT));
|
||||
PRS->ASYNC_CH[chA].CTRL = (PRS->ASYNC_CH[chA].CTRL & ~_PRS_ASYNC_CH_CTRL_FNSEL_MASK)
|
||||
| ((uint32_t)logic << _PRS_ASYNC_CH_CTRL_FNSEL_SHIFT);
|
||||
|
||||
#else
|
||||
PRS->ASYNC_CH[chA].CTRL = (PRS->ASYNC_CH[chA].CTRL
|
||||
& ~(_PRS_ASYNC_CH_CTRL_FNSEL_MASK
|
||||
| _PRS_ASYNC_CH_CTRL_AUXSEL_MASK))
|
||||
| ((uint32_t)logic << _PRS_ASYNC_CH_CTRL_FNSEL_SHIFT)
|
||||
| ((uint32_t)chB << _PRS_ASYNC_CH_CTRL_AUXSEL_SHIFT);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} (end addtogroup prs) */
|
||||
#endif /* defined(PRS_COUNT) && (PRS_COUNT > 0) */
|
||||
402
Libs/platform/emlib/src/em_rmu.c
Normal file
402
Libs/platform/emlib/src/em_rmu.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Reset Management Unit (RMU) peripheral module peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_rmu.h"
|
||||
#if (defined(RMU_COUNT) && (RMU_COUNT > 0)) || (_EMU_RSTCTRL_MASK)
|
||||
|
||||
#include "sl_common.h"
|
||||
#include "em_emu.h"
|
||||
#include "em_bus.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup rmu RMU - Reset Management Unit
|
||||
* @brief Reset Management Unit (RMU) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the RMU peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. RMU ensures correct reset operation and is
|
||||
* responsible for connecting the different reset sources to the reset lines of
|
||||
* the MCU or SoC.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
***************************** DEFINES *********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
#if defined(_RMU_RSTCAUSE_MASK)
|
||||
|
||||
/** Reset cause XMASKS for series-0 and series-1 devices.
|
||||
Reset cause zero and "don't care" bit definitions (XMASKs).
|
||||
An XMASK 1 bit marks a bit that must be zero in RMU_RSTCAUSE. A 0 in XMASK
|
||||
is a "don't care" bit in RMU_RSTCAUSE if also 0 in resetCauseMask
|
||||
in @ref RMU_ResetCauseMasks_Typedef. */
|
||||
|
||||
/* EFM32G */
|
||||
#if (_RMU_RSTCAUSE_MASK == 0x0000007FUL)
|
||||
#define RMU_RSTCAUSE_PORST_XMASK 0x00000000UL /** 0000000000000000 < Power On Reset */
|
||||
#define RMU_RSTCAUSE_BODUNREGRST_XMASK 0x00000001UL /** 0000000000000001 < Brown Out Detector Unregulated Domain Reset */
|
||||
#define RMU_RSTCAUSE_BODREGRST_XMASK 0x0000001BUL /** 0000000000011011 < Brown Out Detector Regulated Domain Reset */
|
||||
#define RMU_RSTCAUSE_EXTRST_XMASK 0x00000003UL /** 0000000000000011 < External Pin Reset */
|
||||
#define RMU_RSTCAUSE_WDOGRST_XMASK 0x00000003UL /** 0000000000000011 < Watchdog Reset */
|
||||
#define RMU_RSTCAUSE_LOCKUPRST_XMASK 0x0000001FUL /** 0000000000011111 < LOCKUP Reset */
|
||||
#define RMU_RSTCAUSE_SYSREQRST_XMASK 0x0000001FUL /** 0000000000011111 < System Request Reset */
|
||||
#define NUM_RSTCAUSES 7
|
||||
|
||||
/* EFM32TG, EFM32HG, EZR32HG, EFM32ZG */
|
||||
#elif (_RMU_RSTCAUSE_MASK == 0x000007FFUL)
|
||||
#define RMU_RSTCAUSE_PORST_XMASK 0x00000000UL /** 0000000000000000 < Power On Reset */
|
||||
#define RMU_RSTCAUSE_BODUNREGRST_XMASK 0x00000081UL /** 0000000010000001 < Brown Out Detector Unregulated Domain Reset */
|
||||
#define RMU_RSTCAUSE_BODREGRST_XMASK 0x00000091UL /** 0000000010010001 < Brown Out Detector Regulated Domain Reset */
|
||||
#define RMU_RSTCAUSE_EXTRST_XMASK 0x00000001UL /** 0000000000000001 < External Pin Reset */
|
||||
#define RMU_RSTCAUSE_WDOGRST_XMASK 0x00000003UL /** 0000000000000011 < Watchdog Reset */
|
||||
#define RMU_RSTCAUSE_LOCKUPRST_XMASK 0x0000EFDFUL /** 1110111111011111 < LOCKUP Reset */
|
||||
#define RMU_RSTCAUSE_SYSREQRST_XMASK 0x0000EF9FUL /** 1110111110011111 < System Request Reset */
|
||||
#define RMU_RSTCAUSE_EM4RST_XMASK 0x00000719UL /** 0000011100011001 < EM4 Reset */
|
||||
#define RMU_RSTCAUSE_EM4WURST_XMASK 0x00000619UL /** 0000011000011001 < EM4 Wake-up Reset */
|
||||
#define RMU_RSTCAUSE_BODAVDD0_XMASK 0x0000041FUL /** 0000010000011111 < AVDD0 Bod Reset. */
|
||||
#define RMU_RSTCAUSE_BODAVDD1_XMASK 0x0000021FUL /** 0000001000011111 < AVDD1 Bod Reset. */
|
||||
#define NUM_RSTCAUSES 11
|
||||
|
||||
/* EFM32GG, EFM32LG, EZR32LG, EFM32WG, EZR32WG */
|
||||
#elif (_RMU_RSTCAUSE_MASK == 0x0000FFFFUL)
|
||||
#define RMU_RSTCAUSE_PORST_XMASK 0x00000000UL /** 0000000000000000 < Power On Reset */
|
||||
#define RMU_RSTCAUSE_BODUNREGRST_XMASK 0x00000081UL /** 0000000010000001 < Brown Out Detector Unregulated Domain Reset */
|
||||
#define RMU_RSTCAUSE_BODREGRST_XMASK 0x00000091UL /** 0000000010010001 < Brown Out Detector Regulated Domain Reset */
|
||||
#define RMU_RSTCAUSE_EXTRST_XMASK 0x00000001UL /** 0000000000000001 < External Pin Reset */
|
||||
#define RMU_RSTCAUSE_WDOGRST_XMASK 0x00000003UL /** 0000000000000011 < Watchdog Reset */
|
||||
#define RMU_RSTCAUSE_LOCKUPRST_XMASK 0x0000EFDFUL /** 1110111111011111 < LOCKUP Reset */
|
||||
#define RMU_RSTCAUSE_SYSREQRST_XMASK 0x0000EF9FUL /** 1110111110011111 < System Request Reset */
|
||||
#define RMU_RSTCAUSE_EM4RST_XMASK 0x00000719UL /** 0000011100011001 < EM4 Reset */
|
||||
#define RMU_RSTCAUSE_EM4WURST_XMASK 0x00000619UL /** 0000011000011001 < EM4 Wake-up Reset */
|
||||
#define RMU_RSTCAUSE_BODAVDD0_XMASK 0x0000041FUL /** 0000010000011111 < AVDD0 Bod Reset */
|
||||
#define RMU_RSTCAUSE_BODAVDD1_XMASK 0x0000021FUL /** 0000001000011111 < AVDD1 Bod Reset */
|
||||
#define RMU_RSTCAUSE_BUBODVDDDREG_XMASK 0x00000001UL /** 0000000000000001 < Backup Brown Out Detector, VDD_DREG */
|
||||
#define RMU_RSTCAUSE_BUBODBUVIN_XMASK 0x00000001UL /** 0000000000000001 < Backup Brown Out Detector, BU_VIN */
|
||||
#define RMU_RSTCAUSE_BUBODUNREG_XMASK 0x00000001UL /** 0000000000000001 < Backup Brown Out Detector Unregulated Domain */
|
||||
#define RMU_RSTCAUSE_BUBODREG_XMASK 0x00000001UL /** 0000000000000001 < Backup Brown Out Detector Regulated Domain */
|
||||
#define RMU_RSTCAUSE_BUMODERST_XMASK 0x00000001UL /** 0000000000000001 < Backup mode reset */
|
||||
#define NUM_RSTCAUSES 16
|
||||
|
||||
/* EFM32xG1, EFM32xG12, EFM32xG13 */
|
||||
#elif ((_RMU_RSTCAUSE_MASK & 0x0FFFFFFF) == 0x00010F1DUL)
|
||||
#define RMU_RSTCAUSE_PORST_XMASK 0x00000000UL /** 0000000000000000 < Power On Reset */
|
||||
#define RMU_RSTCAUSE_BODAVDD_XMASK 0x00000001UL /** 0000000000000001 < AVDD BOD Reset */
|
||||
#define RMU_RSTCAUSE_BODDVDD_XMASK 0x00000001UL /** 0000000000000001 < DVDD BOD Reset */
|
||||
#define RMU_RSTCAUSE_BODREGRST_XMASK 0x00000001UL /** 0000000000000001 < Regulated Domain (DEC) BOD Reset */
|
||||
#define RMU_RSTCAUSE_EXTRST_XMASK 0x00000001UL /** 0000000000000001 < External Pin Reset */
|
||||
#define RMU_RSTCAUSE_LOCKUPRST_XMASK 0x0000001DUL /** 0000000000011101 < LOCKUP Reset */
|
||||
#define RMU_RSTCAUSE_SYSREQRST_XMASK 0x0000001DUL /** 0000000000011101 < System Request Reset */
|
||||
#define RMU_RSTCAUSE_WDOGRST_XMASK 0x0000001DUL /** 0000000000011101 < Watchdog Reset */
|
||||
#define RMU_RSTCAUSE_EM4RST_XMASK 0x0000001DUL /** 0000000000011101 < EM4H/S Reset */
|
||||
#define NUM_RSTCAUSES 9
|
||||
|
||||
/* EFM32GG11 */
|
||||
#elif ((_RMU_RSTCAUSE_MASK & 0x0FFFFFFF) == 0x00011F1DUL)
|
||||
#define RMU_RSTCAUSE_PORST_XMASK 0x00000000UL /** 0000000000000000 < Power On Reset */
|
||||
#define RMU_RSTCAUSE_BODAVDD_XMASK 0x00000001UL /** 0000000000000001 < AVDD BOD Reset */
|
||||
#define RMU_RSTCAUSE_BODDVDD_XMASK 0x00000001UL /** 0000000000000001 < DVDD BOD Reset */
|
||||
#define RMU_RSTCAUSE_BODREGRST_XMASK 0x00000001UL /** 0000000000000001 < Regulated Domain (DEC) BOD Reset */
|
||||
#define RMU_RSTCAUSE_EXTRST_XMASK 0x00000001UL /** 0000000000000001 < External Pin Reset */
|
||||
#define RMU_RSTCAUSE_LOCKUPRST_XMASK 0x0000001DUL /** 0000000000011101 < LOCKUP Reset */
|
||||
#define RMU_RSTCAUSE_SYSREQRST_XMASK 0x0000001DUL /** 0000000000011101 < System Request Reset */
|
||||
#define RMU_RSTCAUSE_WDOGRST_XMASK 0x0000001DUL /** 0000000000011101 < Watchdog Reset */
|
||||
#define RMU_RSTCAUSE_BUMODERST_XMASK 0x0000001DUL /** 0000000000011101 < Backup mode reset */
|
||||
#define RMU_RSTCAUSE_EM4RST_XMASK 0x0000001DUL /** 0000000000011101 < EM4H/S Reset */
|
||||
#define NUM_RSTCAUSES 10
|
||||
|
||||
#else
|
||||
#error "RMU_RSTCAUSE XMASKs are not defined for this family."
|
||||
#endif
|
||||
|
||||
/* Pin reset definitions. */
|
||||
#define LB_CLW0 (*((volatile uint32_t *)(LOCKBITS_BASE) +122))
|
||||
#define LB_CLW0_PINRESETSOFT (1 << 2)
|
||||
|
||||
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
||||
/* Fix for errata EMU_E208 - Occasional Full Reset After Exiting EM4H. */
|
||||
#define ERRATA_FIX_EMU_E208_EN
|
||||
#endif
|
||||
|
||||
#endif /* #if defined(_RMU_RSTCAUSE_MASK) */
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* STRUCTS ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(_RMU_RSTCAUSE_MASK)
|
||||
/** Reset cause mask type for series-0 and series-1 devices. */
|
||||
typedef struct {
|
||||
/** Reset cause 1 bits. */
|
||||
uint32_t resetCauseMask;
|
||||
/** Reset cause 0 and "don't care" bits. */
|
||||
uint32_t resetCauseZeroXMask;
|
||||
} RMU_ResetCauseMasks_Typedef;
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* TYPEDEFS **********************************
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(_RMU_RSTCAUSE_MASK)
|
||||
/** Reset cause mask table. */
|
||||
static const RMU_ResetCauseMasks_Typedef resetCauseMasks[NUM_RSTCAUSES] =
|
||||
{
|
||||
{ RMU_RSTCAUSE_PORST, RMU_RSTCAUSE_PORST_XMASK },
|
||||
#if defined(RMU_RSTCAUSE_BODUNREGRST)
|
||||
{ RMU_RSTCAUSE_BODUNREGRST, RMU_RSTCAUSE_BODUNREGRST_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_BODREGRST)
|
||||
{ RMU_RSTCAUSE_BODREGRST, RMU_RSTCAUSE_BODREGRST_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_AVDDBOD)
|
||||
{ RMU_RSTCAUSE_AVDDBOD, RMU_RSTCAUSE_BODAVDD_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_DVDDBOD)
|
||||
{ RMU_RSTCAUSE_DVDDBOD, RMU_RSTCAUSE_BODDVDD_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_DECBOD)
|
||||
{ RMU_RSTCAUSE_DECBOD, RMU_RSTCAUSE_BODREGRST_XMASK },
|
||||
#endif
|
||||
{ RMU_RSTCAUSE_EXTRST, RMU_RSTCAUSE_EXTRST_XMASK },
|
||||
{ RMU_RSTCAUSE_WDOGRST, RMU_RSTCAUSE_WDOGRST_XMASK },
|
||||
{ RMU_RSTCAUSE_LOCKUPRST, RMU_RSTCAUSE_LOCKUPRST_XMASK },
|
||||
{ RMU_RSTCAUSE_SYSREQRST, RMU_RSTCAUSE_SYSREQRST_XMASK },
|
||||
#if defined(RMU_RSTCAUSE_EM4RST)
|
||||
{ RMU_RSTCAUSE_EM4RST, RMU_RSTCAUSE_EM4RST_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_EM4WURST)
|
||||
{ RMU_RSTCAUSE_EM4WURST, RMU_RSTCAUSE_EM4WURST_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_BODAVDD0)
|
||||
{ RMU_RSTCAUSE_BODAVDD0, RMU_RSTCAUSE_BODAVDD0_XMASK },
|
||||
#endif
|
||||
#if defined(RMU_RSTCAUSE_BODAVDD1)
|
||||
{ RMU_RSTCAUSE_BODAVDD1, RMU_RSTCAUSE_BODAVDD1_XMASK },
|
||||
#endif
|
||||
#if defined(BU_PRESENT) && defined(_SILICON_LABS_32B_SERIES_0)
|
||||
{ RMU_RSTCAUSE_BUBODVDDDREG, RMU_RSTCAUSE_BUBODVDDDREG_XMASK },
|
||||
{ RMU_RSTCAUSE_BUBODBUVIN, RMU_RSTCAUSE_BUBODBUVIN_XMASK },
|
||||
{ RMU_RSTCAUSE_BUBODUNREG, RMU_RSTCAUSE_BUBODUNREG_XMASK },
|
||||
{ RMU_RSTCAUSE_BUBODREG, RMU_RSTCAUSE_BUBODREG_XMASK },
|
||||
{ RMU_RSTCAUSE_BUMODERST, RMU_RSTCAUSE_BUMODERST_XMASK },
|
||||
#elif defined(RMU_RSTCAUSE_BUMODERST)
|
||||
{ RMU_RSTCAUSE_BUMODERST, RMU_RSTCAUSE_BUMODERST_XMASK },
|
||||
#endif
|
||||
};
|
||||
#endif /* #if defined(_RMU_RSTCAUSE_MASK) */
|
||||
|
||||
/*******************************************************************************
|
||||
******************************** TEST ********************************
|
||||
******************************************************************************/
|
||||
#if defined(EMLIB_REGRESSION_TEST)
|
||||
/* A test variable that replaces the RSTCAUSE cause register when testing
|
||||
the RMU_ResetCauseGet function. */
|
||||
extern uint32_t rstCause;
|
||||
#else
|
||||
static uint32_t rstCause = UINT32_MAX;
|
||||
#endif
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Disable/enable reset for various peripherals and signal sources.
|
||||
*
|
||||
* @param[in] reset Reset types to enable/disable.s
|
||||
*
|
||||
* @param[in] mode Reset mode.
|
||||
******************************************************************************/
|
||||
#if defined(__GNUC__) && __GNUC__ >= 11
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wanalyzer-shift-count-overflow"
|
||||
#endif
|
||||
void RMU_ResetControl(RMU_Reset_TypeDef reset, RMU_ResetMode_TypeDef mode)
|
||||
{
|
||||
/* Note that the RMU supports bit-band access, but not peripheral bit-field set/clear. */
|
||||
#if defined(_RMU_CTRL_PINRMODE_MASK)
|
||||
uint32_t val;
|
||||
#endif
|
||||
uint32_t shift;
|
||||
|
||||
shift = SL_CTZ((uint32_t)reset);
|
||||
#if defined(_EMU_RSTCTRL_MASK)
|
||||
BUS_RegBitWrite(&EMU->RSTCTRL, (uint32_t)shift, mode ? 1 : 0);
|
||||
#elif defined(_RMU_CTRL_PINRMODE_MASK)
|
||||
EFM_ASSERT(shift < 32);
|
||||
val = (uint32_t)mode << shift;
|
||||
RMU->CTRL = (RMU->CTRL & ~reset) | val;
|
||||
#else
|
||||
BUS_RegBitWrite(&RMU->CTRL, (uint32_t)shift, mode ? 1 : 0);
|
||||
#endif
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__ >= 11
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Clear the reset cause register.
|
||||
*
|
||||
* @details
|
||||
* This function clears all the reset cause bits of the RSTCAUSE register.
|
||||
* The reset cause bits must be cleared by software before a new reset occurs.
|
||||
* Otherwise, reset causes may accumulate. See @ref RMU_ResetCauseGet().
|
||||
******************************************************************************/
|
||||
void RMU_ResetCauseClear(void)
|
||||
{
|
||||
#if defined(_EMU_RSTCTRL_MASK)
|
||||
EMU->CMD_SET = EMU_CMD_RSTCAUSECLR;
|
||||
#else
|
||||
RMU->CMD = RMU_CMD_RCCLR;
|
||||
#endif
|
||||
|
||||
#if defined(EMU_AUXCTRL_HRCCLR)
|
||||
{
|
||||
uint32_t locked;
|
||||
|
||||
/* Clear reset causes not cleared with the RMU CMD register. */
|
||||
/* (If EMU registers are locked, they must be unlocked first) */
|
||||
locked = EMU->LOCK & EMU_LOCK_LOCKKEY_LOCKED;
|
||||
if (locked) {
|
||||
EMU_Unlock();
|
||||
}
|
||||
|
||||
BUS_RegBitWrite(&(EMU->AUXCTRL), _EMU_AUXCTRL_HRCCLR_SHIFT, 1);
|
||||
BUS_RegBitWrite(&(EMU->AUXCTRL), _EMU_AUXCTRL_HRCCLR_SHIFT, 0);
|
||||
|
||||
if (locked) {
|
||||
EMU_Lock();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the cause of the last reset.
|
||||
*
|
||||
* @details
|
||||
* To be useful, the reset cause must be cleared by software before a new
|
||||
* reset occurs. Otherwise, reset causes may accumulate. See @ref
|
||||
* RMU_ResetCauseClear(). This function call will return the main cause for
|
||||
* reset, which can be a bit mask (several causes) and clear away "noise".
|
||||
*
|
||||
* @return
|
||||
* A reset cause mask. See the reference manual for a description
|
||||
* of the reset cause mask.
|
||||
******************************************************************************/
|
||||
uint32_t RMU_ResetCauseGet(void)
|
||||
{
|
||||
#if !defined(EMLIB_REGRESSION_TEST)
|
||||
if (rstCause != UINT32_MAX) {
|
||||
// RMU_ResetCauseGet() has already been called since boot. Return what was already obtained.
|
||||
return rstCause;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_EMU_RSTCAUSE_MASK)
|
||||
#if !defined(EMLIB_REGRESSION_TEST)
|
||||
rstCause = EMU->RSTCAUSE;
|
||||
#endif
|
||||
return rstCause;
|
||||
#endif
|
||||
|
||||
#if defined(_RMU_RSTCAUSE_MASK)
|
||||
#if !defined(EMLIB_REGRESSION_TEST)
|
||||
rstCause = RMU->RSTCAUSE;
|
||||
#endif
|
||||
uint32_t validRstCause = 0;
|
||||
uint32_t zeroXMask;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < NUM_RSTCAUSES; i++) {
|
||||
zeroXMask = resetCauseMasks[i].resetCauseZeroXMask;
|
||||
#if defined(_SILICON_LABS_32B_SERIES_1)
|
||||
/* Handle soft/hard pin reset. */
|
||||
if (!(LB_CLW0 & LB_CLW0_PINRESETSOFT)) {
|
||||
/* RSTCAUSE_EXTRST must be 0 if pin reset is configured as hard reset. */
|
||||
switch (resetCauseMasks[i].resetCauseMask) {
|
||||
case RMU_RSTCAUSE_LOCKUPRST:
|
||||
/* Fallthrough */
|
||||
case RMU_RSTCAUSE_SYSREQRST:
|
||||
/* Fallthrough */
|
||||
case RMU_RSTCAUSE_WDOGRST:
|
||||
/* Fallthrough */
|
||||
case RMU_RSTCAUSE_EM4RST:
|
||||
zeroXMask |= RMU_RSTCAUSE_EXTRST;
|
||||
break;
|
||||
default:
|
||||
/* MISRA requires a default case. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_EMU_EM4CTRL_MASK) && defined(ERRATA_FIX_EMU_E208_EN)
|
||||
/* Ignore BOD flags impacted by EMU_E208. */
|
||||
if (*(volatile uint32_t *)(EMU_BASE + 0x88) & (0x1 << 8)) {
|
||||
zeroXMask &= ~(RMU_RSTCAUSE_DECBOD
|
||||
| RMU_RSTCAUSE_DVDDBOD
|
||||
| RMU_RSTCAUSE_AVDDBOD);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check reset cause requirements. Note that a bit is "don't care" if 0 in
|
||||
both resetCauseMask and resetCauseZeroXMask. */
|
||||
if ((rstCause & resetCauseMasks[i].resetCauseMask)
|
||||
&& !(rstCause & zeroXMask)) {
|
||||
/* Add this reset-cause to the mask of qualified reset-causes. */
|
||||
validRstCause |= resetCauseMasks[i].resetCauseMask;
|
||||
}
|
||||
}
|
||||
#if defined(_EMU_EM4CTRL_MASK) && defined(ERRATA_FIX_EMU_E208_EN)
|
||||
/* Clear BOD flags impacted by EMU_E208. */
|
||||
if (validRstCause & RMU_RSTCAUSE_EM4RST) {
|
||||
validRstCause &= ~(RMU_RSTCAUSE_DECBOD
|
||||
| RMU_RSTCAUSE_DVDDBOD
|
||||
| RMU_RSTCAUSE_AVDDBOD);
|
||||
}
|
||||
#endif
|
||||
#if !defined(EMLIB_REGRESSION_TEST)
|
||||
// keep validRstCause in the static local variable for future calls
|
||||
rstCause = validRstCause
|
||||
#endif
|
||||
return validRstCause;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} (end addtogroup rmu) */
|
||||
#endif /* defined(RMU_COUNT) && (RMU_COUNT > 0) */
|
||||
433
Libs/platform/emlib/src/em_system.c
Normal file
433
Libs/platform/emlib/src/em_system.c
Normal file
@@ -0,0 +1,433 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief System Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_system.h"
|
||||
#include "sl_assert.h"
|
||||
#include <stddef.h>
|
||||
#if defined(SYSCFG_PRESENT)
|
||||
#include "em_syscfg.h"
|
||||
#endif
|
||||
/***************************************************************************//**
|
||||
* @addtogroup system
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
********************************* DEFINES *********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/* Bit mask used to extract the part number value without the new naming
|
||||
* bitfield. */
|
||||
#define SYSCFG_CHIPREV_PARTNUMBER1 0xFE0
|
||||
#define SYSCFG_CHIPREV_PARTNUMBER0 0xF
|
||||
|
||||
/* Bit mask to convert NON-SECURE to SECURE */
|
||||
#define CONVERT_NS_TO_S (~(1 << 28U))
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get a chip major/minor revision.
|
||||
*
|
||||
* @param[out] rev
|
||||
* A location to place the chip revision information.
|
||||
******************************************************************************/
|
||||
void SYSTEM_ChipRevisionGet(SYSTEM_ChipRevision_TypeDef *rev)
|
||||
{
|
||||
#if defined(_SYSCFG_CHIPREV_FAMILY_MASK) || defined(_SYSCFG_CHIPREV_PARTNUMBER_MASK)
|
||||
/* On series-2 (and higher) the revision info is in the SYSCFG->CHIPREV register. */
|
||||
uint32_t chiprev = SYSCFG_readChipRev();
|
||||
#if defined(_SYSCFG_CHIPREV_PARTNUMBER_MASK)
|
||||
rev->partNumber = ((chiprev & SYSCFG_CHIPREV_PARTNUMBER1) >> 5) | (chiprev & SYSCFG_CHIPREV_PARTNUMBER0);
|
||||
#else
|
||||
rev->family = (chiprev & _SYSCFG_CHIPREV_FAMILY_MASK) >> _SYSCFG_CHIPREV_FAMILY_SHIFT;
|
||||
#endif
|
||||
rev->major = (chiprev & _SYSCFG_CHIPREV_MAJOR_MASK) >> _SYSCFG_CHIPREV_MAJOR_SHIFT;
|
||||
rev->minor = (chiprev & _SYSCFG_CHIPREV_MINOR_MASK) >> _SYSCFG_CHIPREV_MINOR_SHIFT;
|
||||
#else
|
||||
uint8_t tmp;
|
||||
|
||||
EFM_ASSERT(rev);
|
||||
|
||||
/* CHIP FAMILY bit [5:2] */
|
||||
tmp = (uint8_t)(((ROMTABLE->PID1 & _ROMTABLE_PID1_FAMILYMSB_MASK)
|
||||
>> _ROMTABLE_PID1_FAMILYMSB_SHIFT) << 2);
|
||||
/* CHIP FAMILY bit [1:0] */
|
||||
tmp |= (uint8_t)((ROMTABLE->PID0 & _ROMTABLE_PID0_FAMILYLSB_MASK)
|
||||
>> _ROMTABLE_PID0_FAMILYLSB_SHIFT);
|
||||
rev->family = tmp;
|
||||
|
||||
/* CHIP MAJOR bit [3:0] */
|
||||
rev->major = (uint8_t)((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK)
|
||||
>> _ROMTABLE_PID0_REVMAJOR_SHIFT);
|
||||
|
||||
/* CHIP MINOR bit [7:4] */
|
||||
tmp = (uint8_t)(((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK)
|
||||
>> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4);
|
||||
/* CHIP MINOR bit [3:0] */
|
||||
tmp |= (uint8_t)((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK)
|
||||
>> _ROMTABLE_PID3_REVMINORLSB_SHIFT);
|
||||
rev->minor = tmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get a factory calibration value for a given peripheral register.
|
||||
*
|
||||
* @param[in] regAddress
|
||||
* The peripheral calibration register address to get a calibration value for. If
|
||||
* the calibration value is found, this register is updated with the
|
||||
* calibration value.
|
||||
*
|
||||
* @return
|
||||
* True if a calibration value exists, false otherwise.
|
||||
******************************************************************************/
|
||||
bool SYSTEM_GetCalibrationValue(volatile uint32_t *regAddress)
|
||||
{
|
||||
SYSTEM_CalAddrVal_TypeDef * p, * end;
|
||||
|
||||
uint32_t s_regAddress = (uint32_t)regAddress;
|
||||
s_regAddress = s_regAddress & CONVERT_NS_TO_S;
|
||||
|
||||
#if defined(MSC_FLASH_CHIPCONFIG_MEM_BASE)
|
||||
p = (SYSTEM_CalAddrVal_TypeDef *)MSC_FLASH_CHIPCONFIG_MEM_BASE;
|
||||
end = (SYSTEM_CalAddrVal_TypeDef *)MSC_FLASH_CHIPCONFIG_MEM_END;
|
||||
#else
|
||||
p = (SYSTEM_CalAddrVal_TypeDef *)(DEVINFO_BASE & 0xFFFFF000U);
|
||||
end = (SYSTEM_CalAddrVal_TypeDef *)DEVINFO_BASE;
|
||||
#endif
|
||||
|
||||
for (; p < end; p++) {
|
||||
if (p->address == 0) {
|
||||
/* p->address == 0 marks the end of the table */
|
||||
return false;
|
||||
}
|
||||
if (p->address == s_regAddress) {
|
||||
*regAddress = p->calValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Nothing found for regAddress. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get family security capability.
|
||||
*
|
||||
* @note
|
||||
* This function retrieves the family security capability based on the
|
||||
* device number. The device number is one letter and 3 digits:
|
||||
* DEVICENUMBER = (alpha-'A')*1000 + numeric. i.e. 0d = "A000"; 1123d = "B123".
|
||||
* The security capabilities are represented by ::SYSTEM_SecurityCapability_TypeDef.
|
||||
*
|
||||
* @return
|
||||
* Security capability of MCU.
|
||||
******************************************************************************/
|
||||
SYSTEM_SecurityCapability_TypeDef SYSTEM_GetSecurityCapability(void)
|
||||
{
|
||||
SYSTEM_SecurityCapability_TypeDef sc;
|
||||
|
||||
#if (_SILICON_LABS_32B_SERIES == 0)
|
||||
sc = securityCapabilityNA;
|
||||
#elif (_SILICON_LABS_32B_SERIES == 1)
|
||||
sc = securityCapabilityBasic;
|
||||
#else
|
||||
sc = securityCapabilityUnknown;
|
||||
#endif
|
||||
|
||||
#if (_SILICON_LABS_32B_SERIES == 2)
|
||||
uint16_t mcuFeatureSetMajor;
|
||||
uint16_t deviceNumber;
|
||||
deviceNumber = SYSTEM_GetPartNumber();
|
||||
mcuFeatureSetMajor = 'A' + (deviceNumber / 1000);
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
|
||||
// override feature set since BRD4182A Rev A00 -> rev B02 are marked "A"
|
||||
mcuFeatureSetMajor = 'C';
|
||||
#endif
|
||||
|
||||
switch (mcuFeatureSetMajor) {
|
||||
case 'A':
|
||||
sc = securityCapabilitySE;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
sc = securityCapabilityVault;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
sc = securityCapabilityRoT;
|
||||
break;
|
||||
|
||||
default:
|
||||
sc = securityCapabilityUnknown;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the unique number for this device.
|
||||
*
|
||||
* @return
|
||||
* Unique number for this device.
|
||||
******************************************************************************/
|
||||
uint64_t SYSTEM_GetUnique(void)
|
||||
{
|
||||
#if defined (_DEVINFO_EUI64H_MASK)
|
||||
uint32_t tmp = DEVINFO->EUI64L;
|
||||
return (uint64_t)((uint64_t)DEVINFO->EUI64H << 32) | tmp;
|
||||
#elif defined(_DEVINFO_UNIQUEH_MASK)
|
||||
uint32_t tmp = DEVINFO->UNIQUEL;
|
||||
return (uint64_t)((uint64_t)DEVINFO->UNIQUEH << 32) | tmp;
|
||||
#else
|
||||
#error (em_system.c): Location of device unique number is not defined.
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the production revision for this part.
|
||||
*
|
||||
* @return
|
||||
* Production revision for this part.
|
||||
******************************************************************************/
|
||||
uint8_t SYSTEM_GetProdRev(void)
|
||||
{
|
||||
#if defined (_DEVINFO_PART_PROD_REV_MASK)
|
||||
return (uint8_t)((DEVINFO->PART & _DEVINFO_PART_PROD_REV_MASK)
|
||||
>> _DEVINFO_PART_PROD_REV_SHIFT);
|
||||
#elif defined (_DEVINFO_INFO_PRODREV_MASK)
|
||||
return (uint8_t)((DEVINFO->INFO & _DEVINFO_INFO_PRODREV_MASK)
|
||||
>> _DEVINFO_INFO_PRODREV_SHIFT);
|
||||
#else
|
||||
#error (em_system.c): Location of production revision is not defined.
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the SRAM Base Address.
|
||||
*
|
||||
* @note
|
||||
* This function is used to retrieve the base address of the SRAM.
|
||||
*
|
||||
* @return
|
||||
* Base address SRAM (32-bit unsigned integer).
|
||||
******************************************************************************/
|
||||
uint32_t SYSTEM_GetSRAMBaseAddress(void)
|
||||
{
|
||||
return (uint32_t)SRAM_BASE;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the SRAM size (in KB).
|
||||
*
|
||||
* @note
|
||||
* This function retrieves SRAM size by reading the chip device
|
||||
* info structure. If your binary is made for one specific device only,
|
||||
* use SRAM_SIZE instead.
|
||||
*
|
||||
* @return
|
||||
* Size of internal SRAM (in KB).
|
||||
******************************************************************************/
|
||||
uint16_t SYSTEM_GetSRAMSize(void)
|
||||
{
|
||||
uint16_t sizekb;
|
||||
|
||||
#if defined(_EFM32_GECKO_FAMILY)
|
||||
/* Early Gecko devices had a bug where SRAM and Flash size were swapped. */
|
||||
if (SYSTEM_GetProdRev() < 5) {
|
||||
sizekb = (DEVINFO->MSIZE & _DEVINFO_MSIZE_FLASH_MASK)
|
||||
>> _DEVINFO_MSIZE_FLASH_SHIFT;
|
||||
}
|
||||
#endif
|
||||
sizekb = (uint16_t)((DEVINFO->MSIZE & _DEVINFO_MSIZE_SRAM_MASK)
|
||||
>> _DEVINFO_MSIZE_SRAM_SHIFT);
|
||||
|
||||
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80) && defined(_EFR_DEVICE)
|
||||
/* Do not include EFR32xG1 RAMH. */
|
||||
sizekb--;
|
||||
#endif
|
||||
|
||||
return sizekb;
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the flash size (in KB).
|
||||
*
|
||||
* @note
|
||||
* This function retrieves flash size by reading the chip device
|
||||
* info structure. If your binary is made for one specific device only,
|
||||
* use FLASH_SIZE instead.
|
||||
*
|
||||
* @return
|
||||
* Size of internal flash (in KB).
|
||||
******************************************************************************/
|
||||
uint16_t SYSTEM_GetFlashSize(void)
|
||||
{
|
||||
#if defined(_EFM32_GECKO_FAMILY)
|
||||
/* Early Gecko devices had a bug where SRAM and Flash size were swapped. */
|
||||
if (SYSTEM_GetProdRev() < 5) {
|
||||
return (DEVINFO->MSIZE & _DEVINFO_MSIZE_SRAM_MASK)
|
||||
>> _DEVINFO_MSIZE_SRAM_SHIFT;
|
||||
}
|
||||
#endif
|
||||
return (uint16_t)((DEVINFO->MSIZE & _DEVINFO_MSIZE_FLASH_MASK)
|
||||
>> _DEVINFO_MSIZE_FLASH_SHIFT);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the flash page size in bytes.
|
||||
*
|
||||
* @note
|
||||
* This function retrieves flash page size by reading the chip device
|
||||
* info structure. If your binary is made for one specific device only,
|
||||
* use FLASH_PAGE_SIZE instead.
|
||||
*
|
||||
* @return
|
||||
* Page size of internal flash in bytes.
|
||||
******************************************************************************/
|
||||
uint32_t SYSTEM_GetFlashPageSize(void)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
|
||||
#if defined(_EFM32_GIANT_FAMILY)
|
||||
if (SYSTEM_GetProdRev() < 18) {
|
||||
/* Early Giant/Leopard devices did not have MEMINFO in DEVINFO. */
|
||||
return FLASH_PAGE_SIZE;
|
||||
}
|
||||
#elif defined(_EFM32_ZERO_FAMILY)
|
||||
if (SYSTEM_GetProdRev() < 24) {
|
||||
/* Early Zero devices have an incorrect DEVINFO flash page size */
|
||||
return FLASH_PAGE_SIZE;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_DEVINFO_MEMINFO_FLASHPAGESIZE_MASK)
|
||||
tmp = (DEVINFO->MEMINFO & _DEVINFO_MEMINFO_FLASHPAGESIZE_MASK)
|
||||
>> _DEVINFO_MEMINFO_FLASHPAGESIZE_SHIFT;
|
||||
#elif defined(_DEVINFO_MEMINFO_FLASH_PAGE_SIZE_MASK)
|
||||
tmp = (DEVINFO->MEMINFO & _DEVINFO_MEMINFO_FLASH_PAGE_SIZE_MASK)
|
||||
>> _DEVINFO_MEMINFO_FLASH_PAGE_SIZE_SHIFT;
|
||||
#else
|
||||
#error (em_system.c): Location of flash page size is not defined.
|
||||
#endif
|
||||
|
||||
return 1UL << ((tmp + 10UL) & 0x1FUL);
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the MCU part number.
|
||||
*
|
||||
* @return
|
||||
* The part number of MCU.
|
||||
******************************************************************************/
|
||||
uint16_t SYSTEM_GetPartNumber(void)
|
||||
{
|
||||
#if defined(_DEVINFO_PART_DEVICENUM_MASK)
|
||||
return (uint16_t)((DEVINFO->PART & _DEVINFO_PART_DEVICENUM_MASK)
|
||||
>> _DEVINFO_PART_DEVICENUM_SHIFT);
|
||||
#elif defined(_DEVINFO_PART_DEVICE_NUMBER_MASK)
|
||||
return (uint16_t)((DEVINFO->PART & _DEVINFO_PART_DEVICE_NUMBER_MASK)
|
||||
>> _DEVINFO_PART_DEVICE_NUMBER_SHIFT);
|
||||
#else
|
||||
#error (em_system.c): Location of device part number is not defined.
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the calibration temperature (in degrees Celsius).
|
||||
*
|
||||
* @return
|
||||
* Calibration temperature in Celsius.
|
||||
******************************************************************************/
|
||||
uint8_t SYSTEM_GetCalibrationTemperature(void)
|
||||
{
|
||||
#if defined(_DEVINFO_CAL_TEMP_MASK)
|
||||
return (uint8_t)((DEVINFO->CAL & _DEVINFO_CAL_TEMP_MASK)
|
||||
>> _DEVINFO_CAL_TEMP_SHIFT);
|
||||
#elif defined(_DEVINFO_CALTEMP_TEMP_MASK)
|
||||
return (uint8_t)((DEVINFO->CALTEMP & _DEVINFO_CALTEMP_TEMP_MASK)
|
||||
>> _DEVINFO_CALTEMP_TEMP_SHIFT);
|
||||
#else
|
||||
#error (em_system.c): Location of calibration temperature is not defined.
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Get the MCU family identifier.
|
||||
*
|
||||
* @note
|
||||
* This function retrieves family ID by reading the chip's device info
|
||||
* structure in flash memory. Users can retrieve family ID directly
|
||||
* by reading DEVINFO->PART item and decode with mask and shift
|
||||
* \#defines defined in \<part_family\>_devinfo.h (refer to code
|
||||
* below for details).
|
||||
*
|
||||
* @return
|
||||
* Family identifier of MCU.
|
||||
******************************************************************************/
|
||||
SYSTEM_PartFamily_TypeDef SYSTEM_GetFamily(void)
|
||||
{
|
||||
#if defined(_DEVINFO_PART_FAMILY_MASK)
|
||||
return (SYSTEM_PartFamily_TypeDef)
|
||||
((uint32_t)((DEVINFO->PART & (_DEVINFO_PART_FAMILY_MASK
|
||||
| _DEVINFO_PART_FAMILYNUM_MASK))));
|
||||
#elif defined(_DEVINFO_PART_DEVICE_FAMILY_MASK)
|
||||
return (SYSTEM_PartFamily_TypeDef)
|
||||
((uint32_t)((DEVINFO->PART & _DEVINFO_PART_DEVICE_FAMILY_MASK)
|
||||
>> _DEVINFO_PART_DEVICE_FAMILY_SHIFT));
|
||||
#else
|
||||
#error (em_system.h): Location of device family name is not defined.
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} (end addtogroup system) */
|
||||
520
Libs/platform/emlib/src/em_timer.c
Normal file
520
Libs/platform/emlib/src/em_timer.c
Normal file
@@ -0,0 +1,520 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Timer/counter (TIMER) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_timer.h"
|
||||
#if defined(TIMER_COUNT) && (TIMER_COUNT > 0)
|
||||
|
||||
#include "sl_assert.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup timer TIMER - Timer/Counter
|
||||
* @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. */
|
||||
#if defined(TIMER9)
|
||||
#define TIMER_DEVICE_ID(timer) ( \
|
||||
(timer) == TIMER0 ? 0 \
|
||||
: (timer) == TIMER1 ? 1 \
|
||||
: (timer) == TIMER2 ? 2 \
|
||||
: (timer) == TIMER3 ? 3 \
|
||||
: (timer) == TIMER4 ? 4 \
|
||||
: (timer) == TIMER5 ? 5 \
|
||||
: (timer) == TIMER6 ? 6 \
|
||||
: (timer) == TIMER7 ? 7 \
|
||||
: (timer) == TIMER8 ? 8 \
|
||||
: (timer) == TIMER9 ? 9 \
|
||||
: -1)
|
||||
#elif defined(TIMER7)
|
||||
#define TIMER_DEVICE_ID(timer) ( \
|
||||
(timer) == TIMER0 ? 0 \
|
||||
: (timer) == TIMER1 ? 1 \
|
||||
: (timer) == TIMER2 ? 2 \
|
||||
: (timer) == TIMER3 ? 3 \
|
||||
: (timer) == TIMER4 ? 4 \
|
||||
: (timer) == TIMER5 ? 5 \
|
||||
: (timer) == TIMER6 ? 6 \
|
||||
: (timer) == TIMER7 ? 7 \
|
||||
: -1)
|
||||
#elif defined(TIMER4)
|
||||
#define TIMER_DEVICE_ID(timer) ( \
|
||||
(timer) == TIMER0 ? 0 \
|
||||
: (timer) == TIMER1 ? 1 \
|
||||
: (timer) == TIMER2 ? 2 \
|
||||
: (timer) == TIMER3 ? 3 \
|
||||
: (timer) == TIMER4 ? 4 \
|
||||
: -1)
|
||||
#else
|
||||
#define TIMER_DEVICE_ID(timer) ( \
|
||||
(timer) == TIMER0 ? 0 \
|
||||
: (timer) == TIMER1 ? 1 \
|
||||
: (timer) == TIMER2 ? 2 \
|
||||
: (timer) == TIMER3 ? 3 \
|
||||
: -1)
|
||||
#endif
|
||||
|
||||
#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);
|
||||
volatile PRS_TIMERn_TypeDef * base = (PRS_TIMERn_TypeDef *) &PRS->CONSUMER_TIMER0_CC0;
|
||||
EFM_ASSERT(i >= 0);
|
||||
|
||||
if (i >= 0) {
|
||||
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_SyncWait(timer);
|
||||
timer->EN_CLR = TIMER_EN_EN;
|
||||
#if defined(_TIMER_EN_DISABLING_MASK)
|
||||
while (timer->EN & _TIMER_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
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->disSyncOut ? TIMER_CFG_DISSYNCOUT : 0)
|
||||
| (init->ati ? TIMER_CFG_ATI : 0)
|
||||
| (init->rssCoist ? TIMER_CFG_RSSCOIST : 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_0) || defined(_SILICON_LABS_32B_SERIES_1)
|
||||
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)
|
||||
#if defined(TIMER_CTRL_DISSYNCOUT)
|
||||
| (init->disSyncOut ? TIMER_CTRL_DISSYNCOUT : 0)
|
||||
#endif
|
||||
| (init->sync ? TIMER_CTRL_SYNC : 0);
|
||||
|
||||
#if defined(TIMER_CTRL_X2CNT) && defined(TIMER_CTRL_ATI) && defined(TIMER_CTRL_RSSCOIST)
|
||||
ctrlRegVal |= (init->count2x ? TIMER_CTRL_X2CNT : 0)
|
||||
| (init->ati ? TIMER_CTRL_ATI : 0)
|
||||
| (init->rssCoist ? TIMER_CTRL_RSSCOIST : 0);
|
||||
#endif
|
||||
|
||||
#else
|
||||
ctrlRegVal = ((uint32_t)init->fallAction << _TIMER_CTRL_FALLA_SHIFT)
|
||||
| ((uint32_t)init->riseAction << _TIMER_CTRL_RISEA_SHIFT)
|
||||
| (init->count2x ? TIMER_CTRL_X2CNT : 0);
|
||||
#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_REF_CH_VALIDATE(timer, ch));
|
||||
|
||||
#if defined (_TIMER_CC_CFG_MASK)
|
||||
TIMER_SyncWait(timer);
|
||||
timer->EN_CLR = TIMER_EN_EN;
|
||||
#if defined(_TIMER_EN_DISABLING_MASK)
|
||||
while (timer->EN & _TIMER_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
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(TIMER_SupportsDTI(timer));
|
||||
|
||||
/* Make sure the DTI unit is disabled while initializing. */
|
||||
TIMER_EnableDTI(timer, false);
|
||||
|
||||
#if defined (_TIMER_DTCFG_MASK)
|
||||
TIMER_SyncWait(timer);
|
||||
timer->EN_CLR = TIMER_EN_EN;
|
||||
#if defined(_TIMER_EN_DISABLING_MASK)
|
||||
while (timer->EN & _TIMER_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
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));
|
||||
|
||||
#if defined(TIMER_EN_EN)
|
||||
timer->EN_SET = TIMER_EN_EN;
|
||||
#endif
|
||||
|
||||
/* 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_REF_CH_VALIDATE(timer, 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)
|
||||
TIMER_SyncWait(timer);
|
||||
/* CFG registers must be reset after the timer is disabled */
|
||||
timer->EN_CLR = TIMER_EN_EN;
|
||||
#if defined(_TIMER_EN_DISABLING_MASK)
|
||||
while (timer->EN & _TIMER_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
timer->CFG = _TIMER_CFG_RESETVALUE;
|
||||
for (i = 0; TIMER_REF_CH_VALIDATE(timer, 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
|
||||
}
|
||||
|
||||
#if defined(TIMER_STATUS_SYNCBUSY)
|
||||
/**
|
||||
* @brief Wait for pending synchronization to finish
|
||||
*
|
||||
* @param[in] timer
|
||||
*/
|
||||
void TIMER_SyncWait(TIMER_TypeDef * timer)
|
||||
{
|
||||
while (((timer->EN & TIMER_EN_EN) != 0U)
|
||||
&& ((timer->STATUS & TIMER_STATUS_SYNCBUSY) != 0U)) {
|
||||
/* Wait for synchronization to complete */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} (end addtogroup timer) */
|
||||
#endif /* defined(TIMER_COUNT) && (TIMER_COUNT > 0) */
|
||||
1444
Libs/platform/emlib/src/em_usart.c
Normal file
1444
Libs/platform/emlib/src/em_usart.c
Normal file
File diff suppressed because it is too large
Load Diff
636
Libs/platform/emlib/src/em_vdac.c
Normal file
636
Libs/platform/emlib/src/em_vdac.c
Normal file
@@ -0,0 +1,636 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Digital to Analog Converter (VDAC) Peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_vdac.h"
|
||||
#if defined(VDAC_COUNT) && (VDAC_COUNT > 0)
|
||||
#include "em_cmu.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup vdac
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
******************************* DEFINES ***********************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
/** Validation of the VDAC channel for assert statements. */
|
||||
#define VDAC_CH_VALID(ch) ((ch) <= 1)
|
||||
|
||||
/** A maximum VDAC clock. */
|
||||
#define VDAC_MAX_CLOCK 1000000
|
||||
|
||||
/** The maximum clock frequency of the internal clock oscillator, 10 MHz + 20%. */
|
||||
#define VDAC_INTERNAL_CLOCK_FREQ 12000000
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
*************************** LOCAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
||||
|
||||
#if defined(_VDAC_EN_MASK)
|
||||
static void VDAC_DisableModule(VDAC_TypeDef* vdac)
|
||||
{
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
|
||||
/* Wait for all synchronizations to finish */
|
||||
if (vdac->EN & VDAC_EN_EN) {
|
||||
vdac->CMD = _VDAC_CMD_CH0DIS_MASK;
|
||||
while (vdac->STATUS & (VDAC_STATUS_CH0ENS)) {
|
||||
}
|
||||
|
||||
vdac->CMD = _VDAC_CMD_CH1DIS_MASK;
|
||||
while (vdac->STATUS & (VDAC_STATUS_CH1ENS)) {
|
||||
}
|
||||
|
||||
#if defined(_VDAC_CMD_CH0FIFOFLUSH_MASK)
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
|
||||
vdac->CMD = VDAC_CMD_CH0FIFOFLUSH | VDAC_CMD_CH1FIFOFLUSH;
|
||||
|
||||
while (vdac->STATUS & (VDAC_STATUS_SYNCBUSY | VDAC_STATUS_CH0FIFOFLBUSY | VDAC_STATUS_CH1FIFOFLBUSY)) {
|
||||
}
|
||||
#endif
|
||||
vdac->EN_CLR = _VDAC_EN_EN_MASK;
|
||||
while (vdac->EN & _VDAC_EN_DISABLING_MASK) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable/disable the VDAC channel.
|
||||
*
|
||||
* @param[in] vdac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A channel to enable/disable.
|
||||
*
|
||||
* @param[in] enable
|
||||
* True to enable VDAC channel, false to disable.
|
||||
******************************************************************************/
|
||||
void VDAC_Enable(VDAC_TypeDef *vdac, unsigned int ch, bool enable)
|
||||
{
|
||||
EFM_ASSERT(VDAC_REF_VALID(vdac));
|
||||
EFM_ASSERT(VDAC_CH_VALID(ch));
|
||||
|
||||
#if defined(_VDAC_STATUS_SYNCBUSY_MASK)
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ch == 0) {
|
||||
if (enable) {
|
||||
vdac->CMD = VDAC_CMD_CH0EN;
|
||||
while ((vdac->STATUS & VDAC_STATUS_CH0ENS) == 0) {
|
||||
}
|
||||
} else {
|
||||
vdac->CMD = VDAC_CMD_CH0DIS;
|
||||
while (vdac->STATUS & VDAC_STATUS_CH0ENS) {
|
||||
}
|
||||
#if defined(_VDAC_CMD_CH0FIFOFLUSH_MASK)
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
vdac->CMD = VDAC_CMD_CH0FIFOFLUSH;
|
||||
while (vdac->STATUS & VDAC_STATUS_CH0FIFOFLBUSY) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (enable) {
|
||||
vdac->CMD = VDAC_CMD_CH1EN;
|
||||
while ((vdac->STATUS & VDAC_STATUS_CH1ENS) == 0) {
|
||||
}
|
||||
} else {
|
||||
vdac->CMD = VDAC_CMD_CH1DIS;
|
||||
while (vdac->STATUS & VDAC_STATUS_CH1ENS) {
|
||||
}
|
||||
|
||||
#if defined(_VDAC_CMD_CH1FIFOFLUSH_MASK)
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
vdac->CMD = VDAC_CMD_CH1FIFOFLUSH;
|
||||
while (vdac->STATUS & VDAC_STATUS_CH1FIFOFLBUSY) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize VDAC.
|
||||
*
|
||||
* @details
|
||||
* Initializes the common parts for both channels. This function will also load
|
||||
* calibration values from the Device Information (DI) page into the VDAC
|
||||
* calibration register.
|
||||
* To complete a VDAC setup, channel control configuration must also be done.
|
||||
* See VDAC_InitChannel().
|
||||
*
|
||||
* @note
|
||||
* This function will disable both channels prior to configuration.
|
||||
*
|
||||
* @param[in] vdac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the VDAC initialization structure.
|
||||
******************************************************************************/
|
||||
void VDAC_Init(VDAC_TypeDef *vdac, const VDAC_Init_TypeDef *init)
|
||||
{
|
||||
EFM_ASSERT(VDAC_REF_VALID(vdac));
|
||||
uint32_t config = 0;
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
|
||||
uint32_t cal;
|
||||
uint32_t const volatile *calData;
|
||||
|
||||
/* Make sure both channels are disabled. */
|
||||
vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS;
|
||||
while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) {
|
||||
}
|
||||
|
||||
/* Get the OFFSETTRIM calibration value. */
|
||||
cal = ((DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_OFFSETTRIM_MASK)
|
||||
>> _DEVINFO_VDAC0CH1CAL_OFFSETTRIM_SHIFT)
|
||||
<< _VDAC_CAL_OFFSETTRIM_SHIFT;
|
||||
|
||||
if (init->mainCalibration) {
|
||||
calData = &DEVINFO->VDAC0MAINCAL;
|
||||
} else {
|
||||
calData = &DEVINFO->VDAC0ALTCAL;
|
||||
}
|
||||
|
||||
/* Get the correct GAINERRTRIM calibration value. */
|
||||
switch (init->reference) {
|
||||
case vdacRef1V25Ln:
|
||||
config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25LN_MASK)
|
||||
>> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25LN_SHIFT;
|
||||
break;
|
||||
|
||||
case vdacRef2V5Ln:
|
||||
config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5LN_MASK)
|
||||
>> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5LN_SHIFT;
|
||||
break;
|
||||
|
||||
case vdacRef1V25:
|
||||
config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25_MASK)
|
||||
>> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25_SHIFT;
|
||||
break;
|
||||
|
||||
case vdacRef2V5:
|
||||
config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5_MASK)
|
||||
>> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5_SHIFT;
|
||||
break;
|
||||
|
||||
case vdacRefAvdd:
|
||||
case vdacRefExtPin:
|
||||
config = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIMVDDANAEXTPIN_MASK)
|
||||
>> _DEVINFO_VDAC0MAINCAL_GAINERRTRIMVDDANAEXTPIN_SHIFT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the sGAINERRTRIM calibration value. */
|
||||
cal |= config << _VDAC_CAL_GAINERRTRIM_SHIFT;
|
||||
|
||||
/* Get the GAINERRTRIMCH1 calibration value. */
|
||||
switch (init->reference) {
|
||||
case vdacRef1V25Ln:
|
||||
case vdacRef1V25:
|
||||
case vdacRefAvdd:
|
||||
case vdacRefExtPin:
|
||||
config = (DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1A_MASK)
|
||||
>> _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1A_SHIFT;
|
||||
break;
|
||||
|
||||
case vdacRef2V5Ln:
|
||||
case vdacRef2V5:
|
||||
config = (DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1B_MASK)
|
||||
>> _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1B_SHIFT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the GAINERRTRIM calibration value. */
|
||||
cal |= config << _VDAC_CAL_GAINERRTRIMCH1_SHIFT;
|
||||
|
||||
config = ((uint32_t)init->asyncClockMode << _VDAC_CTRL_DACCLKMODE_SHIFT)
|
||||
| ((uint32_t)init->warmupKeepOn << _VDAC_CTRL_WARMUPMODE_SHIFT)
|
||||
| ((uint32_t)init->refresh << _VDAC_CTRL_REFRESHPERIOD_SHIFT)
|
||||
| (((uint32_t)init->prescaler << _VDAC_CTRL_PRESC_SHIFT)
|
||||
& _VDAC_CTRL_PRESC_MASK)
|
||||
| ((uint32_t)init->reference << _VDAC_CTRL_REFSEL_SHIFT)
|
||||
| ((uint32_t)init->ch0ResetPre << _VDAC_CTRL_CH0PRESCRST_SHIFT)
|
||||
| ((uint32_t)init->outEnablePRS << _VDAC_CTRL_OUTENPRS_SHIFT)
|
||||
| ((uint32_t)init->sineEnable << _VDAC_CTRL_SINEMODE_SHIFT)
|
||||
| ((uint32_t)init->diff << _VDAC_CTRL_DIFF_SHIFT);
|
||||
|
||||
/* Write to VDAC registers. */
|
||||
vdac->CAL = cal;
|
||||
vdac->CTRL = config;
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
|
||||
VDAC_DisableModule(vdac);
|
||||
|
||||
config = (
|
||||
#if defined(VDAC_CFG_SINEMODEPRS)
|
||||
((uint32_t)init->sineModePrsEnable ? VDAC_CFG_SINEMODEPRS : 0U) |
|
||||
#endif
|
||||
#if defined(VDAC_CFG_OUTENPRS)
|
||||
((uint32_t)init->prsOutEnable ? VDAC_CFG_OUTENPRS : 0U) |
|
||||
#endif
|
||||
(((uint32_t)init->warmupTime << _VDAC_CFG_WARMUPTIME_SHIFT) & _VDAC_CFG_WARMUPTIME_MASK)
|
||||
| ((uint32_t)init->dbgHalt << _VDAC_CFG_DBGHALT_SHIFT)
|
||||
| ((uint32_t)init->onDemandClk << _VDAC_CFG_ONDEMANDCLK_SHIFT)
|
||||
| ((uint32_t)init->dmaWakeUp << _VDAC_CFG_DMAWU_SHIFT)
|
||||
| ((uint32_t)init->biasKeepWarm << _VDAC_CFG_BIASKEEPWARM_SHIFT)
|
||||
| ((uint32_t)init->refresh << _VDAC_CFG_REFRESHPERIOD_SHIFT)
|
||||
| ((uint32_t)init->timerOverflow << _VDAC_CFG_TIMEROVRFLOWPERIOD_SHIFT)
|
||||
| (((uint32_t)init->prescaler << _VDAC_CFG_PRESC_SHIFT) & _VDAC_CFG_PRESC_MASK)
|
||||
| ((uint32_t)init->reference << _VDAC_CFG_REFRSEL_SHIFT)
|
||||
| ((uint32_t)init->ch0ResetPre << _VDAC_CFG_CH0PRESCRST_SHIFT)
|
||||
| ((uint32_t)init->sineReset << _VDAC_CFG_SINERESET_SHIFT)
|
||||
| ((uint32_t)init->sineEnable << _VDAC_CFG_SINEMODE_SHIFT)
|
||||
| ((uint32_t)init->diff << _VDAC_CFG_DIFF_SHIFT));
|
||||
|
||||
vdac->CFG = config;
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize a VDAC channel.
|
||||
*
|
||||
* @param[in] vdac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* A pointer to the VDAC channel initialization structure.
|
||||
*
|
||||
* @param[in] ch
|
||||
* A channel number to initialize.
|
||||
******************************************************************************/
|
||||
void VDAC_InitChannel(VDAC_TypeDef *vdac,
|
||||
const VDAC_InitChannel_TypeDef *init,
|
||||
unsigned int ch)
|
||||
{
|
||||
uint32_t channelConfig, vdacStatus;
|
||||
|
||||
EFM_ASSERT(VDAC_REF_VALID(vdac));
|
||||
EFM_ASSERT(VDAC_CH_VALID(ch));
|
||||
|
||||
vdacStatus = vdac->STATUS;
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
|
||||
|
||||
/* Make sure both channels are disabled. */
|
||||
vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS;
|
||||
while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) {
|
||||
}
|
||||
|
||||
channelConfig = ((uint32_t)init->prsSel << _VDAC_CH0CTRL_PRSSEL_SHIFT)
|
||||
| ((uint32_t)init->prsAsync << _VDAC_CH0CTRL_PRSASYNC_SHIFT)
|
||||
| ((uint32_t)init->trigMode << _VDAC_CH0CTRL_TRIGMODE_SHIFT)
|
||||
| ((uint32_t)init->sampleOffMode << _VDAC_CH0CTRL_CONVMODE_SHIFT);
|
||||
|
||||
if (ch == 0) {
|
||||
vdac->CH0CTRL = channelConfig;
|
||||
} else {
|
||||
vdac->CH1CTRL = channelConfig;
|
||||
}
|
||||
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_2)
|
||||
|
||||
VDAC_DisableModule(vdac);
|
||||
|
||||
channelConfig = ((uint32_t)init->warmupKeepOn << _VDAC_CH0CFG_KEEPWARM_SHIFT)
|
||||
| ((uint32_t)init->highCapLoadEnable << _VDAC_CH0CFG_HIGHCAPLOADEN_SHIFT)
|
||||
| (((uint32_t)init->fifoLowDataThreshold << _VDAC_CH0CFG_FIFODVL_SHIFT) & _VDAC_CH0CFG_FIFODVL_MASK)
|
||||
| ((uint32_t)init->chRefreshSource << _VDAC_CH0CFG_REFRESHSOURCE_SHIFT)
|
||||
| ((uint32_t)init->trigMode << _VDAC_CH0CFG_TRIGMODE_SHIFT)
|
||||
| ((uint32_t)init->powerMode << _VDAC_CH0CFG_POWERMODE_SHIFT)
|
||||
| ((uint32_t)init->sampleOffMode << _VDAC_CH0CFG_CONVMODE_SHIFT);
|
||||
|
||||
if (ch == 0) {
|
||||
vdac->CH0CFG = channelConfig;
|
||||
|
||||
vdac->OUTTIMERCFG = ((uint32_t)(vdac->OUTTIMERCFG & ~(_VDAC_OUTTIMERCFG_CH0OUTHOLDTIME_MASK)))
|
||||
| (((uint32_t)init->holdOutTime << _VDAC_OUTTIMERCFG_CH0OUTHOLDTIME_SHIFT) & _VDAC_OUTTIMERCFG_CH0OUTHOLDTIME_MASK);
|
||||
|
||||
vdac->EN_SET = _VDAC_EN_EN_MASK;
|
||||
|
||||
vdac->OUTCTRL = ((uint32_t)(vdac->OUTCTRL & ~(_VDAC_OUTCTRL_ABUSPINSELCH0_MASK | _VDAC_OUTCTRL_ABUSPORTSELCH0_MASK | _VDAC_OUTCTRL_SHORTCH0_MASK | _VDAC_OUTCTRL_AUXOUTENCH0_MASK | _VDAC_OUTCTRL_MAINOUTENCH0_MASK)))
|
||||
| (((uint32_t)init->pin << _VDAC_OUTCTRL_ABUSPINSELCH0_SHIFT) & _VDAC_OUTCTRL_ABUSPINSELCH0_MASK)
|
||||
| ((uint32_t)init->port << _VDAC_OUTCTRL_ABUSPORTSELCH0_SHIFT)
|
||||
| ((uint32_t)init->shortOutput << _VDAC_OUTCTRL_SHORTCH0_SHIFT)
|
||||
| ((uint32_t)init->auxOutEnable << _VDAC_OUTCTRL_AUXOUTENCH0_SHIFT)
|
||||
| ((uint32_t)init->mainOutEnable << _VDAC_OUTCTRL_MAINOUTENCH0_SHIFT);
|
||||
} else if (ch == 1) {
|
||||
vdac->CH1CFG = channelConfig;
|
||||
|
||||
vdac->OUTTIMERCFG = (vdac->OUTTIMERCFG & ~(_VDAC_OUTTIMERCFG_CH1OUTHOLDTIME_MASK))
|
||||
| (((uint32_t)init->holdOutTime << _VDAC_OUTTIMERCFG_CH1OUTHOLDTIME_SHIFT) & _VDAC_OUTTIMERCFG_CH1OUTHOLDTIME_MASK);
|
||||
|
||||
vdac->EN_SET = _VDAC_EN_EN_MASK;
|
||||
|
||||
vdac->OUTCTRL = ((uint32_t)(vdac->OUTCTRL & ~(_VDAC_OUTCTRL_ABUSPINSELCH1_MASK | _VDAC_OUTCTRL_ABUSPORTSELCH1_MASK | _VDAC_OUTCTRL_SHORTCH1_MASK | _VDAC_OUTCTRL_AUXOUTENCH1_MASK | _VDAC_OUTCTRL_MAINOUTENCH1_MASK)))
|
||||
| (((uint32_t)init->pin << _VDAC_OUTCTRL_ABUSPINSELCH1_SHIFT) & _VDAC_OUTCTRL_ABUSPINSELCH1_MASK)
|
||||
| ((uint32_t)init->port << _VDAC_OUTCTRL_ABUSPORTSELCH1_SHIFT)
|
||||
| ((uint32_t)init->shortOutput << _VDAC_OUTCTRL_SHORTCH1_SHIFT)
|
||||
| ((uint32_t)init->auxOutEnable << _VDAC_OUTCTRL_AUXOUTENCH1_SHIFT)
|
||||
| ((uint32_t)init->mainOutEnable << _VDAC_OUTCTRL_MAINOUTENCH1_SHIFT);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check if the channel must be enabled. */
|
||||
if (init->enable) {
|
||||
if (ch == 0) {
|
||||
vdac->CMD = VDAC_CMD_CH0EN;
|
||||
} else {
|
||||
vdac->CMD = VDAC_CMD_CH1EN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the other channel had to be turned off above
|
||||
* and needs to be turned on again. */
|
||||
if (ch == 0) {
|
||||
if (vdacStatus & VDAC_STATUS_CH1ENS) {
|
||||
vdac->CMD = VDAC_CMD_CH1EN;
|
||||
}
|
||||
} else {
|
||||
if (vdacStatus & VDAC_STATUS_CH0ENS) {
|
||||
vdac->CMD = VDAC_CMD_CH0EN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Set the output signal of a VDAC channel to a given value.
|
||||
*
|
||||
* @details
|
||||
* This function sets the output signal of a VDAC channel by writing @p value
|
||||
* to the corresponding CHnDATA register.
|
||||
*
|
||||
* @param[in] vdac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
*
|
||||
* @param[in] channel
|
||||
* A channel number to set the output of.
|
||||
*
|
||||
* @param[in] value
|
||||
* A value to write to the channel output register CHnDATA.
|
||||
******************************************************************************/
|
||||
void VDAC_ChannelOutputSet(VDAC_TypeDef *vdac,
|
||||
unsigned int channel,
|
||||
uint32_t value)
|
||||
{
|
||||
switch (channel) {
|
||||
case 0:
|
||||
VDAC_Channel0OutputSet(vdac, value);
|
||||
break;
|
||||
case 1:
|
||||
VDAC_Channel1OutputSet(vdac, value);
|
||||
break;
|
||||
default:
|
||||
EFM_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Calculate the prescaler value used to determine VDAC clock.
|
||||
*
|
||||
* @details
|
||||
* The VDAC clock is given by the input clock divided by the prescaler+1.
|
||||
*
|
||||
* VDAC_CLK = IN_CLK / (prescale + 1)
|
||||
*
|
||||
* The maximum VDAC clock is 1 MHz. The input clock is HFPERCLK/HFPERCCLK
|
||||
* when VDAC synchronous mode is selected, or an internal oscillator of
|
||||
* 10 MHz +/- 20% when asynchronous mode is selected.
|
||||
*
|
||||
* @note
|
||||
* If the requested VDAC frequency is low and the maximum prescaler value can't
|
||||
* adjust the actual VDAC frequency lower than requested, the maximum prescaler
|
||||
* value is returned resulting in a higher VDAC frequency than requested.
|
||||
*
|
||||
* @param[in] vdacFreq VDAC frequency target. The frequency will automatically
|
||||
* be adjusted to be below maximum allowed VDAC clock.
|
||||
*
|
||||
* @param[in] syncMode Set to true if you intend to use VDAC in synchronous
|
||||
* mode.
|
||||
*
|
||||
* @param[in] hfperFreq Frequency in Hz of HFPERCLK/HFPERCCLK oscillator.
|
||||
* Set to 0 to use the currently defined HFPERCLK/HFPERCCLK clock setting.
|
||||
* This parameter is only used when syncMode is set to true.
|
||||
*
|
||||
* @return
|
||||
* A prescaler value to use for VDAC to achieve a clock value less than
|
||||
* or equal to @p vdacFreq.
|
||||
******************************************************************************/
|
||||
uint32_t VDAC_PrescaleCalc(uint32_t vdacFreq, bool syncMode, uint32_t hfperFreq)
|
||||
{
|
||||
uint32_t ret, refFreq;
|
||||
|
||||
/* Make sure that the selected VDAC clock is below the maximum value. */
|
||||
if (vdacFreq > VDAC_MAX_CLOCK) {
|
||||
vdacFreq = VDAC_MAX_CLOCK;
|
||||
}
|
||||
|
||||
if (!syncMode) {
|
||||
refFreq = VDAC_INTERNAL_CLOCK_FREQ;
|
||||
} else {
|
||||
if (hfperFreq) {
|
||||
refFreq = hfperFreq;
|
||||
} else {
|
||||
refFreq = CMU_ClockFreqGet(cmuClock_VDAC0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate to determine the best prescaler value. Start with the lowest */
|
||||
/* prescaler value to get the first equal or less VDAC */
|
||||
/* frequency value. */
|
||||
for (ret = 0; ret <= (_VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT); ret++) {
|
||||
if ((refFreq / (ret + 1)) <= vdacFreq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If ret is higher than the maximum prescaler value, make sure to return
|
||||
the maximum value. */
|
||||
if (ret > (_VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT)) {
|
||||
ret = _VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Calculate the prescaler value used to determine VDAC clock.
|
||||
*
|
||||
* @details
|
||||
* The VDAC clock is given by the input clock divided by the prescaler+1.
|
||||
*
|
||||
* VDAC_CLK = IN_CLK / (prescale + 1)
|
||||
*
|
||||
* The maximum VDAC clock is 1 MHz.
|
||||
*
|
||||
* @note
|
||||
* If the requested VDAC frequency is low and the maximum prescaler value can't
|
||||
* adjust the actual VDAC frequency lower than requested, the maximum prescaler
|
||||
* value is returned resulting in a higher VDAC frequency than requested.
|
||||
*
|
||||
* @param[in] vdac
|
||||
* Pointer to VDAC peripheral register block.
|
||||
*
|
||||
* @param[in] vdacFreq VDAC frequency target. The frequency will automatically
|
||||
* be adjusted to be below maximum allowed VDAC clock.
|
||||
*
|
||||
* @return
|
||||
* A prescaler value to use for VDAC to achieve a clock value less than
|
||||
* or equal to @p vdacFreq.
|
||||
******************************************************************************/
|
||||
uint32_t VDAC_PrescaleCalc(VDAC_TypeDef *vdac, uint32_t vdacFreq)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
uint32_t refFreq = 0;
|
||||
|
||||
/* Make sure that the selected VDAC clock is below the maximum value. */
|
||||
if (vdacFreq > VDAC_MAX_CLOCK) {
|
||||
vdacFreq = VDAC_MAX_CLOCK;
|
||||
}
|
||||
|
||||
if (vdac == VDAC0) {
|
||||
refFreq = CMU_ClockFreqGet(cmuClock_VDAC0);
|
||||
}
|
||||
#if defined(VDAC1)
|
||||
else if (vdac == VDAC1) {
|
||||
refFreq = CMU_ClockFreqGet(cmuClock_VDAC1);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
EFM_ASSERT(0);
|
||||
}
|
||||
|
||||
/* Iterate to determine the best prescaler value. Start with the lowest */
|
||||
/* prescaler value to get the first equal or less VDAC */
|
||||
/* frequency value. */
|
||||
for (ret = 0; ret <= (_VDAC_CFG_PRESC_MASK >> _VDAC_CFG_PRESC_SHIFT); ret++) {
|
||||
if ((refFreq / (ret + 1)) <= vdacFreq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If ret is higher than the maximum prescaler value, make sure to return
|
||||
the maximum value. */
|
||||
if (ret > (_VDAC_CFG_PRESC_MASK >> _VDAC_CFG_PRESC_SHIFT)) {
|
||||
ret = _VDAC_CFG_PRESC_MASK >> _VDAC_CFG_PRESC_SHIFT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Reset VDAC to same state that it was in after a hardwares reset.
|
||||
*
|
||||
* @param[in] vdac
|
||||
* A pointer to the VDAC peripheral register block.
|
||||
******************************************************************************/
|
||||
void VDAC_Reset(VDAC_TypeDef *vdac)
|
||||
{
|
||||
#if defined(VDAC_SWRST_SWRST)
|
||||
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
|
||||
/* Wait for all synchronizations to finish and disable the vdac channels */
|
||||
if (vdac->EN & VDAC_EN_EN) {
|
||||
vdac->CMD = _VDAC_CMD_CH0DIS_MASK;
|
||||
while (vdac->STATUS & VDAC_STATUS_CH0ENS ) {
|
||||
}
|
||||
|
||||
vdac->CMD = _VDAC_CMD_CH1DIS_MASK;
|
||||
while (vdac->STATUS & VDAC_STATUS_CH1ENS ) {
|
||||
}
|
||||
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
|
||||
vdac->CMD = _VDAC_CMD_CH0FIFOFLUSH_MASK | _VDAC_CMD_CH1FIFOFLUSH_MASK;
|
||||
while (vdac->STATUS & (VDAC_STATUS_CH0FIFOFLBUSY | VDAC_STATUS_CH1FIFOFLBUSY)) {
|
||||
}
|
||||
|
||||
while (vdac->STATUS & VDAC_STATUS_SYNCBUSY) {
|
||||
}
|
||||
}
|
||||
|
||||
vdac->SWRST_SET = VDAC_SWRST_SWRST;
|
||||
while (vdac->SWRST & _VDAC_SWRST_RESETTING_MASK) {
|
||||
}
|
||||
|
||||
#else
|
||||
/* Disable channels before resetting other registers. */
|
||||
vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS;
|
||||
while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) {
|
||||
}
|
||||
vdac->CH0CTRL = _VDAC_CH0CTRL_RESETVALUE;
|
||||
vdac->CH1CTRL = _VDAC_CH1CTRL_RESETVALUE;
|
||||
vdac->CH0DATA = _VDAC_CH0DATA_RESETVALUE;
|
||||
vdac->CH1DATA = _VDAC_CH1DATA_RESETVALUE;
|
||||
vdac->CTRL = _VDAC_CTRL_RESETVALUE;
|
||||
vdac->IEN = _VDAC_IEN_RESETVALUE;
|
||||
vdac->IFC = _VDAC_IFC_MASK;
|
||||
vdac->CAL = _VDAC_CAL_RESETVALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} (end addtogroup vdac) */
|
||||
#endif /* defined(VDAC_COUNT) && (VDAC_COUNT > 0) */
|
||||
355
Libs/platform/emlib/src/em_wdog.c
Normal file
355
Libs/platform/emlib/src/em_wdog.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/***************************************************************************//**
|
||||
* @file
|
||||
* @brief Watchdog (WDOG) peripheral API
|
||||
*******************************************************************************
|
||||
* # License
|
||||
* <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Zlib
|
||||
*
|
||||
* The licensor of this software is Silicon Laboratories Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* 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. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "em_wdog.h"
|
||||
#if defined(WDOG_COUNT) && (WDOG_COUNT > 0)
|
||||
|
||||
#include "em_bus.h"
|
||||
#include "em_core.h"
|
||||
|
||||
/***************************************************************************//**
|
||||
* @addtogroup wdog WDOG - Watchdog
|
||||
* @brief Watchdog (WDOG) Peripheral API
|
||||
* @details
|
||||
* This module contains functions to control the WDOG peripheral of Silicon
|
||||
* Labs 32-bit MCUs and SoCs. The WDOG resets the system in case of a fault
|
||||
* condition.
|
||||
* @{
|
||||
******************************************************************************/
|
||||
|
||||
/** In some scenarioes when the watchdog is disabled the synchronization
|
||||
* register might be set and not be cleared until the watchdog is enabled
|
||||
* again. This will happen when for instance some watchdog register is modified
|
||||
* while the watchdog clock is disabled. In these scenarioes we need to make
|
||||
* sure that the software does not wait forever. */
|
||||
#define WDOG_SYNC_TIMEOUT 30000
|
||||
|
||||
/*******************************************************************************
|
||||
************************** GLOBAL FUNCTIONS *******************************
|
||||
******************************************************************************/
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Enable/disable the watchdog timer.
|
||||
*
|
||||
* @note
|
||||
* This function modifies the WDOG CTRL 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.
|
||||
*
|
||||
* @param[in] wdog
|
||||
* A pointer to the WDOG peripheral register block.
|
||||
*
|
||||
* @param[in] enable
|
||||
* True to enable Watchdog, false to disable. Watchdog cannot be disabled if
|
||||
* it's been locked.
|
||||
******************************************************************************/
|
||||
void WDOGn_Enable(WDOG_TypeDef *wdog, bool enable)
|
||||
{
|
||||
// SYNCBUSY may stall when locked.
|
||||
#if defined(_WDOG_STATUS_MASK)
|
||||
if ((wdog->STATUS & _WDOG_STATUS_LOCK_MASK) == WDOG_STATUS_LOCK_LOCKED) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (wdog->CTRL & WDOG_CTRL_LOCK) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WDOG_EN_MASK)
|
||||
if (!enable) {
|
||||
while (wdog->SYNCBUSY & WDOG_SYNCBUSY_CMD) {
|
||||
}
|
||||
wdog->EN_CLR = WDOG_EN_EN;
|
||||
#if defined(_WDOG_EN_DISABLING_MASK)
|
||||
while (wdog->EN & _WDOG_EN_DISABLING_MASK) {
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
wdog->EN_SET = WDOG_EN_EN;
|
||||
}
|
||||
#else
|
||||
// Wait for previous operations/modifications to complete
|
||||
int i = 0;
|
||||
while (((wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U)
|
||||
&& (i < WDOG_SYNC_TIMEOUT)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
bool wdogState = ((wdog->CTRL & _WDOG_CTRL_EN_MASK) != 0U);
|
||||
|
||||
// Make sure to only write to the CTRL register if we are changing mode
|
||||
if (wdogState != enable) {
|
||||
BUS_RegBitWrite(&wdog->CTRL, _WDOG_CTRL_EN_SHIFT, enable);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Feed WDOG.
|
||||
*
|
||||
* @details
|
||||
* When WDOG is activated, it must be fed (i.e., clearing the counter)
|
||||
* before it reaches the defined timeout period. Otherwise, WDOG
|
||||
* will generate a reset.
|
||||
*
|
||||
* @note
|
||||
* Note that WDOG is an asynchronous peripheral and when calling the
|
||||
* WDOGn_Feed() function the hardware starts the process of clearing the
|
||||
* counter. This process takes some time before it completes depending on the
|
||||
* selected oscillator (up to 4 peripheral clock cycles). When using the
|
||||
* ULFRCO for instance as the oscillator the watchdog runs on a 1 kHz clock
|
||||
* and a watchdog clear operation might take up to 4 ms.
|
||||
*
|
||||
* If the device enters EM2 or EM3 while a command is in progress then that
|
||||
* command will be aborted. An application can use @ref WDOGn_SyncWait()
|
||||
* to wait for a command to complete.
|
||||
*
|
||||
* @param[in] wdog
|
||||
* A pointer to the WDOG peripheral register block.
|
||||
******************************************************************************/
|
||||
void WDOGn_Feed(WDOG_TypeDef *wdog)
|
||||
{
|
||||
#if (_SILICON_LABS_32B_SERIES < 2)
|
||||
|
||||
// WDOG should not be fed while it is disabled.
|
||||
if (!(wdog->CTRL & WDOG_CTRL_EN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a previous clearing is synchronized to the LF domain, there
|
||||
// is no point in waiting for it to complete before clearing over again.
|
||||
// This avoids stalling the core in the typical use case where some idle loop
|
||||
// keeps clearing WDOG.
|
||||
if (wdog->SYNCBUSY & WDOG_SYNCBUSY_CMD) {
|
||||
return;
|
||||
}
|
||||
// Before writing to the WDOG_CMD register, make sure that
|
||||
// any previous write to the WDOG_CTRL is complete.
|
||||
while ( (wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U ) {
|
||||
}
|
||||
|
||||
wdog->CMD = WDOG_CMD_CLEAR;
|
||||
|
||||
#else // Series 2 devices
|
||||
|
||||
CORE_DECLARE_IRQ_STATE;
|
||||
|
||||
// WDOG should not be fed while it is disabled.
|
||||
if ((wdog->EN & WDOG_EN_EN) == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need an atomic section around the check for sync and the clear command
|
||||
// because sending a clear command while a previous command is being synchronized
|
||||
// will cause a BusFault.
|
||||
CORE_ENTER_ATOMIC();
|
||||
if ((wdog->SYNCBUSY & WDOG_SYNCBUSY_CMD) == 0U) {
|
||||
wdog->CMD = WDOG_CMD_CLEAR;
|
||||
}
|
||||
CORE_EXIT_ATOMIC();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Initialize WDOG (assuming the WDOG configuration has not been
|
||||
* locked).
|
||||
*
|
||||
* @note
|
||||
* This function modifies the WDOG CTRL 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.
|
||||
*
|
||||
* @param[in] wdog
|
||||
* Pointer to the WDOG peripheral register block.
|
||||
*
|
||||
* @param[in] init
|
||||
* The structure holding the WDOG configuration. A default setting
|
||||
* #WDOG_INIT_DEFAULT is available for initialization.
|
||||
******************************************************************************/
|
||||
void WDOGn_Init(WDOG_TypeDef *wdog, const WDOG_Init_TypeDef *init)
|
||||
{
|
||||
#if defined(_WDOG_CFG_MASK)
|
||||
// Handle series-2 devices
|
||||
|
||||
if (wdog->EN != 0U) {
|
||||
while (wdog->SYNCBUSY != 0U) {
|
||||
// Wait for any potential synchronization to finish
|
||||
}
|
||||
wdog->EN_CLR = WDOG_EN_EN;
|
||||
#if defined(_WDOG_EN_DISABLING_MASK)
|
||||
while (wdog->EN & _WDOG_EN_DISABLING_MASK) {
|
||||
/* Wait for disabling to finish */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
wdog->CFG = (init->debugRun ? WDOG_CFG_DEBUGRUN : 0U)
|
||||
| (init->clrSrc ? WDOG_CFG_CLRSRC : 0U)
|
||||
#if defined(_WDOG_CFG_EM1RUN_MASK)
|
||||
| (init->em1Run ? WDOG_CFG_EM1RUN : 0U)
|
||||
#endif
|
||||
| (init->em2Run ? WDOG_CFG_EM2RUN : 0U)
|
||||
| (init->em3Run ? WDOG_CFG_EM3RUN : 0U)
|
||||
| (init->em4Block ? WDOG_CFG_EM4BLOCK : 0U)
|
||||
| (init->prs0MissRstEn ? WDOG_CFG_PRS0MISSRSTEN : 0U)
|
||||
| (init->prs1MissRstEn ? WDOG_CFG_PRS1MISSRSTEN : 0U)
|
||||
| (init->resetDisable ? WDOG_CFG_WDOGRSTDIS : 0U)
|
||||
| ((uint32_t)(init->warnSel) << _WDOG_CFG_WARNSEL_SHIFT)
|
||||
| ((uint32_t)(init->winSel) << _WDOG_CFG_WINSEL_SHIFT)
|
||||
| ((uint32_t)(init->perSel) << _WDOG_CFG_PERSEL_SHIFT);
|
||||
|
||||
WDOGn_Enable(wdog, init->enable);
|
||||
|
||||
if (init->lock) {
|
||||
WDOGn_Lock(wdog);
|
||||
}
|
||||
#else
|
||||
// Handle series-0 and series-1 devices
|
||||
uint32_t setting;
|
||||
|
||||
setting = (init->enable ? WDOG_CTRL_EN : 0U)
|
||||
| (init->debugRun ? WDOG_CTRL_DEBUGRUN : 0U)
|
||||
#if defined(_WDOG_CTRL_CLRSRC_MASK)
|
||||
| (init->clrSrc ? WDOG_CTRL_CLRSRC : 0U)
|
||||
#endif
|
||||
| (init->em2Run ? WDOG_CTRL_EM2RUN : 0U)
|
||||
| (init->em3Run ? WDOG_CTRL_EM3RUN : 0U)
|
||||
| (init->em4Block ? WDOG_CTRL_EM4BLOCK : 0U)
|
||||
| (init->swoscBlock ? WDOG_CTRL_SWOSCBLOCK : 0U)
|
||||
| (init->lock ? WDOG_CTRL_LOCK : 0U)
|
||||
| ((uint32_t)(init->clkSel) << _WDOG_CTRL_CLKSEL_SHIFT)
|
||||
| ((uint32_t)(init->perSel) << _WDOG_CTRL_PERSEL_SHIFT);
|
||||
|
||||
#if defined(_WDOG_CTRL_WDOGRSTDIS_MASK)
|
||||
setting |= (init->resetDisable ? WDOG_CTRL_WDOGRSTDIS : 0U);
|
||||
#endif
|
||||
#if defined(_WDOG_CTRL_WARNSEL_MASK)
|
||||
setting |= ((uint32_t)(init->warnSel) << _WDOG_CTRL_WARNSEL_SHIFT);
|
||||
#endif
|
||||
#if defined(_WDOG_CTRL_WINSEL_MASK)
|
||||
setting |= ((uint32_t)(init->winSel) << _WDOG_CTRL_WINSEL_SHIFT);
|
||||
#endif
|
||||
|
||||
// Wait for previous operations/modifications to complete
|
||||
int i = 0;
|
||||
while (((wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U)
|
||||
&& (i < WDOG_SYNC_TIMEOUT)) {
|
||||
i++;
|
||||
}
|
||||
wdog->CTRL = setting;
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Lock the WDOG configuration.
|
||||
*
|
||||
* @details
|
||||
* This prevents errors from overwriting the WDOG configuration, possibly
|
||||
* disabling it. Only a reset can unlock the WDOG configuration once locked.
|
||||
*
|
||||
* If the LFRCO or LFXO clocks are used to clock WDOG,
|
||||
* consider using the option of inhibiting those clocks to be disabled.
|
||||
* See the WDOG_Enable() initialization structure.
|
||||
*
|
||||
* @note
|
||||
* This function modifies the WDOG CTRL 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.
|
||||
*
|
||||
* @param[in] wdog
|
||||
* A pointer to WDOG peripheral register block.
|
||||
******************************************************************************/
|
||||
void WDOGn_Lock(WDOG_TypeDef *wdog)
|
||||
{
|
||||
#if defined(_WDOG_LOCK_MASK)
|
||||
wdog->LOCK = _WDOG_LOCK_LOCKKEY_LOCK;
|
||||
#else
|
||||
// Wait for any pending previous write operation to have been completed in
|
||||
// the low-frequency domain.
|
||||
while ( (wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U ) {
|
||||
}
|
||||
|
||||
// Disable writing to the control register.
|
||||
BUS_RegBitWrite(&wdog->CTRL, _WDOG_CTRL_LOCK_SHIFT, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Wait for the WDOG to complete all synchronization of register changes
|
||||
* and commands.
|
||||
*
|
||||
* @param[in] wdog
|
||||
* A pointer to WDOG peripheral register block.
|
||||
******************************************************************************/
|
||||
void WDOGn_SyncWait(WDOG_TypeDef *wdog)
|
||||
{
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
while ((wdog->EN != 0U) && (wdog->SYNCBUSY != 0U)) {
|
||||
// Wait for synchronization to finish
|
||||
}
|
||||
#else
|
||||
while (wdog->SYNCBUSY != 0U) {
|
||||
// Wait for synchronization to finish
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************//**
|
||||
* @brief
|
||||
* Unlock the WDOG configuration.
|
||||
*
|
||||
* @details
|
||||
* Note that this function will have no effect on devices where a reset is
|
||||
* the only way to unlock the watchdog.
|
||||
*
|
||||
* @param[in] wdog
|
||||
* A pointer to WDOG peripheral register block.
|
||||
******************************************************************************/
|
||||
void WDOGn_Unlock(WDOG_TypeDef *wdog)
|
||||
{
|
||||
#if defined(_WDOG_LOCK_MASK)
|
||||
wdog->LOCK = _WDOG_LOCK_LOCKKEY_UNLOCK;
|
||||
#else
|
||||
(void) wdog;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @} (end addtogroup wdog) */
|
||||
#endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */
|
||||
Reference in New Issue
Block a user