Initial commit of firmware

This commit is contained in:
2025-04-12 13:30:57 +01:00
commit 264a3462e0
374 changed files with 332649 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
/***************************************************************************//**
* @file
* @brief Emlib peripheral API "assert" implementation.
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_ASSERT_H
#define EM_ASSERT_H
#include "sl_assert.h"
#endif /* EM_ASSERT_H */

View File

@@ -0,0 +1,473 @@
/***************************************************************************//**
* @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.
*
******************************************************************************/
#ifndef EM_BURTC_H
#define EM_BURTC_H
#include "em_device.h"
#if defined(BURTC_PRESENT)
#include <stdbool.h>
#include "sl_assert.h"
#include "em_bus.h"
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup burtc
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** BURTC clock divisors. These values are valid for the BURTC prescaler. */
#define burtcClkDiv_1 1 /**< Divide clock by 1. */
#define burtcClkDiv_2 2 /**< Divide clock by 2. */
#define burtcClkDiv_4 4 /**< Divide clock by 4. */
#define burtcClkDiv_8 8 /**< Divide clock by 8. */
#define burtcClkDiv_16 16 /**< Divide clock by 16. */
#define burtcClkDiv_32 32 /**< Divide clock by 32. */
#define burtcClkDiv_64 64 /**< Divide clock by 64. */
#define burtcClkDiv_128 128 /**< Divide clock by 128. */
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
#if defined(_SILICON_LABS_32B_SERIES_0)
/** BURTC clock selection. */
typedef enum {
/** Ultra low frequency (1 kHz) clock. */
burtcClkSelULFRCO = BURTC_CTRL_CLKSEL_ULFRCO,
/** Low frequency RC oscillator. */
burtcClkSelLFRCO = BURTC_CTRL_CLKSEL_LFRCO,
/** Low frequency crystal oscillator. */
burtcClkSelLFXO = BURTC_CTRL_CLKSEL_LFXO
} BURTC_ClkSel_TypeDef;
/** BURTC mode of operation. */
typedef enum {
/** Disable BURTC */
burtcModeDisable = BURTC_CTRL_MODE_DISABLE,
/** Enable and start BURTC counter in EM0 to EM2. */
burtcModeEM2 = BURTC_CTRL_MODE_EM2EN,
/** Enable and start BURTC counter in EM0 to EM3. */
burtcModeEM3 = BURTC_CTRL_MODE_EM3EN,
/** Enable and start BURTC counter in EM0 to EM4. */
burtcModeEM4 = BURTC_CTRL_MODE_EM4EN,
} BURTC_Mode_TypeDef;
/** BURTC low power mode. */
typedef enum {
/** Low Power Mode is disabled. */
burtcLPDisable = BURTC_LPMODE_LPMODE_DISABLE,
/** Low Power Mode is always enabled. */
burtcLPEnable = BURTC_LPMODE_LPMODE_ENABLE,
/** Low Power Mode when system enters backup mode. */
burtcLPBU = BURTC_LPMODE_LPMODE_BUEN
} BURTC_LP_TypeDef;
#endif
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
#if defined(_SILICON_LABS_32B_SERIES_0)
/** BURTC initialization structure for Series 0 devices. */
typedef struct {
bool enable; /**< Enable BURTC after initialization (starts counter). */
BURTC_Mode_TypeDef mode; /**< Configure energy mode operation. */
bool debugRun; /**< If true, counter will keep running under debug halt. */
BURTC_ClkSel_TypeDef clkSel; /**< Select clock source. */
uint32_t clkDiv; /**< Clock divider; for ULFRCO 1Khz or 2kHz operation. */
uint32_t lowPowerComp; /**< Number of least significant clock bits to ignore in low power mode. */
bool timeStamp; /**< Enable time stamp on entering backup power domain. */
bool compare0Top; /**< Set if Compare Value 0 is also top value (counter restart). */
BURTC_LP_TypeDef lowPowerMode; /**< Low power operation mode, requires LFXO or LFRCO. */
} BURTC_Init_TypeDef;
/** Default configuration for BURTC initialization structure. */
#define BURTC_INIT_DEFAULT \
{ \
true, \
burtcModeEM2, \
false, \
burtcClkSelULFRCO, \
burtcClkDiv_1, \
0, \
true, \
false, \
burtcLPDisable, \
}
#elif defined(_SILICON_LABS_32B_SERIES_2)
/** BURTC initialization structure for Series 2 devices. */
typedef struct {
bool start; /**< Start BURTC after initialization */
bool debugRun; /**< If true, counter will keep running under debug halt */
uint32_t clkDiv; /**< Clock divider. Supported range is 1-32768 */
bool compare0Top; /**< Set if Compare Value 0 is also top value (counter restart) */
bool em4comp; /**< Enable EM4 wakeup on compare match. */
bool em4overflow; /**< Enable EM4 wakeup on counter overflow. */
} BURTC_Init_TypeDef;
/** Default configuration for BURTC init structure */
#define BURTC_INIT_DEFAULT \
{ \
true, \
false, \
1, \
0, \
false, \
false, \
}
#endif
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Clear one or more pending BURTC interrupts.
*
* @param[in] flags
* BURTC interrupt sources to clear. Use a set of interrupt flags OR-ed
* together to clear multiple interrupt sources for the BURTC module
* (BURTC_IFS_nnn).
******************************************************************************/
__STATIC_INLINE void BURTC_IntClear(uint32_t flags)
{
#if defined(BURTC_HAS_SET_CLEAR)
BURTC->IF_CLR = flags;
#else
BURTC->IFC = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more BURTC interrupts.
*
* @param[in] flags
* BURTC interrupt sources to disable. Use a set of interrupt flags OR-ed
* together to disable multiple interrupt sources for the BURTC module
* (BURTC_IFS_nnn).
******************************************************************************/
__STATIC_INLINE void BURTC_IntDisable(uint32_t flags)
{
#if defined(BURTC_HAS_SET_CLEAR)
BURTC->IEN_CLR = flags;
#else
BURTC->IEN &= ~(flags);
#endif
}
/***************************************************************************//**
* @brief
* Enable one or more BURTC interrupts.
*
* @note
* Depending on use, a pending interrupt may already be set prior to
* enabling the interrupt. Consider using BURTC_IntClear() prior to enabling
* if a pending interrupt should be ignored.
*
* @param[in] flags
* BURTC interrupt sources to enable. Use a set of interrupt flags OR-ed
* together to set multiple interrupt sources for the BURTC module
* (BURTC_IFS_nnn).
******************************************************************************/
__STATIC_INLINE void BURTC_IntEnable(uint32_t flags)
{
#if defined(BURTC_HAS_SET_CLEAR)
BURTC->IEN_SET = flags;
#else
BURTC->IEN |= flags;
#endif
}
/***************************************************************************//**
* @brief
* Get pending BURTC interrupt flags.
*
* @note
* This function does not clear the event bits.
*
* @return
* Pending BURTC interrupt sources. Returns a set of interrupt flags OR-ed
* together for multiple interrupt sources in the BURTC module (BURTC_IFS_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t BURTC_IntGet(void)
{
return BURTC->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending BURTC interrupt flags.
*
* @note
* The event bits are not cleared by the use of this function.
*
* @return
* Pending BURTC interrupt sources that is also enabled. Returns a set of
* interrupt flags OR-ed together for multiple interrupt sources in the
* BURTC module (BURTC_IFS_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t BURTC_IntGetEnabled(void)
{
uint32_t tmp;
/* Get enabled interrupts */
tmp = BURTC->IEN;
/* Return set interrupts */
return BURTC->IF & tmp;
}
/***************************************************************************//**
* @brief
* Set one or more pending BURTC interrupts from SW.
*
* @param[in] flags
* BURTC interrupt sources to set to pending. Use a set of interrupt flags
* OR-ed together to set multiple interrupt sources for the BURTC module
* (BURTC_IFS_nnn).
******************************************************************************/
__STATIC_INLINE void BURTC_IntSet(uint32_t flags)
{
#if defined(BURTC_HAS_SET_CLEAR)
BURTC->IF_SET = flags;
#else
BURTC->IFS = flags;
#endif
}
/***************************************************************************//**
* @brief
* Status of BURTC RAM, timestamp and LP Mode
*
* @return A mask logically OR-ed status bits
******************************************************************************/
__STATIC_INLINE uint32_t BURTC_Status(void)
{
return BURTC->STATUS;
}
#if defined(BURTC_CMD_CLRSTATUS)
/***************************************************************************//**
* @brief
* Clear and reset BURTC status register
******************************************************************************/
__STATIC_INLINE void BURTC_StatusClear(void)
{
BURTC->CMD = BURTC_CMD_CLRSTATUS;
}
#endif
/***************************************************************************//**
* @brief
* Wait for the BURTC to complete all synchronization of register changes
* and commands.
******************************************************************************/
__STATIC_INLINE void BURTC_SyncWait(void)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
while ((BURTC->EN != 0U) && (BURTC->SYNCBUSY != 0U)) {
/* Wait for previous synchronization to finish */
}
#else
while (BURTC->SYNCBUSY != 0U) {
/* Wait for previous synchronization to finish */
}
#endif
}
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Start BURTC counter.
*
* This function will send a start command to the BURTC peripheral. The BURTC
* peripheral will use some LF clock ticks before the command is executed.
* The @ref BURTC_SyncWait() function can be used to wait for the start command
* to be executed.
*
* @note
* This function requires the BURTC to be enabled.
******************************************************************************/
__STATIC_INLINE void BURTC_Start(void)
{
BURTC_SyncWait();
BURTC->CMD = BURTC_CMD_START;
}
/***************************************************************************//**
* @brief
* Stop the BURTC counter.
*
* This function will send a stop command to the BURTC peripheral. The BURTC
* peripheral will use some LF clock ticks before the command is executed.
* The @ref BURTC_SyncWait() function can be used to wait for the stop command
* to be executed.
*
* @note
* This function requires the BURTC to be enabled.
******************************************************************************/
__STATIC_INLINE void BURTC_Stop(void)
{
BURTC_SyncWait();
BURTC->CMD = BURTC_CMD_STOP;
}
#endif
/***************************************************************************//**
* @brief Get BURTC counter.
*
* @return
* BURTC counter value
******************************************************************************/
__STATIC_INLINE uint32_t BURTC_CounterGet(void)
{
return BURTC->CNT;
}
#if defined(_SILICON_LABS_32B_SERIES_0)
/***************************************************************************//**
* @brief Get BURTC timestamp for entering BU.
*
* @return
* BURTC Time Stamp value
******************************************************************************/
__STATIC_INLINE uint32_t BURTC_TimestampGet(void)
{
return BURTC->TIMESTAMP;
}
/***************************************************************************//**
* @brief Freeze register updates until enabled.
* @param[in] enable If true, registers are not updated until enabled again.
******************************************************************************/
__STATIC_INLINE void BURTC_FreezeEnable(bool enable)
{
BUS_RegBitWrite(&BURTC->FREEZE, _BURTC_FREEZE_REGFREEZE_SHIFT, enable);
}
/***************************************************************************//**
* @brief Shut down power to retention register bank.
* @param[in] enable
* If true, shuts off power to retention registers.
* @note
* When power retention is disabled, it can't be enabled again (until
* reset).
******************************************************************************/
__STATIC_INLINE void BURTC_Powerdown(bool enable)
{
BUS_RegBitWrite(&BURTC->POWERDOWN, _BURTC_POWERDOWN_RAM_SHIFT, enable);
}
/***************************************************************************//**
* @brief
* Set a value in one of the retention registers.
*
* @param[in] num
* Register to set
* @param[in] data
* Value to put into register
******************************************************************************/
__STATIC_INLINE void BURTC_RetRegSet(uint32_t num, uint32_t data)
{
EFM_ASSERT(num <= 127);
BURTC->RET[num].REG = data;
}
/***************************************************************************//**
* @brief
* Read a value from one of the retention registers.
*
* @param[in] num
* Retention Register to read
*
* @return
* Value of the retention register
******************************************************************************/
__STATIC_INLINE uint32_t BURTC_RetRegGet(uint32_t num)
{
EFM_ASSERT(num <= 127);
return BURTC->RET[num].REG;
}
#endif
/***************************************************************************//**
* @brief
* Lock BURTC registers, which will protect from writing new config settings.
******************************************************************************/
__STATIC_INLINE void BURTC_Lock(void)
{
BURTC->LOCK = 0x0;
}
/***************************************************************************//**
* @brief
* Unlock BURTC registers, which will enable write access to change configuration.
******************************************************************************/
__STATIC_INLINE void BURTC_Unlock(void)
{
BURTC->LOCK = BURTC_LOCK_LOCKKEY_UNLOCK;
}
void BURTC_Reset(void);
void BURTC_Init(const BURTC_Init_TypeDef *burtcInit);
void BURTC_Enable(bool enable);
void BURTC_CounterReset(void);
void BURTC_CompareSet(unsigned int comp, uint32_t value);
uint32_t BURTC_CompareGet(unsigned int comp);
#if defined(_BURTC_CTRL_MASK)
uint32_t BURTC_ClockFreqGet(void);
#endif
/** @} (end addtogroup burtc) */
#ifdef __cplusplus
}
#endif
#endif /* BURTC_PRESENT */
#endif /* EM_BURTC_H */

View File

@@ -0,0 +1,350 @@
/***************************************************************************//**
* @file
* @brief RAM and peripheral bit-field set and clear 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.
*
******************************************************************************/
#ifndef EM_BUS_H
#define EM_BUS_H
#include "sl_assert.h"
#include "sl_core.h"
#include "em_device.h"
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup bus BUS - Bitfield Read/Write
* @brief BUS register and RAM bit/field read/write API
* @details
* API to perform bit-band and field set/clear access to RAM and peripherals.
* @{
******************************************************************************/
/***************************************************************************//**
* @brief
* Perform a single-bit write operation on a 32-bit word in RAM.
*
* @details
* This function uses Cortex-M bit-banding hardware to perform an atomic
* read-modify-write operation on a single bit write on a 32-bit word in RAM.
* See the reference manual for more details about bit-banding.
*
* @note
* This function is atomic on Cortex-M cores with bit-banding support. Bit-
* banding is a multi cycle read-modify-write bus operation. RAM bit-banding is
* performed using the memory alias region at BITBAND_RAM_BASE.
*
* @param[in] addr An ddress of a 32-bit word in RAM.
*
* @param[in] bit A bit position to write, 0-31.
*
* @param[in] val A value to set bit to, 0 or 1.
******************************************************************************/
__STATIC_INLINE void BUS_RamBitWrite(volatile uint32_t *addr,
unsigned int bit,
unsigned int val)
{
#if defined(BITBAND_RAM_BASE)
uint32_t aliasAddr =
BITBAND_RAM_BASE + (((uint32_t)addr - SRAM_BASE) * (uint32_t) 32) + (bit * (uint32_t) 4);
*(volatile uint32_t *)aliasAddr = (uint32_t)val;
#else
uint32_t tmp = *addr;
/* Make sure val is not more than 1 because only one bit needs to be set. */
*addr = (tmp & ~(1UL << bit)) | ((val & 1UL) << bit);
#endif
}
/***************************************************************************//**
* @brief
* Perform a single-bit read operation on a 32-bit word in RAM.
*
* @details
* This function uses Cortex-M bit-banding hardware to perform an atomic
* read operation on a single register bit. See the
* reference manual for more details about bit-banding.
*
* @note
* This function is atomic on Cortex-M cores with bit-banding support.
* RAM bit-banding is performed using the memory alias region
* at BITBAND_RAM_BASE.
*
* @param[in] addr RAM address.
*
* @param[in] bit A bit position to read, 0-31.
*
* @return
* The requested bit shifted to bit position 0 in the return value.
******************************************************************************/
__STATIC_INLINE unsigned int BUS_RamBitRead(volatile const uint32_t *addr,
unsigned int bit)
{
#if defined(BITBAND_RAM_BASE)
uint32_t aliasAddr =
BITBAND_RAM_BASE + (((uint32_t)addr - SRAM_BASE) * (uint32_t) 32) + (bit * (uint32_t) 4);
return *(volatile uint32_t *)aliasAddr;
#else
return ((*addr) >> bit) & 1UL;
#endif
}
/***************************************************************************//**
* @brief
* Perform a single-bit write operation on a peripheral register.
*
* @details
* This function uses Cortex-M bit-banding hardware to perform an atomic
* read-modify-write operation on a single register bit. See the
* reference manual for more details about bit-banding.
*
* @note
* This function is atomic on Cortex-M cores with bit-banding support. Bit-
* banding is a multi cycle read-modify-write bus operation. Peripheral register
* bit-banding is performed using the memory alias region at BITBAND_PER_BASE.
*
* @param[in] addr A peripheral register address.
*
* @param[in] bit A bit position to write, 0-31.
*
* @param[in] val A value to set bit to, 0 or 1.
******************************************************************************/
__STATIC_INLINE void BUS_RegBitWrite(volatile uint32_t *addr,
unsigned int bit,
unsigned int val)
{
EFM_ASSERT(bit < 32U);
#if defined(PER_REG_BLOCK_SET_OFFSET) && defined(PER_REG_BLOCK_CLR_OFFSET)
uint32_t aliasAddr;
if (val != 0U) {
aliasAddr = (uint32_t)addr + PER_REG_BLOCK_SET_OFFSET;
} else {
aliasAddr = (uint32_t)addr + PER_REG_BLOCK_CLR_OFFSET;
}
*(volatile uint32_t *)aliasAddr = 1UL << bit;
#elif defined(BITBAND_PER_BASE)
uint32_t aliasAddr =
BITBAND_PER_BASE + (((uint32_t)addr - PER_MEM_BASE) * (uint32_t) 32) + (bit * (uint32_t) 4);
*(volatile uint32_t *)aliasAddr = (uint32_t)val;
#else
uint32_t tmp = *addr;
/* Make sure val is not more than 1 because only one bit needs to be set. */
*addr = (tmp & ~(1 << bit)) | ((val & 1) << bit);
#endif
}
/***************************************************************************//**
* @brief
* Perform a single-bit read operation on a peripheral register.
*
* @details
* This function uses Cortex-M bit-banding hardware to perform an atomic
* read operation on a single register bit. See the
* reference manual for more details about bit-banding.
*
* @note
* This function is atomic on Cortex-M cores with bit-banding support.
* Peripheral register bit-banding is performed using the memory alias
* region at BITBAND_PER_BASE.
*
* @param[in] addr A peripheral register address.
*
* @param[in] bit A bit position to read, 0-31.
*
* @return
* The requested bit shifted to bit position 0 in the return value.
******************************************************************************/
__STATIC_INLINE unsigned int BUS_RegBitRead(volatile const uint32_t *addr,
unsigned int bit)
{
#if defined(BITBAND_PER_BASE)
uint32_t aliasAddr =
BITBAND_PER_BASE + (((uint32_t)addr - PER_MEM_BASE) * (uint32_t)32) + (bit * (uint32_t) 4);
return *(volatile uint32_t *)aliasAddr;
#else
return ((*addr) >> bit) & 1UL;
#endif
}
/***************************************************************************//**
* @brief
* Perform a masked set operation on a peripheral register address.
*
* @details
* A peripheral register masked set provides a single-cycle and atomic set
* operation of a bit-mask in a peripheral register. All 1s in the mask are
* set to 1 in the register. All 0s in the mask are not changed in the
* register.
* RAMs and special peripherals are not supported. See the
* reference manual for more details about the peripheral register field set.
*
* @note
* This function is single-cycle and atomic on cores with peripheral bit set
* and clear support. It uses the memory alias region at PER_BITSET_MEM_BASE.
*
* @param[in] addr A peripheral register address.
*
* @param[in] mask A mask to set.
******************************************************************************/
__STATIC_INLINE void BUS_RegMaskedSet(volatile uint32_t *addr,
uint32_t mask)
{
#if defined(PER_REG_BLOCK_SET_OFFSET)
uint32_t aliasAddr = (uint32_t)addr + PER_REG_BLOCK_SET_OFFSET;
*(volatile uint32_t *)aliasAddr = mask;
#elif defined(PER_BITSET_MEM_BASE)
uint32_t aliasAddr = PER_BITSET_MEM_BASE + ((uint32_t)addr - PER_MEM_BASE);
*(volatile uint32_t *)aliasAddr = mask;
#else
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_CRITICAL();
*addr |= mask;
CORE_EXIT_CRITICAL();
#endif
}
/***************************************************************************//**
* @brief
* Perform a masked clear operation on the peripheral register address.
*
* @details
* A peripheral register masked clear provides a single-cycle and atomic clear
* operation of a bit-mask in a peripheral register. All 1s in the mask are
* set to 0 in the register.
* All 0s in the mask are not changed in the register.
* RAMs and special peripherals are not supported. See the
* reference manual for more details about the peripheral register field clear.
*
* @note
* This function is single-cycle and atomic on cores with peripheral bit set
* and clear support. It uses the memory alias region at PER_BITCLR_MEM_BASE.
*
* @param[in] addr A peripheral register address.
*
* @param[in] mask A mask to clear.
******************************************************************************/
__STATIC_INLINE void BUS_RegMaskedClear(volatile uint32_t *addr,
uint32_t mask)
{
#if defined(PER_REG_BLOCK_CLR_OFFSET)
uint32_t aliasAddr = (uint32_t)addr + PER_REG_BLOCK_CLR_OFFSET;
*(volatile uint32_t *)aliasAddr = mask;
#elif defined(PER_BITCLR_MEM_BASE)
uint32_t aliasAddr = PER_BITCLR_MEM_BASE + ((uint32_t)addr - PER_MEM_BASE);
*(volatile uint32_t *)aliasAddr = mask;
#else
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_CRITICAL();
*addr &= ~mask;
CORE_EXIT_CRITICAL();
#endif
}
/***************************************************************************//**
* @brief
* Perform peripheral register masked write.
*
* @details
* This function first reads the peripheral register and updates only bits
* that are set in the mask with content of val. Typically, the mask is a
* bit-field in the register and the value val is within the mask.
*
* @note
* The read-modify-write operation is executed in a critical section to
* guarantee atomicity. Note that atomicity can only be guaranteed if register
* is modified only by the core, and not by other peripherals (like DMA).
*
* @param[in] addr A peripheral register address.
*
* @param[in] mask A peripheral register mask.
*
* @param[in] val A peripheral register value. The value must be shifted to the
correct bit position in the register corresponding to the field
defined by the mask parameter. The register value must be
contained in the field defined by the mask parameter. The
register value is masked to prevent involuntary spillage.
******************************************************************************/
#if defined(__GNUC__) && __GNUC__ >= 10
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wanalyzer-null-dereference"
#endif
__STATIC_INLINE void BUS_RegMaskedWrite(volatile uint32_t *addr,
uint32_t mask,
uint32_t val)
{
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_CRITICAL();
EFM_ASSERT(addr != 0);
*addr = (*addr & ~mask) | (val & mask);
CORE_EXIT_CRITICAL();
}
#if defined(__GNUC__) && __GNUC__ >= 10
#pragma GCC diagnostic pop
#endif
/***************************************************************************//**
* @brief
* Perform a peripheral register masked read.
*
* @details
* Read an unshifted and masked value from a peripheral register.
*
* @note
* This operation is not hardware accelerated.
*
* @param[in] addr A peripheral register address.
*
* @param[in] mask A peripheral register mask.
*
* @return
* An unshifted and masked register value.
******************************************************************************/
__STATIC_INLINE uint32_t BUS_RegMaskedRead(volatile const uint32_t *addr,
uint32_t mask)
{
return *addr & mask;
}
/** @} (end addtogroup bus) */
#ifdef __cplusplus
}
#endif
#endif /* EM_BUS_H */

View File

@@ -0,0 +1,483 @@
/***************************************************************************//**
* @file
* @brief Chip Errata Workarounds
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_CHIP_H
#define EM_CHIP_H
#include "em_device.h"
#include "sl_common.h"
#if defined(_SILICON_LABS_32B_SERIES) && (_SILICON_LABS_32B_SERIES <= 2)
#include "em_system.h"
#endif
#include "em_bus.h"
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
#include "em_gpio.h"
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_240)
#include "em_cmu.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup chip CHIP - Chip Errata Workarounds
* @brief Chip errata workaround APIs
* @details
* API to apply chip errata workarounds at initialization and reset.
* @{
******************************************************************************/
/**************************************************************************//**
* @brief
* Chip initialization routine for revision errata workarounds.
*
* @note
* This function must be called immediately in main().
*
* This initialization function configures the device to a state
* as similar to later revisions as possible to improve software compatibility
* with newer parts. See the device-specific errata for details.
*****************************************************************************/
__STATIC_INLINE void CHIP_Init(void)
{
#if defined(MSC_CACHECMD_INVCACHE)
MSC->CACHECMD = MSC_CACHECMD_INVCACHE;
#elif defined(MSC_CMD_INVCACHE)
MSC->CMD = MSC_CMD_INVCACHE;
#endif
#if defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GECKO_FAMILY)
uint32_t rev;
SYSTEM_ChipRevision_TypeDef chipRev;
volatile uint32_t *reg;
rev = *(volatile uint32_t *)(0x0FE081FC);
/* Engineering Sample calibration setup. */
if ((rev >> 24) == 0) {
reg = (volatile uint32_t *)0x400CA00C;
*reg &= ~(0x70UL);
/* DREG */
reg = (volatile uint32_t *)0x400C6020;
*reg &= ~(0xE0000000UL);
*reg |= ~(7UL << 25);
}
if ((rev >> 24) <= 3) {
/* DREG */
reg = (volatile uint32_t *)0x400C6020;
*reg &= ~(0x00001F80UL);
/* Update CMU reset values. */
reg = (volatile uint32_t *)0x400C8040;
*reg = 0;
reg = (volatile uint32_t *)0x400C8044;
*reg = 0;
reg = (volatile uint32_t *)0x400C8058;
*reg = 0;
reg = (volatile uint32_t *)0x400C8060;
*reg = 0;
reg = (volatile uint32_t *)0x400C8078;
*reg = 0;
}
SYSTEM_ChipRevisionGet(&chipRev);
if (chipRev.major == 0x01) {
/* Rev A errata handling for EM2/3. Must enable DMA clock to get EM2/3 */
/* to work. This will be fixed in later chip revisions and is only needed for rev A. */
if (chipRev.minor == 00) {
reg = (volatile uint32_t *)0x400C8040;
*reg |= 0x2;
}
/* Rev A+B errata handling for I2C when using EM2/3. USART0 clock must be enabled */
/* after waking up from EM2/EM3 to get I2C to work. This will be fixed in */
/* later chip revisions and is only needed for rev A+B. */
if (chipRev.minor <= 0x01) {
reg = (volatile uint32_t *)0x400C8044;
*reg |= 0x1;
}
}
/* Ensure correct ADC/DAC calibration value. */
rev = *(volatile uint32_t *)0x0FE081F0;
if (rev < 0x4C8ABA00) {
uint32_t cal;
/* Enable ADC/DAC clocks. */
reg = (volatile uint32_t *)0x400C8044UL;
*reg |= (1 << 14 | 1 << 11);
/* Retrive calibration values. */
cal = ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x00007F00UL)
>> 8) << 24;
cal |= ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x0000007FUL)
>> 0) << 16;
cal |= ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x00007F00UL)
>> 8) << 8;
cal |= ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x0000007FUL)
>> 0) << 0;
/* ADC0->CAL = 1.25 reference. */
reg = (volatile uint32_t *)0x40002034UL;
*reg = cal;
/* DAC0->CAL = 1.25 reference. */
reg = (volatile uint32_t *)(0x4000402CUL);
cal = *(volatile uint32_t *)0x0FE081C8UL;
*reg = cal;
/* Turn off ADC/DAC clocks. */
reg = (volatile uint32_t *)0x400C8044UL;
*reg &= ~(1 << 14 | 1 << 11);
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GIANT_FAMILY)
/****************************/
/* Fix for errata CMU_E113. */
uint8_t prodRev;
SYSTEM_ChipRevision_TypeDef chipRev;
prodRev = SYSTEM_GetProdRev();
SYSTEM_ChipRevisionGet(&chipRev);
// All Giant and Leopard parts except Leopard Rev E
if ((prodRev >= 16) && (chipRev.minor >= 3)
&& !((chipRev.major == 2) && (chipRev.minor == 4))) {
/* This fixes an issue with the LFXO on high temperatures. */
*(volatile uint32_t*)0x400C80C0 =
(*(volatile uint32_t*)0x400C80C0 & ~(1 << 6) ) | (1 << 4);
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_HAPPY_FAMILY)
uint8_t prodRev;
prodRev = SYSTEM_GetProdRev();
if (prodRev <= 129) {
/* This fixes a mistaken internal connection between PC0 and PC4. */
/* This disables an internal pull-down on PC4. */
*(volatile uint32_t*)(0x400C6018) = (1 << 26) | (5 << 0);
/* This disables an internal LDO test signal driving PC4. */
*(volatile uint32_t*)(0x400C80E4) &= ~(1 << 24);
}
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
/****************************
* Fixes for errata GPIO_E201 (slewrate) and
* HFXO high-temperature oscillator startup robustness fix. */
uint32_t port;
uint32_t clkEn;
uint8_t prodRev;
const uint32_t setVal = (0x5 << _GPIO_P_CTRL_SLEWRATEALT_SHIFT)
| (0x5 << _GPIO_P_CTRL_SLEWRATE_SHIFT);
const uint32_t resetVal = _GPIO_P_CTRL_RESETVALUE
& ~(_GPIO_P_CTRL_SLEWRATE_MASK
| _GPIO_P_CTRL_SLEWRATEALT_MASK);
prodRev = SYSTEM_GetProdRev();
SYSTEM_ChipRevision_TypeDef chipRev;
SYSTEM_ChipRevisionGet(&chipRev);
/* This errata is fixed in hardware from PRODREV 0x8F. */
if (prodRev < 0x8F) {
/* Fixes for errata GPIO_E201 (slewrate). */
/* Save HFBUSCLK enable state and enable GPIO clock. */
clkEn = CMU->HFBUSCLKEN0;
CMU->HFBUSCLKEN0 = clkEn | CMU_HFBUSCLKEN0_GPIO;
/* Update slewrate. */
for (port = 0; port <= GPIO_PORT_MAX; port++) {
GPIO->P[port].CTRL = setVal | resetVal;
}
/* Restore HFBUSCLK enable state. */
CMU->HFBUSCLKEN0 = clkEn;
}
/* This errata is fixed in hardware from PRODREV 0x90. */
if (prodRev < 0x90) {
/* HFXO high-temperature oscillator startup robustness fix. */
CMU->HFXOSTARTUPCTRL =
(CMU->HFXOSTARTUPCTRL & ~_CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_MASK)
| (0x20 << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT);
}
if (chipRev.major == 0x01) {
/* Fix for errata EMU_E210 - Potential Power-Down When Entering EM2 */
*(volatile uint32_t *)(EMU_BASE + 0x164) |= 0x4;
}
/****************************
* Fix for errata DCDC_E206.
* Disable bypass limit enabled temporarily in SystemInit() errata
* workaround. */
BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, 0);
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84)
uint8_t prodRev = SYSTEM_GetProdRev();
/* EM2 current fixes for early samples. */
if (prodRev == 0U) {
*(volatile uint32_t *)(EMU_BASE + 0x190UL) = 0x0000ADE8UL;
*(volatile uint32_t *)(EMU_BASE + 0x198UL) |= (0x1UL << 2);
*(volatile uint32_t *)(EMU_BASE + 0x190UL) = 0x0;
}
if (prodRev < 2U) {
*(volatile uint32_t *)(EMU_BASE + 0x164UL) |= (0x1UL << 13);
}
/* Set optimal LFRCOCTRL VREFUPDATE and enable duty cycling of VREF. */
CMU->LFRCOCTRL = (CMU->LFRCOCTRL & ~_CMU_LFRCOCTRL_VREFUPDATE_MASK)
| CMU_LFRCOCTRL_VREFUPDATE_64CYCLES
| CMU_LFRCOCTRL_ENVREF;
#endif
#if defined(_SILICON_LABS_32B_SERIES_1) \
&& defined(_EFR_DEVICE) && (_SILICON_LABS_GECKO_INTERNAL_SDID >= 84)
MSC->CTRL |= 0x1UL << 8;
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_89)
SYSTEM_ChipRevision_TypeDef chipRev;
SYSTEM_ChipRevisionGet(&chipRev);
if ((chipRev.major > 1) || (chipRev.minor >= 3)) {
/* PLFRCO trim values */
*(volatile uint32_t *)(CMU_BASE + 0x28CUL) = 608;
*(volatile uint32_t *)(CMU_BASE + 0x290UL) = 356250;
*(volatile uint32_t *)(CMU_BASE + 0x2F0UL) = 0x04000118;
*(volatile uint32_t *)(CMU_BASE + 0x2F8UL) = 0x08328400;
}
#endif
/* Charge redist setup (fixed value): LCD->DBGCTRL.CHGRDSTSTR = 1 (reset: 0). */
#if defined(_LCD_DISPCTRL_CHGRDST_MASK)
#if defined(_SILICON_LABS_32B_SERIES_1)
CMU->HFBUSCLKEN0 |= CMU_HFBUSCLKEN0_LE;
CMU->LFACLKEN0 |= CMU_LFACLKEN0_LCD;
*(volatile uint32_t *)(LCD_BASE + 0x034) |= (0x1UL << 12);
CMU->LFACLKEN0 &= ~CMU_LFACLKEN0_LCD;
CMU->HFBUSCLKEN0 &= ~CMU_HFBUSCLKEN0_LE;
#endif
#endif
#if defined(_SILICON_LABS_32B_SERIES_1) \
&& !defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80) \
&& !defined(ERRATA_FIX_EMU_E220_DECBOD_IGNORE)
/* First part of the EMU_E220 DECBOD Errata fix. DECBOD Reset can occur
* during voltage scaling after EM2/3 wakeup. Second part is in em_emu.c */
*(volatile uint32_t *)(EMU_BASE + 0x1A4) |= 0x1f << 10;
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
SYSTEM_ChipRevision_TypeDef chipRev;
SYSTEM_ChipRevisionGet(&chipRev);
if (chipRev.major == 0x01 && (HFXO0->STATUS & HFXO_STATUS_ENS) == 0U) {
/* Change HFXO default peak detector settings. */
*(volatile uint32_t*)(HFXO0_BASE + 0x34U) =
(*(volatile uint32_t*)(HFXO0_BASE + 0x34U) & 0xFF8000FFU)
| 0x00178500U;
/* Change HFXO low power control settings. */
*(volatile uint32_t*)(HFXO0_BASE + 0x30U) =
(*(volatile uint32_t*)(HFXO0_BASE + 0x30U) & 0xFFFF0FFFU)
| 0x0000C000U;
/* Change default SQBUF bias current. */
*(volatile uint32_t*)(HFXO0_BASE + 0x30U) |= 0x700;
}
if (chipRev.major == 0x01 && chipRev.minor == 0x0) {
/* Trigger RAM read for each RAM instance */
volatile uint32_t *dmem = (volatile uint32_t *) DMEM_RAM0_RAM_MEM_BASE;
for (uint32_t i = 0U; i < DMEM_NUM_BANK; i++) {
// Force memory read
*dmem;
dmem += (DMEM_BANK0_SIZE / 4U);
}
}
/* Set TRACE clock to intended reset value. */
CMU->TRACECLKCTRL = (CMU->TRACECLKCTRL & ~_CMU_TRACECLKCTRL_CLKSEL_MASK)
| CMU_TRACECLKCTRL_CLKSEL_HFRCOEM23;
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_205)
#if defined(SL_TRUSTZONE_SECURE)
#define HFRCO_CLK_CFG_CLR_ADDR (0x40012020UL)
#else
#define HFRCO_CLK_CFG_CLR_ADDR (0x50012020UL)
#endif
#define HFRCO_CLK_CFG_CLKOUTDIS0 (0x4UL)
if (SYSTEM_GetProdRev() == 1) {
bool hfrcoClkIsOff = (CMU->CLKEN0 & CMU_CLKEN0_HFRCO0) == 0;
CMU->CLKEN0_SET = CMU_CLKEN0_HFRCO0;
/* Enable HFRCO CLKOUT0. */
*(volatile uint32_t*)(HFRCO_CLK_CFG_CLR_ADDR) = HFRCO_CLK_CFG_CLKOUTDIS0;
if (hfrcoClkIsOff) {
CMU->CLKEN0_CLR = CMU_CLKEN0_HFRCO0;
}
}
#endif
/* PM-3503 */
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_210)
{
bool syscfgClkIsOff = ((CMU->CLKEN0 & CMU_CLKEN0_SYSCFG) == 0);
CMU->CLKEN0_SET = CMU_CLKEN0_SYSCFG;
bool dcdcClkIsOff = ((CMU->CLKEN0 & CMU_CLKEN0_DCDC) == 0);
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
bool dcdcIsLock = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK_LOCKED) != 0);
DCDC->LOCK = DCDC_LOCK_LOCKKEY_UNLOCKKEY;
while (DCDC->SYNCBUSY & DCDC_SYNCBUSY_CTRL) {
/* Wait for previous synchronization to finish */
}
DCDC->CTRL_CLR = DCDC_CTRL_MODE;
while ((DCDC->STATUS & DCDC_STATUS_BYPSW) == 0U) {
/* Wait for BYPASS switch enable. */
}
if (dcdcIsLock) {
DCDC->LOCK = ~DCDC_LOCK_LOCKKEY_UNLOCKKEY;
}
if (dcdcClkIsOff) {
CMU->CLKEN0_CLR = CMU_CLKEN0_DCDC;
}
if (syscfgClkIsOff) {
CMU->CLKEN0_CLR = CMU_CLKEN0_SYSCFG;
}
}
#endif
/* PM-5163 */
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_215) \
&& defined(_SILICON_LABS_EFR32_2G4HZ_HP_PA_PRESENT) \
&& (_SILICON_LABS_EFR32_2G4HZ_HP_PA_MAX_OUTPUT_DBM == 20)
SYSTEM_ChipRevision_TypeDef chipRev;
SYSTEM_ChipRevisionGet(&chipRev);
if (chipRev.major == 0x01 && chipRev.minor == 0x00) {
bool hfxo0ClkIsOff = (CMU->CLKEN0 & CMU_CLKEN0_HFXO0) == 0;
CMU->CLKEN0_SET = CMU_CLKEN0_HFXO0;
*(volatile uint32_t*)(HFXO0_BASE + 0x0034UL) =
(*(volatile uint32_t*)(HFXO0_BASE + 0x0034UL) & 0xE3FFFFFFUL)
| 0x0C000000UL;
if (hfxo0ClkIsOff) {
CMU->CLKEN0_CLR = CMU_CLKEN0_HFXO0;
}
}
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_240)
// Enable ICache out of reset.
CMU->CLKEN1_SET = _CMU_CLKEN1_ICACHE0_MASK;
ICACHE0->CTRL_CLR = _ICACHE_CTRL_CACHEDIS_MASK;
CMU->CLKEN1_CLR = _CMU_CLKEN1_ICACHE0_MASK;
CMU->CLKEN0_SET = _CMU_CLKEN0_HFRCO0_MASK;
if (((HFRCO0->CAL & _HFRCO_CAL_TUNING_MASK) >> _HFRCO_CAL_TUNING_SHIFT) == _HFRCO_CAL_TUNING_MASK) {
CMU_HFRCODPLLBandSet(cmuHFRCODPLLFreq_19M0Hz);
}
CMU->CLKEN0_CLR = _CMU_CLKEN0_HFRCO0_MASK;
#endif
}
/**************************************************************************//**
* @brief
* Chip reset routine with errata workarounds.
*
* @note
* This function should be called to reset the chip. It does not return.
*
* This function applies any errata workarounds needed to cleanly reset the
* device and then performs a system reset. See the device-specific errata for
* details.
*****************************************************************************/
__STATIC_INLINE void CHIP_Reset(void)
{
#if defined(_EFR_DEVICE) && defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
/****************************
* Workaround for errata DCDC_E206.
* Disable radio interference minimization features when resetting */
// Ensure access to EMU registers
EMU->LOCK = EMU_LOCK_LOCKKEY_UNLOCK;
EMU->PWRLOCK = EMU_PWRLOCK_LOCKKEY_LOCK;
// No need to do anything if the DCDC is not powering DVDD
if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) == EMU_PWRCFG_PWRCFG_DCDCTODVDD) {
// Make sure radio cannot accidentally re-enable features
*(volatile uint32_t *)(0x40084040UL) = 0x1UL;
// If DCDC is in use, disable features
uint32_t dcdcMode = EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK;
if ((dcdcMode == EMU_DCDCCTRL_DCDCMODE_LOWNOISE)
|| (dcdcMode == EMU_DCDCCTRL_DCDCMODE_LOWPOWER)) {
BUS_RegBitWrite((volatile uint32_t *)(0x400E3060UL), 28UL, 0);
BUS_RegBitWrite((volatile uint32_t *)(0x400E3074UL), 0, 0);
}
}
#endif
NVIC_SystemReset();
}
/** @} (end addtogroup chip) */
#ifdef __cplusplus
}
#endif
#endif /* EM_CHIP_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,184 @@
/***************************************************************************//**
* @file
* @brief CMU Compatibility Header
*******************************************************************************
* # License
* <b>Copyright 2020 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.
*
******************************************************************************/
#ifndef EM_CMU_COMPAT_H
#define EM_CMU_COMPAT_H
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
#define CMU_IF_CALRDYIF CMU_IF_CALRDY
#define _CMU_IF_CALRDYIF_SHIFT _CMU_IF_CALRDY_SHIFT
#define _CMU_IF_CALRDYIF_MASK _CMU_IF_CALRDY_MASK
#define _CMU_IF_CALRDYIF_DEFAULT _CMU_IF_CALRDY_DEFAULT
#define CMU_IF_CALRDYIF_DEFAULT CMU_IF_CALRDY_DEFAULT
#define CMU_IF_CALOFIF CMU_IF_CALOF
#define _CMU_IF_CALOFIF_SHIFT _CMU_IF_CALOF_SHIFT
#define _CMU_IF_CALOFIF_MASK _CMU_IF_CALOF_MASK
#define _CMU_IF_CALOFIF_DEFAULT _CMU_IF_CALOF_DEFAULT
#define CMU_IF_CALOFIF_DEFAULT CMU_IF_CALOF_DEFAULT
#define CMU_IEN_CALRDYIEN CMU_IEN_CALRDY
#define _CMU_IEN_CALRDYIEN_SHIFT _CMU_IEN_CALRDY_SHIFT
#define _CMU_IEN_CALRDYIEN_MASK _CMU_IEN_CALRDY_MASK
#define _CMU_IEN_CALRDYIEN_DEFAULT _CMU_IEN_CALRDY_DEFAULT
#define CMU_IEN_CALRDYIEN_DEFAULT CMU_IEN_CALRDY_DEFAULT
#define CMU_IEN_CALOFIEN CMU_IEN_CALOF
#define _CMU_IEN_CALOFIEN_SHIFT _CMU_IEN_CALOF_SHIFT
#define _CMU_IEN_CALOFIEN_MASK _CMU_IEN_CALOF_MASK
#define _CMU_IEN_CALOFIEN_DEFAULT _CMU_IEN_CALOF_DEFAULT
#define CMU_IEN_CALOFIEN_DEFAULT CMU_IEN_CALOF_DEFAULT
#define HFRCO_IF_RDYIF HFRCO_IF_RDY
#define _HFRCO_IF_RDYIF_SHIFT _HFRCO_IF_RDY_SHIFT
#define _HFRCO_IF_RDYIF_MASK _HFRCO_IF_RDY_MASK
#define _HFRCO_IF_RDYIF_DEFAULT _HFRCO_IF_RDY_DEFAULT
#define HFRCO_IF_RDYIF_DEFAULT HFRCO_IF_RDY_DEFAULT
#define HFRCO_IEN_RDYIEN HFRCO_IEN_RDY
#define _HFRCO_IEN_RDYIEN_SHIFT _HFRCO_IEN_RDY_SHIFT
#define _HFRCO_IEN_RDYIEN_MASK _HFRCO_IEN_RDY_MASK
#define _HFRCO_IEN_RDYIEN_DEFAULT _HFRCO_IEN_RDY_DEFAULT
#define HFRCO_IEN_RDYIEN_DEFAULT HFRCO_IEN_RDY_DEFAULT
#define LFRCO_IF_RDYIF LFRCO_IF_RDY
#define _LFRCO_IF_RDYIF_SHIFT _LFRCO_IF_RDY_SHIFT
#define _LFRCO_IF_RDYIF_MASK _LFRCO_IF_RDY_MASK
#define _LFRCO_IF_RDYIF_DEFAULT _LFRCO_IF_RDY_DEFAULT
#define LFRCO_IF_RDYIF_DEFAULT LFRCO_IF_RDY_DEFAULT
#define LFRCO_IF_POSEDGEIF LFRCO_IF_POSEDGE
#define _LFRCO_IF_POSEDGEIF_SHIFT _LFRCO_IF_POSEDGE_SHIFT
#define _LFRCO_IF_POSEDGEIF_MASK _LFRCO_IF_POSEDGE_MASK
#define _LFRCO_IF_POSEDGEIF_DEFAULT _LFRCO_IF_POSEDGE_DEFAULT
#define LFRCO_IF_POSEDGEIF_DEFAULT LFRCO_IF_POSEDGE_DEFAULT
#define LFRCO_IF_NEGEDGEIF LFRCO_IF_NEGEDGE
#define _LFRCO_IF_NEGEDGEIF_SHIFT _LFRCO_IF_NEGEDGE_SHIFT
#define _LFRCO_IF_NEGEDGEIF_MASK _LFRCO_IF_NEGEDGE_MASK
#define _LFRCO_IF_NEGEDGEIF_DEFAULT _LFRCO_IF_NEGEDGE_DEFAULT
#define LFRCO_IF_NEGEDGEIF_DEFAULT LFRCO_IF_NEGEDGE_DEFAULT
#define LFRCO_IF_TCDONEIF LFRCO_IF_TCDONE
#define _LFRCO_IF_TCDONEIF_SHIFT _LFRCO_IF_TCDONE_SHIFT
#define _LFRCO_IF_TCDONEIF_MASK _LFRCO_IF_TCDONE_MASK
#define _LFRCO_IF_TCDONEIF_DEFAULT _LFRCO_IF_TCDONE_DEFAULT
#define LFRCO_IF_TCDONEIF_DEFAULT LFRCO_IF_TCDONE_DEFAULT
#define LFRCO_IF_CALDONEIF LFRCO_IF_CALDONE
#define _LFRCO_IF_CALDONEIF_SHIFT _LFRCO_IF_CALDONE_SHIFT
#define _LFRCO_IF_CALDONEIF_MASK _LFRCO_IF_CALDONE_MASK
#define _LFRCO_IF_CALDONEIF_DEFAULT _LFRCO_IF_CALDONE_DEFAULT
#define LFRCO_IF_CALDONEIF_DEFAULT LFRCO_IF_CALDONE_DEFAULT
#define LFRCO_IF_TEMPCHANGEIF LFRCO_IF_TEMPCHANGE
#define _LFRCO_IF_TEMPCHANGEIF_SHIFT _LFRCO_IF_TEMPCHANGE_SHIFT
#define _LFRCO_IF_TEMPCHANGEIF_MASK _LFRCO_IF_TEMPCHANGE_MASK
#define _LFRCO_IF_TEMPCHANGEIF_DEFAULT _LFRCO_IF_TEMPCHANGE_DEFAULT
#define LFRCO_IF_TEMPCHANGEIF_DEFAULT LFRCO_IF_TEMPCHANGE_DEFAULT
#define LFRCO_IF_SCHEDERRIF LFRCO_IF_SCHEDERR
#define _LFRCO_IF_SCHEDERRIF_SHIFT _LFRCO_IF_SCHEDERR_SHIFT
#define _LFRCO_IF_SCHEDERRIF_MASK _LFRCO_IF_SCHEDERR_MASK
#define _LFRCO_IF_SCHEDERRIF_DEFAULT _LFRCO_IF_SCHEDERR_DEFAULT
#define LFRCO_IF_SCHEDERRIF_DEFAULT LFRCO_IF_SCHEDERR_DEFAULT
#define LFRCO_IF_TCOORIF LFRCO_IF_TCOOR
#define _LFRCO_IF_TCOORIF_SHIFT _LFRCO_IF_TCOOR_SHIFT
#define _LFRCO_IF_TCOORIF_MASK _LFRCO_IF_TCOOR_MASK
#define _LFRCO_IF_TCOORIF_DEFAULT _LFRCO_IF_TCOOR_DEFAULT
#define LFRCO_IF_TCOORIF_DEFAULT LFRCO_IF_TCOOR_DEFAULT
#define LFRCO_IF_CALOORIF LFRCO_IF_CALOOR
#define _LFRCO_IF_CALOORIF_SHIFT _LFRCO_IF_CALOOR_SHIFT
#define _LFRCO_IF_CALOORIF_MASK _LFRCO_IF_CALOOR_MASK
#define _LFRCO_IF_CALOORIF_DEFAULT _LFRCO_IF_CALOOR_DEFAULT
#define LFRCO_IF_CALOORIF_DEFAULT LFRCO_IF_CALOOR_DEFAULT
#define LFRCO_IEN_RDYIEN LFRCO_IEN_RDY
#define _LFRCO_IEN_RDYIEN_SHIFT _LFRCO_IEN_RDY_SHIFT
#define _LFRCO_IEN_RDYIEN_MASK _LFRCO_IEN_RDY_MASK
#define _LFRCO_IEN_RDYIEN_DEFAULT _LFRCO_IEN_RDY_DEFAULT
#define LFRCO_IEN_RDYIEN_DEFAULT LFRCO_IEN_RDY_DEFAULT
#define LFRCO_IEN_POSEDGEIEN LFRCO_IEN_POSEDGE
#define _LFRCO_IEN_POSEDGEIEN_SHIFT _LFRCO_IEN_POSEDGE_SHIFT
#define _LFRCO_IEN_POSEDGEIEN_MASK _LFRCO_IEN_POSEDGE_MASK
#define _LFRCO_IEN_POSEDGEIEN_DEFAULT _LFRCO_IEN_POSEDGE_DEFAULT
#define LFRCO_IEN_POSEDGEIEN_DEFAULT LFRCO_IEN_POSEDGE_DEFAULT
#define LFRCO_IEN_NEGEDGEIEN LFRCO_IEN_NEGEDGE
#define _LFRCO_IEN_NEGEDGEIEN_SHIFT _LFRCO_IEN_NEGEDGE_SHIFT
#define _LFRCO_IEN_NEGEDGEIEN_MASK _LFRCO_IEN_NEGEDGE_MASK
#define _LFRCO_IEN_NEGEDGEIEN_DEFAULT _LFRCO_IEN_NEGEDGE_DEFAULT
#define LFRCO_IEN_NEGEDGEIEN_DEFAULT LFRCO_IEN_NEGEDGE_DEFAULT
#define LFRCO_IEN_TCDONEIEN LFRCO_IEN_TCDONE
#define _LFRCO_IEN_TCDONEIEN_SHIFT _LFRCO_IEN_TCDONE_SHIFT
#define _LFRCO_IEN_TCDONEIEN_MASK _LFRCO_IEN_TCDONE_MASK
#define _LFRCO_IEN_TCDONEIEN_DEFAULT _LFRCO_IEN_TCDONE_DEFAULT
#define LFRCO_IEN_TCDONEIEN_DEFAULT LFRCO_IEN_TCDONE_DEFAULT
#define LFRCO_IEN_CALDONEIEN LFRCO_IEN_CALDONE
#define _LFRCO_IEN_CALDONEIEN_SHIFT _LFRCO_IEN_CALDONE_SHIFT
#define _LFRCO_IEN_CALDONEIEN_MASK _LFRCO_IEN_CALDONE_MASK
#define _LFRCO_IEN_CALDONEIEN_DEFAULT _LFRCO_IEN_CALDONE_DEFAULT
#define LFRCO_IEN_CALDONEIEN_DEFAULT LFRCO_IEN_CALDONE_DEFAULT
#define LFRCO_IEN_TEMPCHANGEIEN LFRCO_IEN_TEMPCHANGE
#define _LFRCO_IEN_TEMPCHANGEIEN_SHIFT _LFRCO_IEN_TEMPCHANGE_SHIFT
#define _LFRCO_IEN_TEMPCHANGEIEN_MASK _LFRCO_IEN_TEMPCHANGE_MASK
#define _LFRCO_IEN_TEMPCHANGEIEN_DEFAULT _LFRCO_IEN_TEMPCHANGE_DEFAULT
#define LFRCO_IEN_TEMPCHANGEIEN_DEFAULT LFRCO_IEN_TEMPCHANGE_DEFAULT
#define LFRCO_IEN_SCHEDERRIEN LFRCO_IEN_SCHEDERR
#define _LFRCO_IEN_SCHEDERRIEN_SHIFT _LFRCO_IEN_SCHEDERR_SHIFT
#define _LFRCO_IEN_SCHEDERRIEN_MASK _LFRCO_IEN_SCHEDERR_MASK
#define _LFRCO_IEN_SCHEDERRIEN_DEFAULT _LFRCO_IEN_SCHEDERR_DEFAULT
#define LFRCO_IEN_SCHEDERRIEN_DEFAULT LFRCO_IEN_SCHEDERR_DEFAULT
#define LFRCO_IEN_TCOORIEN LFRCO_IEN_TCOOR
#define _LFRCO_IEN_TCOORIEN_SHIFT _LFRCO_IEN_TCOOR_SHIFT
#define _LFRCO_IEN_TCOORIEN_MASK _LFRCO_IEN_TCOOR_MASK
#define _LFRCO_IEN_TCOORIEN_DEFAULT _LFRCO_IEN_TCOOR_DEFAULT
#define LFRCO_IEN_TCOORIEN_DEFAULT LFRCO_IEN_TCOOR_DEFAULT
#define LFRCO_IEN_CALOORIEN LFRCO_IEN_CALOOR
#define _LFRCO_IEN_CALOORIEN_SHIFT _LFRCO_IEN_CALOOR_SHIFT
#define _LFRCO_IEN_CALOORIEN_MASK _LFRCO_IEN_CALOOR_MASK
#define _LFRCO_IEN_CALOORIEN_DEFAULT _LFRCO_IEN_CALOOR_DEFAULT
#define LFRCO_IEN_CALOORIEN_DEFAULT LFRCO_IEN_CALOOR_DEFAULT
#endif /* _SILICON_LABS_32B_SERIES_2_CONFIG_2 */
#endif

View File

@@ -0,0 +1,36 @@
/***************************************************************************//**
* @file
* @brief General purpose utilities.
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_COMMON_H
#define EM_COMMON_H
#include "em_device.h"
#include "sl_common.h"
#endif /* EM_COMMON_H */

View File

@@ -0,0 +1,174 @@
/***************************************************************************//**
* @file
* @brief Core interrupt handling API (Device Specific)
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_CORE_H
#define EM_CORE_H
#include "em_device.h"
#include "em_core_generic.h"
#include "sl_common.h"
/***************************************************************************//**
* @addtogroup core
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** Number of words in a NVIC mask set. */
#define CORE_NVIC_REG_WORDS ((EXT_IRQ_COUNT + 31) / 32)
/** Number of entries in a default interrupt vector table. */
#define CORE_DEFAULT_VECTOR_TABLE_ENTRIES (EXT_IRQ_COUNT + 16)
/** Highest priority for core interrupt. */
#define CORE_INTERRUPT_HIGHEST_PRIORITY 0
/** Default priority for core interrupt. */
#define CORE_INTERRUPT_DEFAULT_PRIORITY 5
/** Lowest priority for core interrupt. */
#define CORE_INTERRUPT_LOWEST_PRIORITY 7
// Compile time sanity check.
#if (CORE_NVIC_REG_WORDS > 3)
#error "em_core: Unexpected NVIC external interrupt count."
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*******************************************************************************
************************ MACRO API ***************************************
******************************************************************************/
//
// NVIC mask section macro API.
//
/** Allocate storage for NVIC interrupt masks for use by
* CORE_ENTER/EXIT_NVIC() macros. */
#define CORE_DECLARE_NVIC_STATE CORE_nvicMask_t nvicState
/** Allocate storage for NVIC interrupt masks.
* @param[in] x
* The storage variable name to use.*/
#define CORE_DECLARE_NVIC_MASK(x) CORE_nvicMask_t x
/** Allocate storage for and zero initialize NVIC interrupt mask.
* @param[in] x
* The storage variable name to use.*/
#define CORE_DECLARE_NVIC_ZEROMASK(x) CORE_nvicMask_t x = { { 0 } }
/** NVIC mask style interrupt disable.
* @param[in] mask
* Mask specifying which NVIC interrupts to disable. */
#define CORE_NVIC_DISABLE(mask) CORE_NvicDisableMask(mask)
/** NVIC mask style interrupt enable.
* @param[in] mask
* Mask specifying which NVIC interrupts to enable. */
#define CORE_NVIC_ENABLE(mask) CORE_NvicEnableMask(mask)
/** Convenience macro for implementing a NVIC mask section.
* @param[in] mask
* Mask specifying which NVIC interrupts to disable within the section.
* @param[in] yourcode
* The code for the section. */
#define CORE_NVIC_SECTION(mask, yourcode) \
{ \
CORE_DECLARE_NVIC_STATE; \
CORE_ENTER_NVIC(mask); \
{ \
yourcode \
} \
CORE_EXIT_NVIC(); \
}
/** Enter NVIC mask section. Assumes that a @ref CORE_DECLARE_NVIC_STATE exist
* in scope.
* @param[in] disable
* Mask specifying which NVIC interrupts to disable within the section. */
#define CORE_ENTER_NVIC(disable) CORE_EnterNvicMask(&nvicState, disable)
/** Exit NVIC mask section. Assumes that a @ref CORE_DECLARE_NVIC_STATE exist
* in scope. */
#define CORE_EXIT_NVIC() CORE_NvicEnableMask(&nvicState)
/** NVIC maks style yield.
* @param[in] enable
* Mask specifying which NVIC interrupts to briefly enable. */
#define CORE_YIELD_NVIC(enable) CORE_YieldNvicMask(enable)
/*******************************************************************************
************************* TYPEDEFS ****************************************
******************************************************************************/
/** Storage for NVIC interrupt masks. */
typedef struct {
uint32_t a[CORE_NVIC_REG_WORDS]; /*!< Array of NVIC mask words. */
} CORE_nvicMask_t;
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
bool CORE_IrqIsBlocked(IRQn_Type irqN) SL_DEPRECATED_API_SDK_2024_6;
void CORE_GetNvicEnabledMask(CORE_nvicMask_t *mask) SL_DEPRECATED_API_SDK_2024_6;
bool CORE_GetNvicMaskDisableState(const CORE_nvicMask_t *mask) SL_DEPRECATED_API_SDK_2024_6;
void CORE_EnterNvicMask(CORE_nvicMask_t *nvicState,
const CORE_nvicMask_t *disable) SL_DEPRECATED_API_SDK_2024_6;
void CORE_NvicDisableMask(const CORE_nvicMask_t *disable) SL_DEPRECATED_API_SDK_2024_6;
void CORE_NvicEnableMask(const CORE_nvicMask_t *enable) SL_DEPRECATED_API_SDK_2024_6;
void CORE_YieldNvicMask(const CORE_nvicMask_t *enable) SL_DEPRECATED_API_SDK_2024_6;
void CORE_NvicMaskSetIRQ(IRQn_Type irqN, CORE_nvicMask_t *mask) SL_DEPRECATED_API_SDK_2024_6;
void CORE_NvicMaskClearIRQ(IRQn_Type irqN, CORE_nvicMask_t *mask) SL_DEPRECATED_API_SDK_2024_6;
bool CORE_NvicIRQDisabled(IRQn_Type irqN) SL_DEPRECATED_API_SDK_2024_6;
void *CORE_GetNvicRamTableHandler(IRQn_Type irqN) SL_DEPRECATED_API_SDK_2024_6;
void CORE_SetNvicRamTableHandler(IRQn_Type irqN, void *handler) SL_DEPRECATED_API_SDK_2024_6;
void CORE_InitNvicVectorTable(uint32_t *sourceTable,
uint32_t sourceSize,
uint32_t *targetTable,
uint32_t targetSize,
void *defaultHandler,
bool overwriteActive);
#ifdef __cplusplus
}
#endif
/** @} (end addtogroup core) */
#endif /* EM_CORE_H */

View File

@@ -0,0 +1,36 @@
/***************************************************************************//**
* @file
* @brief Core interrupt handling API (Generic)
*******************************************************************************
* # License
* <b>Copyright 2023 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.
*
******************************************************************************/
#ifndef EM_CORE_GENERIC_H
#define EM_CORE_GENERIC_H
#include "sl_core.h"
#endif /* EM_CORE_GENERIC_H */

View File

