From c3be9e23a392669c803d4d5d18324ee7515964a5 Mon Sep 17 00:00:00 2001
From: Peter Loes <54844299+peterloes@users.noreply.github.com>
Date: Wed, 9 Oct 2019 09:02:31 +0200
Subject: [PATCH] Add files via upload
---
Software/emlib/src/em_mpu.c | 124 ++++
Software/emlib/src/em_msc.c | 617 +++++++++++++++++
Software/emlib/src/em_opamp.c | 406 ++++++++++++
Software/emlib/src/em_pcnt.c | 762 +++++++++++++++++++++
Software/emlib/src/em_prs.c | 132 ++++
Software/emlib/src/em_rmu.c | 220 +++++++
Software/emlib/src/em_rtc.c | 362 ++++++++++
Software/emlib/src/em_system.c | 109 +++
Software/emlib/src/em_timer.c | 304 +++++++++
Software/emlib/src/em_usart.c | 1128 ++++++++++++++++++++++++++++++++
Software/emlib/src/em_vcmp.c | 184 ++++++
Software/emlib/src/em_wdog.c | 224 +++++++
12 files changed, 4572 insertions(+)
create mode 100644 Software/emlib/src/em_mpu.c
create mode 100644 Software/emlib/src/em_msc.c
create mode 100644 Software/emlib/src/em_opamp.c
create mode 100644 Software/emlib/src/em_pcnt.c
create mode 100644 Software/emlib/src/em_prs.c
create mode 100644 Software/emlib/src/em_rmu.c
create mode 100644 Software/emlib/src/em_rtc.c
create mode 100644 Software/emlib/src/em_system.c
create mode 100644 Software/emlib/src/em_timer.c
create mode 100644 Software/emlib/src/em_usart.c
create mode 100644 Software/emlib/src/em_vcmp.c
create mode 100644 Software/emlib/src/em_wdog.c
diff --git a/Software/emlib/src/em_mpu.c b/Software/emlib/src/em_mpu.c
new file mode 100644
index 0000000..48b01df
--- /dev/null
+++ b/Software/emlib/src/em_mpu.c
@@ -0,0 +1,124 @@
+/***************************************************************************//**
+ * @file
+ * @brief Memory Protection Unit (MPU) Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_mpu.h"
+#if defined(__MPU_PRESENT) && (__MPU_PRESENT == 1)
+#include "em_assert.h"
+
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+
+/***************************************************************************//**
+ * @addtogroup MPU
+ * @brief Memory Protection Unit (MPU) Peripheral API
+ * @details
+ * This module contains functions to enable, disable and setup the MPU.
+ * The MPU is used to control access attributes and permissions in the
+ * memory map. The settings that can be controlled are:
+ *
+ * @li Executable attribute.
+ * @li Cachable, bufferable and shareable attributes.
+ * @li Cache policy.
+ * @li Access permissions: Priviliged or User state, read or write access,
+ * and combinations of all these.
+ *
+ * The MPU can be activated and deactivated with functions:
+ * @verbatim
+ * MPU_Enable(..);
+ * MPU_Disable();@endverbatim
+ * The MPU can control 8 memory regions with individual access control
+ * settings. Section attributes and permissions are set with:
+ * @verbatim
+ * MPU_ConfigureRegion(..);@endverbatim
+ * It is advisable to disable the MPU when altering region settings.
+ *
+ *
+ * @{
+ ******************************************************************************/
+
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+
+/***************************************************************************//**
+ * @brief
+ * Configure an MPU region.
+ *
+ * @details
+ * Writes to MPU RBAR and RASR registers.
+ * Refer to Cortex-M3 Reference Manual, MPU chapter for further details.
+ * To disable a region it is only required to set init->regionNo to the
+ * desired value and init->regionEnable = false.
+ *
+ * @param[in] init
+ * Pointer to a structure containing MPU region init information.
+ ******************************************************************************/
+void MPU_ConfigureRegion(const MPU_RegionInit_TypeDef *init)
+{
+ EFM_ASSERT(init->regionNo < ((MPU->TYPE & MPU_TYPE_DREGION_Msk) >>
+ MPU_TYPE_DREGION_Pos));
+
+ MPU->RNR = init->regionNo;
+
+ if (init->regionEnable)
+ {
+ EFM_ASSERT(!(init->baseAddress & ~MPU_RBAR_ADDR_Msk));
+ EFM_ASSERT(init->tex <= 0x7);
+
+ MPU->RBAR = init->baseAddress;
+ MPU->RASR = ((init->disableExec ? 1 : 0) << MPU_RASR_XN_Pos) |
+ (init->accessPermission << MPU_RASR_AP_Pos) |
+ (init->tex << MPU_RASR_TEX_Pos) |
+ ((init->shareable ? 1 : 0) << MPU_RASR_S_Pos) |
+ ((init->cacheable ? 1 : 0) << MPU_RASR_C_Pos) |
+ ((init->bufferable ? 1 : 0) << MPU_RASR_B_Pos) |
+ (init->srd << MPU_RASR_SRD_Pos) |
+ (init->size << MPU_RASR_SIZE_Pos) |
+ (1 << MPU_RASR_ENABLE_Pos);
+ }
+ else
+ {
+ MPU->RBAR = 0;
+ MPU->RASR = 0;
+ }
+}
+
+
+/** @} (end addtogroup CMU) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(__MPU_PRESENT) && (__MPU_PRESENT == 1) */
diff --git a/Software/emlib/src/em_msc.c b/Software/emlib/src/em_msc.c
new file mode 100644
index 0000000..25cea98
--- /dev/null
+++ b/Software/emlib/src/em_msc.c
@@ -0,0 +1,617 @@
+/***************************************************************************//**
+ * @file
+ * @brief Flash controller (MSC) Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_msc.h"
+#if defined(MSC_COUNT) && (MSC_COUNT > 0)
+
+#include "em_system.h"
+#if defined( _MSC_TIMEBASE_MASK )
+#include "em_cmu.h"
+#endif
+#include "em_assert.h"
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#if defined( MSC_WRITECTRL_WDOUBLE )
+#define WORDS_PER_DATA_PHASE (FLASH_SIZE<(512*1024) ? 1 : 2)
+#else
+#define WORDS_PER_DATA_PHASE (1)
+#endif
+
+
+#ifdef __CC_ARM /* MDK-ARM compiler */
+msc_Return_TypeDef MscLoadData(uint32_t *data, int num);
+msc_Return_TypeDef MscLoadAddress(uint32_t *address);
+#endif /* __CC_ARM */
+
+#ifdef __ICCARM__ /* IAR compiler */
+__ramfunc msc_Return_TypeDef MscLoadData(uint32_t *data, int num);
+__ramfunc msc_Return_TypeDef MscLoadAddress(uint32_t *address);
+#endif /* __ICCARM__ */
+
+#ifdef __GNUC__ /* GCC based compilers */
+#ifdef __CROSSWORKS_ARM /* Rowley Crossworks */
+msc_Return_TypeDef MscLoadData(uint32_t *data, int num) __attribute__ ((section(".fast")));
+msc_Return_TypeDef MscLoadAddress(uint32_t *address) __attribute__ ((section(".fast")));
+#else /* Sourcery G++ */
+msc_Return_TypeDef MscLoadData(uint32_t *data, int num) __attribute__ ((section(".ram")));
+msc_Return_TypeDef MscLoadAddress(uint32_t *address) __attribute__ ((section(".ram")));
+#endif /* __CROSSWORKS_ARM */
+#endif /* __GNUC__ */
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup MSC
+ * @brief Flash controller (MSC) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Enables the flash controller for writing.
+ * @note
+ * IMPORTANT: This function must be called before flash operations when
+ * AUXHFRCO clock has been changed from default 14MHz band.
+ ******************************************************************************/
+void MSC_Init(void)
+{
+#if defined( _MSC_TIMEBASE_MASK )
+ uint32_t freq, cycles;
+#endif
+ /* Unlock the MSC */
+ MSC->LOCK = MSC_UNLOCK_CODE;
+ /* Disable writing to the flash */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+
+#if defined( _MSC_TIMEBASE_MASK )
+ /* Configure MSC->TIMEBASE according to selected frequency */
+ freq = CMU_ClockFreqGet(cmuClock_AUX);
+
+ if (freq > 7000000)
+ {
+ /* Calculate number of clock cycles for 1us as base period */
+ freq = (freq * 11) / 10;
+ cycles = (freq / 1000000) + 1;
+
+ /* Configure clock cycles for flash timing */
+ MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK |
+ _MSC_TIMEBASE_PERIOD_MASK)) |
+ MSC_TIMEBASE_PERIOD_1US |
+ (cycles << _MSC_TIMEBASE_BASE_SHIFT);
+ }
+ else
+ {
+ /* Calculate number of clock cycles for 5us as base period */
+ freq = (freq * 5 * 11) / 10;
+ cycles = (freq / 1000000) + 1;
+
+ /* Configure clock cycles for flash timing */
+ MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK |
+ _MSC_TIMEBASE_PERIOD_MASK)) |
+ MSC_TIMEBASE_PERIOD_5US |
+ (cycles << _MSC_TIMEBASE_BASE_SHIFT);
+ }
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Disables the flash controller for writing.
+ ******************************************************************************/
+void MSC_Deinit(void)
+{
+ /* Disable writing to the flash */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ /* Lock the MSC */
+ MSC->LOCK = 0;
+}
+
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ * Perform address phase of FLASH write cycle.
+ * @details
+ * This function performs the address phase of a Flash write operation by
+ * writing the given flash address to the ADDRB register and issuing the
+ * LADDRIM command to load the address.
+ * @note
+ * This function MUST be executed from RAM. Failure to execute this portion
+ * of the code in RAM will result in a hardfault. For IAR, Rowley and
+ * Codesourcery this will be achieved automatically. For Keil uVision 4 you
+ * must define a section called "ram_code" and place this manually in your
+ * project's scatter file.
+ * @param[in] address
+ * Address in flash memory. Must be aligned at a 4 byte boundary.
+ * @return
+ * Returns the status of the address load operation, #msc_Return_TypeDef
+ * @verbatim
+ * mscReturnOk - Operation completed successfully.
+ * mscReturnInvalidAddr - Operation tried to erase a non-flash area.
+ * mscReturnLocked - Operation tried to erase a locked area of the flash.
+ * @endverbatim
+ ******************************************************************************/
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code="ram_code"
+#endif /* __CC_ARM */
+#if defined(__ICCARM__)
+/* Suppress warnings originating from use of EFM_ASSERT(): */
+/* "Call to a non __ramfunc function from within a __ramfunc function" */
+/* "Possible rom access from within a __ramfunc function" */
+#pragma diag_suppress=Ta022
+#pragma diag_suppress=Ta023
+#endif
+
+msc_Return_TypeDef MscLoadAddress(uint32_t* address)
+{
+ uint32_t status;
+
+ /* Load address */
+ MSC->ADDRB = (uint32_t) (address);
+ MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
+
+ status = MSC->STATUS;
+ if (status & (MSC_STATUS_INVADDR | MSC_STATUS_LOCKED))
+ {
+ /* Check for invalid address */
+ if (status & MSC_STATUS_INVADDR)
+ return mscReturnInvalidAddr;
+ /* Check for write protected page */
+ if (status & MSC_STATUS_LOCKED)
+ return mscReturnLocked;
+ }
+ return mscReturnOk;
+}
+
+#if defined(__ICCARM__)
+#pragma diag_default=Ta022
+#pragma diag_default=Ta023
+#endif
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code
+#endif /* __CC_ARM */
+
+
+
+/***************************************************************************//**
+ * @brief
+ * Perform data phase of FLASH write cycle.
+ * @details
+ * This function performs the data phase of a Flash write operation by loading
+ * the given number of 32-bit words to the WDATA register.
+ * @note
+ * This function MUST be executed from RAM. Failure to execute this portion
+ * of the code in RAM will result in a hardfault. For IAR, Rowley and
+ * Codesourcery this will be achieved automatically. For Keil uVision 4 you
+ * must define a section called "ram_code" and place this manually in your
+ * project's scatter file.
+ * @param[in] data
+ * Pointer to the first data word to load.
+ * @param[in] num
+ * Number of data words (32-bit) to load.
+ * @return
+ * Returns the status of the data load operation, #msc_Return_TypeDef
+ * @verbatim
+ * mscReturnOk - Operation completed successfully.
+ * mscReturnTimeOut - Operation timed out waiting for flash operation
+ * to complete.
+ * @endverbatim
+ ******************************************************************************/
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code="ram_code"
+#endif /* __CC_ARM */
+#if defined(__ICCARM__)
+/* Suppress warnings originating from use of EFM_ASSERT(): */
+/* "Call to a non __ramfunc function from within a __ramfunc function" */
+/* "Possible rom access from within a __ramfunc function" */
+#pragma diag_suppress=Ta022
+#pragma diag_suppress=Ta023
+#endif
+
+msc_Return_TypeDef MscLoadData(uint32_t* data, int num)
+{
+ int timeOut = MSC_PROGRAM_TIMEOUT;
+ int i;
+
+ /* Wait for the MSC to be ready for a new data word.
+ * Due to the timing of this function, the MSC should
+ * already by ready */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while (((MSC->STATUS & MSC_STATUS_WDATAREADY) == 0) && (timeOut != 0))
+ {
+ timeOut--;
+ }
+
+ /* Check for timeout */
+ if (timeOut == 0)
+ return mscReturnTimeOut;
+
+ /* Load 'num' 32-bit words into write data register. */
+ for (i=0; iWDATA = *data;
+
+ /* Trigger write once */
+ MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
+
+ /* Wait for the write to complete */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0))
+ {
+ timeOut--;
+ }
+
+ /* Check for timeout */
+ if (timeOut == 0) return mscReturnTimeOut;
+
+ return mscReturnOk;
+}
+#if defined(__ICCARM__)
+#pragma diag_default=Ta022
+#pragma diag_default=Ta023
+#endif
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code
+#endif /* __CC_ARM */
+
+/** @endcond */
+
+
+/***************************************************************************//**
+ * @brief
+ * Erases a page in flash memory.
+ * @note
+ * This function MUST be executed from RAM. Failure to execute this portion
+ * of the code in RAM will result in a hardfault. For IAR, Rowley and
+ * Codesourcery this will be achieved automatically. For Keil uVision 4 you
+ * must define a section called "ram_code" and place this manually in your
+ * project's scatter file.
+ * @param[in] startAddress
+ * Pointer to the flash page to erase. Must be aligned to beginning of page
+ * boundary.
+ * @return
+ * Returns the status of erase operation, #msc_Return_TypeDef
+ * @verbatim
+ * mscReturnOk - Operation completed successfully.
+ * mscReturnInvalidAddr - Operation tried to erase a non-flash area.
+ * mscReturnLocked - Operation tried to erase a locked area of the flash.
+ * mscReturnTimeOut - Operation timed out waiting for flash operation
+ * to complete.
+ * @endverbatim
+ ******************************************************************************/
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code="ram_code"
+#endif /* __CC_ARM */
+#if defined(__ICCARM__)
+/* Suppress warnings originating from use of EFM_ASSERT(): */
+/* "Call to a non __ramfunc function from within a __ramfunc function" */
+/* "Possible rom access from within a __ramfunc function" */
+#pragma diag_suppress=Ta022
+#pragma diag_suppress=Ta023
+#endif
+msc_Return_TypeDef MSC_ErasePage(uint32_t *startAddress)
+{
+ int timeOut = MSC_PROGRAM_TIMEOUT;
+
+ /* Address must be aligned to pages */
+ EFM_ASSERT((((uint32_t) startAddress) & (FLASH_PAGE_SIZE - 1)) == 0);
+
+ /* Enable writing to the MSC */
+ MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
+
+ /* Load address */
+ MSC->ADDRB = (uint32_t) startAddress;
+ MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
+
+ /* Check for invalid address */
+ if (MSC->STATUS & MSC_STATUS_INVADDR)
+ {
+ /* Disable writing to the MSC */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnInvalidAddr;
+ }
+
+ /* Check for write protected page */
+ if (MSC->STATUS & MSC_STATUS_LOCKED)
+ {
+ /* Disable writing to the MSC */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnLocked;
+ }
+
+ /* Send erase page command */
+ MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
+
+ /* Wait for the erase to complete */
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0))
+ {
+ timeOut--;
+ }
+
+ if (timeOut == 0)
+ {
+ /* Disable writing to the MSC */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnTimeOut;
+ }
+
+ /* Disable writing to the MSC */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnOk;
+}
+#if defined(__ICCARM__)
+#pragma diag_default=Ta022
+#pragma diag_default=Ta023
+#endif
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code
+#endif /* __CC_ARM */
+
+
+
+/***************************************************************************//**
+ * @brief
+ * Writes a single word to flash memory. Data to write must be aligned to
+ * words and contain a number of bytes that is divisable by four.
+ * @note
+ * The flash must be erased prior to writing a new word.
+ * This function must be run from RAM. Failure to execute this portion
+ * of the code in RAM will result in a hardfault. For IAR, Rowley and
+ * Codesourcery this will be achieved automatically. For Keil uVision 4 you
+ * must define a section called "ram_code" and place this manually in your
+ * project's scatter file.
+ *
+ * @param[in] address
+ * Pointer to the flash word to write to. Must be aligned to words.
+ * @param[in] data
+ * Data to write to flash.
+ * @param[in] numBytes
+ * Number of bytes to write from flash. NB: Must be divisable by four.
+ * @return
+ * Returns the status of the write operation, #msc_Return_TypeDef
+ * @verbatim
+ * flashReturnOk - Operation completed successfully.
+ * flashReturnInvalidAddr - Operation tried to erase a non-flash area.
+ * flashReturnLocked - Operation tried to erase a locked area of the flash.
+ * flashReturnTimeOut - Operation timed out waiting for flash operation
+ * to complete.
+ * @endverbatim
+ ******************************************************************************/
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code="ram_code"
+#endif /* __CC_ARM */
+#if defined(__ICCARM__)
+/* Suppress warnings originating from use of EFM_ASSERT(): */
+/* "Call to a non __ramfunc function from within a __ramfunc function" */
+/* "Possible rom access from within a __ramfunc function" */
+#pragma diag_suppress=Ta022
+#pragma diag_suppress=Ta023
+#endif
+
+msc_Return_TypeDef MSC_WriteWord(uint32_t *address, void const *data, int numBytes)
+{
+ int wordCount;
+ int numWords;
+#if defined(_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)
+ int pageWords;
+ uint32_t* pData;
+#endif
+ msc_Return_TypeDef retval = mscReturnOk;
+
+ /* Check alignment (Must be aligned to words) */
+ EFM_ASSERT(((uint32_t) address & 0x3) == 0);
+
+ /* Check number of bytes. Must be divisable by four */
+ EFM_ASSERT((numBytes & 0x3) == 0);
+
+ /* Enable writing to the MSC */
+ MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
+
+ /* Convert bytes to words */
+ numWords = numBytes >> 2;
+
+#if (defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)) && (2==WORDS_PER_DATA_PHASE)
+
+ /* For the Giant and Wonder families we want to use the double word write
+ * feature in order to improve the speed. The devices with flash size
+ * larger than 512KiBytes support double word write cycles. The address is
+ * loaded by the software for the first word, and automatically incremented
+ * by the hardware for the second word. However this will not work across a
+ * page boundary, so we need to align the address of double word write
+ * cycles to an even address.
+ */
+
+ if ((((uint32_t) address) % FLASH_PAGE_SIZE + numBytes)
+ > FLASH_PAGE_SIZE)
+ {
+ if (((uint32_t) address) & 0x7)
+ {
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
+ retval = MscLoadAddress(address);
+ if (mscReturnOk != retval) goto msc_write_word_exit;
+ retval = MscLoadData((uint32_t *) data, 1);
+ if (mscReturnOk != retval) goto msc_write_word_exit;
+ data = (void *) ((uint32_t) data + sizeof(uint32_t));
+ address++;
+ numWords--;
+ }
+ }
+
+ /* If there is an odd number of words remaining to be written,
+ * we write the last word now because this has to be written
+ * as a single word and will simplify the for() loop below writing
+ * double words. */
+
+ if (numWords & 0x1)
+ {
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
+ retval = MscLoadAddress(address + numWords - 1);
+ if (mscReturnOk != retval) goto msc_write_word_exit;
+ retval = MscLoadData(((uint32_t *) data) + numWords - 1, 1);
+ if (mscReturnOk != retval) goto msc_write_word_exit;
+ numWords--;
+ }
+
+ MSC->WRITECTRL |= MSC_WRITECTRL_WDOUBLE;
+
+#endif /* (defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)) && (2==WORDS_PER_DATA_PHASE) */
+
+#if defined(_EFM32_TINY_FAMILY) || defined (_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)
+
+ pData = (uint32_t*) data;
+
+ /* The following loop splits the data into chunks corresponding to flash pages.
+ The address is loaded only once per page, because the hardware automatically
+ increments the address internally for each data load inside a page. */
+ for (wordCount = 0; wordCount < numWords; )
+ {
+ /* First we load address. The address is auto-incremented within a page.
+ Therefore the address phase is only needed once for each page. */
+ retval = MscLoadAddress(address + wordCount);
+ if (mscReturnOk != retval) goto msc_write_word_exit;
+
+ /* Compute the number of words to write to the current page. */
+ pageWords =
+ (FLASH_PAGE_SIZE - ((uint32_t) (address + wordCount)) % FLASH_PAGE_SIZE) /
+ sizeof(uint32_t);
+ if (pageWords > numWords-wordCount)
+ pageWords = numWords-wordCount;
+ wordCount += pageWords;
+
+ /* Now program the data in this page. */
+ for (; pageWords; pData+=WORDS_PER_DATA_PHASE, pageWords-=WORDS_PER_DATA_PHASE)
+ {
+ retval = MscLoadData(pData, WORDS_PER_DATA_PHASE);
+ if (mscReturnOk != retval)
+ goto msc_write_word_exit;
+ }
+ }
+
+#else /* _EFM32_GECKO_FAMILY */
+
+ for (wordCount = 0; wordCount < numWords; wordCount++)
+ {
+ retval = MscLoadAddress(address + wordCount);
+ if (mscReturnOk != retval)
+ goto msc_write_word_exit;
+ retval = MscLoadData(((uint32_t *) data) + wordCount, 1);
+ if (mscReturnOk != retval)
+ goto msc_write_word_exit;
+ }
+
+#endif
+
+ msc_write_word_exit:
+
+ /* Disable writing to the MSC */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+
+#if (defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)) && (2==WORDS_PER_DATA_PHASE)
+ /* Turn off double word write cycle support. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
+#endif
+
+ return retval;
+}
+
+#if defined(__ICCARM__)
+#pragma diag_default=Ta022
+#pragma diag_default=Ta023
+#endif
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code
+#endif /* __CC_ARM */
+
+
+#if defined( _MSC_MASSLOCK_MASK )
+/***************************************************************************//**
+ * @brief
+ * Erase 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 successful mass erase.
+ ******************************************************************************/
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code="ram_code"
+#endif /* __CC_ARM */
+msc_Return_TypeDef MSC_MassErase(void)
+{
+ /* Enable writing to the MSC */
+ MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
+
+ /* Unlock device mass erase */
+ MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_UNLOCK;
+
+ /* Erase first 512K block */
+ MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN0;
+
+ /* Waiting for erase to complete */
+ while ((MSC->STATUS & MSC_STATUS_BUSY))
+ {
+ }
+
+#if FLASH_SIZE >= (512 * 1024)
+ /* Erase second 512K block */
+ MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN1;
+
+ /* Waiting for erase to complete */
+ while ((MSC->STATUS & MSC_STATUS_BUSY))
+ {
+ }
+#endif
+
+ /* Restore mass erase lock */
+ MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_LOCK;
+
+ /* This will only successfully return if calling function is also in SRAM */
+ return mscReturnOk;
+}
+#ifdef __CC_ARM /* MDK-ARM compiler */
+#pragma arm section code
+#endif /* __CC_ARM */
+#endif
+
+/** @} (end addtogroup MSC) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(MSC_COUNT) && (MSC_COUNT > 0) */
diff --git a/Software/emlib/src/em_opamp.c b/Software/emlib/src/em_opamp.c
new file mode 100644
index 0000000..8f3c239
--- /dev/null
+++ b/Software/emlib/src/em_opamp.c
@@ -0,0 +1,406 @@
+/**************************************************************************//**
+ * @file
+ * @brief Operational Amplifier (OPAMP) peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ ******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_device.h"
+#if defined( OPAMP_PRESENT ) && ( OPAMP_COUNT == 1 )
+
+#include "em_system.h"
+#include "em_assert.h"
+#include "em_opamp.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+
+/***************************************************************************//**
+ * @addtogroup OPAMP
+ * @brief Operational Amplifier (OPAMP) peripheral API
+ * @details
+ * This module contains functions to:
+ * @li OPAMP_Enable() Configure and enable an opamp.
+ * @li OPAMP_Disable() Disable an opamp.
+ *
+ * All OPAMP functions assume that the DAC clock is running. If the DAC is not
+ * used, the clock can be turned off when the opamp's are configured.
+ *
+ * If the available gain values dont 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
+ * The terms POSPAD and NEGPAD in the figures are used to indicate that these
+ * pads should be connected to a suitable signal ground.
+ *
+ * \nUnity gain voltage follower.\n
+ * Use predefined macros @ref OPA_INIT_UNITY_GAIN and
+ * @ref OPA_INIT_UNITY_GAIN_OPA2.
+ * @verbatim
+
+ |\
+ ___________|+\
+ | \_______
+ ___|_ / |
+ | | / |
+ | |/ |
+ |___________|
+ @endverbatim
+ *
+ * \nNon-inverting amplifier.\n
+ * Use predefined macros @ref OPA_INIT_NON_INVERTING and
+ * @ref OPA_INIT_NON_INVERTING_OPA2.
+ * @verbatim
+
+ |\
+ ___________|+\
+ | \_______
+ ___|_ / |
+ | | / |
+ | |/ |
+ |_____R2____|
+ |
+ R1
+ |
+ NEGPAD @endverbatim
+ *
+ * \nInverting amplifier.\n
+ * Use predefined macros @ref OPA_INIT_INVERTING and
+ * @ref OPA_INIT_INVERTING_OPA2.
+ * @verbatim
+
+ _____R2____
+ | |
+ | |\ |
+ ____R1_|___|_\ |
+ | \____|___
+ ___| /
+ | |+/
+ | |/
+ |
+ POSPAD @endverbatim
+ *
+ * \nCascaded non-inverting amplifiers.\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
+ *
+ * \nCascaded inverting amplifiers.\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
+ *
+ * \nDifferential driver with two opamp's.\n
+ * Use predefined macros @ref OPA_INIT_DIFF_DRIVER_OPA0 and
+ * @ref OPA_INIT_DIFF_DRIVER_OPA1.
+ * @verbatim
+
+ __________________________
+ | +
+ | _____R2____
+ |\ | | |
+ ___________|+\ OPA0 | | |\ OPA1 |
+ | \_________|____R1_|___|_\ | _
+ ___|_ / | | \____|______
+ | | / | ___| /
+ | |/ | | |+/
+ |________________| | |/
+ |
+ POSPAD @endverbatim
+ *
+ * \nDifferential receiver with three opamp's.\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
+ *
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Disable an Operational Amplifier.
+ *
+ * @param[in] dac
+ * Pointer to DAC peripheral register block.
+ *
+ * @param[in] opa
+ * Selects an OPA, valid vaules are @ref OPA0, @ref OPA1 and @ref OPA2.
+ ******************************************************************************/
+void OPAMP_Disable( DAC_TypeDef *dac, OPAMP_TypeDef opa )
+{
+ 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;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Configure and enable an Operational Amplifier.
+ *
+ * @details
+ *
+ * @param[in] dac
+ * Pointer to DAC peripheral register block.
+ *
+ * @param[in] opa
+ * Selects an OPA, valid vaules are @ref OPA0, @ref OPA1 and @ref OPA2.
+ *
+ * @param[in] init
+ * Pointer to a structure containing OPAMP init information.
+ ******************************************************************************/
+void OPAMP_Enable( DAC_TypeDef *dac, OPAMP_TypeDef opa, const OPAMP_Init_TypeDef *init )
+{
+ uint32_t offset;
+
+ 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 )
+ {
+ offset = SYSTEM_GetCalibrationValue( &dac->CAL );
+ dac->CAL = ( dac->CAL & ~_DAC_CAL_CH0OFFSET_MASK ) |
+ ( offset & _DAC_CAL_CH0OFFSET_MASK );
+ }
+ 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 )
+ {
+ offset = SYSTEM_GetCalibrationValue( &dac->CAL );
+ dac->CAL = ( dac->CAL & ~_DAC_CAL_CH1OFFSET_MASK ) |
+ ( offset & _DAC_CAL_CH1OFFSET_MASK );
+ }
+ 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 )
+ {
+ offset = SYSTEM_GetCalibrationValue( &dac->OPAOFFSET );
+ dac->OPAOFFSET = ( dac->OPAOFFSET & ~_DAC_OPAOFFSET_OPA2OFFSET_MASK ) |
+ ( offset & _DAC_OPAOFFSET_OPA2OFFSET_MASK );
+ }
+ 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 );
+ }
+}
+
+/** @} (end addtogroup OPAMP) */
+/** @} (end addtogroup EM_Library) */
+
+#endif /* defined( OPAMP_PRESENT ) && ( OPAMP_COUNT == 1 ) */
diff --git a/Software/emlib/src/em_pcnt.c b/Software/emlib/src/em_pcnt.c
new file mode 100644
index 0000000..1020404
--- /dev/null
+++ b/Software/emlib/src/em_pcnt.c
@@ -0,0 +1,762 @@
+/***************************************************************************//**
+ * @file
+ * @brief Pulse Counter (PCNT) peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_pcnt.h"
+#if defined(PCNT_COUNT) && (PCNT_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_assert.h"
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup PCNT
+ * @brief Pulse Counter (PCNT) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+
+/** Validation of PCNT register block pointer reference for assert statements. */
+#if (PCNT_COUNT == 1)
+#define PCNT_REF_VALID(ref) ((ref) == PCNT0)
+#elif (PCNT_COUNT == 2)
+#define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1))
+#elif (PCNT_COUNT == 3)
+#define PCNT_REF_VALID(ref) (((ref) == PCNT0) || ((ref) == PCNT1) || \
+ ((ref) == PCNT2))
+#else
+#error Undefined number of pulse counters (PCNT).
+#endif
+
+/** @endcond */
+
+
+/*******************************************************************************
+ ************************** LOCAL FUNCTIONS ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ * Map PCNT structure into instance number.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block
+ *
+ * @return
+ * Instance number.
+ ******************************************************************************/
+__STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt)
+{
+ return(((uint32_t)pcnt - PCNT0_BASE) / 0x400);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Wait for ongoing sync of register(s) to low frequency domain to complete.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block
+ *
+ * @param[in] mask
+ * 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 (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE)
+ {
+ return;
+ }
+
+ /* Wait for any pending previous write operation to have been completed in low
+ * frequency domain. */
+ while (pcnt->SYNCBUSY & mask)
+ ;
+}
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Reset PCNT counters and TOP register.
+ *
+ * @note
+ * Notice that special SYNCBUSY handling is not applicable for the RSTEN
+ * bit of the control register, so we don't need to wait for it when only
+ * modifying RSTEN. (It would mean undefined wait time if clocked by external
+ * clock.) The SYNCBUSY bit will however be set, leading to a synchronization
+ * in the LF domain, with in reality no changes.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ ******************************************************************************/
+void PCNT_CounterReset(PCNT_TypeDef *pcnt)
+{
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+ /* Enable reset of CNT and TOP register */
+ BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
+
+ /* Disable reset of CNT and TOP register */
+ BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Set counter and top values.
+ *
+ * @details
+ * The pulse counter is disabled while changing these values, and reenabled
+ * (if originally enabled) when values have 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 PCNT peripheral register block.
+ *
+ * @param[in] count
+ * Value to set in counter register.
+ *
+ * @param[in] top
+ * Value to set in top register.
+ ******************************************************************************/
+void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top)
+{
+ uint32_t ctrl;
+
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+#ifdef PCNT0
+ if (PCNT0 == pcnt)
+ {
+ EFM_ASSERT((1< count);
+ EFM_ASSERT((1< top);
+ }
+#endif
+
+#ifdef PCNT1
+ if (PCNT1 == pcnt)
+ {
+ EFM_ASSERT((1< count);
+ EFM_ASSERT((1< top);
+ }
+#endif
+
+#ifdef PCNT2
+ if (PCNT2 == pcnt)
+ {
+ EFM_ASSERT((1< count);
+ EFM_ASSERT((1< top);
+ }
+#endif
+
+ /* Keep current control setting, must be restored */
+ ctrl = pcnt->CTRL;
+
+ /* If enabled, disable pulse counter before changing values */
+ if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
+ {
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+ pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE;
+ }
+
+ /* Load into TOPB */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
+ pcnt->TOPB = count;
+
+ /* Load TOPB value into TOP */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
+
+ /* This bit has no effect on rev. C and onwards parts - for compatibility */
+ pcnt->CMD = PCNT_CMD_LTOPBIM;
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
+
+ /* Load TOP into CNT */
+ pcnt->CMD = PCNT_CMD_LCNTIM;
+
+ /* Restore TOP? ('count' setting has been loaded into pcnt->TOP, better
+ * to use 'top' than pcnt->TOP in compare, since latter may in theory not
+ * be visible yet.) */
+ if (top != count)
+ {
+ /* Wait for command to sync LCNTIM before setting TOPB */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
+
+ /* Load into TOPB, we don't need to check for TOPB sync complete here,
+ * it has been ensured above. */
+ pcnt->TOPB = top;
+
+ /* Load TOPB value into TOP */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
+ pcnt->CMD = PCNT_CMD_LTOPBIM;
+ }
+
+ /* Reenable if it was enabled */
+ if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
+ {
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD);
+ pcnt->CTRL = ctrl;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Set PCNT operational mode.
+ *
+ * @details
+ * Notice that this function does not do any configuration. Setting operational
+ * mode is normally only required after initialization is done, and if not
+ * done as part of initialization. Or if requiring to disable/reenable pulse
+ * counter.
+ *
+ * @note
+ * This function may 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.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ *
+ * @param[in] mode
+ * Operational mode to use for PCNT.
+ ******************************************************************************/
+void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode)
+{
+ uint32_t tmp;
+
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+ /* Set as specified */
+ tmp = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK;
+ tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT;
+
+ /* LF register about to be modified require sync. busy check */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+ pcnt->CTRL = tmp;
+}
+
+#if defined( _PCNT_INPUT_MASK )
+/***************************************************************************//**
+ * @brief
+ * Enable/disable the selected PRS input of PCNT.
+ *
+ * @details
+ * Notice that this function does not do any configuration.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ *
+ * @param[in] prsInput
+ * PRS input (S0 or S1) of the selected PCNT module.
+ *
+ * @param[in] enable
+ * Set to true to enable, false to disable the selected PRS input.
+ ******************************************************************************/
+void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
+ PCNT_PRSInput_TypeDef prsInput,
+ bool enable)
+{
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+ /* Enable/disable the selected PRS input on the selected PCNT module. */
+ switch (prsInput)
+ {
+ /* Enable/disable PRS input S0. */
+ case pcntPRSInputS0:
+ {
+ BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, (uint32_t)enable);
+ }
+ break;
+
+ /* Enable/disable PRS input S1. */
+ case pcntPRSInputS1:
+ {
+ BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, (uint32_t)enable);
+ }
+ break;
+
+ /* Invalid parameter, asserted. */
+ default:
+ {
+ EFM_ASSERT(0);
+ }
+ break;
+ }
+}
+#endif
+
+
+/***************************************************************************//**
+ * @brief
+ * PCNT register synchronization freeze control.
+ *
+ * @details
+ * Some PCNT 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 PCNT synchronization to LF domain to complete (Normally
+ * synchronization will not be in progress.) However for this reason, when
+ * using freeze mode, modifications of registers requiring LF synchronization
+ * should be done within one freeze enable/disable block to avoid unecessary
+ * stalling.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ *
+ * @param[in] enable
+ * @li true - enable freeze, modified registers are not propagated to the
+ * LF domain
+ * @li false - disables freeze, modified registers are propagated to LF
+ * domain
+ ******************************************************************************/
+void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable)
+{
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+ if (enable)
+ {
+ /* Wait for any ongoing LF synchronization to complete. This is just to
+ * protect against the rare case when a user:
+ * - modifies a register requiring LF sync
+ * - then enables freeze before LF sync completed
+ * - then modifies the same register again
+ * since modifying a register while it is in sync progress should be
+ * avoided. */
+ while (pcnt->SYNCBUSY)
+ ;
+
+ pcnt->FREEZE = PCNT_FREEZE_REGFREEZE;
+ }
+ else
+ {
+ pcnt->FREEZE = 0;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Init pulse counter.
+ *
+ * @details
+ * This function will configure the pulse counter. The clock selection is
+ * configured as follows, depending on operational mode:
+ *
+ * @li #pcntModeOvsSingle - Use LFACLK.
+ * @li #pcntModeExtSingle - Use external PCNTn_S0 pin.
+ * @li #pcntModeExtQuad - Use external PCNTn_S0 pin.
+ *
+ * Notice that the LFACLK must be enabled in all modes, since some basic setup
+ * is done with this clock even if external pin clock usage mode is chosen.
+ * The pulse counter clock for the selected instance must also be enabled
+ * prior to init.
+ *
+ * Notice that pins used by the PCNT module must be properly configured
+ * by the user explicitly through setting the ROUTE register, in order for
+ * the PCNT to work as intended.
+ *
+ * Writing to CNT will not occur in external clock modes (EXTCLKQUAD and
+ * EXTCLKSINGLE) because the external clock rate is unknown. The user should
+ * handle it manually depending on the application
+ *
+ * TOPB is written for all modes but in external clock mode it will take
+ * 3 external clock cycles to sync to TOP
+ *
+ *
+ * @note
+ * Initializing requires synchronization into the low frequency domain. This
+ * may cause some delay.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to initialize.
+ ******************************************************************************/
+void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init)
+{
+ unsigned int inst;
+ uint32_t tmp;
+
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+#ifdef PCNT0
+ if (PCNT0 == pcnt)
+ {
+ EFM_ASSERT((1< init->counter);
+ EFM_ASSERT((1< init->top);
+ }
+#endif
+
+#ifdef PCNT1
+ if (PCNT1 == pcnt)
+ {
+ EFM_ASSERT((1< init->counter);
+ EFM_ASSERT((1< init->top);
+ }
+#endif
+
+#ifdef PCNT2
+ if (PCNT2 == pcnt)
+ {
+ EFM_ASSERT((1< init->counter);
+ EFM_ASSERT((1< init->top);
+ }
+#endif
+
+ /* Map pointer to instance */
+ inst = PCNT_Map(pcnt);
+
+#if defined( _PCNT_INPUT_MASK )
+ /* Selecting the PRS channels for the PRS input sources of the PCNT. These are
+ * written with a Read-Modify-Write sequence in order to keep the value of the
+ * input enable bits which can be modified using PCNT_PRSInputEnable(). */
+ tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK);
+ tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT) |
+ ((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT);
+ pcnt->INPUT = tmp;
+#endif
+
+ /* Build CTRL setting, except for mode */
+ tmp = 0;
+ if (init->negEdge)
+ {
+ tmp |= PCNT_CTRL_EDGE_NEG;
+ }
+
+ if (init->countDown)
+ {
+ tmp |= PCNT_CTRL_CNTDIR_DOWN;
+ }
+
+ if (init->filter)
+ {
+ tmp |= PCNT_CTRL_FILT;
+ }
+
+#if defined( PCNT_CTRL_HYST )
+ if (init->hyst)
+ {
+ tmp |= PCNT_CTRL_HYST;
+ }
+#endif
+
+#if defined( PCNT_CTRL_S1CDIR )
+ if (init->s1CntDir)
+ {
+ tmp |= PCNT_CTRL_S1CDIR;
+ }
+#endif
+
+ /* Configure counter events for regular and auxiliary counter. */
+#if defined( _PCNT_CTRL_CNTEV_SHIFT )
+ tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT;
+#endif
+
+#if defined( _PCNT_CTRL_AUXCNTEV_SHIFT )
+ {
+ /* Modify the auxCntEvent value before writing to the AUXCNTEV field in
+ the CTRL register because the AUXCNTEV field values are different from
+ the CNTEV field values, and cntEvent and auxCntEvent are of the same type
+ PCNT_CntEvent_TypeDef.
+ */
+ uint32_t auxCntEventField = 0; /* Get rid of compiler warning. */
+ switch (init->auxCntEvent)
+ {
+ case pcntCntEventBoth:
+ auxCntEventField = pcntCntEventNone;
+ break;
+ case pcntCntEventNone:
+ auxCntEventField = pcntCntEventBoth;
+ break;
+ case pcntCntEventUp:
+ case pcntCntEventDown:
+ auxCntEventField = init->auxCntEvent;
+ break;
+ default:
+ /* Invalid parameter, asserted. */
+ EFM_ASSERT(0);
+ }
+ tmp |= auxCntEventField << _PCNT_CTRL_AUXCNTEV_SHIFT;
+ }
+#endif
+
+ /* Reset pulse counter while changing clock source. The reset bit */
+ /* is asynchronous, we don't have to check for SYNCBUSY. */
+ BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
+
+ /* Select LFACLK to clock in control setting */
+ CMU_PCNTClockExternalSet(inst, false);
+
+ /* Handling depends on whether using external clock or not. */
+ switch (init->mode)
+ {
+ case pcntModeExtSingle:
+ case pcntModeExtQuad:
+ tmp |= init->mode << _PCNT_CTRL_MODE_SHIFT;
+
+ /* In most cases, the SYNCBUSY bit is set due to reset bit set, and waiting
+ * for asynchronous reset bit is strictly not necessary.
+ * But in theory, other operations on CTRL register may have been done
+ * outside this function, so wait. */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+
+ /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
+ * the clock source to an external clock */
+ pcnt->CTRL = PCNT_CTRL_RSTEN;
+
+ /* Wait until CTRL write synchronized into LF domain. */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+
+ /* Change to external clock BEFORE disabling reset */
+ CMU_PCNTClockExternalSet(inst, true);
+
+ /* Write to TOPB. If using external clock TOPB will sync to TOP at the same
+ * time as the mode. This will insure that if the user chooses to count
+ * down, the first "countable" pulse will make CNT go to TOP and not 0xFF
+ * (default TOP value). */
+ pcnt->TOPB = init->top;
+
+ /* This bit has no effect on rev. C and onwards parts - for compatibility */
+ pcnt->CMD = PCNT_CMD_LTOPBIM;
+
+ /* Write the CTRL register with the configurations.
+ * This should be written after TOPB in the eventuality of a pulse between
+ * these two writes that would cause the CTRL register to be synced one
+ * clock cycle earlier than the TOPB. */
+ pcnt->CTRL = tmp;
+
+ /* There are no syncs for TOP, CMD or CTRL because the clock rate is unknown
+ * and the program could stall
+ * These will be synced within 3 clock cycles of the external clock /
+ * For the same reason CNT cannot be written here. */
+ break;
+
+ /* pcntModeDisable */
+ /* pcntModeOvsSingle */
+ default:
+ /* No need to set disabled mode if already disabled. */
+ if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
+ {
+ /* Set control to disabled mode, leave reset on until ensured disabled.
+ * We don't need to wait for CTRL SYNCBUSY completion here, it was
+ * triggered by reset bit above, which is asynchronous. */
+ pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN;
+
+ /* Wait until CTRL write synchronized into LF domain before proceeding
+ * to disable reset. */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+ }
+
+ /* Disable reset bit, counter should now be in disabled mode. */
+ BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
+
+ /* Set counter and top values as specified. */
+ PCNT_CounterTopSet(pcnt, init->counter, init->top);
+
+ /* Enter oversampling mode if selected. */
+ if (init->mode == pcntModeOvsSingle)
+ {
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+ pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT);
+ }
+ break;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Reset PCNT to same state as after a HW reset.
+ *
+ * @details
+ * Notice the LFACLK must be enabled, since some basic reset is done with
+ * this clock. The pulse counter clock for the selected instance must also
+ * be enabled prior to init.
+ *
+ * @note
+ * The ROUTE register is NOT reset by this function, in order to allow for
+ * centralized setup of this feature.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ ******************************************************************************/
+void PCNT_Reset(PCNT_TypeDef *pcnt)
+{
+ unsigned int inst;
+
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+ /* Map pointer to instance and clock info */
+ inst = PCNT_Map(pcnt);
+
+ pcnt->IEN = _PCNT_IEN_RESETVALUE;
+
+ /* Notice that special SYNCBUSY handling is not applicable for the RSTEN
+ * bit of the control register, so we don't need to wait for it when only
+ * modifying RSTEN. The SYNCBUSY bit will be set, leading to a
+ * synchronization in the LF domain, with in reality no changes to LF domain.
+ * Enable reset of CNT and TOP register. */
+ BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
+
+ /* Select LFACLK as default */
+ CMU_PCNTClockExternalSet(inst, false);
+
+ PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE);
+
+ /* Reset CTRL leaving RSTEN set */
+ pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN;
+
+ /* Disable reset after CTRL reg has been synchronized */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+ BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
+
+ /* Clear pending interrupts */
+ pcnt->IFC = _PCNT_IFC_MASK;
+
+ /* Do not reset route register, setting should be done independently */
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Set top buffer value.
+ *
+ * @note
+ * This function may 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.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ *
+ * @param[in] val
+ * Value to set in top buffer register.
+ ******************************************************************************/
+void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val)
+{
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+ /* LF register about to be modified require sync. busy check */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
+ pcnt->TOPB = val;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Set top value.
+ *
+ * @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.
+ *
+ * @param[in] pcnt
+ * Pointer to PCNT peripheral register block.
+ *
+ * @param[in] val
+ * Value to set in top register.
+ ******************************************************************************/
+void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val)
+{
+ EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+#ifdef PCNT0
+ if (PCNT0 == pcnt)
+ {
+ EFM_ASSERT((1< val);
+ }
+#endif
+
+#ifdef PCNT1
+ if (PCNT1 == pcnt)
+ {
+ EFM_ASSERT((1< val);
+ }
+#endif
+
+#ifdef PCNT2
+ if (PCNT2 == pcnt)
+ {
+ EFM_ASSERT((1< val);
+ }
+#endif
+
+ /* LF register about to be modified require sync. busy check */
+
+ /* Load into TOPB */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
+ pcnt->TOPB = val;
+
+ /* Load TOPB value into TOP */
+ PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
+ pcnt->CMD = PCNT_CMD_LTOPBIM;
+}
+
+
+/** @} (end addtogroup PCNT) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */
diff --git a/Software/emlib/src/em_prs.c b/Software/emlib/src/em_prs.c
new file mode 100644
index 0000000..f2c3288
--- /dev/null
+++ b/Software/emlib/src/em_prs.c
@@ -0,0 +1,132 @@
+/***************************************************************************//**
+ * @file
+ * @brief Peripheral Reflex System (PRS) Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_prs.h"
+#if defined(PRS_COUNT) && (PRS_COUNT > 0)
+
+#include "em_assert.h"
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup PRS
+ * @brief Peripheral Reflex System (PRS) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Set source and signal to be used for a channel.
+ *
+ * @param[in] ch
+ * Channel to define signal and source for.
+ *
+ * @param[in] source
+ * Source to select for channel. Use one of PRS_CH_CTRL_SOURCESEL_x defines.
+ *
+ * @param[in] signal
+ * Signal (for selected @p source) to use. Use one of PRS_CH_CTRL_SIGSEL_x
+ * defines.
+ *
+ * @param[in] edge
+ * Edge (for selected source/signal) to generate pulse for.
+ ******************************************************************************/
+void PRS_SourceSignalSet(unsigned int ch,
+ uint32_t source,
+ uint32_t signal,
+ PRS_Edge_TypeDef edge)
+{
+ 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;
+}
+
+#if defined( PRS_CH_CTRL_ASYNC )
+/***************************************************************************//**
+ * @brief
+ * Set source and asynchronous signal to be used 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: they can
+ * only be used by a subset of the reflex consumers. Please refer to PRS
+ * chapter in the reference manual for the complete list of supported
+ * asynchronous signals and consumers.
+ *
+ * @note
+ * This function is supported on the following device families:
+ * @li Giant Gecko (EFM32GGxxxFxxx)
+ * @li Leopard Gecko (EFM32LGxxxFxxx)
+ * @li Tiny Gecko (EFM32TGxxxFxxx)
+ * @li Wonder Gecko (EFM32WGxxxFxxx)
+ * @li Zero Gecko (EFM32ZGxxxFxxx)
+ * In asynchronous mode, the edge detector only works in EM0, hence it shall
+ * not be used. The EDSEL parameter in PRS_CHx_CTRL register is set to 0 (OFF)
+ * by default.
+ *
+ * @param[in] ch
+ * Channel to define source and asynchronous signal for.
+ *
+ * @param[in] source
+ * Source to select for channel. Use one of PRS_CH_CTRL_SOURCESEL_x defines.
+ *
+ * @param[in] signal
+ * Asynchronous signal (for selected @p source) to use. Use one of the
+ * PRS_CH_CTRL_SIGSEL_x defines that support asynchronous operation.
+ ******************************************************************************/
+void PRS_SourceAsyncSignalSet(unsigned int ch,
+ uint32_t source,
+ uint32_t signal)
+{
+ EFM_ASSERT(ch < PRS_CHAN_COUNT);
+
+ PRS->CH[ch].CTRL = PRS_CH_CTRL_ASYNC |
+ (source & _PRS_CH_CTRL_SOURCESEL_MASK) |
+ (signal & _PRS_CH_CTRL_SIGSEL_MASK) |
+ PRS_CH_CTRL_EDSEL_OFF;
+}
+#endif
+
+/** @} (end addtogroup PRS) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(PRS_COUNT) && (PRS_COUNT > 0) */
diff --git a/Software/emlib/src/em_rmu.c b/Software/emlib/src/em_rmu.c
new file mode 100644
index 0000000..0f0e533
--- /dev/null
+++ b/Software/emlib/src/em_rmu.c
@@ -0,0 +1,220 @@
+/***************************************************************************//**
+ * @file
+ * @brief Reset Management Unit (RMU) peripheral module peripheral API
+ *
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_rmu.h"
+#if defined(RMU_COUNT) && (RMU_COUNT > 0)
+
+#include "em_emu.h"
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup RMU
+ * @brief Reset Management Unit (RMU) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Disable/enable reset for various peripherals and signal sources
+ *
+ * @param[in] reset Reset types to enable/disable
+ *
+ * @param[in] enable
+ * @li false - Disable reset signal or flag
+ * @li true - Enable reset signal or flag
+ ******************************************************************************/
+void RMU_ResetControl(RMU_Reset_TypeDef reset, bool enable)
+{
+ BITBAND_Peripheral(&(RMU->CTRL), (uint32_t)reset, (uint32_t)enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Clear the reset cause register.
+ ******************************************************************************/
+void RMU_ResetCauseClear(void)
+{
+ uint32_t locked;
+
+ RMU->CMD = RMU_CMD_RCCLR;
+
+ /* Clear some reset causes not cleared with RMU CMD register */
+ /* (If EMU registers locked, they must be unlocked first) */
+ locked = EMU->LOCK & EMU_LOCK_LOCKKEY_LOCKED;
+ if (locked)
+ {
+ EMU_Unlock();
+ }
+
+ BITBAND_Peripheral(&(EMU->AUXCTRL), 0, 1);
+ BITBAND_Peripheral(&(EMU->AUXCTRL), 0, 0);
+
+ if (locked)
+ {
+ EMU_Lock();
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Get the cause of the last reset.
+ *
+ * @details
+ * In order to be useful, the reset cause must be cleared by SW before a new
+ * reset occurs, otherwise reset causes may accumulate. See
+ * 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
+ * The reset cause, a bit mask of (typically, but not always, only one) of:
+ * @li RMU_RSTCAUSE_PORST - Power on reset
+ * @li RMU_RSTCAUSE_BODUNREGRST - Brown out detector, unregulated power
+ * @li RMU_RSTCAUSE_BODREGRST - Brown out detector, regulated power
+ * @li RMU_RSTCAUSE_EXTRST - External reset
+ * @li RMU_RSTCAUSE_WDOGRST - Watchdog reset
+ * @li RMU_RSTCAUSE_LOCKUPRST - Cortex-M3 lockup reset
+ * @li RMU_RSTCAUSE_SYSREQRST - Cortex-M3 system request reset
+ * @li RMU_RSTCAUSE_EM4RST - Set if the system has been in EM4
+ * @li RMU_RSTCAUSE_EM4WURST - Set if the system woke up on a pin from EM4
+ * @li RMU_RSTCAUSE_BODAVDD0 - Analog power domain 0 brown out detector reset
+ * @li RMU_RSTCAUSE_BODAVDD1 - Analog power domain 1 brown out detector reset
+ * @li RMU_RSTCAUSE_BUBODVDDDREG - Backup BOD on VDDD_REG triggered
+ * @li RMU_RSTCAUSE_BUBODBUVIN - Backup BOD on BU_VIN triggered
+ * @li RMU_RSTCAUSE_BUBODUNREG - Backup BOD on unregulated power triggered
+ * @li RMU_RSTCAUSE_BUBODREG - Backup BOD on regulated powered has triggered
+ * @li RMU_RSTCAUSE_BUMODERST - System has been in Backup mode
+ ******************************************************************************/
+uint32_t RMU_ResetCauseGet(void)
+{
+ uint32_t ret = RMU->RSTCAUSE;
+
+ /* Inspect and decode bits. The decoding must be done in correct order, */
+ /* since some reset causes may trigger other reset causes due to internal */
+ /* design. We are only interested in the main cause. */
+#if defined( RMU_RSTCAUSE_EM4RST )
+ /* Clear "stray" bits if EM4 bit is set, they will always be active */
+ if (ret & RMU_RSTCAUSE_EM4RST)
+ {
+ ret &= ~(RMU_RSTCAUSE_BODREGRST|
+ RMU_RSTCAUSE_BODUNREGRST|
+ RMU_RSTCAUSE_LOCKUPRST|
+ RMU_RSTCAUSE_SYSREQRST);
+ }
+#endif
+ if (ret & RMU_RSTCAUSE_PORST)
+ {
+ ret = RMU_RSTCAUSE_PORST;
+ }
+ else if ((ret & 0x83) == RMU_RSTCAUSE_BODUNREGRST)
+ {
+ ret = RMU_RSTCAUSE_BODUNREGRST;
+ }
+ else if ((ret & 0x1f) == RMU_RSTCAUSE_BODREGRST)
+ {
+ ret = RMU_RSTCAUSE_BODREGRST;
+ }
+ /* Both external and watchdog reset may occur at the same time */
+ else if ((ret & 0x1b) & (RMU_RSTCAUSE_EXTRST | RMU_RSTCAUSE_WDOGRST))
+ {
+ ret &= RMU_RSTCAUSE_EXTRST | RMU_RSTCAUSE_WDOGRST;
+ }
+ /* Both lockup and system reset may occur at the same time */
+ else if ((ret & 0x7ff) & (RMU_RSTCAUSE_LOCKUPRST | RMU_RSTCAUSE_SYSREQRST))
+ {
+ ret &= RMU_RSTCAUSE_LOCKUPRST | RMU_RSTCAUSE_SYSREQRST;
+ }
+#if defined( RMU_RSTCAUSE_BODAVDD0 )
+ /* EM4 wake up and pin retention support */
+ else if (ret & RMU_RSTCAUSE_BODAVDD0)
+ {
+ ret = RMU_RSTCAUSE_BODAVDD0;
+ }
+ else if (ret & RMU_RSTCAUSE_BODAVDD1)
+ {
+ ret = RMU_RSTCAUSE_BODAVDD1;
+ }
+ else if (ret & (RMU_RSTCAUSE_EM4WURST|RMU_RSTCAUSE_EM4RST))
+ {
+ ret &= (RMU_RSTCAUSE_EM4WURST|
+#if defined( RMU_RSTCAUSE_BUMODERST )
+ RMU_RSTCAUSE_BUMODERST|
+#endif
+ RMU_RSTCAUSE_EM4RST);
+ }
+ else if (ret & (RMU_RSTCAUSE_EM4RST|RMU_RSTCAUSE_EXTRST))
+ {
+ ret &= (RMU_RSTCAUSE_EM4RST|RMU_RSTCAUSE_EXTRST);
+ }
+#endif
+#if defined( RMU_RSTCAUSE_BUBODVDDDREG )
+ /* Backup power domain support */
+ else if (ret & (RMU_RSTCAUSE_BUBODVDDDREG))
+ {
+ /* Keep backup mode flag, will only be present in this scenario */
+ ret &= (RMU_RSTCAUSE_BUBODVDDDREG|RMU_RSTCAUSE_BUMODERST);
+ }
+ else if (ret & (RMU_RSTCAUSE_BUBODBUVIN))
+ {
+ ret &= (RMU_RSTCAUSE_BUBODBUVIN);
+ }
+ else if (ret & (RMU_RSTCAUSE_BUBODUNREG))
+ {
+ ret &= (RMU_RSTCAUSE_BUBODUNREG);
+ }
+ else if (ret & (RMU_RSTCAUSE_BUBODREG))
+ {
+ ret &= (RMU_RSTCAUSE_BUBODREG);
+ }
+#endif
+ else
+ {
+ ret = 0;
+ }
+ return ret;
+}
+
+
+/** @} (end addtogroup RMU) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(RMU_COUNT) && (RMU_COUNT > 0) */
diff --git a/Software/emlib/src/em_rtc.c b/Software/emlib/src/em_rtc.c
new file mode 100644
index 0000000..27bd974
--- /dev/null
+++ b/Software/emlib/src/em_rtc.c
@@ -0,0 +1,362 @@
+/***************************************************************************//**
+ * @file
+ * @brief Real Time Counter (RTC) Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_rtc.h"
+#if defined(RTC_COUNT) && (RTC_COUNT > 0)
+
+#include "em_assert.h"
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup RTC
+ * @brief Real Time Counter (RTC) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Validation of valid comparator register for assert statements. */
+#define RTC_COMP_REG_VALID(reg) (((reg) <= 1))
+
+/** @endcond */
+
+
+/*******************************************************************************
+ ************************** LOCAL FUNCTIONS ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#if defined(_EFM32_GECKO_FAMILY)
+/***************************************************************************//**
+ * @brief
+ * Wait for ongoing sync of register(s) to low frequency domain to complete.
+ *
+ * @note
+ * This only applies to the Gecko Family, see the reference manual
+ * chapter about Access to Low Energy Peripherals (Asynchronos Registers)
+ * for details. For Tiny Gecko and Giant Gecko, the RTC supports immediate
+ * updates of registers, and will automatically hold the bus until the
+ * register has been updated.
+ *
+ * @param[in] mask
+ * Bitmask corresponding to SYNCBUSY register defined bits, indicating
+ * registers that must complete any ongoing synchronization.
+ ******************************************************************************/
+__STATIC_INLINE void RTC_Sync(uint32_t mask)
+{
+ /* Avoid deadlock if modifying the same register twice when freeze mode is */
+ /* activated. */
+ if (RTC->FREEZE & RTC_FREEZE_REGFREEZE)
+ return;
+
+ /* Wait for any pending previous write operation to have been completed */
+ /* in low frequency domain. This is only required for the Gecko Family */
+ while (RTC->SYNCBUSY & mask)
+ ;
+}
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Get RTC compare register value.
+ *
+ * @param[in] comp
+ * Compare register to get, either 0 or 1
+ *
+ * @return
+ * Compare register value, 0 if invalid register selected.
+ ******************************************************************************/
+uint32_t RTC_CompareGet(unsigned int comp)
+{
+ uint32_t ret;
+
+ EFM_ASSERT(RTC_COMP_REG_VALID(comp));
+
+ /* Initialize selected compare value */
+ switch (comp)
+ {
+ case 0:
+ ret = RTC->COMP0;
+ break;
+
+ case 1:
+ ret = RTC->COMP1;
+ break;
+
+ default:
+ /* Unknown compare register selected */
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Set RTC compare register value.
+ *
+ * @note
+ * The setting of a compare register requires synchronization into the
+ * low frequency domain. If the same register is modified before a previous
+ * update has completed, this function will stall until the previous
+ * synchronization has completed. This only applies to the Gecko Family, see
+ * comment in the RTC_Sync() internal function call.
+ *
+ * @param[in] comp
+ * Compare register to set, either 0 or 1
+ *
+ * @param[in] value
+ * Initialization value (<= 0x00ffffff)
+ ******************************************************************************/
+void RTC_CompareSet(unsigned int comp, uint32_t value)
+{
+ volatile uint32_t *compReg;
+#if defined(_EFM32_GECKO_FAMILY)
+ uint32_t syncbusy;
+#endif
+
+ EFM_ASSERT(RTC_COMP_REG_VALID(comp) &&
+ ((value & ~(_RTC_COMP0_COMP0_MASK >> _RTC_COMP0_COMP0_SHIFT)) == 0));
+
+ /* Initialize selected compare value */
+ switch (comp)
+ {
+ case 0:
+ compReg = &(RTC->COMP0);
+#if defined(_EFM32_GECKO_FAMILY)
+ syncbusy = RTC_SYNCBUSY_COMP0;
+#endif
+ break;
+
+ case 1:
+ compReg = &(RTC->COMP1);
+#if defined(_EFM32_GECKO_FAMILY)
+ syncbusy = RTC_SYNCBUSY_COMP1;
+#endif
+ break;
+
+ default:
+ /* Unknown compare register selected, abort */
+ return;
+ }
+#if defined(_EFM32_GECKO_FAMILY)
+ /* LF register about to be modified require sync. busy check */
+ RTC_Sync(syncbusy);
+#endif
+
+ *compReg = value;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Enable/disable RTC.
+ *
+ * @note
+ * The enabling/disabling of the RTC modifies the RTC 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. This
+ * only applies to the Gecko Family, see comment in the RTC_Sync() internal
+ * function call.
+ *
+ * @param[in] enable
+ * true to enable counting, false to disable.
+ ******************************************************************************/
+void RTC_Enable(bool enable)
+{
+#if defined(_EFM32_GECKO_FAMILY)
+ /* LF register about to be modified require sync. busy check */
+ RTC_Sync(RTC_SYNCBUSY_CTRL);
+#endif
+
+ BITBAND_Peripheral(&(RTC->CTRL), _RTC_CTRL_EN_SHIFT, (unsigned int) enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * RTC register synchronization freeze control.
+ *
+ * @details
+ * Some RTC 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 RTC synchronization to LF domain to complete (Normally
+ * synchronization will not be in progress.) However for this reason, when
+ * using freeze mode, modifications of registers requiring LF synchronization
+ * should be done within one freeze enable/disable block to avoid unecessary
+ * stalling. This only applies to the Gecko Family, see the reference manual
+ * chapter about Access to Low Energy Peripherals (Asynchronos Registers)
+ * for details.
+ *
+ * @param[in] enable
+ * @li true - enable freeze, modified registers are not propagated to the
+ * LF domain
+ * @li false - disables freeze, modified registers are propagated to LF
+ * domain
+ ******************************************************************************/
+void RTC_FreezeEnable(bool enable)
+{
+ if (enable)
+ {
+#if defined(_EFM32_GECKO_FAMILY)
+ /* Wait for any ongoing LF synchronization to complete. This is just to */
+ /* protect against the rare case when a user */
+ /* - modifies a register requiring LF sync */
+ /* - then enables freeze before LF sync completed */
+ /* - then modifies the same register again */
+ /* since modifying a register while it is in sync progress should be */
+ /* avoided. */
+ while (RTC->SYNCBUSY)
+ ;
+#endif
+ RTC->FREEZE = RTC_FREEZE_REGFREEZE;
+ }
+ else
+ {
+ RTC->FREEZE = 0;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Initialize RTC.
+ *
+ * @details
+ * Note that the compare values must be set separately with RTC_CompareSet().
+ * That should probably be done prior to the use of this function if
+ * configuring the RTC to start when initialization is completed.
+ *
+ * @note
+ * The initialization of the RTC modifies the RTC 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. This
+ * only applies to the Gecko Family, see comment in the RTC_Sync() internal
+ * function call.
+ *
+ * @param[in] init
+ * Pointer to RTC initialization structure.
+ ******************************************************************************/
+void RTC_Init(const RTC_Init_TypeDef *init)
+{
+ uint32_t tmp;
+
+ if (init->enable)
+ {
+ tmp = RTC_CTRL_EN;
+ }
+ else
+ {
+ tmp = 0;
+ }
+
+ /* Configure DEBUGRUN flag, sets whether or not counter should be
+ * updated when debugger is active */
+ if (init->debugRun)
+ {
+ tmp |= RTC_CTRL_DEBUGRUN;
+ }
+
+ /* Configure COMP0TOP, this will use the COMP0 compare value as an
+ * overflow value, instead of default 24-bit 0x00ffffff */
+ if (init->comp0Top)
+ {
+ tmp |= RTC_CTRL_COMP0TOP;
+ }
+
+#if defined(_EFM32_GECKO_FAMILY)
+ /* LF register about to be modified require sync. busy check */
+ RTC_Sync(RTC_SYNCBUSY_CTRL);
+#endif
+
+ RTC->CTRL = tmp;
+}
+
+
+
+/***************************************************************************//**
+ * @brief
+ * Restore RTC to reset state
+ ******************************************************************************/
+void RTC_Reset(void)
+{
+ /* Restore all essential RTC register to default config */
+ RTC->FREEZE = _RTC_FREEZE_RESETVALUE;
+ RTC->CTRL = _RTC_CTRL_RESETVALUE;
+ RTC->COMP0 = _RTC_COMP0_RESETVALUE;
+ RTC->COMP1 = _RTC_COMP1_RESETVALUE;
+ RTC->IEN = _RTC_IEN_RESETVALUE;
+ RTC->IFC = _RTC_IFC_RESETVALUE;
+}
+
+
+
+/***************************************************************************//**
+ * @brief
+ * Restart RTC counter from zero
+ ******************************************************************************/
+void RTC_CounterReset(void)
+{
+ /* A disable/enable sequnce will start the counter at zero */
+ RTC_Enable(false);
+ RTC_Enable(true);
+}
+
+
+/** @} (end addtogroup RTC) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(RTC_COUNT) && (RTC_COUNT > 0) */
diff --git a/Software/emlib/src/em_system.c b/Software/emlib/src/em_system.c
new file mode 100644
index 0000000..455f329
--- /dev/null
+++ b/Software/emlib/src/em_system.c
@@ -0,0 +1,109 @@
+/***************************************************************************//**
+ * @file
+ * @brief System Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_system.h"
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup SYSTEM
+ * @brief System Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Get chip major/minor revision.
+ *
+ * @param[out] rev
+ * Location to place chip revision info.
+ ******************************************************************************/
+void SYSTEM_ChipRevisionGet(SYSTEM_ChipRevision_TypeDef *rev)
+{
+ uint8_t tmp;
+
+ EFM_ASSERT(rev);
+
+ rev->major = (ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) >> _ROMTABLE_PID0_REVMAJOR_SHIFT;
+
+ tmp = (ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK);
+ tmp |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) >> _ROMTABLE_PID3_REVMINORLSB_SHIFT);
+ rev->minor = tmp;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get factory calibration value for a given peripheral register.
+ *
+ * @param[in] regAddress
+ * Address of register to get a calibration value for.
+ *
+ * @return
+ * Calibration value for the requested register.
+ ******************************************************************************/
+uint32_t SYSTEM_GetCalibrationValue(volatile uint32_t *regAddress)
+{
+ int regCount;
+ CALIBRATE_TypeDef *p;
+
+ regCount = 1;
+ p = CALIBRATE;
+
+ for (;; )
+ {
+ if ((regCount > CALIBRATE_MAX_REGISTERS) ||
+ (p->VALUE == 0xFFFFFFFF))
+ {
+ EFM_ASSERT(false);
+ return 0; /* End of device calibration table reached. */
+ }
+
+ if (p->ADDRESS == (uint32_t)regAddress)
+ {
+ return p->VALUE; /* Calibration value found ! */
+ }
+
+ p++;
+ regCount++;
+ }
+}
+
+/** @} (end addtogroup SYSTEM) */
+/** @} (end addtogroup EM_Library) */
diff --git a/Software/emlib/src/em_timer.c b/Software/emlib/src/em_timer.c
new file mode 100644
index 0000000..79ccf96
--- /dev/null
+++ b/Software/emlib/src/em_timer.c
@@ -0,0 +1,304 @@
+/***************************************************************************//**
+ * @file
+ * @brief Timer/counter (TIMER) Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_timer.h"
+#if defined(TIMER_COUNT) && (TIMER_COUNT > 0)
+
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup TIMER
+ * @brief Timer/Counter (TIMER) Peripheral API
+ * @details
+ * The timer module consists of three main parts:
+ * @li General timer config and enable control.
+ * @li Compare/capture control.
+ * @li Dead time insertion control (may not be available for all timers).
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+
+/** Validation of TIMER register block pointer reference for assert statements. */
+#if (TIMER_COUNT == 1)
+#define TIMER_REF_VALID(ref) ((ref) == TIMER0)
+#elif (TIMER_COUNT == 2)
+#define TIMER_REF_VALID(ref) (((ref) == TIMER0) || ((ref) == TIMER1))
+#elif (TIMER_COUNT == 3)
+#define TIMER_REF_VALID(ref) (((ref) == TIMER0) || \
+ ((ref) == TIMER1) || \
+ ((ref) == TIMER2))
+#elif (TIMER_COUNT == 4)
+#define TIMER_REF_VALID(ref) (((ref) == TIMER0) || \
+ ((ref) == TIMER1) || \
+ ((ref) == TIMER2) || \
+ ((ref) == TIMER3))
+#else
+#error Undefined number of timers.
+#endif
+
+/** Validation of TIMER compare/capture channel number */
+#define TIMER_CH_VALID(ch) ((ch) < 3)
+
+/** @endcond */
+
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Start/stop TIMER.
+ *
+ * @param[in] timer
+ * Pointer to TIMER peripheral register block.
+ *
+ * @param[in] enable
+ * true to enable counting, false to disable.
+ ******************************************************************************/
+void TIMER_Enable(TIMER_TypeDef *timer, bool enable)
+{
+ EFM_ASSERT(TIMER_REF_VALID(timer));
+
+ if (enable)
+ {
+ timer->CMD = TIMER_CMD_START;
+ }
+ else
+ {
+ timer->CMD = TIMER_CMD_STOP;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Initialize TIMER.
+ *
+ * @details
+ * Notice that counter top must be configured separately with for instance
+ * TIMER_TopSet(). In addition, compare/capture and dead-time insertion
+ * init must be initialized separately if used. That should probably
+ * be done prior to the use of this function if configuring the TIMER to
+ * start when initialization is completed.
+ *
+ * @param[in] timer
+ * Pointer to TIMER peripheral register block.
+ *
+ * @param[in] init
+ * Pointer to TIMER initialization structure.
+ ******************************************************************************/
+void TIMER_Init(TIMER_TypeDef *timer, const TIMER_Init_TypeDef *init)
+{
+ EFM_ASSERT(TIMER_REF_VALID(timer));
+
+ /* Stop timer if specified to be disabled (dosn't hurt if already stopped) */
+ if (!(init->enable))
+ {
+ timer->CMD = TIMER_CMD_STOP;
+ }
+
+ /* Reset counter */
+ timer->CNT = _TIMER_CNT_RESETVALUE;
+
+ timer->CTRL =
+ ((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_X2CNT ) && defined( TIMER_CTRL_ATI )
+ (init->count2x ? TIMER_CTRL_X2CNT : 0) |
+ (init->ati ? TIMER_CTRL_ATI : 0) |
+#endif
+ (init->sync ? TIMER_CTRL_SYNC : 0);
+
+ /* Start timer if specified to be enabled (dosn't hurt if already started) */
+ if (init->enable)
+ {
+ timer->CMD = TIMER_CMD_START;
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Initialize TIMER compare/capture channel.
+ *
+ * @details
+ * Notice that if operating channel in compare mode, the CCV and CCVB register
+ * must be set separately as required.
+ *
+ * @param[in] timer
+ * Pointer to TIMER peripheral register block.
+ *
+ * @param[in] ch
+ * Compare/capture channel to init for.
+ *
+ * @param[in] init
+ * Pointer to TIMER initialization structure.
+ ******************************************************************************/
+void TIMER_InitCC(TIMER_TypeDef *timer,
+ unsigned int ch,
+ const TIMER_InitCC_TypeDef *init)
+{
+ EFM_ASSERT(TIMER_REF_VALID(timer));
+ EFM_ASSERT(TIMER_CH_VALID(ch));
+
+ timer->CC[ch].CTRL =
+ ((uint32_t)(init->eventCtrl) << _TIMER_CC_CTRL_ICEVCTRL_SHIFT) |
+ ((uint32_t)(init->edge) << _TIMER_CC_CTRL_ICEDGE_SHIFT) |
+#if defined(ADC_PRESENT)
+ ((uint32_t)(init->prsSel) << _TIMER_CC_CTRL_PRSSEL_SHIFT) |
+#endif
+ ((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);
+}
+
+#ifdef TIMER_DTLOCK_LOCKKEY_LOCK
+/***************************************************************************//**
+ * @brief
+ * Lock the TIMER in order to protect some of its registers against unintended
+ * modification.
+ *
+ * @details
+ * Please refer to the reference manual for TIMER registers that will be
+ * locked.
+ *
+ * @note
+ * If locking the TIMER registers, they must be unlocked prior to using any
+ * TIMER API functions modifying TIMER registers protected by the lock.
+ *
+ * @param[in] timer
+ * Pointer to TIMER peripheral register block.
+ ******************************************************************************/
+void TIMER_Lock(TIMER_TypeDef *timer)
+{
+ EFM_ASSERT(TIMER_REF_VALID(timer));
+
+ timer->DTLOCK = TIMER_DTLOCK_LOCKKEY_LOCK;
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Reset TIMER to same state as after a HW reset.
+ *
+ * @note
+ * The ROUTE register is NOT reset by this function, in order to allow for
+ * centralized setup of this feature.
+ *
+ * @param[in] timer
+ * Pointer to TIMER peripheral register block.
+ ******************************************************************************/
+void TIMER_Reset(TIMER_TypeDef *timer)
+{
+ int i;
+
+ EFM_ASSERT(TIMER_REF_VALID(timer));
+
+ /* Make sure disabled first, before resetting other registers */
+ timer->CMD = TIMER_CMD_STOP;
+
+ timer->CTRL = _TIMER_CTRL_RESETVALUE;
+ timer->IEN = _TIMER_IEN_RESETVALUE;
+ timer->IFC = _TIMER_IFC_MASK;
+ timer->TOP = _TIMER_TOP_RESETVALUE;
+ timer->TOPB = _TIMER_TOPB_RESETVALUE;
+ timer->CNT = _TIMER_CNT_RESETVALUE;
+ /* Do not reset route register, setting should be done independently */
+ /* (Note: ROUTE register may be locked by DTLOCK register.) */
+
+ for (i = 0; TIMER_CH_VALID(i); i++)
+ {
+ timer->CC[i].CTRL = _TIMER_CC_CTRL_RESETVALUE;
+ timer->CC[i].CCV = _TIMER_CC_CCV_RESETVALUE;
+ timer->CC[i].CCVB = _TIMER_CC_CCVB_RESETVALUE;
+ }
+
+ /* Reset dead time insertion module, no effect on timers without DTI */
+
+#ifdef TIMER_DTLOCK_LOCKKEY_UNLOCK
+ /* Unlock DTI registers first in case 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
+}
+
+
+#ifdef TIMER_DTLOCK_LOCKKEY_UNLOCK
+/***************************************************************************//**
+ * @brief
+ * Unlock the TIMER so that writing to locked registers again is possible.
+ *
+ * @param[in] timer
+ * Pointer to TIMER peripheral register block.
+ ******************************************************************************/
+void TIMER_Unlock(TIMER_TypeDef *timer)
+{
+ EFM_ASSERT(TIMER_REF_VALID(timer));
+
+ timer->DTLOCK = TIMER_DTLOCK_LOCKKEY_UNLOCK;
+}
+#endif
+
+
+/** @} (end addtogroup TIMER) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(TIMER_COUNT) && (TIMER_COUNT > 0) */
diff --git a/Software/emlib/src/em_usart.c b/Software/emlib/src/em_usart.c
new file mode 100644
index 0000000..20a38aa
--- /dev/null
+++ b/Software/emlib/src/em_usart.c
@@ -0,0 +1,1128 @@
+/***************************************************************************//**
+ * @file
+ * @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART)
+ * Peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_usart.h"
+#if defined(USART_COUNT) && (USART_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup USART
+ * @brief Universal Synchronous/Asynchronous Receiver/Transmitter
+ * Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+
+/** Validation of USART register block pointer reference for assert statements. */
+#if (USART_COUNT == 1) && defined(USART0)
+#define USART_REF_VALID(ref) ((ref) == USART0)
+
+#elif (USART_COUNT == 1) && defined(USART1)
+#define USART_REF_VALID(ref) ((ref) == USART1)
+
+#elif (USART_COUNT == 2)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1))
+
+#elif (USART_COUNT == 3)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \
+ ((ref) == USART2))
+#elif (USART_COUNT == 4)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \
+ ((ref) == USART2) || ((ref) == USART3))
+#else
+#error Undefined number of USARTs.
+#endif
+
+#if defined(USART0)
+#define USART_IRDA_VALID(ref) ((ref) == USART0)
+#elif (USART_COUNT == 1) && defined(USART1)
+#define USART_IRDA_VALID(ref) ((ref) == USART1)
+#else
+#define USART_IRDA_VALID(ref) (0)
+#endif
+
+#if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_ZERO_FAMILY)
+#define USART_I2S_VALID(ref) ((ref) == USART1)
+
+#elif defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)
+#define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART2))
+#endif
+
+#if (UART_COUNT == 1)
+#define UART_REF_VALID(ref) ((ref) == UART0)
+#elif (UART_COUNT == 2)
+#define UART_REF_VALID(ref) (((ref) == UART0) || ((ref) == UART1))
+#else
+#define UART_REF_VALID(ref) (0)
+#endif
+
+/** @endcond */
+
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Configure USART/UART operating in asynchronous mode to use a given
+ * baudrate (or as close as possible to specified baudrate).
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] refFreq
+ * USART/UART reference clock frequency in Hz that will be used. If set to 0,
+ * the currently configured reference clock is assumed.
+ *
+ * @param[in] baudrate
+ * Baudrate to try to achieve for USART/UART.
+ *
+ * @param[in] ovs
+ * Oversampling to be used. Normal is 16x oversampling, but lower oversampling
+ * may be used to achieve higher rates or better baudrate accuracy in some
+ * cases. Notice that lower oversampling frequency makes channel more
+ * vulnerable to bit faults during reception due to clock inaccuracies
+ * compared to link partner.
+ ******************************************************************************/
+void USART_BaudrateAsyncSet(USART_TypeDef *usart,
+ uint32_t refFreq,
+ uint32_t baudrate,
+ USART_OVS_TypeDef ovs)
+{
+ uint32_t clkdiv;
+ uint32_t oversample;
+
+ /* Inhibit divide by 0 */
+ EFM_ASSERT(baudrate);
+
+ /*
+ * We want to use integer division to avoid forcing in float division
+ * utils, and yet keep rounding effect errors to a minimum.
+ *
+ * CLKDIV in asynchronous mode is given by:
+ *
+ * CLKDIV = 256 * (fHFPERCLK/(oversample * br) - 1)
+ * or
+ * CLKDIV = (256 * fHFPERCLK)/(oversample * br) - 256
+ *
+ * The basic problem with integer division in the above formula is that
+ * the dividend (256 * fHFPERCLK) may become higher than max 32 bit
+ * integer. Yet, we want to evaluate dividend first before dividing in
+ * order to get as small rounding effects as possible. We do not want
+ * to make too harsh restrictions on max fHFPERCLK value either.
+ *
+ * One can possibly factorize 256 and oversample/br. However,
+ * since the last 6 bits of CLKDIV are don't care, we can base our
+ * integer arithmetic on the below formula
+ *
+ * CLKDIV / 64 = (4 * fHFPERCLK)/(oversample * br) - 4
+ *
+ * and calculate 1/64 of CLKDIV first. This allows for fHFPERCLK
+ * up to 1GHz without overflowing a 32 bit value!
+ */
+
+ /* HFPERCLK used to clock all USART/UART peripheral modules */
+ if (!refFreq)
+ {
+ refFreq = CMU_ClockFreqGet(cmuClock_HFPER);
+ }
+
+ /* Map oversampling */
+ switch (ovs)
+ {
+ case USART_CTRL_OVS_X16:
+ EFM_ASSERT(baudrate <= (refFreq / 16));
+ oversample = 16;
+ break;
+
+ case USART_CTRL_OVS_X8:
+ EFM_ASSERT(baudrate <= (refFreq / 8));
+ oversample = 8;
+ break;
+
+ case USART_CTRL_OVS_X6:
+ EFM_ASSERT(baudrate <= (refFreq / 6));
+ oversample = 6;
+ break;
+
+ case USART_CTRL_OVS_X4:
+ EFM_ASSERT(baudrate <= (refFreq / 4));
+ oversample = 4;
+ break;
+
+ default:
+ /* Invalid input */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Calculate and set CLKDIV with fractional bits.
+ * The addend (oversample*baudrate)/2 in the first line is to round the
+ * divisor up by half the divisor before the division in order to reduce the
+ * integer division error, which consequently results in a higher baudrate
+ * than desired. */
+ clkdiv = 4 * refFreq + (oversample * baudrate) / 2;
+ clkdiv /= (oversample * baudrate);
+ clkdiv -= 4;
+ clkdiv *= 64;
+
+ /* Verify that resulting clock divider is within limits */
+ EFM_ASSERT(clkdiv <= _USART_CLKDIV_MASK);
+
+ /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */
+ clkdiv &= _USART_CLKDIV_MASK;
+
+ usart->CTRL &= ~_USART_CTRL_OVS_MASK;
+ usart->CTRL |= ovs;
+ usart->CLKDIV = clkdiv;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Calculate baudrate for USART/UART given reference frequency, clock division
+ * and oversampling rate (if async mode).
+ *
+ * @details
+ * This function returns the baudrate that a USART/UART module will use if
+ * configured with the given frequency, clock divisor and mode. Notice that
+ * this function will not use actual HW configuration. It can be used
+ * to determinate if a given configuration is sufficiently accurate for the
+ * application.
+ *
+ * @param[in] refFreq
+ * USART/UART HF peripheral frequency used.
+ *
+ * @param[in] clkdiv
+ * Clock division factor to be used.
+ *
+ * @param[in] syncmode
+ * @li true - synchronous mode operation.
+ * @li false - asynchronous mode operation.
+ *
+ * @param[in] ovs
+ * Oversampling used if asynchronous mode. Not used if @p syncmode is true.
+ *
+ * @return
+ * Baudrate with given settings.
+ ******************************************************************************/
+uint32_t USART_BaudrateCalc(uint32_t refFreq,
+ uint32_t clkdiv,
+ bool syncmode,
+ USART_OVS_TypeDef ovs)
+{
+ uint32_t oversample;
+ uint32_t divisor;
+ uint32_t factor;
+ uint32_t remainder;
+ uint32_t quotient;
+ uint32_t br;
+
+ /* Mask out unused bits */
+ clkdiv &= _USART_CLKDIV_MASK;
+
+ /* We want to use integer division to avoid forcing in float division */
+ /* utils, and yet keep rounding effect errors to a minimum. */
+
+ /* Baudrate calculation depends on if synchronous or asynchronous mode */
+ if (syncmode)
+ {
+ /*
+ * Baudrate is given by:
+ *
+ * br = fHFPERCLK/(2 * (1 + (CLKDIV / 256)))
+ *
+ * which can be rewritten to
+ *
+ * br = (128 * fHFPERCLK)/(256 + CLKDIV)
+ */
+ oversample = 1; /* Not used in sync mode, ie 1 */
+ factor = 128;
+ }
+ else
+ {
+ /*
+ * Baudrate in asynchronous mode is given by:
+ *
+ * br = fHFPERCLK/(oversample * (1 + (CLKDIV / 256)))
+ *
+ * which can be rewritten to
+ *
+ * br = (256 * fHFPERCLK)/(oversample * (256 + CLKDIV))
+ *
+ * First of all we can reduce the 256 factor of the dividend with
+ * (part of) oversample part of the divisor.
+ */
+
+ switch (ovs)
+ {
+ case USART_CTRL_OVS_X16:
+ oversample = 1;
+ factor = 256 / 16;
+ break;
+
+ case USART_CTRL_OVS_X8:
+ oversample = 1;
+ factor = 256 / 8;
+ break;
+
+ case USART_CTRL_OVS_X6:
+ oversample = 3;
+ factor = 256 / 2;
+ break;
+
+ default:
+ oversample = 1;
+ factor = 256 / 4;
+ break;
+ }
+ }
+
+ /*
+ * The basic problem with integer division in the above formula is that
+ * the dividend (factor * fHFPERCLK) may become higher than max 32 bit
+ * integer. Yet we want to evaluate dividend first before dividing in
+ * order to get as small rounding effects as possible. We do not want
+ * to make too harsh restrictions on max fHFPERCLK value either.
+ *
+ * For division a/b, we can write
+ *
+ * a = qb + r
+ *
+ * where q is the quotient and r is the remainder, both integers.
+ *
+ * The orignal baudrate formula can be rewritten as
+ *
+ * br = xa / b = x(qb + r)/b = xq + xr/b
+ *
+ * where x is 'factor', a is 'refFreq' and b is 'divisor', referring to
+ * variable names.
+ */
+
+ /* Divisor will never exceed max 32 bit value since clkdiv <= 0x1fffc0 */
+ /* and 'oversample' has been reduced to <= 3. */
+ divisor = oversample * (256 + clkdiv);
+
+ quotient = refFreq / divisor;
+ remainder = refFreq % divisor;
+
+ /* factor <= 128 and since divisor >= 256, the below cannot exceed max */
+ /* 32 bit value. */
+ br = factor * quotient;
+
+ /*
+ * factor <= 128 and remainder < (oversample*(256 + clkdiv)), which
+ * means dividend (factor * remainder) worst case is
+ * 128*(3 * (256 + 0x1fffc0)) = 0x30012000.
+ */
+ br += (factor * remainder) / divisor;
+
+ return br;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Get current baudrate for USART/UART.
+ *
+ * @details
+ * This function returns the actual baudrate (not considering oscillator
+ * inaccuracies) used by a USART/UART peripheral.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Current baudrate.
+ ******************************************************************************/
+uint32_t USART_BaudrateGet(USART_TypeDef *usart)
+{
+ uint32_t freq;
+ USART_OVS_TypeDef ovs;
+ bool syncmode;
+
+ if (usart->CTRL & USART_CTRL_SYNC)
+ {
+ syncmode = true;
+ }
+ else
+ {
+ syncmode = false;
+ }
+
+ /* HFPERCLK used to clock all USART/UART peripheral modules */
+ freq = CMU_ClockFreqGet(cmuClock_HFPER);
+ ovs = (USART_OVS_TypeDef) (usart->CTRL & _USART_CTRL_OVS_MASK);
+ return USART_BaudrateCalc(freq, usart->CLKDIV, syncmode, ovs);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Configure USART operating in synchronous mode to use a given baudrate
+ * (or as close as possible to specified baudrate).
+ *
+ * @details
+ * The configuration will be set to use a baudrate <= the specified baudrate
+ * in order to ensure that the baudrate does not exceed the specified value.
+ *
+ * Fractional clock division is suppressed, although the HW design allows it.
+ * It could cause half clock cycles to exceed specified limit, and thus
+ * potentially violate specifications for the slave device. In some special
+ * situations fractional clock division may be useful even in synchronous
+ * mode, but in those cases it must be directly adjusted, possibly assisted
+ * by USART_BaudrateCalc():
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block. (Cannot be used on UART
+ * modules.)
+ *
+ * @param[in] refFreq
+ * USART reference clock frequency in Hz that will be used. If set to 0,
+ * the currently configured reference clock is assumed.
+ *
+ * @param[in] baudrate
+ * Baudrate to try to achieve for USART.
+ ******************************************************************************/
+void USART_BaudrateSyncSet(USART_TypeDef *usart, uint32_t refFreq, uint32_t baudrate)
+{
+ uint32_t clkdiv;
+
+ /* Inhibit divide by 0 */
+ EFM_ASSERT(baudrate);
+
+ /*
+ * We want to use integer division to avoid forcing in float division
+ * utils, and yet keep rounding effect errors to a minimum.
+ *
+ * CLKDIV in synchronous mode is given by:
+ *
+ * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1)
+ * or
+ * CLKDIV = (256 * fHFPERCLK)/(2 * br) - 256 = (128 * fHFPERCLK)/br - 256
+ *
+ * The basic problem with integer division in the above formula is that
+ * the dividend (128 * fHFPERCLK) may become higher than max 32 bit
+ * integer. Yet, we want to evaluate dividend first before dividing in
+ * order to get as small rounding effects as possible. We do not want
+ * to make too harsh restrictions on max fHFPERCLK value either.
+ *
+ * One can possibly factorize 128 and br. However, since the last
+ * 6 bits of CLKDIV are don't care, we can base our integer arithmetic
+ * on the below formula without loosing any extra precision:
+ *
+ * CLKDIV / 64 = (2 * fHFPERCLK)/br - 4
+ *
+ * and calculate 1/64 of CLKDIV first. This allows for fHFPERCLK
+ * up to 2GHz without overflowing a 32 bit value!
+ */
+
+ /* HFPERCLK used to clock all USART/UART peripheral modules */
+ if (!refFreq)
+ {
+ refFreq = CMU_ClockFreqGet(cmuClock_HFPER);
+ }
+
+ /* Calculate and set CLKDIV with fractional bits */
+ clkdiv = 2 * refFreq;
+ clkdiv += baudrate - 1;
+ clkdiv /= baudrate;
+ clkdiv -= 4;
+ clkdiv *= 64;
+ /* Make sure we don't use fractional bits by rounding CLKDIV */
+ /* up (and thus reducing baudrate, not increasing baudrate above */
+ /* specified value). */
+ clkdiv += 0xc0;
+ clkdiv &= 0xffffff00;
+
+ /* Verify that resulting clock divider is within limits */
+ EFM_ASSERT(clkdiv <= _USART_CLKDIV_MASK);
+
+ /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */
+ clkdiv &= _USART_CLKDIV_DIV_MASK;
+
+ usart->CLKDIV = clkdiv;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Enable/disable USART/UART receiver and/or transmitter.
+ *
+ * @details
+ * Notice that this function does not do any configuration. Enabling should
+ * normally be done after initialization is done (if not enabled as part
+ * of init).
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] enable
+ * Select status for receiver/transmitter.
+ ******************************************************************************/
+void USART_Enable(USART_TypeDef *usart, USART_Enable_TypeDef enable)
+{
+ uint32_t tmp;
+
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart) || (UART_REF_VALID(usart)));
+
+ /* Disable as specified */
+ tmp = ~((uint32_t) (enable));
+ tmp &= _USART_CMD_RXEN_MASK | _USART_CMD_TXEN_MASK;
+ usart->CMD = tmp << 1;
+
+ /* Enable as specified */
+ usart->CMD = (uint32_t) (enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Init USART/UART for normal asynchronous mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in normal
+ * asynchronous mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL register.
+ *
+ * Notice that pins used by the USART/UART module must be properly configured
+ * by the user explicitly, in order for the USART/UART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure basic async setup.
+ ******************************************************************************/
+void USART_InitAsync(USART_TypeDef *usart, const USART_InitAsync_TypeDef *init)
+{
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart) || UART_REF_VALID(usart));
+
+ /* Init USART registers to HW reset state. */
+ USART_Reset(usart);
+
+#if defined(USART_INPUT_RXPRS) && defined(USART_CTRL_MVDIS)
+ /* Disable majority vote if specified. */
+ if (init->mvdis)
+ {
+ usart->CTRL |= USART_CTRL_MVDIS;
+ }
+
+ /* Configure PRS input mode. */
+ if (init->prsRxEnable)
+ {
+ usart->INPUT = (uint32_t) init->prsRxCh | USART_INPUT_RXPRS;
+ }
+#endif
+
+ /* Configure databits, stopbits and parity */
+ usart->FRAME = (uint32_t) (init->databits) |
+ (uint32_t) (init->stopbits) |
+ (uint32_t) (init->parity);
+
+ /* Configure baudrate */
+ USART_BaudrateAsyncSet(usart, init->refFreq, init->baudrate, init->oversampling);
+
+ /* Finally enable (as specified) */
+ usart->CMD = (uint32_t) (init->enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Init USART for synchronous mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in
+ * synchronous mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL register.
+ *
+ * Notice that pins used by the USART module must be properly configured
+ * by the user explicitly, in order for the USART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block. (UART does not support this
+ * mode.)
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure basic async setup.
+ ******************************************************************************/
+void USART_InitSync(USART_TypeDef *usart, const USART_InitSync_TypeDef *init)
+{
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart));
+
+ /* Init USART registers to HW reset state. */
+ USART_Reset(usart);
+
+ /* Set bits for synchronous mode */
+ usart->CTRL |= (USART_CTRL_SYNC) |
+ ((uint32_t) init->clockMode) |
+ (init->msbf ? USART_CTRL_MSBF : 0);
+
+#if defined(USART_INPUT_RXPRS) && defined(USART_TRIGCTRL_AUTOTXTEN)
+ usart->CTRL |= (init->prsRxEnable ? USART_INPUT_RXPRS : 0) |
+ (init->autoTx ? USART_CTRL_AUTOTX : 0);
+#endif
+
+ /* Configure databits, leave stopbits and parity at reset default (not used) */
+ usart->FRAME = ((uint32_t) (init->databits)) |
+ (USART_FRAME_STOPBITS_DEFAULT) |
+ (USART_FRAME_PARITY_DEFAULT);
+
+ /* Configure baudrate */
+ USART_BaudrateSyncSet(usart, init->refFreq, init->baudrate);
+
+ /* Finally enable (as specified) */
+ if (init->master)
+ {
+ usart->CMD = USART_CMD_MASTEREN;
+ }
+
+ usart->CMD = (uint32_t) (init->enable);
+}
+
+
+#if defined(USART0) || ((USART_COUNT == 1) && defined(USART1))
+/***************************************************************************//**
+ * @brief
+ * Init USART0 for asynchronous IrDA mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in
+ * asynchronous IrDA mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL and IRCTRL
+ * registers.
+ *
+ * Notice that pins used by the USART/UART module must be properly configured
+ * by the user explicitly, in order for the USART/UART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure async IrDA setup.
+ *
+ * @note
+ * This function only applies to USART0 as IrDA is not supported on the other
+ * USART modules.
+ *
+ ******************************************************************************/
+void USART_InitIrDA(const USART_InitIrDA_TypeDef *init)
+{
+ #if (USART_COUNT == 1) && defined(USART1)
+ USART_TypeDef *usart = USART1;
+ #else
+ USART_TypeDef *usart = USART0;
+ #endif
+
+ /* Init USART as async device */
+ USART_InitAsync(usart, &(init->async));
+
+ /* Set IrDA modulation to RZI (return-to-zero-inverted) */
+ usart->CTRL |= USART_CTRL_TXINV;
+
+ /* Invert Rx signal before demodulator if enabled */
+ if (init->irRxInv)
+ {
+ usart->CTRL |= USART_CTRL_RXINV;
+ }
+
+ /* Configure IrDA */
+ usart->IRCTRL |= (uint32_t) init->irPw |
+ (uint32_t) init->irPrsSel |
+ ((uint32_t) init->irFilt << _USART_IRCTRL_IRFILT_SHIFT) |
+ ((uint32_t) init->irPrsEn << _USART_IRCTRL_IRPRSEN_SHIFT);
+
+ /* Enable IrDA */
+ usart->IRCTRL |= USART_IRCTRL_IREN;
+}
+#endif
+
+
+#if defined(_USART_I2SCTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Init USART for I2S mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in I2S
+ * mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL and I2SCTRL
+ * registers.
+ *
+ * Notice that pins used by the USART module must be properly configured
+ * by the user explicitly, in order for the USART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block. (UART does not support this
+ * mode.)
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure basic I2S setup.
+ *
+ * @note
+ * This function does not apply to all USART's. Refer to chip manuals.
+ *
+ ******************************************************************************/
+void USART_InitI2s(USART_TypeDef *usart, USART_InitI2s_TypeDef *init)
+{
+ USART_Enable_TypeDef enable;
+
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_I2S_VALID(usart));
+
+ /* Override the enable setting. */
+ enable = init->sync.enable;
+ init->sync.enable = usartDisable;
+
+ /* Init USART as a sync device. */
+ USART_InitSync(usart, &init->sync);
+
+ /* Configure and enable I2CCTRL register acording to selected mode. */
+ usart->I2SCTRL = ((uint32_t) init->format) |
+ ((uint32_t) init->justify) |
+ (init->delay ? USART_I2SCTRL_DELAY : 0) |
+ (init->dmaSplit ? USART_I2SCTRL_DMASPLIT : 0) |
+ (init->mono ? USART_I2SCTRL_MONO : 0) |
+ (USART_I2SCTRL_EN);
+
+ if (enable != usartDisable)
+ {
+ USART_Enable(usart, enable);
+ }
+}
+#endif
+
+
+/***************************************************************************//**
+ * @brief
+ * Initialize automatic transmissions using PRS channel as trigger
+ * @note
+ * Initialize USART with USART_Init() before setting up PRS configuration
+ *
+ * @param[in] usart Pointer to USART to configure
+ * @param[in] init Pointer to initialization structure
+ ******************************************************************************/
+void USART_InitPrsTrigger(USART_TypeDef *usart, const USART_PrsTriggerInit_TypeDef *init)
+{
+ uint32_t trigctrl;
+
+ /* Clear values that will be reconfigured */
+ trigctrl = usart->TRIGCTRL & ~(_USART_TRIGCTRL_RXTEN_MASK |
+ _USART_TRIGCTRL_TXTEN_MASK |
+#if defined(USART_TRIGCTRL_AUTOTXTEN)
+ _USART_TRIGCTRL_AUTOTXTEN_MASK |
+#endif
+ _USART_TRIGCTRL_TSEL_MASK);
+
+#if defined(USART_TRIGCTRL_AUTOTXTEN)
+ if (init->autoTxTriggerEnable)
+ {
+ trigctrl |= USART_TRIGCTRL_AUTOTXTEN;
+ }
+#endif
+ if (init->txTriggerEnable)
+ {
+ trigctrl |= USART_TRIGCTRL_TXTEN;
+ }
+ if (init->rxTriggerEnable)
+ {
+ trigctrl |= USART_TRIGCTRL_RXTEN;
+ }
+ trigctrl |= init->prsTriggerChannel;
+
+ /* Enable new configuration */
+ usart->TRIGCTRL = trigctrl;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Reset USART/UART to same state as after a HW reset.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ ******************************************************************************/
+void USART_Reset(USART_TypeDef *usart)
+{
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart) || UART_REF_VALID(usart));
+
+ /* Make sure disabled first, before resetting other registers */
+ usart->CMD = USART_CMD_RXDIS | USART_CMD_TXDIS | USART_CMD_MASTERDIS |
+ USART_CMD_RXBLOCKDIS | USART_CMD_TXTRIDIS | USART_CMD_CLEARTX | USART_CMD_CLEARRX;
+ usart->CTRL = _USART_CTRL_RESETVALUE;
+ usart->FRAME = _USART_FRAME_RESETVALUE;
+ usart->TRIGCTRL = _USART_TRIGCTRL_RESETVALUE;
+ usart->CLKDIV = _USART_CLKDIV_RESETVALUE;
+ usart->IEN = _USART_IEN_RESETVALUE;
+ usart->IFC = _USART_IFC_MASK;
+ usart->ROUTE = _USART_ROUTE_RESETVALUE;
+
+ if (USART_IRDA_VALID(usart))
+ {
+ usart->IRCTRL = _USART_IRCTRL_RESETVALUE;
+ }
+
+#if defined(_USART_INPUT_RESETVALUE)
+ usart->INPUT = _USART_INPUT_RESETVALUE;
+#endif
+
+#if defined(_USART_I2SCTRL_RESETVALUE)
+ if (USART_I2S_VALID(usart))
+ {
+ usart->I2SCTRL = _USART_I2SCTRL_RESETVALUE;
+ }
+#endif
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Receive one 4-8 bit frame, (or part of 10-16 bit frame).
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 4-8 bits. Please refer to USART_RxExt() for reception of
+ * 9 bit frames.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint8_t USART_Rx(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXDATAV))
+ ;
+
+ return (uint8_t) (usart->RXDATA);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Receive two 4-8 bit frames, or one 10-16 bit frame.
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 10-16 bits. Please refer to USART_RxDoubleExt() for reception
+ * of two 9 bit frames.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint16_t USART_RxDouble(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXFULL))
+ ;
+
+ return (uint16_t) (usart->RXDOUBLE);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Receive two 4-9 bit frames, or one 10-16 bit frame with extended
+ * information.
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 10-16 bits and additional RX status information is required.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint32_t USART_RxDoubleExt(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXFULL))
+ ;
+
+ return usart->RXDOUBLEX;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Receive one 4-9 bit frame, (or part of 10-16 bit frame) with extended
+ * information.
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 4-9 bits and additional RX status information is required.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint16_t USART_RxExt(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXDATAV))
+ ;
+
+ return (uint16_t) (usart->RXDATAX);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Perform one 8 bit frame SPI transfer.
+ *
+ * @note
+ * This function will stall if the transmit buffer is full. When a transmit
+ * buffer becomes available, data is written and the function will wait until
+ * the data is fully transmitted. The SPI return value is then read out and
+ * returned.
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint8_t USART_SpiTransfer(USART_TypeDef *usart, uint8_t data)
+{
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDATA = (uint32_t) data;
+ while (!(usart->STATUS & USART_STATUS_TXC))
+ ;
+ return (uint8_t) (usart->RXDATA);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Transmit one 4-9 bit frame.
+ *
+ * @details
+ * Depending on frame length configuration, 4-8 (least significant) bits from
+ * @p data are transmitted. If frame length is 9, 8 bits are transmitted from
+ * @p data and one bit as specified by CTRL register, BIT8DV field. Please
+ * refer to USART_TxExt() for transmitting 9 bit frame with full control of
+ * all 9 bits.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit. See details above for further info.
+ ******************************************************************************/
+void USART_Tx(USART_TypeDef *usart, uint8_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDATA = (uint32_t) data;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Transmit two 4-9 bit frames, or one 10-16 bit frame.
+ *
+ * @details
+ * Depending on frame length configuration, 4-8 (least significant) bits from
+ * each byte in @p data are transmitted. If frame length is 9, 8 bits are
+ * transmitted from each byte in @p data adding one bit as specified by CTRL
+ * register, BIT8DV field, to each byte. Please refer to USART_TxDoubleExt()
+ * for transmitting two 9 bit frames with full control of all 9 bits.
+ *
+ * If frame length is 10-16, 10-16 (least significant) bits from @p data
+ * are transmitted.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit, the least significant byte holds the frame transmitted
+ * first. See details above for further info.
+ ******************************************************************************/
+void USART_TxDouble(USART_TypeDef *usart, uint16_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDOUBLE = (uint32_t) data;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Transmit two 4-9 bit frames, or one 10-16 bit frame with extended control.
+ *
+ * @details
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit with extended control. Contains two 16 bit words
+ * concatenated. Least significant word holds frame transitted first. If frame
+ * length is 4-9, two frames with 4-9 least significant bits from each 16 bit
+ * word are transmitted.
+ * @par
+ * If frame length is 10-16 bits, 8 data bits are taken from the least
+ * significant 16 bit word, and the remaining bits from the other 16 bit word.
+ * @par
+ * Additional control bits are available as documented in the EFM32 reference
+ * manual (set to 0 if not used). For 10-16 bit frame length, these control
+ * bits are taken from the most significant 16 bit word.
+ ******************************************************************************/
+void USART_TxDoubleExt(USART_TypeDef *usart, uint32_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDOUBLEX = data;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Transmit one 4-9 bit frame with extended control.
+ *
+ * @details
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit with extended control. Least significant bits contains
+ * frame bits, and additional control bits are available as documented in
+ * the EFM32 reference manual (set to 0 if not used).
+ ******************************************************************************/
+void USART_TxExt(USART_TypeDef *usart, uint16_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDATAX = (uint32_t) data;
+}
+
+
+/** @} (end addtogroup USART) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(USART_COUNT) && (USART_COUNT > 0) */
diff --git a/Software/emlib/src/em_vcmp.c b/Software/emlib/src/em_vcmp.c
new file mode 100644
index 0000000..e2503e1
--- /dev/null
+++ b/Software/emlib/src/em_vcmp.c
@@ -0,0 +1,184 @@
+/***************************************************************************//**
+ * @file
+ * @brief Voltage Comparator (VCMP) peripheral API
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_vcmp.h"
+#if defined(VCMP_COUNT) && (VCMP_COUNT > 0)
+
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup VCMP
+ * @brief Voltage Comparator (VCMP) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Configure and enable Voltage Comparator
+ *
+ * @param[in] vcmpInit
+ * VCMP Initialization structure
+ ******************************************************************************/
+void VCMP_Init(const VCMP_Init_TypeDef *vcmpInit)
+{
+ /* Verify input */
+ EFM_ASSERT((vcmpInit->inactive == 0) || (vcmpInit->inactive == 1));
+ EFM_ASSERT((vcmpInit->biasProg >= 0) && (vcmpInit->biasProg < 16));
+
+ /* Configure Half Bias setting */
+ if (vcmpInit->halfBias)
+ {
+ VCMP->CTRL |= VCMP_CTRL_HALFBIAS;
+ }
+ else
+ {
+ VCMP->CTRL &= ~(VCMP_CTRL_HALFBIAS);
+ }
+
+ /* Configure bias prog */
+ VCMP->CTRL &= ~(_VCMP_CTRL_BIASPROG_MASK);
+ VCMP->CTRL |= (vcmpInit->biasProg << _VCMP_CTRL_BIASPROG_SHIFT);
+
+ /* Configure sense for falling edge */
+ if (vcmpInit->irqFalling)
+ {
+ VCMP->CTRL |= VCMP_CTRL_IFALL;
+ }
+ else
+ {
+ VCMP->CTRL &= ~(VCMP_CTRL_IFALL);
+ }
+
+ /* Configure sense for rising edge */
+ if (vcmpInit->irqRising)
+ {
+ VCMP->CTRL |= VCMP_CTRL_IRISE;
+ }
+ else
+ {
+ VCMP->CTRL &= ~(VCMP_CTRL_IRISE);
+ }
+
+ /* Configure warm-up time */
+ VCMP->CTRL &= ~(_VCMP_CTRL_WARMTIME_MASK);
+ VCMP->CTRL |= (vcmpInit->warmup << _VCMP_CTRL_WARMTIME_SHIFT);
+
+ /* Configure hysteresis */
+ switch (vcmpInit->hyst)
+ {
+ case vcmpHyst20mV:
+ VCMP->CTRL |= VCMP_CTRL_HYSTEN;
+ break;
+ case vcmpHystNone:
+ VCMP->CTRL &= ~(VCMP_CTRL_HYSTEN);
+ break;
+ default:
+ break;
+ }
+
+ /* Configure inactive output value */
+ VCMP->CTRL |= (vcmpInit->inactive << _VCMP_CTRL_INACTVAL_SHIFT);
+
+ /* Configure trigger level */
+ VCMP_TriggerSet(vcmpInit->triggerLevel);
+
+ /* Enable or disable VCMP */
+ if (vcmpInit->enable)
+ {
+ VCMP->CTRL |= VCMP_CTRL_EN;
+ }
+ else
+ {
+ VCMP->CTRL &= ~(VCMP_CTRL_EN);
+ }
+
+ /* If Low Power Reference is enabled, wait until VCMP is ready */
+ /* before enabling it, see reference manual for deatils */
+ /* Configuring Low Power Ref without enable has no effect */
+ if(vcmpInit->lowPowerRef && vcmpInit->enable)
+ {
+ /* Poll for VCMP ready */
+ while(!VCMP_Ready());
+ VCMP_LowPowerRefSet(vcmpInit->lowPowerRef);
+ }
+
+ /* Clear edge interrupt */
+ VCMP_IntClear(VCMP_IF_EDGE);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Enable or disable Low Power Reference setting
+ *
+ * @param[in] enable
+ * If true, enables low power reference, if false disable low power reference
+ ******************************************************************************/
+void VCMP_LowPowerRefSet(bool enable)
+{
+ if (enable)
+ {
+ VCMP->INPUTSEL |= VCMP_INPUTSEL_LPREF;
+ }
+ else
+ {
+ VCMP->INPUTSEL &= ~(VCMP_INPUTSEL_LPREF);
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Configure trigger level of voltage comparator
+ *
+ * @param[in] level
+ * Trigger value, in range 0-63
+ ******************************************************************************/
+void VCMP_TriggerSet(int level)
+{
+ /* Trigger range is 6 bits, value from 0-63 */
+ EFM_ASSERT((level > 0) && (level < 64));
+
+ /* Set trigger level */
+ VCMP->INPUTSEL = (VCMP->INPUTSEL & ~(_VCMP_INPUTSEL_TRIGLEVEL_MASK)) |
+ (level << _VCMP_INPUTSEL_TRIGLEVEL_SHIFT);
+}
+
+
+/** @} (end addtogroup VCMP) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(VCMP_COUNT) && (VCMP_COUNT > 0) */
diff --git a/Software/emlib/src/em_wdog.c b/Software/emlib/src/em_wdog.c
new file mode 100644
index 0000000..cab8082
--- /dev/null
+++ b/Software/emlib/src/em_wdog.c
@@ -0,0 +1,224 @@
+/***************************************************************************//**
+ * @file
+ * @brief Watchdog (WDOG) peripheral API
+ * devices.
+ * @author Energy Micro AS
+ * @version 3.20.2
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
+ * obligation to support this Software. Energy Micro AS is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Energy Micro AS will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+#include "em_wdog.h"
+#if defined(WDOG_COUNT) && (WDOG_COUNT > 0)
+
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup WDOG
+ * @brief Watchdog (WDOG) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** 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] enable
+ * true to enable watchdog, false to disable. Watchdog cannot be disabled if
+ * watchdog has been locked.
+ ******************************************************************************/
+void WDOG_Enable(bool enable)
+{
+ if (!enable)
+ {
+ /* Wait for any pending previous write operation to have been completed in */
+ /* low frequency domain */
+ while (WDOG->SYNCBUSY & WDOG_SYNCBUSY_CTRL)
+ ;
+ }
+ BITBAND_Peripheral(&(WDOG->CTRL), _WDOG_CTRL_EN_SHIFT, (unsigned int)enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Feed the watchdog.
+ *
+ * @details
+ * When the watchdog is activated, it must be fed (ie clearing the counter)
+ * before it reaches the defined timeout period. Otherwise, the watchdog
+ * will generate a reset.
+ ******************************************************************************/
+void WDOG_Feed(void)
+{
+ /* The watchdog should not be fed while it is disabled */
+ if ( !(WDOG->CTRL & WDOG_CTRL_EN) )
+ {
+ return;
+ }
+
+ /* If a previous clearing is being synchronized to LF domain, then 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 the watchdog. */
+ if (WDOG->SYNCBUSY & WDOG_SYNCBUSY_CMD)
+ {
+ return;
+ }
+ /* Before writing to the WDOG_CMD register we also need to make sure that
+ * any previous write to WDOG_CTRL is complete. */
+ while ( WDOG->SYNCBUSY & WDOG_SYNCBUSY_CTRL );
+
+ WDOG->CMD = WDOG_CMD_CLEAR;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Initialize watchdog (assuming the watchdog 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] init
+ * Structure holding watchdog configuration. A default setting
+ * #WDOG_INIT_DEFAULT is available for init.
+ ******************************************************************************/
+void WDOG_Init(const WDOG_Init_TypeDef *init)
+{
+ uint32_t setting;
+
+ if (init->enable)
+ {
+ setting = WDOG_CTRL_EN;
+ }
+ else
+ {
+ setting = 0;
+ }
+
+ if (init->debugRun)
+ {
+ setting |= WDOG_CTRL_DEBUGRUN;
+ }
+
+ if (init->em2Run)
+ {
+ setting |= WDOG_CTRL_EM2RUN;
+ }
+
+ if (init->em3Run)
+ {
+ setting |= WDOG_CTRL_EM3RUN;
+ }
+
+ if (init->em4Block)
+ {
+ setting |= WDOG_CTRL_EM4BLOCK;
+ }
+
+ if (init->swoscBlock)
+ {
+ setting |= WDOG_CTRL_SWOSCBLOCK;
+ }
+
+ setting |= ((uint32_t)(init->clkSel) << _WDOG_CTRL_CLKSEL_SHIFT) |
+ ((uint32_t)(init->perSel) << _WDOG_CTRL_PERSEL_SHIFT);
+
+ /* Wait for any pending previous write operation to have been completed in */
+ /* low frequency domain */
+ while (WDOG->SYNCBUSY & WDOG_SYNCBUSY_CTRL)
+ ;
+
+ WDOG->CTRL = setting;
+
+ /* Optional register locking */
+ if (init->lock)
+ {
+ if (init->enable)
+ {
+ WDOG_Lock();
+ }
+ else
+ {
+ BITBAND_Peripheral(&(WDOG->CTRL), _WDOG_CTRL_LOCK_SHIFT, 1);
+ }
+ }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Lock the watchdog configuration.
+ *
+ * @details
+ * This prevents errors from overwriting the watchdog configuration, possibly
+ * disabling it. Only a reset can unlock the watchdog config, once locked.
+ *
+ * If the LFRCO or LFXO clocks are used to clock the watchdog, one should
+ * consider using the option of inhibiting those clocks to be disabled,
+ * please see the WDOG_Enable() init 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.
+ ******************************************************************************/
+void WDOG_Lock(void)
+{
+ /* Wait for any pending previous write operation to have been completed in */
+ /* low frequency domain */
+ while (WDOG->SYNCBUSY & WDOG_SYNCBUSY_CTRL)
+ ;
+
+ /* Disable writing to the control register */
+ BITBAND_Peripheral(&(WDOG->CTRL), _WDOG_CTRL_LOCK_SHIFT, 1);
+}
+
+
+/** @} (end addtogroup WDOG) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */