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