@@ -0,0 +1,130 @@
/***************************************************************************//**
* @file
* @brief Debug (DBG) 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.
*
******************************************************************************/
#ifndef EM_DBG_H
#define EM_DBG_H
#include <stdbool.h>
#include "em_device.h"
#if defined(CoreDebug_DHCSR_C_DEBUGEN_Msk)
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup dbg
* @{
******************************************************************************/
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Lock modes */
typedef enum {
dbgLockModeAllowErase = 1UL, /**< Lock debug access. */
#if !defined(_SILICON_LABS_32B_SERIES_0)
dbgLockModePermanent = 2UL /**< Lock debug access permanently. */
#endif
} DBG_LockMode_TypeDef;
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
#if defined(GPIO_ROUTE_SWCLKPEN) \
|| defined(GPIO_ROUTEPEN_SWCLKTCKPEN) \
|| defined(GPIO_DBGROUTEPEN_SWCLKTCKPEN)
/***************************************************************************//**
* @brief
* Check if a debugger is connected (and debug session activated).
*
* @details
* Used to make run-time decisions depending on whether or not a debug session
* has been active since last reset, i.e., using a debug probe or similar. In
* some cases, special handling is required in that scenario.
*
* @return
* True if a debug session is active since last reset, otherwise false.
******************************************************************************/
__STATIC_INLINE bool DBG_Connected(void)
{
return (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) ? true : false;
}
#endif
#if defined(GPIO_ROUTE_SWOPEN) \
|| defined(GPIO_ROUTEPEN_SWVPEN) \
|| defined(GPIO_TRACEROUTEPEN_SWVPEN)
void DBG_SWOEnable(unsigned int location);
#endif
#if defined (EMU_CTRL_EM2DBGEN)
/***************************************************************************//**
* @brief
* Enable or disable debug support while in EM2 mode.
*
* @warning
* Disabling debug support in EM2 will reduce current consumption with 1-2 uA,
* but some debuggers will have problems regaining control over a device which
* is in EM2 and has debug support disabled.
*
* To remedy this, set the WSTK switch next to the battery holder to USB
* (powers down the EFR). Execute Simplicity Commander with command line
* parameters:
* "./commander.exe device recover"
* and then immediately move the switch to the AEM position. An additional
* "./commander.exe device masserase"
* command completes the recovery procedure.
*
* @param[in] enable
* Boolean true enables EM2 debug support, false disables.
******************************************************************************/
__STATIC_INLINE void DBG_EM2DebugEnable(bool enable)
{
if (enable) {
EMU->CTRL_SET = EMU_CTRL_EM2DBGEN;
} else {
EMU->CTRL_CLR = EMU_CTRL_EM2DBGEN;
}
}
#endif
/** @} (end addtogroup dbg) */
#ifdef __cplusplus
}
#endif
#endif /* defined( CoreDebug_DHCSR_C_DEBUGEN_Msk ) */
#endif /* EM_DBG_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
/***************************************************************************//**
* @file
* @brief EUSART Compatibility Header
*******************************************************************************
* # License
* <b>Copyright 2020 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.
*
******************************************************************************/
#ifndef EM_EUSART_COMPAT_H
#define EM_EUSART_COMPAT_H
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
#define EUSART_IF_TXCIF EUSART_IF_TXC
#define _EUSART_IF_TXCIF_SHIFT _EUSART_IF_TXC_SHIFT
#define _EUSART_IF_TXCIF_MASK _EUSART_IF_TXC_MASK
#define _EUSART_IF_TXCIF_DEFAULT _EUSART_IF_TXC_DEFAULT
#define EUSART_IF_TXCIF_DEFAULT EUSART_IF_TXC_DEFAULT
#define EUSART_IF_TXFLIF EUSART_IF_TXFL
#define _EUSART_IF_TXFLIF_SHIFT _EUSART_IF_TXFL_SHIFT
#define _EUSART_IF_TXFLIF_MASK _EUSART_IF_TXFL_MASK
#define _EUSART_IF_TXFLIF_DEFAULT _EUSART_IF_TXFL_DEFAULT
#define EUSART_IF_TXFLIF_DEFAULT EUSART_IF_TXFL_DEFAULT
#define EUSART_IF_RXFLIF EUSART_IF_RXFL
#define _EUSART_IF_RXFLIF_SHIFT _EUSART_IF_RXFL_SHIFT
#define _EUSART_IF_RXFLIF_MASK _EUSART_IF_RXFL_MASK
#define _EUSART_IF_RXFLIF_DEFAULT _EUSART_IF_RXFL_DEFAULT
#define EUSART_IF_RXFLIF_DEFAULT EUSART_IF_RXFL_DEFAULT
#define EUSART_IF_RXFULLIF EUSART_IF_RXFULL
#define _EUSART_IF_RXFULLIF_SHIFT _EUSART_IF_RXFULL_SHIFT
#define _EUSART_IF_RXFULLIF_MASK _EUSART_IF_RXFULL_MASK
#define _EUSART_IF_RXFULLIF_DEFAULT _EUSART_IF_RXFULL_DEFAULT
#define EUSART_IF_RXFULLIF_DEFAULT EUSART_IF_RXFULL_DEFAULT
#define EUSART_IF_RXOFIF EUSART_IF_RXOF
#define _EUSART_IF_RXOFIF_SHIFT _EUSART_IF_RXOF_SHIFT
#define _EUSART_IF_RXOFIF_MASK _EUSART_IF_RXOF_MASK
#define _EUSART_IF_RXOFIF_DEFAULT _EUSART_IF_RXOF_DEFAULT
#define EUSART_IF_RXOFIF_DEFAULT EUSART_IF_RXOF_DEFAULT
#define EUSART_IF_RXUFIF EUSART_IF_RXUF
#define _EUSART_IF_RXUFIF_SHIFT _EUSART_IF_RXUF_SHIFT
#define _EUSART_IF_RXUFIF_MASK _EUSART_IF_RXUF_MASK
#define _EUSART_IF_RXUFIF_DEFAULT _EUSART_IF_RXUF_DEFAULT
#define EUSART_IF_RXUFIF_DEFAULT EUSART_IF_RXUF_DEFAULT
#define EUSART_IF_TXOFIF EUSART_IF_TXOF
#define _EUSART_IF_TXOFIF_SHIFT _EUSART_IF_TXOF_SHIFT
#define _EUSART_IF_TXOFIF_MASK _EUSART_IF_TXOF_MASK
#define _EUSART_IF_TXOFIF_DEFAULT _EUSART_IF_TXOF_DEFAULT
#define EUSART_IF_TXOFIF_DEFAULT EUSART_IF_TXOF_DEFAULT
#define EUSART_IF_PERRIF EUSART_IF_PERR
#define _EUSART_IF_PERRIF_SHIFT _EUSART_IF_PERR_SHIFT
#define _EUSART_IF_PERRIF_MASK _EUSART_IF_PERR_MASK
#define _EUSART_IF_PERRIF_DEFAULT _EUSART_IF_PERR_DEFAULT
#define EUSART_IF_PERRIF_DEFAULT EUSART_IF_PERR_DEFAULT
#define EUSART_IF_FERRIF EUSART_IF_FERR
#define _EUSART_IF_FERRIF_SHIFT _EUSART_IF_FERR_SHIFT
#define _EUSART_IF_FERRIF_MASK _EUSART_IF_FERR_MASK
#define _EUSART_IF_FERRIF_DEFAULT _EUSART_IF_FERR_DEFAULT
#define EUSART_IF_FERRIF_DEFAULT EUSART_IF_FERR_DEFAULT
#define EUSART_IF_MPAFIF EUSART_IF_MPAF
#define _EUSART_IF_MPAFIF_SHIFT _EUSART_IF_MPAF_SHIFT
#define _EUSART_IF_MPAFIF_MASK _EUSART_IF_MPAF_MASK
#define _EUSART_IF_MPAFIF_DEFAULT _EUSART_IF_MPAF_DEFAULT
#define EUSART_IF_MPAFIF_DEFAULT EUSART_IF_MPAF_DEFAULT
#define EUSART_IF_CCFIF EUSART_IF_CCF
#define _EUSART_IF_CCFIF_SHIFT _EUSART_IF_CCF_SHIFT
#define _EUSART_IF_CCFIF_MASK _EUSART_IF_CCF_MASK
#define _EUSART_IF_CCFIF_DEFAULT _EUSART_IF_CCF_DEFAULT
#define EUSART_IF_CCFIF_DEFAULT EUSART_IF_CCF_DEFAULT
#define EUSART_IF_TXIDLEIF EUSART_IF_TXIDLE
#define _EUSART_IF_TXIDLEIF_SHIFT _EUSART_IF_TXIDLE_SHIFT
#define _EUSART_IF_TXIDLEIF_MASK _EUSART_IF_TXIDLE_MASK
#define _EUSART_IF_TXIDLEIF_DEFAULT _EUSART_IF_TXIDLE_DEFAULT
#define EUSART_IF_TXIDLEIF_DEFAULT EUSART_IF_TXIDLE_DEFAULT
#define EUSART_IF_STARTFIF EUSART_IF_STARTF
#define _EUSART_IF_STARTFIF_SHIFT _EUSART_IF_STARTF_SHIFT
#define _EUSART_IF_STARTFIF_MASK _EUSART_IF_STARTF_MASK
#define _EUSART_IF_STARTFIF_DEFAULT _EUSART_IF_STARTF_DEFAULT
#define EUSART_IF_STARTFIF_DEFAULT EUSART_IF_STARTF_DEFAULT
#define EUSART_IF_SIGFIF EUSART_IF_SIGF
#define _EUSART_IF_SIGFIF_SHIFT _EUSART_IF_SIGF_SHIFT
#define _EUSART_IF_SIGFIF_MASK _EUSART_IF_SIGF_MASK
#define _EUSART_IF_SIGFIF_DEFAULT _EUSART_IF_SIGF_DEFAULT
#define EUSART_IF_SIGFIF_DEFAULT EUSART_IF_SIGF_DEFAULT
#define EUSART_IF_AUTOBAUDDONEIF EUSART_IF_AUTOBAUDDONE
#define _EUSART_IF_AUTOBAUDDONEIF_SHIFT _EUSART_IF_AUTOBAUDDONE_SHIFT
#define _EUSART_IF_AUTOBAUDDONEIF_MASK _EUSART_IF_AUTOBAUDDONE_MASK
#define _EUSART_IF_AUTOBAUDDONEIF_DEFAULT _EUSART_IF_AUTOBAUDDONE_DEFAULT
#define EUSART_IF_AUTOBAUDDONEIF_DEFAULT EUSART_IF_AUTOBAUDDONE_DEFAULT
#define EUSART_IEN_TXCIEN EUSART_IEN_TXC
#define _EUSART_IEN_TXCIEN_SHIFT _EUSART_IEN_TXC_SHIFT
#define _EUSART_IEN_TXCIEN_MASK _EUSART_IEN_TXC_MASK
#define _EUSART_IEN_TXCIEN_DEFAULT _EUSART_IEN_TXC_DEFAULT
#define EUSART_IEN_TXCIEN_DEFAULT EUSART_IEN_TXC_DEFAULT
#define EUSART_IEN_TXFLIEN EUSART_IEN_TXFL
#define _EUSART_IEN_TXFLIEN_SHIFT _EUSART_IEN_TXFL_SHIFT
#define _EUSART_IEN_TXFLIEN_MASK _EUSART_IEN_TXFL_MASK
#define _EUSART_IEN_TXFLIEN_DEFAULT _EUSART_IEN_TXFL_DEFAULT
#define EUSART_IEN_TXFLIEN_DEFAULT EUSART_IEN_TXFL_DEFAULT
#define EUSART_IEN_RXFLIEN EUSART_IEN_RXFL
#define _EUSART_IEN_RXFLIEN_SHIFT _EUSART_IEN_RXFL_SHIFT
#define _EUSART_IEN_RXFLIEN_MASK _EUSART_IEN_RXFL_MASK
#define _EUSART_IEN_RXFLIEN_DEFAULT _EUSART_IEN_RXFL_DEFAULT
#define EUSART_IEN_RXFLIEN_DEFAULT EUSART_IEN_RXFL_DEFAULT
#define EUSART_IEN_RXFULLIEN EUSART_IEN_RXFULL
#define _EUSART_IEN_RXFULLIEN_SHIFT _EUSART_IEN_RXFULL_SHIFT
#define _EUSART_IEN_RXFULLIEN_MASK _EUSART_IEN_RXFULL_MASK
#define _EUSART_IEN_RXFULLIEN_DEFAULT _EUSART_IEN_RXFULL_DEFAULT
#define EUSART_IEN_RXFULLIEN_DEFAULT EUSART_IEN_RXFULL_DEFAULT
#define EUSART_IEN_RXOFIEN EUSART_IEN_RXOF
#define _EUSART_IEN_RXOFIEN_SHIFT _EUSART_IEN_RXOF_SHIFT
#define _EUSART_IEN_RXOFIEN_MASK _EUSART_IEN_RXOF_MASK
#define _EUSART_IEN_RXOFIEN_DEFAULT _EUSART_IEN_RXOF_DEFAULT
#define EUSART_IEN_RXOFIEN_DEFAULT EUSART_IEN_RXOF_DEFAULT
#define EUSART_IEN_RXUFIEN EUSART_IEN_RXUF
#define _EUSART_IEN_RXUFIEN_SHIFT _EUSART_IEN_RXUF_SHIFT
#define _EUSART_IEN_RXUFIEN_MASK _EUSART_IEN_RXUF_MASK
#define _EUSART_IEN_RXUFIEN_DEFAULT _EUSART_IEN_RXUF_DEFAULT
#define EUSART_IEN_RXUFIEN_DEFAULT EUSART_IEN_RXUF_DEFAULT
#define EUSART_IEN_TXOFIEN EUSART_IEN_TXOF
#define _EUSART_IEN_TXOFIEN_SHIFT _EUSART_IEN_TXOF_SHIFT
#define _EUSART_IEN_TXOFIEN_MASK _EUSART_IEN_TXOF_MASK
#define _EUSART_IEN_TXOFIEN_DEFAULT _EUSART_IEN_TXOF_DEFAULT
#define EUSART_IEN_TXOFIEN_DEFAULT EUSART_IEN_TXOF_DEFAULT
#define EUSART_IEN_PERRIEN EUSART_IEN_PERR
#define _EUSART_IEN_PERRIEN_SHIFT _EUSART_IEN_PERR_SHIFT
#define _EUSART_IEN_PERRIEN_MASK _EUSART_IEN_PERR_MASK
#define _EUSART_IEN_PERRIEN_DEFAULT _EUSART_IEN_PERR_DEFAULT
#define EUSART_IEN_PERRIEN_DEFAULT EUSART_IEN_PERR_DEFAULT
#define EUSART_IEN_FERRIEN EUSART_IEN_FERR
#define _EUSART_IEN_FERRIEN_SHIFT _EUSART_IEN_FERR_SHIFT
#define _EUSART_IEN_FERRIEN_MASK _EUSART_IEN_FERR_MASK
#define _EUSART_IEN_FERRIEN_DEFAULT _EUSART_IEN_FERR_DEFAULT
#define EUSART_IEN_FERRIEN_DEFAULT EUSART_IEN_FERR_DEFAULT
#define EUSART_IEN_MPAFIEN EUSART_IEN_MPAF
#define _EUSART_IEN_MPAFIEN_SHIFT _EUSART_IEN_MPAF_SHIFT
#define _EUSART_IEN_MPAFIEN_MASK _EUSART_IEN_MPAF_MASK
#define _EUSART_IEN_MPAFIEN_DEFAULT _EUSART_IEN_MPAF_DEFAULT
#define EUSART_IEN_MPAFIEN_DEFAULT EUSART_IEN_MPAF_DEFAULT
#define EUSART_IEN_CCFIEN EUSART_IEN_CCF
#define _EUSART_IEN_CCFIEN_SHIFT _EUSART_IEN_CCF_SHIFT
#define _EUSART_IEN_CCFIEN_MASK _EUSART_IEN_CCF_MASK
#define _EUSART_IEN_CCFIEN_DEFAULT _EUSART_IEN_CCF_DEFAULT
#define EUSART_IEN_CCFIEN_DEFAULT EUSART_IEN_CCF_DEFAULT
#define EUSART_IEN_TXIDLEIEN EUSART_IEN_TXIDLE
#define _EUSART_IEN_TXIDLEIEN_SHIFT _EUSART_IEN_TXIDLE_SHIFT
#define _EUSART_IEN_TXIDLEIEN_MASK _EUSART_IEN_TXIDLE_MASK
#define _EUSART_IEN_TXIDLEIEN_DEFAULT _EUSART_IEN_TXIDLE_DEFAULT
#define EUSART_IEN_TXIDLEIEN_DEFAULT EUSART_IEN_TXIDLE_DEFAULT
#define EUSART_IEN_STARTFIEN EUSART_IEN_STARTF
#define _EUSART_IEN_STARTFIEN_SHIFT _EUSART_IEN_STARTF_SHIFT
#define _EUSART_IEN_STARTFIEN_MASK _EUSART_IEN_STARTF_MASK
#define _EUSART_IEN_STARTFIEN_DEFAULT _EUSART_IEN_STARTF_DEFAULT
#define EUSART_IEN_STARTFIEN_DEFAULT EUSART_IEN_STARTF_DEFAULT
#define EUSART_IEN_SIGFIEN EUSART_IEN_SIGF
#define _EUSART_IEN_SIGFIEN_SHIFT _EUSART_IEN_SIGF_SHIFT
#define _EUSART_IEN_SIGFIEN_MASK _EUSART_IEN_SIGF_MASK
#define _EUSART_IEN_SIGFIEN_DEFAULT _EUSART_IEN_SIGF_DEFAULT
#define EUSART_IEN_SIGFIEN_DEFAULT EUSART_IEN_SIGF_DEFAULT
#define EUSART_IEN_AUTOBAUDDONEIEN EUSART_IEN_AUTOBAUDDONE
#define _EUSART_IEN_AUTOBAUDDONEIEN_SHIFT _EUSART_IEN_AUTOBAUDDONE_SHIFT
#define _EUSART_IEN_AUTOBAUDDONEIEN_MASK _EUSART_IEN_AUTOBAUDDONE_MASK
#define _EUSART_IEN_AUTOBAUDDONEIEN_DEFAULT _EUSART_IEN_AUTOBAUDDONE_DEFAULT
#define EUSART_IEN_AUTOBAUDDONEIEN_DEFAULT EUSART_IEN_AUTOBAUDDONE_DEFAULT
#endif // _SILICON_LABS_32B_SERIES_2_CONFIG_2
#endif

View File

@@ -0,0 +1,346 @@
/***************************************************************************//**
* @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.
*
******************************************************************************/
#ifndef EM_GPCRC_H
#define EM_GPCRC_H
#include "em_bus.h"
#include "em_device.h"
#if defined(GPCRC_PRESENT) && (GPCRC_COUNT > 0)
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup gpcrc GPCRC - General Purpose CRC
* @brief General Purpose Cyclic Redundancy Check (GPCRC) API
*
* @details
* The GPCRC API functions provide full support for the GPCRC peripheral.
*
* The GPCRC module is a peripheral that implements a Cyclic Redundancy Check
* (CRC) function. It supports a fixed 32-bit polynomial and a user
* configurable 16-bit polynomial. The fixed 32-bit polynomial is the commonly
* used IEEE 802.3 polynomial 0x04C11DB7.
*
* When using a 16-bit polynomial it is up to the user to choose a polynomial
* that fits the application. Commonly used 16-bit polynomials are 0x1021
* (CCITT-16), 0x3D65 (IEC16-MBus), and 0x8005 (ZigBee, 802.15.4, and USB).
* See this link for other polynomials:
* https://en.wikipedia.org/wiki/Cyclic_redundancy_check
*
* Before a CRC calculation can begin, call the
* @ref GPCRC_Start function. This function will reset CRC calculation
* by copying the configured initialization value over to the CRC data register.
*
* There are two ways of sending input data to the GPCRC. Either write
* the input data into the input data register using input functions
* @ref GPCRC_InputU32, @ref GPCRC_InputU16 and @ref GPCRC_InputU8, or the
* user can configure @ref ldma to transfer data directly to one of the GPCRC
* input data registers.
*
* <b> Examples of GPCRC usage: </b>
*
* A CRC-32 Calculation:
*
* @include em_gpcrc_crc32.c
*
* A CRC-16 Calculation:
*
* @include em_gpcrc_crc16.c
*
* A CRC-CCITT calculation:
*
* @include em_gpcrc_ccit.c
*
* @{
******************************************************************************/
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** CRC initialization structure. */
typedef struct {
/**
* CRC polynomial value. GPCRC supports either a fixed 32-bit polynomial
* or a user-configurable 16 bit polynomial. The fixed 32-bit polynomial
* is the one used in IEEE 802.3, which has the value 0x04C11DB7. To use the
* 32-bit fixed polynomial, assign 0x04C11DB7 to the crcPoly field.
* To use a 16-bit polynomial, assign a value to crcPoly where the upper 16
* bits are zero.
*
* The polynomial should be written in normal bit order. For instance,
* to use the CRC-16 polynomial X^16 + X^15 + X^2 + 1, first convert
* it to hex representation and remove the highest order term
* of the polynomial. This will give 0x8005 as the value to write into
* crcPoly.
*/
uint32_t crcPoly;
/**
* CRC initialization value. This value is assigned to the GPCRC_INIT register.
* The initValue is loaded into the data register when calling the
* @ref GPCRC_Start function or when one of the data registers are read
* while @ref autoInit is enabled.
*/
uint32_t initValue;
/**
* Reverse byte order. This has an effect when sending a 32-bit word or
* 16-bit half word input to the CRC calculation. When set to true, the input
* bytes are reversed before entering the CRC calculation. When set to
* false, the input bytes stay in the same order.
*/
bool reverseByteOrder;
/**
* Reverse bits within each input byte. This setting enables or disables byte
* level bit reversal. When byte-level bit reversal is enabled, then each byte
* of input data will be reversed before entering CRC calculation.
*/
bool reverseBits;
/**
* Enable/disable byte mode. When byte mode is enabled, then all input
* is treated as single byte input even though the input is a 32-bit word
* or a 16-bit half word. Only the least significant byte of the data-word
* will be used for CRC calculation for all writes.
*/
bool enableByteMode;
/**
* Enable automatic initialization by re-seeding the CRC result based on
* the init value after reading one of the CRC data registers.
*/
bool autoInit;
/** Enable/disable GPCRC when initialization is completed. */
bool enable;
} GPCRC_Init_TypeDef;
/** Default configuration for GPCRC_Init_TypeDef structure. */
#define GPCRC_INIT_DEFAULT \
{ \
0x04C11DB7UL, /* CRC32 Polynomial value. */ \
0x00000000UL, /* Initialization value. */ \
false, /* Byte order is normal. */ \
false, /* Bit order is not reversed on output. */ \
false, /* Disable byte mode. */ \
false, /* Disable automatic initialization on data read. */ \
true, /* Enable GPCRC. */ \
}
/*******************************************************************************
****************************** PROTOTYPES *********************************
******************************************************************************/
void GPCRC_Init(GPCRC_TypeDef * gpcrc, const GPCRC_Init_TypeDef * init);
void GPCRC_Reset(GPCRC_TypeDef * gpcrc);
/***************************************************************************//**
* @brief
* Enable/disable GPCRC.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @param[in] enable
* True to enable GPCRC, false to disable.
******************************************************************************/
__STATIC_INLINE void GPCRC_Enable(GPCRC_TypeDef * gpcrc, bool enable)
{
#if defined(GPCRC_EN_EN)
BUS_RegBitWrite(&gpcrc->EN, _GPCRC_EN_EN_SHIFT, enable);
#else
BUS_RegBitWrite(&gpcrc->CTRL, _GPCRC_CTRL_EN_SHIFT, enable);
#endif
}
/***************************************************************************//**
* @brief
* Issue a command to initialize the CRC calculation.
*
* @details
* Issues the command INIT in GPCRC_CMD that initializes the
* CRC calculation by writing the initial values to the DATA register.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
******************************************************************************/
__STATIC_INLINE void GPCRC_Start(GPCRC_TypeDef * gpcrc)
{
gpcrc->CMD = GPCRC_CMD_INIT;
}
/***************************************************************************//**
* @brief
* Set the initialization value of the CRC.
*
* @param [in] initValue
* Value to use to initialize a CRC calculation. This value is moved into
* the data register when calling @ref GPCRC_Start
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
******************************************************************************/
__STATIC_INLINE void GPCRC_InitValueSet(GPCRC_TypeDef * gpcrc, uint32_t initValue)
{
gpcrc->INIT = initValue;
}
/***************************************************************************//**
* @brief
* Write a 32-bit value to the input data register of the CRC.
*
* @details
* Use this function to write a 32-bit input data to the CRC. CRC
* calculation is based on the provided input data using the configured
* CRC polynomial.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @param[in] data
* Data to be written to the input data register.
******************************************************************************/
__STATIC_INLINE void GPCRC_InputU32(GPCRC_TypeDef * gpcrc, uint32_t data)
{
gpcrc->INPUTDATA = data;
}
/***************************************************************************//**
* @brief
* Write a 16-bit value to the input data register of the CRC.
*
* @details
* Use this function to write a 16 bit input data to the CRC. CRC
* calculation is based on the provided input data using the configured
* CRC polynomial.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @param[in] data
* Data to be written to the input data register.
******************************************************************************/
__STATIC_INLINE void GPCRC_InputU16(GPCRC_TypeDef * gpcrc, uint16_t data)
{
gpcrc->INPUTDATAHWORD = data;
}
/***************************************************************************//**
* @brief
* Write an 8-bit value to the CRC input data register.
*
* @details
* Use this function to write an 8-bit input data to the CRC. CRC
* calculation is based on the provided input data using the configured
* CRC polynomial.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @param[in] data
* Data to be written to the input data register.
******************************************************************************/
__STATIC_INLINE void GPCRC_InputU8(GPCRC_TypeDef * gpcrc, uint8_t data)
{
gpcrc->INPUTDATABYTE = data;
}
/***************************************************************************//**
* @brief
* Read the CRC data register.
*
* @details
* Use this function to read the calculated CRC value.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @return
* Content of the CRC data register.
******************************************************************************/
__STATIC_INLINE uint32_t GPCRC_DataRead(GPCRC_TypeDef * gpcrc)
{
return gpcrc->DATA;
}
/***************************************************************************//**
* @brief
* Read the data register of the CRC bit reversed.
*
* @details
* Use this function to read the calculated CRC value bit reversed. When
* using a 32-bit polynomial, bits [31:0] are reversed, when using a
* 16-bit polynomial, bits [15:0] are reversed.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @return
* Content of the CRC data register bit reversed.
******************************************************************************/
__STATIC_INLINE uint32_t GPCRC_DataReadBitReversed(GPCRC_TypeDef * gpcrc)
{
return gpcrc->DATAREV;
}
/***************************************************************************//**
* @brief
* Read the data register of the CRC byte reversed.
*
* @details
* Use this function to read the calculated CRC value byte reversed.
*
* @param[in] gpcrc
* Pointer to GPCRC peripheral register block.
*
* @return
* Content of the CRC data register byte reversed.
******************************************************************************/
__STATIC_INLINE uint32_t GPCRC_DataReadByteReversed(GPCRC_TypeDef * gpcrc)
{
return gpcrc->DATABYTEREV;
}
/** @} (end addtogroup gpcrc) */
#ifdef __cplusplus
}
#endif
#endif /* defined(GPCRC_COUNT) && (GPCRC_COUNT > 0) */
#endif /* EM_GPCRC_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,525 @@
/***************************************************************************//**
* @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.
*
******************************************************************************/
#ifndef EM_I2C_H
#define EM_I2C_H
#include "em_device.h"
#if defined(I2C_COUNT) && (I2C_COUNT > 0)
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup i2c
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/**
* @brief
* Standard mode max frequency assuming using 4:4 ratio for Nlow:Nhigh.
* @details
* From I2C specification: Min Tlow = 4.7us, min Thigh = 4.0us,
* max Trise=1.0us, max Tfall=0.3us. Since ratio is 4:4, have to use
* worst case value of Tlow or Thigh as base.
*
* 1/(Tlow + Thigh + 1us + 0.3us) = 1/(4.7 + 4.7 + 1.3)us = 93458Hz
* @note
* Due to chip characteristics, max value is somewhat reduced.
*/
#if defined(_SILICON_LABS_32B_SERIES_0) \
&& (defined(_EFM32_GECKO_FAMILY) \
|| defined(_EFM32_TINY_FAMILY) \
|| defined(_EFM32_ZERO_FAMILY) \
|| defined(_EFM32_HAPPY_FAMILY))
#define I2C_FREQ_STANDARD_MAX 93000
#elif defined(_SILICON_LABS_32B_SERIES_0) \
&& (defined(_EFM32_GIANT_FAMILY) \
|| defined(_EFM32_WONDER_FAMILY))
#define I2C_FREQ_STANDARD_MAX 92000
#elif defined(_SILICON_LABS_32B_SERIES_1)
// None of the chips on this platform has been characterized on this parameter.
// Use same value as on Wonder until further notice.
#define I2C_FREQ_STANDARD_MAX 92000
#elif defined(_SILICON_LABS_32B_SERIES_2)
#define I2C_FREQ_STANDARD_MAX 100000
#else
#error "Unknown device family."
#endif
/**
* @brief
* Fast mode max frequency assuming using 6:3 ratio for Nlow:Nhigh.
* @details
* From I2C specification: Min Tlow = 1.3us, min Thigh = 0.6us,
* max Trise=0.3us, max Tfall=0.3us. Since ratio is 6:3, have to use
* worst case value of Tlow or 2xThigh as base.
*
* 1/(Tlow + Thigh + 0.3us + 0.3us) = 1/(1.3 + 0.65 + 0.6)us = 392157Hz
*/
#define I2C_FREQ_FAST_MAX 392157
/**
* @brief
* Fast mode+ max frequency assuming using 11:6 ratio for Nlow:Nhigh.
* @details
* From I2C specification: Min Tlow = 0.5us, min Thigh = 0.26us,
* max Trise=0.12us, max Tfall=0.12us. Since ratio is 11:6, have to use
* worst case value of Tlow or (11/6)xThigh as base.
*
* 1/(Tlow + Thigh + 0.12us + 0.12us) = 1/(0.5 + 0.273 + 0.24)us = 987167Hz
*/
#define I2C_FREQ_FASTPLUS_MAX 987167
/**
* @brief
* Indicate plain write sequence: S+ADDR(W)+DATA0+P.
* @details
* @li S - Start
* @li ADDR(W) - address with W/R bit cleared
* @li DATA0 - Data taken from buffer with index 0
* @li P - Stop
*/
#define I2C_FLAG_WRITE 0x0001
/**
* @brief
* Indicate plain read sequence: S+ADDR(R)+DATA0+P.
* @details
* @li S - Start
* @li ADDR(R) - Address with W/R bit set
* @li DATA0 - Data read into buffer with index 0
* @li P - Stop
*/
#define I2C_FLAG_READ 0x0002
/**
* @brief
* Indicate combined write/read sequence: S+ADDR(W)+DATA0+Sr+ADDR(R)+DATA1+P.
* @details
* @li S - Start
* @li Sr - Repeated start
* @li ADDR(W) - Address with W/R bit cleared
* @li ADDR(R) - Address with W/R bit set
* @li DATAn - Data written from/read into buffer with index n
* @li P - Stop
*/
#define I2C_FLAG_WRITE_READ 0x0004
/**
* @brief
* Indicate write sequence using two buffers: S+ADDR(W)+DATA0+DATA1+P.
* @details
* @li S - Start
* @li ADDR(W) - Address with W/R bit cleared
* @li DATAn - Data written from buffer with index n
* @li P - Stop
*/
#define I2C_FLAG_WRITE_WRITE 0x0008
/** Use 10 bit address. */
#define I2C_FLAG_10BIT_ADDR 0x0010
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Clock low to high ratio settings. */
typedef enum {
i2cClockHLRStandard = _I2C_CTRL_CLHR_STANDARD, /**< Ratio is 4:4 */
i2cClockHLRAsymetric = _I2C_CTRL_CLHR_ASYMMETRIC, /**< Ratio is 6:3 */
i2cClockHLRFast = _I2C_CTRL_CLHR_FAST /**< Ratio is 11:3 */
} I2C_ClockHLR_TypeDef;
/** Return codes for single Controller mode transfer function. */
typedef enum {
/* In progress code (>0) */
i2cTransferInProgress = 1, /**< Transfer in progress. */
/* Complete code (=0) */
i2cTransferDone = 0, /**< Transfer completed successfully. */
/* Transfer error codes (<0). */
i2cTransferNack = -1, /**< NACK received during transfer. */
i2cTransferBusErr = -2, /**< Bus error during transfer (misplaced START/STOP). */
i2cTransferArbLost = -3, /**< Arbitration lost during transfer. */
i2cTransferUsageFault = -4, /**< Usage fault. */
i2cTransferSwFault = -5 /**< SW fault. */
} I2C_TransferReturn_TypeDef;
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** I2C initialization structure. */
typedef struct {
/** Enable I2C peripheral when initialization completed. */
bool enable;
/** Set to Controller (true) or Target (false) mode */
bool master;
/**
* I2C reference clock assumed when configuring bus frequency setup.
* Set it to 0 if currently configured reference clock will be used
* This parameter is only applicable if operating in Controller mode.
*/
uint32_t refFreq;
/**
* (Max) I2C bus frequency to use. This parameter is only applicable
* if operating in Controller mode.
*/
uint32_t freq;
/** Clock low/high ratio control. */
I2C_ClockHLR_TypeDef clhr;
} I2C_Init_TypeDef;
/** Suggested default configuration for I2C initialization structure. */
#define I2C_INIT_DEFAULT \
{ \
true, /* Enable when initialization done. */ \
true, /* Set to Controller mode. */ \
0, /* Use currently configured reference clock. */ \
I2C_FREQ_STANDARD_MAX, /* Set to standard rate assuring being */ \
/* within I2C specification. */ \
i2cClockHLRStandard /* Set to use 4:4 low/high duty cycle. */ \
}
/**
* @brief
* Master mode transfer message structure used to define a complete
* I2C transfer sequence (from start to stop).
* @details
* The structure allows for defining the following types of sequences
* (refer to defines for sequence details):
* @li #I2C_FLAG_READ - Data read into buf[0].data
* @li #I2C_FLAG_WRITE - Data written from buf[0].data
* @li #I2C_FLAG_WRITE_READ - Data written from buf[0].data and read
* into buf[1].data
* @li #I2C_FLAG_WRITE_WRITE - Data written from buf[0].data and
* buf[1].data
*/
typedef struct {
/**
* @brief
* Address to use after (repeated) start.
* @details
* Layout details, A = Address bit, X = don't care bit (set to 0):
* @li 7 bit address - Use format AAAA AAAX
* @li 10 bit address - Use format XXXX XAAX AAAA AAAA
*/
uint16_t addr;
/** Flags defining sequence type and details, see I2C_FLAG_ defines. */
uint16_t flags;
/**
* Buffers used to hold data to send from or receive into, depending
* on sequence type.
*/
struct {
/** Buffer used for data to transmit/receive, must be @p len long. */
uint8_t *data;
/**
* Number of bytes in @p data to send or receive. Notice that when
* receiving data to this buffer, at least 1 byte must be received.
* Setting @p len to 0 in the receive case is considered a usage fault.
* Transmitting 0 bytes is legal, in which case only the address
* is transmitted after the start condition.
*/
uint16_t len;
} buf[2];
} I2C_TransferSeq_TypeDef;
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c);
void I2C_BusFreqSet(I2C_TypeDef *i2c,
uint32_t freqRef,
uint32_t freqScl,
I2C_ClockHLR_TypeDef i2cMode);
void I2C_Enable(I2C_TypeDef *i2c, bool enable);
void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init);
/***************************************************************************//**
* @brief
* Clear one or more pending I2C interrupts.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @param[in] flags
* Pending I2C interrupt source to clear. Use a bitwise logic OR combination of
* valid interrupt flags for the I2C module (I2C_IF_nnn).
******************************************************************************/
__STATIC_INLINE void I2C_IntClear(I2C_TypeDef *i2c, uint32_t flags)
{
#if defined (I2C_HAS_SET_CLEAR)
i2c->IF_CLR = flags;
#else
i2c->IFC = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more I2C interrupts.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @param[in] flags
* I2C interrupt sources to disable. Use a bitwise logic OR combination of
* valid interrupt flags for the I2C module (I2C_IF_nnn).
******************************************************************************/
__STATIC_INLINE void I2C_IntDisable(I2C_TypeDef *i2c, uint32_t flags)
{
#if defined (I2C_HAS_SET_CLEAR)
i2c->IEN_CLR = flags;
#else
i2c->IEN &= ~(flags);
#endif
}
/***************************************************************************//**
* @brief
* Enable one or more I2C interrupts.
*
* @note
* Depending on the use, a pending interrupt may already be set prior to
* enabling the interrupt. To ignore a pending interrupt, consider using
* I2C_IntClear() prior to enabling the interrupt.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @param[in] flags
* I2C interrupt sources to enable. Use a bitwise logic OR combination of
* valid interrupt flags for the I2C module (I2C_IF_nnn).
******************************************************************************/
__STATIC_INLINE void I2C_IntEnable(I2C_TypeDef *i2c, uint32_t flags)
{
#if defined (I2C_HAS_SET_CLEAR)
i2c->IEN_SET = flags;
#else
i2c->IEN |= flags;
#endif
}
/***************************************************************************//**
* @brief
* Get pending I2C interrupt flags.
*
* @note
* Event bits are not cleared by the use of this function.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @return
* I2C interrupt sources pending. A bitwise logic OR combination of valid
* interrupt flags for the I2C module (I2C_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t I2C_IntGet(I2C_TypeDef *i2c)
{
return i2c->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending I2C interrupt flags.
* Useful for handling more interrupt sources in the same interrupt handler.
*
* @note
* Interrupt flags are not cleared by the use of this function.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @return
* Pending and enabled I2C interrupt sources
* Return value is the bitwise AND of
* - the enabled interrupt sources in I2Cn_IEN and
* - the pending interrupt flags I2Cn_IF
******************************************************************************/
__STATIC_INLINE uint32_t I2C_IntGetEnabled(I2C_TypeDef *i2c)
{
uint32_t ien;
ien = i2c->IEN;
return i2c->IF & ien;
}
/***************************************************************************//**
* @brief
* Set one or more pending I2C interrupts from SW.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @param[in] flags
* I2C interrupt sources to set to pending. Use a bitwise logic OR combination
* of valid interrupt flags for the I2C module (I2C_IF_nnn).
******************************************************************************/
__STATIC_INLINE void I2C_IntSet(I2C_TypeDef *i2c, uint32_t flags)
{
#if defined (I2C_HAS_SET_CLEAR)
i2c->IF_SET = flags;
#else
i2c->IFS = flags;
#endif
}
void I2C_Reset(I2C_TypeDef *i2c);
/***************************************************************************//**
* @brief
* Get Target address used for I2C peripheral (when operating in Target mode).
*
* @details
* For 10-bit addressing mode, the address is split in two bytes, and only
* the first byte setting is fetched, effectively only controlling the 2 most
* significant bits of the 10-bit address. Full handling of 10-bit addressing
* in Target mode requires additional SW handling.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @return
* I2C Target address in use. The 7 most significant bits define the actual
* address, the least significant bit is reserved and always returned as 0.
******************************************************************************/
__STATIC_INLINE uint8_t I2C_SlaveAddressGet(I2C_TypeDef *i2c)
{
return ((uint8_t)(i2c->SADDR));
}
/***************************************************************************//**
* @brief
* Set Target address to use for I2C peripheral (when operating in Target mode).
*
* @details
* For 10- bit addressing mode, the address is split in two bytes, and only
* the first byte is set, effectively only controlling the 2 most significant
* bits of the 10-bit address. Full handling of 10-bit addressing in Target
* mode requires additional SW handling.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @param[in] addr
* I2C Target address to use. The 7 most significant bits define the actual
* address, the least significant bit is reserved and always set to 0.
******************************************************************************/
__STATIC_INLINE void I2C_SlaveAddressSet(I2C_TypeDef *i2c, uint8_t addr)
{
i2c->SADDR = (uint32_t)addr & 0xfe;
}
/***************************************************************************//**
* @brief
* Get Target address mask used for I2C peripheral (when operating in Target
* mode).
*
* @details
* The address mask defines how the comparator works. A bit position with
* value 0 means that the corresponding Target address bit is ignored during
* comparison (don't care). A bit position with value 1 means that the
* corresponding Target address bit must match.
*
* For 10-bit addressing mode, the address is split in two bytes, and only
* the mask for the first address byte is fetched, effectively only
* controlling the 2 most significant bits of the 10-bit address.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @return
* I2C Target address mask in use. The 7 most significant bits define the
* actual address mask, the least significant bit is reserved and always
* returned as 0.
******************************************************************************/
__STATIC_INLINE uint8_t I2C_SlaveAddressMaskGet(I2C_TypeDef *i2c)
{
return ((uint8_t)(i2c->SADDRMASK));
}
/***************************************************************************//**
* @brief
* Set Target address mask used for I2C peripheral (when operating in Target
* mode).
*
* @details
* The address mask defines how the comparator works. A bit position with
* value 0 means that the corresponding Target address bit is ignored during
* comparison (don't care). A bit position with value 1 means that the
* corresponding Target address bit must match.
*
* For 10-bit addressing mode, the address is split in two bytes, and only
* the mask for the first address byte is set, effectively only controlling
* the 2 most significant bits of the 10-bit address.
*
* @param[in] i2c
* Pointer to I2C peripheral register block.
*
* @param[in] mask
* I2C Target address mask to use. The 7 most significant bits define the
* actual address mask, the least significant bit is reserved and should
* be 0.
******************************************************************************/
__STATIC_INLINE void I2C_SlaveAddressMaskSet(I2C_TypeDef *i2c, uint8_t mask)
{
i2c->SADDRMASK = (uint32_t)mask & 0xfe;
}
I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c);
I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
I2C_TransferSeq_TypeDef *seq);
/** @} (end addtogroup i2c) */
#ifdef __cplusplus
}
#endif
#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */
#endif /* EM_I2C_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
/***************************************************************************//**
* @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.
*
******************************************************************************/
#ifndef EM_LETIMER_H
#define EM_LETIMER_H
#include <stdbool.h>
#include "em_device.h"
#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0)
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup letimer
* @{
******************************************************************************/
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Repeat mode. */
typedef enum {
/** Count until stopped by SW. */
letimerRepeatFree = _LETIMER_CTRL_REPMODE_FREE,
/** Count REP0 times. */
letimerRepeatOneshot = _LETIMER_CTRL_REPMODE_ONESHOT,
/**
* Count REP0 times, if REP1 has been written to, it is loaded into
* REP0 when REP0 is about to be decremented to 0.
*/
letimerRepeatBuffered = _LETIMER_CTRL_REPMODE_BUFFERED,
/**
* Run as long as both REP0 and REP1 are not 0. Both REP0 and REP1
* are decremented when counter underflows.
*/
letimerRepeatDouble = _LETIMER_CTRL_REPMODE_DOUBLE
} LETIMER_RepeatMode_TypeDef;
/** Underflow action on output. */
typedef enum {
/** No output action. */
letimerUFOANone = _LETIMER_CTRL_UFOA0_NONE,
/** Toggle output when counter underflows. */
letimerUFOAToggle = _LETIMER_CTRL_UFOA0_TOGGLE,
/** Hold output one LETIMER clock cycle when counter underflows. */
letimerUFOAPulse = _LETIMER_CTRL_UFOA0_PULSE,
/** Set output idle when counter underflows, and active when matching COMP1. */
letimerUFOAPwm = _LETIMER_CTRL_UFOA0_PWM
} LETIMER_UFOA_TypeDef;
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** LETIMER initialization structure. */
typedef struct {
bool enable; /**< Start counting when initialization completes. */
bool debugRun; /**< Counter shall keep running during debug halt. */
#if defined(LETIMER_CTRL_RTCC0TEN)
bool rtcComp0Enable; /**< Start counting on RTC COMP0 match. */
bool rtcComp1Enable; /**< Start counting on RTC COMP1 match. */
#endif
bool comp0Top; /**< Load COMP0 register into CNT when counter underflows. */
bool bufTop; /**< Load COMP1 into COMP0 when REP0 reaches 0. */
uint8_t out0Pol; /**< Idle value for output 0. */
uint8_t out1Pol; /**< Idle value for output 1. */
LETIMER_UFOA_TypeDef ufoa0; /**< Underflow output 0 action. */
LETIMER_UFOA_TypeDef ufoa1; /**< Underflow output 1 action. */
LETIMER_RepeatMode_TypeDef repMode; /**< Repeat mode. */
uint32_t topValue; /**< Top value. Counter wraps when top value matches counter value is reached. */
} LETIMER_Init_TypeDef;
/** Default configuration for LETIMER initialization structure. */
#if defined(LETIMER_CTRL_RTCC0TEN)
#define LETIMER_INIT_DEFAULT \
{ \
true, /* Enable timer when initialization completes. */ \
false, /* Stop counter during debug halt. */ \
false, /* Do not start counting on RTC COMP0 match. */ \
false, /* Do not start counting on RTC COMP1 match. */ \
false, /* Do not load COMP0 into CNT on underflow. */ \
false, /* Do not load COMP1 into COMP0 when REP0 reaches 0. */ \
0, /* Idle value 0 for output 0. */ \
0, /* Idle value 0 for output 1. */ \
letimerUFOANone, /* No action on underflow on output 0. */ \
letimerUFOANone, /* No action on underflow on output 1. */ \
letimerRepeatFree, /* Count until stopped by SW. */ \
0 /* Use default top Value. */ \
}
#else
#define LETIMER_INIT_DEFAULT \
{ \
true, /* Enable timer when initialization completes. */ \
false, /* Stop counter during debug halt. */ \
false, /* Do not load COMP0 into CNT on underflow. */ \
false, /* Do not load COMP1 into COMP0 when REP0 reaches 0. */ \
0, /* Idle value 0 for output 0. */ \
0, /* Idle value 0 for output 1. */ \
letimerUFOANone, /* No action on underflow on output 0. */ \
letimerUFOANone, /* No action on underflow on output 1. */ \
letimerRepeatFree, /* Count until stopped by SW. */ \
0 /* Use default top Value. */ \
}
#endif
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp);
void LETIMER_CompareSet(LETIMER_TypeDef *letimer,
unsigned int comp,
uint32_t value);
uint32_t LETIMER_CounterGet(LETIMER_TypeDef *letimer);
#if !defined(_EFM32_GECKO_FAMILY)
void LETIMER_CounterSet(LETIMER_TypeDef *letimer, uint32_t value);
#endif
void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable);
#if defined(_LETIMER_FREEZE_MASK)
void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable);
#endif
void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init);
/***************************************************************************//**
* @brief
* Clear one or more pending LETIMER interrupts.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
*
* @param[in] flags
* Pending LETIMER interrupt source to clear. Use a bitwise logic OR
* combination of valid interrupt flags for the LETIMER module
* (LETIMER_IF_nnn).
******************************************************************************/
__STATIC_INLINE void LETIMER_IntClear(LETIMER_TypeDef *letimer, uint32_t flags)
{
#if defined (LETIMER_HAS_SET_CLEAR)
letimer->IF_CLR = flags;
#else
letimer->IFC = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more LETIMER interrupts.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
*
* @param[in] flags
* LETIMER interrupt sources to disable. Use a bitwise logic OR combination of
* valid interrupt flags for the LETIMER module (LETIMER_IF_nnn).
******************************************************************************/
__STATIC_INLINE void LETIMER_IntDisable(LETIMER_TypeDef *letimer, uint32_t flags)
{
letimer->IEN &= ~flags;
}
/***************************************************************************//**
* @brief
* Enable one or more LETIMER interrupts.
*
* @note
* Depending on the use, a pending interrupt may already be set prior to
* enabling the interrupt. To ignore a pending interrupt, consider using
* LETIMER_IntClear() prior to enabling the interrupt.
*
* @param[in] letimer
* Pointer to the LETIMER peripheral register block.
*
* @param[in] flags
* LETIMER interrupt sources to enable. Use a bitwise logic OR combination of
* valid interrupt flags for the LETIMER module (LETIMER_IF_nnn).
******************************************************************************/
__STATIC_INLINE void LETIMER_IntEnable(LETIMER_TypeDef *letimer, uint32_t flags)
{
letimer->IEN |= flags;
}
/***************************************************************************//**
* @brief
* Get pending LETIMER interrupt flags.
*
* @note
* Event bits are not cleared by the use of this function.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
*
* @return
* LETIMER interrupt sources pending. A bitwise logic OR combination of
* valid interrupt flags for the LETIMER module (LETIMER_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t LETIMER_IntGet(LETIMER_TypeDef *letimer)
{
return letimer->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending LETIMER interrupt flags.
*
* @details
* Useful for handling more interrupt sources in the same interrupt handler.
*
* @note
* Event bits are not cleared by the use of this function.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
*
* @return
* Pending and enabled LETIMER interrupt sources.
* Return value is the bitwise AND combination of
* - the OR combination of enabled interrupt sources in LETIMER_IEN_nnn
* register (LETIMER_IEN_nnn) and
* - the OR combination of valid interrupt flags of the LETIMER module
* (LETIMER_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t LETIMER_IntGetEnabled(LETIMER_TypeDef *letimer)
{
uint32_t ien;
/* Store flags in temporary variable in order to define explicit order
* of volatile accesses. */
ien = letimer->IEN;
/* Bitwise AND of pending and enabled interrupts */
return letimer->IF & ien;
}
/***************************************************************************//**
* @brief
* Set one or more pending LETIMER interrupts from SW.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
*
* @param[in] flags
* LETIMER interrupt sources to set to pending. Use a bitwise logic OR
* combination of valid interrupt flags for the LETIMER module (LETIMER_IF_nnn).
******************************************************************************/
__STATIC_INLINE void LETIMER_IntSet(LETIMER_TypeDef *letimer, uint32_t flags)
{
#if defined (LETIMER_HAS_SET_CLEAR)
letimer->IF_SET = flags;
#else
letimer->IFS = flags;
#endif
}
#if defined(_LETIMER_LOCK_MASK)
/***************************************************************************//**
* @brief
* Lock LETIMER registers.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
*
* @note When LETIMER registers are locked LETIMER_EN, LETIMER_SWRST,
* LETIMER_CTRL, LETIMER_CMD, LETIMER_CNT, LETIMER_COMPx,
* LETIMER_TOP, LETIMER_TOPBUFF, LETIMER_REPx, and PRSMODE registers
* cannot be written to.
******************************************************************************/
__STATIC_INLINE void LETIMER_Lock(LETIMER_TypeDef *letimer)
{
letimer->LOCK = ~LETIMER_LOCK_LETIMERLOCKKEY_UNLOCK;
}
#endif
#if defined(_LETIMER_LOCK_MASK)
/***************************************************************************//**
* @brief
* Unlock LETIMER registers.
*
* @param[in] letimer
* Pointer to LETIMER peripheral register block.
******************************************************************************/
__STATIC_INLINE void LETIMER_Unlock(LETIMER_TypeDef *letimer)
{
letimer->LOCK = LETIMER_LOCK_LETIMERLOCKKEY_UNLOCK;
}
#endif
uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep);
void LETIMER_RepeatSet(LETIMER_TypeDef *letimer,
unsigned int rep,
uint32_t value);
void LETIMER_Reset(LETIMER_TypeDef *letimer);
void LETIMER_SyncWait(LETIMER_TypeDef *letimer);
void LETIMER_TopSet(LETIMER_TypeDef *letimer, uint32_t value);
uint32_t LETIMER_TopGet(LETIMER_TypeDef *letimer);
/** @} (end addtogroup letimer) */
#ifdef __cplusplus
}
#endif
#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */
#endif /* EM_LETIMER_H */

View File

@@ -0,0 +1,889 @@
/***************************************************************************//**
* @file
* @brief Flash Controller (MSC) 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.
*
******************************************************************************/
#ifndef EM_MSC_H
#define EM_MSC_H
#include "em_device.h"
#if defined(MSC_COUNT) && (MSC_COUNT > 0)
#include <stdint.h>
#include <stdbool.h>
#include "em_bus.h"
#include "em_msc_compat.h"
#include "em_ramfunc.h"
#include "sl_assert.h"
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
#include "sli_tz_ns_interface.h"
#include "sli_tz_service_msc.h"
#include "sli_tz_s_interface.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup msc MSC - Memory System Controller
* @brief Memory System Controller API
* @details
* Contains functions to control the MSC, primarily the Flash.
* Users can perform Flash memory write and erase operations, as well as
* optimization of the CPU instruction fetch interface for the application.
* Available instruction fetch features depends on the MCU or SoC family, but
* features such as instruction pre-fetch, cache, and configurable branch prediction
* are typically available.
*
* @note Flash wait-state configuration is handled by @ref cmu.
* When core clock configuration is changed by a call to functions such as
* CMU_ClockSelectSet() or CMU_HFRCOBandSet(), then Flash wait-state
* configuration is also updated.
*
* MSC resets into a safe state. To initialize the instruction interface
* to recommended settings:
* @include em_msc_init_exec.c
*
* @note The optimal configuration is highly application dependent. Performance
* benchmarking is supported by most families. See MSC_StartCacheMeasurement()
* and MSC_GetCacheMeasurement() for more details.
*
* @note
* The flash write and erase runs from RAM on the EFM32G devices. On all other
* devices the flash write and erase functions run from flash.
*
* @note
* Flash erase may add ms of delay to interrupt latency if executing from Flash.
*
* Flash write and erase operations are supported by @ref MSC_WriteWord(),
* @ref MSC_ErasePage(), and MSC_MassErase().
* Mass erase is supported for MCU and SoC families with larger Flash sizes.
*
* @note
* @ref MSC_Init() must be called prior to any Flash write or erase operation.
*
* The following steps are necessary to perform a page erase and write:
* @include em_msc_erase_write.c
*
* @deprecated
* The configuration called EM_MSC_RUN_FROM_FLASH is deprecated. This was
* previously used for allocating the flash write functions in either flash
* or RAM.
*
* @note
* The configuration EM_MSC_RUN_FROM_RAM is used to allocate the flash
* write functions in RAM. By default, flash write
* functions are placed in RAM on EFM32G and Series 2 devices
* unless SL_RAMFUNC_DISABLE is defined. For other devices,
* flash write functions are placed in FLASH by default unless
* EM_MSC_RUN_FROM_RAM is defined and SL_RAMFUNC_DISABLE is not defined.
*
* @deprecated
* The function called MSC_WriteWordFast() is deprecated.
*
* @{
******************************************************************************/
/*******************************************************************************
************************* DEFINES *****************************************
******************************************************************************/
/**
* @brief
* Timeout used while waiting for Flash to become ready after a write.
* This number indicates the number of iterations to perform before
* issuing a timeout.
*
* @note
* Timeout is set very large (in the order of 100x longer than
* necessary). This is to avoid any corner case.
*/
#define MSC_PROGRAM_TIMEOUT 10000000UL
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#if (defined(_EFM32_GECKO_FAMILY) \
|| defined(_SILICON_LABS_32B_SERIES_2) \
|| defined(EM_MSC_RUN_FROM_RAM)) \
&& !defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
#define MSC_RAMFUNC_DECLARATOR SL_RAMFUNC_DECLARATOR
#define MSC_RAMFUNC_DEFINITION_BEGIN SL_RAMFUNC_DEFINITION_BEGIN
#define MSC_RAMFUNC_DEFINITION_END SL_RAMFUNC_DEFINITION_END
#else
#define MSC_RAMFUNC_DECLARATOR
#define MSC_RAMFUNC_DEFINITION_BEGIN
#define MSC_RAMFUNC_DEFINITION_END
#endif
/** @endcond */
/*******************************************************************************
************************* TYPEDEFS ****************************************
******************************************************************************/
/** Return codes for writing/erasing Flash. */
typedef enum {
mscReturnOk = 0, /**< Flash write/erase successful. */
mscReturnInvalidAddr = -1, /**< Invalid address. Write to an address that is not Flash. */
mscReturnLocked = -2, /**< Flash address is locked. */
mscReturnTimeOut = -3, /**< Timeout while writing to Flash. */
mscReturnUnaligned = -4 /**< Unaligned access to Flash. */
} MSC_Status_TypeDef;
#if defined(_MSC_READCTRL_BUSSTRATEGY_MASK)
/** Strategy for prioritized bus access. */
typedef enum {
mscBusStrategyCPU = MSC_READCTRL_BUSSTRATEGY_CPU, /**< Prioritize CPU bus accesses. */
mscBusStrategyDMA = MSC_READCTRL_BUSSTRATEGY_DMA, /**< Prioritize DMA bus accesses. */
mscBusStrategyDMAEM1 = MSC_READCTRL_BUSSTRATEGY_DMAEM1, /**< Prioritize DMAEM1 for bus accesses. */
mscBusStrategyNone = MSC_READCTRL_BUSSTRATEGY_NONE /**< No unit has bus priority. */
} MSC_BusStrategy_Typedef;
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_MASK)
/** AHBHOST masters that can use alternate MPAHBRAM ports. */
typedef enum {
mscDmemMasterLDMA = _SYSCFG_DMEM0PORTMAPSEL_LDMAPORTSEL_SHIFT,
mscDmemMasterSRWAES = _SYSCFG_DMEM0PORTMAPSEL_SRWAESPORTSEL_SHIFT,
mscDmemMasterAHBSRW = _SYSCFG_DMEM0PORTMAPSEL_AHBSRWPORTSEL_SHIFT,
#if defined(_SYSCFG_DMEM0PORTMAPSEL_IFADCDEBUGPORTSEL_MASK)
mscDmemMasterIFADCDEBUG = _SYSCFG_DMEM0PORTMAPSEL_IFADCDEBUGPORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_SRWECA0PORTSEL_MASK)
mscDmemMasterSRWECA0 = _SYSCFG_DMEM0PORTMAPSEL_SRWECA0PORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_SRWECA1PORTSEL_MASK)
mscDmemMasterSRWECA1 = _SYSCFG_DMEM0PORTMAPSEL_SRWECA1PORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_MVPAHBDATA0PORTSEL_MASK)
mscDmemMasterMVPAHBDATA0 = _SYSCFG_DMEM0PORTMAPSEL_MVPAHBDATA0PORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_MVPAHBDATA1PORTSEL_MASK)
mscDmemMasterMVPAHBDATA1 = _SYSCFG_DMEM0PORTMAPSEL_MVPAHBDATA1PORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_MVPAHBDATA2PORTSEL_MASK)
mscDmemMasterMVPAHBDATA2 = _SYSCFG_DMEM0PORTMAPSEL_MVPAHBDATA2PORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_LDMA1PORTSEL_MASK)
mscDmemMasterLDMA1 = _SYSCFG_DMEM0PORTMAPSEL_LDMA1PORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_SRWLDMAPORTSEL_MASK)
mscDmemMasterSRWLDMA = _SYSCFG_DMEM0PORTMAPSEL_SRWLDMAPORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_USBPORTSEL_MASK)
mscDmemMasterUSB = _SYSCFG_DMEM0PORTMAPSEL_USBPORTSEL_SHIFT,
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_BUFCPORTSEL_MASK)
mscDmemMasterBUFC = _SYSCFG_DMEM0PORTMAPSEL_BUFCPORTSEL_SHIFT
#endif
} MSC_DmemMaster_TypeDef;
#endif
#if defined(_MPAHBRAM_CTRL_AHBPORTPRIORITY_MASK)
/** AHB port given priority. */
typedef enum {
mscPortPriorityNone = _MPAHBRAM_CTRL_AHBPORTPRIORITY_NONE,
mscPortPriorityPort0 = _MPAHBRAM_CTRL_AHBPORTPRIORITY_PORT0,
mscPortPriorityPort1 = _MPAHBRAM_CTRL_AHBPORTPRIORITY_PORT1,
#if defined(_MPAHBRAM_CTRL_AHBPORTPRIORITY_PORT2)
mscPortPriorityPort2 = _MPAHBRAM_CTRL_AHBPORTPRIORITY_PORT2,
#endif
#if defined(_MPAHBRAM_CTRL_AHBPORTPRIORITY_PORT3)
mscPortPriorityPort3 = _MPAHBRAM_CTRL_AHBPORTPRIORITY_PORT3,
#endif
} MSC_PortPriority_TypeDef;
#endif
#if defined(MSC_READCTRL_DOUTBUFEN) || defined(MSC_RDATACTRL_DOUTBUFEN)
/** Code execution configuration */
typedef struct {
bool doutBufEn; /**< Flash dout pipeline buffer enable */
} MSC_ExecConfig_TypeDef;
/** Default MSC ExecConfig initialization */
#define MSC_EXECCONFIG_DEFAULT \
{ \
false, \
}
#else
/** Code execution configuration. */
typedef struct {
bool scbtEn; /**< Enable Suppressed Conditional Branch Target Prefetch. */
bool prefetchEn; /**< Enable MSC prefetching. */
bool ifcDis; /**< Disable instruction cache. */
bool aiDis; /**< Disable automatic cache invalidation on write or erase. */
bool iccDis; /**< Disable automatic caching of fetches in interrupt context. */
bool useHprot; /**< Use ahb_hprot to determine if the instruction is cacheable or not. */
} MSC_ExecConfig_TypeDef;
/** Default MSC ExecConfig initialization. */
#define MSC_EXECCONFIG_DEFAULT \
{ \
false, \
true, \
false, \
false, \
false, \
false, \
}
#endif
#if defined(_MSC_ECCCTRL_MASK) \
|| defined(_SYSCFG_DMEM0ECCCTRL_MASK) \
|| defined(_MPAHBRAM_CTRL_MASK)
#if defined(_SILICON_LABS_32B_SERIES_1_CONFIG_1)
/** EFM32GG11B incorporates 2 memory banks including ECC support. */
#define MSC_ECC_BANKS (2)
/** Default MSC EccConfig initialization. */
#define MSC_ECCCONFIG_DEFAULT \
{ \
{ false, false }, \
{ 0, 1 }, \
}
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_106)
/** EFM32GG12B incorporates 3 memory banks including ECC support. */
#define MSC_ECC_BANKS (3)
/** Default MSC EccConfig initialization. */
#define MSC_ECCCONFIG_DEFAULT \
{ \
{ false, false, false }, \
{ 0, 1 }, \
}
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_6)
/** xG26 chips incorporate 2 memory banks including ECC support. */
#define MSC_ECC_BANKS (2)
/** Default MSC EccConfig initialization */
#define MSC_ECCCONFIG_DEFAULT \
{ \
{ false, false }, \
{ 0, 1 }, \
}
#elif defined(_SILICON_LABS_32B_SERIES_2)
/** Series 2 chips incorporate 1 memory bank including ECC support. */
#define MSC_ECC_BANKS (1)
/** Default MSC EccConfig initialization */
#define MSC_ECCCONFIG_DEFAULT \
{ \
{ false }, \
{ 0, 1 }, \
}
#else
#error Device not supported.
#endif
/** ECC configuration. */
typedef struct {
bool enableEccBank[MSC_ECC_BANKS]; /**< Array of bools to enable/disable
Error Correcting Code (ECC) for
each RAM bank that supports ECC on
the device. */
uint32_t dmaChannels[2]; /**< Array of 2 DMA channel numbers to
use for ECC initialization. */
} MSC_EccConfig_TypeDef;
#endif /* #if defined(_MSC_ECCCTRL_MASK) */
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* Deprecated type names. */
#define mscBusStrategy_Typedef MSC_BusStrategy_Typedef
#define msc_Return_TypeDef MSC_Status_TypeDef
/** @endcond */
/*******************************************************************************
************************* Inline Functions ********************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Get the status of the MSC register lock.
*
* @return
* Boolean true if register lock is applied, false otherwise.
******************************************************************************/
__STATIC_INLINE bool MSC_LockGetLocked(void)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
return (bool)sli_tz_ns_interface_dispatch_simple_noarg(
(sli_tz_veneer_simple_noarg_fn)sli_tz_s_interface_dispatch_simple_no_args,
SLI_TZ_MSC_GET_LOCKED_SID);
#elif defined(_MSC_STATUS_REGLOCK_MASK)
return (MSC->STATUS & _MSC_STATUS_REGLOCK_MASK) != MSC_STATUS_REGLOCK_UNLOCKED;
#else
return (MSC->LOCK & _MSC_LOCK_MASK) != MSC_LOCK_LOCKKEY_UNLOCKED;
#endif
}
/***************************************************************************//**
* @brief
* Set the MSC register lock to a locked state.
******************************************************************************/
__STATIC_INLINE void MSC_LockSetLocked(void)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
(void)sli_tz_ns_interface_dispatch_simple_noarg(
(sli_tz_veneer_simple_noarg_fn)sli_tz_s_interface_dispatch_simple_no_args,
SLI_TZ_MSC_SET_LOCKED_SID);
#else
MSC->LOCK = MSC_LOCK_LOCKKEY_LOCK;
#endif
}
/***************************************************************************//**
* @brief
* Set the MSC register lock to an unlocked state.
******************************************************************************/
__STATIC_INLINE void MSC_LockSetUnlocked(void)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
(void)sli_tz_ns_interface_dispatch_simple_noarg(
(sli_tz_veneer_simple_noarg_fn)sli_tz_s_interface_dispatch_simple_no_args,
SLI_TZ_MSC_SET_UNLOCKED_SID);
#else
MSC->LOCK = MSC_LOCK_LOCKKEY_UNLOCK;
#endif
}
/***************************************************************************//**
* @brief
* Get the current value of the read control register (MSC_READCTRL).
*
* @return
* The 32-bit value read from the MSC_READCTRL register.
******************************************************************************/
__STATIC_INLINE uint32_t MSC_ReadCTRLGet(void)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
return sli_tz_ns_interface_dispatch_simple_noarg(
(sli_tz_veneer_simple_noarg_fn)sli_tz_s_interface_dispatch_simple_no_args,
SLI_TZ_MSC_GET_READCTRL_SID);
#else
return MSC->READCTRL;
#endif
}
/***************************************************************************//**
* @brief
* Write a value to the read control register (MSC_READCTRL).
*
* @param[in] value
* The 32-bit value to write to the MSC_READCTRL register.
******************************************************************************/
__STATIC_INLINE void MSC_ReadCTRLSet(uint32_t value)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
(void)sli_tz_ns_interface_dispatch_simple(
(sli_tz_veneer_simple_fn)sli_tz_s_interface_dispatch_simple,
SLI_TZ_MSC_SET_READCTRL_SID,
value);
#else
MSC->READCTRL = value;
#endif
}
#if defined(_MSC_PAGELOCK0_MASK) || defined(_MSC_INST_PAGELOCKWORD0_MASK)
/***************************************************************************//**
* @brief
* Set the lockbit for a flash page in order to prevent page writes/erases to
* the corresponding page.
*
* @param[in] page_number
* The index of the page to apply the pagelock to. Must be in the range
* [0, (flash_size / page_size) - 1].
******************************************************************************/
__STATIC_INLINE void MSC_PageLockSetLocked(uint32_t page_number)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
(void)sli_tz_ns_interface_dispatch_simple(
(sli_tz_veneer_simple_fn)sli_tz_s_interface_dispatch_simple,
SLI_TZ_MSC_SET_PAGELOCK_SID,
page_number);
#else
EFM_ASSERT(page_number < (FLASH_SIZE / FLASH_PAGE_SIZE));
#if defined(_MSC_PAGELOCK0_MASK)
uint32_t *pagelock_registers = (uint32_t *)&MSC->PAGELOCK0;
#elif defined(_MSC_INST_PAGELOCKWORD0_MASK)
uint32_t *pagelock_registers = (uint32_t *)&MSC->INST_PAGELOCKWORD0;
#endif
pagelock_registers[page_number / 32] |= (1 << (page_number % 32));
#endif
}
/***************************************************************************//**
* @brief
* Get the value of the lockbit for a flash page.
*
* @param[in] page_number
* The index of the page to get the lockbit value from. Must be in the range
* [0, (flash_size / page_size) - 1].
*
* @return
* Boolean true if the page is locked, false otherwise.
******************************************************************************/
__STATIC_INLINE bool MSC_PageLockGetLocked(uint32_t page_number)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
return (bool)sli_tz_ns_interface_dispatch_simple(
(sli_tz_veneer_simple_fn)sli_tz_s_interface_dispatch_simple,
SLI_TZ_MSC_GET_PAGELOCK_SID,
page_number);
#else
EFM_ASSERT(page_number < (FLASH_SIZE / FLASH_PAGE_SIZE));
#if defined(_MSC_PAGELOCK0_MASK)
uint32_t *pagelock_registers = (uint32_t *)&MSC->PAGELOCK0;
#elif defined(_MSC_INST_PAGELOCKWORD0_MASK)
uint32_t *pagelock_registers = (uint32_t *)&MSC->INST_PAGELOCKWORD0;
#endif
return pagelock_registers[page_number / 32] & (1 << (page_number % 32));
#endif
}
#endif // _MSC_PAGELOCK0_MASK || _MSC_INST_PAGELOCKWORD0_MASK
#if defined(_MSC_USERDATASIZE_MASK)
/***************************************************************************//**
* @brief
* Get the size of the user data region in flash.
*
* @return
* The size of the user data region divided by 256.
******************************************************************************/
__STATIC_INLINE uint32_t MSC_UserDataGetSize(void)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
return sli_tz_ns_interface_dispatch_simple_noarg(
(sli_tz_veneer_simple_noarg_fn)sli_tz_s_interface_dispatch_simple_no_args,
SLI_TZ_MSC_GET_USERDATA_SIZE_SID);
#else
return MSC->USERDATASIZE;
#endif
}
#endif // _MSC_USERDATASIZE_MASK
#if defined(_MSC_MISCLOCKWORD_MASK)
/***************************************************************************//**
* @brief
* Get the current value of the mass erase and user data page lock word
* (MSC_MISCLOCKWORD).
*
* @return
* The 32-bit value read from the MSC_MISCLOCKWORD register.
******************************************************************************/
__STATIC_INLINE uint32_t MSC_MiscLockWordGet(void)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
return sli_tz_ns_interface_dispatch_simple_noarg(
(sli_tz_veneer_simple_noarg_fn)sli_tz_s_interface_dispatch_simple_no_args,
SLI_TZ_MSC_GET_MISCLOCKWORD_SID);
#else
return MSC->MISCLOCKWORD;
#endif
}
/***************************************************************************//**
* @brief
* Write a value to the mass erase and user data page lock word
* (MSC_MISCLOCKWORD).
*
* @param[in] value
* The 32-bit value to write to the MSC_MISCLOCKWORD register.
******************************************************************************/
__STATIC_INLINE void MSC_MiscLockWordSet(uint32_t value)
{
#if defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
(void)sli_tz_ns_interface_dispatch_simple(
(sli_tz_veneer_simple_fn)sli_tz_s_interface_dispatch_simple,
SLI_TZ_MSC_SET_MISCLOCKWORD_SID,
value);
#else
MSC->MISCLOCKWORD = value;
#endif
}
#endif // _MSC_USERDATASIZE_MASK
#if !defined(SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT)
/***************************************************************************//**
* @brief
* Clear one or more pending MSC interrupts.
*
* @param[in] flags
* Pending MSC interrupt source to clear. Use a bitwise logic OR combination
* of valid interrupt flags for the MSC module (MSC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void MSC_IntClear(uint32_t flags)
{
#if defined(MSC_HAS_SET_CLEAR)
MSC->IF_CLR = flags;
#else
MSC->IFC = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more MSC interrupts.
*
* @param[in] flags
* MSC interrupt sources to disable. Use a bitwise logic OR combination of
* valid interrupt flags for the MSC module (MSC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void MSC_IntDisable(uint32_t flags)
{
#if defined(MSC_HAS_SET_CLEAR)
MSC->IEN_CLR = flags;
#else
MSC->IEN &= ~(flags);
#endif
}
/***************************************************************************//**
* @brief
* Enable one or more MSC interrupts.
*
* @note
* Depending on the use, a pending interrupt may already be set prior to
* enabling the interrupt. To ignore a pending interrupt, consider using
* MSC_IntClear() prior to enabling the interrupt.
*
* @param[in] flags
* MSC interrupt sources to enable. Use a bitwise logic OR combination of
* valid interrupt flags for the MSC module (MSC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void MSC_IntEnable(uint32_t flags)
{
#if defined(MSC_HAS_SET_CLEAR)
MSC->IEN_SET = flags;
#else
MSC->IEN |= flags;
#endif
}
/***************************************************************************//**
* @brief
* Get pending MSC interrupt flags.
*
* @note
* The event bits are not cleared by the use of this function.
*
* @return
* MSC interrupt sources pending. A bitwise logic OR combination of valid
* interrupt flags for the MSC module (MSC_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t MSC_IntGet(void)
{
return MSC->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending MSC interrupt flags.
* Useful for handling more interrupt sources in the same interrupt handler.
*
* @note
* Interrupt flags are not cleared by the use of this function.
*
* @return
* Pending and enabled MSC interrupt sources.
* The return value is the bitwise AND of
* - the enabled interrupt sources in MSC_IEN and
* - the pending interrupt flags MSC_IF
******************************************************************************/
__STATIC_INLINE uint32_t MSC_IntGetEnabled(void)
{
uint32_t ien;
ien = MSC->IEN;
return MSC->IF & ien;
}
/***************************************************************************//**
* @brief
* Set one or more pending MSC interrupts from SW.
*
* @param[in] flags
* MSC interrupt sources to set to pending. Use a bitwise logic OR combination of
* valid interrupt flags for the MSC module (MSC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void MSC_IntSet(uint32_t flags)
{
#if defined(MSC_HAS_SET_CLEAR)
MSC->IF_SET = flags;
#else
MSC->IFS = flags;
#endif
}
#if defined(MSC_IF_CHOF) && defined(MSC_IF_CMOF)
/***************************************************************************//**
* @brief
* Start measuring the cache hit ratio.
* @details
* Starts performance counters. It is defined inline to
* minimize the impact of this code on the measurement itself.
******************************************************************************/
__STATIC_INLINE void MSC_StartCacheMeasurement(void)
{
/* Clear CMOF and CHOF to catch these later. */
MSC->IFC = MSC_IF_CHOF | MSC_IF_CMOF;
/* Start performance counters. */
#if defined(_MSC_CACHECMD_MASK)
MSC->CACHECMD = MSC_CACHECMD_STARTPC;
#else
MSC->CMD = MSC_CMD_STARTPC;
#endif
}
/***************************************************************************//**
* @brief
* Stop measuring the hit rate.
* @note
* Defined inline to minimize the impact of this
* code on the measurement itself.
* Only works for relatively short sections of code.
* To measure longer sections of code, implement an IRQ Handler for
* the CHOF and CMOF overflow interrupts. These overflows need to be
* counted and included in the total.
* Functions can then be implemented as follows:
* @verbatim
* volatile uint32_t hitOverflows
* volatile uint32_t missOverflows
*
* void MSC_IRQHandler(void)
* {
* uint32_t flags;
* flags = MSC->IF;
* if (flags & MSC_IF_CHOF) {
* MSC->IFC = MSC_IF_CHOF;
* hitOverflows++;
* }
* if (flags & MSC_IF_CMOF) {
* MSC->IFC = MSC_IF_CMOF;
* missOverflows++;
* }
* }
*
* void startPerformanceCounters(void)
* {
* hitOverflows = 0;
* missOverflows = 0;
*
* MSC_IntEnable(MSC_IF_CHOF | MSC_IF_CMOF);
* NVIC_EnableIRQ(MSC_IRQn);
*
* MSC_StartCacheMeasurement();
* }
* @endverbatim
* @return
* Returns -1 if there has been no cache accesses.
* Returns -2 if there has been an overflow in the performance counters.
* If not, it will return the percentage of hits versus misses.
******************************************************************************/
__STATIC_INLINE int32_t MSC_GetCacheMeasurement(void)
{
int32_t total;
int32_t hits;
/* Stop counter before computing hit-rate. */
#if defined(_MSC_CACHECMD_MASK)
MSC->CACHECMD = MSC_CACHECMD_STOPPC;
#else
MSC->CMD = MSC_CMD_STOPPC;
#endif
/* Check for overflows in performance counters. */
if (MSC->IF & (MSC_IF_CHOF | MSC_IF_CMOF)) {
return -2;
}
hits = (int32_t)MSC->CACHEHITS;
total = (int32_t)MSC->CACHEMISSES + hits;
/* To avoid a division by zero. */
if (total == 0) {
return -1;
}
return (hits * 100) / total;
}
/***************************************************************************//**
* @brief
* Flush contents of instruction cache.
******************************************************************************/
__STATIC_INLINE void MSC_FlushCache(void)
{
#if defined(_MSC_CACHECMD_MASK)
MSC->CACHECMD = MSC_CACHECMD_INVCACHE;
#else
MSC->CMD = MSC_CMD_INVCACHE;
#endif
}
/***************************************************************************//**
* @brief
* Enable or disable instruction cache functionality.
* @param[in] enable
* Enable instruction cache. Default is on.
******************************************************************************/
__STATIC_INLINE void MSC_EnableCache(bool enable)
{
BUS_RegBitWrite(&(MSC->READCTRL), _MSC_READCTRL_IFCDIS_SHIFT, !enable);
}
#if defined(MSC_READCTRL_ICCDIS)
/***************************************************************************//**
* @brief
* Enable or disable instruction cache functionality in IRQs.
* @param[in] enable
* Enable instruction cache. Default is on.
******************************************************************************/
__STATIC_INLINE void MSC_EnableCacheIRQs(bool enable)
{
BUS_RegBitWrite(&(MSC->READCTRL), _MSC_READCTRL_ICCDIS_SHIFT, !enable);
}
#endif
/***************************************************************************//**
* @brief
* Enable or disable instruction cache flushing when writing to flash.
* @param[in] enable
* Enable automatic cache flushing. Default is on.
******************************************************************************/
__STATIC_INLINE void MSC_EnableAutoCacheFlush(bool enable)
{
BUS_RegBitWrite(&(MSC->READCTRL), _MSC_READCTRL_AIDIS_SHIFT, !enable);
}
#endif /* defined( MSC_IF_CHOF ) && defined( MSC_IF_CMOF ) */
#if defined(_MSC_READCTRL_BUSSTRATEGY_MASK)
/***************************************************************************//**
* @brief
* Configure which unit should get priority on system bus.
* @param[in] mode
* Unit to prioritize bus accesses for.
******************************************************************************/
__STATIC_INLINE void MSC_BusStrategy(mscBusStrategy_Typedef mode)
{
MSC->READCTRL = (MSC->READCTRL & ~(_MSC_READCTRL_BUSSTRATEGY_MASK)) | mode;
}
#endif
/*******************************************************************************
************************* PROTOTYPES **************************************
******************************************************************************/
void MSC_ExecConfigSet(MSC_ExecConfig_TypeDef *execConfig);
#if defined(_MSC_ECCCTRL_MASK) \
|| defined(_SYSCFG_DMEM0ECCCTRL_MASK) \
|| defined(_MPAHBRAM_CTRL_MASK)
void MSC_EccConfigSet(MSC_EccConfig_TypeDef *eccConfig);
#endif
#if defined(_SYSCFG_DMEM0PORTMAPSEL_MASK)
void MSC_DmemPortMapSet(MSC_DmemMaster_TypeDef master, uint8_t port);
#endif
#if defined(_MPAHBRAM_CTRL_AHBPORTPRIORITY_MASK)
void MSC_PortSetPriority(MSC_PortPriority_TypeDef portPriority);
MSC_PortPriority_TypeDef MSC_PortGetCurrentPriority(void);
#endif
#if !defined(_SILICON_LABS_32B_SERIES_2)
/* Note that this function is deprecated because we no longer support
* placing msc code in ram. */
MSC_RAMFUNC_DECLARATOR
MSC_Status_TypeDef MSC_WriteWordFast(uint32_t *address,
void const *data,
uint32_t numBytes);
#endif
#if defined(MSC_WRITECMD_ERASEMAIN0)
/***************************************************************************//**
* @brief
* Erase the entire Flash in one operation.
*
* @note
* This command will erase the entire contents of the device.
* Use with care, both a debug session and all contents of the flash will be
* lost. The lock bit, MLW will prevent this operation from executing and
* might prevent a successful mass erase.
*
* @return
* Returns the status of the operation.
******************************************************************************/
SL_RAMFUNC_DECLARATOR
MSC_Status_TypeDef MSC_MassErase(void);
#endif
#endif /* !SL_CATALOG_TZ_SECURE_KEY_LIBRARY_NS_PRESENT */
MSC_RAMFUNC_DECLARATOR
MSC_Status_TypeDef MSC_ErasePage(uint32_t *startAddress);
MSC_RAMFUNC_DECLARATOR
MSC_Status_TypeDef MSC_WriteWord(uint32_t *address,
void const *data,
uint32_t numBytes);
#if (_SILICON_LABS_32B_SERIES > 0)
MSC_Status_TypeDef MSC_WriteWordDma(int ch,
uint32_t *address,
const void *data,
uint32_t numBytes);
#endif
void MSC_Init(void);
void MSC_Deinit(void);
/** @} (end addtogroup msc) */
#ifdef __cplusplus
}
#endif
#endif /* defined(MSC_COUNT) && (MSC_COUNT > 0) */
#endif /* EM_MSC_H */

View File

@@ -0,0 +1,81 @@
/***************************************************************************//**
* @file
* @brief Flash Controller (MSC) Compatibility Header
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_MSC_COMPAT_H
#define EM_MSC_COMPAT_H
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
#define MSC_IF_PWROFFIF MSC_IF_PWROFF
#define _MSC_IF_PWROFFIF_SHIFT _MSC_IF_PWROFF_SHIFT
#define _MSC_IF_PWROFFIF_MASK _MSC_IF_PWROFF_MASK
#define _MSC_IF_PWROFFIF_DEFAULT _MSC_IF_PWROFF_DEFAULT
#define MSC_IF_PWROFFIF_DEFAULT MSC_IF_PWROFF_DEFAULT
#define MSC_IEN_PWROFFIEN MSC_IEN_PWROFF
#define _MSC_IEN_PWROFFIEN_SHIFT _MSC_IEN_PWROFF_SHIFT
#define _MSC_IEN_PWROFFIEN_MASK _MSC_IEN_PWROFF_MASK
#define _MSC_IEN_PWROFFIEN_DEFAULT _MSC_IEN_PWROFF_DEFAULT
#define MSC_IEN_PWROFFIEN_DEFAULT MSC_IEN_PWROFF_DEFAULT
#define ICACHE_IEN_RAMERRORIEN ICACHE_IEN_RAMERROR
#define _ICACHE_IEN_RAMERRORIEN_SHIFT _ICACHE_IEN_RAMERROR_SHIFT
#define _ICACHE_IEN_RAMERRORIEN_MASK _ICACHE_IEN_RAMERROR_MASK
#define _ICACHE_IEN_RAMERRORIEN_DEFAULT _ICACHE_IEN_RAMERROR_DEFAULT
#define ICACHE_IEN_RAMERRORIEN_DEFAULT ICACHE_IEN_RAMERROR_DEFAULT
#define SYSCFG_IF_FRCRAMERR1BIF SYSCFG_IF_FRCRAMERR1B
#define _SYSCFG_IF_FRCRAMERR1BIF_SHIFT _SYSCFG_IF_FRCRAMERR1B_SHIFT
#define _SYSCFG_IF_FRCRAMERR1BIF_MASK _SYSCFG_IF_FRCRAMERR1B_MASK
#define _SYSCFG_IF_FRCRAMERR1BIF_DEFAULT _SYSCFG_IF_FRCRAMERR1B_DEFAULT
#define SYSCFG_IF_FRCRAMERR1BIF_DEFAULT SYSCFG_IF_FRCRAMERR1B_DEFAULT
#define SYSCFG_IF_FRCRAMERR2BIF SYSCFG_IF_FRCRAMERR2B
#define _SYSCFG_IF_FRCRAMERR2BIF_SHIFT _SYSCFG_IF_FRCRAMERR2B_SHIFT
#define _SYSCFG_IF_FRCRAMERR2BIF_MASK _SYSCFG_IF_FRCRAMERR2B_MASK
#define _SYSCFG_IF_FRCRAMERR2BIF_DEFAULT _SYSCFG_IF_FRCRAMERR2B_DEFAULT
#define SYSCFG_IF_FRCRAMERR2BIF_DEFAULT SYSCFG_IF_FRCRAMERR2B_DEFAULT
#define SYSCFG_IEN_FRCRAMERR1BIEN SYSCFG_IEN_FRCRAMERR1B
#define _SYSCFG_IEN_FRCRAMERR1BIEN_SHIFT _SYSCFG_IEN_FRCRAMERR1B_SHIFT
#define _SYSCFG_IEN_FRCRAMERR1BIEN_MASK _SYSCFG_IEN_FRCRAMERR1B_MASK
#define _SYSCFG_IEN_FRCRAMERR1BIEN_DEFAULT _SYSCFG_IEN_FRCRAMERR1B_DEFAULT
#define SYSCFG_IEN_FRCRAMERR1BIEN_DEFAULT SYSCFG_IEN_FRCRAMERR1B_DEFAULT
#define SYSCFG_IEN_FRCRAMERR2BIEN SYSCFG_IEN_FRCRAMERR2B
#define _SYSCFG_IEN_FRCRAMERR2BIEN_SHIFT _SYSCFG_IEN_FRCRAMERR2B_SHIFT
#define _SYSCFG_IEN_FRCRAMERR2BIEN_MASK _SYSCFG_IEN_FRCRAMERR2B_MASK
#define _SYSCFG_IEN_FRCRAMERR2BIEN_DEFAULT _SYSCFG_IEN_FRCRAMERR2B_DEFAULT
#define SYSCFG_IEN_FRCRAMERR2BIEN_DEFAULT SYSCFG_IEN_FRCRAMERR2B_DEFAULT
#endif /* _SILICON_LABS_32B_SERIES_2_CONFIG_2 */
#endif /* EM_MSC_COMPAT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,905 @@
/***************************************************************************//**
* @file
* @brief Pulse Counter (PCNT) 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.
*
******************************************************************************/
#ifndef EM_PCNT_H
#define EM_PCNT_H
#include "em_device.h"
#if defined(PCNT_COUNT) && (PCNT_COUNT > 0)
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup pcnt
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** PCNT0 Counter register size. */
#if defined(_EFM32_GECKO_FAMILY)
#define PCNT0_CNT_SIZE (8) /**< PCNT0 counter is 8 bits. */
#else
#define PCNT0_CNT_SIZE (16) /**< PCNT0 counter is 16 bits. */
#endif
#ifdef PCNT1
/** PCNT1 Counter register size. */
#if defined(_SILICON_LABS_32B_SERIES_0)
#define PCNT1_CNT_SIZE (8) /**< PCNT1 counter is 8 bits. */
#else
#define PCNT1_CNT_SIZE (16) /**< PCNT1 counter is 16 bits. */
#endif
#endif
#ifdef PCNT2
/** PCNT2 Counter register size. */
#if defined(_SILICON_LABS_32B_SERIES_0)
#define PCNT2_CNT_SIZE (8) /**< PCNT2 counter is 8 bits. */
#else
#define PCNT2_CNT_SIZE (16) /**< PCNT2 counter is 16 bits. */
#endif
#endif
/* Define values that can be used in case some state/mode are not defined for some devices.*/
/** PCNT mode disable. */
#define PCNT_MODE_DISABLE 0xFF
/** PCNT count event is none. */
#define PCNT_CNT_EVENT_NONE 0xFF
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Mode selection. */
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
typedef enum {
/** Disable pulse counter. */
pcntModeDisable = _PCNT_CTRL_MODE_DISABLE,
/** Single input LFACLK oversampling mode (available in EM0-EM2). */
pcntModeOvsSingle = _PCNT_CTRL_MODE_OVSSINGLE,
/** Externally clocked single input counter mode (available in EM0-EM3). */
pcntModeExtSingle = _PCNT_CTRL_MODE_EXTCLKSINGLE,
/** Externally clocked quadrature decoder mode (available in EM0-EM3). */
pcntModeExtQuad = _PCNT_CTRL_MODE_EXTCLKQUAD,
#if defined(_PCNT_CTRL_MODE_OVSQUAD1X)
/** LFACLK oversampling quadrature decoder 1X mode (available in EM0-EM2). */
pcntModeOvsQuad1 = _PCNT_CTRL_MODE_OVSQUAD1X,
/** LFACLK oversampling quadrature decoder 2X mode (available in EM0-EM2). */
pcntModeOvsQuad2 = _PCNT_CTRL_MODE_OVSQUAD2X,
/** LFACLK oversampling quadrature decoder 4X mode (available in EM0-EM2). */
pcntModeOvsQuad4 = _PCNT_CTRL_MODE_OVSQUAD4X,
#endif
} PCNT_Mode_TypeDef;
#else
typedef enum {
/** Disable pulse counter. */
pcntModeDisable = PCNT_MODE_DISABLE,
/** Single input LFACLK oversampling mode (available in EM0-EM2). */
pcntModeOvsSingle = _PCNT_CFG_MODE_OVSSINGLE,
/** Externally clocked single input counter mode (available in EM0-EM3). */
pcntModeExtSingle = _PCNT_CFG_MODE_EXTCLKSINGLE,
/** Externally clocked quadrature decoder mode (available in EM0-EM3). */
pcntModeExtQuad = _PCNT_CFG_MODE_EXTCLKQUAD,
/** LFACLK oversampling quadrature decoder 1X mode (available in EM0-EM2). */
pcntModeOvsQuad1 = _PCNT_CFG_MODE_OVSQUAD1X,
/** LFACLK oversampling quadrature decoder 2X mode (available in EM0-EM2). */
pcntModeOvsQuad2 = _PCNT_CFG_MODE_OVSQUAD2X,
/** LFACLK oversampling quadrature decoder 4X mode (available in EM0-EM2). */
pcntModeOvsQuad4 = _PCNT_CFG_MODE_OVSQUAD4X,
} PCNT_Mode_TypeDef;
#endif
#if defined(_PCNT_CTRL_CNTEV_MASK)
/** Counter event selection.
* Note: unshifted values are being used for enumeration because multiple
* configuration structure members use this type definition. */
typedef enum {
/** Counts up on up-count and down on down-count events. */
pcntCntEventBoth = _PCNT_CTRL_CNTEV_BOTH,
/** Only counts up on up-count events. */
pcntCntEventUp = _PCNT_CTRL_CNTEV_UP,
/** Only counts down on down-count events. */
pcntCntEventDown = _PCNT_CTRL_CNTEV_DOWN,
/** Never counts. */
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
pcntCntEventNone = _PCNT_CTRL_CNTEV_NONE
#else
pcntCntEventNone = PCNT_CNT_EVENT_NONE
#endif
} PCNT_CntEvent_TypeDef;
#endif
/** PRS sources for @p s0PRS and @p s1PRS. */
#if defined(_PCNT_INPUT_MASK)
typedef enum {
pcntPRSCh0 = 0, /**< PRS channel 0. */
pcntPRSCh1 = 1, /**< PRS channel 1. */
pcntPRSCh2 = 2, /**< PRS channel 2. */
pcntPRSCh3 = 3, /**< PRS channel 3. */
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH4)
pcntPRSCh4 = 4, /**< PRS channel 4. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH5)
pcntPRSCh5 = 5, /**< PRS channel 5. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH6)
pcntPRSCh6 = 6, /**< PRS channel 6. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH7)
pcntPRSCh7 = 7, /**< PRS channel 7. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH8)
pcntPRSCh8 = 8, /**< PRS channel 8. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH9)
pcntPRSCh9 = 9, /**< PRS channel 9. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH10)
pcntPRSCh10 = 10, /**< PRS channel 10. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH11)
pcntPRSCh11 = 11, /**< PRS channel 11. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH12)
pcntPRSCh12 = 12, /**< PRS channel 12. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH13)
pcntPRSCh13 = 13, /**< PRS channel 13. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH14)
pcntPRSCh14 = 14, /**< PRS channel 14. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH15)
pcntPRSCh15 = 15, /**< PRS channel 15. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH16)
pcntPRSCh16 = 16, /**< PRS channel 16. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH17)
pcntPRSCh17 = 17, /**< PRS channel 17. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH18)
pcntPRSCh18 = 18, /**< PRS channel 18. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH19)
pcntPRSCh19 = 19, /**< PRS channel 19. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH20)
pcntPRSCh20 = 20, /**< PRS channel 20. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH21)
pcntPRSCh21 = 21, /**< PRS channel 21. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH22)
pcntPRSCh22 = 22, /**< PRS channel 22. */
#endif
#if defined(PCNT_INPUT_S0PRSSEL_PRSCH23)
pcntPRSCh23 = 23, /**< PRS channel 23. */
#endif
} PCNT_PRSSel_TypeDef;
#elif defined(_SILICON_LABS_32B_SERIES_2)
typedef unsigned int PCNT_PRSSel_TypeDef;
#endif
#if defined(_PCNT_INPUT_MASK) || defined(_SILICON_LABS_32B_SERIES_2)
/** PRS inputs of PCNT. */
typedef enum {
pcntPRSInputS0 = 0, /** PRS input 0. */
pcntPRSInputS1 = 1 /** PRS input 1. */
} PCNT_PRSInput_TypeDef;
#endif
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** Initialization structure. */
typedef struct {
/** Mode to operate in. */
PCNT_Mode_TypeDef mode;
/** Initial counter value (refer to reference manual for max value allowed).
* Only used for #pcntModeOvsSingle (and possibly #pcntModeDisable) modes.
* If using #pcntModeExtSingle or #pcntModeExtQuad modes, counter
* value is reset to HW reset value. */
uint32_t counter;
/** Initial top value (refer to reference manual for max value allowed).
* Only used for #pcntModeOvsSingle (and possibly #pcntModeDisable) modes.
* If using #pcntModeExtSingle or #pcntModeExtQuad modes, top
* value is reset to HW reset value. */
uint32_t top;
/** Polarity of incoming edge.
* @li #pcntModeExtSingle mode - if false, positive edges are counted,
* otherwise negative edges.
* @li #pcntModeExtQuad mode - if true, counting direction is inverted. */
bool negEdge;
/** Counting direction, only applicable for #pcntModeOvsSingle and
* #pcntModeExtSingle modes. */
bool countDown;
/** Enable filter, only available in #pcntModeOvsSingle* mode. */
bool filter;
#if defined(_SILICON_LABS_32B_SERIES_2)
/** Enable/disable PCNT counting during debug halt. Only in OVSSINGLE and OVSQUAD modes. */
bool debugHalt;
#endif
#if defined(PCNT_CTRL_HYST) || defined(_SILICON_LABS_32B_SERIES_2)
/** Set to true to enable hysteresis. When enabled, PCNT will always
* overflow and underflow to TOP/2. */
bool hyst;
#endif
#if defined(PCNT_CTRL_S1CDIR)
/** Set to true to enable S1 to determine the direction of counting in
* OVSSINGLE or EXTCLKSINGLE modes. @n
* When S1 is high, the count direction is given by CNTDIR, and when S1 is
* low, the count direction is the opposite. */
bool s1CntDir;
#endif
#if defined(_PCNT_CTRL_CNTEV_SHIFT)
/** Selects whether the regular counter responds to up-count events,
* down-count events, both, or none. */
PCNT_CntEvent_TypeDef cntEvent;
#endif
#if defined(_PCNT_CTRL_AUXCNTEV_SHIFT)
/** Selects whether the auxiliary counter responds to up-count events,
* down-count events, both, or none. */
PCNT_CntEvent_TypeDef auxCntEvent;
#endif
#if defined(_PCNT_INPUT_MASK) || defined(_SILICON_LABS_32B_SERIES_2)
/** Select PRS channel as input to S0IN in PCNTx_INPUT register. */
PCNT_PRSSel_TypeDef s0PRS;
/** Select PRS channel as input to S1IN in PCNTx_INPUT register. */
PCNT_PRSSel_TypeDef s1PRS;
#endif
} PCNT_Init_TypeDef;
/** Default Debug. */
#if defined(_SILICON_LABS_32B_SERIES_2)
#define DEFAULT_DEBUG_HALT true,
#else
#define DEFAULT_DEBUG_HALT
#endif
/** Default Mode. */
#define DEFAULT_MODE pcntModeDisable, /**< Disabled by default. */
/** Default Hysteresis. */
#if defined(PCNT_CTRL_HYST) || defined(_SILICON_LABS_32B_SERIES_2)
#define DEFAULT_HYST false, /**< Hysteresis disabled. */
#else
#define DEFAULT_HYST
#endif
/** Default counter direction*/
#if defined(PCNT_CTRL_S1CDIR)
#define DEFAULT_CDIR true, /**< Counter direction is given by CNTDIR. */
#else
#define DEFAULT_CDIR
#endif
/** Default count event*/
#if defined(_PCNT_CTRL_CNTEV_SHIFT)
#define DEFAULT_CNTEV pcntCntEventUp, /**< Regular counter counts up on upcount events. */
#else
#define DEFAULT_CNTEV
#endif
/** Default auxiliary count event. */
#if defined(_PCNT_CTRL_AUXCNTEV_SHIFT)
#define DEFAULT_AUXCNTEV pcntCntEventNone, /**< Auxiliary counter doesn't respond to events. */
#else
#define DEFAULT_AUXCNTEV
#endif
/** Default selected PRS channel as S0IN and S1IN. */
#if defined(_PCNT_INPUT_MASK)
#define DEFAULT_PRS_CH pcntPRSCh0, /**< PRS channel 0 selected as S0IN and as S1IN. */
#elif defined(_SILICON_LABS_32B_SERIES_2)
#define DEFAULT_PRS_CH 0u,
#else
#define DEFAULT_PRS_CH
#endif
/** Default configuration for PCNT initialization structure. */
#define PCNT_INIT_DEFAULT \
{ \
DEFAULT_MODE /* Default mode. */ \
_PCNT_CNT_RESETVALUE, /* Default counter HW reset value. */ \
_PCNT_TOP_RESETVALUE, /* Default counter HW reset value. */ \
false, /* Use positive edge. */ \
false, /* Up-counting. */ \
false, /* Filter disabled. */ \
DEFAULT_DEBUG_HALT /* Debug Halt enabled. */ \
DEFAULT_HYST /* Default Hysteresis. */ \
DEFAULT_CDIR /* Default CNTDIR. */ \
DEFAULT_CNTEV /* Faults CNTEV. */ \
DEFAULT_AUXCNTEV /* Default AUXCNTEV. */ \
DEFAULT_PRS_CH /* PRS channel 0 selected as S0IN. */ \
DEFAULT_PRS_CH /* PRS channel 0 selected as S1IN. */ \
}
#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) || defined(_SILICON_LABS_32B_SERIES_2)
/** Filter initialization structure */
typedef struct {
/** Used only in OVSINGLE and OVSQUAD1X-4X modes. To use this, enable filter by
* setting filter to true during PCNT_Init(). Filter length = (filtLen + 5) LFACLK cycles. */
uint8_t filtLen;
/** When set, removes flutter from Quaddecoder inputs S0IN and S1IN.
* Available only in OVSQUAD1X-4X modes. */
bool flutterrm;
} PCNT_Filter_TypeDef;
#endif
/** Default configuration for PCNT initialization structure. */
#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) || defined(_SILICON_LABS_32B_SERIES_2)
#define PCNT_FILTER_DEFAULT \
{ \
0, /* Default length is 5 LFACLK cycles. */ \
false /* No flutter removal. */ \
}
#endif
#if defined(PCNT_CTRL_TCCMODE_DEFAULT)
/** Modes for Triggered Compare and Clear module. */
typedef enum {
/** Triggered compare and clear not enabled. */
tccModeDisabled = _PCNT_CTRL_TCCMODE_DISABLED,
/** Compare and clear performed on each (optionally prescaled) LFA clock cycle. */
tccModeLFA = _PCNT_CTRL_TCCMODE_LFA,
/** Compare and clear performed on PRS edges. Polarity defined by prsPolarity. */
tccModePRS = _PCNT_CTRL_TCCMODE_PRS
} PCNT_TCCMode_TypeDef;
/** Prescaler values for LFA compare and clear events. Only has effect when TCC mode is LFA. */
typedef enum {
/** Compare and clear event each LFA cycle. */
tccPrescDiv1 = _PCNT_CTRL_TCCPRESC_DIV1,
/** Compare and clear event every other LFA cycle. */
tccPrescDiv2 = _PCNT_CTRL_TCCPRESC_DIV2,
/** Compare and clear event every 4th LFA cycle. */
tccPrescDiv4 = _PCNT_CTRL_TCCPRESC_DIV4,
/** Compare and clear event every 8th LFA cycle. */
tccPrescDiv8 = _PCNT_CTRL_TCCPRESC_DIV8
} PCNT_TCCPresc_Typedef;
/** Compare modes for TCC module. */
typedef enum {
/** Compare match if PCNT_CNT is less than, or equal to PCNT_TOP. */
tccCompLTOE = _PCNT_CTRL_TCCCOMP_LTOE,
/** Compare match if PCNT_CNT is greater than or equal to PCNT_TOP. */
tccCompGTOE = _PCNT_CTRL_TCCCOMP_GTOE,
/** Compare match if PCNT_CNT is less than, or equal to PCNT_TOP[15:8]], and greater
* than, or equal to PCNT_TOP[7:0]. */
tccCompRange = _PCNT_CTRL_TCCCOMP_RANGE
} PCNT_TCCComp_Typedef;
/** TCC initialization structure. */
typedef struct {
/** Mode to operate in. */
PCNT_TCCMode_TypeDef mode;
/** Prescaler value for LFACLK in LFA mode. */
PCNT_TCCPresc_Typedef prescaler;
/** Choose the event that will trigger a clear. */
PCNT_TCCComp_Typedef compare;
/** PRS input to TCC module, either for gating the PCNT clock, triggering the TCC comparison, or both. */
PCNT_PRSSel_TypeDef tccPRS;
/** TCC PRS input polarity. @n
* False = Rising edge for comparison trigger, and PCNT clock gated when PRS signal is high. @n
* True = Falling edge for comparison trigger, and PCNT clock gated when PRS signal is low. */
bool prsPolarity;
/** Enable gating PCNT input clock through TCC PRS signal.
* Polarity selection is done through prsPolarity. */
bool prsGateEnable;
} PCNT_TCC_TypeDef;
/** TCC Default. */
#define PCNT_TCC_DEFAULT \
{ \
tccModeDisabled, /* Disabled by default. */ \
tccPrescDiv1, /* Do not prescale LFA clock in LFA mode. */ \
tccCompLTOE, /* Clear when CNT <= TOP. */ \
pcntPRSCh0, /* Select PRS channel 0 as input to TCC. */ \
false, /* PRS polarity is rising edge, and gate when 1. */ \
false /* Do not gate PCNT counter input. */ \
}
#endif
/* defined(PCNT_CTRL_TCCMODE_DEFAULT) */
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Get the pulse counter value.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @return
* Current pulse counter value.
******************************************************************************/
__STATIC_INLINE uint32_t PCNT_CounterGet(PCNT_TypeDef *pcnt)
{
return pcnt->CNT;
}
#if defined(_PCNT_AUXCNT_MASK)
/***************************************************************************//**
* @brief
* Get the auxiliary counter value.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @return
* Current auxiliary counter value.
******************************************************************************/
__STATIC_INLINE uint32_t PCNT_AuxCounterGet(PCNT_TypeDef *pcnt)
{
return pcnt->AUXCNT;
}
#endif
void PCNT_CounterReset(PCNT_TypeDef *pcnt);
void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top);
/***************************************************************************//**
* @brief
* Set a counter value.
*
* @details
* Pulse counter is disabled while changing counter value and re-enabled
* (if originally enabled) when counter value has been set.
*
* @note
* This function will stall until synchronization to low-frequency domain is
* completed. For that reason, it should normally not be used when using
* an external clock to clock the PCNT module since stall time may be
* undefined in that case. The counter should normally only be set when
* operating in (or about to enable) #pcntModeOvsSingle mode.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @param[in] count
* Value to set in counter register.
******************************************************************************/
__STATIC_INLINE void PCNT_CounterSet(PCNT_TypeDef *pcnt, uint32_t count)
{
PCNT_CounterTopSet(pcnt, count, pcnt->TOP);
}
void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode);
bool PCNT_IsEnabled(PCNT_TypeDef *pcnt);
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable);
#endif
void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init);
#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) || defined(_SILICON_LABS_32B_SERIES_2)
void PCNT_FilterConfiguration(PCNT_TypeDef *pcnt, const PCNT_Filter_TypeDef *config, bool enable);
#endif
#if defined(_PCNT_INPUT_MASK) || defined(_SILICON_LABS_32B_SERIES_2)
void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
PCNT_PRSInput_TypeDef prsInput,
bool enable);
#endif
#if defined(PCNT_CTRL_TCCMODE_DEFAULT)
void PCNT_TCCConfiguration(PCNT_TypeDef *pcnt, const PCNT_TCC_TypeDef *config);
#endif
/***************************************************************************//**
* @brief
* Clear one or more pending PCNT interrupts.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @param[in] flags
* Pending PCNT interrupt source to clear. Use a bitwise logic OR combination
* of valid interrupt flags for the PCNT module (PCNT_IF_nnn).
******************************************************************************/
__STATIC_INLINE void PCNT_IntClear(PCNT_TypeDef *pcnt, uint32_t flags)
{
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
pcnt->IFC = flags;
#else
pcnt->IF_CLR = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more PCNT interrupts.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @param[in] flags
* PCNT interrupt sources to disable. Use a bitwise logic OR combination of
* valid interrupt flags for PCNT module (PCNT_IF_nnn).
******************************************************************************/
__STATIC_INLINE void PCNT_IntDisable(PCNT_TypeDef *pcnt, uint32_t flags)
{
#if defined(PCNT_HAS_SET_CLEAR)
pcnt->IEN_CLR = flags;
#else
pcnt->IEN &= ~flags;
#endif
}
/***************************************************************************//**
* @brief
* Enable one or more PCNT interrupts.
*
* @note
* Depending on the use, a pending interrupt may already be set prior to
* enabling the interrupt. To ignore a pending interrupt, consider using
* PCNT_IntClear() prior to enabling the interrupt.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @param[in] flags
* PCNT interrupt sources to enable. Use a bitwise logic OR combination of
* valid interrupt flags for PCNT module (PCNT_IF_nnn).
******************************************************************************/
__STATIC_INLINE void PCNT_IntEnable(PCNT_TypeDef *pcnt, uint32_t flags)
{
#if defined(PCNT_HAS_SET_CLEAR)
pcnt->IEN_SET = flags;
#else
pcnt->IEN |= flags;
#endif
}
/***************************************************************************//**
* @brief
* Get pending PCNT interrupt flags.
*
* @note
* The event bits are not cleared by the use of this function.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @return
* PCNT interrupt sources pending. A bitwise logic OR combination of valid
* interrupt flags for PCNT module (PCNT_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t PCNT_IntGet(PCNT_TypeDef *pcnt)
{
return pcnt->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending PCNT interrupt flags.
*
* @details
* Useful for handling more interrupt sources in the same interrupt handler.
*
* @note
* The event bits are not cleared by the use of this function.
*
* @param[in] pcnt
* Pointer to thePCNT peripheral register block.
*
* @return
* Pending and enabled PCNT interrupt sources.
* The return value is the bitwise AND combination of
* - the OR combination of enabled interrupt sources in PCNT_IEN_nnn
* register (PCNT_IEN_nnn) and
* - the OR combination of valid interrupt flags of the PCNT module
* (PCNT_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t PCNT_IntGetEnabled(PCNT_TypeDef *pcnt)
{
uint32_t ien;
/* Store pcnt->IEN in temporary variable in order to define explicit order
* of volatile accesses. */
ien = pcnt->IEN;
/* Bitwise AND of pending and enabled interrupts. */
return pcnt->IF & ien;
}
/***************************************************************************//**
* @brief
* Set one or more pending PCNT interrupts from SW.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @param[in] flags
* PCNT interrupt sources to set to pending. Use a bitwise logic OR combination
* of valid interrupt flags for PCNT module (PCNT_IF_nnn).
******************************************************************************/
__STATIC_INLINE void PCNT_IntSet(PCNT_TypeDef *pcnt, uint32_t flags)
{
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
pcnt->IFS = flags;
#else
pcnt->IF_SET = flags;
#endif
}
#if defined(_PCNT_LOCK_MASK)
/***************************************************************************//**
* @brief
* Lock PCNT registers.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @note When PCNT registers are locked PCNT_CFG, PCNT_EN, PCNT_SWRST, PCNT_CMD,
* PCNT_CTRL, PCNT_OVSCTRL, PCNT_CNT, PCNT_TOP, and PCNT_TOPB registers
* cannot be written to.
******************************************************************************/
__STATIC_INLINE void PCNT_Lock(PCNT_TypeDef *pcnt)
{
pcnt->LOCK = ~PCNT_LOCK_PCNTLOCKKEY_UNLOCK;
}
#endif
#if defined(_PCNT_LOCK_MASK)
/***************************************************************************//**
* @brief
* Unlock PCNT registers.
*
* @param[in] pcnt
* Pointer to thePCNT peripheral register block.
******************************************************************************/
__STATIC_INLINE void PCNT_Unlock(PCNT_TypeDef *pcnt)
{
pcnt->LOCK = PCNT_LOCK_PCNTLOCKKEY_UNLOCK;
}
#endif
void PCNT_Reset(PCNT_TypeDef *pcnt);
/***************************************************************************//**
* @brief
* Get the pulse counter top buffer value.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @return
* Current pulse counter top buffer value.
******************************************************************************/
__STATIC_INLINE uint32_t PCNT_TopBufferGet(PCNT_TypeDef *pcnt)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
while (pcnt->SYNCBUSY & PCNT_SYNCBUSY_TOPB) {
}
#endif
return pcnt->TOPB;
}
void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val);
/***************************************************************************//**
* @brief
* Get the pulse counter top value.
*
* @param[in] pcnt
* Pointer to the PCNT peripheral register block.
*
* @return
* Current pulse counter top value.
******************************************************************************/
__STATIC_INLINE uint32_t PCNT_TopGet(PCNT_TypeDef *pcnt)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
while (pcnt->SYNCBUSY & PCNT_SYNCBUSY_TOP) {
}
#endif
return pcnt->TOP;
}
void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val);
/***************************************************************************//**
* @brief
* Wait for an ongoing sync of register(s) to low-frequency domain to complete.
*
* @param[in] pcnt
* A pointer to the PCNT 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 PCNT_Sync(PCNT_TypeDef *pcnt, uint32_t mask)
{
/* Avoid deadlock if modifying the same register twice when freeze mode is
* activated. */
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
if (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE) {
return;
}
#endif
/* Wait for any pending previous write operation to have been completed in
* low-frequency domain. */
while (pcnt->SYNCBUSY & mask) {
}
}
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Start the main PCNT counter.
*
* @details
* This function will send a start command to the PCNT peripheral. The PCNT
* peripheral will use some LF clock ticks before the command is executed.
* The @ref PCNT_Sync() function can be used to wait for the start command
* to be executed.
*
* @param[in] pcnt
* A pointer to the PCNT peripheral register block.
*
* @note
* This function requires the PCNT to be enabled.
******************************************************************************/
__STATIC_INLINE void PCNT_StartMainCnt(PCNT_TypeDef *pcnt)
{
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
pcnt->CMD_SET = PCNT_CMD_STARTCNT;
}
/***************************************************************************//**
* @brief
* Stop the main PCNT counter.
*
* @details
* This function will send a stop command to the PCNT peripheral. The PCNT
* peripheral will use some LF clock ticks before the command is executed.
* The @ref PCNT_Sync() function can be used to wait for the stop command
* to be executed.
*
* @param[in] pcnt
* A pointer to the PCNT peripheral register block.
*
* @note
* This function requires the PCNT to be enabled.
******************************************************************************/
__STATIC_INLINE void PCNT_StopMainCnt(PCNT_TypeDef *pcnt)
{
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
pcnt->CMD_SET = PCNT_CMD_STOPCNT;
}
/***************************************************************************//**
* @brief
* Start the auxiliary PCNT counter.
*
* @details
* This function will send a start command to the PCNT peripheral. The PCNT
* peripheral will use some LF clock ticks before the command is executed.
* The @ref PCNT_Sync() function can be used to wait for the start command
* to be executed.
*
* @param[in] pcnt
* A pointer to the PCNT peripheral register block.
*
* @note
* This function requires the PCNT to be enabled.
******************************************************************************/
__STATIC_INLINE void PCNT_StartAuxCnt(PCNT_TypeDef *pcnt)
{
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
pcnt->CMD_SET = PCNT_CMD_STARTAUXCNT;
}
/***************************************************************************//**
* @brief
* Stop the auxiliary PCNT counter.
*
* @details
* This function will send a stop command to the PCNT peripheral. The PCNT
* peripheral will use some LF clock ticks before the command is executed.
* The @ref PCNT_Sync() function can be used to wait for the stop command
* to be executed.
*
* @param[in] pcnt
* A pointer to the PCNT peripheral register block.
*
* @note
* This function requires the PCNT to be enabled.
******************************************************************************/
__STATIC_INLINE void PCNT_StopAuxCnt(PCNT_TypeDef *pcnt)
{
PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
pcnt->CMD_SET = PCNT_CMD_STOPAUXCNT;
}
#endif
/** @} (end addtogroup pcnt) */
#ifdef __cplusplus
}
#endif
#endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */
#endif /* EM_PCNT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
/***************************************************************************//**
* @file
* @brief RAM code support.
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_RAMFUNC_H
#define EM_RAMFUNC_H
#include "sl_code_classification.h"
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-OFF* */
/***************************************************************************//**
* @addtogroup ramfunc RAMFUNC - RAM Function Support
* @brief RAM code support
* @details
* Provides support for executing code from RAM.
* Provides a unified method to manage RAM code across all supported tools.
* @{
@note
Other cross-compiler support macros are implemented in [COMMON](../../common/api/group-common).
@note
Functions executing from RAM should not be declared as static.
@warning
Standard library facilities are available to the tool with GCC in hosted
mode (default), regardless of the section attribute. Calls to standard
libraries placed in the default section may therefore occur. To disable
hosted mode, add '-ffreestanding' to the build command line. This is the
only way to guarantee no calls to standard libraries with GCC.
Read more at www.gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Standards.html
@warning
Keil/ARM uVision users must add a section named "ram_code" in their linker
scatter file. This section must be in RAM memory. Look in the MCU SDK for
example scatter files (ram_code.sct).
@n @section ramfunc_usage Usage
In your .h file:
@verbatim
#include "em_ramfunc.h"
SL_RAMFUNC_DECLARATOR
void MyPrint(const char* string);
@endverbatim
Issues have been observed with ARM GCC when there is no declarator. It is
recommended to have a declarator also for internal functions but move the
declarator to the .c file.
In your .c file:
@verbatim
#include "em_ramfunc.h"
SL_RAMFUNC_DEFINITION_BEGIN
void MyPrint(const char* string)
{
...
}
SL_RAMFUNC_DEFINITION_END
@endverbatim
******************************************************************************/
/* *INDENT-ON* */
/*******************************************************************************
****************************** DEFINES ***********************************
******************************************************************************/
/**
* @brief
* This define is not present by default. By compiling with define
* @ref SL_RAMFUNC_DISABLE, code placed in RAM by SL_RAMFUNC macros
* will be placed in default code space (Flash) instead.
*
* @note
* This define is not present by default.
*/
#if defined(DOXY_DOC_ONLY)
#define SL_RAMFUNC_DISABLE
#endif
#if defined(SL_RAMFUNC_DISABLE)
/** @brief Compiler ported function declarator for RAM code. */
#define SL_RAMFUNC_DECLARATOR
/** @brief Compiler ported function definition begin marker for RAM code. */
#define SL_RAMFUNC_DEFINITION_BEGIN
/** @brief Compiler ported function definition end marker for RAM code. */
#define SL_RAMFUNC_DEFINITION_END
#elif defined(__CC_ARM)
/* MDK-ARM compiler */
#define SL_RAMFUNC_DECLARATOR
#define SL_RAMFUNC_DEFINITION_BEGIN _Pragma("arm section code=\"ram_code\"")
#define SL_RAMFUNC_DEFINITION_END _Pragma("arm section code")
#elif defined(__ICCARM__)
/* IAR Embedded Workbench */
#define SL_RAMFUNC_DECLARATOR SL_CODE_RAM
#define SL_RAMFUNC_DEFINITION_BEGIN SL_RAMFUNC_DECLARATOR
#define SL_RAMFUNC_DEFINITION_END
#elif defined(__GNUC__) && (defined(__CROSSWORKS_ARM) || defined(__SES_ARM))
/* Rowley Crossworks and Segger Embedded Studio */
#define SL_RAMFUNC_DECLARATOR SL_CODE_RAM
#define SL_RAMFUNC_DEFINITION_BEGIN SL_RAMFUNC_DECLARATOR
#define SL_RAMFUNC_DEFINITION_END
#elif defined(__GNUC__) && defined(CONFIG_SOC_FAMILY_EXX32)
/* Zephyr environment */
#define SL_RAMFUNC_DECLARATOR SL_CODE_RAM
#define SL_RAMFUNC_DEFINITION_BEGIN SL_RAMFUNC_DECLARATOR
#define SL_RAMFUNC_DEFINITION_END
#elif defined(__GNUC__)
/* Simplicity Studio, Atollic and Vanilla armgcc */
#define SL_RAMFUNC_DECLARATOR SL_CODE_RAM
#define SL_RAMFUNC_DEFINITION_BEGIN SL_RAMFUNC_DECLARATOR
#define SL_RAMFUNC_DEFINITION_END
#endif
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* Deprecated macro names */
#define RAMFUNC_DECLARATOR SL_RAMFUNC_DECLARATOR
#define RAMFUNC_DEFINITION_BEGIN SL_RAMFUNC_DEFINITION_BEGIN
#define RAMFUNC_DEFINITION_END SL_RAMFUNC_DEFINITION_END
/** @endcond */
/** @} (end addtogroup ramfunc) */
#ifdef __cplusplus
}
#endif
#endif /* EM_RAMFUNC_H */

View File

@@ -0,0 +1,178 @@
/***************************************************************************//**
* @file
* @brief Reset Management Unit (RMU) 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.
*
******************************************************************************/
#ifndef EM_RMU_H
#define EM_RMU_H
#include "em_device.h"
#if (defined(RMU_COUNT) && (RMU_COUNT > 0)) || (_EMU_RSTCTRL_MASK)
#include "sl_assert.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup rmu
* @{
******************************************************************************/
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** RMU reset modes. */
typedef enum {
#if defined(_RMU_CTRL_PINRMODE_MASK)
rmuResetModeDisabled = _RMU_CTRL_PINRMODE_DISABLED, /**< Reset mode disabled. */
rmuResetModeLimited = _RMU_CTRL_PINRMODE_LIMITED, /**< Reset mode limited. */
rmuResetModeExtended = _RMU_CTRL_PINRMODE_EXTENDED, /**< Reset mode extended. */
rmuResetModeFull = _RMU_CTRL_PINRMODE_FULL, /**< Reset mode full. */
#elif defined(_EMU_RSTCTRL_MASK)
rmuResetModeDisabled = 0, /**< Reset mode disabled. */
rmuResetModeEnabled = 1, /**< Reset mode enabled. */
#else
rmuResetModeClear = 0, /**< Reset mode clear. */
rmuResetModeSet = 1, /**< Reset mode set. */
#endif
} RMU_ResetMode_TypeDef;
/** RMU controlled peripheral reset control and reset source control. */
typedef enum {
#if defined(RMU_CTRL_BURSTEN)
rmuResetBU = _RMU_CTRL_BURSTEN_MASK, /**< Reset control over Backup Power domain select. */
#endif
#if defined(RMU_CTRL_LOCKUPRDIS)
rmuResetLockUp = _RMU_CTRL_LOCKUPRDIS_MASK, /**< Cortex lockup reset select. */
#elif defined(_RMU_CTRL_LOCKUPRMODE_MASK)
rmuResetLockUp = _RMU_CTRL_LOCKUPRMODE_MASK, /**< Cortex lockup reset select. */
#endif
#if defined(_RMU_CTRL_WDOGRMODE_MASK)
rmuResetWdog = _RMU_CTRL_WDOGRMODE_MASK, /**< WDOG reset select. */
#endif
#if defined(_RMU_CTRL_LOCKUPRMODE_MASK)
rmuResetCoreLockup = _RMU_CTRL_LOCKUPRMODE_MASK, /**< Cortex lockup reset select. */
#endif
#if defined(_RMU_CTRL_SYSRMODE_MASK)
rmuResetSys = _RMU_CTRL_SYSRMODE_MASK, /**< SYSRESET select. */
#endif
#if defined(_RMU_CTRL_PINRMODE_MASK)
rmuResetPin = _RMU_CTRL_PINRMODE_MASK, /**< Pin reset select. */
#endif
#if defined(_EMU_RSTCTRL_WDOG0RMODE_MASK)
rmuResetWdog0 = _EMU_RSTCTRL_WDOG0RMODE_MASK, /**< WDOG0 reset select */
#endif
#if defined(_EMU_RSTCTRL_WDOG1RMODE_MASK)
rmuResetWdog1 = _EMU_RSTCTRL_WDOG1RMODE_MASK, /**< WDOG1 reset select */
#endif
#if defined(_EMU_RSTCTRL_SYSRMODE_MASK)
rmuResetSys = _EMU_RSTCTRL_SYSRMODE_MASK, /**< SYSRESET select */
#endif
#if defined(_EMU_RSTCTRL_LOCKUPRMODE_MASK)
rmuResetCoreLockup = _EMU_RSTCTRL_LOCKUPRMODE_MASK, /**< Cortex lockup reset select */
#endif
#if defined(_EMU_RSTCTRL_AVDDBODRMODE_MASK)
rmuResetAVDD = _EMU_RSTCTRL_AVDDBODRMODE_MASK, /**< AVDD monitoring select */
#endif
#if defined(_EMU_RSTCTRL_IOVDD0BODRMODE_MASK)
rmuResetIOVDD0 = _EMU_RSTCTRL_IOVDD0BODRMODE_MASK, /**< IOVDD0 monitoring select */
#endif
#if defined(_EMU_RSTCTRL_IOVDD1BODRMODE_MASK)
rmuResetIOVDD1 = _EMU_RSTCTRL_IOVDD1BODRMODE_MASK, /**< IOVDD1 monitoring select */
#endif
#if defined(_EMU_RSTCTRL_IOVDD2BODRMODE_MASK)
rmuResetIOVDD2 = _EMU_RSTCTRL_IOVDD2BODRMODE_MASK, /**< IOVDD2 monitoring select */
#endif
#if defined(_EMU_RSTCTRL_DECBODRMODE_MASK)
rmuResetDecouple = _EMU_RSTCTRL_DECBODRMODE_MASK, /**< Decouple monitoring select */
#endif
#if defined(_EMU_RSTCTRL_SESYSRMODE_MASK)
rmuResetSESys = _EMU_RSTCTRL_SESYSRMODE_MASK, /**< M0+ (SE) system reset select */
#endif
#if defined(_EMU_RSTCTRL_SELOCKUPRMODE_MASK)
rmuResetSELockup = _EMU_RSTCTRL_SELOCKUPRMODE_MASK, /**< M0+ (SE) lockup select */
#endif
#if defined(_EMU_RSTCTRL_DCIRMODE_MASK)
rmuResetDCI = _EMU_RSTCTRL_DCIRMODE_MASK, /**< DCI reset select */
#endif
} RMU_Reset_TypeDef;
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
/** RMU_LockupResetDisable kept for backwards compatibility. */
#define RMU_LockupResetDisable(A) RMU_ResetControl(rmuResetLockUp, A)
void RMU_ResetControl(RMU_Reset_TypeDef reset, RMU_ResetMode_TypeDef mode);
void RMU_ResetCauseClear(void);
uint32_t RMU_ResetCauseGet(void);
#if defined(_RMU_CTRL_RESETSTATE_MASK)
/***************************************************************************//**
* @brief
* Set user reset state. Reset only by a Power-on-reset and a pin reset.
*
* @param[in] userState User state to set
******************************************************************************/
__STATIC_INLINE void RMU_UserResetStateSet(uint32_t userState)
{
EFM_ASSERT(!(userState
& ~(_RMU_CTRL_RESETSTATE_MASK >> _RMU_CTRL_RESETSTATE_SHIFT)));
RMU->CTRL = (RMU->CTRL & ~_RMU_CTRL_RESETSTATE_MASK)
| (userState << _RMU_CTRL_RESETSTATE_SHIFT);
}
/***************************************************************************//**
* @brief
* Get user reset state. Reset only by a Power-on-reset and a pin reset.
*
* @return
* Reset surviving user state.
******************************************************************************/
__STATIC_INLINE uint32_t RMU_UserResetStateGet(void)
{
uint32_t userState = (RMU->CTRL & _RMU_CTRL_RESETSTATE_MASK)
>> _RMU_CTRL_RESETSTATE_SHIFT;
return userState;
}
#endif
/** @} (end addtogroup rmu) */
#ifdef __cplusplus
}
#endif
#endif /* defined(RMU_COUNT) && (RMU_COUNT > 0) */
#endif /* EM_RMU_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
/***************************************************************************//**
* @file
* @brief API defining acces to SYSCFG registers
*******************************************************************************
* # License
* <b>Copyright 2022 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.
*
******************************************************************************/
#ifndef EM_SYSCFG_H
#define EM_SYSCFG_H
#include "em_device.h"
#if defined(SL_TRUSTZONE_NONSECURE)
#include "sli_tz_service_syscfg.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(SYSCFG)
/*******************************************************************************
******************************** TZ SERVICES **********************************
******************************************************************************/
#if defined(_SYSCFG_CHIPREV_FAMILY_MASK) || defined(_SYSCFG_CHIPREV_PARTNUMBER_MASK)
/*******************************************************************************
* @brief Reads CHIPREV register
******************************************************************************/
__STATIC_INLINE uint32_t SYSCFG_readChipRev(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
return sli_tz_syscfg_read_chiprev_register();
#else
#if defined(CMU_CLKEN0_SYSCFG)
CMU->CLKEN0_SET = CMU_CLKEN0_SYSCFG;
#endif
return SYSCFG->CHIPREV;
#endif
}
#endif // defined(_SYSCFG_CHIPREV_FAMILY_MASK) || defined(_SYSCFG_CHIPREV_PARTNUMBER_MASK)
#if defined(_SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK)
/*******************************************************************************
* @brief Sets DMEM0RAMCTRL RAMWSEN bit to 1
******************************************************************************/
__STATIC_INLINE void SYSCFG_setDmem0RamCtrlRamwsenBit(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
sli_tz_syscfg_set_dmem0ramctrl_ramwsen_bit();
#else
SYSCFG->DMEM0RAMCTRL = SYSCFG->DMEM0RAMCTRL | _SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK;
#endif
}
/*******************************************************************************
* @brief Clears DMEM0RAMCTRL RAMWSEN bit to 0
******************************************************************************/
__STATIC_INLINE void SYSCFG_clearDmem0RamCtrlRamwsenBit(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
sli_tz_syscfg_clear_dmem0ramctrl_ramwsen_bit();
#else
SYSCFG->DMEM0RAMCTRL = SYSCFG->DMEM0RAMCTRL & ~_SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK;
#endif
}
/*******************************************************************************
* @brief Reads DMEM0RAMCTRL RAMWSEN bit
******************************************************************************/
__STATIC_INLINE uint32_t SYSCFG_getDmem0RamCtrlRamwsenBit(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
return sli_tz_syscfg_get_dmem0ramctrl_ramwsen_bit();
#else
return (SYSCFG->DMEM0RAMCTRL & _SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK) >> _SYSCFG_DMEM0RAMCTRL_RAMWSEN_SHIFT;
#endif
}
#endif //_SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK
#if defined(_SYSCFG_DMEM0RETNCTRL_MASK)
/*******************************************************************************
* @brief Reads DMEM0RETNCTRL register
******************************************************************************/
__STATIC_INLINE uint32_t SYSCFG_readDmem0RetnCtrl(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
return sli_tz_syscfg_read_dmem0retnctrl_register();
#else
return SYSCFG->DMEM0RETNCTRL;
#endif
}
/*******************************************************************************
* @brief Mask DMEM0RETNCTRL register with provided mask
*
* @param mask - A mask that is to be used to mask the DMEM0RETNCTRL register
******************************************************************************/
__STATIC_INLINE void SYSCFG_maskDmem0RetnCtrl(uint32_t mask)
{
#if defined(SL_TRUSTZONE_NONSECURE)
sli_tz_syscfg_mask_dmem0retnctrl_register(mask);
#else
SYSCFG->DMEM0RETNCTRL = SYSCFG->DMEM0RETNCTRL | mask;
#endif
}
/*******************************************************************************
* @brief Set DMEM0RETNCTRL to zero
******************************************************************************/
__STATIC_INLINE void SYSCFG_zeroDmem0RetnCtrl(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
sli_tz_syscfg_zero_dmem0retnctrl_register();
#else
SYSCFG->DMEM0RETNCTRL = 0x0UL;
#endif
}
#endif // _SYSCFG_DMEM0RETNCTRL_MASK
#if defined(_SYSCFG_CFGSYSTIC_MASK)
/*******************************************************************************
* @brief Set SYSTICEXTCLKEN bit in CFGSYSTIC to one
******************************************************************************/
__STATIC_INLINE void SYSCFG_setSysTicExtClkEnCfgSysTic(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
sli_tz_syscfg_set_systicextclken_cfgsystic();
#else
SYSCFG->CFGSYSTIC = (SYSCFG->CFGSYSTIC | _SYSCFG_CFGSYSTIC_SYSTICEXTCLKEN_MASK);
#endif
}
/*******************************************************************************
* @brief Clear SYSTICEXTCLKEN bit in CFGSYSTIC to zero
******************************************************************************/
__STATIC_INLINE void SYSCFG_clearSysTicExtClkEnCfgSysTic(void)
{
#if defined(SL_TRUSTZONE_NONSECURE)
sli_tz_syscfg_clear_systicextclken_cfgsystic();
#else
SYSCFG->CFGSYSTIC = (SYSCFG->CFGSYSTIC & ~_SYSCFG_CFGSYSTIC_SYSTICEXTCLKEN_MASK);
#endif
}
#endif //_SYSCFG_CFGSYSTIC_MASK
#endif //SYSCFG
#ifdef __cplusplus
}
#endif
#endif // EM_SYSCFG_H

View File

@@ -0,0 +1,365 @@
/***************************************************************************//**
* @file
* @brief System 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.
*
******************************************************************************/
#ifndef EM_SYSTEM_H
#define EM_SYSTEM_H
#include "em_device.h"
#include "em_system_generic.h"
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup system SYSTEM - System Utils
* @brief System API
* @details
* This module contains functions to read information such as RAM and Flash size,
* device unique ID, chip revision, family, and part number from DEVINFO and
* SCB blocks. Functions to configure and read status from FPU are available for
* compatible devices.
* @{
******************************************************************************/
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Family identifiers. */
typedef enum {
/* New style family #defines */
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32G)
systemPartFamilyEfm32Gecko = _DEVINFO_PART_DEVICE_FAMILY_EFM32G, /**< EFM32 Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32GG)
systemPartFamilyEfm32Giant = _DEVINFO_PART_DEVICE_FAMILY_EFM32GG, /**< EFM32 Giant Gecko Series 0 Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32GG11B)
systemPartFamilyEfm32Giant11B = _DEVINFO_PART_DEVICE_FAMILY_EFM32GG11B, /**< EFM32 Giant Gecko Series 1 Configuration 1 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32GG12B)
systemPartFamilyEfm32Giant12B = _DEVINFO_PART_DEVICE_FAMILY_EFM32GG12B, /**< EFM32 Giant Gecko Series 1 Configuration 2 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32TG)
systemPartFamilyEfm32Tiny = _DEVINFO_PART_DEVICE_FAMILY_EFM32TG, /**< EFM32 Tiny Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32TG11B)
systemPartFamilyEfm32Tiny11B = _DEVINFO_PART_DEVICE_FAMILY_EFM32TG11B, /**< EFM32 Tiny Gecko 11 Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32LG)
systemPartFamilyEfm32Leopard = _DEVINFO_PART_DEVICE_FAMILY_EFM32LG, /**< EFM32 Leopard Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32WG)
systemPartFamilyEfm32Wonder = _DEVINFO_PART_DEVICE_FAMILY_EFM32WG, /**< EFM32 Wonder Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32ZG)
systemPartFamilyEfm32Zero = _DEVINFO_PART_DEVICE_FAMILY_EFM32ZG, /**< EFM32 Zero Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32HG)
systemPartFamilyEfm32Happy = _DEVINFO_PART_DEVICE_FAMILY_EFM32HG, /**< EFM32 Happy Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32PG1B)
systemPartFamilyEfm32Pearl1B = _DEVINFO_PART_DEVICE_FAMILY_EFM32PG1B, /**< EFM32 Pearl Gecko Series 1 Configuration 1 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32JG1B)
systemPartFamilyEfm32Jade1B = _DEVINFO_PART_DEVICE_FAMILY_EFM32JG1B, /**< EFM32 Jade Gecko Series 1 Configuration 1 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32PG12B)
systemPartFamilyEfm32Pearl12B = _DEVINFO_PART_DEVICE_FAMILY_EFM32PG12B, /**< EFM32 Pearl Gecko Series 1 Configuration 2 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32JG12B)
systemPartFamilyEfm32Jade12B = _DEVINFO_PART_DEVICE_FAMILY_EFM32JG12B, /**< EFM32 Jade Gecko Series 1 Configuration 2 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EZR32WG)
systemPartFamilyEzr32Wonder = _DEVINFO_PART_DEVICE_FAMILY_EZR32WG, /**< EZR32 Wonder Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EZR32LG)
systemPartFamilyEzr32Leopard = _DEVINFO_PART_DEVICE_FAMILY_EZR32LG, /**< EZR32 Leopard Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EZR32HG)
systemPartFamilyEzr32Happy = _DEVINFO_PART_DEVICE_FAMILY_EZR32HG, /**< EZR32 Happy Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG1P)
systemPartFamilyMighty1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG1P, /**< EFR32 Mighty Gecko Series 1 Configuration 1 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG1B)
systemPartFamilyMighty1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG1B, /**< EFR32 Mighty Gecko Series 1 Configuration 1 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG1V)
systemPartFamilyMighty1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG1V, /**< EFR32 Mighty Gecko Series 1 Configuration 1 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG1P)
systemPartFamilyBlue1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG1P, /**< EFR32 Blue Gecko Series 1 Configuration 1 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG1B)
systemPartFamilyBlue1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG1B, /**< EFR32 Blue Gecko Series 1 Configuration 1 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG1V)
systemPartFamilyBlue1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG1V, /**< EFR32 Blue Gecko Series 1 Configuration 1 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG1P)
systemPartFamilyFlex1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG1P, /**< EFR32 Flex Gecko Series 1 Configuration 1 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG1B)
systemPartFamilyFlex1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG1B, /**< EFR32 Flex Gecko Series 1 Configuration 1 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG1V)
systemPartFamilyFlex1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG1V, /**< EFR32 Flex Gecko Series 1 Configuration 1 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG12P)
systemPartFamilyMighty12P = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG12P, /**< EFR32 Mighty Gecko Series 1 Configuration 2 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG12B)
systemPartFamilyMighty12B = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG12B, /**< EFR32 Mighty Gecko Series 1 Configuration 2 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG12V)
systemPartFamilyMighty12V = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG12V, /**< EFR32 Mighty Gecko Series 1 Configuration 2 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG12P)
systemPartFamilyBlue12P = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG12P, /**< EFR32 Blue Gecko Series 1 Configuration 2 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG12B)
systemPartFamilyBlue12B = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG12B, /**< EFR32 Blue Gecko Series 1 Configuration 2 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG12V)
systemPartFamilyBlue12V = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG12V, /**< EFR32 Blue Gecko Series 1 Configuration 2 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG12P)
systemPartFamilyFlex12P = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG12P, /**< EFR32 Flex Gecko Series 1 Configuration 2 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG12B)
systemPartFamilyFlex12B = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG12B, /**< EFR32 Flex Gecko Series 1 Configuration 2 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG12V)
systemPartFamilyFlex12V = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG12V, /**< EFR32 Flex Gecko Series 1 Configuration 2 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG13P)
systemPartFamilyMighty13P = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG13P, /**< EFR32 Mighty Gecko Series 1 Configuration 3 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG13B)
systemPartFamilyMighty13B = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG13B, /**< EFR32 Mighty Gecko Series 1 Configuration 3 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG13V)
systemPartFamilyMighty13V = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG13V, /**< EFR32 Mighty Gecko Series 1 Configuration 3 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG13P)
systemPartFamilyBlue13P = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG13P, /**< EFR32 Blue Gecko Series 1 Configuration 3 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG13B)
systemPartFamilyBlue13B = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG13B, /**< EFR32 Blue Gecko Series 1 Configuration 3 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG13V)
systemPartFamilyBlue13V = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG13V, /**< EFR32 Blue Gecko Series 1 Configuration 3 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG13P)
systemPartFamilyFlex13P = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG13P, /**< EFR32 Flex Gecko Series 1 Configuration 3 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG13B)
systemPartFamilyFlex13B = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG13B, /**< EFR32 Flex Gecko Series 1 Configuration 3 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG13V)
systemPartFamilyFlex13V = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG13V, /**< EFR32 Flex Gecko Series 1 Configuration 3 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32ZG13P)
systemPartFamilyZen13P = _DEVINFO_PART_DEVICE_FAMILY_EFR32ZG13P, /**< EFR32 Zen Gecko Series 1 Configuration 3 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32ZG13L)
systemPartFamilyZen13L = _DEVINFO_PART_DEVICE_FAMILY_EFR32ZG13L, /**< EFR32 Zen Gecko Series 1 Configuration 3 Led Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32ZG13S)
systemPartFamilyZen13S = _DEVINFO_PART_DEVICE_FAMILY_EFR32ZG13S, /**< EFR32 Zen Gecko Series 1 Configuration 3 Sensor Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG14P)
systemPartFamilyMighty14P = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG14P, /**< EFR32 Mighty Gecko Series 1 Configuration 4 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG14B)
systemPartFamilyMighty14B = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG14B, /**< EFR32 Mighty Gecko Series 1 Configuration 4 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG14V)
systemPartFamilyMighty14V = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG14V, /**< EFR32 Mighty Gecko Series 1 Configuration 4 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG14P)
systemPartFamilyFlex14P = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG14P, /**< EFR32 Flex Gecko Series 1 Configuration 4 Premium Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG14B)
systemPartFamilyFlex14B = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG14B, /**< EFR32 Flex Gecko Series 1 Configuration 4 Basic Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG14V)
systemPartFamilyFlex14V = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG14V, /**< EFR32 Flex Gecko Series 1 Configuration 4 Value Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32ZG14P)
systemPartFamilyZen14P = _DEVINFO_PART_DEVICE_FAMILY_EFR32ZG14P, /**< EFR32 Zen Gecko Series 1 Configuration 4 Premium Device Family. */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
systemPartFamilyMighty21 = DEVINFO_PART_FAMILY_MG | (21 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty Gecko Series 2 Config 1 Value Device Family */
systemPartFamilyFlex21 = DEVINFO_PART_FAMILY_FG | (21 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Flex Gecko Series 2 Config 1 Value Device Family */
systemPartFamilyBlue21 = DEVINFO_PART_FAMILY_BG | (21 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Blue Gecko Series 2 Config 1 Value Device Family */
systemPartFamilyMightyRcp21 = DEVINFO_PART_FAMILY_MR | (21 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty RCP Series 2 Config 1 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
systemPartFamilyMighty22 = DEVINFO_PART_FAMILY_MG | (22 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty Gecko Series 2 Config 2 Value Device Family */
systemPartFamilyFlex22 = DEVINFO_PART_FAMILY_FG | (22 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Flex Gecko Series 2 Config 2 Value Device Family */
systemPartFamilyBlue22 = DEVINFO_PART_FAMILY_BG | (22 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Blue Gecko Series 2 Config 2 Value Device Family */
systemPartFamilyEfm32Pearl22 = DEVINFO_PART_FAMILY_PG | (22 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFM32 Pearl Gecko Series 2 Config 2 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
systemPartFamilyFlex23 = DEVINFO_PART_FAMILY_FG | (23 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Flex Gecko Series 2 Config 3 Value Device Family */
systemPartFamilyZen23 = DEVINFO_PART_FAMILY_ZG | (23 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Zen Gecko Series 2 Config 3 Value Device Family */
systemPartFamilyEfm32Pearl23 = DEVINFO_PART_FAMILY_PG | (23 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFM32 Pearl Gecko Series 2 Config 3 Value Device Family */
systemPartFamilySideWalk23 = DEVINFO_PART_FAMILY_SG | (23 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Side Walk Gecko Series 2 Config 3 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_4)
systemPartFamilyMighty24 = DEVINFO_PART_FAMILY_MG | (24 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty Gecko Series 2 Config 4 Value Device Family */
systemPartFamilyFlex24 = DEVINFO_PART_FAMILY_FG | (24 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Flex Gecko Series 2 Config 4 Value Device Family */
systemPartFamilyBlue24 = DEVINFO_PART_FAMILY_BG | (24 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Blue Gecko Series 2 Config 4 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_5)
systemPartFamilyFlex25 = DEVINFO_PART_FAMILY_FG | (25 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Flex Gecko Series 2 Config 5 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_6)
systemPartFamilyMighty26 = DEVINFO_PART_FAMILY_MG | (26 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty Gecko Series 2 Config 6 Value Device Family */
systemPartFamilyBlue26 = DEVINFO_PART_FAMILY_BG | (26 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Blue Gecko Series 2 Config 6 Value Device Family */
systemPartFamilyEfm32Pearl26 = DEVINFO_PART_FAMILY_PG | (26 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFM32 Pearl Gecko Series 2 Config 6 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
systemPartFamilyMighty27 = DEVINFO_PART_FAMILY_MG | (27 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty Gecko Series 2 Config 7 Value Device Family */
systemPartFamilyBlue27 = DEVINFO_PART_FAMILY_BG | (27 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Blue Gecko Series 2 Config 7 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_8)
systemPartFamilyFlex28 = DEVINFO_PART_FAMILY_FG | (28 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Flex Gecko Series 2 Config 8 Value Device Family */
systemPartFamilyZen28 = DEVINFO_PART_FAMILY_ZG | (28 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Zen Gecko Series 2 Config 8 Value Device Family */
systemPartFamilySideWalk28 = DEVINFO_PART_FAMILY_SG | (28 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Side Walk Gecko Series 2 Config 8 Value Device Family */
systemPartFamilyEfm32Pearl28 = DEVINFO_PART_FAMILY_PG | (28 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFM32 Pearl Gecko Series 2 Config 8 Value Device Family */
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_9)
systemPartFamilyMighty29 = DEVINFO_PART_FAMILY_MG | (29 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Mighty Gecko Series 2 Config 9 Value Device Family */
systemPartFamilyBlue29 = DEVINFO_PART_FAMILY_BG | (29 << _DEVINFO_PART_FAMILYNUM_SHIFT), /**< EFR32 Blue Gecko Series 2 Config 9 Value Device Family */
#endif
/* Deprecated family #defines */
#if defined(_DEVINFO_PART_DEVICE_FAMILY_G)
systemPartFamilyGecko = _DEVINFO_PART_DEVICE_FAMILY_G, /**< Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_GG)
systemPartFamilyGiant = _DEVINFO_PART_DEVICE_FAMILY_GG, /**< Giant Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_TG)
systemPartFamilyTiny = _DEVINFO_PART_DEVICE_FAMILY_TG, /**< Tiny Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_LG)
systemPartFamilyLeopard = _DEVINFO_PART_DEVICE_FAMILY_LG, /**< Leopard Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_WG)
systemPartFamilyWonder = _DEVINFO_PART_DEVICE_FAMILY_WG, /**< Wonder Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_ZG)
systemPartFamilyZero = _DEVINFO_PART_DEVICE_FAMILY_ZG, /**< Zero Gecko Device Family. */
#endif
#if defined(_DEVINFO_PART_DEVICE_FAMILY_HG)
systemPartFamilyHappy = _DEVINFO_PART_DEVICE_FAMILY_HG, /**< Happy Gecko Device Family. */
#endif
systemPartFamilyUnknown = 0xFF /**< Unknown Device Family.
Family ID is missing
on unprogrammed parts. */
} SYSTEM_PartFamily_TypeDef;
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** Chip revision details. */
typedef struct {
uint8_t minor; /**< Minor revision number. */
uint8_t major; /**< Major revision number. */
#if defined(_SYSCFG_CHIPREV_PARTNUMBER_MASK)
uint16_t partNumber; /**< Device part number. */
#else
uint8_t family; /**< Device family number. */
#endif
} SYSTEM_ChipRevision_TypeDef;
#if defined(__FPU_PRESENT) && (__FPU_PRESENT == 1)
/** Floating point co-processor access modes. */
typedef enum {
fpuAccessDenied = (0x0 << 20), /**< Access denied, any attempted access generates a NOCP UsageFault. */
fpuAccessPrivilegedOnly = (0x5 << 20), /**< Privileged access only, an unprivileged access generates a NOCP UsageFault. */
fpuAccessReserved = (0xA << 20), /**< Reserved. */
fpuAccessFull = (0xF << 20) /**< Full access. */
} SYSTEM_FpuAccess_TypeDef;
#endif
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
void SYSTEM_ChipRevisionGet(SYSTEM_ChipRevision_TypeDef *rev);
SYSTEM_PartFamily_TypeDef SYSTEM_GetFamily(void);
#if defined(_DEVINFO_DEVINFOREV_DEVINFOREV_MASK) || defined(_DEVINFO_INFO_DEVINFOREV_MASK)
/***************************************************************************//**
* @brief
* Get DEVINFO revision.
*
* @return
* Revision of the DEVINFO contents.
******************************************************************************/
__STATIC_INLINE uint8_t SYSTEM_GetDevinfoRev(void)
{
#if defined(_DEVINFO_DEVINFOREV_DEVINFOREV_MASK)
return (uint8_t)((DEVINFO->DEVINFOREV & _DEVINFO_DEVINFOREV_DEVINFOREV_MASK)
>> _DEVINFO_DEVINFOREV_DEVINFOREV_SHIFT);
#elif defined(_DEVINFO_INFO_DEVINFOREV_MASK)
return (uint8_t)((DEVINFO->INFO & _DEVINFO_INFO_DEVINFOREV_MASK)
>> _DEVINFO_INFO_DEVINFOREV_SHIFT);
#endif
}
#endif
#if defined(__FPU_PRESENT) && (__FPU_PRESENT == 1)
/***************************************************************************//**
* @brief
* Set floating point co-processor (FPU) access mode.
*
* @param[in] accessMode
* Floating point co-processor access mode. See @ref SYSTEM_FpuAccess_TypeDef
* for details.
******************************************************************************/
__STATIC_INLINE void SYSTEM_FpuAccessModeSet(SYSTEM_FpuAccess_TypeDef accessMode)
{
SCB->CPACR = (SCB->CPACR & ~(0xFUL << 20)) | (uint32_t)accessMode;
}
#endif
/** @} (end addtogroup system) */
#ifdef __cplusplus
}
#endif
#endif /* EM_SYSTEM_H */

View File

@@ -0,0 +1,91 @@
/***************************************************************************//**
* @file
* @brief System API (Generic)
*******************************************************************************
* # License
* <b>Copyright 2023 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.
*
******************************************************************************/
#ifndef EM_SYSTEM_GENERIC_H
#define EM_SYSTEM_GENERIC_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup system
* @{
******************************************************************************/
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Family security capability. */
typedef enum {
securityCapabilityUnknown, /**< Unknown security capability. */
securityCapabilityNA, /**< Security capability not applicable. */
securityCapabilityBasic, /**< Basic security capability. */
securityCapabilityRoT, /**< Root of Trust security capability. */
securityCapabilitySE, /**< Secure Element security capability. */
securityCapabilityVault /**< Secure Vault security capability. */
} SYSTEM_SecurityCapability_TypeDef;
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** DEVINFO calibration address/value pair. */
typedef struct {
uint32_t address; /**< Peripheral calibration register address. */
uint32_t calValue; /**< Calibration value for register at address. */
}
SYSTEM_CalAddrVal_TypeDef;
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
bool SYSTEM_GetCalibrationValue(volatile uint32_t *regAddress);
SYSTEM_SecurityCapability_TypeDef SYSTEM_GetSecurityCapability(void);
uint64_t SYSTEM_GetUnique(void);
uint8_t SYSTEM_GetProdRev(void);
uint32_t SYSTEM_GetSRAMBaseAddress(void);
uint16_t SYSTEM_GetSRAMSize(void);
uint16_t SYSTEM_GetFlashSize(void);
uint32_t SYSTEM_GetFlashPageSize(void);
uint16_t SYSTEM_GetPartNumber(void);
uint8_t SYSTEM_GetCalibrationTemperature(void);
/** @} (end addtogroup system) */
#ifdef __cplusplus
}
#endif
#endif /* EM_SYSTEM_GENERIC_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,773 @@
/***************************************************************************//**
* @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.
*
******************************************************************************/
#ifndef EM_VDAC_H
#define EM_VDAC_H
#include "em_device.h"
#if defined(VDAC_COUNT) && (VDAC_COUNT > 0)
#include "sl_assert.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup vdac VDAC - Voltage DAC
* @brief Digital to Analog Voltage Converter (VDAC) Peripheral API
*
* @details
* This module contains functions to control the VDAC peripheral of Silicon
* Labs' 32-bit MCUs and SoCs. VDAC converts digital values to analog
* signals at up to 500 ksps with 12-bit accuracy. VDAC is designed for
* low energy consumption, but can also provide very good performance.
*
* The following steps are necessary for basic operation:
*
* Clock enable:
* @code
CMU_ClockEnable(cmuClock_VDAC0, true);@endcode
*
* Initialize the VDAC with default settings and modify selected fields:
* @code
VDAC_Init_TypeDef vdacInit = VDAC_INIT_DEFAULT;
VDAC_InitChannel_TypeDef vdacChInit = VDAC_INITCHANNEL_DEFAULT;
// Set prescaler to get 1 MHz VDAC clock frequency.
vdacInit.prescaler = VDAC_PrescaleCalc(1000000, true, 0); // function call for series 0/1
VDAC_Init(VDAC0, &vdacInit);
vdacChInit.enable = true;
VDAC_InitChannel(VDAC0, &vdacChInit, 0);@endcode
*
* Perform a conversion:
* @code
VDAC_ChannelOutputSet(VDAC0, 0, 250);@endcode
*
* @note The output stage of a VDAC channel consists of an on-chip operational
* amplifier (OPAMP) in the OPAMP module. This OPAMP is highly configurable;
* and to exploit the VDAC functionality fully, configure the OPAMP using
* the OPAMP API. Using the OPAMP API also loads OPAMP calibration values.
* The default (reset) settings of OPAMP is sufficient for many applications.
* @{
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/** Validation of VDAC register block pointer reference for assert statements.*/
#if VDAC_COUNT == 1
#define VDAC_REF_VALID(ref) ((ref) == VDAC0)
#elif VDAC_COUNT == 2
#define VDAC_REF_VALID(ref) (((ref) == VDAC0) || ((ref) == VDAC1))
#else
#error "Undefined number of VDACs."
#endif
/** @endcond */
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
#if !defined(_SILICON_LABS_32B_SERIES_2)
/** Channel refresh period. */
typedef enum {
vdacRefresh8 = _VDAC_CTRL_REFRESHPERIOD_8CYCLES, /**< Refresh every 8 clock cycles. */
vdacRefresh16 = _VDAC_CTRL_REFRESHPERIOD_16CYCLES, /**< Refresh every 16 clock cycles. */
vdacRefresh32 = _VDAC_CTRL_REFRESHPERIOD_32CYCLES, /**< Refresh every 32 clock cycles. */
vdacRefresh64 = _VDAC_CTRL_REFRESHPERIOD_64CYCLES, /**< Refresh every 64 clock cycles. */
} VDAC_Refresh_TypeDef;
/** Reference voltage for VDAC. */
typedef enum {
vdacRef1V25Ln = _VDAC_CTRL_REFSEL_1V25LN, /**< Internal low noise 1.25 V band gap reference. */
vdacRef2V5Ln = _VDAC_CTRL_REFSEL_2V5LN, /**< Internal low noise 2.5 V band gap reference. */
vdacRef1V25 = _VDAC_CTRL_REFSEL_1V25, /**< Internal 1.25 V band gap reference. */
vdacRef2V5 = _VDAC_CTRL_REFSEL_2V5, /**< Internal 2.5 V band gap reference. */
vdacRefAvdd = _VDAC_CTRL_REFSEL_VDD, /**< AVDD reference. */
vdacRefExtPin = _VDAC_CTRL_REFSEL_EXT, /**< External pin reference. */
} VDAC_Ref_TypeDef;
/** Peripheral Reflex System signal used to trigger VDAC channel conversion. */
typedef enum {
vdacPrsSelCh0 = _VDAC_CH0CTRL_PRSSEL_PRSCH0, /**< PRS ch 0 triggers conversion. */
vdacPrsSelCh1 = _VDAC_CH0CTRL_PRSSEL_PRSCH1, /**< PRS ch 1 triggers conversion. */
vdacPrsSelCh2 = _VDAC_CH0CTRL_PRSSEL_PRSCH2, /**< PRS ch 2 triggers conversion. */
vdacPrsSelCh3 = _VDAC_CH0CTRL_PRSSEL_PRSCH3, /**< PRS ch 3 triggers conversion. */
vdacPrsSelCh4 = _VDAC_CH0CTRL_PRSSEL_PRSCH4, /**< PRS ch 4 triggers conversion. */
vdacPrsSelCh5 = _VDAC_CH0CTRL_PRSSEL_PRSCH5, /**< PRS ch 5 triggers conversion. */
vdacPrsSelCh6 = _VDAC_CH0CTRL_PRSSEL_PRSCH6, /**< PRS ch 6 triggers conversion. */
vdacPrsSelCh7 = _VDAC_CH0CTRL_PRSSEL_PRSCH7, /**< PRS ch 7 triggers conversion. */
#if defined(_VDAC_CH0CTRL_PRSSEL_PRSCH8)
vdacPrsSelCh8 = _VDAC_CH0CTRL_PRSSEL_PRSCH8, /**< PRS ch 8 triggers conversion. */
#endif
#if defined(_VDAC_CH0CTRL_PRSSEL_PRSCH9)
vdacPrsSelCh9 = _VDAC_CH0CTRL_PRSSEL_PRSCH9, /**< PRS ch 9 triggers conversion. */
#endif
#if defined(_VDAC_CH0CTRL_PRSSEL_PRSCH10)
vdacPrsSelCh10 = _VDAC_CH0CTRL_PRSSEL_PRSCH10, /**< PRS ch 10 triggers conversion. */
#endif
#if defined(_VDAC_CH0CTRL_PRSSEL_PRSCH11)
vdacPrsSelCh11 = _VDAC_CH0CTRL_PRSSEL_PRSCH11, /**< PRS ch 11 triggers conversion. */
#endif
} VDAC_PrsSel_TypeDef;
/** Channel conversion trigger mode. */
typedef enum {
vdacTrigModeSw = _VDAC_CH0CTRL_TRIGMODE_SW, /**< Channel is triggered by CHnDATA or COMBDATA write. */
vdacTrigModePrs = _VDAC_CH0CTRL_TRIGMODE_PRS, /**< Channel is triggered by PRS input. */
vdacTrigModeRefresh = _VDAC_CH0CTRL_TRIGMODE_REFRESH, /**< Channel is triggered by Refresh timer. */
vdacTrigModeSwPrs = _VDAC_CH0CTRL_TRIGMODE_SWPRS, /**< Channel is triggered by CHnDATA/COMBDATA write or PRS input. */
vdacTrigModeSwRefresh = _VDAC_CH0CTRL_TRIGMODE_SWREFRESH, /**< Channel is triggered by CHnDATA/COMBDATA write or Refresh timer. */
vdacTrigModeLesense = _VDAC_CH0CTRL_TRIGMODE_LESENSE, /**< Channel is triggered by LESENSE. */
} VDAC_TrigMode_TypeDef;
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** VDAC initialization structure, common for both channels. */
typedef struct {
/** Selects between main and alternate output path calibration values. */
bool mainCalibration;
/** Selects clock from asynchronous or synchronous (with respect to
peripheral clock) source. */
bool asyncClockMode;
/** Warm-up mode, keep VDAC on (in idle) - or shutdown between conversions.*/
bool warmupKeepOn;
/** Channel refresh period. */
VDAC_Refresh_TypeDef refresh;
/** Prescaler for VDAC clock. Clock is source clock divided by prescaler+1. */
uint32_t prescaler;
/** Reference voltage to use. */
VDAC_Ref_TypeDef reference;
/** Enable/disable reset of prescaler on CH 0 start. */
bool ch0ResetPre;
/** Enable/disable output enable control by CH1 PRS signal. */
bool outEnablePRS;
/** Enable/disable sine mode. */
bool sineEnable;
/** Select if single ended or differential output mode. */
bool diff;
} VDAC_Init_TypeDef;
/** Default configuration for VDAC initialization structure. */
#define VDAC_INIT_DEFAULT \
{ \
true, /* Use main output path calibration values. */ \
false, /* Use synchronous clock mode. */ \
false, /* Turn off between sample off conversions.*/ \
vdacRefresh8, /* Refresh every 8th cycle. */ \
0, /* No prescaling. */ \
vdacRef1V25Ln, /* 1.25 V internal low noise reference. */ \
false, /* Do not reset prescaler on CH 0 start. */ \
false, /* VDAC output enable always on. */ \
false, /* Disable sine mode. */ \
false /* Single ended mode. */ \
}
/** VDAC channel initialization structure. */
typedef struct {
/** Enable channel. */
bool enable;
/**
* Peripheral reflex system trigger selection. Only applicable if @p trigMode
* is set to @p vdacTrigModePrs or @p vdacTrigModeSwPrs. */
VDAC_PrsSel_TypeDef prsSel;
/** Treat the PRS signal asynchronously. */
bool prsAsync;
/** Channel conversion trigger mode. */
VDAC_TrigMode_TypeDef trigMode;
/** Set channel conversion mode to sample/shut-off mode. Default is
* continuous.*/
bool sampleOffMode;
} VDAC_InitChannel_TypeDef;
/** Default configuration for VDAC channel initialization structure. */
#define VDAC_INITCHANNEL_DEFAULT \
{ \
false, /* Leave channel disabled when initialization is done. */ \
vdacPrsSelCh0, /* PRS CH 0 triggers conversion. */ \
false, /* Treat PRS channel as a synchronous signal. */ \
vdacTrigModeSw, /* Conversion trigged by CH0DATA or COMBDATA write. */ \
false, /* Channel conversion set to continuous. */ \
}
#else // defined(_SILICON_LABS_32B_SERIES_2)
/** Channel refresh period. */
typedef enum {
vdacRefresh2 = _VDAC_CFG_REFRESHPERIOD_CYCLES2, /**< Refresh every 2 clock cycles. */
vdacRefresh4 = _VDAC_CFG_REFRESHPERIOD_CYCLES4, /**< Refresh every 4 clock cycles. */
vdacRefresh8 = _VDAC_CFG_REFRESHPERIOD_CYCLES8, /**< Refresh every 8 clock cycles. */
vdacRefresh16 = _VDAC_CFG_REFRESHPERIOD_CYCLES16, /**< Refresh every 16 clock cycles. */
vdacRefresh32 = _VDAC_CFG_REFRESHPERIOD_CYCLES32, /**< Refresh every 32 clock cycles. */
vdacRefresh64 = _VDAC_CFG_REFRESHPERIOD_CYCLES64, /**< Refresh every 64 clock cycles. */
vdacRefresh128 = _VDAC_CFG_REFRESHPERIOD_CYCLES128, /**< Refresh every 128 clock cycles. */
vdacRefresh256 = _VDAC_CFG_REFRESHPERIOD_CYCLES256, /**< Refresh every 256 clock cycles. */
} VDAC_Refresh_TypeDef;
/** Timer overflow period. */
typedef enum {
vdacCycles2 = _VDAC_CFG_TIMEROVRFLOWPERIOD_CYCLES2, /**< Overflows every 2 clock cycles. */
vdacCycles4 = _VDAC_CFG_TIMEROVRFLOWPERIOD_CYCLES4, /**< Overflows every 4 clock cycles. */
vdacCycles8 = _VDAC_CFG_TIMEROVRFLOWPERIOD_CYCLES8, /**< Overflows every 8 clock cycles. */
vdacCycles16 = _VDAC_CFG_TIMEROVRFLOWPERIOD_CYCLES16, /**< Overflows every 16 clock cycles. */
vdacCycles32 = _VDAC_CFG_TIMEROVRFLOWPERIOD_CYCLES32, /**< Overflows every 32 clock cycles. */
vdacCycles64 = _VDAC_CFG_TIMEROVRFLOWPERIOD_CYCLES64 /**< Overflows every 64 clock cycles. */
} VDAC_TimerOverflow_TypeDef;
/** Reference voltage for VDAC. */
typedef enum {
vdacRef1V25 = _VDAC_CFG_REFRSEL_V125, /**< Internal 1.25 V band gap reference. */
vdacRef2V5 = _VDAC_CFG_REFRSEL_V25, /**< Internal 2.5 V band gap reference. */
vdacRefAvdd = _VDAC_CFG_REFRSEL_VDD, /**< AVDD reference. */
vdacRefExtPin = _VDAC_CFG_REFRSEL_EXT, /**< External pin reference. */
} VDAC_Ref_TypeDef;
/** Refresh source for VDAC. */
typedef enum {
vdacRefreshSrcNone = _VDAC_CH0CFG_REFRESHSOURCE_NONE, /**< No refresh source. */
vdacRefreshSrcRefreshTimer = _VDAC_CH0CFG_REFRESHSOURCE_REFRESHTIMER,/**< Refresh triggered by refresh timer overflow. */
vdacRefreshSrcSyncPrs = _VDAC_CH0CFG_REFRESHSOURCE_SYNCPRS, /**< Refresh triggered by sync PRS. */
vdacRefreshSrcAsyncPrs = _VDAC_CH0CFG_REFRESHSOURCE_ASYNCPRS, /**< Refresh triggered by async PRS. */
} VDAC_RefreshSource_TypeDef;
/** Channel conversion trigger mode. */
typedef enum {
vdacTrigModeNone = _VDAC_CH0CFG_TRIGMODE_NONE, /**< No conversion trigger source selected. */
vdacTrigModeSw = _VDAC_CH0CFG_TRIGMODE_SW, /**< Channel is triggered by CHnDATA or COMBDATA write. */
vdacTrigModeSyncPrs = _VDAC_CH0CFG_TRIGMODE_SYNCPRS, /**< Channel is triggered by Sync PRS input. */
#if defined(LESENSE_PRESENT) && defined(_VDAC_CH0CFG_TRIGMODE_LESENSE)
vdacTrigModeLesense = _VDAC_CH0CFG_TRIGMODE_LESENSE, /**< Channel is triggered by LESENSE. */
#endif
vdacTrigModeInternalTimer = _VDAC_CH0CFG_TRIGMODE_INTERNALTIMER, /**< Channel is triggered by Internal Timer. */
vdacTrigModeAsyncPrs = _VDAC_CH0CFG_TRIGMODE_ASYNCPRS /**< Channel is triggered by Async PRS input. */
} VDAC_TrigMode_TypeDef;
/** Channel power mode. */
typedef enum {
vdacPowerModeHighPower = _VDAC_CH0CFG_POWERMODE_HIGHPOWER, /**< High power buffer mode. */
vdacPowerModeLowPower = _VDAC_CH0CFG_POWERMODE_LOWPOWER /**< Low power buffer mode. */
} VDAC_PowerMode_TypeDef;
/** VDAC channel Abus port selection. */
typedef enum {
/** No GPIO selected. */
vdacChPortNone = _VDAC_OUTCTRL_ABUSPORTSELCH0_NONE,
/** Port A selected. */
vdacChPortA = _VDAC_OUTCTRL_ABUSPORTSELCH0_PORTA,
/** Port B selected. */
vdacChPortB = _VDAC_OUTCTRL_ABUSPORTSELCH0_PORTB,
/** Port C selected. */
vdacChPortC = _VDAC_OUTCTRL_ABUSPORTSELCH0_PORTC,
/** Port D selected. */
vdacChPortD = _VDAC_OUTCTRL_ABUSPORTSELCH0_PORTD,
} VDAC_ChPortSel_t;
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** VDAC initialization structure, common for both channels. */
typedef struct {
/** Number of prescaled CLK_DAC + 1 for the vdac to warmup. */
uint32_t warmupTime;
/** Halt during debug. */
bool dbgHalt;
/** Always allow clk_dac. */
bool onDemandClk;
/** DMA Wakeup. */
bool dmaWakeUp;
/** Bias keep warm enable. */
bool biasKeepWarm;
/** Channel refresh period. */
VDAC_Refresh_TypeDef refresh;
/** Internal timer overflow period. */
VDAC_TimerOverflow_TypeDef timerOverflow;
/** Prescaler for VDAC clock. Clock is source clock divided by prescaler+1. */
uint32_t prescaler;
/** Reference voltage to use. */
VDAC_Ref_TypeDef reference;
/** Enable/disable reset of prescaler on CH 0 start. */
bool ch0ResetPre;
/** Sine reset mode. */
bool sineReset;
/** Enable/disable sine mode. */
bool sineEnable;
/** Select if single ended or differential output mode. */
bool diff;
#if defined(VDAC_CFG_SINEMODEPRS)
/** PRS controlled sinemode enable. */
bool sineModePrsEnable;
#endif
#if defined(VDAC_CFG_OUTENPRS)
/** PRS controlled channel output enable. */
bool prsOutEnable;
#endif
} VDAC_Init_TypeDef;
#if defined(VDAC_CFG_SINEMODEPRS)
/** Default configuration for VDAC initialization structure. */
#define VDAC_INIT_DEFAULT \
{ \
_VDAC_CFG_WARMUPTIME_DEFAULT, /* Number of prescaled DAC_CLK for Vdac to warmup. */ \
false, /* Continue while debugging. */ \
false, /* On demand clock. */ \
false, /* DMA wake up. */ \
false, /* Bias keep warm. */ \
vdacRefresh2, /* Refresh every 2th cycle. */ \
vdacCycles2, /* Internal overflow every 2th cycle. */ \
0, /* No prescaling. */ \
vdacRef1V25, /* 1.25 V internal low noise reference. */ \
false, /* Do not reset prescaler on CH 0 start. */ \
false, /* Sine wave is stopped at the sample its currently outputting. */ \
false, /* Disable sine mode. */ \
false, /* Differential mode. */ \
false, /* PRS controlled sinemode. */ \
false, /* PRS controlled output enable. */ \
}
#else
/** Default configuration for VDAC initialization structure. */
#define VDAC_INIT_DEFAULT \
{ \
_VDAC_CFG_WARMUPTIME_DEFAULT, /* Number of prescaled DAC_CLK for Vdac to warmup. */ \
false, /* Continue while debugging. */ \
false, /* On demand clock. */ \
false, /* DMA wake up. */ \
false, /* Bias keep warm. */ \
vdacRefresh2, /* Refresh every 2th cycle. */ \
vdacCycles2, /* Internal overflow every 2th cycle. */ \
0, /* No prescaling. */ \
vdacRef1V25, /* 1.25 V internal low noise reference. */ \
false, /* Do not reset prescaler on CH 0 start. */ \
false, /* Sine wave is stopped at the sample its currently outputting. */ \
false, /* Disable sine mode. */ \
false, /* Differential mode. */ \
}
#endif
#if defined(VDAC_CFG_SINEMODEPRS)
/** Sine mode configuration for VDAC initialization structure. */
#define VDAC_INIT_SINE_GENERATION_MODE \
{ \
_VDAC_CFG_WARMUPTIME_DEFAULT, /* Number of prescaled DAC_CLK for Vdac to warmup. */ \
false, /* Continue while debugging. */ \
true, /* On demand clock. */ \
false, /* DMA wake up. */ \
false, /* Bias keep warm. */ \
vdacRefresh8, /* Refresh every 8th cycle. */ \
vdacCycles2, /* Internal overflow every 8th cycle. */ \
0, /* No prescaling. */ \
vdacRef1V25, /* 1.25 V internal low noise reference. */ \
false, /* Do not reset prescaler on CH 0 start. */ \
false, /* Sine wave is stopped at the sample its currently outputting. */ \
true, /* Enable sine mode. */ \
false, /* Differential mode. */ \
false, /* PRS controlled sinemode. */ \
false, /* PRS controlled output enable. */ \
}
#else
/** Sine mode configuration for VDAC initialization structure. */
#define VDAC_INIT_SINE_GENERATION_MODE \
{ \
_VDAC_CFG_WARMUPTIME_DEFAULT, /* Number of prescaled DAC_CLK for Vdac to warmup. */ \
false, /* Continue while debugging. */ \
true, /* On demand clock. */ \
false, /* DMA wake up. */ \
false, /* Bias keep warm. */ \
vdacRefresh8, /* Refresh every 8th cycle. */ \
vdacCycles2, /* Internal overflow every 8th cycle. */ \
0, /* No prescaling. */ \
vdacRef1V25, /* 1.25 V internal low noise reference. */ \
false, /* Do not reset prescaler on CH 0 start. */ \
false, /* Sine wave is stopped at the sample its currently outputting. */ \
true, /* Enable sine mode. */ \
false, /* Differential mode. */ \
}
#endif
/** VDAC channel initialization structure. */
typedef struct {
/** Enable channel. */
bool enable;
/** Warm-up mode, keep VDAC on (in idle) - or shutdown between conversions.*/
bool warmupKeepOn;
/** Select high capacitance load mode in conjunction with high power. */
bool highCapLoadEnable;
/** Channel x FIFO Low threshold data valid level. */
uint32_t fifoLowDataThreshold;
/** Channel refresh source. */
VDAC_RefreshSource_TypeDef chRefreshSource;
/** Channel conversion trigger mode. */
VDAC_TrigMode_TypeDef trigMode;
/** Channel power mode. */
VDAC_PowerMode_TypeDef powerMode;
/** Set channel conversion mode to sample/shut-off mode. Default is
* continuous.*/
bool sampleOffMode;
/** Vdac channel output pin. */
uint32_t pin;
/** Vdac channel output port. */
VDAC_ChPortSel_t port;
/** Short High power and low power output. */
bool shortOutput;
/** Alternative output enable. */
bool auxOutEnable;
/** Main output enable. */
bool mainOutEnable;
/** Channel output hold time. */
uint32_t holdOutTime;
} VDAC_InitChannel_TypeDef;
/** Default configuration for VDAC channel initialization structure. */
#define VDAC_INITCHANNEL_DEFAULT \
{ \
false, /* Leave channel disabled when initialization is done. */ \
false, /* Turn off between sample off conversions.*/ \
true, /* Enable High cap mode. */ \
0, /* FIFO data low watermark at 0. */ \
vdacRefreshSrcNone, /* Channel refresh source. */ \
vdacTrigModeSw, /* Conversion trigged by CH0DATA or COMBDATA write. */ \
vdacPowerModeHighPower, /* High power mode enabled. */ \
false, /* Continuous conversion mode. */ \
0, /* ABUS pin selected. */ \
vdacChPortNone, /* No Analog bus port selected. */ \
false, /* Output not shorted */ \
false, /* Alternative output disabled. */ \
true, /* Main output enabled. */ \
0, /* Hold out time. Previously called settle time */ \
}
#endif
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
void VDAC_ChannelOutputSet(VDAC_TypeDef *vdac,
unsigned int channel,
uint32_t value);
void VDAC_Enable(VDAC_TypeDef *vdac, unsigned int ch, bool enable);
void VDAC_Init(VDAC_TypeDef *vdac, const VDAC_Init_TypeDef *init);
void VDAC_InitChannel(VDAC_TypeDef *vdac,
const VDAC_InitChannel_TypeDef *init,
unsigned int ch);
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Start/stop Sinemode.
*
* @details
* This function sends the sine mode start/stop signal to the DAC.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] start
* True to start the Sine mode, false to stop it.
******************************************************************************/
__STATIC_INLINE void VDAC_SineModeStart(VDAC_TypeDef *vdac, bool start)
{
EFM_ASSERT(VDAC_REF_VALID(vdac));
while (0UL != (vdac->STATUS & VDAC_STATUS_SYNCBUSY)) {
}
if (start) {
vdac->CMD = VDAC_CMD_SINEMODESTART;
while (0UL == (vdac->STATUS & VDAC_STATUS_SINEACTIVE)) {
}
} else {
vdac->CMD = VDAC_CMD_SINEMODESTOP;
while (0UL != (vdac->STATUS & VDAC_STATUS_SINEACTIVE)) {
}
}
}
#endif
/***************************************************************************//**
* @brief
* Set the output signal of VDAC channel 0 to a given value.
*
* @details
* This function sets the output signal of VDAC channel 0 by writing @p value
* to the CH0DATA register.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] value
* Value to write to channel 0 output register CH0DATA.
******************************************************************************/
__STATIC_INLINE void VDAC_Channel0OutputSet(VDAC_TypeDef *vdac,
uint32_t value)
{
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
EFM_ASSERT(value <= _VDAC_CH0DATA_MASK);
vdac->CH0DATA = value;
#elif defined(_SILICON_LABS_32B_SERIES_2)
EFM_ASSERT(value <= _VDAC_CH0F_MASK);
vdac->CH0F = value;
#endif
}
/***************************************************************************//**
* @brief
* Set the output signal of VDAC channel 1 to a given value.
*
* @details
* This function sets the output signal of VDAC channel 1 by writing @p value
* to the CH1DATA register.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] value
* Value to write to channel 1 output register CH1DATA.
******************************************************************************/
__STATIC_INLINE void VDAC_Channel1OutputSet(VDAC_TypeDef *vdac,
uint32_t value)
{
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
EFM_ASSERT(value <= _VDAC_CH1DATA_MASK);
vdac->CH1DATA = value;
#elif defined(_SILICON_LABS_32B_SERIES_2)
EFM_ASSERT(value <= _VDAC_CH1F_MASK);
vdac->CH1F = value;
#endif
}
/***************************************************************************//**
* @brief
* Clear one or more pending VDAC interrupts.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] flags
* Pending VDAC interrupt source to clear. Use a bitwise logic OR combination
* of valid interrupt flags for the VDAC module (VDAC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void VDAC_IntClear(VDAC_TypeDef *vdac, uint32_t flags)
{
#if defined(VDAC_HAS_SET_CLEAR)
vdac->IF_CLR = flags;
#else
vdac->IFC = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more VDAC interrupts.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] flags
* VDAC interrupt sources to disable. Use a bitwise logic OR combination of
* valid interrupt flags for the VDAC module (VDAC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void VDAC_IntDisable(VDAC_TypeDef *vdac, uint32_t flags)
{
#if defined(VDAC_HAS_SET_CLEAR)
vdac->IEN_CLR = flags;
#else
vdac->IEN &= ~flags;
#endif
}
/***************************************************************************//**
* @brief
* Enable one or more VDAC interrupts.
*
* @note
* Depending on the use, a pending interrupt may already be set prior to
* enabling the interrupt. To ignore a pending interrupt, consider using
* VDAC_IntClear() prior to enabling the interrupt.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] flags
* VDAC interrupt sources to enable. Use a bitwise logic OR combination
* of valid interrupt flags for the VDAC module (VDAC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void VDAC_IntEnable(VDAC_TypeDef *vdac, uint32_t flags)
{
#if defined(VDAC_HAS_SET_CLEAR)
vdac->IEN_SET = flags;
#else
vdac->IEN |= flags;
#endif
}
/***************************************************************************//**
* @brief
* Get pending VDAC interrupt flags.
*
* @note
* The event bits are not cleared by the use of this function.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @return
* VDAC interrupt sources pending. Use a bitwise logic OR combination
* of valid interrupt flags for the VDAC module (VDAC_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t VDAC_IntGet(VDAC_TypeDef *vdac)
{
return vdac->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending VDAC interrupt flags.
* Useful for handling more interrupt sources in the same interrupt handler.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @note
* Interrupt flags are not cleared by the use of this function.
*
* @return
* Pending and enabled VDAC interrupt sources.
* The return value is the bitwise AND combination of
* - the OR combination of enabled interrupt sources in VDACx_IEN_nnn
* register (VDACx_IEN_nnn) and
* - the OR combination of valid interrupt flags of the VDAC module
* (VDACx_IF_nnn).
******************************************************************************/
__STATIC_INLINE uint32_t VDAC_IntGetEnabled(VDAC_TypeDef *vdac)
{
uint32_t ien = vdac->IEN;
/* Bitwise AND of pending and enabled interrupts */
return vdac->IF & ien;
}
/***************************************************************************//**
* @brief
* Set one or more pending VDAC interrupts from SW.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @param[in] flags
* VDAC interrupt sources to set to pending. Use a bitwise logic OR
* combination of valid interrupt flags for the VDAC module (VDAC_IF_nnn).
******************************************************************************/
__STATIC_INLINE void VDAC_IntSet(VDAC_TypeDef *vdac, uint32_t flags)
{
#if defined(VDAC_HAS_SET_CLEAR)
vdac->IF_SET = flags;
#else
vdac->IFS = flags;
#endif
}
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Get the VDAC Status register.
*
* @param[in] vdac
* Pointer to VDAC peripheral register block.
*
* @return
* Current STATUS register value.
******************************************************************************/
__STATIC_INLINE uint32_t VDAC_GetStatus(VDAC_TypeDef *vdac)
{
return vdac->STATUS;
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1)
uint32_t VDAC_PrescaleCalc(uint32_t vdacFreq, bool syncMode, uint32_t hfperFreq);
#else
uint32_t VDAC_PrescaleCalc(VDAC_TypeDef *vdac, uint32_t vdacFreq);
#endif
void VDAC_Reset(VDAC_TypeDef *vdac);
/** @} (end addtogroup vdac) */
#ifdef __cplusplus
}
#endif
#endif /* defined(VDAC_COUNT) && (VDAC_COUNT > 0) */
#endif /* EM_VDAC_H */

View File

@@ -0,0 +1,68 @@
/***************************************************************************//**
* @file
* @brief CMSIS and EMLIB versions
*******************************************************************************
* # 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.
*
******************************************************************************/
#ifndef EM_VERSION_H
#define EM_VERSION_H
#include "em_device.h"
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup version VERSION - Version Defines
* @brief Version API
* @details
* Macros specifying the EMLIB and CMSIS version.
* @{
******************************************************************************/
/* *INDENT-OFF* */
/** Version number of targeted CMSIS package. */
#define _CMSIS_VERSION 5.8.0
/* *INDENT-ON* */
/** Major version of CMSIS. */
#define _CMSIS_VERSION_MAJOR 5
/** Minor version of CMSIS. */
#define _CMSIS_VERSION_MINOR 8
/** Patch revision of CMSIS. */
#define _CMSIS_VERSION_PATCH 0
/** @} (end addtogroup version) */
#ifdef __cplusplus
}
#endif
#endif /* EM_VERSION_H */

View File

@@ -0,0 +1,455 @@
/***************************************************************************//**
* @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.
*
******************************************************************************/
#ifndef EM_WDOG_H
#define EM_WDOG_H
#include "em_device.h"
#if defined(WDOG_COUNT) && (WDOG_COUNT > 0)
#include <stdbool.h>
#include "sl_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************//**
* @addtogroup wdog
* @{
******************************************************************************/
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
/** Default WDOG instance for deprecated functions. */
#if !defined(DEFAULT_WDOG)
#if defined(WDOG0)
#define DEFAULT_WDOG WDOG0
#elif defined(WDOG)
#define DEFAULT_WDOG WDOG
#endif
#endif
/*******************************************************************************
******************************** ENUMS ************************************
******************************************************************************/
/** Watchdog clock selection. */
#if defined(_WDOG_CTRL_CLKSEL_MASK)
typedef enum {
wdogClkSelULFRCO = _WDOG_CTRL_CLKSEL_ULFRCO, /**< Ultra low frequency (1 kHz) clock */
wdogClkSelLFRCO = _WDOG_CTRL_CLKSEL_LFRCO, /**< Low frequency RC oscillator */
wdogClkSelLFXO = _WDOG_CTRL_CLKSEL_LFXO /**< Low frequency crystal oscillator */
} WDOG_ClkSel_TypeDef;
#endif
/** Watchdog period selection. */
typedef enum {
wdogPeriod_9 = 0x0, /**< 9 clock periods */
wdogPeriod_17 = 0x1, /**< 17 clock periods */
wdogPeriod_33 = 0x2, /**< 33 clock periods */
wdogPeriod_65 = 0x3, /**< 65 clock periods */
wdogPeriod_129 = 0x4, /**< 129 clock periods */
wdogPeriod_257 = 0x5, /**< 257 clock periods */
wdogPeriod_513 = 0x6, /**< 513 clock periods */
wdogPeriod_1k = 0x7, /**< 1025 clock periods */
wdogPeriod_2k = 0x8, /**< 2049 clock periods */
wdogPeriod_4k = 0x9, /**< 4097 clock periods */
wdogPeriod_8k = 0xA, /**< 8193 clock periods */
wdogPeriod_16k = 0xB, /**< 16385 clock periods */
wdogPeriod_32k = 0xC, /**< 32769 clock periods */
wdogPeriod_64k = 0xD, /**< 65537 clock periods */
wdogPeriod_128k = 0xE, /**< 131073 clock periods */
wdogPeriod_256k = 0xF /**< 262145 clock periods */
} WDOG_PeriodSel_TypeDef;
#if defined(_WDOG_CTRL_WARNSEL_MASK) \
|| defined(_WDOG_CFG_WARNSEL_MASK)
/** Select Watchdog warning timeout period as percentage of timeout. */
typedef enum {
wdogWarnDisable = 0, /**< Watchdog warning period is disabled. */
wdogWarnTime25pct = 1, /**< Watchdog warning period is 25% of the timeout. */
wdogWarnTime50pct = 2, /**< Watchdog warning period is 50% of the timeout. */
wdogWarnTime75pct = 3, /**< Watchdog warning period is 75% of the timeout. */
} WDOG_WarnSel_TypeDef;
#endif
#if defined(_WDOG_CTRL_WINSEL_MASK) \
|| defined(_WDOG_CFG_WINSEL_MASK)
/** Select Watchdog illegal window limit. */
typedef enum {
wdogIllegalWindowDisable = 0, /**< Watchdog illegal window disabled. */
wdogIllegalWindowTime12_5pct = 1, /**< Window timeout is 12.5% of the timeout. */
wdogIllegalWindowTime25_0pct = 2, /**< Window timeout is 25% of the timeout. */
wdogIllegalWindowTime37_5pct = 3, /**< Window timeout is 37.5% of the timeout. */
wdogIllegalWindowTime50_0pct = 4, /**< Window timeout is 50% of the timeout. */
wdogIllegalWindowTime62_5pct = 5, /**< Window timeout is 62.5% of the timeout. */
wdogIllegalWindowTime75_0pct = 6, /**< Window timeout is 75% of the timeout. */
wdogIllegalWindowTime87_5pct = 7, /**< Window timeout is 87.5% of the timeout. */
} WDOG_WinSel_TypeDef;
#endif
/*******************************************************************************
******************************* STRUCTS ***********************************
******************************************************************************/
/** Watchdog initialization structure. */
typedef struct {
/** Enable Watchdog when initialization completed. */
bool enable;
/** Counter keeps running during debug halt. */
bool debugRun;
#if defined(_WDOG_CTRL_CLRSRC_MASK) \
|| defined(_WDOG_CFG_CLRSRC_MASK)
/** Select WDOG clear source:
* False: Write to the clear bit will clear the WDOG counter
* True: Rising edge on the PRS Source 0 will clear the WDOG counter
* */
bool clrSrc;
#endif
#if defined(_WDOG_CFG_EM1RUN_MASK)
/** Counter keeps running when in EM1. Available for series2. */
bool em1Run;
#endif
/** Counter keeps running when in EM2. */
bool em2Run;
/** Counter keeps running when in EM3. */
bool em3Run;
/** Block EMU from entering EM4. */
bool em4Block;
#if defined(_WDOG_CFG_MASK)
/** When set, a PRS Source 0 missing event will trigger a WDOG reset. */
bool prs0MissRstEn;
/** When set, a PRS Source 1 missing event will trigger a WDOG reset. */
bool prs1MissRstEn;
#endif
/** Block SW from disabling LFRCO/LFXO oscillators. */
#if defined(_WDOG_CTRL_SWOSCBLOCK_MASK)
bool swoscBlock;
#endif
/** Block SW from modifying the configuration (a reset is needed to reconfigure). */
bool lock;
/** Clock source to use for Watchdog. */
#if defined(_WDOG_CTRL_CLKSEL_MASK)
WDOG_ClkSel_TypeDef clkSel;
#endif
/** Watchdog timeout period. */
WDOG_PeriodSel_TypeDef perSel;
#if defined(_WDOG_CTRL_WARNSEL_MASK) \
|| defined(_WDOG_CFG_WARNSEL_MASK)
/** Select warning time as % of the Watchdog timeout */
WDOG_WarnSel_TypeDef warnSel;
#endif
#if defined(_WDOG_CTRL_WINSEL_MASK) \
|| defined(_WDOG_CFG_WINSEL_MASK)
/** Select illegal window time as % of the Watchdog timeout */
WDOG_WinSel_TypeDef winSel;
#endif
#if defined(_WDOG_CTRL_WDOGRSTDIS_MASK) \
|| defined(_WDOG_CFG_WDOGRSTDIS_MASK)
/** Disable Watchdog reset output if true */
bool resetDisable;
#endif
} WDOG_Init_TypeDef;
/** Suggested default configuration for WDOG initialization structure. */
#if defined(_WDOG_CFG_MASK) && defined(_WDOG_CFG_EM1RUN_MASK)
#define WDOG_INIT_DEFAULT \
{ \
true, /* Start Watchdog when initialization is done. */ \
false, /* WDOG is not counting during debug halt. */ \
false, /* The clear bit will clear the WDOG counter. */ \
false, /* WDOG is not counting when in EM1. */ \
false, /* WDOG is not counting when in EM2. */ \
false, /* WDOG is not counting when in EM3. */ \
false, /* EM4 can be entered. */ \
false, /* PRS Source 0 missing event will not trigger a WDOG reset. */ \
false, /* PRS Source 1 missing event will not trigger a WDOG reset. */ \
false, /* Do not lock WDOG configuration. */ \
wdogPeriod_256k, /* Set longest possible timeout period. */ \
wdogWarnDisable, /* Disable warning interrupt. */ \
wdogIllegalWindowDisable, /* Disable illegal window interrupt. */ \
false /* Do not disable reset. */ \
}
#elif defined(_WDOG_CFG_MASK)
#define WDOG_INIT_DEFAULT \
{ \
true, /* Start Watchdog when initialization is done. */ \
false, /* WDOG is not counting during debug halt. */ \
false, /* The clear bit will clear the WDOG counter. */ \
false, /* WDOG is not counting when in EM2. */ \
false, /* WDOG is not counting when in EM3. */ \
false, /* EM4 can be entered. */ \
false, /* PRS Source 0 missing event will not trigger a WDOG reset. */ \
false, /* PRS Source 1 missing event will not trigger a WDOG reset. */ \
false, /* Do not lock WDOG configuration. */ \
wdogPeriod_256k, /* Set longest possible timeout period. */ \
wdogWarnDisable, /* Disable warning interrupt. */ \
wdogIllegalWindowDisable, /* Disable illegal window interrupt. */ \
false /* Do not disable reset. */ \
}
#elif defined(_WDOG_CTRL_WARNSEL_MASK) \
&& defined(_WDOG_CTRL_WDOGRSTDIS_MASK) \
&& defined(_WDOG_CTRL_WINSEL_MASK)
#define WDOG_INIT_DEFAULT \
{ \
true, /* Start Watchdog when initialization is done. */ \
false, /* WDOG is not counting during debug halt. */ \
false, /* The clear bit will clear the WDOG counter. */ \
false, /* WDOG is not counting when in EM2. */ \
false, /* WDOG is not counting when in EM3. */ \
false, /* EM4 can be entered. */ \
false, /* Do not block disabling LFRCO/LFXO in CMU. */ \
false, /* Do not lock WDOG configuration. */ \
wdogClkSelLFRCO, /* Select 32.768 kHZ WDOG oscillator. */ \
wdogPeriod_256k, /* Set longest possible timeout period. */ \
wdogWarnDisable, /* Disable warning interrupt. */ \
wdogIllegalWindowDisable, /* Disable illegal window interrupt. */ \
false /* Do not disable reset. */ \
}
#else
#define WDOG_INIT_DEFAULT \
{ \
true, /* Start Watchdog when initialization is done. */ \
false, /* WDOG is not counting during debug halt. */ \
false, /* WDOG is not counting when in EM2. */ \
false, /* WDOG is not counting when in EM3. */ \
false, /* EM4 can be entered. */ \
false, /* Do not block disabling LFRCO/LFXO in CMU. */ \
false, /* Do not lock WDOG configuration. */ \
wdogClkSelLFRCO, /* Select 32.768 kHz WDOG oscillator. */ \
wdogPeriod_256k /* Set longest possible timeout period. */ \
}
#endif
/*******************************************************************************
***************************** PROTOTYPES **********************************
******************************************************************************/
void WDOGn_Enable(WDOG_TypeDef *wdog, bool enable);
void WDOGn_Feed(WDOG_TypeDef *wdog);
void WDOGn_Init(WDOG_TypeDef *wdog, const WDOG_Init_TypeDef *init);
void WDOGn_Lock(WDOG_TypeDef *wdog);
void WDOGn_SyncWait(WDOG_TypeDef *wdog);
void WDOGn_Unlock(WDOG_TypeDef *wdog);
#if defined(_WDOG_IF_MASK)
/***************************************************************************//**
* @brief
* Clear one or more pending WDOG interrupts.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @param[in] flags
* WDOG interrupt sources to clear. Use a set of interrupt flags OR-ed
* together to clear multiple interrupt sources.
******************************************************************************/
__STATIC_INLINE void WDOGn_IntClear(WDOG_TypeDef *wdog, uint32_t flags)
{
#if defined(WDOG_HAS_SET_CLEAR)
wdog->IF_CLR = flags;
#else
wdog->IFC = flags;
#endif
}
/***************************************************************************//**
* @brief
* Disable one or more WDOG interrupts.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @param[in] flags
* WDOG interrupt sources to disable. Use a set of interrupt flags OR-ed
* together to disable multiple interrupt.
******************************************************************************/
__STATIC_INLINE void WDOGn_IntDisable(WDOG_TypeDef *wdog, uint32_t flags)
{
#if defined(WDOG_HAS_SET_CLEAR)
wdog->IEN_CLR = flags;
#else
wdog->IEN &= ~flags;
#endif
}
/***************************************************************************//**
* @brief
* Enable one or more WDOG interrupts.
*
* @note
* Depending on the use, a pending interrupt may already be set prior to
* enabling the interrupt. To ignore a pending interrupt, consider using
* WDOG_IntClear() prior to enabling the interrupt.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @param[in] flags
* WDOG interrupt sources to enable. Use a set of interrupt flags OR-ed
* together to set multiple interrupt.
******************************************************************************/
__STATIC_INLINE void WDOGn_IntEnable(WDOG_TypeDef *wdog, uint32_t flags)
{
#if defined(WDOG_HAS_SET_CLEAR)
wdog->IEN_SET = flags;
#else
wdog->IEN |= flags;
#endif
}
/***************************************************************************//**
* @brief
* Get pending WDOG interrupt flags.
*
* @note
* The event bits are not cleared by the use of this function.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @return
* Pending WDOG interrupt sources. Returns a set of interrupt flags OR-ed
* together for the interrupt sources set.
******************************************************************************/
__STATIC_INLINE uint32_t WDOGn_IntGet(WDOG_TypeDef *wdog)
{
return wdog->IF;
}
/***************************************************************************//**
* @brief
* Get enabled and pending WDOG interrupt flags.
*
* @details
* Useful for handling more interrupt sources in the same interrupt handler.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @return
* Pending and enabled WDOG interrupt sources. Returns a set of interrupt
* flags OR-ed together for the interrupt sources set.
******************************************************************************/
__STATIC_INLINE uint32_t WDOGn_IntGetEnabled(WDOG_TypeDef *wdog)
{
uint32_t tmp;
tmp = wdog->IEN;
/* Bitwise AND of pending and enabled interrupt flags. */
return wdog->IF & tmp;
}
/***************************************************************************//**
* @brief
* Set one or more pending WDOG interrupts from SW.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @param[in] flags
* WDOG interrupt sources to set to pending. Use a set of interrupt flags
* (WDOG_IFS_nnn).
******************************************************************************/
__STATIC_INLINE void WDOGn_IntSet(WDOG_TypeDef *wdog, uint32_t flags)
{
#if defined(WDOG_HAS_SET_CLEAR)
wdog->IF_SET = flags;
#else
wdog->IFS = flags;
#endif
}
#endif
/***************************************************************************//**
* @brief
* Get enabled status of the Watchdog.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @return
* True if Watchdog is enabled.
******************************************************************************/
__STATIC_INLINE bool WDOGn_IsEnabled(WDOG_TypeDef *wdog)
{
#if defined(_WDOG_EN_MASK)
return (wdog->EN & _WDOG_EN_EN_MASK) == WDOG_EN_EN;
#else
return (wdog->CTRL & _WDOG_CTRL_EN_MASK) == WDOG_CTRL_EN;
#endif
}
/***************************************************************************//**
* @brief
* Get locked status of the Watchdog.
*
* @param[in] wdog
* Pointer to the WDOG peripheral register block.
*
* @return
* True if Watchdog is locked.
******************************************************************************/
__STATIC_INLINE bool WDOGn_IsLocked(WDOG_TypeDef *wdog)
{
#if defined(_WDOG_STATUS_MASK)
return (wdog->STATUS & _WDOG_STATUS_LOCK_MASK) == WDOG_STATUS_LOCK_LOCKED;
#else
return (wdog->CTRL & _WDOG_CTRL_LOCK_MASK) == WDOG_CTRL_LOCK;
#endif
}
/** @} (end addtogroup wdog) */
#ifdef __cplusplus
}
#endif
#endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */
#endif /* EM_WDOG_H */

File diff suppressed because it is too large Load Diff

View 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) */

View 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 */

File diff suppressed because it is too large Load Diff

View 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) */

View 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 ) */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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) */

View 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) */

View 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) */

File diff suppressed because it is too large Load Diff

View 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 ) */

View 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) */

File diff suppressed because it is too large Load Diff

View 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) */

File diff suppressed because it is too large Load Diff

View 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) */

View 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) */

View 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) */

View 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) */

File diff suppressed because it is too large Load Diff

View 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) */

View 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) */