diff --git a/cpu/efm32_common/emlib/Changes_emlib.txt b/cpu/efm32_common/emlib/Changes_emlib.txt new file mode 100644 index 0000000000000..0e1de71e53f36 --- /dev/null +++ b/cpu/efm32_common/emlib/Changes_emlib.txt @@ -0,0 +1,386 @@ +================ Revision history ============================================ +4.2.1: + - Added errata fix for an issue that may cause BOD resets in EM2 when using + DCDC-to-DVDD mode. The fix is implemented in EMU_DCDCInit(). + - Added function EMU_DCDCPowerOff() for boards with physically disconnected DCDC. + - Current consumption is optimized for DCDC bypass mode. This update is + implemented in EMU_DCDCInit(). + +4.2.0: + - Updated I2C clock divider equation for platform 2 parts. Added constraints + to HFPER clock frequency in I2C_BusFreqSet(). + - EMU EMU_EM23VregMode_TypeDef replaced with a bool. + - Added support for GPIO alternate drive strength and alternate control modes. + - DCDC setup is simplified. More tuning and optimization settings added to + EMU_DCDCInit(). + - Added member pinRetentionMode to EMU_EM4Init_TypeDef. + - Added function EMU_UnlatchPinRetention() to support unlatching of pin + retention in EM4H/S. + - Fixed bug in ADC_InitScan() which caused a overwrite of single conversion + mode calibration values. + - Added support for CRYPTO module on Pearl and Jade Geckos (em_crypto.c/h) + +4.1.1: + - EMU_DCDCInit() updated with new parameters for EM2 and 3. Current consumption + with DCDC at expected levels for EFR32 and EFM32PG revA1, A2 and B0. + - EMU_DCDCInit_TypeDef updated with more parameters. EMU_DcdcLpcmpBiasMode_TypeDef + is removed. + - More assertions added to EMU_DCDCInit(). + - HFXO default parameters updated. + - ADC defaults updated. + - RMU pin mode set fixed. + - Added missing define for cmuSelect_ULFRCO. + - Added missing functions for handling peripheral interrupts. + - Added support for VMON. + +4.1.0: + - The typedef EMU_EM23Init_TypeDef which is a parameter to EMU_EM23Init() + has got a new definition. + - Initial support _SILICON_LABS_32B_PLATFORM_2 devices added + +4.0.0: + - Use ARM CMSIS version 4.2.0. + - New style version macros in em_version.h. + +3.20.14: + - USB release only. + +3.20.13: + - Added new style family #defines in em_system.h, including EZR32 families. + - Fixed I2C_FREQ_STANDARD_MAX macros. + - Fixed bug in MSC_WriteWord which called internal functions that were linked + to flash for armgcc. All subsequent calls of MSC_WriteWord should now be + linked to RAM for all supported compilers. The internals of MSC_WriteWord + will check the global variable SystemCoreClock in order to make sure the + frequency is high enough for flash operations. If the core clock frequency + is changed, software is responsible for calling MSC_Init or + SystemCoreClockGet in order to set the SystemCoreClock variable to the + correct value. + - Added errata fix IDAC_101. + +3.20.12: + - Added errata fix EMU_108. + - #ifdef's now use register defines instead of a mix of register and family defines. + - Added a case for when there are only 4 DMA channels available: + Alignment was (correctly) defined at 7 bit, but got asserted for 8 bit, leading + to unpredictable tripped asserts. + - Added USART_INITPRSTRIGGER_DEFAULT defined structure to support HWCONF. + - Added support for LFC clock tree. + - Added CMU_USHFRCOBandSet() and CMU_USHFRCOBandGet(). + +3.20.10: + - Maintenance release, no changes. + +3.20.9: + - Added support for Happy Gecko including support for the new oscillator USHFRCO. + - Added MSC_WriteWordFast() function. This flash write function has a similar + performance as the old MSC_WriteWord(), but it disables interrupts and + requires a core clock frequency of at least 14MHz. The new MSC_WriteWord() + is slower, but it does not disable interrupts and may be called with core + clock frequencies down to 1MHz. + - Fixed a bug in EMU_EnterEM4() that set other EM4 configuration bits to 0 + on EM4 entry. + - Added EMU_EM23Init(). + - Fixed a bug in CMU_FlashWaitStateControl() where it failed to set the + required wait-state configuration if the MSC is locked. + - Added EMU interrupt handling functions. + - BURTC_Reset() changed to use async reset RMU_CTRL_BURSTEN instead of + reset value writeback. This makes the function independent of a selected + and enabled clock. + - BURTC_Sync() now returns without waiting for BURTC->SYNCBUSY to clear + when no clock is selected in BURTC_CTRL_CLKSEL. + - Fixed assertion bug in ACMP_ChannelSet() that checked the negSel parameter + against the wrong upper bound. + +3.20.7: + - Fixed CMU_MAX_FREQ_HFLE macro for Wonder family. + - Fixed MSC_WriteWord() bug. + - Added syncbusy wait in RTC_Reset() for Gecko family. + +3.20.6: + - Corrected fix for Errata EMU_E107. + +3.20.5: + - Updated license texts. + - Removed unnecessary fix for Wonder Gecko. + - Updated LFXO temperature compensation in CHIP_Init(). + - Changed LESENSE_ScanStart, LESENSE_ScanStop, LESENSE_DecoderStart, + LESENSE_ResultBufferClear() and LESENSE_Reset() functions to wait until + CMD register writes complete in order to make sure CMD register writes do + not break each other, and for register values to be consistent when + returning from functions that write to the CMD register. + - Added fix for Errata EMU_E107. + - Added family to SYSTEM_ChipRevision_TypeDef. + - Fixed bug in function AES_OFB128 which failed on Zero Gecko. + - Fixed RMU_ResetCauseGet() to return correct reset causes. + - Fixed bug in RTC_CounterReset() which failed to reset counter immediately + after return on Gecko devices. + - Added static inline non-blocking USART receive functions (USART_Rx...). + - Added function SYSTEM_GetFamily(). + - Added function DAC_ChannelOutputSet(). + - Fixed MSC_WriteWord() to not use WDOUBLE if LPWRITE is set. + +3.20.2: + - Fixed bug regarding when MEMINFO in DEVINFO was introduced. + The correct crossover is production revision 18. + - Fixed bug in WDOG_Feed() which does not feed the watchdog if the watchdog + is disabled. Previously, the watchdog was broken after WDOG_Feed() fed it + when it was disabled. +- Fixed issue in em_i2c.c, which should set the NACK bit in the I2C CMD + register for the next to last byte received. The exception is when only + one byte is to be received. Then the NACK bit must be set like the + previous code was doing. +- Added function BURTC_ClockFreqGet() in order to determine clock frequency + of BURTC. +- Fixed bug in BURTC_Reset() which made a subsequent call to BURTC_Init hang. +- Added support for the IDAC module on the Zero Gecko family, em_idac.c/h. +- Fixed bug in DAC_PrescaleCalc() which could return higher values than + the maximum prescaler value. The fix makes sure to return the max prescaler + value resulting in possible higher DAC frequency than requested. +- Fixed I2C_BusFreqSet to use documented values for Nlow and Nhigh values, + and do not decrement the div(isor) by one according to the formula because + this resulted in higher I2C bus frequencies than desired. + +3.20.0: + - LEUART: Added LEUART_TxDmaInEM2Enable() and LEUART_RxDmaInEM2Enable() for + enabling and disabling DMA LEUART RX and Tx in EM2 support. + +3.0.3: + - Internal release for testing Wonder Gecko support. + - SYSTEM: Added function to enable/disable FPU access on Wonder parts, + SYSTEM_FpuAccessModeSet(). + - USART: Added USART_SpiTransfer() function. + +3.0.2: + - MSC: In MSC_WriteWord(), added support for double word write cycle support + (WDOUBLE) on devices with more than 512KiBytes of Flash memory. This can + almost double the speed of the MSC_WriteWord function for large data sizes. + - MSC: In MSC_ErasePage(), added support for devices with Flash page size + larger than 512 bytes, like Giant and Leopard Gecko. + - CMU: Fixed bug in CMU_ClockDivSet(). Clear HFLE and HFCORECLKLEDIV flags when + the core runs at frequencies up to 32MHz. + - CMU: Fixed bug in CMU_ClockEnable(): Set the HFLE and HFCORECLKLEDIV flags + when the CORE clock runs at frequencies higher than 32MHz. + - CMU: Fixed bug in CMU_ClockSelectSet(): Set HFLE and DIV4 factor for peripheral + clock if HFCORE clock for LE is enabled and the CORE clock runs at + frequencies higher than 32MHz. + - BITBAND: Added BITBAND_PeripheralRead() and BITBAND_SRAMRead() functions. + - DMA: Added #ifndef EXCLUDE_DEFAULT_DMA_IRQ_HANDLER around DMA_IRQHandler in + order for the user to implement a custom IRQ handler or run without a DMA + IRQ handler by defining EXCLUDE_DEFAULT_DMA_IRQ_HANDLER with the -D compiler + option. + - BURTC: In functions BURTC_Init() and BURTC_CompareSet(), moved SYNCBUSY + loops in front of modifications of registers COMP0 and LPMODE. + - MSC: Fixed ram_code section error on Keil toolchain. + - MSC: Removed uneeded code from MSC init and deinit which would have no + effect (Big thanks to Martin Schreiber for reporting this bug!). + - System: Added access functions for reading some values out of the Device + Information page. + +3.0.1: + - LFXO fix for Giant family. + - USART: Fix for EFM32TG108Fxx which does not have USART0. + - EBI: The write to the CTRL register now happens before the ROUTE registers + are set. This avoids potential glitches. + - LESENSE: Fix issue when using lesenseAltExMapACMP. + - TIMER: Fix compilation on devices where ADC is not available. + - LCD: Fix bug where Aloc field would not be set to 0. + - BURTC: Fix Reset function by adding reset of COMP0 register and removing + reset of POWERDOWN register. The POWERDOWN register cannot be used to + power up the blocks after it has been powered down. + - CMU: Fixed bug where ClockDivSet, ClockDivGet and ClockFreqGet didn't work for + cmuClock_LCDpre clock. Also corrected 3 wrongly typed constants. + - CMU: Fixed bug where LFBE field in LFCLKSEL was not cleared before setting + bit-value. + - CMU: Fixed bug with CMU_ClockSelectGet. Did not give correct return value + for cmuClock_LFB. + - I2C: Fixed bug where I2C_Init would set divisor depending on the previous + master/slave configuration, not the one set in the initialization. + - I2C: Fixed issue in the function I2C_BusFreqSet (called by I2C_Init). The + input parameter 'I2C_ClockHLR_TypeDef type' was not in use. The fix enables + the parameter to add support for 'i2cClockHLRAsymetric' and 'i2cClockHLRFast' + modes. In order to use 'i2cClockHLRAsymetric' and 'i2cClockHLRFast' the + frequency of the HFPER clock may need to be increased. + - OPAMP: Fixed bug in the function OPAMP_Enable where an incorrect register + was used when setting the OPA2 calibration value. + - LEUART: Fixed issue in LEUART_BaudrateSet when a high clock frequency and a + low baudrate can overflow the clock divisor register (CLKDIV). The fix uses + an assert statement to check whether the calculated clock divisor is out of + range. + - USART: Fixed issue in USART_BaudrateAsyncSet when a high clock frequency and + a low baudrate can overflow the clock divisor register (CLKDIV). The fix uses + an assert statement to check whether the calculated clock divisor is out of + range. + +3.0.0: + - efm32lib renamed emlib, as it will include support for non-EFM32 devices + in the future + - Added CMSIS_V3 compatibility fixes, and use of CMSIS_V3 definitions + - See Device/Changes-EnergyMicro.txt for detailed path changes + - New prefixes of all files, efm32_.c/h to em_.c/h + - New names for readme and changes files + - RMU - BUMODERST not masked away when EM4 bits has been set + - CMU - CMU_LFClkGet now accounts for ULFRCO bit for Tiny Gecko + +2.4.1: + - New, open source friendly license + - Fixed BURTC initialization hang if init->enable was false + - Fixed CMU issue with USBC and USB checks not being used correctly + - Added CMU feature, missing TIMER3 support + - Improved accuracy of SPI mode for USART baudrate calculation + - Corrected USBC HFCLKNODIV setting to comply with new header file defines + +2.4.0: + - Added efm32_version.h defining software version number + - Added BURTC support for Giant and Leopard Gecko + - Added RMU_ResetControl for BU reset flag + - Added some missing features to EMU for back up domain and EM4 support + - ADC TimebaseCalc(), Giant/Leopard Gecko have max 5 bits in TIMEBASE field + - Removed EMU Backup Power Domain threshold setings from EMU_BUPDInit, added + EMU_BUThresRangeSet() and EMU_BUThresholdSet() API calls. Threshold values + are factory calibrated and should not usually be overridden by the user. + +2.3.2: + - Added Tiny Gecko and Giant Gecko support in RMU for new reset causes + - CMU_ClockFreqGet will now report correct clock rates if HFLE is set (/4) + - Added Giant Gecko specific MSC_MassErase(), erase entire flash + - Added Giant Gecko specific MSC_BusStrategy (inline) function + - MSC_Init() will now configure TIMEBASE correctly according to AUXHFRCO clock + rate for Tiny Gecko and Giant Gecko + +2.3.0: + - USART - Added USART_InitPrsTrigger to initialize USART PRS triggered + transmissions. + - CMU - numerous updates, now supports full clock tree of Giant/Tiny Gecko + - CMU_ClockDivSet/Get will now use real dividend and not logarithmic values + as earlier. Prior enumerated values have been kept for backward compatibility. + - Added support for CMU HFLE and DIV4 factor for core clock for LE + peripherals + - Added support for alternate LCD segment animation range for Giant Gecko + - Fixed bug: Don't enable VCMP low power reference until after warm up, + allow biasprog value of 0 in VCMP_Init() + - Added support for ALTMAP (256MB address map) in EBI_BankAddress() + - TIMER_Init() will now reset CNT value + +2.2.2: + - Added DAC0 channel 0 and 1 to ACMP for Tiny and Giant devices + - Fixed bug in CMU for MSC WAITSTATE configuration, leading to too high wait + states depending on clock rate + - Fixed bug in CMU for UART1 clock enable + +2.2.1: + - UART_Reset() and LEUART_Reset() will now reset ROUTE register as well, this + will mean GPIO pins will not be driven after this call. Take care to ensure + that GPIO ROUTE register is configured after calls to *UART_Init*Sync + - Fixed problems with EFM_ASSERT when using UART in USART API + - Added Giant Gecko support for EBI (new modes and TFT direct drive) + - Added Giant Gecko support for CMU 2 WAIT STATES, and I2C1 + - Added Giant Gecko support for UART1 in CMU + - Added Giant Gecko support for DMA LOOP and 2D Copy operations + +2.1.0: + - EMU_Restore will now disable HFRCO if it was not enabled when entering + an Energy Mode + - Run time changes only applies to Gecko devices, filter out Tiny and Giant + for CHIP_Init(); + - Added const specificers to various initialization structures, to ensure + they can reside in flash instead of SRAM + - Bugfix in efm32_i2c.c, keep returning i2cTransferInProgress until done + +2.0.1: + - Changed enum OPAMP_PosSel_TypeDef. Enum value opaPosSelOpaIn changed from + DAC_OPA0MUX_POSSEL_OPA1IN to DAC_OPA0MUX_POSSEL_OPA0INP. + - Bugfix in efm32_lesense.h, LESENSE_ChClk_TypeDef now contains unshifted + values, fixed the implementation in efm32_lesense.c where the bug prevented + the sampleClk to be set to AUXHFRCO. + +2.0.0: + - USART_Init-functions now calls USART_Reset() which will also disable/reset + interrupt + - USART_BaudrateSyncSet() now asserts on invalid oversample configuration + - Added initialization of parity bit in LEUART_Init() + - Added Tiny Gecko support for CMU, ULFRCO, LESENSE clocks and continuous + calibration + - Added Tiny Gecko support for GPIO, EM4 pin retention and wake up support + - Added Tiny Gecko support for I2S, SPI auto TX mode on USART + - Added Tiny Gecko support for CACHE mesasurements for MSC module + - Added Tiny Gecko support for LCD module (with no HIGH segment registers) + - Added Tiny Gecko support for TIMER, PWM 2x, (DT lock not supported) + - Added Tiny Gecko support for LESENSE module + - Added Tiny Gecko support for PRS input in PCNT + - Added Tiny Gecko support for async signals in PRS, PRS_SourceAsyncSignalSet() + - Initial support for some Giant Gecko features, where overlapping with Tiny + - Removed LPFEN / LPFREQ support from DAC + - Fixed comments around interrupt functions, making it clear it is bitwise + logical or interrupt flags + - Fixed PCNT initialization for external clock configurations, making sure + config is synchronized at startup to 3 clocks. Note fix only works for + >revC EFM32G devices. + - Fixed efm32_cmu.c, EFM_ASSERT statement for LEUART clock div logic was + inverted + - Fixed ADC_InitScan, PRSSEL shift value corrected + - Fixed CMU_ClockFreqGet for devices that do not have I2C + - Fixed I2C_TransferInit for devices with more than one I2C-bus (Giant Gecko) + - Changed ACMP_Disable() implementation, now only disables the ACMP instance + by clearing the EN bit in the CTRL register + - Removed ACMP_DisableNoReset() function + - Fixed ACMP_Init(), removed automatic enabling, added new structure member + "enaReq" for ACMP_Init_TypeDef to control, fixed the EFM_ASSERT of the + biasprog parameter + - Added default configuration macro ACMP_INIT_DEFAULT for ACMP_Init_TypeDef + - Fixed ACMP_CapsenseInit(), removed automatic enabling, added new structure member + "enaReq" for ACMP_CapsenseInit_TypeDef to control, fixed the EFM_ASSERT of + the biasprog parameter + - Changed the name of the default configuration macro for + ACMP_CapsenseInit_TypeDef to ACMP_CAPSENSE_INIT_DEFAULT + - Added RTC_Reset and RTC_CounterReset functions for RTC + +1.3.0: + - MSC is automatically enabled/disabled when using the MSC API. This saves + power, and reduces errors due to not calling MSC_Init(). + - Added API for controlling Cortex-M3 MPU (memory protection unit) + - Adjusted bit fields to comply with latest CMSIS release, see EFM_CMSIS + changes file for details + - Fixed issue with bit mask clearing in ACMP + - Functions ACMP_Enable and ACMP_DisableNoReset added + - Added comment about rev.C chips in PCNT, CMD_LTOPBIM not neccessary any more + - Added missing instance validity asserts to peripherals (ACMP, LEUART, USART) + - Fixed UART0 check in CMU_ClockFreqGet() + - Fixed command sync for PCNT before setting TOPB value during init + - Fixed instance validity check macro in PCNT + - Fixed TIMER_Reset() removed write to unimplemented timer channel registers + - Fixed EFM_ASSERT statements in ACMP, VCMP + - General code style update: added missing curly braces, default cases, etc. + +1.2.1: + - Feature complete efm32lib, now also includes peripheral API for modules + AES,PCNT,MSC,ACMP,VCMP,LCD,EBI + - Fixed _TIMER_CC_CTRL_ICEDGE flags for correct timer configuration + - Fixed ADC calibration of Single and Scan mode of operation + - Added PCNT (ChipRev A/B PCNT0 errata NOT supported) and AES support + - Fixed conditional inclusion in efm32_emu.h + - Fixed code for LEUART0 for devices with multiple LEUARTs. + - Fixed incorrect setting of DOUT for GPIO configuration + +1.1.4 + - Fix for TIMER_INIT_DEFAULT + +1.1.3: + - Added ADC, DAC, LETIMER, PRS, TIMER (except DTI) support + - Added utility for fetching chip revision (efm32_system.c/h) + - Removed RTC instance ref in API, only one RTC will be supported + (Affects also define in efm32_cmu.h) + - Added default init struct macros for LEUART, USART + - Added msbf parameter in USART synchronous init struct, USART_InitSync_TypeDef. + - Updated reset for I2C, USART, LEUART to also reset IEN register. + - Corrected fault in GPIO_PortOutSet() + +1.1.2: + - Corrected minor issues in EMU, EM3 mode when restoring clocks + - Corrected RMU reset cause checking + - Changed GPIO enumerator symbols to start with gpio (from GPIO_) + - Changed CMU and WDOG enum typedefs to start with CMU_/WDOG_ (from cmu/wdog) + - Added USART/UART, LEUART, DMA, I2C support + +1.1.1: + - First version including support for CMU, DBG, EMU, GPIO, RTC, WDOG diff --git a/cpu/efm32_common/emlib/ReadMe_emlib.txt b/cpu/efm32_common/emlib/ReadMe_emlib.txt new file mode 100644 index 0000000000000..a70002be25a84 --- /dev/null +++ b/cpu/efm32_common/emlib/ReadMe_emlib.txt @@ -0,0 +1,68 @@ +================ Silicon Labs Peripheral Library ============================ + +This directory, "emlib", contains the Silicon Labs Peripheral Support +library for the EFM32 and EZR32 series of microcontrollers and System-On-Chip +devices. + +Some design guidelines for this library: + +* Follow the guidelines established by ARM's and Silicon Labs's adaptation + of the CMSIS (see below) standard. + +* Be usable as a starting point for developing richer, more target specific + functionality (i.e. copy and modify further). + +* Ability to be used as a standalone software component, to be used by other + drivers, that should cover "the most common cases". + +* Readability of the code and usability preferred before optimization for speed + and size or covering a particular "narrow" purpose. + +* As little "cross-dependency" between modules as possible, to enable users to + pick and choose what they want. + +================ About CMSIS ================================================ + +These APIs are based on EM_CMSIS "Device" header file structure. + +As a result of this, the library requires basic C99-support. You might have +to enable C99 support in your compiler. Comments are in doxygen compatible +format. + +The EM_CMSIS library contains all peripheral module registers and bit field +descriptors. + +To download EM_CMSIS, go to + http://www.silabs.com/support/pages/document-library.aspx?p=MCUs--32-bit + +For more information about CMSIS see + http://www.onarm.com + http://www.arm.com/products/CPUs/CMSIS.html + +The requirements for using CMSIS also apply to this package. + +================ File structure ============================================== + +inc/ - header files +src/ - source files + +================ Licenses ==================================================== + +See the top of each file for SW license. Basically you are free to use the +Silicon Labs code for any project using Silicon Labs devices. Parts of the +CMSIS library is copyrighted by ARM Inc. See "License.doc" for ARM's CMSIS +license. + +================ Software updates ============================================ + +Silicon Labs continually works to provide updated and improved emlib, example +code and other software of use for Silicon Labs customers. Please check the +download section of Silicon Labs's web site at + + http://www.silabs.com + +for the latest releases. If you download and install the +Simplicity Studio application, you will be notified about updates when +available. + +(C) Copyright Silicon Labs, 2015 diff --git a/cpu/efm32_common/emlib/inc/em_acmp.h b/cpu/efm32_common/emlib/inc/em_acmp.h new file mode 100644 index 0000000000000..14c6d820707eb --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_acmp.h @@ -0,0 +1,933 @@ +/***************************************************************************//** + * @file em_acmp.h + * @brief Analog Comparator (ACMP) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_ACMP_H__ +#define __SILICON_LABS_EM_ACMP_H__ + +#include "em_device.h" +#if defined(ACMP_COUNT) && (ACMP_COUNT > 0) + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup ACMP + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Resistor values used for the internal capacative sense resistor. See the + * datasheet for your device for details on each resistor value. */ +typedef enum +{ + acmpResistor0 = _ACMP_INPUTSEL_CSRESSEL_RES0, /**< Resistor value 0 */ + acmpResistor1 = _ACMP_INPUTSEL_CSRESSEL_RES1, /**< Resistor value 1 */ + acmpResistor2 = _ACMP_INPUTSEL_CSRESSEL_RES2, /**< Resistor value 2 */ + acmpResistor3 = _ACMP_INPUTSEL_CSRESSEL_RES3, /**< Resistor value 3 */ +#if defined(_ACMP_INPUTSEL_CSRESSEL_RES4) + acmpResistor4 = _ACMP_INPUTSEL_CSRESSEL_RES4, /**< Resistor value 4 */ + acmpResistor5 = _ACMP_INPUTSEL_CSRESSEL_RES5, /**< Resistor value 5 */ + acmpResistor6 = _ACMP_INPUTSEL_CSRESSEL_RES6, /**< Resistor value 6 */ + acmpResistor7 = _ACMP_INPUTSEL_CSRESSEL_RES7, /**< Resistor value 7 */ +#endif +} ACMP_CapsenseResistor_TypeDef; + +/** Hysteresis level. See datasheet for your device for details on each + * level. */ +typedef enum +{ +#if defined(_ACMP_CTRL_HYSTSEL_MASK) + acmpHysteresisLevel0 = _ACMP_CTRL_HYSTSEL_HYST0, /**< Hysteresis level 0 */ + acmpHysteresisLevel1 = _ACMP_CTRL_HYSTSEL_HYST1, /**< Hysteresis level 1 */ + acmpHysteresisLevel2 = _ACMP_CTRL_HYSTSEL_HYST2, /**< Hysteresis level 2 */ + acmpHysteresisLevel3 = _ACMP_CTRL_HYSTSEL_HYST3, /**< Hysteresis level 3 */ + acmpHysteresisLevel4 = _ACMP_CTRL_HYSTSEL_HYST4, /**< Hysteresis level 4 */ + acmpHysteresisLevel5 = _ACMP_CTRL_HYSTSEL_HYST5, /**< Hysteresis level 5 */ + acmpHysteresisLevel6 = _ACMP_CTRL_HYSTSEL_HYST6, /**< Hysteresis level 6 */ + acmpHysteresisLevel7 = _ACMP_CTRL_HYSTSEL_HYST7 /**< Hysteresis level 7 */ +#endif +#if defined(_ACMP_HYSTERESIS0_HYST_MASK) + acmpHysteresisLevel0 = _ACMP_HYSTERESIS0_HYST_HYST0, /**< Hysteresis level 0 */ + acmpHysteresisLevel1 = _ACMP_HYSTERESIS0_HYST_HYST1, /**< Hysteresis level 1 */ + acmpHysteresisLevel2 = _ACMP_HYSTERESIS0_HYST_HYST2, /**< Hysteresis level 2 */ + acmpHysteresisLevel3 = _ACMP_HYSTERESIS0_HYST_HYST3, /**< Hysteresis level 3 */ + acmpHysteresisLevel4 = _ACMP_HYSTERESIS0_HYST_HYST4, /**< Hysteresis level 4 */ + acmpHysteresisLevel5 = _ACMP_HYSTERESIS0_HYST_HYST5, /**< Hysteresis level 5 */ + acmpHysteresisLevel6 = _ACMP_HYSTERESIS0_HYST_HYST6, /**< Hysteresis level 6 */ + acmpHysteresisLevel7 = _ACMP_HYSTERESIS0_HYST_HYST7, /**< Hysteresis level 7 */ + acmpHysteresisLevel8 = _ACMP_HYSTERESIS0_HYST_HYST8, /**< Hysteresis level 8 */ + acmpHysteresisLevel9 = _ACMP_HYSTERESIS0_HYST_HYST9, /**< Hysteresis level 9 */ + acmpHysteresisLevel10 = _ACMP_HYSTERESIS0_HYST_HYST10, /**< Hysteresis level 10 */ + acmpHysteresisLevel11 = _ACMP_HYSTERESIS0_HYST_HYST11, /**< Hysteresis level 11 */ + acmpHysteresisLevel12 = _ACMP_HYSTERESIS0_HYST_HYST12, /**< Hysteresis level 12 */ + acmpHysteresisLevel13 = _ACMP_HYSTERESIS0_HYST_HYST13, /**< Hysteresis level 13 */ + acmpHysteresisLevel14 = _ACMP_HYSTERESIS0_HYST_HYST14, /**< Hysteresis level 14 */ + acmpHysteresisLevel15 = _ACMP_HYSTERESIS0_HYST_HYST15, /**< Hysteresis level 15 */ +#endif +} ACMP_HysteresisLevel_TypeDef; + +#if defined(_ACMP_CTRL_WARMTIME_MASK) +/** ACMP warmup time. The delay is measured in HFPERCLK cycles and should + * be at least 10 us. */ +typedef enum +{ + /** 4 HFPERCLK cycles warmup */ + acmpWarmTime4 = _ACMP_CTRL_WARMTIME_4CYCLES, + /** 8 HFPERCLK cycles warmup */ + acmpWarmTime8 = _ACMP_CTRL_WARMTIME_8CYCLES, + /** 16 HFPERCLK cycles warmup */ + acmpWarmTime16 = _ACMP_CTRL_WARMTIME_16CYCLES, + /** 32 HFPERCLK cycles warmup */ + acmpWarmTime32 = _ACMP_CTRL_WARMTIME_32CYCLES, + /** 64 HFPERCLK cycles warmup */ + acmpWarmTime64 = _ACMP_CTRL_WARMTIME_64CYCLES, + /** 128 HFPERCLK cycles warmup */ + acmpWarmTime128 = _ACMP_CTRL_WARMTIME_128CYCLES, + /** 256 HFPERCLK cycles warmup */ + acmpWarmTime256 = _ACMP_CTRL_WARMTIME_256CYCLES, + /** 512 HFPERCLK cycles warmup */ + acmpWarmTime512 = _ACMP_CTRL_WARMTIME_512CYCLES +} ACMP_WarmTime_TypeDef; +#endif + +#if defined(_ACMP_CTRL_INPUTRANGE_MASK) +/** + * Adjust performance of the ACMP for a given input voltage range + */ +typedef enum +{ + acmpInputRangeFull = _ACMP_CTRL_INPUTRANGE_FULL, /**< Input can be from 0 to Vdd */ + acmpInputRangeHigh = _ACMP_CTRL_INPUTRANGE_GTVDDDIV2, /**< Input will always be greater than Vdd/2 */ + acmpInputRangeLow = _ACMP_CTRL_INPUTRANGE_LTVDDDIV2 /**< Input will always be less than Vdd/2 */ +} ACMP_InputRange_TypeDef; +#endif + +#if defined(_ACMP_CTRL_PWRSEL_MASK) +/** + * ACMP Power source. + */ +typedef enum +{ + acmpPowerSourceAvdd = _ACMP_CTRL_PWRSEL_AVDD, /**< Power the ACMP using the AVDD supply */ + acmpPowerSourceVddVreg = _ACMP_CTRL_PWRSEL_VREGVDD, /**< Power the ACMP using the VREGVDD supply */ + acmpPowerSourceIOVdd0 = _ACMP_CTRL_PWRSEL_IOVDD0, /**< Power the ACMP using the IOVDD/IOVDD0 supply */ + acmpPowerSourceIOVdd1 = _ACMP_CTRL_PWRSEL_IOVDD1, /**< Power the ACMP using the IOVDD1 supply (if part has two I/O voltages) */ +} ACMP_PowerSource_TypeDef; +#endif + +#if defined(_ACMP_CTRL_ACCURACY_MASK) +/** + * ACMP accuracy mode. + */ +typedef enum +{ + acmpAccuracyLow = _ACMP_CTRL_ACCURACY_LOW, /**< Low-accuracy mode but consume less current */ + acmpAccuracyHigh = _ACMP_CTRL_ACCURACY_HIGH /**< High-accuracy mode but consume more current */ +} ACMP_Accuracy_TypeDef; +#endif + +#if defined(_ACMP_INPUTSEL_VASEL_MASK) +/** ACMP Input to the VA divider. This enum is used to select the input for + * the VA Divider */ +typedef enum +{ + acmpVAInputVDD = _ACMP_INPUTSEL_VASEL_VDD, + acmpVAInputAPORT2YCH0 = _ACMP_INPUTSEL_VASEL_APORT2YCH0, + acmpVAInputAPORT2YCH2 = _ACMP_INPUTSEL_VASEL_APORT2YCH2, + acmpVAInputAPORT2YCH4 = _ACMP_INPUTSEL_VASEL_APORT2YCH4, + acmpVAInputAPORT2YCH6 = _ACMP_INPUTSEL_VASEL_APORT2YCH6, + acmpVAInputAPORT2YCH8 = _ACMP_INPUTSEL_VASEL_APORT2YCH8, + acmpVAInputAPORT2YCH10 = _ACMP_INPUTSEL_VASEL_APORT2YCH10, + acmpVAInputAPORT2YCH12 = _ACMP_INPUTSEL_VASEL_APORT2YCH12, + acmpVAInputAPORT2YCH14 = _ACMP_INPUTSEL_VASEL_APORT2YCH14, + acmpVAInputAPORT2YCH16 = _ACMP_INPUTSEL_VASEL_APORT2YCH16, + acmpVAInputAPORT2YCH18 = _ACMP_INPUTSEL_VASEL_APORT2YCH18, + acmpVAInputAPORT2YCH20 = _ACMP_INPUTSEL_VASEL_APORT2YCH20, + acmpVAInputAPORT2YCH22 = _ACMP_INPUTSEL_VASEL_APORT2YCH22, + acmpVAInputAPORT2YCH24 = _ACMP_INPUTSEL_VASEL_APORT2YCH24, + acmpVAInputAPORT2YCH26 = _ACMP_INPUTSEL_VASEL_APORT2YCH26, + acmpVAInputAPORT2YCH28 = _ACMP_INPUTSEL_VASEL_APORT2YCH28, + acmpVAInputAPORT2YCH30 = _ACMP_INPUTSEL_VASEL_APORT2YCH30, + acmpVAInputAPORT1XCH0 = _ACMP_INPUTSEL_VASEL_APORT1XCH0, + acmpVAInputAPORT1YCH1 = _ACMP_INPUTSEL_VASEL_APORT1YCH1, + acmpVAInputAPORT1XCH2 = _ACMP_INPUTSEL_VASEL_APORT1XCH2, + acmpVAInputAPORT1YCH3 = _ACMP_INPUTSEL_VASEL_APORT1YCH3, + acmpVAInputAPORT1XCH4 = _ACMP_INPUTSEL_VASEL_APORT1XCH4, + acmpVAInputAPORT1YCH5 = _ACMP_INPUTSEL_VASEL_APORT1YCH5, + acmpVAInputAPORT1XCH6 = _ACMP_INPUTSEL_VASEL_APORT1XCH6, + acmpVAInputAPORT1YCH7 = _ACMP_INPUTSEL_VASEL_APORT1YCH7, + acmpVAInputAPORT1XCH8 = _ACMP_INPUTSEL_VASEL_APORT1XCH8, + acmpVAInputAPORT1YCH9 = _ACMP_INPUTSEL_VASEL_APORT1YCH9, + acmpVAInputAPORT1XCH10 = _ACMP_INPUTSEL_VASEL_APORT1XCH10, + acmpVAInputAPORT1YCH11 = _ACMP_INPUTSEL_VASEL_APORT1YCH11, + acmpVAInputAPORT1XCH12 = _ACMP_INPUTSEL_VASEL_APORT1XCH12, + acmpVAInputAPORT1YCH13 = _ACMP_INPUTSEL_VASEL_APORT1YCH13, + acmpVAInputAPORT1XCH14 = _ACMP_INPUTSEL_VASEL_APORT1XCH14, + acmpVAInputAPORT1YCH15 = _ACMP_INPUTSEL_VASEL_APORT1YCH15, + acmpVAInputAPORT1XCH16 = _ACMP_INPUTSEL_VASEL_APORT1XCH16, + acmpVAInputAPORT1YCH17 = _ACMP_INPUTSEL_VASEL_APORT1YCH17, + acmpVAInputAPORT1XCH18 = _ACMP_INPUTSEL_VASEL_APORT1XCH18, + acmpVAInputAPORT1YCH19 = _ACMP_INPUTSEL_VASEL_APORT1YCH19, + acmpVAInputAPORT1XCH20 = _ACMP_INPUTSEL_VASEL_APORT1XCH20, + acmpVAInputAPORT1YCH21 = _ACMP_INPUTSEL_VASEL_APORT1YCH21, + acmpVAInputAPORT1XCH22 = _ACMP_INPUTSEL_VASEL_APORT1XCH22, + acmpVAInputAPORT1YCH23 = _ACMP_INPUTSEL_VASEL_APORT1YCH23, + acmpVAInputAPORT1XCH24 = _ACMP_INPUTSEL_VASEL_APORT1XCH24, + acmpVAInputAPORT1YCH25 = _ACMP_INPUTSEL_VASEL_APORT1YCH25, + acmpVAInputAPORT1XCH26 = _ACMP_INPUTSEL_VASEL_APORT1XCH26, + acmpVAInputAPORT1YCH27 = _ACMP_INPUTSEL_VASEL_APORT1YCH27, + acmpVAInputAPORT1XCH28 = _ACMP_INPUTSEL_VASEL_APORT1XCH28, + acmpVAInputAPORT1YCH29 = _ACMP_INPUTSEL_VASEL_APORT1YCH29, + acmpVAInputAPORT1XCH30 = _ACMP_INPUTSEL_VASEL_APORT1XCH30, + acmpVAInputAPORT1YCH31 = _ACMP_INPUTSEL_VASEL_APORT1YCH31 +} ACMP_VAInput_TypeDef; +#endif + +#if defined(_ACMP_INPUTSEL_VBSEL_MASK) +/** + * ACMP Input to the VB divider. This enum is used to select the input for + * the VB divider. + */ +typedef enum +{ + acmpVBInput1V25 = _ACMP_INPUTSEL_VBSEL_1V25, + acmpVBInput2V5 = _ACMP_INPUTSEL_VBSEL_2V5 +} ACMP_VBInput_TypeDef; +#endif + +#if defined(_ACMP_INPUTSEL_VLPSEL_MASK) +/** + * ACMP Low-Power Input Selection. + */ +typedef enum +{ + acmpVLPInputVADIV = _ACMP_INPUTSEL_VLPSEL_VADIV, + acmpVLPInputVBDIV = _ACMP_INPUTSEL_VLPSEL_VBDIV +} ACMP_VLPInput_Typedef; +#endif + +#if defined(_SILICON_LABS_32B_PLATFORM_2) +/** ACMP Input Selection */ +typedef enum +{ + acmpInputAPORT0XCH0 = _ACMP_INPUTSEL_POSSEL_APORT0XCH0, + acmpInputAPORT0XCH1 = _ACMP_INPUTSEL_POSSEL_APORT0XCH1, + acmpInputAPORT0XCH2 = _ACMP_INPUTSEL_POSSEL_APORT0XCH2, + acmpInputAPORT0XCH3 = _ACMP_INPUTSEL_POSSEL_APORT0XCH3, + acmpInputAPORT0XCH4 = _ACMP_INPUTSEL_POSSEL_APORT0XCH4, + acmpInputAPORT0XCH5 = _ACMP_INPUTSEL_POSSEL_APORT0XCH5, + acmpInputAPORT0XCH6 = _ACMP_INPUTSEL_POSSEL_APORT0XCH6, + acmpInputAPORT0XCH7 = _ACMP_INPUTSEL_POSSEL_APORT0XCH7, + acmpInputAPORT0XCH8 = _ACMP_INPUTSEL_POSSEL_APORT0XCH8, + acmpInputAPORT0XCH9 = _ACMP_INPUTSEL_POSSEL_APORT0XCH9, + acmpInputAPORT0XCH10 = _ACMP_INPUTSEL_POSSEL_APORT0XCH10, + acmpInputAPORT0XCH11 = _ACMP_INPUTSEL_POSSEL_APORT0XCH11, + acmpInputAPORT0XCH12 = _ACMP_INPUTSEL_POSSEL_APORT0XCH12, + acmpInputAPORT0XCH13 = _ACMP_INPUTSEL_POSSEL_APORT0XCH13, + acmpInputAPORT0XCH14 = _ACMP_INPUTSEL_POSSEL_APORT0XCH14, + acmpInputAPORT0XCH15 = _ACMP_INPUTSEL_POSSEL_APORT0XCH15, + acmpInputAPORT0YCH0 = _ACMP_INPUTSEL_POSSEL_APORT0YCH0, + acmpInputAPORT0YCH1 = _ACMP_INPUTSEL_POSSEL_APORT0YCH1, + acmpInputAPORT0YCH2 = _ACMP_INPUTSEL_POSSEL_APORT0YCH2, + acmpInputAPORT0YCH3 = _ACMP_INPUTSEL_POSSEL_APORT0YCH3, + acmpInputAPORT0YCH4 = _ACMP_INPUTSEL_POSSEL_APORT0YCH4, + acmpInputAPORT0YCH5 = _ACMP_INPUTSEL_POSSEL_APORT0YCH5, + acmpInputAPORT0YCH6 = _ACMP_INPUTSEL_POSSEL_APORT0YCH6, + acmpInputAPORT0YCH7 = _ACMP_INPUTSEL_POSSEL_APORT0YCH7, + acmpInputAPORT0YCH8 = _ACMP_INPUTSEL_POSSEL_APORT0YCH8, + acmpInputAPORT0YCH9 = _ACMP_INPUTSEL_POSSEL_APORT0YCH9, + acmpInputAPORT0YCH10 = _ACMP_INPUTSEL_POSSEL_APORT0YCH10, + acmpInputAPORT0YCH11 = _ACMP_INPUTSEL_POSSEL_APORT0YCH11, + acmpInputAPORT0YCH12 = _ACMP_INPUTSEL_POSSEL_APORT0YCH12, + acmpInputAPORT0YCH13 = _ACMP_INPUTSEL_POSSEL_APORT0YCH13, + acmpInputAPORT0YCH14 = _ACMP_INPUTSEL_POSSEL_APORT0YCH14, + acmpInputAPORT0YCH15 = _ACMP_INPUTSEL_POSSEL_APORT0YCH15, + acmpInputAPORT1XCH0 = _ACMP_INPUTSEL_POSSEL_APORT1XCH0, + acmpInputAPORT1YCH1 = _ACMP_INPUTSEL_POSSEL_APORT1YCH1, + acmpInputAPORT1XCH2 = _ACMP_INPUTSEL_POSSEL_APORT1XCH2, + acmpInputAPORT1YCH3 = _ACMP_INPUTSEL_POSSEL_APORT1YCH3, + acmpInputAPORT1XCH4 = _ACMP_INPUTSEL_POSSEL_APORT1XCH4, + acmpInputAPORT1YCH5 = _ACMP_INPUTSEL_POSSEL_APORT1YCH5, + acmpInputAPORT1XCH6 = _ACMP_INPUTSEL_POSSEL_APORT1XCH6, + acmpInputAPORT1YCH7 = _ACMP_INPUTSEL_POSSEL_APORT1YCH7, + acmpInputAPORT1XCH8 = _ACMP_INPUTSEL_POSSEL_APORT1XCH8, + acmpInputAPORT1YCH9 = _ACMP_INPUTSEL_POSSEL_APORT1YCH9, + acmpInputAPORT1XCH10 = _ACMP_INPUTSEL_POSSEL_APORT1XCH10, + acmpInputAPORT1YCH11 = _ACMP_INPUTSEL_POSSEL_APORT1YCH11, + acmpInputAPORT1XCH12 = _ACMP_INPUTSEL_POSSEL_APORT1XCH12, + acmpInputAPORT1YCH13 = _ACMP_INPUTSEL_POSSEL_APORT1YCH13, + acmpInputAPORT1XCH14 = _ACMP_INPUTSEL_POSSEL_APORT1XCH14, + acmpInputAPORT1YCH15 = _ACMP_INPUTSEL_POSSEL_APORT1YCH15, + acmpInputAPORT1XCH16 = _ACMP_INPUTSEL_POSSEL_APORT1XCH16, + acmpInputAPORT1YCH17 = _ACMP_INPUTSEL_POSSEL_APORT1YCH17, + acmpInputAPORT1XCH18 = _ACMP_INPUTSEL_POSSEL_APORT1XCH18, + acmpInputAPORT1YCH19 = _ACMP_INPUTSEL_POSSEL_APORT1YCH19, + acmpInputAPORT1XCH20 = _ACMP_INPUTSEL_POSSEL_APORT1XCH20, + acmpInputAPORT1YCH21 = _ACMP_INPUTSEL_POSSEL_APORT1YCH21, + acmpInputAPORT1XCH22 = _ACMP_INPUTSEL_POSSEL_APORT1XCH22, + acmpInputAPORT1YCH23 = _ACMP_INPUTSEL_POSSEL_APORT1YCH23, + acmpInputAPORT1XCH24 = _ACMP_INPUTSEL_POSSEL_APORT1XCH24, + acmpInputAPORT1YCH25 = _ACMP_INPUTSEL_POSSEL_APORT1YCH25, + acmpInputAPORT1XCH26 = _ACMP_INPUTSEL_POSSEL_APORT1XCH26, + acmpInputAPORT1YCH27 = _ACMP_INPUTSEL_POSSEL_APORT1YCH27, + acmpInputAPORT1XCH28 = _ACMP_INPUTSEL_POSSEL_APORT1XCH28, + acmpInputAPORT1YCH29 = _ACMP_INPUTSEL_POSSEL_APORT1YCH29, + acmpInputAPORT1XCH30 = _ACMP_INPUTSEL_POSSEL_APORT1XCH30, + acmpInputAPORT1YCH31 = _ACMP_INPUTSEL_POSSEL_APORT1YCH31, + acmpInputAPORT2YCH0 = _ACMP_INPUTSEL_POSSEL_APORT2YCH0, + acmpInputAPORT2XCH1 = _ACMP_INPUTSEL_POSSEL_APORT2XCH1, + acmpInputAPORT2YCH2 = _ACMP_INPUTSEL_POSSEL_APORT2YCH2, + acmpInputAPORT2XCH3 = _ACMP_INPUTSEL_POSSEL_APORT2XCH3, + acmpInputAPORT2YCH4 = _ACMP_INPUTSEL_POSSEL_APORT2YCH4, + acmpInputAPORT2XCH5 = _ACMP_INPUTSEL_POSSEL_APORT2XCH5, + acmpInputAPORT2YCH6 = _ACMP_INPUTSEL_POSSEL_APORT2YCH6, + acmpInputAPORT2XCH7 = _ACMP_INPUTSEL_POSSEL_APORT2XCH7, + acmpInputAPORT2YCH8 = _ACMP_INPUTSEL_POSSEL_APORT2YCH8, + acmpInputAPORT2XCH9 = _ACMP_INPUTSEL_POSSEL_APORT2XCH9, + acmpInputAPORT2YCH10 = _ACMP_INPUTSEL_POSSEL_APORT2YCH10, + acmpInputAPORT2XCH11 = _ACMP_INPUTSEL_POSSEL_APORT2XCH11, + acmpInputAPORT2YCH12 = _ACMP_INPUTSEL_POSSEL_APORT2YCH12, + acmpInputAPORT2XCH13 = _ACMP_INPUTSEL_POSSEL_APORT2XCH13, + acmpInputAPORT2YCH14 = _ACMP_INPUTSEL_POSSEL_APORT2YCH14, + acmpInputAPORT2XCH15 = _ACMP_INPUTSEL_POSSEL_APORT2XCH15, + acmpInputAPORT2YCH16 = _ACMP_INPUTSEL_POSSEL_APORT2YCH16, + acmpInputAPORT2XCH17 = _ACMP_INPUTSEL_POSSEL_APORT2XCH17, + acmpInputAPORT2YCH18 = _ACMP_INPUTSEL_POSSEL_APORT2YCH18, + acmpInputAPORT2XCH19 = _ACMP_INPUTSEL_POSSEL_APORT2XCH19, + acmpInputAPORT2YCH20 = _ACMP_INPUTSEL_POSSEL_APORT2YCH20, + acmpInputAPORT2XCH21 = _ACMP_INPUTSEL_POSSEL_APORT2XCH21, + acmpInputAPORT2YCH22 = _ACMP_INPUTSEL_POSSEL_APORT2YCH22, + acmpInputAPORT2XCH23 = _ACMP_INPUTSEL_POSSEL_APORT2XCH23, + acmpInputAPORT2YCH24 = _ACMP_INPUTSEL_POSSEL_APORT2YCH24, + acmpInputAPORT2XCH25 = _ACMP_INPUTSEL_POSSEL_APORT2XCH25, + acmpInputAPORT2YCH26 = _ACMP_INPUTSEL_POSSEL_APORT2YCH26, + acmpInputAPORT2XCH27 = _ACMP_INPUTSEL_POSSEL_APORT2XCH27, + acmpInputAPORT2YCH28 = _ACMP_INPUTSEL_POSSEL_APORT2YCH28, + acmpInputAPORT2XCH29 = _ACMP_INPUTSEL_POSSEL_APORT2XCH29, + acmpInputAPORT2YCH30 = _ACMP_INPUTSEL_POSSEL_APORT2YCH30, + acmpInputAPORT2XCH31 = _ACMP_INPUTSEL_POSSEL_APORT2XCH31, + acmpInputAPORT3XCH0 = _ACMP_INPUTSEL_POSSEL_APORT3XCH0, + acmpInputAPORT3YCH1 = _ACMP_INPUTSEL_POSSEL_APORT3YCH1, + acmpInputAPORT3XCH2 = _ACMP_INPUTSEL_POSSEL_APORT3XCH2, + acmpInputAPORT3YCH3 = _ACMP_INPUTSEL_POSSEL_APORT3YCH3, + acmpInputAPORT3XCH4 = _ACMP_INPUTSEL_POSSEL_APORT3XCH4, + acmpInputAPORT3YCH5 = _ACMP_INPUTSEL_POSSEL_APORT3YCH5, + acmpInputAPORT3XCH6 = _ACMP_INPUTSEL_POSSEL_APORT3XCH6, + acmpInputAPORT3YCH7 = _ACMP_INPUTSEL_POSSEL_APORT3YCH7, + acmpInputAPORT3XCH8 = _ACMP_INPUTSEL_POSSEL_APORT3XCH8, + acmpInputAPORT3YCH9 = _ACMP_INPUTSEL_POSSEL_APORT3YCH9, + acmpInputAPORT3XCH10 = _ACMP_INPUTSEL_POSSEL_APORT3XCH10, + acmpInputAPORT3YCH11 = _ACMP_INPUTSEL_POSSEL_APORT3YCH11, + acmpInputAPORT3XCH12 = _ACMP_INPUTSEL_POSSEL_APORT3XCH12, + acmpInputAPORT3YCH13 = _ACMP_INPUTSEL_POSSEL_APORT3YCH13, + acmpInputAPORT3XCH14 = _ACMP_INPUTSEL_POSSEL_APORT3XCH14, + acmpInputAPORT3YCH15 = _ACMP_INPUTSEL_POSSEL_APORT3YCH15, + acmpInputAPORT3XCH16 = _ACMP_INPUTSEL_POSSEL_APORT3XCH16, + acmpInputAPORT3YCH17 = _ACMP_INPUTSEL_POSSEL_APORT3YCH17, + acmpInputAPORT3XCH18 = _ACMP_INPUTSEL_POSSEL_APORT3XCH18, + acmpInputAPORT3YCH19 = _ACMP_INPUTSEL_POSSEL_APORT3YCH19, + acmpInputAPORT3XCH20 = _ACMP_INPUTSEL_POSSEL_APORT3XCH20, + acmpInputAPORT3YCH21 = _ACMP_INPUTSEL_POSSEL_APORT3YCH21, + acmpInputAPORT3XCH22 = _ACMP_INPUTSEL_POSSEL_APORT3XCH22, + acmpInputAPORT3YCH23 = _ACMP_INPUTSEL_POSSEL_APORT3YCH23, + acmpInputAPORT3XCH24 = _ACMP_INPUTSEL_POSSEL_APORT3XCH24, + acmpInputAPORT3YCH25 = _ACMP_INPUTSEL_POSSEL_APORT3YCH25, + acmpInputAPORT3XCH26 = _ACMP_INPUTSEL_POSSEL_APORT3XCH26, + acmpInputAPORT3YCH27 = _ACMP_INPUTSEL_POSSEL_APORT3YCH27, + acmpInputAPORT3XCH28 = _ACMP_INPUTSEL_POSSEL_APORT3XCH28, + acmpInputAPORT3YCH29 = _ACMP_INPUTSEL_POSSEL_APORT3YCH29, + acmpInputAPORT3XCH30 = _ACMP_INPUTSEL_POSSEL_APORT3XCH30, + acmpInputAPORT3YCH31 = _ACMP_INPUTSEL_POSSEL_APORT3YCH31, + acmpInputAPORT4YCH0 = _ACMP_INPUTSEL_POSSEL_APORT4YCH0, + acmpInputAPORT4XCH1 = _ACMP_INPUTSEL_POSSEL_APORT4XCH1, + acmpInputAPORT4YCH2 = _ACMP_INPUTSEL_POSSEL_APORT4YCH2, + acmpInputAPORT4XCH3 = _ACMP_INPUTSEL_POSSEL_APORT4XCH3, + acmpInputAPORT4YCH4 = _ACMP_INPUTSEL_POSSEL_APORT4YCH4, + acmpInputAPORT4XCH5 = _ACMP_INPUTSEL_POSSEL_APORT4XCH5, + acmpInputAPORT4YCH6 = _ACMP_INPUTSEL_POSSEL_APORT4YCH6, + acmpInputAPORT4XCH7 = _ACMP_INPUTSEL_POSSEL_APORT4XCH7, + acmpInputAPORT4YCH8 = _ACMP_INPUTSEL_POSSEL_APORT4YCH8, + acmpInputAPORT4XCH9 = _ACMP_INPUTSEL_POSSEL_APORT4XCH9, + acmpInputAPORT4YCH10 = _ACMP_INPUTSEL_POSSEL_APORT4YCH10, + acmpInputAPORT4XCH11 = _ACMP_INPUTSEL_POSSEL_APORT4XCH11, + acmpInputAPORT4YCH12 = _ACMP_INPUTSEL_POSSEL_APORT4YCH12, + acmpInputAPORT4XCH13 = _ACMP_INPUTSEL_POSSEL_APORT4XCH13, + acmpInputAPORT4YCH16 = _ACMP_INPUTSEL_POSSEL_APORT4YCH16, + acmpInputAPORT4XCH17 = _ACMP_INPUTSEL_POSSEL_APORT4XCH17, + acmpInputAPORT4YCH18 = _ACMP_INPUTSEL_POSSEL_APORT4YCH18, + acmpInputAPORT4XCH19 = _ACMP_INPUTSEL_POSSEL_APORT4XCH19, + acmpInputAPORT4YCH20 = _ACMP_INPUTSEL_POSSEL_APORT4YCH20, + acmpInputAPORT4XCH21 = _ACMP_INPUTSEL_POSSEL_APORT4XCH21, + acmpInputAPORT4YCH22 = _ACMP_INPUTSEL_POSSEL_APORT4YCH22, + acmpInputAPORT4XCH23 = _ACMP_INPUTSEL_POSSEL_APORT4XCH23, + acmpInputAPORT4YCH24 = _ACMP_INPUTSEL_POSSEL_APORT4YCH24, + acmpInputAPORT4XCH25 = _ACMP_INPUTSEL_POSSEL_APORT4XCH25, + acmpInputAPORT4YCH26 = _ACMP_INPUTSEL_POSSEL_APORT4YCH26, + acmpInputAPORT4XCH27 = _ACMP_INPUTSEL_POSSEL_APORT4XCH27, + acmpInputAPORT4YCH28 = _ACMP_INPUTSEL_POSSEL_APORT4YCH28, + acmpInputAPORT4XCH29 = _ACMP_INPUTSEL_POSSEL_APORT4XCH29, + acmpInputAPORT4YCH30 = _ACMP_INPUTSEL_POSSEL_APORT4YCH30, + acmpInputAPORT4YCH14 = _ACMP_INPUTSEL_POSSEL_APORT4YCH14, + acmpInputAPORT4XCH15 = _ACMP_INPUTSEL_POSSEL_APORT4XCH15, + acmpInputAPORT4XCH31 = _ACMP_INPUTSEL_POSSEL_APORT4XCH31, + acmpInputDACOUT0 = _ACMP_INPUTSEL_POSSEL_DACOUT0, + acmpInputDACOUT1 = _ACMP_INPUTSEL_POSSEL_DACOUT1, + acmpInputVLP = _ACMP_INPUTSEL_POSSEL_VLP, + acmpInputVBDIV = _ACMP_INPUTSEL_POSSEL_VBDIV, + acmpInputVADIV = _ACMP_INPUTSEL_POSSEL_VADIV, + acmpInputVDD = _ACMP_INPUTSEL_POSSEL_VDD, + acmpInputVSS = _ACMP_INPUTSEL_POSSEL_VSS, +} ACMP_Channel_TypeDef; +#else +/** ACMP inputs. Note that scaled VDD and bandgap references can only be used + * as negative inputs. */ +typedef enum +{ + /** Channel 0 */ + acmpChannel0 = _ACMP_INPUTSEL_NEGSEL_CH0, + /** Channel 1 */ + acmpChannel1 = _ACMP_INPUTSEL_NEGSEL_CH1, + /** Channel 2 */ + acmpChannel2 = _ACMP_INPUTSEL_NEGSEL_CH2, + /** Channel 3 */ + acmpChannel3 = _ACMP_INPUTSEL_NEGSEL_CH3, + /** Channel 4 */ + acmpChannel4 = _ACMP_INPUTSEL_NEGSEL_CH4, + /** Channel 5 */ + acmpChannel5 = _ACMP_INPUTSEL_NEGSEL_CH5, + /** Channel 6 */ + acmpChannel6 = _ACMP_INPUTSEL_NEGSEL_CH6, + /** Channel 7 */ + acmpChannel7 = _ACMP_INPUTSEL_NEGSEL_CH7, + /** 1.25V internal reference */ + acmpChannel1V25 = _ACMP_INPUTSEL_NEGSEL_1V25, + /** 2.5V internal reference */ + acmpChannel2V5 = _ACMP_INPUTSEL_NEGSEL_2V5, + /** Scaled VDD reference */ + acmpChannelVDD = _ACMP_INPUTSEL_NEGSEL_VDD, + +#if defined(_ACMP_INPUTSEL_NEGSEL_DAC0CH0) + /** DAC0 channel 0 */ + acmpChannelDAC0Ch0 = _ACMP_INPUTSEL_NEGSEL_DAC0CH0, +#endif + +#if defined(_ACMP_INPUTSEL_NEGSEL_DAC0CH1) + /** DAC0 channel 1 */ + acmpChannelDAC0Ch1 = _ACMP_INPUTSEL_NEGSEL_DAC0CH1, +#endif + +#if defined(_ACMP_INPUTSEL_NEGSEL_CAPSENSE) + /** Capacitive sense mode */ + acmpChannelCapSense = _ACMP_INPUTSEL_NEGSEL_CAPSENSE, +#endif +} ACMP_Channel_TypeDef; +#endif + +/******************************************************************************* + ****************************** STRUCTS ************************************ + ******************************************************************************/ + +/** Capsense initialization structure. */ +typedef struct +{ + /** Full bias current. See the ACMP chapter about bias and response time in + * the reference manual for details. */ + bool fullBias; + +#if defined(_ACMP_CTRL_HALFBIAS_MASK) + /** Half bias current. See the ACMP chapter about bias and response time in + * the reference manual for details. */ + bool halfBias; +#endif + + /** Bias current. See the ACMP chapter about bias and response time in the + * reference manual for details. */ + uint32_t biasProg; + +#if defined(_ACMP_CTRL_WARMTIME_MASK) + /** Warmup time. This is measured in HFPERCLK cycles and should be + * about 10us in wall clock time. */ + ACMP_WarmTime_TypeDef warmTime; +#endif + +#if defined(_ACMP_CTRL_HYSTSEL_MASK) + /** Hysteresis level */ + ACMP_HysteresisLevel_TypeDef hysteresisLevel; +#else + /** Hysteresis level when ACMP output is 0 */ + ACMP_HysteresisLevel_TypeDef hysteresisLevel_0; + + /** Hysteresis level when ACMP output is 1 */ + ACMP_HysteresisLevel_TypeDef hysteresisLevel_1; +#endif + + /** Resistor used in the capacative sensing circuit. For values see + * your device datasheet. */ + ACMP_CapsenseResistor_TypeDef resistor; + +#if defined(_ACMP_INPUTSEL_LPREF_MASK) + /** Low power reference enabled. This setting, if enabled, reduces the + * power used by the VDD and bandgap references. */ + bool lowPowerReferenceEnabled; +#endif + +#if defined(_ACMP_INPUTSEL_VDDLEVEL_MASK) + /** Vdd reference value. VDD_SCALED = (Vdd * VDDLEVEL) / 63. + * Valid values are in the range 0-63. */ + uint32_t vddLevel; +#else + /** + * This value configures the upper voltage threshold of the capsense + * oscillation rail. + * + * The voltage threshold is calculated as + * Vdd * (vddLevelHigh + 1) / 64 + */ + uint32_t vddLevelHigh; + + /** + * This value configures the lower voltage threshold of the capsense + * oscillation rail. + * + * The voltage threshold is calculated as + * Vdd * (vddLevelLow + 1) / 64 + */ + uint32_t vddLevelLow; +#endif + + /** If true, ACMP is being enabled after configuration. */ + bool enable; +} ACMP_CapsenseInit_TypeDef; + +/** Default config for capacitive sense mode initialization. */ +#if defined(_ACMP_HYSTERESIS0_HYST_MASK) +#define ACMP_CAPSENSE_INIT_DEFAULT \ +{ \ + false, /* Don't use fullBias to lower power consumption */ \ + 0x20, /* Using biasProg value of 0x20 (32) */ \ + acmpHysteresisLevel8, /* Use hysteresis level 8 when ACMP output is 0 */ \ + acmpHysteresisLevel8, /* Use hysteresis level 8 when ACMP output is 1 */ \ + acmpResistor5, /* Use internal resistor value 5 */ \ + 0x30, /* VDD level high */ \ + 0x10, /* VDD level low */ \ + true /* Enable after init. */ \ +} +#elif defined(_ACMP_CTRL_WARMTIME_MASK) +#define ACMP_CAPSENSE_INIT_DEFAULT \ +{ \ + false, /* fullBias */ \ + false, /* halfBias */ \ + 0x7, /* biasProg */ \ + acmpWarmTime512, /* 512 cycle warmup to be safe */ \ + acmpHysteresisLevel5, \ + acmpResistor3, \ + false, /* low power reference */ \ + 0x3D, /* VDD level */ \ + true /* Enable after init. */ \ +} +#else +#define ACMP_CAPSENSE_INIT_DEFAULT \ +{ \ + false, /* fullBias */ \ + false, /* halfBias */ \ + 0x7, /* biasProg */ \ + acmpHysteresisLevel5, \ + acmpResistor3, \ + false, /* low power reference */ \ + 0x3D, /* VDD level */ \ + true /* Enable after init. */ \ +} +#endif + +/** ACMP initialization structure. */ +typedef struct +{ + /** Full bias current. See the ACMP chapter about bias and response time in + * the reference manual for details. */ + bool fullBias; + +#if defined(_ACMP_CTRL_HALFBIAS_MASK) + /** Half bias current. See the ACMP chapter about bias and response time in + * the reference manual for details. */ + bool halfBias; +#endif + + /** Bias current. See the ACMP chapter about bias and response time in the + * reference manual for details. Valid values are in the range 0-7. */ + uint32_t biasProg; + + /** Enable setting the interrupt flag on falling edge */ + bool interruptOnFallingEdge; + + /** Enable setting the interrupt flag on rising edge */ + bool interruptOnRisingEdge; + +#if defined(_ACMP_CTRL_INPUTRANGE_MASK) + /** Input range. Adjust this setting to optimize the performance for a + * given input voltage range. */ + ACMP_InputRange_TypeDef inputRange; +#endif + +#if defined(_ACMP_CTRL_ACCURACY_MASK) + /** ACMP accuracy mode. Select the accuracy mode that matches the + * required current usage and accuracy requirement. Low accuracy + * consumes less current while high accuracy consumes more current. */ + ACMP_Accuracy_TypeDef accuracy; +#endif + +#if defined(_ACMP_CTRL_PWRSEL_MASK) + /** Select the power source for the ACMP. */ + ACMP_PowerSource_TypeDef powerSource; +#endif + +#if defined(_ACMP_CTRL_WARMTIME_MASK) + /** Warmup time. This is measured in HFPERCLK cycles and should be + * about 10us in wall clock time. */ + ACMP_WarmTime_TypeDef warmTime; +#endif + +#if defined(_ACMP_CTRL_HYSTSEL_MASK) + /** Hysteresis level */ + ACMP_HysteresisLevel_TypeDef hysteresisLevel; +#else + /** Hysteresis when ACMP output is 0 */ + ACMP_HysteresisLevel_TypeDef hysteresisLevel_0; + + /** Hysteresis when ACMP output is 1 */ + ACMP_HysteresisLevel_TypeDef hysteresisLevel_1; +#endif + +#if defined(_ACMP_INPUTSEL_VLPSEL_MASK) + /** VLP Input source. Select between using VADIV or VBDIV as the VLP + * source. */ + ACMP_VLPInput_Typedef vlpInput; +#endif + + /** Inactive value emitted by the ACMP during warmup */ + bool inactiveValue; + +#if defined(_ACMP_INPUTSEL_LPREF_MASK) + /** Low power reference enabled. This setting, if enabled, reduces the + * power used by the VDD and bandgap references. */ + bool lowPowerReferenceEnabled; +#endif + +#if defined(_ACMP_INPUTSEL_VDDLEVEL_MASK) + /** Vdd reference value. VDD_SCALED = VDD * VDDLEVEL * 50mV/3.8V. + * Valid values are in the range 0-63. */ + uint32_t vddLevel; +#endif + + /** If true, ACMP is being enabled after configuration. */ + bool enable; +} ACMP_Init_TypeDef; + +/** Default config for ACMP regular initialization. */ +#if defined(_ACMP_HYSTERESIS0_HYST_MASK) +#define ACMP_INIT_DEFAULT \ +{ \ + false, /* fullBias */ \ + 0x7, /* biasProg */ \ + false, /* No interrupt on falling edge. */ \ + false, /* No interrupt on rising edge. */ \ + acmpInputRangeFull, /* Input range from 0 to Vdd. */ \ + acmpAccuracyLow, /* Low accuracy, less current usage. */ \ + acmpPowerSourceAvdd, /* Use the AVDD supply. */ \ + acmpHysteresisLevel5, /* Use hysteresis level 5 when output is 0 */ \ + acmpHysteresisLevel5, /* Use hysteresis level 5 when output is 1 */ \ + acmpVLPInputVADIV, /* Use VADIV as the VLP input source. */ \ + false, /* Output 0 when ACMP is inactive. */ \ + true /* Enable after init. */ \ +} +#elif defined(_ACMP_CTRL_WARMTIME_MASK) +#define ACMP_INIT_DEFAULT \ +{ \ + false, /* fullBias */ \ + false, /* halfBias */ \ + 0x7, /* biasProg */ \ + false, /* No interrupt on falling edge. */ \ + false, /* No interrupt on rising edge. */ \ + acmpWarmTime512, /* 512 cycle warmup to be safe */ \ + acmpHysteresisLevel5, \ + false, /* Disabled emitting inactive value during warmup. */ \ + false, /* low power reference */ \ + 0x3D, /* VDD level */ \ + true /* Enable after init. */ \ +} +#else +#define ACMP_INIT_DEFAULT \ +{ \ + false, /* fullBias */ \ + false, /* halfBias */ \ + 0x7, /* biasProg */ \ + false, /* No interrupt on falling edge. */ \ + false, /* No interrupt on rising edge. */ \ + acmpHysteresisLevel5, \ + false, /* Disabled emitting inactive value during warmup. */ \ + false, /* low power reference */ \ + 0x3D, /* VDD level */ \ + true /* Enable after init. */ \ +} +#endif + +#if defined(_ACMP_INPUTSEL_VASEL_MASK) +/** VA Configuration structure. This struct is used to configure the + * VA voltage input source and it's dividers. */ +typedef struct +{ + ACMP_VAInput_TypeDef input; /**< VA voltage input source */ + + /** + * Divider for VA voltage input source when ACMP output is 0. This value is + * used to divide the VA voltage input source by a specific value. The valid + * range is between 0 and 63. + * + * VA divided = VA input * (div0 + 1) / 64 + */ + uint32_t div0; + + /** + * Divider for VA voltage input source when ACMP output is 1. This value is + * used to divide the VA voltage input source by a specific value. The valid + * range is between 0 and 63. + * + * VA divided = VA input * (div1 + 1) / 64 + */ + uint32_t div1; +} ACMP_VAConfig_TypeDef; + +#define ACMP_VACONFIG_DEFAULT \ +{ \ + acmpVAInputVDD, /* Use Vdd as VA voltage input source */ \ + 63, /* No division of the VA source when ACMP output is 0 */ \ + 63, /* No division of the VA source when ACMP output is 1 */ \ +} +#endif + +#if defined(_ACMP_INPUTSEL_VBSEL_MASK) +/** VB Configuration structure. This struct is used to configure the + * VB voltage input source and it's dividers. */ +typedef struct +{ + ACMP_VBInput_TypeDef input; /**< VB Voltage input source */ + + /** + * Divider for VB voltage input source when ACMP output is 0. This value is + * used to divide the VB voltage input source by a specific value. The valid + * range is between 0 and 63. + * + * VB divided = VB input * (div0 + 1) / 64 + */ + uint32_t div0; + + /** + * Divider for VB voltage input source when ACMP output is 1. This value is + * used to divide the VB voltage input source by a specific value. The valid + * range is between 0 and 63. + * + * VB divided = VB input * (div1 + 1) / 64 + */ + uint32_t div1; +} ACMP_VBConfig_TypeDef; + +#define ACMP_VBCONFIG_DEFAULT \ +{ \ + acmpVBInput1V25, /* Use 1.25 V as VB voltage input source */ \ + 63, /* No division of the VB source when ACMP output is 0 */ \ + 63, /* No division of the VB source when ACMP output is 1 */ \ +} +#endif + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init); +void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel); +void ACMP_ChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef negSel, ACMP_Channel_TypeDef posSel); +void ACMP_Disable(ACMP_TypeDef *acmp); +void ACMP_Enable(ACMP_TypeDef *acmp); +void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert); +void ACMP_Init(ACMP_TypeDef *acmp, const ACMP_Init_TypeDef *init); +void ACMP_Reset(ACMP_TypeDef *acmp); +#if defined(_ACMP_INPUTSEL_VASEL_MASK) +void ACMP_VASetup(ACMP_TypeDef *acmp, const ACMP_VAConfig_TypeDef *vaconfig); +#endif +#if defined(_ACMP_INPUTSEL_VBSEL_MASK) +void ACMP_VBSetup(ACMP_TypeDef *acmp, const ACMP_VBConfig_TypeDef *vbconfig); +#endif + +/***************************************************************************//** + * @brief + * Clear one or more pending ACMP interrupts. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @param[in] flags + * Pending ACMP interrupt source to clear. Use a bitwise logic OR combination + * of valid interrupt flags for the ACMP module (ACMP_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ACMP_IntClear(ACMP_TypeDef *acmp, uint32_t flags) +{ + acmp->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more ACMP interrupts. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @param[in] flags + * ACMP interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the ACMP module (ACMP_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ACMP_IntDisable(ACMP_TypeDef *acmp, uint32_t flags) +{ + acmp->IEN &= ~(flags); +} + + +/***************************************************************************//** + * @brief + * Enable one or more ACMP interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using ACMP_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @param[in] flags + * ACMP interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the ACMP module (ACMP_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ACMP_IntEnable(ACMP_TypeDef *acmp, uint32_t flags) +{ + acmp->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending ACMP interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @return + * ACMP interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the ACMP module (ACMP_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t ACMP_IntGet(ACMP_TypeDef *acmp) +{ + return acmp->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending ACMP interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled ACMP interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in ACMPx_IEN_nnn + * register (ACMPx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the ACMP module + * (ACMPx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t ACMP_IntGetEnabled(ACMP_TypeDef *acmp) +{ + uint32_t tmp; + + /* Store ACMPx->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + tmp = acmp->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return acmp->IF & tmp; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending ACMP interrupts from SW. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @param[in] flags + * ACMP interrupt sources to set to pending. Use a bitwise logic OR + * combination of valid interrupt flags for the ACMP module (ACMP_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ACMP_IntSet(ACMP_TypeDef *acmp, uint32_t flags) +{ + acmp->IFS = flags; +} + +/** @} (end addtogroup ACMP) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(ACMP_COUNT) && (ACMP_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_ACMP_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_adc.h b/cpu/efm32_common/emlib/inc/em_adc.h new file mode 100644 index 0000000000000..42d193dc5baa9 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_adc.h @@ -0,0 +1,1304 @@ +/***************************************************************************//** + * @file em_adc.h + * @brief Analog to Digital Converter (ADC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_ADC_H__ +#define __SILICON_LABS_EM_ADC_H__ + +#include "em_device.h" +#if defined( ADC_COUNT ) && ( ADC_COUNT > 0 ) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup ADC + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Acquisition time (in ADC clock cycles). */ +typedef enum +{ + adcAcqTime1 = _ADC_SINGLECTRL_AT_1CYCLE, /**< 1 clock cycle. */ + adcAcqTime2 = _ADC_SINGLECTRL_AT_2CYCLES, /**< 2 clock cycles. */ + adcAcqTime4 = _ADC_SINGLECTRL_AT_4CYCLES, /**< 4 clock cycles. */ + adcAcqTime8 = _ADC_SINGLECTRL_AT_8CYCLES, /**< 8 clock cycles. */ + adcAcqTime16 = _ADC_SINGLECTRL_AT_16CYCLES, /**< 16 clock cycles. */ + adcAcqTime32 = _ADC_SINGLECTRL_AT_32CYCLES, /**< 32 clock cycles. */ + adcAcqTime64 = _ADC_SINGLECTRL_AT_64CYCLES, /**< 64 clock cycles. */ + adcAcqTime128 = _ADC_SINGLECTRL_AT_128CYCLES, /**< 128 clock cycles. */ + adcAcqTime256 = _ADC_SINGLECTRL_AT_256CYCLES /**< 256 clock cycles. */ +} ADC_AcqTime_TypeDef; + +#if defined( _ADC_CTRL_LPFMODE_MASK ) +/** Lowpass filter mode. */ +typedef enum +{ + /** No filter or decoupling capacitor. */ + adcLPFilterBypass = _ADC_CTRL_LPFMODE_BYPASS, + + /** On-chip RC filter. */ + adcLPFilterRC = _ADC_CTRL_LPFMODE_RCFILT, + + /** On-chip decoupling capacitor. */ + adcLPFilterDeCap = _ADC_CTRL_LPFMODE_DECAP +} ADC_LPFilter_TypeDef; +#endif + +/** Oversample rate select. */ +typedef enum +{ + /** 2 samples per conversion result. */ + adcOvsRateSel2 = _ADC_CTRL_OVSRSEL_X2, + + /** 4 samples per conversion result. */ + adcOvsRateSel4 = _ADC_CTRL_OVSRSEL_X4, + + /** 8 samples per conversion result. */ + adcOvsRateSel8 = _ADC_CTRL_OVSRSEL_X8, + + /** 16 samples per conversion result. */ + adcOvsRateSel16 = _ADC_CTRL_OVSRSEL_X16, + + /** 32 samples per conversion result. */ + adcOvsRateSel32 = _ADC_CTRL_OVSRSEL_X32, + + /** 64 samples per conversion result. */ + adcOvsRateSel64 = _ADC_CTRL_OVSRSEL_X64, + + /** 128 samples per conversion result. */ + adcOvsRateSel128 = _ADC_CTRL_OVSRSEL_X128, + + /** 256 samples per conversion result. */ + adcOvsRateSel256 = _ADC_CTRL_OVSRSEL_X256, + + /** 512 samples per conversion result. */ + adcOvsRateSel512 = _ADC_CTRL_OVSRSEL_X512, + + /** 1024 samples per conversion result. */ + adcOvsRateSel1024 = _ADC_CTRL_OVSRSEL_X1024, + + /** 2048 samples per conversion result. */ + adcOvsRateSel2048 = _ADC_CTRL_OVSRSEL_X2048, + + /** 4096 samples per conversion result. */ + adcOvsRateSel4096 = _ADC_CTRL_OVSRSEL_X4096 +} ADC_OvsRateSel_TypeDef; + + +/** Peripheral Reflex System signal used to trigger single sample. */ +typedef enum +{ +#if defined( _ADC_SINGLECTRL_PRSSEL_MASK ) + adcPRSSELCh0 = _ADC_SINGLECTRL_PRSSEL_PRSCH0, /**< PRS channel 0. */ + adcPRSSELCh1 = _ADC_SINGLECTRL_PRSSEL_PRSCH1, /**< PRS channel 1. */ + adcPRSSELCh2 = _ADC_SINGLECTRL_PRSSEL_PRSCH2, /**< PRS channel 2. */ + adcPRSSELCh3 = _ADC_SINGLECTRL_PRSSEL_PRSCH3, /**< PRS channel 3. */ +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH4 ) + adcPRSSELCh4 = _ADC_SINGLECTRL_PRSSEL_PRSCH4, /**< PRS channel 4. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH5 ) + adcPRSSELCh5 = _ADC_SINGLECTRL_PRSSEL_PRSCH5, /**< PRS channel 5. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH6 ) + adcPRSSELCh6 = _ADC_SINGLECTRL_PRSSEL_PRSCH6, /**< PRS channel 6. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH7 ) + adcPRSSELCh7 = _ADC_SINGLECTRL_PRSSEL_PRSCH7, /**< PRS channel 7. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH8 ) + adcPRSSELCh8 = _ADC_SINGLECTRL_PRSSEL_PRSCH8, /**< PRS channel 8. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH9 ) + adcPRSSELCh9 = _ADC_SINGLECTRL_PRSSEL_PRSCH9, /**< PRS channel 9. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH10 ) + adcPRSSELCh10 = _ADC_SINGLECTRL_PRSSEL_PRSCH10, /**< PRS channel 10. */ +#endif +#if defined( _ADC_SINGLECTRL_PRSSEL_PRSCH11 ) + adcPRSSELCh11 = _ADC_SINGLECTRL_PRSSEL_PRSCH11, /**< PRS channel 11. */ +#endif +#elif defined(_ADC_SINGLECTRLX_PRSSEL_MASK) + adcPRSSELCh0 = _ADC_SINGLECTRLX_PRSSEL_PRSCH0, /**< PRS channel 0. */ + adcPRSSELCh1 = _ADC_SINGLECTRLX_PRSSEL_PRSCH1, /**< PRS channel 1. */ + adcPRSSELCh2 = _ADC_SINGLECTRLX_PRSSEL_PRSCH2, /**< PRS channel 2. */ + adcPRSSELCh3 = _ADC_SINGLECTRLX_PRSSEL_PRSCH3, /**< PRS channel 3. */ + adcPRSSELCh4 = _ADC_SINGLECTRLX_PRSSEL_PRSCH4, /**< PRS channel 4. */ + adcPRSSELCh5 = _ADC_SINGLECTRLX_PRSSEL_PRSCH5, /**< PRS channel 5. */ + adcPRSSELCh6 = _ADC_SINGLECTRLX_PRSSEL_PRSCH6, /**< PRS channel 6. */ + adcPRSSELCh7 = _ADC_SINGLECTRLX_PRSSEL_PRSCH7, /**< PRS channel 7. */ + adcPRSSELCh8 = _ADC_SINGLECTRLX_PRSSEL_PRSCH8, /**< PRS channel 8. */ + adcPRSSELCh9 = _ADC_SINGLECTRLX_PRSSEL_PRSCH9, /**< PRS channel 9. */ + adcPRSSELCh10 = _ADC_SINGLECTRLX_PRSSEL_PRSCH10, /**< PRS channel 10. */ + adcPRSSELCh11 = _ADC_SINGLECTRLX_PRSSEL_PRSCH11, /**< PRS channel 11. */ +#if defined( _ADC_SINGLECTRLX_PRSSEL_PRSCH12 ) + adcPRSSELCh12 = _ADC_SINGLECTRLX_PRSSEL_PRSCH12, /**< PRS channel 12. */ + adcPRSSELCh13 = _ADC_SINGLECTRLX_PRSSEL_PRSCH13, /**< PRS channel 13. */ + adcPRSSELCh14 = _ADC_SINGLECTRLX_PRSSEL_PRSCH14, /**< PRS channel 14. */ + adcPRSSELCh15 = _ADC_SINGLECTRLX_PRSSEL_PRSCH15, /**< PRS channel 15. */ +#endif +#endif +} ADC_PRSSEL_TypeDef; + + +/** Single and scan mode voltage references. Using unshifted enums and or + in ADC_CTRLX_VREFSEL_REG to select the extension register CTRLX_VREFSEL. */ +#if defined( _ADC_SCANCTRLX_VREFSEL_MASK ) +#define ADC_CTRLX_VREFSEL_REG 0x80 +#endif +typedef enum +{ + /** Internal 1.25V reference. */ + adcRef1V25 = _ADC_SINGLECTRL_REF_1V25, + + /** Internal 2.5V reference. */ + adcRef2V5 = _ADC_SINGLECTRL_REF_2V5, + + /** Buffered VDD. */ + adcRefVDD = _ADC_SINGLECTRL_REF_VDD, + + /** Internal differential 5V reference. */ + adcRef5VDIFF = _ADC_SINGLECTRL_REF_5VDIFF, + + /** Single ended external reference from pin 6. */ + adcRefExtSingle = _ADC_SINGLECTRL_REF_EXTSINGLE, + + /** Differential external reference from pin 6 and 7. */ + adcRef2xExtDiff = _ADC_SINGLECTRL_REF_2XEXTDIFF, + + /** Unbuffered 2xVDD. */ + adcRef2xVDD = _ADC_SINGLECTRL_REF_2XVDD, + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VBGR ) + /** Custom VFS: Internal Bandgap reference */ + adcRefVBGR = _ADC_SINGLECTRLX_VREFSEL_VBGR | ADC_CTRLX_VREFSEL_REG, +#endif + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VDDXWATT ) + /** Custom VFS: Scaled AVDD: AVDD * VREFATT */ + adcRefVddxAtt = _ADC_SINGLECTRLX_VREFSEL_VDDXWATT | ADC_CTRLX_VREFSEL_REG, +#endif + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VREFPWATT ) + /** Custom VFS: Scaled singled ended external reference from pin 6: + VREFP * VREFATT */ + adcRefVPxAtt = _ADC_SINGLECTRLX_VREFSEL_VREFPWATT | ADC_CTRLX_VREFSEL_REG, +#endif + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VREFP ) + /** Custom VFS: Raw single ended external reference from pin 6. */ + adcRefP = _ADC_SINGLECTRLX_VREFSEL_VREFP | ADC_CTRLX_VREFSEL_REG, +#endif + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VENTROPY ) + /** Custom VFS: Special mode for entropy generation */ + adcRefVEntropy = _ADC_SINGLECTRLX_VREFSEL_VENTROPY | ADC_CTRLX_VREFSEL_REG, +#endif + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VREFPNWATT ) + /** Custom VFS: Scaled differential external Vref from pin 6 and 7: + (VREFP - VREFN) * VREFATT */ + adcRefVPNxAtt = _ADC_SINGLECTRLX_VREFSEL_VREFPNWATT | ADC_CTRLX_VREFSEL_REG, +#endif + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VREFPN ) + /** Custom VFS: Raw differential external Vref from pin 6 and 7: + VREFP - VREFN */ + adcRefPN = _ADC_SINGLECTRLX_VREFSEL_VREFPN | ADC_CTRLX_VREFSEL_REG, +#endif +} ADC_Ref_TypeDef; + + +/** Sample resolution. */ +typedef enum +{ + adcRes12Bit = _ADC_SINGLECTRL_RES_12BIT, /**< 12 bit sampling. */ + adcRes8Bit = _ADC_SINGLECTRL_RES_8BIT, /**< 8 bit sampling. */ + adcRes6Bit = _ADC_SINGLECTRL_RES_6BIT, /**< 6 bit sampling. */ + adcResOVS = _ADC_SINGLECTRL_RES_OVS /**< Oversampling. */ +} ADC_Res_TypeDef; + + +#if defined( _ADC_SINGLECTRL_INPUTSEL_MASK ) +/** Single sample input selection. */ +typedef enum +{ + /* Differential mode disabled */ + adcSingleInputCh0 = _ADC_SINGLECTRL_INPUTSEL_CH0, /**< Channel 0. */ + adcSingleInputCh1 = _ADC_SINGLECTRL_INPUTSEL_CH1, /**< Channel 1. */ + adcSingleInputCh2 = _ADC_SINGLECTRL_INPUTSEL_CH2, /**< Channel 2. */ + adcSingleInputCh3 = _ADC_SINGLECTRL_INPUTSEL_CH3, /**< Channel 3. */ + adcSingleInputCh4 = _ADC_SINGLECTRL_INPUTSEL_CH4, /**< Channel 4. */ + adcSingleInputCh5 = _ADC_SINGLECTRL_INPUTSEL_CH5, /**< Channel 5. */ + adcSingleInputCh6 = _ADC_SINGLECTRL_INPUTSEL_CH6, /**< Channel 6. */ + adcSingleInputCh7 = _ADC_SINGLECTRL_INPUTSEL_CH7, /**< Channel 7. */ + adcSingleInputTemp = _ADC_SINGLECTRL_INPUTSEL_TEMP, /**< Temperature reference. */ + adcSingleInputVDDDiv3 = _ADC_SINGLECTRL_INPUTSEL_VDDDIV3, /**< VDD divided by 3. */ + adcSingleInputVDD = _ADC_SINGLECTRL_INPUTSEL_VDD, /**< VDD. */ + adcSingleInputVSS = _ADC_SINGLECTRL_INPUTSEL_VSS, /**< VSS. */ + adcSingleInputVrefDiv2 = _ADC_SINGLECTRL_INPUTSEL_VREFDIV2, /**< Vref divided by 2. */ + adcSingleInputDACOut0 = _ADC_SINGLECTRL_INPUTSEL_DAC0OUT0, /**< DAC output 0. */ + adcSingleInputDACOut1 = _ADC_SINGLECTRL_INPUTSEL_DAC0OUT1, /**< DAC output 1. */ + /* TBD: Use define when available */ + adcSingleInputATEST = 15, /**< ATEST. */ + + /* Differential mode enabled */ + adcSingleInputCh0Ch1 = _ADC_SINGLECTRL_INPUTSEL_CH0CH1, /**< Positive Ch0, negative Ch1. */ + adcSingleInputCh2Ch3 = _ADC_SINGLECTRL_INPUTSEL_CH2CH3, /**< Positive Ch2, negative Ch3. */ + adcSingleInputCh4Ch5 = _ADC_SINGLECTRL_INPUTSEL_CH4CH5, /**< Positive Ch4, negative Ch5. */ + adcSingleInputCh6Ch7 = _ADC_SINGLECTRL_INPUTSEL_CH6CH7, /**< Positive Ch6, negative Ch7. */ + /* TBD: Use define when available */ + adcSingleInputDiff0 = 4 /**< Differential 0. */ +} ADC_SingleInput_TypeDef; + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/* Legacy enum names */ +#define adcSingleInpCh0 adcSingleInputCh0 +#define adcSingleInpCh1 adcSingleInputCh1 +#define adcSingleInpCh2 adcSingleInputCh2 +#define adcSingleInpCh3 adcSingleInputCh3 +#define adcSingleInpCh4 adcSingleInputCh4 +#define adcSingleInpCh5 adcSingleInputCh5 +#define adcSingleInpCh6 adcSingleInputCh6 +#define adcSingleInpCh7 adcSingleInputCh7 +#define adcSingleInpTemp adcSingleInputTemp +#define adcSingleInpVDDDiv3 adcSingleInputVDDDiv3 +#define adcSingleInpVDD adcSingleInputVDD +#define adcSingleInpVSS adcSingleInputVSS +#define adcSingleInpVrefDiv2 adcSingleInputVrefDiv2 +#define adcSingleInpDACOut0 adcSingleInputDACOut0 +#define adcSingleInpDACOut1 adcSingleInputDACOut1 +#define adcSingleInpATEST adcSingleInputATEST +#define adcSingleInpCh0Ch1 adcSingleInputCh0Ch1 +#define adcSingleInpCh2Ch3 adcSingleInputCh2Ch3 +#define adcSingleInpCh4Ch5 adcSingleInputCh4Ch5 +#define adcSingleInpCh6Ch7 adcSingleInputCh6Ch7 +#define adcSingleInpDiff0 adcSingleInputDiff0 +/** @endcond */ +#endif + +#if defined( _ADC_SINGLECTRL_POSSEL_MASK ) +/** Positive input selection for single and scan coversion. */ +typedef enum +{ + adcPosSelAPORT0XCH0 = _ADC_SINGLECTRL_POSSEL_APORT0XCH0, + adcPosSelAPORT0XCH1 = _ADC_SINGLECTRL_POSSEL_APORT0XCH1, + adcPosSelAPORT0XCH2 = _ADC_SINGLECTRL_POSSEL_APORT0XCH2, + adcPosSelAPORT0XCH3 = _ADC_SINGLECTRL_POSSEL_APORT0XCH3, + adcPosSelAPORT0XCH4 = _ADC_SINGLECTRL_POSSEL_APORT0XCH4, + adcPosSelAPORT0XCH5 = _ADC_SINGLECTRL_POSSEL_APORT0XCH5, + adcPosSelAPORT0XCH6 = _ADC_SINGLECTRL_POSSEL_APORT0XCH6, + adcPosSelAPORT0XCH7 = _ADC_SINGLECTRL_POSSEL_APORT0XCH7, + adcPosSelAPORT0XCH8 = _ADC_SINGLECTRL_POSSEL_APORT0XCH8, + adcPosSelAPORT0XCH9 = _ADC_SINGLECTRL_POSSEL_APORT0XCH9, + adcPosSelAPORT0XCH10 = _ADC_SINGLECTRL_POSSEL_APORT0XCH10, + adcPosSelAPORT0XCH11 = _ADC_SINGLECTRL_POSSEL_APORT0XCH11, + adcPosSelAPORT0XCH12 = _ADC_SINGLECTRL_POSSEL_APORT0XCH12, + adcPosSelAPORT0XCH13 = _ADC_SINGLECTRL_POSSEL_APORT0XCH13, + adcPosSelAPORT0XCH14 = _ADC_SINGLECTRL_POSSEL_APORT0XCH14, + adcPosSelAPORT0XCH15 = _ADC_SINGLECTRL_POSSEL_APORT0XCH15, + adcPosSelAPORT0YCH0 = _ADC_SINGLECTRL_POSSEL_APORT0YCH0, + adcPosSelAPORT0YCH1 = _ADC_SINGLECTRL_POSSEL_APORT0YCH1, + adcPosSelAPORT0YCH2 = _ADC_SINGLECTRL_POSSEL_APORT0YCH2, + adcPosSelAPORT0YCH3 = _ADC_SINGLECTRL_POSSEL_APORT0YCH3, + adcPosSelAPORT0YCH4 = _ADC_SINGLECTRL_POSSEL_APORT0YCH4, + adcPosSelAPORT0YCH5 = _ADC_SINGLECTRL_POSSEL_APORT0YCH5, + adcPosSelAPORT0YCH6 = _ADC_SINGLECTRL_POSSEL_APORT0YCH6, + adcPosSelAPORT0YCH7 = _ADC_SINGLECTRL_POSSEL_APORT0YCH7, + adcPosSelAPORT0YCH8 = _ADC_SINGLECTRL_POSSEL_APORT0YCH8, + adcPosSelAPORT0YCH9 = _ADC_SINGLECTRL_POSSEL_APORT0YCH9, + adcPosSelAPORT0YCH10 = _ADC_SINGLECTRL_POSSEL_APORT0YCH10, + adcPosSelAPORT0YCH11 = _ADC_SINGLECTRL_POSSEL_APORT0YCH11, + adcPosSelAPORT0YCH12 = _ADC_SINGLECTRL_POSSEL_APORT0YCH12, + adcPosSelAPORT0YCH13 = _ADC_SINGLECTRL_POSSEL_APORT0YCH13, + adcPosSelAPORT0YCH14 = _ADC_SINGLECTRL_POSSEL_APORT0YCH14, + adcPosSelAPORT0YCH15 = _ADC_SINGLECTRL_POSSEL_APORT0YCH15, + adcPosSelAPORT1XCH0 = _ADC_SINGLECTRL_POSSEL_APORT1XCH0, + adcPosSelAPORT1YCH1 = _ADC_SINGLECTRL_POSSEL_APORT1YCH1, + adcPosSelAPORT1XCH2 = _ADC_SINGLECTRL_POSSEL_APORT1XCH2, + adcPosSelAPORT1YCH3 = _ADC_SINGLECTRL_POSSEL_APORT1YCH3, + adcPosSelAPORT1XCH4 = _ADC_SINGLECTRL_POSSEL_APORT1XCH4, + adcPosSelAPORT1YCH5 = _ADC_SINGLECTRL_POSSEL_APORT1YCH5, + adcPosSelAPORT1XCH6 = _ADC_SINGLECTRL_POSSEL_APORT1XCH6, + adcPosSelAPORT1YCH7 = _ADC_SINGLECTRL_POSSEL_APORT1YCH7, + adcPosSelAPORT1XCH8 = _ADC_SINGLECTRL_POSSEL_APORT1XCH8, + adcPosSelAPORT1YCH9 = _ADC_SINGLECTRL_POSSEL_APORT1YCH9, + adcPosSelAPORT1XCH10 = _ADC_SINGLECTRL_POSSEL_APORT1XCH10, + adcPosSelAPORT1YCH11 = _ADC_SINGLECTRL_POSSEL_APORT1YCH11, + adcPosSelAPORT1XCH12 = _ADC_SINGLECTRL_POSSEL_APORT1XCH12, + adcPosSelAPORT1YCH13 = _ADC_SINGLECTRL_POSSEL_APORT1YCH13, + adcPosSelAPORT1XCH14 = _ADC_SINGLECTRL_POSSEL_APORT1XCH14, + adcPosSelAPORT1YCH15 = _ADC_SINGLECTRL_POSSEL_APORT1YCH15, + adcPosSelAPORT1XCH16 = _ADC_SINGLECTRL_POSSEL_APORT1XCH16, + adcPosSelAPORT1YCH17 = _ADC_SINGLECTRL_POSSEL_APORT1YCH17, + adcPosSelAPORT1XCH18 = _ADC_SINGLECTRL_POSSEL_APORT1XCH18, + adcPosSelAPORT1YCH19 = _ADC_SINGLECTRL_POSSEL_APORT1YCH19, + adcPosSelAPORT1XCH20 = _ADC_SINGLECTRL_POSSEL_APORT1XCH20, + adcPosSelAPORT1YCH21 = _ADC_SINGLECTRL_POSSEL_APORT1YCH21, + adcPosSelAPORT1XCH22 = _ADC_SINGLECTRL_POSSEL_APORT1XCH22, + adcPosSelAPORT1YCH23 = _ADC_SINGLECTRL_POSSEL_APORT1YCH23, + adcPosSelAPORT1XCH24 = _ADC_SINGLECTRL_POSSEL_APORT1XCH24, + adcPosSelAPORT1YCH25 = _ADC_SINGLECTRL_POSSEL_APORT1YCH25, + adcPosSelAPORT1XCH26 = _ADC_SINGLECTRL_POSSEL_APORT1XCH26, + adcPosSelAPORT1YCH27 = _ADC_SINGLECTRL_POSSEL_APORT1YCH27, + adcPosSelAPORT1XCH28 = _ADC_SINGLECTRL_POSSEL_APORT1XCH28, + adcPosSelAPORT1YCH29 = _ADC_SINGLECTRL_POSSEL_APORT1YCH29, + adcPosSelAPORT1XCH30 = _ADC_SINGLECTRL_POSSEL_APORT1XCH30, + adcPosSelAPORT1YCH31 = _ADC_SINGLECTRL_POSSEL_APORT1YCH31, + adcPosSelAPORT2YCH0 = _ADC_SINGLECTRL_POSSEL_APORT2YCH0, + adcPosSelAPORT2XCH1 = _ADC_SINGLECTRL_POSSEL_APORT2XCH1, + adcPosSelAPORT2YCH2 = _ADC_SINGLECTRL_POSSEL_APORT2YCH2, + adcPosSelAPORT2XCH3 = _ADC_SINGLECTRL_POSSEL_APORT2XCH3, + adcPosSelAPORT2YCH4 = _ADC_SINGLECTRL_POSSEL_APORT2YCH4, + adcPosSelAPORT2XCH5 = _ADC_SINGLECTRL_POSSEL_APORT2XCH5, + adcPosSelAPORT2YCH6 = _ADC_SINGLECTRL_POSSEL_APORT2YCH6, + adcPosSelAPORT2XCH7 = _ADC_SINGLECTRL_POSSEL_APORT2XCH7, + adcPosSelAPORT2YCH8 = _ADC_SINGLECTRL_POSSEL_APORT2YCH8, + adcPosSelAPORT2XCH9 = _ADC_SINGLECTRL_POSSEL_APORT2XCH9, + adcPosSelAPORT2YCH10 = _ADC_SINGLECTRL_POSSEL_APORT2YCH10, + adcPosSelAPORT2XCH11 = _ADC_SINGLECTRL_POSSEL_APORT2XCH11, + adcPosSelAPORT2YCH12 = _ADC_SINGLECTRL_POSSEL_APORT2YCH12, + adcPosSelAPORT2XCH13 = _ADC_SINGLECTRL_POSSEL_APORT2XCH13, + adcPosSelAPORT2YCH14 = _ADC_SINGLECTRL_POSSEL_APORT2YCH14, + adcPosSelAPORT2XCH15 = _ADC_SINGLECTRL_POSSEL_APORT2XCH15, + adcPosSelAPORT2YCH16 = _ADC_SINGLECTRL_POSSEL_APORT2YCH16, + adcPosSelAPORT2XCH17 = _ADC_SINGLECTRL_POSSEL_APORT2XCH17, + adcPosSelAPORT2YCH18 = _ADC_SINGLECTRL_POSSEL_APORT2YCH18, + adcPosSelAPORT2XCH19 = _ADC_SINGLECTRL_POSSEL_APORT2XCH19, + adcPosSelAPORT2YCH20 = _ADC_SINGLECTRL_POSSEL_APORT2YCH20, + adcPosSelAPORT2XCH21 = _ADC_SINGLECTRL_POSSEL_APORT2XCH21, + adcPosSelAPORT2YCH22 = _ADC_SINGLECTRL_POSSEL_APORT2YCH22, + adcPosSelAPORT2XCH23 = _ADC_SINGLECTRL_POSSEL_APORT2XCH23, + adcPosSelAPORT2YCH24 = _ADC_SINGLECTRL_POSSEL_APORT2YCH24, + adcPosSelAPORT2XCH25 = _ADC_SINGLECTRL_POSSEL_APORT2XCH25, + adcPosSelAPORT2YCH26 = _ADC_SINGLECTRL_POSSEL_APORT2YCH26, + adcPosSelAPORT2XCH27 = _ADC_SINGLECTRL_POSSEL_APORT2XCH27, + adcPosSelAPORT2YCH28 = _ADC_SINGLECTRL_POSSEL_APORT2YCH28, + adcPosSelAPORT2XCH29 = _ADC_SINGLECTRL_POSSEL_APORT2XCH29, + adcPosSelAPORT2YCH30 = _ADC_SINGLECTRL_POSSEL_APORT2YCH30, + adcPosSelAPORT2XCH31 = _ADC_SINGLECTRL_POSSEL_APORT2XCH31, + adcPosSelAPORT3XCH0 = _ADC_SINGLECTRL_POSSEL_APORT3XCH0, + adcPosSelAPORT3YCH1 = _ADC_SINGLECTRL_POSSEL_APORT3YCH1, + adcPosSelAPORT3XCH2 = _ADC_SINGLECTRL_POSSEL_APORT3XCH2, + adcPosSelAPORT3YCH3 = _ADC_SINGLECTRL_POSSEL_APORT3YCH3, + adcPosSelAPORT3XCH4 = _ADC_SINGLECTRL_POSSEL_APORT3XCH4, + adcPosSelAPORT3YCH5 = _ADC_SINGLECTRL_POSSEL_APORT3YCH5, + adcPosSelAPORT3XCH6 = _ADC_SINGLECTRL_POSSEL_APORT3XCH6, + adcPosSelAPORT3YCH7 = _ADC_SINGLECTRL_POSSEL_APORT3YCH7, + adcPosSelAPORT3XCH8 = _ADC_SINGLECTRL_POSSEL_APORT3XCH8, + adcPosSelAPORT3YCH9 = _ADC_SINGLECTRL_POSSEL_APORT3YCH9, + adcPosSelAPORT3XCH10 = _ADC_SINGLECTRL_POSSEL_APORT3XCH10, + adcPosSelAPORT3YCH11 = _ADC_SINGLECTRL_POSSEL_APORT3YCH11, + adcPosSelAPORT3XCH12 = _ADC_SINGLECTRL_POSSEL_APORT3XCH12, + adcPosSelAPORT3YCH13 = _ADC_SINGLECTRL_POSSEL_APORT3YCH13, + adcPosSelAPORT3XCH14 = _ADC_SINGLECTRL_POSSEL_APORT3XCH14, + adcPosSelAPORT3YCH15 = _ADC_SINGLECTRL_POSSEL_APORT3YCH15, + adcPosSelAPORT3XCH16 = _ADC_SINGLECTRL_POSSEL_APORT3XCH16, + adcPosSelAPORT3YCH17 = _ADC_SINGLECTRL_POSSEL_APORT3YCH17, + adcPosSelAPORT3XCH18 = _ADC_SINGLECTRL_POSSEL_APORT3XCH18, + adcPosSelAPORT3YCH19 = _ADC_SINGLECTRL_POSSEL_APORT3YCH19, + adcPosSelAPORT3XCH20 = _ADC_SINGLECTRL_POSSEL_APORT3XCH20, + adcPosSelAPORT3YCH21 = _ADC_SINGLECTRL_POSSEL_APORT3YCH21, + adcPosSelAPORT3XCH22 = _ADC_SINGLECTRL_POSSEL_APORT3XCH22, + adcPosSelAPORT3YCH23 = _ADC_SINGLECTRL_POSSEL_APORT3YCH23, + adcPosSelAPORT3XCH24 = _ADC_SINGLECTRL_POSSEL_APORT3XCH24, + adcPosSelAPORT3YCH25 = _ADC_SINGLECTRL_POSSEL_APORT3YCH25, + adcPosSelAPORT3XCH26 = _ADC_SINGLECTRL_POSSEL_APORT3XCH26, + adcPosSelAPORT3YCH27 = _ADC_SINGLECTRL_POSSEL_APORT3YCH27, + adcPosSelAPORT3XCH28 = _ADC_SINGLECTRL_POSSEL_APORT3XCH28, + adcPosSelAPORT3YCH29 = _ADC_SINGLECTRL_POSSEL_APORT3YCH29, + adcPosSelAPORT3XCH30 = _ADC_SINGLECTRL_POSSEL_APORT3XCH30, + adcPosSelAPORT3YCH31 = _ADC_SINGLECTRL_POSSEL_APORT3YCH31, + adcPosSelAPORT4YCH0 = _ADC_SINGLECTRL_POSSEL_APORT4YCH0, + adcPosSelAPORT4XCH1 = _ADC_SINGLECTRL_POSSEL_APORT4XCH1, + adcPosSelAPORT4YCH2 = _ADC_SINGLECTRL_POSSEL_APORT4YCH2, + adcPosSelAPORT4XCH3 = _ADC_SINGLECTRL_POSSEL_APORT4XCH3, + adcPosSelAPORT4YCH4 = _ADC_SINGLECTRL_POSSEL_APORT4YCH4, + adcPosSelAPORT4XCH5 = _ADC_SINGLECTRL_POSSEL_APORT4XCH5, + adcPosSelAPORT4YCH6 = _ADC_SINGLECTRL_POSSEL_APORT4YCH6, + adcPosSelAPORT4XCH7 = _ADC_SINGLECTRL_POSSEL_APORT4XCH7, + adcPosSelAPORT4YCH8 = _ADC_SINGLECTRL_POSSEL_APORT4YCH8, + adcPosSelAPORT4XCH9 = _ADC_SINGLECTRL_POSSEL_APORT4XCH9, + adcPosSelAPORT4YCH10 = _ADC_SINGLECTRL_POSSEL_APORT4YCH10, + adcPosSelAPORT4XCH11 = _ADC_SINGLECTRL_POSSEL_APORT4XCH11, + adcPosSelAPORT4YCH12 = _ADC_SINGLECTRL_POSSEL_APORT4YCH12, + adcPosSelAPORT4XCH13 = _ADC_SINGLECTRL_POSSEL_APORT4XCH13, + adcPosSelAPORT4YCH14 = _ADC_SINGLECTRL_POSSEL_APORT4YCH14, + adcPosSelAPORT4XCH15 = _ADC_SINGLECTRL_POSSEL_APORT4XCH15, + adcPosSelAPORT4YCH16 = _ADC_SINGLECTRL_POSSEL_APORT4YCH16, + adcPosSelAPORT4XCH17 = _ADC_SINGLECTRL_POSSEL_APORT4XCH17, + adcPosSelAPORT4YCH18 = _ADC_SINGLECTRL_POSSEL_APORT4YCH18, + adcPosSelAPORT4XCH19 = _ADC_SINGLECTRL_POSSEL_APORT4XCH19, + adcPosSelAPORT4YCH20 = _ADC_SINGLECTRL_POSSEL_APORT4YCH20, + adcPosSelAPORT4XCH21 = _ADC_SINGLECTRL_POSSEL_APORT4XCH21, + adcPosSelAPORT4YCH22 = _ADC_SINGLECTRL_POSSEL_APORT4YCH22, + adcPosSelAPORT4XCH23 = _ADC_SINGLECTRL_POSSEL_APORT4XCH23, + adcPosSelAPORT4YCH24 = _ADC_SINGLECTRL_POSSEL_APORT4YCH24, + adcPosSelAPORT4XCH25 = _ADC_SINGLECTRL_POSSEL_APORT4XCH25, + adcPosSelAPORT4YCH26 = _ADC_SINGLECTRL_POSSEL_APORT4YCH26, + adcPosSelAPORT4XCH27 = _ADC_SINGLECTRL_POSSEL_APORT4XCH27, + adcPosSelAPORT4YCH28 = _ADC_SINGLECTRL_POSSEL_APORT4YCH28, + adcPosSelAPORT4XCH29 = _ADC_SINGLECTRL_POSSEL_APORT4XCH29, + adcPosSelAPORT4YCH30 = _ADC_SINGLECTRL_POSSEL_APORT4YCH30, + adcPosSelAPORT4XCH31 = _ADC_SINGLECTRL_POSSEL_APORT4XCH31, + adcPosSelAVDD = _ADC_SINGLECTRL_POSSEL_AVDD, + adcPosSelBU = _ADC_SINGLECTRL_POSSEL_BU, + adcPosSelAREG = _ADC_SINGLECTRL_POSSEL_AREG, + adcPosSelVREGOUTPA = _ADC_SINGLECTRL_POSSEL_VREGOUTPA, + adcPosSelPDBU = _ADC_SINGLECTRL_POSSEL_PDBU, + adcPosSelIO0 = _ADC_SINGLECTRL_POSSEL_IO0, + adcPosSelIO1 = _ADC_SINGLECTRL_POSSEL_IO1, + adcPosSelVSP = _ADC_SINGLECTRL_POSSEL_VSP, + adcPosSelSP0 = _ADC_SINGLECTRL_POSSEL_SP0, + adcPosSelTEMP = _ADC_SINGLECTRL_POSSEL_TEMP, + adcPosSelDAC0OUT0 = _ADC_SINGLECTRL_POSSEL_DAC0OUT0, + adcPosSelTESTP = _ADC_SINGLECTRL_POSSEL_TESTP, + adcPosSelSP1 = _ADC_SINGLECTRL_POSSEL_SP1, + adcPosSelSP2 = _ADC_SINGLECTRL_POSSEL_SP2, + adcPosSelDAC0OUT1 = _ADC_SINGLECTRL_POSSEL_DAC0OUT1, + adcPosSelSUBLSB = _ADC_SINGLECTRL_POSSEL_SUBLSB, + adcPosSelDEFAULT = _ADC_SINGLECTRL_POSSEL_DEFAULT, + adcPosSelVSS = _ADC_SINGLECTRL_POSSEL_VSS +} ADC_PosSel_TypeDef; +#endif + + +#if defined( _ADC_SINGLECTRL_NEGSEL_MASK ) +/** Negative input selection for single and scan coversion. */ +typedef enum +{ + adcNegSelAPORT0XCH0 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH0, + adcNegSelAPORT0XCH1 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH1, + adcNegSelAPORT0XCH2 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH2, + adcNegSelAPORT0XCH3 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH3, + adcNegSelAPORT0XCH4 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH4, + adcNegSelAPORT0XCH5 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH5, + adcNegSelAPORT0XCH6 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH6, + adcNegSelAPORT0XCH7 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH7, + adcNegSelAPORT0XCH8 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH8, + adcNegSelAPORT0XCH9 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH9, + adcNegSelAPORT0XCH10 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH10, + adcNegSelAPORT0XCH11 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH11, + adcNegSelAPORT0XCH12 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH12, + adcNegSelAPORT0XCH13 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH13, + adcNegSelAPORT0XCH14 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH14, + adcNegSelAPORT0XCH15 = _ADC_SINGLECTRL_NEGSEL_APORT0XCH15, + adcNegSelAPORT0YCH0 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH0, + adcNegSelAPORT0YCH1 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH1, + adcNegSelAPORT0YCH2 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH2, + adcNegSelAPORT0YCH3 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH3, + adcNegSelAPORT0YCH4 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH4, + adcNegSelAPORT0YCH5 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH5, + adcNegSelAPORT0YCH6 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH6, + adcNegSelAPORT0YCH7 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH7, + adcNegSelAPORT0YCH8 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH8, + adcNegSelAPORT0YCH9 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH9, + adcNegSelAPORT0YCH10 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH10, + adcNegSelAPORT0YCH11 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH11, + adcNegSelAPORT0YCH12 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH12, + adcNegSelAPORT0YCH13 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH13, + adcNegSelAPORT0YCH14 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH14, + adcNegSelAPORT0YCH15 = _ADC_SINGLECTRL_NEGSEL_APORT0YCH15, + adcNegSelAPORT1XCH0 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH0, + adcNegSelAPORT1YCH1 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH1, + adcNegSelAPORT1XCH2 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH2, + adcNegSelAPORT1YCH3 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH3, + adcNegSelAPORT1XCH4 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH4, + adcNegSelAPORT1YCH5 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH5, + adcNegSelAPORT1XCH6 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH6, + adcNegSelAPORT1YCH7 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH7, + adcNegSelAPORT1XCH8 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH8, + adcNegSelAPORT1YCH9 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH9, + adcNegSelAPORT1XCH10 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH10, + adcNegSelAPORT1YCH11 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH11, + adcNegSelAPORT1XCH12 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH12, + adcNegSelAPORT1YCH13 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH13, + adcNegSelAPORT1XCH14 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH14, + adcNegSelAPORT1YCH15 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH15, + adcNegSelAPORT1XCH16 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH16, + adcNegSelAPORT1YCH17 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH17, + adcNegSelAPORT1XCH18 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH18, + adcNegSelAPORT1YCH19 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH19, + adcNegSelAPORT1XCH20 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH20, + adcNegSelAPORT1YCH21 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH21, + adcNegSelAPORT1XCH22 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH22, + adcNegSelAPORT1YCH23 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH23, + adcNegSelAPORT1XCH24 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH24, + adcNegSelAPORT1YCH25 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH25, + adcNegSelAPORT1XCH26 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH26, + adcNegSelAPORT1YCH27 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH27, + adcNegSelAPORT1XCH28 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH28, + adcNegSelAPORT1YCH29 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH29, + adcNegSelAPORT1XCH30 = _ADC_SINGLECTRL_NEGSEL_APORT1XCH30, + adcNegSelAPORT1YCH31 = _ADC_SINGLECTRL_NEGSEL_APORT1YCH31, + adcNegSelAPORT2YCH0 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH0, + adcNegSelAPORT2XCH1 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH1, + adcNegSelAPORT2YCH2 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH2, + adcNegSelAPORT2XCH3 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH3, + adcNegSelAPORT2YCH4 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH4, + adcNegSelAPORT2XCH5 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH5, + adcNegSelAPORT2YCH6 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH6, + adcNegSelAPORT2XCH7 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH7, + adcNegSelAPORT2YCH8 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH8, + adcNegSelAPORT2XCH9 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH9, + adcNegSelAPORT2YCH10 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH10, + adcNegSelAPORT2XCH11 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH11, + adcNegSelAPORT2YCH12 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH12, + adcNegSelAPORT2XCH13 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH13, + adcNegSelAPORT2YCH14 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH14, + adcNegSelAPORT2XCH15 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH15, + adcNegSelAPORT2YCH16 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH16, + adcNegSelAPORT2XCH17 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH17, + adcNegSelAPORT2YCH18 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH18, + adcNegSelAPORT2XCH19 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH19, + adcNegSelAPORT2YCH20 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH20, + adcNegSelAPORT2XCH21 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH21, + adcNegSelAPORT2YCH22 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH22, + adcNegSelAPORT2XCH23 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH23, + adcNegSelAPORT2YCH24 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH24, + adcNegSelAPORT2XCH25 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH25, + adcNegSelAPORT2YCH26 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH26, + adcNegSelAPORT2XCH27 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH27, + adcNegSelAPORT2YCH28 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH28, + adcNegSelAPORT2XCH29 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH29, + adcNegSelAPORT2YCH30 = _ADC_SINGLECTRL_NEGSEL_APORT2YCH30, + adcNegSelAPORT2XCH31 = _ADC_SINGLECTRL_NEGSEL_APORT2XCH31, + adcNegSelAPORT3XCH0 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH0, + adcNegSelAPORT3YCH1 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH1, + adcNegSelAPORT3XCH2 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH2, + adcNegSelAPORT3YCH3 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH3, + adcNegSelAPORT3XCH4 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH4, + adcNegSelAPORT3YCH5 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH5, + adcNegSelAPORT3XCH6 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH6, + adcNegSelAPORT3YCH7 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH7, + adcNegSelAPORT3XCH8 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH8, + adcNegSelAPORT3YCH9 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH9, + adcNegSelAPORT3XCH10 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH10, + adcNegSelAPORT3YCH11 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH11, + adcNegSelAPORT3XCH12 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH12, + adcNegSelAPORT3YCH13 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH13, + adcNegSelAPORT3XCH14 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH14, + adcNegSelAPORT3YCH15 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH15, + adcNegSelAPORT3XCH16 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH16, + adcNegSelAPORT3YCH17 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH17, + adcNegSelAPORT3XCH18 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH18, + adcNegSelAPORT3YCH19 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH19, + adcNegSelAPORT3XCH20 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH20, + adcNegSelAPORT3YCH21 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH21, + adcNegSelAPORT3XCH22 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH22, + adcNegSelAPORT3YCH23 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH23, + adcNegSelAPORT3XCH24 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH24, + adcNegSelAPORT3YCH25 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH25, + adcNegSelAPORT3XCH26 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH26, + adcNegSelAPORT3YCH27 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH27, + adcNegSelAPORT3XCH28 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH28, + adcNegSelAPORT3YCH29 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH29, + adcNegSelAPORT3XCH30 = _ADC_SINGLECTRL_NEGSEL_APORT3XCH30, + adcNegSelAPORT3YCH31 = _ADC_SINGLECTRL_NEGSEL_APORT3YCH31, + adcNegSelAPORT4YCH0 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH0, + adcNegSelAPORT4XCH1 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH1, + adcNegSelAPORT4YCH2 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH2, + adcNegSelAPORT4XCH3 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH3, + adcNegSelAPORT4YCH4 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH4, + adcNegSelAPORT4XCH5 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH5, + adcNegSelAPORT4YCH6 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH6, + adcNegSelAPORT4XCH7 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH7, + adcNegSelAPORT4YCH8 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH8, + adcNegSelAPORT4XCH9 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH9, + adcNegSelAPORT4YCH10 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH10, + adcNegSelAPORT4XCH11 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH11, + adcNegSelAPORT4YCH12 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH12, + adcNegSelAPORT4XCH13 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH13, + adcNegSelAPORT4YCH14 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH14, + adcNegSelAPORT4XCH15 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH15, + adcNegSelAPORT4YCH16 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH16, + adcNegSelAPORT4XCH17 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH17, + adcNegSelAPORT4YCH18 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH18, + adcNegSelAPORT4XCH19 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH19, + adcNegSelAPORT4YCH20 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH20, + adcNegSelAPORT4XCH21 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH21, + adcNegSelAPORT4YCH22 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH22, + adcNegSelAPORT4XCH23 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH23, + adcNegSelAPORT4YCH24 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH24, + adcNegSelAPORT4XCH25 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH25, + adcNegSelAPORT4YCH26 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH26, + adcNegSelAPORT4XCH27 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH27, + adcNegSelAPORT4YCH28 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH28, + adcNegSelAPORT4XCH29 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH29, + adcNegSelAPORT4YCH30 = _ADC_SINGLECTRL_NEGSEL_APORT4YCH30, + adcNegSelAPORT4XCH31 = _ADC_SINGLECTRL_NEGSEL_APORT4XCH31, + adcNegSelTESTN = _ADC_SINGLECTRL_NEGSEL_TESTN, + adcNegSelDEFAULT = _ADC_SINGLECTRL_NEGSEL_DEFAULT, + adcNegSelVSS = _ADC_SINGLECTRL_NEGSEL_VSS +} ADC_NegSel_TypeDef; +#endif + + +#if defined( _ADC_SCANINPUTSEL_MASK ) + /* ADC scan input groups */ +typedef enum +{ + adcScanInputGroup0 = 0, + adcScanInputGroup1 = 1, + adcScanInputGroup2 = 2, + adcScanInputGroup3 = 3, +} ADC_ScanInputGroup_TypeDef; + + /* ADC scan alternative negative inputs */ +typedef enum +{ + adcScanNegInput1 = 1, + adcScanNegInput3 = 3, + adcScanNegInput5 = 5, + adcScanNegInput7 = 7, + adcScanNegInput8 = 8, + adcScanNegInput10 = 10, + adcScanNegInput12 = 12, + adcScanNegInput14 = 14, + adcScanNegInputDefault = 0xFF, +} ADC_ScanNegInput_TypeDef; +#endif + + +/** ADC Start command. */ +typedef enum +{ + /** Start single conversion. */ + adcStartSingle = ADC_CMD_SINGLESTART, + + /** Start scan sequence. */ + adcStartScan = ADC_CMD_SCANSTART, + + /** + * Start scan sequence and single conversion, typically used when tailgating + * single conversion after scan sequence. + */ + adcStartScanAndSingle = ADC_CMD_SCANSTART | ADC_CMD_SINGLESTART +} ADC_Start_TypeDef; + + +/** Warm-up mode. */ +typedef enum +{ + /** ADC shutdown after each conversion. */ + adcWarmupNormal = _ADC_CTRL_WARMUPMODE_NORMAL, + +#if defined( _ADC_CTRL_WARMUPMODE_FASTBG ) + /** Do not warm-up bandgap references. */ + adcWarmupFastBG = _ADC_CTRL_WARMUPMODE_FASTBG, +#endif + +#if defined( _ADC_CTRL_WARMUPMODE_KEEPSCANREFWARM ) + /** Reference selected for scan mode kept warm.*/ + adcWarmupKeepScanRefWarm = _ADC_CTRL_WARMUPMODE_KEEPSCANREFWARM, +#endif + +#if defined( _ADC_CTRL_WARMUPMODE_KEEPINSTANDBY ) + /** ADC is kept in standby mode between conversion. 1us warmup time needed + before next conversion. */ + adcWarmupKeepInStandby = _ADC_CTRL_WARMUPMODE_KEEPINSTANDBY, +#endif + +#if defined( _ADC_CTRL_WARMUPMODE_KEEPINSLOWACC ) + /** ADC is kept in slow acquisition mode between conversions. 1us warmup + time needed before next conversion. */ + adcWarmupKeepInSlowAcq = _ADC_CTRL_WARMUPMODE_KEEPINSLOWACC, +#endif + + /** ADC and reference selected for scan mode kept warmup, allowing + continuous conversion. */ + adcWarmupKeepADCWarm = _ADC_CTRL_WARMUPMODE_KEEPADCWARM, + +} ADC_Warmup_TypeDef; + + +#if defined( _ADC_CTRL_ADCCLKMODE_MASK ) + /** ADC EM2 clock configuration */ +typedef enum +{ + adcEm2Disabled = 0, + adcEm2ClockOnDemand = _ADC_CTRL_ADCCLKMODE_ASYNC | _ADC_CTRL_ASYNCCLKEN_ASNEEDED, + adcEm2ClockAlwaysOn = _ADC_CTRL_ADCCLKMODE_ASYNC | _ADC_CTRL_ASYNCCLKEN_ALWAYSON, +} ADC_EM2ClockConfig_TypeDef; +#endif + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** ADC init structure, common for single conversion and scan sequence. */ +typedef struct +{ + /** + * Oversampling rate select. In order to have any effect, oversampling must + * be enabled for single/scan mode. + */ + ADC_OvsRateSel_TypeDef ovsRateSel; + +#if defined( _ADC_CTRL_LPFMODE_MASK ) + /** Lowpass or decoupling capacitor filter to use. */ + ADC_LPFilter_TypeDef lpfMode; +#endif + + /** Warm-up mode to use for ADC. */ + ADC_Warmup_TypeDef warmUpMode; + + /** + * Timebase used for ADC warm up. Select N to give (N+1)HFPERCLK cycles. + * (Additional delay is added for bandgap references, please refer to the + * reference manual.) Normally, N should be selected so that the timebase + * is at least 1 us. See ADC_TimebaseCalc() for a way to obtain + * a suggested timebase of at least 1 us. + */ + uint8_t timebase; + + /** Clock division factor N, ADC clock = HFPERCLK / (N + 1). */ + uint8_t prescale; + + /** Enable/disable conversion tailgating. */ + bool tailgate; + + /** ADC EM2 clock configuration */ +#if defined( _ADC_CTRL_ADCCLKMODE_MASK ) + ADC_EM2ClockConfig_TypeDef em2ClockConfig; +#endif +} ADC_Init_TypeDef; + + +/** Default config for ADC init structure. */ +#if defined( _ADC_CTRL_LPFMODE_MASK ) && (!defined( _ADC_CTRL_ADCCLKMODE_MASK )) +#define ADC_INIT_DEFAULT \ +{ \ + adcOvsRateSel2, /* 2x oversampling (if enabled). */ \ + adcLPFilterBypass, /* No input filter selected. */ \ + adcWarmupNormal, /* ADC shutdown after each conversion. */ \ + _ADC_CTRL_TIMEBASE_DEFAULT, /* Use HW default value. */ \ + _ADC_CTRL_PRESC_DEFAULT, /* Use HW default value. */ \ + false /* Do not use tailgate. */ \ +} +#elif (!defined( _ADC_CTRL_LPFMODE_MASK )) && (!defined( _ADC_CTRL_ADCCLKMODE_MASK )) +#define ADC_INIT_DEFAULT \ +{ \ + adcOvsRateSel2, /* 2x oversampling (if enabled). */ \ + adcWarmupNormal, /* ADC shutdown after each conversion. */ \ + _ADC_CTRL_TIMEBASE_DEFAULT, /* Use HW default value. */ \ + _ADC_CTRL_PRESC_DEFAULT, /* Use HW default value. */ \ + false /* Do not use tailgate. */ \ +} +#elif (!defined( _ADC_CTRL_LPFMODE_MASK )) && defined( _ADC_CTRL_ADCCLKMODE_MASK ) +#define ADC_INIT_DEFAULT \ +{ \ + adcOvsRateSel2, /* 2x oversampling (if enabled). */ \ + adcWarmupNormal, /* ADC shutdown after each conversion. */ \ + _ADC_CTRL_TIMEBASE_DEFAULT, /* Use HW default value. */ \ + _ADC_CTRL_PRESC_DEFAULT, /* Use HW default value. */ \ + false, /* Do not use tailgate. */ \ + adcEm2Disabled /* ADC disabled in EM2 */ \ +} +#endif + + +/** Scan input configuration */ +typedef struct +{ + /** Input range select to be applied to ADC_SCANCHCONF. */ + int32_t scanInputSel; + + /** Input enable mask */ + uint32_t scanInputEn; + + /** Alternative negative input */ + uint32_t scanNegSel; +} ADC_InitScanInput_TypeDef; + + +/** Scan sequence init structure. */ +typedef struct +{ + /** + * Peripheral reflex system trigger selection. Only applicable if @p prsEnable + * is enabled. + */ + ADC_PRSSEL_TypeDef prsSel; + + /** Acquisition time (in ADC clock cycles). */ + ADC_AcqTime_TypeDef acqTime; + + /** + * Sample reference selection. Notice that for external references, the + * ADC calibration register must be set explicitly. + */ + ADC_Ref_TypeDef reference; + + /** Sample resolution. */ + ADC_Res_TypeDef resolution; + +#if defined( _ADC_SCANCTRL_INPUTMASK_MASK ) + /** + * Scan input selection. If single ended (@p diff is false), use logical + * combination of ADC_SCANCTRL_INPUTMASK_CHx defines. If differential input + * (@p diff is true), use logical combination of ADC_SCANCTRL_INPUTMASK_CHxCHy + * defines. (Notice underscore prefix for defines used.) + */ + uint32_t input; +#endif + +#if defined( _ADC_SCANINPUTSEL_MASK ) + /** + * Scan input configuration. Use ADC_ScanSingleEndedInit() or ADC_ScanDifferentialInit() + * to write this struct. Note that the diff variable is included in ADC_InitScanInput_TypeDef. + */ + ADC_InitScanInput_TypeDef scanInputConfig; +#endif + + /** Select if single ended or differential input. */ + bool diff; + + /** Peripheral reflex system trigger enable. */ + bool prsEnable; + + /** Select if left adjustment should be done. */ + bool leftAdjust; + + /** Select if continuous conversion until explicit stop. */ + bool rep; + + /** When true, DMA is available in EM2 for scan conversion */ +#if defined( _ADC_CTRL_SCANDMAWU_MASK ) + bool scanDmaEm2Wu; +#endif + +#if defined( _ADC_SCANCTRLX_FIFOOFACT_MASK ) + /** When true, the FIFO overwrites old data when full. If false, then the FIFO discards new data. + The SINGLEOF IRQ is triggered in both cases. */ + bool fifoOverwrite; +#endif +} ADC_InitScan_TypeDef; + +/** Default config for ADC scan init structure. */ +#if defined( _ADC_SCANCTRL_INPUTMASK_MASK ) +#define ADC_INITSCAN_DEFAULT \ +{ \ + adcPRSSELCh0, /* PRS ch0 (if enabled). */ \ + adcAcqTime1, /* 1 ADC_CLK cycle acquisition time. */ \ + adcRef1V25, /* 1.25V internal reference. */ \ + adcRes12Bit, /* 12 bit resolution. */ \ + 0, /* No input selected. */ \ + false, /* Single-ended input. */ \ + false, /* PRS disabled. */ \ + false, /* Right adjust. */ \ + false, /* Deactivate conversion after one scan sequence. */ \ +} +#endif + +#if defined( _ADC_SCANINPUTSEL_MASK ) +#define ADC_INITSCAN_DEFAULT \ +{ \ + adcPRSSELCh0, /* PRS ch0 (if enabled). */ \ + adcAcqTime1, /* 1 ADC_CLK cycle acquisition time. */ \ + adcRef1V25, /* 1.25V internal reference. */ \ + adcRes12Bit, /* 12 bit resolution. */ \ + 0, /* Default ADC inputs */ \ + 0, /* Default input mask (all off) */ \ + _ADC_SCANNEGSEL_RESETVALUE,/* Default negative select for positive ternimal */ \ + false, /* Single-ended input. */ \ + false, /* PRS disabled. */ \ + false, /* Right adjust. */ \ + false, /* Deactivate conversion after one scan sequence. */ \ + false, /* No EM2 DMA wakeup from scan FIFO DVL */ \ + false /* Discard new data on full FIFO. */ \ +} +#endif + + +/** Single conversion init structure. */ +typedef struct +{ + /** + * Peripheral reflex system trigger selection. Only applicable if @p prsEnable + * is enabled. + */ + ADC_PRSSEL_TypeDef prsSel; + + /** Acquisition time (in ADC clock cycles). */ + ADC_AcqTime_TypeDef acqTime; + + /** + * Sample reference selection. Notice that for external references, the + * ADC calibration register must be set explicitly. + */ + ADC_Ref_TypeDef reference; + + /** Sample resolution. */ + ADC_Res_TypeDef resolution; + +#if defined( _ADC_SINGLECTRL_INPUTSEL_MASK ) + /** + * Sample input selection, use single ended or differential input according + * to setting of @p diff. + */ + ADC_SingleInput_TypeDef input; +#endif + +#if defined( _ADC_SINGLECTRL_POSSEL_MASK ) + /** Select positive input for for single channel conversion mode. */ + ADC_PosSel_TypeDef posSel; +#endif + +#if defined( _ADC_SINGLECTRL_NEGSEL_MASK ) + /** Select negative input for single channel conversion mode. Negative input is grounded + for single ended (non-differential) converison. */ + ADC_NegSel_TypeDef negSel; +#endif + + /** Select if single ended or differential input. */ + bool diff; + + /** Peripheral reflex system trigger enable. */ + bool prsEnable; + + /** Select if left adjustment should be done. */ + bool leftAdjust; + + /** Select if continuous conversion until explicit stop. */ + bool rep; + +#if defined( _ADC_CTRL_SINGLEDMAWU_MASK ) + /** When true, DMA is available in EM2 for single conversion */ + bool singleDmaEm2Wu; +#endif + +#if defined( _ADC_SINGLECTRLX_FIFOOFACT_MASK ) + /** When true, the FIFO overwrites old data when full. If false, then the FIFO discards new data. + The SCANOF IRQ is triggered in both cases. */ + bool fifoOverwrite; +#endif +} ADC_InitSingle_TypeDef; + +/** Default config for ADC single conversion init structure. */ +#if defined( _ADC_SINGLECTRL_INPUTSEL_MASK ) +#define ADC_INITSINGLE_DEFAULT \ +{ \ + adcPRSSELCh0, /* PRS ch0 (if enabled). */ \ + adcAcqTime1, /* 1 ADC_CLK cycle acquisition time. */ \ + adcRef1V25, /* 1.25V internal reference. */ \ + adcRes12Bit, /* 12 bit resolution. */ \ + adcSingleInpCh0, /* CH0 input selected. */ \ + false, /* Single ended input. */ \ + false, /* PRS disabled. */ \ + false, /* Right adjust. */ \ + false /* Deactivate conversion after one scan sequence. */ \ +} +#else +#define ADC_INITSINGLE_DEFAULT \ +{ \ + adcPRSSELCh0, /* PRS ch0 (if enabled). */ \ + adcAcqTime1, /* 1 ADC_CLK cycle acquisition time. */ \ + adcRef1V25, /* 1.25V internal reference. */ \ + adcRes12Bit, /* 12 bit resolution. */ \ + adcPosSelAPORT0XCH0, /* Select node BUS0XCH0 as posSel */ \ + adcNegSelAPORT0XCH1, /* Select node BUS0XCH1 as negSel */ \ + false, /* Single ended input. */ \ + false, /* PRS disabled. */ \ + false, /* Right adjust. */ \ + false, /* Deactivate conversion after one scan sequence. */ \ + false, /* No EM2 DMA wakeup from single FIFO DVL */ \ + false /* Discard new data on full FIFO. */ \ +} +#endif + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get single conversion result. + * + * @note + * Check data valid flag before calling this function. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @return + * Single conversion data. + ******************************************************************************/ +__STATIC_INLINE uint32_t ADC_DataSingleGet(ADC_TypeDef *adc) +{ + return adc->SINGLEDATA; +} + + +/***************************************************************************//** + * @brief + * Peek single conversion result. + * + * @note + * Check data valid flag before calling this function. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @return + * Single conversion data. + ******************************************************************************/ +__STATIC_INLINE uint32_t ADC_DataSinglePeek(ADC_TypeDef *adc) +{ + return adc->SINGLEDATAP; +} + + +/***************************************************************************//** + * @brief + * Get scan result. + * + * @note + * Check data valid flag before calling this function. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @return + * Scan conversion data. + ******************************************************************************/ +__STATIC_INLINE uint32_t ADC_DataScanGet(ADC_TypeDef *adc) +{ + return adc->SCANDATA; +} + + +/***************************************************************************//** + * @brief + * Peek scan result. + * + * @note + * Check data valid flag before calling this function. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @return + * Scan conversion data. + ******************************************************************************/ +__STATIC_INLINE uint32_t ADC_DataScanPeek(ADC_TypeDef *adc) +{ + return adc->SCANDATAP; +} + + +#if defined( _ADC_SCANDATAX_MASK ) +uint32_t ADC_DataIdScanGet(ADC_TypeDef *adc, uint32_t *scanId); +#endif + +void ADC_Init(ADC_TypeDef *adc, const ADC_Init_TypeDef *init); +void ADC_Reset(ADC_TypeDef *adc); +void ADC_InitScan(ADC_TypeDef *adc, const ADC_InitScan_TypeDef *init); + +#if defined( _ADC_SCANINPUTSEL_MASK ) +void ADC_ScanInputClear(ADC_InitScan_TypeDef *scanInit); +uint32_t ADC_ScanSingleEndedInputAdd(ADC_InitScan_TypeDef *scanInit, + ADC_ScanInputGroup_TypeDef inputGroup, + ADC_PosSel_TypeDef singleEndedSel); +uint32_t ADC_ScanDifferentialInputAdd(ADC_InitScan_TypeDef *scanInit, + ADC_ScanInputGroup_TypeDef inputGroup, + ADC_PosSel_TypeDef posSel, + ADC_ScanNegInput_TypeDef adcScanNegInput); +#endif + +void ADC_InitSingle(ADC_TypeDef *adc, const ADC_InitSingle_TypeDef *init); +uint8_t ADC_TimebaseCalc(uint32_t hfperFreq); +uint8_t ADC_PrescaleCalc(uint32_t adcFreq, uint32_t hfperFreq); + + +/***************************************************************************//** + * @brief + * Clear one or more pending ADC interrupts. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] flags + * Pending ADC interrupt source to clear. Use a bitwise logic OR combination + * of valid interrupt flags for the ADC module (ADC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ADC_IntClear(ADC_TypeDef *adc, uint32_t flags) +{ + adc->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more ADC interrupts. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] flags + * ADC interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the ADC module (ADC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ADC_IntDisable(ADC_TypeDef *adc, uint32_t flags) +{ + adc->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more ADC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using ADC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] flags + * ADC interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the ADC module (ADC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ADC_IntEnable(ADC_TypeDef *adc, uint32_t flags) +{ + adc->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending ADC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @return + * ADC interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the ADC module (ADC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t ADC_IntGet(ADC_TypeDef *adc) +{ + return adc->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending ADC interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled ADC interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in ADCx_IEN_nnn + * register (ADCx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the ADC module + * (ADCx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t ADC_IntGetEnabled(ADC_TypeDef *adc) +{ + uint32_t ien; + + /* Store ADCx->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = adc->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return adc->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending ADC interrupts from SW. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] flags + * ADC interrupt sources to set to pending. Use a bitwise logic OR combination + * of valid interrupt flags for the ADC module (ADC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void ADC_IntSet(ADC_TypeDef *adc, uint32_t flags) +{ + adc->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Start scan sequence and/or single conversion. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] cmd + * Command indicating which type of sampling to start. + ******************************************************************************/ +__STATIC_INLINE void ADC_Start(ADC_TypeDef *adc, ADC_Start_TypeDef cmd) +{ + adc->CMD = (uint32_t)cmd; +} + + +/** @} (end addtogroup ADC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(ADC_COUNT) && (ADC_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_ADC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_aes.h b/cpu/efm32_common/emlib/inc/em_aes.h new file mode 100644 index 0000000000000..e680e04c00ed5 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_aes.h @@ -0,0 +1,268 @@ +/***************************************************************************//** + * @file em_aes.h + * @brief Advanced encryption standard (AES) accelerator peripheral API. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_AES_H__ +#define __SILICON_LABS_EM_AES_H__ + +#include "em_device.h" +#if defined(AES_COUNT) && (AES_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup AES + * @{ + ******************************************************************************/ + +/******************************************************************************* + ****************************** TYPEDEFS *********************************** + ******************************************************************************/ + +/** + * @brief + * AES counter modification function pointer. + * @details + * Parameters: + * @li ctr - Ptr to byte array (16 bytes) holding counter to be modified. + */ +typedef void (*AES_CtrFuncPtr_TypeDef)(uint8_t *ctr); + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void AES_CBC128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt); + +#if defined( AES_CTRL_AES256 ) +void AES_CBC256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt); +#endif + +void AES_CFB128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt); + +#if defined( AES_CTRL_AES256 ) +void AES_CFB256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt); +#endif + +void AES_CTR128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + uint8_t *ctr, + AES_CtrFuncPtr_TypeDef ctrFunc); + +#if defined( AES_CTRL_AES256 ) +void AES_CTR256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + uint8_t *ctr, + AES_CtrFuncPtr_TypeDef ctrFunc); +#endif + +void AES_CTRUpdate32Bit(uint8_t *ctr); + +void AES_DecryptKey128(uint8_t *out, const uint8_t *in); + +#if defined( AES_CTRL_AES256 ) +void AES_DecryptKey256(uint8_t *out, const uint8_t *in); +#endif + +void AES_ECB128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + bool encrypt); + +#if defined( AES_CTRL_AES256 ) +void AES_ECB256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + bool encrypt); +#endif + +/***************************************************************************//** + * @brief + * Clear one or more pending AES interrupts. + * + * @param[in] flags + * Pending AES interrupt source to clear. Use a bitwise logic OR combination of + * valid interrupt flags for the AES module (AES_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void AES_IntClear(uint32_t flags) +{ + AES->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more AES interrupts. + * + * @param[in] flags + * AES interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the AES module (AES_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void AES_IntDisable(uint32_t flags) +{ + AES->IEN &= ~(flags); +} + + +/***************************************************************************//** + * @brief + * Enable one or more AES interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using AES_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * AES interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the AES module (AES_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void AES_IntEnable(uint32_t flags) +{ + AES->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending AES interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * AES interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the AES module (AES_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t AES_IntGet(void) +{ + return AES->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending AES interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled AES interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in AES_IEN and + * - the pending interrupt flags AES_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t AES_IntGetEnabled(void) +{ + uint32_t ien; + + ien = AES->IEN; + return AES->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending AES interrupts from SW. + * + * @param[in] flags + * AES interrupt sources to set to pending. Use a bitwise logic OR combination + * of valid interrupt flags for the AES module (AES_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void AES_IntSet(uint32_t flags) +{ + AES->IFS = flags; +} + + +void AES_OFB128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv); + +#if defined( AES_CTRL_AES256 ) +void AES_OFB256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv); +#endif + + +/** @} (end addtogroup AES) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(AES_COUNT) && (AES_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_AES_H__ */ + + diff --git a/cpu/efm32_common/emlib/inc/em_assert.h b/cpu/efm32_common/emlib/inc/em_assert.h new file mode 100644 index 0000000000000..04fe0d4848bb6 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_assert.h @@ -0,0 +1,79 @@ +/***************************************************************************//** + * @file em_assert.h + * @brief Emlib peripheral API "assert" implementation. + * @version 4.2.1 + * + * @details + * By default, emlib library assert usage is not included in order to reduce + * footprint and processing overhead. Further, emlib assert usage is decoupled + * from ISO C assert handling (NDEBUG usage), to allow a user to use ISO C + * assert without including emlib assert statements. + * + * Below are available defines for controlling emlib assert inclusion. The defines + * are typically defined for a project to be used by the preprocessor. + * + * @li If DEBUG_EFM is defined, the internal emlib library assert handling will + * be used, which may be a quite rudimentary implementation. + * + * @li If DEBUG_EFM_USER is defined instead, the user must provide their own + * assert handling routine (assertEFM()). + * + * As indicated above, if none of the above defines are used, emlib assert + * statements are not compiled. + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_ASSERT_H__ +#define __SILICON_LABS_EM_ASSERT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#if defined(DEBUG_EFM) || defined(DEBUG_EFM_USER) + +/* Due to footprint considerations, we only pass file name and line number, */ +/* not the assert expression (nor function name (C99)) */ +void assertEFM(const char *file, int line); +#define EFM_ASSERT(expr) ((expr) ? ((void)0) : assertEFM(__FILE__, __LINE__)) + +#else + +#define EFM_ASSERT(expr) ((void)(expr)) + +#endif /* defined(DEBUG_EFM) || defined(DEBUG_EFM_USER) */ + +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_ASSERT_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_bitband.h b/cpu/efm32_common/emlib/inc/em_bitband.h new file mode 100644 index 0000000000000..681289ff86d3d --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_bitband.h @@ -0,0 +1,139 @@ +/***************************************************************************//** + * @file em_bitband.h + * @brief Bitband Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_BITBAND_H__ +#define __SILICON_LABS_EM_BITBAND_H__ + +#include "em_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup BITBAND + * @brief BITBAND Peripheral API (deprecated - use em_bus.h) + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Perform bit-band operation on peripheral memory location. + * + * @details + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * @note + * This function is only atomic on cores which fully support bitbanding. + * + * @param[in] addr Peripheral address location to modify bit in. + * + * @param[in] bit Bit position to modify, 0-31. + * + * @param[in] val Value to set bit to, 0 or 1. + ******************************************************************************/ +#define BITBAND_Peripheral(addr, bit, val) BUS_RegBitWrite(addr, bit, val) + + +/***************************************************************************//** + * @brief + * Perform a read operation on the peripheral bit-band memory location. + * + * @details + * This function reads a single bit from the peripheral bit-band alias region. + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * @param[in] addr Peripheral address location to read. + * + * @param[in] bit Bit position to read, 0-31. + * + * @return Value of the requested bit. + ******************************************************************************/ +#define BITBAND_PeripheralRead(addr, bit) BUS_RegBitRead(addr, bit) + + +/***************************************************************************//** + * @brief + * Perform bit-band operation on SRAM memory location. + * + * @details + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * @note + * This function is only atomic on cores which fully support bitbanding. + * + * @param[in] addr SRAM address location to modify bit in. + * + * @param[in] bit Bit position to modify, 0-31. + * + * @param[in] val Value to set bit to, 0 or 1. + ******************************************************************************/ +#define BITBAND_SRAM(addr, bit, val) BUS_RamBitWrite(addr, bit, val) + + +/***************************************************************************//** + * @brief + * Read a single bit from the SRAM bit-band alias region. + * + * @details + * This function reads a single bit from the SRAM bit-band alias region. + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * @param[in] addr SRAM address location to modify bit in. + * + * @param[in] bit Bit position to modify, 0-31. + * + * @return Value of the requested bit. + ******************************************************************************/ +#define BITBAND_SRAMRead(addr, bit) BUS_RamBitRead(addr, bit) + +/** @} (end addtogroup BITBAND) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_BITBAND_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_burtc.h b/cpu/efm32_common/emlib/inc/em_burtc.h new file mode 100644 index 0000000000000..423583fa1ab99 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_burtc.h @@ -0,0 +1,419 @@ +/***************************************************************************//** + * @file em_burtc.h + * @brief Backup Real Time Counter (BURTC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_BURTC_H__ +#define __SILICON_LABS_EM_BURTC_H__ + +#include "em_device.h" +#if defined(BURTC_PRESENT) + +#include +#include "em_assert.h" +#include "em_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup BURTC + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** BURTC clock divisors. These values are valid for the BURTC prescaler. */ +#define burtcClkDiv_1 1 /**< Divide clock by 1. */ +#define burtcClkDiv_2 2 /**< Divide clock by 2. */ +#define burtcClkDiv_4 4 /**< Divide clock by 4. */ +#define burtcClkDiv_8 8 /**< Divide clock by 8. */ +#define burtcClkDiv_16 16 /**< Divide clock by 16. */ +#define burtcClkDiv_32 32 /**< Divide clock by 32. */ +#define burtcClkDiv_64 64 /**< Divide clock by 64. */ +#define burtcClkDiv_128 128 /**< Divide clock by 128. */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** BURTC clock selection */ +typedef enum +{ + /** Ultra low frequency (1 kHz) clock */ + burtcClkSelULFRCO = BURTC_CTRL_CLKSEL_ULFRCO, + /** Low frequency RC oscillator */ + burtcClkSelLFRCO = BURTC_CTRL_CLKSEL_LFRCO, + /** Low frequency crystal osciallator */ + burtcClkSelLFXO = BURTC_CTRL_CLKSEL_LFXO +} BURTC_ClkSel_TypeDef; + + +/** BURTC mode of operation */ +typedef enum +{ + /** Disable BURTC */ + burtcModeDisable = BURTC_CTRL_MODE_DISABLE, + /** Enable and start BURTC counter in EM0 to EM2 */ + burtcModeEM2 = BURTC_CTRL_MODE_EM2EN, + /** Enable and start BURTC counter in EM0 to EM3 */ + burtcModeEM3 = BURTC_CTRL_MODE_EM3EN, + /** Enable and start BURTC counter in EM0 to EM4 */ + burtcModeEM4 = BURTC_CTRL_MODE_EM4EN, +} BURTC_Mode_TypeDef; + +/** BURTC low power mode */ +typedef enum +{ + /** Low Power Mode is disabled */ + burtcLPDisable = BURTC_LPMODE_LPMODE_DISABLE, + /** Low Power Mode is always enabled */ + burtcLPEnable = BURTC_LPMODE_LPMODE_ENABLE, + /** Low Power Mode when system enters backup mode */ + burtcLPBU = BURTC_LPMODE_LPMODE_BUEN +} BURTC_LP_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** BURTC initialization structure. */ +typedef struct +{ + bool enable; /**< Enable BURTC after initialization (starts counter) */ + + BURTC_Mode_TypeDef mode; /**< Configure energy mode operation */ + bool debugRun; /**< If true, counter will keep running under debug halt */ + BURTC_ClkSel_TypeDef clkSel; /**< Select clock source */ + uint32_t clkDiv; /**< Clock divider; for ULFRCO 1Khz or 2kHz operation */ + + uint32_t lowPowerComp; /**< Number of least significantt clock bits to ignore in low power mode */ + bool timeStamp; /**< Enable time stamp on entering backup power domain */ + + bool compare0Top; /**< Set if Compare Value 0 is also top value (counter restart) */ + + BURTC_LP_TypeDef lowPowerMode; /**< Low power operation mode, requires LFXO or LFRCO */ +} BURTC_Init_TypeDef; + +/** Default configuration for BURTC init structure */ +#define BURTC_INIT_DEFAULT \ +{ \ + true, \ + burtcModeEM2, \ + false, \ + burtcClkSelULFRCO, \ + burtcClkDiv_1, \ + 0, \ + true, \ + false, \ + burtcLPDisable, \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Clear one or more pending BURTC interrupts. + * + * @param[in] flags + * BURTC interrupt sources to clear. Use a set of interrupt flags OR-ed + * together to clear multiple interrupt sources for the BURTC module + * (BURTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void BURTC_IntClear(uint32_t flags) +{ + BURTC->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more BURTC interrupts. + * + * @param[in] flags + * BURTC interrupt sources to disable. Use a set of interrupt flags OR-ed + * together to disable multiple interrupt sources for the BURTC module + * (BURTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void BURTC_IntDisable(uint32_t flags) +{ + BURTC->IEN &= ~(flags); +} + + +/***************************************************************************//** + * @brief + * Enable one or more BURTC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using BURTC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * BURTC interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to set multiple interrupt sources for the BURTC module + * (BURTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void BURTC_IntEnable(uint32_t flags) +{ + BURTC->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending BURTC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending BURTC interrupt sources. Returns a set of interrupt flags OR-ed + * together for multiple interrupt sources in the BURTC module (BURTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t BURTC_IntGet(void) +{ + return(BURTC->IF); +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending BURTC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending BURTC interrupt sources that is also enabled. Returns a set of + * interrupt flags OR-ed together for multiple interrupt sources in the + * BURTC module (BURTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t BURTC_IntGetEnabled(void) +{ + uint32_t tmp; + + /* Get enabled interrupts */ + tmp = BURTC->IEN; + + /* Return set intterupts */ + return BURTC->IF & tmp; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending BURTC interrupts from SW. + * + * @param[in] flags + * BURTC interrupt sources to set to pending. Use a set of interrupt flags + * OR-ed together to set multiple interrupt sources for the BURTC module + * (BURTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void BURTC_IntSet(uint32_t flags) +{ + BURTC->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Status of BURTC RAM, timestamp and LP Mode + * + * @return A mask logially OR-ed status bits + ******************************************************************************/ +__STATIC_INLINE uint32_t BURTC_Status(void) +{ + return BURTC->STATUS; +} + + +/***************************************************************************//** + * @brief + * Clear and reset BURTC status register + ******************************************************************************/ +__STATIC_INLINE void BURTC_StatusClear(void) +{ + BURTC->CMD = BURTC_CMD_CLRSTATUS; +} + + +/***************************************************************************//** + * @brief + * Enable or Disable BURTC peripheral reset and start counter + * @param[in] enable + * If true; asserts reset to BURTC, halts counter, if false; deassert reset + ******************************************************************************/ +__STATIC_INLINE void BURTC_Enable(bool enable) +{ + /* Note! If mode is disabled, BURTC counter will not start */ + EFM_ASSERT(((enable == true) + && ((BURTC->CTRL & _BURTC_CTRL_MODE_MASK) + != BURTC_CTRL_MODE_DISABLE)) + || (enable == false)); + if (enable) + { + BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 0); + } + else + { + BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 1); + } +} + + +/***************************************************************************//** + * @brief Get BURTC counter + * + * @return + * BURTC counter value + ******************************************************************************/ +__STATIC_INLINE uint32_t BURTC_CounterGet(void) +{ + return BURTC->CNT; +} + + +/***************************************************************************//** + * @brief Get BURTC timestamp for entering BU + * + * @return + * BURTC Time Stamp value + ******************************************************************************/ +__STATIC_INLINE uint32_t BURTC_TimestampGet(void) +{ + return BURTC->TIMESTAMP; +} + + +/***************************************************************************//** + * @brief Freeze register updates until enabled + * @param[in] enable If true, registers are not updated until enabled again. + ******************************************************************************/ +__STATIC_INLINE void BURTC_FreezeEnable(bool enable) +{ + BUS_RegBitWrite(&BURTC->FREEZE, _BURTC_FREEZE_REGFREEZE_SHIFT, enable); +} + + +/***************************************************************************//** + * @brief Shut down power to rentention register bank. + * @param[in] enable + * If true, shuts off power to retention registers. + * @note + * When power rentention is disabled, it cannot be enabled again (until + * reset). + ******************************************************************************/ +__STATIC_INLINE void BURTC_Powerdown(bool enable) +{ + BUS_RegBitWrite(&BURTC->POWERDOWN, _BURTC_POWERDOWN_RAM_SHIFT, enable); +} + + +/***************************************************************************//** + * @brief + * Set a value in one of the retention registers + * + * @param[in] num + * Register to set + * @param[in] data + * Value to put into register + ******************************************************************************/ +__STATIC_INLINE void BURTC_RetRegSet(uint32_t num, uint32_t data) +{ + EFM_ASSERT(num <= 127); + + BURTC->RET[num].REG = data; +} + + +/***************************************************************************//** + * @brief + * Read a value from one of the retention registers + * + * @param[in] num + * Retention Register to read + ******************************************************************************/ +__STATIC_INLINE uint32_t BURTC_RetRegGet(uint32_t num) +{ + EFM_ASSERT(num <= 127); + + return BURTC->RET[num].REG; +} + + +/***************************************************************************//** + * @brief + * Lock BURTC registers, will protect from writing new config settings + ******************************************************************************/ +__STATIC_INLINE void BURTC_Lock(void) +{ + BURTC->LOCK = BURTC_LOCK_LOCKKEY_LOCK; +} + + +/***************************************************************************//** + * @brief + * Unlock BURTC registers, enable write access to change configuration + ******************************************************************************/ +__STATIC_INLINE void BURTC_Unlock(void) +{ + BURTC->LOCK = BURTC_LOCK_LOCKKEY_UNLOCK; +} + + +void BURTC_Reset(void); +void BURTC_Init(const BURTC_Init_TypeDef *burtcInit); +void BURTC_CounterReset(void); +void BURTC_CompareSet(unsigned int comp, uint32_t value); +uint32_t BURTC_CompareGet(unsigned int comp); +uint32_t BURTC_ClockFreqGet(void); + + +/** @} (end addtogroup BURTC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* BURTC_PRESENT */ +#endif /* __SILICON_LABS_EM_BURTC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_bus.h b/cpu/efm32_common/emlib/inc/em_bus.h new file mode 100644 index 0000000000000..cf4d495a73281 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_bus.h @@ -0,0 +1,327 @@ +/***************************************************************************//** + * @file em_bus.h + * @brief RAM and peripheral bit-field set and clear API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_BUS__ +#define __SILICON_LABS_EM_BUS__ + +#include "em_device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup BUS + * @brief BUS RAM and register bit/field read/write API + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Perform a single-bit write operation on a 32-bit word in RAM + * + * @details + * This function uses Cortex-M bit-banding hardware to perform an atomic + * read-modify-write operation on a single bit write on a 32-bit word in RAM. + * Please refer to the reference manual for further details about bit-banding. + * + * @note + * This function is atomic on Cortex-M cores with bit-banding support. Bit- + * banding is a multicycle read-modify-write bus operation. RAM bit-banding is + * performed using the memory alias region at BITBAND_RAM_BASE. + * + * @param[in] addr Address of 32-bit word in RAM + * + * @param[in] bit Bit position to write, 0-31 + * + * @param[in] val Value to set bit to, 0 or 1 + ******************************************************************************/ +__STATIC_INLINE void BUS_RamBitWrite(volatile uint32_t *addr, + unsigned int bit, + unsigned int val) +{ +#if defined( BITBAND_RAM_BASE ) + uint32_t aliasAddr = + BITBAND_RAM_BASE + (((uint32_t)addr - SRAM_BASE) * 32) + (bit * 4); + + *(volatile uint32_t *)aliasAddr = (uint32_t)val; +#else + uint32_t tmp = *addr; + + /* Make sure val is not more than 1, because we only want to set one bit. */ + *addr = (tmp & ~(1 << bit)) | ((val & 1) << bit); +#endif +} + + +/***************************************************************************//** + * @brief + * Perform a single-bit read operation on a 32-bit word in RAM + * + * @details + * This function uses Cortex-M bit-banding hardware to perform an atomic + * read operation on a single register bit. Please refer to the + * reference manual for further details about bit-banding. + * + * @note + * This function is atomic on Cortex-M cores with bit-banding support. + * RAM bit-banding is performed using the memory alias region + * at BITBAND_RAM_BASE. + * + * @param[in] addr RAM address + * + * @param[in] bit Bit position to read, 0-31 + * + * @return + * The requested bit shifted to bit position 0 in the return value + ******************************************************************************/ +__STATIC_INLINE unsigned int BUS_RamBitRead(volatile const uint32_t *addr, + unsigned int bit) +{ +#if defined( BITBAND_RAM_BASE ) + uint32_t aliasAddr = + BITBAND_RAM_BASE + (((uint32_t)addr - SRAM_BASE) * 32) + (bit * 4); + + return *(volatile uint32_t *)aliasAddr; +#else + return ((*addr) >> bit) & 1; +#endif +} + + +/***************************************************************************//** + * @brief + * Perform a single-bit write operation on a peripheral register + * + * @details + * This function uses Cortex-M bit-banding hardware to perform an atomic + * read-modify-write operation on a single register bit. Please refer to the + * reference manual for further details about bit-banding. + * + * @note + * This function is atomic on Cortex-M cores with bit-banding support. Bit- + * banding is a multicycle read-modify-write bus operation. Peripheral register + * bit-banding is performed using the memory alias region at BITBAND_PER_BASE. + * + * @param[in] addr Peripheral register address + * + * @param[in] bit Bit position to write, 0-31 + * + * @param[in] val Value to set bit to, 0 or 1 + ******************************************************************************/ +__STATIC_INLINE void BUS_RegBitWrite(volatile uint32_t *addr, + unsigned int bit, + unsigned int val) +{ +#if defined( BITBAND_PER_BASE ) + uint32_t aliasAddr = + BITBAND_PER_BASE + (((uint32_t)addr - PER_MEM_BASE) * 32) + (bit * 4); + + *(volatile uint32_t *)aliasAddr = (uint32_t)val; +#else + uint32_t tmp = *addr; + + /* Make sure val is not more than 1, because we only want to set one bit. */ + *addr = (tmp & ~(1 << bit)) | ((val & 1) << bit); +#endif +} + + +/***************************************************************************//** + * @brief + * Perform a single-bit read operation on a peripheral register + * + * @details + * This function uses Cortex-M bit-banding hardware to perform an atomic + * read operation on a single register bit. Please refer to the + * reference manual for further details about bit-banding. + * + * @note + * This function is atomic on Cortex-M cores with bit-banding support. + * Peripheral register bit-banding is performed using the memory alias + * region at BITBAND_PER_BASE. + * + * @param[in] addr Peripheral register address + * + * @param[in] bit Bit position to read, 0-31 + * + * @return + * The requested bit shifted to bit position 0 in the return value + ******************************************************************************/ +__STATIC_INLINE unsigned int BUS_RegBitRead(volatile const uint32_t *addr, + unsigned int bit) +{ +#if defined( BITBAND_PER_BASE ) + uint32_t aliasAddr = + BITBAND_PER_BASE + (((uint32_t)addr - PER_MEM_BASE) * 32) + (bit * 4); + + return *(volatile uint32_t *)aliasAddr; +#else + return ((*addr) >> bit) & 1; +#endif +} + + +/***************************************************************************//** + * @brief + * Perform a masked set operation on peripheral register address. + * + * @details + * Peripheral register masked set provides a single-cycle and atomic set + * operation of a bit-mask in a peripheral register. All 1's in the mask are + * set to 1 in the register. All 0's in the mask are not changed in the + * register. + * RAMs and special peripherals are not supported. Please refer to the + * reference manual for further details about peripheral register field set. + * + * @note + * This function is single-cycle and atomic on cores with peripheral bit set + * and clear support. It uses the memory alias region at PER_BITSET_MEM_BASE. + * + * @param[in] addr Peripheral register address + * + * @param[in] mask Mask to set + ******************************************************************************/ +__STATIC_INLINE void BUS_RegMaskedSet(volatile uint32_t *addr, + uint32_t mask) +{ +#if defined( PER_BITSET_MEM_BASE ) + uint32_t aliasAddr = PER_BITSET_MEM_BASE + ((uint32_t)addr - PER_MEM_BASE); + *(volatile uint32_t *)aliasAddr = mask; +#else + *addr |= mask; +#endif +} + + +/***************************************************************************//** + * @brief + * Perform a masked clear operation on peripheral register address. + * + * @details + * Peripheral register masked clear provides a single-cycle and atomic clear + * operation of a bit-mask in a peripheral register. All 1's in the mask are + * set to 0 in the register. + * All 0's in the mask are not changed in the register. + * RAMs and special peripherals are not supported. Please refer to the + * reference manual for further details about peripheral register field clear. + * + * @note + * This function is single-cycle and atomic on cores with peripheral bit set + * and clear support. It uses the memory alias region at PER_BITCLR_MEM_BASE. + * + * @param[in] addr Peripheral register address + * + * @param[in] mask Mask to clear + ******************************************************************************/ +__STATIC_INLINE void BUS_RegMaskedClear(volatile uint32_t *addr, + uint32_t mask) +{ +#if defined( PER_BITCLR_MEM_BASE ) + uint32_t aliasAddr = PER_BITCLR_MEM_BASE + ((uint32_t)addr - PER_MEM_BASE); + *(volatile uint32_t *)aliasAddr = mask; +#else + *addr &= ~mask; +#endif +} + + +/***************************************************************************//** + * @brief + * Perform peripheral register masked clear and value write. + * + * @details + * This function first clears the mask in the peripheral register, then + * writes the value. Typically the mask is a bit-field in the register, and + * the value val is within the mask. + * + * @note + * This operation is not atomic. Note that the mask is first set to 0 before + * the val is set. + * + * @param[in] addr Peripheral register address + * + * @param[in] mask Peripheral register mask + * + * @param[in] val Peripheral register value. The value must be shifted to the + correct bit position in the register. + ******************************************************************************/ +__STATIC_INLINE void BUS_RegMaskedWrite(volatile uint32_t *addr, + uint32_t mask, + uint32_t val) +{ +#if defined( PER_BITCLR_MEM_BASE ) + BUS_RegMaskedClear(addr, mask); + BUS_RegMaskedSet(addr, val); +#else + *addr = (*addr & ~mask) | val; +#endif +} + + +/***************************************************************************//** + * @brief + * Perform a peripheral register masked read + * + * @details + * Read an unshifted and masked value from a peripheral register. + * + * @note + * This operation is not hardware accelerated. + * + * @param[in] addr Peripheral register address + * + * @param[in] mask Peripheral register mask + * + * @return + * Unshifted and masked register value + ******************************************************************************/ +__STATIC_INLINE uint32_t BUS_RegMaskedRead(volatile const uint32_t *addr, + uint32_t mask) +{ + return *addr & mask; +} + + +/** @} (end addtogroup BUS) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_BUS__ */ diff --git a/cpu/efm32_common/emlib/inc/em_chip.h b/cpu/efm32_common/emlib/inc/em_chip.h new file mode 100644 index 0000000000000..508ed4230f453 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_chip.h @@ -0,0 +1,193 @@ +/***************************************************************************//** + * @file em_chip.h + * @brief Chip Initialization API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_CHIP_H__ +#define __SILICON_LABS_EM_CHIP_H__ + +#include "em_device.h" +#include "em_system.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CHIP + * @brief Chip Initialization API + * @{ + ******************************************************************************/ + +/**************************************************************************//** + * @brief + * Chip initialization routine for revision errata workarounds + * + * This init function will configure the device to a state where it is + * as similar as later revisions as possible, to improve software compatibility + * with newer parts. See the device specific errata for details. + *****************************************************************************/ +__STATIC_INLINE void CHIP_Init(void) +{ +#if defined(_EFM32_GECKO_FAMILY) + uint32_t rev; + SYSTEM_ChipRevision_TypeDef chipRev; + volatile uint32_t *reg; + + rev = *(volatile uint32_t *)(0x0FE081FC); + /* Engineering Sample calibration setup */ + if ((rev >> 24) == 0) + { + reg = (volatile uint32_t *)0x400CA00C; + *reg &= ~(0x70UL); + /* DREG */ + reg = (volatile uint32_t *)0x400C6020; + *reg &= ~(0xE0000000UL); + *reg |= ~(7UL << 25); + } + if ((rev >> 24) <= 3) + { + /* DREG */ + reg = (volatile uint32_t *)0x400C6020; + *reg &= ~(0x00001F80UL); + /* Update CMU reset values */ + reg = (volatile uint32_t *)0x400C8040; + *reg = 0; + reg = (volatile uint32_t *)0x400C8044; + *reg = 0; + reg = (volatile uint32_t *)0x400C8058; + *reg = 0; + reg = (volatile uint32_t *)0x400C8060; + *reg = 0; + reg = (volatile uint32_t *)0x400C8078; + *reg = 0; + } + + SYSTEM_ChipRevisionGet(&chipRev); + if (chipRev.major == 0x01) + { + /* Rev A errata handling for EM2/3. Must enable DMA clock in order for EM2/3 */ + /* to work. This will be fixed in later chip revisions, so only do for rev A. */ + if (chipRev.minor == 00) + { + reg = (volatile uint32_t *)0x400C8040; + *reg |= 0x2; + } + + /* Rev A+B errata handling for I2C when using EM2/3. USART0 clock must be enabled */ + /* after waking up from EM2/EM3 in order for I2C to work. This will be fixed in */ + /* later chip revisions, so only do for rev A+B. */ + if (chipRev.minor <= 0x01) + { + reg = (volatile uint32_t *)0x400C8044; + *reg |= 0x1; + } + } + /* Ensure correct ADC/DAC calibration value */ + rev = *(volatile uint32_t *)0x0FE081F0; + if (rev < 0x4C8ABA00) + { + uint32_t cal; + + /* Enable ADC/DAC clocks */ + reg = (volatile uint32_t *)0x400C8044UL; + *reg |= (1 << 14 | 1 << 11); + + /* Retrive calibration values */ + cal = ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x00007F00UL) >> + 8) << 24; + + cal |= ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x0000007FUL) >> + 0) << 16; + + cal |= ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x00007F00UL) >> + 8) << 8; + + cal |= ((*(volatile uint32_t *)(0x0FE081B4UL) & 0x0000007FUL) >> + 0) << 0; + + /* ADC0->CAL = 1.25 reference */ + reg = (volatile uint32_t *)0x40002034UL; + *reg = cal; + + /* DAC0->CAL = 1.25 reference */ + reg = (volatile uint32_t *)(0x4000402CUL); + cal = *(volatile uint32_t *)0x0FE081C8UL; + *reg = cal; + + /* Turn off ADC/DAC clocks */ + reg = (volatile uint32_t *)0x400C8044UL; + *reg &= ~(1 << 14 | 1 << 11); + } +#endif + +#if defined(_EFM32_GIANT_FAMILY) + uint32_t rev; + SYSTEM_ChipRevision_TypeDef chipRev; + + rev = *(volatile uint32_t *)(0x0FE081FC); + SYSTEM_ChipRevisionGet(&chipRev); + + if (((rev >> 24) > 15) && (chipRev.minor == 3)) + { + /* This fixes an issue with the LFXO on high temperatures. */ + *(volatile uint32_t*)0x400C80C0 = + ( *(volatile uint32_t*)0x400C80C0 & ~(1<<6) ) | (1<<4); + } +#endif + +#if defined(_EFM32_HAPPY_FAMILY) + uint32_t rev; + rev = *(volatile uint32_t *)(0x0FE081FC); + + if ((rev >> 24) <= 129) + { + /* This fixes a mistaken internal connection between PC0 and PC4 */ + /* This disables an internal pulldown on PC4 */ + *(volatile uint32_t*)(0x400C6018) = (1 << 26) | (5 << 0); + /* This disables an internal LDO test signal driving PC4 */ + *(volatile uint32_t*)(0x400C80E4) &= ~(1 << 24); + } +#endif +} + +/** @} (end addtogroup CHIP) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_CHIP_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_cmu.h b/cpu/efm32_common/emlib/inc/em_cmu.h new file mode 100644 index 0000000000000..ad669992e2b53 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_cmu.h @@ -0,0 +1,1387 @@ +/***************************************************************************//** + * @file em_cmu.h + * @brief Clock management unit (CMU) API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ +#ifndef __SILICON_LABS_EM_CMU_H__ +#define __SILICON_LABS_EM_CMU_H__ + +#include "em_device.h" +#if defined( CMU_PRESENT ) + +#include +#include "em_assert.h" +#include "em_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CMU + * @{ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/* Select register id's, for internal use. */ +#define CMU_NOSEL_REG 0 +#define CMU_HFCLKSEL_REG 1 +#define CMU_LFACLKSEL_REG 2 +#define CMU_LFBCLKSEL_REG 3 +#define CMU_LFCCLKSEL_REG 4 +#define CMU_LFECLKSEL_REG 5 +#define CMU_DBGCLKSEL_REG 6 +#define CMU_USBCCLKSEL_REG 7 + +#define CMU_SEL_REG_POS 0 +#define CMU_SEL_REG_MASK 0xf + +/* Divisor/prescaler register id's, for internal use. */ +#define CMU_NODIV_REG 0 +#define CMU_NOPRESC_REG 0 +#define CMU_HFPRESC_REG 1 +#define CMU_HFCLKDIV_REG 1 +#define CMU_HFEXPPRESC_REG 2 +#define CMU_HFCLKLEPRESC_REG 3 +#define CMU_HFPERPRESC_REG 4 +#define CMU_HFPERCLKDIV_REG 4 +#define CMU_HFCOREPRESC_REG 5 +#define CMU_HFCORECLKDIV_REG 5 +#define CMU_HFRADIOPRESC_REG 6 +#define CMU_LFAPRESC0_REG 7 +#define CMU_LFBPRESC0_REG 8 +#define CMU_LFEPRESC0_REG 9 + +#define CMU_PRESC_REG_POS 4 +#define CMU_DIV_REG_POS CMU_PRESC_REG_POS +#define CMU_PRESC_REG_MASK 0xf +#define CMU_DIV_REG_MASK CMU_PRESC_REG_MASK + +/* Enable register id's, for internal use. */ +#define CMU_NO_EN_REG 0 +#define CMU_CTRL_EN_REG 1 +#define CMU_HFPERCLKDIV_EN_REG 1 +#define CMU_HFPERCLKEN0_EN_REG 2 +#define CMU_HFCORECLKEN0_EN_REG 3 +#define CMU_HFRADIOCLKEN0_EN_REG 4 +#define CMU_HFBUSCLKEN0_EN_REG 5 +#define CMU_LFACLKEN0_EN_REG 6 +#define CMU_LFBCLKEN0_EN_REG 7 +#define CMU_LFCCLKEN0_EN_REG 8 +#define CMU_LFECLKEN0_EN_REG 9 +#define CMU_PCNT_EN_REG 10 + +#define CMU_EN_REG_POS 8 +#define CMU_EN_REG_MASK 0xf + +/* Enable register bit positions, for internal use. */ +#define CMU_EN_BIT_POS 12 +#define CMU_EN_BIT_MASK 0x1f + +/* Clock branch bitfield positions, for internal use. */ +#define CMU_HF_CLK_BRANCH 0 +#define CMU_HFCORE_CLK_BRANCH 1 +#define CMU_HFPER_CLK_BRANCH 2 +#define CMU_HFRADIO_CLK_BRANCH 3 +#define CMU_HFBUS_CLK_BRANCH 4 +#define CMU_HFEXP_CLK_BRANCH 5 +#define CMU_DBG_CLK_BRANCH 6 +#define CMU_AUX_CLK_BRANCH 7 +#define CMU_RTC_CLK_BRANCH 8 +#define CMU_RTCC_CLK_BRANCH 8 +#define CMU_LETIMER_CLK_BRANCH 9 +#define CMU_LETIMER0_CLK_BRANCH 9 +#define CMU_LEUART0_CLK_BRANCH 10 +#define CMU_LEUART1_CLK_BRANCH 11 +#define CMU_LFA_CLK_BRANCH 12 +#define CMU_LFB_CLK_BRANCH 13 +#define CMU_LFC_CLK_BRANCH 14 +#define CMU_LFE_CLK_BRANCH 15 +#define CMU_USBC_CLK_BRANCH 16 +#define CMU_USBLE_CLK_BRANCH 17 +#define CMU_LCDPRE_CLK_BRANCH 18 +#define CMU_LCD_CLK_BRANCH 19 +#define CMU_LESENSE_CLK_BRANCH 20 + +#define CMU_CLK_BRANCH_POS 17 +#define CMU_CLK_BRANCH_MASK 0x1f + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Clock divisors. These values are valid for prescalers. */ +#define cmuClkDiv_1 1 /**< Divide clock by 1. */ +#define cmuClkDiv_2 2 /**< Divide clock by 2. */ +#define cmuClkDiv_4 4 /**< Divide clock by 4. */ +#define cmuClkDiv_8 8 /**< Divide clock by 8. */ +#define cmuClkDiv_16 16 /**< Divide clock by 16. */ +#define cmuClkDiv_32 32 /**< Divide clock by 32. */ +#define cmuClkDiv_64 64 /**< Divide clock by 64. */ +#define cmuClkDiv_128 128 /**< Divide clock by 128. */ +#define cmuClkDiv_256 256 /**< Divide clock by 256. */ +#define cmuClkDiv_512 512 /**< Divide clock by 512. */ +#define cmuClkDiv_1024 1024 /**< Divide clock by 1024. */ +#define cmuClkDiv_2048 2048 /**< Divide clock by 2048. */ +#define cmuClkDiv_4096 4096 /**< Divide clock by 4096. */ +#define cmuClkDiv_8192 8192 /**< Divide clock by 8192. */ +#define cmuClkDiv_16384 16384 /**< Divide clock by 16384. */ +#define cmuClkDiv_32768 32768 /**< Divide clock by 32768. */ + +/** Clock divider configuration */ +typedef uint32_t CMU_ClkDiv_TypeDef; + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +/** Clockprescaler configuration */ +typedef uint32_t CMU_ClkPresc_TypeDef; +#endif + +#if defined( _CMU_HFRCOCTRL_BAND_MASK ) +/** High frequency system RCO bands */ +typedef enum +{ + cmuHFRCOBand_1MHz = _CMU_HFRCOCTRL_BAND_1MHZ, /**< 1MHz HFRCO band */ + cmuHFRCOBand_7MHz = _CMU_HFRCOCTRL_BAND_7MHZ, /**< 7MHz HFRCO band */ + cmuHFRCOBand_11MHz = _CMU_HFRCOCTRL_BAND_11MHZ, /**< 11MHz HFRCO band */ + cmuHFRCOBand_14MHz = _CMU_HFRCOCTRL_BAND_14MHZ, /**< 14MHz HFRCO band */ + cmuHFRCOBand_21MHz = _CMU_HFRCOCTRL_BAND_21MHZ, /**< 21MHz HFRCO band */ +#if defined( CMU_HFRCOCTRL_BAND_28MHZ ) + cmuHFRCOBand_28MHz = _CMU_HFRCOCTRL_BAND_28MHZ, /**< 28MHz HFRCO band */ +#endif +} CMU_HFRCOBand_TypeDef; +#endif /* _CMU_HFRCOCTRL_BAND_MASK */ + +#if defined( _CMU_AUXHFRCOCTRL_BAND_MASK ) +/** AUX High frequency RCO bands */ +typedef enum +{ + cmuAUXHFRCOBand_1MHz = _CMU_AUXHFRCOCTRL_BAND_1MHZ, /**< 1MHz RC band */ + cmuAUXHFRCOBand_7MHz = _CMU_AUXHFRCOCTRL_BAND_7MHZ, /**< 7MHz RC band */ + cmuAUXHFRCOBand_11MHz = _CMU_AUXHFRCOCTRL_BAND_11MHZ, /**< 11MHz RC band */ + cmuAUXHFRCOBand_14MHz = _CMU_AUXHFRCOCTRL_BAND_14MHZ, /**< 14MHz RC band */ + cmuAUXHFRCOBand_21MHz = _CMU_AUXHFRCOCTRL_BAND_21MHZ, /**< 21MHz RC band */ +#if defined( CMU_AUXHFRCOCTRL_BAND_28MHZ ) + cmuAUXHFRCOBand_28MHz = _CMU_AUXHFRCOCTRL_BAND_28MHZ, /**< 28MHz RC band */ +#endif +} CMU_AUXHFRCOBand_TypeDef; +#endif + +#if defined( _CMU_USHFRCOCONF_BAND_MASK ) +/** USB High frequency RC bands. */ +typedef enum +{ + /** 24MHz RC band. */ + cmuUSHFRCOBand_24MHz = _CMU_USHFRCOCONF_BAND_24MHZ, + /** 48MHz RC band. */ + cmuUSHFRCOBand_48MHz = _CMU_USHFRCOCONF_BAND_48MHZ, +} CMU_USHFRCOBand_TypeDef; +#endif + +#if defined( _CMU_HFRCOCTRL_FREQRANGE_MASK ) +/** High frequency system RCO bands */ +typedef enum +{ + cmuHFRCOFreq_1M0Hz = 1000000U, /**< 1MHz RC band */ + cmuHFRCOFreq_2M0Hz = 2000000U, /**< 2MHz RC band */ + cmuHFRCOFreq_4M0Hz = 4000000U, /**< 4MHz RC band */ + cmuHFRCOFreq_7M0Hz = 7000000U, /**< 7MHz RC band */ + cmuHFRCOFreq_13M0Hz = 13000000U, /**< 13MHz RC band */ + cmuHFRCOFreq_16M0Hz = 16000000U, /**< 16MHz RC band */ + cmuHFRCOFreq_19M0Hz = 19000000U, /**< 19MHz RC band */ + cmuHFRCOFreq_26M0Hz = 26000000U, /**< 26MHz RC band */ + cmuHFRCOFreq_32M0Hz = 32000000U, /**< 32MHz RC band */ + cmuHFRCOFreq_38M0Hz = 38000000U, /**< 38MHz RC band */ + cmuHFRCOFreq_UserDefined = 0, +} CMU_HFRCOFreq_TypeDef; +#define CMU_HFRCO_MIN cmuHFRCOFreq_1M0Hz +#define CMU_HFRCO_MAX cmuHFRCOFreq_38M0Hz +#endif + +#if defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) +/** AUX High frequency RCO bands */ +typedef enum +{ + cmuAUXHFRCOFreq_1M0Hz = 1000000U, /**< 1MHz RC band */ + cmuAUXHFRCOFreq_2M0Hz = 2000000U, /**< 2MHz RC band */ + cmuAUXHFRCOFreq_4M0Hz = 4000000U, /**< 4MHz RC band */ + cmuAUXHFRCOFreq_7M0Hz = 7000000U, /**< 7MHz RC band */ + cmuAUXHFRCOFreq_13M0Hz = 13000000U, /**< 13MHz RC band */ + cmuAUXHFRCOFreq_16M0Hz = 16000000U, /**< 16MHz RC band */ + cmuAUXHFRCOFreq_19M0Hz = 19000000U, /**< 19MHz RC band */ + cmuAUXHFRCOFreq_26M0Hz = 26000000U, /**< 26MHz RC band */ + cmuAUXHFRCOFreq_32M0Hz = 32000000U, /**< 32MHz RC band */ + cmuAUXHFRCOFreq_38M0Hz = 38000000U, /**< 38MHz RC band */ + cmuAUXHFRCOFreq_UserDefined = 0, +} CMU_AUXHFRCOFreq_TypeDef; +#define CMU_AUXHFRCO_MIN cmuAUXHFRCOFreq_1M0Hz +#define CMU_AUXHFRCO_MAX cmuAUXHFRCOFreq_38M0Hz +#endif + + +/** Clock points in CMU. Please refer to CMU overview in reference manual. */ +typedef enum +{ + /*******************/ + /* HF clock branch */ + /*******************/ + + /** High frequency clock */ +#if defined( _CMU_CTRL_HFCLKDIV_MASK ) \ + || defined( _CMU_HFPRESC_MASK ) + cmuClock_HF = (CMU_HFCLKDIV_REG << CMU_DIV_REG_POS) + | (CMU_HFCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#else + cmuClock_HF = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_HFCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + + /** Debug clock */ + cmuClock_DBG = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_DBGCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_DBG_CLK_BRANCH << CMU_CLK_BRANCH_POS), + + /** AUX clock */ + cmuClock_AUX = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_AUX_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( _CMU_HFEXPPRESC_MASK ) + /**********************/ + /* HF export sub-branch */ + /**********************/ + + /** Export clock */ + cmuClock_EXPORT = (CMU_HFEXPPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_HFEXP_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( _CMU_HFBUSCLKEN0_MASK ) +/**********************************/ + /* HF bus clock sub-branch */ + /**********************************/ + + /** High frequency bus clock. */ + cmuClock_BUS = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_HFBUSCLKEN0_CRYPTO ) + /** Cryptography accelerator clock. */ + cmuClock_CRYPTO = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFBUSCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFBUSCLKEN0_CRYPTO_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFBUSCLKEN0_LDMA ) + /** Direct memory access controller clock. */ + cmuClock_LDMA = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFBUSCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFBUSCLKEN0_LDMA_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFBUSCLKEN0_GPCRC ) + /** General purpose cyclic redundancy checksum clock. */ + cmuClock_GPCRC = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFBUSCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFBUSCLKEN0_GPCRC_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFBUSCLKEN0_GPIO ) + /** General purpose input/output clock. */ + cmuClock_GPIO = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFBUSCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFBUSCLKEN0_GPIO_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + + /** Low energy clocking module clock. */ + cmuClock_CORELE = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFBUSCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFBUSCLKEN0_LE_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_HFBUSCLKEN0_PRS ) + /** Peripheral reflex system clock. */ + cmuClock_PRS = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFBUSCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFBUSCLKEN0_PRS_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif +#endif + + /**********************************/ + /* HF peripheral clock sub-branch */ + /**********************************/ + + /** High frequency peripheral clock */ +#if defined( _CMU_HFPRESC_MASK ) + cmuClock_HFPER = (CMU_HFPERPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_CTRL_EN_REG << CMU_EN_REG_POS) + | (_CMU_CTRL_HFPERCLKEN_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#else + cmuClock_HFPER = (CMU_HFPERCLKDIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKDIV_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKDIV_HFPERCLKEN_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USART0 ) + /** Universal sync/async receiver/transmitter 0 clock. */ + cmuClock_USART0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USART0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USARTRF0 ) + /** Universal sync/async receiver/transmitter 0 clock. */ + cmuClock_USARTRF0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USARTRF0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USARTRF1 ) + /** Universal sync/async receiver/transmitter 0 clock. */ + cmuClock_USARTRF1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USARTRF1_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USART1 ) + /** Universal sync/async receiver/transmitter 1 clock. */ + cmuClock_USART1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USART1_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USART2 ) + /** Universal sync/async receiver/transmitter 2 clock. */ + cmuClock_USART2 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USART2_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USART3 ) + /** Universal sync/async receiver/transmitter 3 clock. */ + cmuClock_USART3 = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USART3_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USART4 ) + /** Universal sync/async receiver/transmitter 4 clock. */ + cmuClock_USART4 = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USART4_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_USART5 ) + /** Universal sync/async receiver/transmitter 5 clock. */ + cmuClock_USART5 = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_USART5_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + + +#if defined( CMU_HFPERCLKEN0_UART0 ) + /** Universal async receiver/transmitter 0 clock. */ + cmuClock_UART0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_UART0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_UART1 ) + /** Universal async receiver/transmitter 1 clock. */ + cmuClock_UART1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_UART1_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_TIMER0 ) + /** Timer 0 clock. */ + cmuClock_TIMER0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_TIMER0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_TIMER1 ) + /** Timer 1 clock. */ + cmuClock_TIMER1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_TIMER1_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_TIMER2 ) + /** Timer 2 clock. */ + cmuClock_TIMER2 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_TIMER2_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_TIMER3 ) + /** Timer 3 clock. */ + cmuClock_TIMER3 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_TIMER3_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_CRYOTIMER ) + /** CRYOtimer clock. */ + cmuClock_CRYOTIMER = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_CRYOTIMER_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_ACMP0 ) + /** Analog comparator 0 clock. */ + cmuClock_ACMP0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_ACMP0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_ACMP1 ) + /** Analog comparator 1 clock. */ + cmuClock_ACMP1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_ACMP1_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_PRS ) + /** Peripheral reflex system clock. */ + cmuClock_PRS = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_PRS_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_DAC0 ) + /** Digital to analog converter 0 clock. */ + cmuClock_DAC0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_DAC0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_IDAC0 ) + /** Digital to analog converter 0 clock. */ + cmuClock_IDAC0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_IDAC0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_GPIO ) + /** General purpose input/output clock. */ + cmuClock_GPIO = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_GPIO_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_VCMP ) + /** Voltage comparator clock. */ + cmuClock_VCMP = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_VCMP_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_ADC0 ) + /** Analog to digital converter 0 clock. */ + cmuClock_ADC0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_ADC0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_I2C0 ) + /** I2C 0 clock. */ + cmuClock_I2C0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_I2C0_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_I2C1 ) + /** I2C 1 clock. */ + cmuClock_I2C1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_I2C1_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFPERCLKEN0_I2C2 ) + /** I2C 2 clock. */ + cmuClock_I2C2 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFPERCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFPERCLKEN0_I2C2_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + + /**********************/ + /* HF core sub-branch */ + /**********************/ + + /** Core clock */ + cmuClock_CORE = (CMU_HFCORECLKDIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_HFCORECLKEN0_AES ) + /** Advanced encryption standard accelerator clock. */ + cmuClock_AES = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFCORECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFCORECLKEN0_AES_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFCORECLKEN0_DMA ) + /** Direct memory access controller clock. */ + cmuClock_DMA = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFCORECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFCORECLKEN0_DMA_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFCORECLKEN0_LE ) +/** Low energy clocking module clock. */ + cmuClock_CORELE = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFCORECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFCORECLKEN0_LE_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFCORECLKEN0_EBI ) + /** External bus interface clock. */ + cmuClock_EBI = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFCORECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFCORECLKEN0_EBI_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFCORECLKEN0_USBC ) + /** USB Core clock. */ + cmuClock_USBC = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_USBCCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFCORECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFCORECLKEN0_USBC_SHIFT << CMU_EN_BIT_POS) + | (CMU_USBC_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#endif + +#if defined( CMU_HFCORECLKEN0_USB ) + /** USB clock. */ + cmuClock_USB = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFCORECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFCORECLKEN0_USB_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_CTRL_HFRADIOCLKEN ) + /**********************************/ + /* HF radio clock sub-branch */ + /**********************************/ + + /** High frequency radio clock. */ + cmuClock_RADIO = (CMU_HFRADIOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_CTRL_EN_REG << CMU_EN_REG_POS) + | (_CMU_CTRL_HFRADIOCLKEN_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_HFRADIOCLKEN0_MODEM ) + /** Modulator/demodulator clock. */ + cmuClock_MODEM = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_MODEM_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_PROTIMER ) + /** Protocol timer clock. */ + cmuClock_PROTIMER = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_PROTIMER_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_CRC ) + /** Cyclic Redundancy Check clock. */ + cmuClock_CRC = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_CRC_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_AGC ) + /** Automatic Gain Control clock. */ + cmuClock_AGC = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_AGC_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_FRC ) + /** Frame Controller clock. */ + cmuClock_FRC = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_FRC_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_SYNTH ) + /** Frequency Synthesizer clock. */ + cmuClock_SYNTH = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_SYNTH_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_BUFC ) + /** Buffer Controller Check clock. */ + cmuClock_BUFC = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_BUFC_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_HFRADIOCLKEN0_RAC ) + /** Radio Controller clock. */ + cmuClock_RAC = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_HFRADIOCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_HFRADIOCLKEN0_RAC_SHIFT << CMU_EN_BIT_POS) + | (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif +#endif + + /***************/ + /* LF A branch */ + /***************/ + + /** Low frequency A clock */ + cmuClock_LFA = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_LFACLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_LFACLKEN0_RTC ) + /** Real time counter clock. */ + cmuClock_RTC = (CMU_LFAPRESC0_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFACLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFACLKEN0_RTC_SHIFT << CMU_EN_BIT_POS) + | (CMU_RTC_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_LFACLKEN0_LETIMER0 ) + /** Low energy timer 0 clock. */ + cmuClock_LETIMER0 = (CMU_LFAPRESC0_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFACLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFACLKEN0_LETIMER0_SHIFT << CMU_EN_BIT_POS) + | (CMU_LETIMER_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_LFACLKEN0_LCD ) + /** Liquid crystal display, pre FDIV clock. */ + cmuClock_LCDpre = (CMU_LFAPRESC0_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_LCDPRE_CLK_BRANCH << CMU_CLK_BRANCH_POS), + + /** Liquid crystal display clock. Please notice that FDIV prescaler + * must be set by special API. */ + cmuClock_LCD = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFACLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFACLKEN0_LCD_SHIFT << CMU_EN_BIT_POS) + | (CMU_LCD_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_PCNTCTRL_PCNT0CLKEN ) + /** Pulse counter 0 clock. */ + cmuClock_PCNT0 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_PCNT_EN_REG << CMU_EN_REG_POS) + | (_CMU_PCNTCTRL_PCNT0CLKEN_SHIFT << CMU_EN_BIT_POS) + | (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_PCNTCTRL_PCNT1CLKEN ) + /** Pulse counter 1 clock. */ + cmuClock_PCNT1 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_PCNT_EN_REG << CMU_EN_REG_POS) + | (_CMU_PCNTCTRL_PCNT1CLKEN_SHIFT << CMU_EN_BIT_POS) + | (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_PCNTCTRL_PCNT2CLKEN ) + /** Pulse counter 2 clock. */ + cmuClock_PCNT2 = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_PCNT_EN_REG << CMU_EN_REG_POS) + | (_CMU_PCNTCTRL_PCNT2CLKEN_SHIFT << CMU_EN_BIT_POS) + | (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif +#if defined( CMU_LFACLKEN0_LESENSE ) + /** LESENSE clock. */ + cmuClock_LESENSE = (CMU_LFAPRESC0_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFACLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFACLKEN0_LESENSE_SHIFT << CMU_EN_BIT_POS) + | (CMU_LESENSE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + + /***************/ + /* LF B branch */ + /***************/ + + /** Low frequency B clock */ + cmuClock_LFB = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_LFBCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_LFB_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_LFBCLKEN0_LEUART0 ) + /** Low energy universal asynchronous receiver/transmitter 0 clock. */ + cmuClock_LEUART0 = (CMU_LFBPRESC0_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFBCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFBCLKEN0_LEUART0_SHIFT << CMU_EN_BIT_POS) + | (CMU_LEUART0_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( CMU_LFBCLKEN0_LEUART1 ) + /** Low energy universal asynchronous receiver/transmitter 1 clock. */ + cmuClock_LEUART1 = (CMU_LFBPRESC0_REG << CMU_DIV_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFBCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFBCLKEN0_LEUART1_SHIFT << CMU_EN_BIT_POS) + | (CMU_LEUART1_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif + +#if defined( _CMU_LFCCLKEN0_MASK ) + /***************/ + /* LF C branch */ + /***************/ + + /** Low frequency C clock */ + cmuClock_LFC = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_LFCCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_LFC_CLK_BRANCH << CMU_CLK_BRANCH_POS), + +#if defined( CMU_LFCCLKEN0_USBLE ) + /** USB LE clock. */ + cmuClock_USBLE = (CMU_NODIV_REG << CMU_DIV_REG_POS) + | (CMU_LFCCLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFCCLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFCCLKEN0_USBLE_SHIFT << CMU_EN_BIT_POS) + | (CMU_USBLE_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif +#endif + +#if defined( _CMU_LFECLKEN0_MASK ) + /***************/ + /* LF E branch */ + /***************/ + + /** Low frequency A clock */ + cmuClock_LFE = (CMU_NOPRESC_REG << CMU_PRESC_REG_POS) + | (CMU_LFECLKSEL_REG << CMU_SEL_REG_POS) + | (CMU_NO_EN_REG << CMU_EN_REG_POS) + | (0 << CMU_EN_BIT_POS) + | (CMU_LFE_CLK_BRANCH << CMU_CLK_BRANCH_POS), + + /** Real time counter and calendar clock. */ +#if defined ( CMU_LFECLKEN0_RTCC ) + cmuClock_RTCC = (CMU_LFEPRESC0_REG << CMU_PRESC_REG_POS) + | (CMU_NOSEL_REG << CMU_SEL_REG_POS) + | (CMU_LFECLKEN0_EN_REG << CMU_EN_REG_POS) + | (_CMU_LFECLKEN0_RTCC_SHIFT << CMU_EN_BIT_POS) + | (CMU_RTCC_CLK_BRANCH << CMU_CLK_BRANCH_POS), +#endif +#endif + +} CMU_Clock_TypeDef; + + +/** Oscillator types. */ +typedef enum +{ + cmuOsc_LFXO, /**< Low frequency crystal oscillator. */ + cmuOsc_LFRCO, /**< Low frequency RC oscillator. */ + cmuOsc_HFXO, /**< High frequency crystal oscillator. */ + cmuOsc_HFRCO, /**< High frequency RC oscillator. */ + cmuOsc_AUXHFRCO, /**< Auxiliary high frequency RC oscillator. */ +#if defined( _CMU_STATUS_USHFRCOENS_MASK ) + cmuOsc_USHFRCO, /**< USB high frequency RC oscillator */ +#endif +#if defined( CMU_LFCLKSEL_LFAE_ULFRCO ) || defined( CMU_LFACLKSEL_LFA_ULFRCO ) + cmuOsc_ULFRCO /**< Ultra low frequency RC oscillator. */ +#endif +} CMU_Osc_TypeDef; + + +/** Selectable clock sources. */ +typedef enum +{ + cmuSelect_Error, /**< Usage error. */ + cmuSelect_Disabled, /**< Clock selector disabled. */ + cmuSelect_LFXO, /**< Low frequency crystal oscillator. */ + cmuSelect_LFRCO, /**< Low frequency RC oscillator. */ + cmuSelect_HFXO, /**< High frequency crystal oscillator. */ + cmuSelect_HFRCO, /**< High frequency RC oscillator. */ +#if defined( CMU_LFACLKSEL_LFA_HFCLKLE ) || defined( CMU_LFBCLKSEL_LFB_HFCLKLE ) + cmuSelect_HFCLKLE, /**< High frequency clock to LE divided by 2 or 4. */ +#endif + cmuSelect_CORELEDIV2, /**< Core low energy clock divided by 2. */ + cmuSelect_AUXHFRCO, /**< Auxilliary clock source can be used for debug clock */ + cmuSelect_HFCLK, /**< Divided HFCLK on Giant for debug clock, undivided on Tiny Gecko and for USBC (not used on Gecko) */ +#if defined( CMU_STATUS_USHFRCOENS ) + cmuSelect_USHFRCO, /**< USB high frequency RC oscillator */ +#endif +#if defined( CMU_CMD_HFCLKSEL_USHFRCODIV2 ) + cmuSelect_USHFRCODIV2,/**< USB high frequency RC oscillator */ +#endif +#if defined( CMU_LFCLKSEL_LFAE_ULFRCO ) || defined( CMU_LFACLKSEL_LFA_ULFRCO ) + cmuSelect_ULFRCO, /**< Ultra low frequency RC oscillator. */ +#endif +} CMU_Select_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +#if defined( _CMU_LFXOCTRL_MASK ) +/** LFXO initialization structure. Init values should be obtained from a configuration tool, + app note or xtal datasheet */ +typedef struct +{ + uint8_t ctune; /**< CTUNE (load capacitance) value */ + uint8_t gain; /**< Gain / max startup margin */ + uint8_t timeout; /**< Startup delay */ +} CMU_LFXOInit_TypeDef; + +/** Default LFXO initialization */ +#define CMU_LFXOINIT_DEFAULT \ + { \ + _CMU_LFXOCTRL_TUNING_DEFAULT, \ + _CMU_LFXOCTRL_GAIN_DEFAULT, \ + _CMU_LFXOCTRL_TIMEOUT_DEFAULT, \ + } +#endif + +#if defined( _CMU_HFXOCTRL_MASK ) +/** HFXO initialization structure. Init values should be obtained from a configuration tool, + app note or xtal datasheet */ +typedef struct +{ + bool lowPowerMode; /**< Enable low-power mode */ + bool autoStartEm01; /**< Enable auto-start on entry to EM0/1 */ + bool autoSelEm01; /**< Enable auto-select on entry to EM0/1 */ + bool autoStartSelOnRacWakeup; /**< Enable auto-start and select on RAC wakeup */ + uint16_t ctuneStartup; /**< Startup phase CTUNE (load capacitance) value */ + uint16_t ctuneSteadyState; /**< Steady-state phase CTUNE (load capacitance) value */ + uint8_t regIshStartup; /**< Shunt startup current */ + uint8_t regIshSteadyState; /**< Shunt steady-state current */ + uint8_t xoCoreBiasTrimStartup; /**< Startup XO core bias current trim */ + uint8_t xoCoreBiasTrimSteadyState; /**< Steady-state XO core bias current trim */ + uint8_t thresholdPeakDetect; /**< Peak detection threshold */ + uint8_t timeoutShuntOptimization; /**< Timeout - shunt optimization */ + uint8_t timeoutPeakDetect; /**< Timeout - peak detection */ + uint8_t timeoutWarmSteady; /**< Timeout - warmup */ + uint8_t timeoutSteady; /**< Timeout - steady-state */ + uint8_t timeoutStartup; /**< Timeout - startup */ +} CMU_HFXOInit_TypeDef; + +/** Default HFXO initialization */ +#if defined( _EFR_DEVICE ) +#define CMU_HFXOINIT_DEFAULT \ +{ \ + false, /* Low-noise mode for EFR32 */ \ + false, /* Disable auto-start on EM0/1 entry */ \ + false, /* Disable auto-select on EM0/1 entry */ \ + false, /* Disable auto-start and select on RAC wakeup */ \ + _CMU_HFXOSTARTUPCTRL_CTUNE_DEFAULT, \ + _CMU_HFXOSTEADYSTATECTRL_CTUNE_DEFAULT, \ + _CMU_HFXOSTARTUPCTRL_REGISHWARM_DEFAULT, \ + _CMU_HFXOSTEADYSTATECTRL_REGISH_DEFAULT, \ + _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_DEFAULT, \ + 0x7, /* Recommended steady-state XO core bias current */ \ + 0x6, /* Recommended peak detection threshold */ \ + _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_DEFAULT, \ + 0xA, /* Recommended peak detection timeout */ \ + _CMU_HFXOTIMEOUTCTRL_WARMSTEADYTIMEOUT_DEFAULT, \ + _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_DEFAULT, \ + _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_DEFAULT, \ +} +/* EFM32 device */ +#else +#define CMU_HFXOINIT_DEFAULT \ +{ \ + true, /* Low-power mode for EFM32 */ \ + false, /* Disable auto-start on EM0/1 entry */ \ + false, /* Disable auto-select on EM0/1 entry */ \ + false, /* Disable auto-start and select on RAC wakeup */ \ + _CMU_HFXOSTARTUPCTRL_CTUNE_DEFAULT, \ + _CMU_HFXOSTEADYSTATECTRL_CTUNE_DEFAULT, \ + _CMU_HFXOSTARTUPCTRL_REGISHWARM_DEFAULT, \ + _CMU_HFXOSTEADYSTATECTRL_REGISH_DEFAULT, \ + _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_DEFAULT, \ + 0x7, /* Recommended steady-state osc core bias current */ \ + 0x6, /* Recommended peak detection threshold */ \ + _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_DEFAULT, \ + 0xA, /* Recommended peak detection timeout */ \ + _CMU_HFXOTIMEOUTCTRL_WARMSTEADYTIMEOUT_DEFAULT, \ + _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_DEFAULT, \ + _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_DEFAULT, \ +} +#endif +#endif /* _CMU_HFXOCTRL_MASK */ + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +#if defined( _CMU_AUXHFRCOCTRL_BAND_MASK ) +CMU_AUXHFRCOBand_TypeDef CMU_AUXHFRCOBandGet(void); +void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOBand_TypeDef band); + +#elif defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) +CMU_AUXHFRCOFreq_TypeDef CMU_AUXHFRCOFreqGet(void); +void CMU_AUXHFRCOFreqSet(CMU_AUXHFRCOFreq_TypeDef freqEnum); +#endif + +uint32_t CMU_Calibrate(uint32_t HFCycles, CMU_Osc_TypeDef reference); + +#if defined( _CMU_CALCTRL_UPSEL_MASK ) && defined( _CMU_CALCTRL_DOWNSEL_MASK ) +void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel, + CMU_Osc_TypeDef upSel); +#endif + +uint32_t CMU_CalibrateCountGet(void); +void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable); +CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock); +void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div); +uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock); + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, uint32_t presc); +uint32_t CMU_ClockPrescGet(CMU_Clock_TypeDef clock); +#endif + +void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref); +CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock); +void CMU_FreezeEnable(bool enable); + +#if defined( _CMU_HFRCOCTRL_BAND_MASK ) +CMU_HFRCOBand_TypeDef CMU_HFRCOBandGet(void); +void CMU_HFRCOBandSet(CMU_HFRCOBand_TypeDef band); + +#elif defined( _CMU_HFRCOCTRL_FREQRANGE_MASK ) +CMU_HFRCOFreq_TypeDef CMU_HFRCOFreqGet(void); +void CMU_HFRCOFreqSet(CMU_HFRCOFreq_TypeDef freqEnum); +#endif + +uint32_t CMU_HFRCOStartupDelayGet(void); +void CMU_HFRCOStartupDelaySet(uint32_t delay); + +#if defined( _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK ) +void CMU_HFXOAutostartEnable(bool enRACStartSel, + bool enEM0EM1Start, + bool enEM0EM1StartSel); +#endif + +#if defined( _CMU_HFXOCTRL_MASK ) +void CMU_HFXOInit(CMU_HFXOInit_TypeDef *hfxoInit); +#endif + + +uint32_t CMU_LCDClkFDIVGet(void); +void CMU_LCDClkFDIVSet(uint32_t div); + +#if defined( _CMU_LFXOCTRL_MASK ) +void CMU_LFXOInit(CMU_LFXOInit_TypeDef *lfxoInit); +#endif + +void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait); +uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc); +void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val); +bool CMU_PCNTClockExternalGet(unsigned int instance); +void CMU_PCNTClockExternalSet(unsigned int instance, bool external); + +#if defined( _CMU_USHFRCOCONF_BAND_MASK ) +CMU_USHFRCOBand_TypeDef CMU_USHFRCOBandGet(void); +void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band); +#endif + + +#if defined( CMU_CALCTRL_CONT ) +/***************************************************************************//** + * @brief + * Configures continuous calibration mode + * @param[in] enable + * If true, enables continuous calibration, if false disables continuous + * calibrartion + ******************************************************************************/ +__STATIC_INLINE void CMU_CalibrateCont(bool enable) +{ + BUS_RegBitWrite(&(CMU->CALCTRL), _CMU_CALCTRL_CONT_SHIFT, enable); +} +#endif + + +/***************************************************************************//** + * @brief + * Starts calibration + * @note + * This call is usually invoked after CMU_CalibrateConfig() and possibly + * CMU_CalibrateCont() + ******************************************************************************/ +__STATIC_INLINE void CMU_CalibrateStart(void) +{ + CMU->CMD = CMU_CMD_CALSTART; +} + + +#if defined( CMU_CMD_CALSTOP ) +/***************************************************************************//** + * @brief + * Stop the calibration counters + ******************************************************************************/ +__STATIC_INLINE void CMU_CalibrateStop(void) +{ + CMU->CMD = CMU_CMD_CALSTOP; +} +#endif + + +/***************************************************************************//** + * @brief + * Convert dividend to logarithmic value. Only works for even + * numbers equal to 2^n. + * + * @param[in] div + * Unscaled dividend. + * + * @return + * Logarithm of 2, as used by fixed prescalers. + ******************************************************************************/ +__STATIC_INLINE uint32_t CMU_DivToLog2(CMU_ClkDiv_TypeDef div) +{ + uint32_t log2; + + /* Fixed 2^n prescalers take argument of 32768 or less. */ + EFM_ASSERT((div > 0U) && (div <= 32768U)); + + /* Count leading zeroes and "reverse" result */ + log2 = (31U - __CLZ(div)); + + return log2; +} + + +/***************************************************************************//** + * @brief + * Clear one or more pending CMU interrupts. + * + * @param[in] flags + * CMU interrupt sources to clear. + ******************************************************************************/ +__STATIC_INLINE void CMU_IntClear(uint32_t flags) +{ + CMU->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more CMU interrupts. + * + * @param[in] flags + * CMU interrupt sources to disable. + ******************************************************************************/ +__STATIC_INLINE void CMU_IntDisable(uint32_t flags) +{ + CMU->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more CMU interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using CMU_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * CMU interrupt sources to enable. + ******************************************************************************/ +__STATIC_INLINE void CMU_IntEnable(uint32_t flags) +{ + CMU->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending CMU interrupts. + * + * @return + * CMU interrupt sources pending. + ******************************************************************************/ +__STATIC_INLINE uint32_t CMU_IntGet(void) +{ + return CMU->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending CMU interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending and enabled CMU interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in CMU_IEN and + * - the pending interrupt flags CMU_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t CMU_IntGetEnabled(void) +{ + uint32_t ien; + + ien = CMU->IEN; + return CMU->IF & ien; +} + + +/**************************************************************************//** + * @brief + * Set one or more pending CMU interrupts. + * + * @param[in] flags + * CMU interrupt sources to set to pending. + *****************************************************************************/ +__STATIC_INLINE void CMU_IntSet(uint32_t flags) +{ + CMU->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Lock the CMU in order to protect some of its registers against unintended + * modification. + * + * @details + * Please refer to the reference manual for CMU registers that will be + * locked. + * + * @note + * If locking the CMU registers, they must be unlocked prior to using any + * CMU API functions modifying CMU registers protected by the lock. + ******************************************************************************/ +__STATIC_INLINE void CMU_Lock(void) +{ + CMU->LOCK = CMU_LOCK_LOCKKEY_LOCK; +} + + +/***************************************************************************//** + * @brief + * Convert logarithm of 2 prescaler to division factor. + * + * @param[in] log2 + * Logarithm of 2, as used by fixed prescalers. + * + * @return + * Dividend. + ******************************************************************************/ +__STATIC_INLINE uint32_t CMU_Log2ToDiv(uint32_t log2) +{ + return 1 << log2; +} + + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +/***************************************************************************//** + * @brief + * Convert prescaler dividend to logarithmic value. Only works for even + * numbers equal to 2^n. + * + * @param[in] presc + * Unscaled dividend (dividend = presc + 1). + * + * @return + * Logarithm of 2, as used by fixed 2^n prescalers. + ******************************************************************************/ +__STATIC_INLINE uint32_t CMU_PrescToLog2(CMU_ClkPresc_TypeDef presc) +{ + uint32_t log2; + + /* Integer prescalers take argument less than 32768. */ + EFM_ASSERT(presc < 32768U); + + /* Count leading zeroes and "reverse" result */ + log2 = (31U - __CLZ(presc + 1)); + + /* Check that presc is a 2^n number */ + EFM_ASSERT(presc == (CMU_Log2ToDiv(log2) - 1)); + + return log2; +} +#endif + + +/***************************************************************************//** + * @brief + * Unlock the CMU so that writing to locked registers again is possible. + ******************************************************************************/ +__STATIC_INLINE void CMU_Unlock(void) +{ + CMU->LOCK = CMU_LOCK_LOCKKEY_UNLOCK; +} + +/** @} (end addtogroup CMU) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( CMU_PRESENT ) */ +#endif /* __SILICON_LABS_EM_CMU_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_common.h b/cpu/efm32_common/emlib/inc/em_common.h new file mode 100644 index 0000000000000..e2e9fe6f32d36 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_common.h @@ -0,0 +1,140 @@ +/***************************************************************************//** + * @file em_common.h + * @brief Emlib general purpose utilities. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_COMMON_H__ +#define __SILICON_LABS_EM_COMMON_H__ + +#include "em_device.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup COMMON + * @brief Emlib general purpose utilities. + * @{ + ******************************************************************************/ + +#if !defined(__GNUC__) + +/** Macro for getting minimum value. */ +#define EFM32_MIN(a, b) ((a) < (b) ? (a) : (b)) +/** Macro for getting maximum value. */ +#define EFM32_MAX(a, b) ((a) > (b) ? (a) : (b)) + +/** Macros for handling packed structs. */ +#define STRINGIZE(X) #X +#define EFM32_PACK_START(X) _Pragma( STRINGIZE( pack( X ) ) ) +#define EFM32_PACK_END() _Pragma( "pack()" ) +#define __attribute__(...) + +#ifdef __CC_ARM +/** Macros for handling aligned structs. */ +#define EFM32_ALIGN(X) __align(X) +#endif +#ifdef __ICCARM__ +/** Macros for handling aligned structs. */ +#define EFM32_ALIGN(X) _Pragma( STRINGIZE( data_alignment=X ) ) +#endif + +#else // !defined(__GNUC__) + +/** Macro for getting minimum value. No sideeffects, a and b are evaluated once only. */ +#define EFM32_MIN(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b; }) +/** Macro for getting maximum value. No sideeffects, a and b are evaluated once only. */ +#define EFM32_MAX(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; }) + +/** Macro for handling packed structs. + * @n Use this macro before the struct definition. + * @n X denotes the maximum alignment of struct members. X is not supported on + * gcc, gcc always use 1 byte maximum alignment. + */ +#define EFM32_PACK_START( x ) + +/** Macro for handling packed structs. + * @n Use this macro after the struct definition. + * @n On gcc add __attribute__ ((packed)) after the closing } of the struct + * definition. + */ +#define EFM32_PACK_END() + +/** Macro for aligning a variable. + * @n Use this macro before the variable definition. + * @n X denotes the storage alignment value in bytes. + * @n On gcc use __attribute__ ((aligned(X))) before the ; on normal variables. + * Use __attribute__ ((aligned(X))) before the opening { on struct variables. + */ +#define EFM32_ALIGN(X) + +#endif // !defined(__GNUC__) + +/***************************************************************************//** + * @brief + * Count trailing number of zero's. + * + * @note + * Disabling SWDClk will disable the debug interface, which may result in + * a lockout if done early in startup (before debugger is able to halt core). + * + * @param[in] value + * Data value to check for number of trailing zero bits. + * + * @return + * Number of trailing zero's in value. + ******************************************************************************/ +__STATIC_INLINE uint32_t EFM32_CTZ(uint32_t value) +{ +#if (__CORTEX_M >= 3) + return __CLZ(__RBIT(value)); + +#else + uint32_t zeros; + for(zeros=0; (zeros<32) && ((value&0x1) == 0); zeros++, value>>=1); + return zeros; +#endif +} + +/** @} (end addtogroup COMMON) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_COMMON_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_crc.h b/cpu/efm32_common/emlib/inc/em_crc.h new file mode 100644 index 0000000000000..a379edf82c870 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_crc.h @@ -0,0 +1,294 @@ +/***************************************************************************//** + * @file + * @brief Cyclic Redundancy Check (CRC) API. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_CRC_H__ +#define __SILICON_LABS_EM_CRC_H__ + +#include "em_device.h" +#if defined(CRC_COUNT) && (CRC_COUNT > 0) + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CRC + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** CRC width values. */ +typedef enum +{ + /** 8 bit (1 byte) CRC code. */ + crcWidth8 = CRC_CTRL_CRCWIDTH_CRCWIDTH8, + + /** 16 bit (2 byte) CRC code. */ + crcWidth16 = CRC_CTRL_CRCWIDTH_CRCWIDTH16, + + /** 24 bit (3 byte) CRC code. */ + crcWidth24 = CRC_CTRL_CRCWIDTH_CRCWIDTH24, + + /** 32 bit (4 byte) CRC code. */ + crcWidth32 = CRC_CTRL_CRCWIDTH_CRCWIDTH32 +} CRC_Width_TypeDef; + + +/** CRC byte reverse values. */ +typedef enum +{ + /** Most significant CRC bytes are transferred first over air via the Frame + * Controller (FRC). */ + crcByteOrderNormal = CRC_CTRL_BYTEREVERSE_NORMAL, + + /** Least significant CRC bytes are transferred first over air via the Frame + * Controller (FRC). */ + crcByteOrderReversed = CRC_CTRL_BYTEREVERSE_REVERSED +} CRC_ByteOrder_TypeDef; + + +/** CRC bit order values. */ +typedef enum +{ + /** Least significant data bit (LSB) is fed first to the CRC generator. */ + crcBitOrderLSBFirst = CRC_CTRL_INPUTBITORDER_LSBFIRST, + + /** Most significant data bit (MSB) is fed first to the CRC generator. */ + crcBitOrderMSBFirst = CRC_CTRL_INPUTBITORDER_MSBFIRST +} CRC_BitOrder_TypeDef; + + +/** CRC bit reverse values. */ +typedef enum +{ + /** The bit ordering of CRC data is the same as defined by the BITORDER field + * in the Frame Controller. */ + crcBitReverseNormal = CRC_CTRL_BITREVERSE_NORMAL, + + /** The bit ordering of CRC data is the opposite as defined by the BITORDER + * field in the Frame Controller. */ + crcBitReverseReversed = CRC_CTRL_BITREVERSE_REVERSED +} CRC_BitReverse_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** CRC initialization structure. */ +typedef struct +{ + /** Width of the CRC code. */ + CRC_Width_TypeDef crcWidth; + + /** CRC polynomial value. This value defines POLY[31:0], which is used as the + * polynomial (in reversed order) during the CRC calculation. If the CRC + * width is less than 32 bits, the most significant part of this register + * remains unused. + * - Set the bit to 1 in the register to get the corresponding degree term + * appear in the polynomial with a coefficient of 1. + * - Set the bit to 0 in the register to get the corresponding degree term + * appear in the polynomial with a coefficient of 0. + * Note: If a CRC polynomial of size less than 32 bits is to be used, the + * polynomial value must be shifted so that the highest degree term is + * located in DATA[0]! + * Please refer to the CRC sub-chapter "CRC Polynomial" in the documentation + * for more details! */ + uint32_t crcPoly; + + /** CRC initialization value. Loaded into the CRC_DATA register upon issuing + * the INIT command by calling CRC_InitCommand(), or when the Frame + * Controller (FRC) uses the CRC for automatic CRC calculation and + * verification. */ + uint32_t initValue; + + /** Number of bits per input word. This value defines the number of valid + * input bits in the CRC_INPUTDATA register, or in data coming from the Frame + * Controller (FRC). The number of bits in each word equals to + * (BITSPERWORD + EXTRABITSPERWORD + 1), where EXTRABITSPERWORD is taken from + * the currently active Frame Control Descriptor (FCD). */ + uint8_t bitsPerWord; + + /** If true, the byte order is reversed and the least significant CRC bytes + * are transferred first over the air. (description TBD) */ + CRC_ByteOrder_TypeDef byteReverse; + + /** Bit order. Defines the order in which bits are fed to the CRC generator. + * This setting applies both to data written to the CRC_INPUTDATA register, + * and data coming from the Frame Controller (FRC). */ + CRC_BitOrder_TypeDef inputBitOrder; + + /** Output bit reverse. In most cases, the bit ordering of the CRC value + * corresponds to the bit ordering of other data transmitted over air. When + * set, the BITREVERSE field has the possibility to reverse this bit ordering + * to comply with some protocols. Note that this field does not affect the + * way the CRC value is calculated, only how it is transmitted over air. */ + CRC_BitReverse_TypeDef bitReverse; + + /** Enable/disable CRC input data padding. When set, CRC input data is zero- + * padded, such that the number of bytes over which the CRC value is + * calculated at least equals the length of the calculated CRC value. If not + * set, no zero-padding of CRC input data is applied. */ + bool inputPadding; + + /** If true, CRC input is inverted. */ + bool invInput; + + /** If true, CRC output to the Frame Controller (FRC) is inverted. */ + bool invOutput; +} CRC_Init_TypeDef; + +/** Default configuration for CRC_Init_TypeDef structure. */ +#define CRC_INIT_DEFAULT \ +{ \ + crcWidth16, /* CRC width is 16 bits. */ \ + 0x00008408UL, /* Polynomial value of IEEE 802.15.4-2006. */ \ + 0x00000000UL, /* Initialization value. */ \ + 8U, /* 8 bits per word. */ \ + crcByteOrderNormal, /* Byte order is normal. */ \ + crcBitOrderLSBFirst, /* Bit order (TBD). */ \ + crcBitReverseNormal, /* Bit order is not reversed on output. */ \ + false, /* No zero-padding. */ \ + false, /* Input is not inverted. */ \ + false /* Output is not inverted. */ \ +} + + +/******************************************************************************* + ****************************** PROTOTYPES ********************************* + ******************************************************************************/ + +void CRC_Init(CRC_Init_TypeDef const *init); +void CRC_Reset(void); + +/***************************************************************************//** + * @brief + * Issues a command to initialize the CRC calculation. + * + * @details + * This function issues the command INITIALIZE in CRC_CMD that initializes the + * CRC calculation by writing the initial values to the DATA register. + * + * @note + * Internal notes: + * Initialize in CRC_CMD + * Conclude on reference of parameters. Register names or config struct members? + ******************************************************************************/ +__STATIC_INLINE void CRC_InitCommand(void) +{ + CRC->CMD = CRC_CMD_INITIALIZE; +} + + +/***************************************************************************//** + * @brief + * Set the initialization value of the CRC. + ******************************************************************************/ +__STATIC_INLINE void CRC_InitValueSet(uint32_t initValue) +{ + CRC->INIT = initValue; +} + + +/***************************************************************************//** + * @brief + * Writes data to the input data register of the CRC. + * + * @details + * Use this function to write input data to the CRC when the FRC is not being + * used for automatic handling of the CRC. The CRC calculation is based on + * the provided input data using the configured CRC polynomial. + * + * @param[in] data + * Data to be written to the input data register. + ******************************************************************************/ +__STATIC_INLINE void CRC_InputDataWrite(uint16_t data) +{ + CRC->INPUTDATA = (uint32_t)data; +} + + +/***************************************************************************//** + * @brief + * Reads the data register of the CRC. + * + * @details + * Use this function to read the calculated CRC value. + * + * @return + * Content of the CRC data register. + ******************************************************************************/ +__STATIC_INLINE uint32_t CRC_DataRead(void) +{ + return CRC->DATA; +} + + +/***************************************************************************//** + * @brief + * Gets if the CRC is busy. + * + * @details + * Returns true when the CRC module is busy, false otherwise. + * + * @return + * CRC busy flag. + * @li true - CRC module is busy. + * @li false - CRC module is not busy. + ******************************************************************************/ +__STATIC_INLINE bool CRC_BusyGet(void) +{ + return (bool)((CRC->STATUS & _CRC_STATUS_BUSY_MASK) + >> _CRC_STATUS_BUSY_SHIFT); +} + + +/** @} (end addtogroup CRC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(CRC_COUNT) && (CRC_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_CRC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_cryotimer.h b/cpu/efm32_common/emlib/inc/em_cryotimer.h new file mode 100644 index 0000000000000..a81b123d511a5 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_cryotimer.h @@ -0,0 +1,357 @@ +/***************************************************************************//** + * @file em_cryotimer.h + * @brief Ultra Low Energy Timer/Counter (CRYOTIMER) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef EM_CRYOTIMER_H__ +#define EM_CRYOTIMER_H__ + +#include +#include "em_device.h" +#include "em_bus.h" + +#if defined(CRYOTIMER_PRESENT) && (CRYOTIMER_COUNT == 1) + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CRYOTIMER + * @brief Ultra Low Energy Timer/Counter (CRYOTIMER) Peripheral API + * + * @details + * The user is responsible for choosing which oscillator to use for the + * CRYOTIMER. The oscillator that is choosen must be enabled and ready before + * calling this @ref CRYOTIMER_Init function. See @ref CMU_OscillatorEnable + * for details of how to enable and wait for an oscillator to become ready. + * Note that ULFRCO is always ready while LFRCO and LFXO must be enable by + * the user. + * + * @details + * Note that the only oscillator which is running in EM3 is ULFRCO. Keep this + * in mind when choosing which oscillator to use for the CRYOTIMER. + * + * @details + * Special care must be taken if the user wants the CRYOTIMER to run during + * EM4. All the low frequency oscillators can be used in EM4, however the + * oscillator that is used must be be configured to be retained when going + * into EM4. This can be configured by using functions in the @ref EMU module. + * See @ref EMU_EM4Init and @ref EMU_EM4Init_TypeDef. If an oscillator is + * retained in EM4 the user is also responsible for unlatching the retained + * configuration on a wakeup from EM4. + * + * @{ + ******************************************************************************/ + +/******************************************************************************* + ********************************* ENUM ************************************ + ******************************************************************************/ + +/** Prescaler selection. */ +typedef enum +{ + cryotimerPresc_1 = _CRYOTIMER_CTRL_PRESC_DIV1, /**< Divide clock by 1. */ + cryotimerPresc_2 = _CRYOTIMER_CTRL_PRESC_DIV2, /**< Divide clock by 2. */ + cryotimerPresc_4 = _CRYOTIMER_CTRL_PRESC_DIV4, /**< Divide clock by 4. */ + cryotimerPresc_8 = _CRYOTIMER_CTRL_PRESC_DIV8, /**< Divide clock by 8. */ + cryotimerPresc_16 = _CRYOTIMER_CTRL_PRESC_DIV16, /**< Divide clock by 16. */ + cryotimerPresc_32 = _CRYOTIMER_CTRL_PRESC_DIV32, /**< Divide clock by 32. */ + cryotimerPresc_64 = _CRYOTIMER_CTRL_PRESC_DIV64, /**< Divide clock by 64. */ + cryotimerPresc_128 = _CRYOTIMER_CTRL_PRESC_DIV128, /**< Divide clock by 128. */ +} CRYOTIMER_Presc_TypeDef; + +/** Low frequency oscillator selection. */ +typedef enum +{ + cryotimerOscLFRCO = _CRYOTIMER_CTRL_OSCSEL_LFRCO, /**< Select Low Frequency RC Oscillator. */ + cryotimerOscLFXO = _CRYOTIMER_CTRL_OSCSEL_LFXO, /**< Select Low Frequency Crystal Oscillator. */ + cryotimerOscULFRCO = _CRYOTIMER_CTRL_OSCSEL_ULFRCO, /**< Select Ultra Low Frequency RC Oscillator. */ +} CRYOTIMER_Osc_TypeDef; + +/** Period selection value */ +typedef enum +{ + cryotimerPeriod_1 = 0, /**< Wakeup event after every Pre-scaled clock cycle. */ + cryotimerPeriod_2 = 1, /**< Wakeup event after 2 Pre-scaled clock cycles. */ + cryotimerPeriod_4 = 2, /**< Wakeup event after 4 Pre-scaled clock cycles. */ + cryotimerPeriod_8 = 3, /**< Wakeup event after 8 Pre-scaled clock cycles. */ + cryotimerPeriod_16 = 4, /**< Wakeup event after 16 Pre-scaled clock cycles. */ + cryotimerPeriod_32 = 5, /**< Wakeup event after 32 Pre-scaled clock cycles. */ + cryotimerPeriod_64 = 6, /**< Wakeup event after 64 Pre-scaled clock cycles. */ + cryotimerPeriod_128 = 7, /**< Wakeup event after 128 Pre-scaled clock cycles. */ + cryotimerPeriod_256 = 8, /**< Wakeup event after 256 Pre-scaled clock cycles. */ + cryotimerPeriod_512 = 9, /**< Wakeup event after 512 Pre-scaled clock cycles. */ + cryotimerPeriod_1k = 10, /**< Wakeup event after 1k Pre-scaled clock cycles. */ + cryotimerPeriod_2k = 11, /**< Wakeup event after 2k Pre-scaled clock cycles. */ + cryotimerPeriod_4k = 12, /**< Wakeup event after 4k Pre-scaled clock cycles. */ + cryotimerPeriod_8k = 13, /**< Wakeup event after 8k Pre-scaled clock cycles. */ + cryotimerPeriod_16k = 14, /**< Wakeup event after 16k Pre-scaled clock cycles. */ + cryotimerPeriod_32k = 15, /**< Wakeup event after 32k Pre-scaled clock cycles. */ + cryotimerPeriod_64k = 16, /**< Wakeup event after 64k Pre-scaled clock cycles. */ + cryotimerPeriod_128k = 17, /**< Wakeup event after 128k Pre-scaled clock cycles. */ + cryotimerPeriod_256k = 18, /**< Wakeup event after 256k Pre-scaled clock cycles. */ + cryotimerPeriod_512k = 19, /**< Wakeup event after 512k Pre-scaled clock cycles. */ + cryotimerPeriod_1m = 20, /**< Wakeup event after 1m Pre-scaled clock cycles. */ + cryotimerPeriod_2m = 21, /**< Wakeup event after 2m Pre-scaled clock cycles. */ + cryotimerPeriod_4m = 22, /**< Wakeup event after 4m Pre-scaled clock cycles. */ + cryotimerPeriod_8m = 23, /**< Wakeup event after 8m Pre-scaled clock cycles. */ + cryotimerPeriod_16m = 24, /**< Wakeup event after 16m Pre-scaled clock cycles. */ + cryotimerPeriod_32m = 25, /**< Wakeup event after 32m Pre-scaled clock cycles. */ + cryotimerPeriod_64m = 26, /**< Wakeup event after 64m Pre-scaled clock cycles. */ + cryotimerPeriod_128m = 27, /**< Wakeup event after 128m Pre-scaled clock cycles. */ + cryotimerPeriod_256m = 28, /**< Wakeup event after 256m Pre-scaled clock cycles. */ + cryotimerPeriod_512m = 29, /**< Wakeup event after 512m Pre-scaled clock cycles. */ + cryotimerPeriod_1024m = 30, /**< Wakeup event after 1024m Pre-scaled clock cycles. */ + cryotimerPeriod_2048m = 31, /**< Wakeup event after 2048m Pre-scaled clock cycles. */ + cryotimerPeriod_4096m = 32, /**< Wakeup event after 4096m Pre-scaled clock cycles. */ +} CRYOTIMER_Period_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** CRYOTIMER initialization structure. */ +typedef struct +{ + /** Enable/disable counting when initialization is completed. */ + bool enable; + + /** Enable/disable timer counting during debug halt. */ + bool debugRun; + + /** Enable/disable EM4 Wakeup. */ + bool em4Wakeup; + + /** Select the oscillator for the CRYOTIMER. */ + CRYOTIMER_Osc_TypeDef osc; + + /** Prescaler. */ + CRYOTIMER_Presc_TypeDef presc; + + /** Period between wakeup event/interrupt. */ + CRYOTIMER_Period_TypeDef period; +} CRYOTIMER_Init_TypeDef; + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** Default CRYOTIMER init structure. */ +#define CRYOTIMER_INIT_DEFAULT \ +{ \ + true, /* Start counting when init done. */ \ + false, /* Disable CRYOTIMER during debug halt. */ \ + false, /* Disable EM4 wakeup. */ \ + cryotimerOscLFRCO, /* Select Low Frequency RC Oscillator. */ \ + cryotimerPresc_1, /* LF Oscillator frequency undivided. */ \ + cryotimerPeriod_4096m, /* Wakeup event after 4096M pre-scaled clock cycles. */ \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Clear the CRYOTIMER period interrupt. + * + * @param[in] flags + * CRYOTIMER interrupt sources to clear. Use CRYOTIMER_IFC_PERIOD + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_IntClear(uint32_t flags) +{ + CRYOTIMER->IFC = flags & _CRYOTIMER_IFC_MASK; +} + +/***************************************************************************//** + * @brief + * Get the CRYOTIMER interrupt flag. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending CRYOTIMER interrupt sources. + ******************************************************************************/ +__STATIC_INLINE uint32_t CRYOTIMER_IntGet(void) +{ + return CRYOTIMER->IF; +} + +/***************************************************************************//** + * @brief + * Get enabled and pending CRYOTIMER interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled CRYOTIMER interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in CRYOTIMER_IEN and + * - the pending interrupt flags CRYOTIMER_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t CRYOTIMER_IntGetEnabled(void) +{ + uint32_t ien; + + ien = CRYOTIMER->IEN & _CRYOTIMER_IEN_MASK; + return CRYOTIMER->IF & ien; +} + +/***************************************************************************//** + * @brief + * Enable one or more CRYOTIMER interrupts. + * + * @param[in] flags + * CRYOTIMER interrupt sources to enable. Use CRYOTIMER_IEN_PERIOD. + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_IntEnable(uint32_t flags) +{ + CRYOTIMER->IEN |= (flags & _CRYOTIMER_IEN_MASK); +} + +/***************************************************************************//** + * @brief + * Disable one or more CRYOTIMER interrupts. + * + * @param[in] flags + * CRYOTIMER interrupt sources to disable. Use CRYOTIMER_IEN_PERIOD. + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_IntDisable(uint32_t flags) +{ + CRYOTIMER->IEN &= ~(flags & _CRYOTIMER_IEN_MASK); +} + +/***************************************************************************//** + * @brief + * Set the CRYOTIMER period interrupt flag. + * + * @note + * Writes 1 to the interrupt flag set register. + * + * @param[in] flags + * CRYOTIMER interrupt sources to set to pending. Use CRYOTIMER_IFS_PERIOD. + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_IntSet(uint32_t flags) +{ + CRYOTIMER->IFS = flags & _CRYOTIMER_IFS_MASK; +} + +/***************************************************************************//** + * @brief + * Set the CRYOTIMER period select + * + * @note + * Sets the duration between the Interrupts/Wakeup events based on + * the pre-scaled clock. + * + * @param[in] period + * 2^period is the number of clock cycles before a wakeup event or + * interrupt is triggered. The CRYOTIMER_Periodsel_TypeDef enum can + * be used a convenience type when calling this function. + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_PeriodSet(uint32_t period) +{ + CRYOTIMER->PERIODSEL = period & _CRYOTIMER_PERIODSEL_MASK; +} + +/***************************************************************************//** + * @brief + * Get the CRYOTIMER period select value + * + * @note + * Gets the duration between the Interrupts/Wakeup events in the + * CRYOTIMER. + * + * @return + * Duration between the interrupts/wakeup events. Returns the value + * of the PERIODSEL register. The number of clock cycles can be calculated + * as the 2^n where n is the return value of this function. + ******************************************************************************/ +__STATIC_INLINE uint32_t CRYOTIMER_PeriodGet(void) +{ + return CRYOTIMER->PERIODSEL; +} + +/***************************************************************************//** + * @brief + * Get the CRYOTIMER counter value + * + * @return + * Returns the current CRYOTIMER counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t CRYOTIMER_CounterGet(void) +{ + return CRYOTIMER->CNT; +} + +/***************************************************************************//** + * @brief + * Enable/disable EM4 wakeup capability. + * + * @param[in] enable + * True to enable EM4 wakeup, false to disable. + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_EM4WakeupEnable(bool enable) +{ + BUS_RegBitWrite((&CRYOTIMER->EM4WUEN), _CRYOTIMER_EM4WUEN_EM4WU_SHIFT, enable); +} + +/***************************************************************************//** + * @brief + * Enable/disable the CRYOTIMER. + * + * @param[in] enable + * True to enable the CRYOTIMER, false to disable. + ******************************************************************************/ +__STATIC_INLINE void CRYOTIMER_Enable(bool enable) +{ + BUS_RegBitWrite((&CRYOTIMER->CTRL), _CRYOTIMER_CTRL_EN_SHIFT, enable); +} + +void CRYOTIMER_Init(const CRYOTIMER_Init_TypeDef *init); + +#ifdef __cplusplus +} +#endif + +/** @} (end addtogroup CRYOTIMER) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(CRYOTIMER_PRESENT) && (CRYOTIMER_COUNT == 1) */ +#endif /* EM_CRYOTIMER_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_crypto.h b/cpu/efm32_common/emlib/inc/em_crypto.h new file mode 100644 index 0000000000000..a1d6b19d90a57 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_crypto.h @@ -0,0 +1,1334 @@ +/***************************************************************************//** + * @file em_crypto.h + * @brief Cryptography accelerator peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ +#ifndef __SILICON_LABS_EM_CRYPTO_H__ +#define __SILICON_LABS_EM_CRYPTO_H__ + +#include "em_device.h" + +#if defined(CRYPTO_COUNT) && (CRYPTO_COUNT > 0) + +#include "em_bus.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CRYPTO + * @{ + ******************************************************************************/ + + /******************************************************************************* + ****************************** DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/** Data sizes used by CRYPTO operations. */ +#define CRYPTO_DATA_SIZE_IN_BITS (128) +#define CRYPTO_DATA_SIZE_IN_BYTES (CRYPTO_DATA_SIZE_IN_BITS/8) +#define CRYPTO_DATA_SIZE_IN_32BIT_WORDS (CRYPTO_DATA_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_KEYBUF_SIZE_IN_BITS (256) +#define CRYPTO_KEYBUF_SIZE_IN_BYTES (CRYPTO_DDATA_SIZE_IN_BITS/8) +#define CRYPTO_KEYBUF_SIZE_IN_32BIT_WORDS (CRYPTO_DDATA_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_DDATA_SIZE_IN_BITS (256) +#define CRYPTO_DDATA_SIZE_IN_BYTES (CRYPTO_DDATA_SIZE_IN_BITS/8) +#define CRYPTO_DDATA_SIZE_IN_32BIT_WORDS (CRYPTO_DDATA_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_QDATA_SIZE_IN_BITS (512) +#define CRYPTO_QDATA_SIZE_IN_BYTES (CRYPTO_QDATA_SIZE_IN_BITS/8) +#define CRYPTO_QDATA_SIZE_IN_32BIT_WORDS (CRYPTO_QDATA_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_DATA260_SIZE_IN_32BIT_WORDS (9) + +/** SHA-1 digest sizes */ +#define CRYPTO_SHA1_DIGEST_SIZE_IN_BITS (160) +#define CRYPTO_SHA1_DIGEST_SIZE_IN_BYTES (CRYPTO_SHA1_DIGEST_SIZE_IN_BITS/8) + +/** SHA-256 digest sizes */ +#define CRYPTO_SHA256_DIGEST_SIZE_IN_BITS (256) +#define CRYPTO_SHA256_DIGEST_SIZE_IN_BYTES (CRYPTO_SHA256_DIGEST_SIZE_IN_BITS/8) + +/** + * Read and write all 260 bits of DDATA0 when in 260 bit mode. + */ +#define CRYPTO_DDATA0_260_BITS_READ(bigint260) CRYPTO_DData0Read260(bigint260) +#define CRYPTO_DDATA0_260_BITS_WRITE(bigint260) CRYPTO_DData0Write260(bigint260) +/** @endcond */ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/** + * Instruction sequence load macros CRYPTO_SEQ_LOAD_X (where X is in the range + * 1-20). E.g. @ref CRYPTO_SEQ_LOAD_20. + * Use these macros in order for faster execution than the function API. + */ +#define CRYPTO_SEQ_LOAD_1(a1) { \ + CRYPTO->SEQ0 = a1 | (CRYPTO_CMD_INSTR_END<<8);} +#define CRYPTO_SEQ_LOAD_2(a1, a2) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (CRYPTO_CMD_INSTR_END<<16);} +#define CRYPTO_SEQ_LOAD_3(a1, a2, a3) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (CRYPTO_CMD_INSTR_END<<24);} +#define CRYPTO_SEQ_LOAD_4(a1, a2, a3, a4) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = CRYPTO_CMD_INSTR_END;} +#define CRYPTO_SEQ_LOAD_5(a1, a2, a3, a4, a5) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (CRYPTO_CMD_INSTR_END<<8);} +#define CRYPTO_SEQ_LOAD_6(a1, a2, a3, a4, a5, a6) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (CRYPTO_CMD_INSTR_END<<16);} +#define CRYPTO_SEQ_LOAD_7(a1, a2, a3, a4, a5, a6, a7) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (CRYPTO_CMD_INSTR_END<<24);} +#define CRYPTO_SEQ_LOAD_8(a1, a2, a3, a4, a5, a6, a7, a8) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = CRYPTO_CMD_INSTR_END;} +#define CRYPTO_SEQ_LOAD_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (CRYPTO_CMD_INSTR_END<<8);} +#define CRYPTO_SEQ_LOAD_10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (CRYPTO_CMD_INSTR_END<<16);} +#define CRYPTO_SEQ_LOAD_11(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (CRYPTO_CMD_INSTR_END<<24);} +#define CRYPTO_SEQ_LOAD_12(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = CRYPTO_CMD_INSTR_END;} +#define CRYPTO_SEQ_LOAD_13(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (CRYPTO_CMD_INSTR_END<<8);} +#define CRYPTO_SEQ_LOAD_14(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (CRYPTO_CMD_INSTR_END<<16);} +#define CRYPTO_SEQ_LOAD_15(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (CRYPTO_CMD_INSTR_END<<24);} +#define CRYPTO_SEQ_LOAD_16(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = CRYPTO_CMD_INSTR_END;} +#define CRYPTO_SEQ_LOAD_17(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (CRYPTO_CMD_INSTR_END<<8);} +#define CRYPTO_SEQ_LOAD_18(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (a18<<8) | (CRYPTO_CMD_INSTR_END<<16);} +#define CRYPTO_SEQ_LOAD_19(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (a18<<8) | (a19<<16) | (CRYPTO_CMD_INSTR_END<<24);} +#define CRYPTO_SEQ_LOAD_20(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (a18<<8) | (a19<<16) | (a20<<24);} +/** @endcond */ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/** + * Instruction sequence execution macros CRYPTO_EXECUTE_X (where X is in the range + * 1-20). E.g. @ref CRYPTO_EXECUTE_19. + * Use these macros in order for faster execution than the function API. + */ +#define CRYPTO_EXECUTE_1(a1) { \ + CRYPTO->SEQ0 = a1 | (CRYPTO_CMD_INSTR_EXEC<<8); } +#define CRYPTO_EXECUTE_2(a1, a2) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (CRYPTO_CMD_INSTR_EXEC<<16); } +#define CRYPTO_EXECUTE_3(a1, a2, a3) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (CRYPTO_CMD_INSTR_EXEC<<24); } +#define CRYPTO_EXECUTE_4(a1, a2, a3, a4) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = CRYPTO_CMD_INSTR_EXEC; } +#define CRYPTO_EXECUTE_5(a1, a2, a3, a4, a5) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (CRYPTO_CMD_INSTR_EXEC<<8); } +#define CRYPTO_EXECUTE_6(a1, a2, a3, a4, a5, a6) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (CRYPTO_CMD_INSTR_EXEC<<16); } +#define CRYPTO_EXECUTE_7(a1, a2, a3, a4, a5, a6, a7) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (CRYPTO_CMD_INSTR_EXEC<<24); } +#define CRYPTO_EXECUTE_8(a1, a2, a3, a4, a5, a6, a7, a8) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = CRYPTO_CMD_INSTR_EXEC; } +#define CRYPTO_EXECUTE_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (CRYPTO_CMD_INSTR_EXEC<<8); } +#define CRYPTO_EXECUTE_10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (CRYPTO_CMD_INSTR_EXEC<<16); } +#define CRYPTO_EXECUTE_11(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (CRYPTO_CMD_INSTR_EXEC<<24); } +#define CRYPTO_EXECUTE_12(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = CRYPTO_CMD_INSTR_EXEC; } +#define CRYPTO_EXECUTE_13(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (CRYPTO_CMD_INSTR_EXEC<<8); } +#define CRYPTO_EXECUTE_14(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (CRYPTO_CMD_INSTR_EXEC<<16); } +#define CRYPTO_EXECUTE_15(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (CRYPTO_CMD_INSTR_EXEC<<24); } +#define CRYPTO_EXECUTE_16(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = CRYPTO_CMD_INSTR_EXEC; } +#define CRYPTO_EXECUTE_17(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (CRYPTO_CMD_INSTR_EXEC<<8); } +#define CRYPTO_EXECUTE_18(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (a18<<8) | (CRYPTO_CMD_INSTR_EXEC<<16); } +#define CRYPTO_EXECUTE_19(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (a18<<8) | (a19<<16) | (CRYPTO_CMD_INSTR_EXEC<<24); } +#define CRYPTO_EXECUTE_20(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) { \ + CRYPTO->SEQ0 = a1 | (a2<<8) | (a3<<16) | (a4<<24); \ + CRYPTO->SEQ1 = a5 | (a6<<8) | (a7<<16) | (a8<<24); \ + CRYPTO->SEQ2 = a9 | (a10<<8) | (a11<<16) | (a12<<24); \ + CRYPTO->SEQ3 = a13 | (a14<<8) | (a15<<16) | (a16<<24); \ + CRYPTO->SEQ4 = a17 | (a18<<8) | (a19<<16) | (a20<<24); \ + CRYPTO_InstructionSequenceExecute();} +/** @endcond */ + +/******************************************************************************* + ****************************** TYPEDEFS *********************************** + ******************************************************************************/ + +/** + * CRYPTO data types used for data load functions. This data type is + * capable of storing a 128 bits value as used in the crypto DATA + * registers + */ +typedef uint32_t CRYPTO_Data_TypeDef[CRYPTO_DATA_SIZE_IN_32BIT_WORDS]; + +/** + * CRYPTO data type used for data load functions. This data type + * is capable of storing a 256 bits value as used in the crypto DDATA + * registers + */ +typedef uint32_t CRYPTO_DData_TypeDef[CRYPTO_DDATA_SIZE_IN_32BIT_WORDS]; + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +typedef uint32_t* CRYPTO_DDataPtr_TypeDef; +/** @endcond */ + +/** + * CRYPTO data type used for data load functions. This data type is + * capable of storing a 512 bits value as used in the crypto QDATA + * registers + */ +typedef uint32_t CRYPTO_QData_TypeDef[CRYPTO_QDATA_SIZE_IN_32BIT_WORDS]; + +/** + * CRYPTO data type used for data load functions. This data type is + * capable of storing a 260 bits value as used by the @ref CRYPTO_DData0Write260 + * function. + * + * Note that this data type is multiple of 32 bit words, so the + * actual storage used by this type is 32x9=288 bits. + */ +typedef uint32_t CRYPTO_Data260_TypeDef[CRYPTO_DATA260_SIZE_IN_32BIT_WORDS]; + +/** + * CRYPTO data type used for data load functions. This data type is + * capable of storing 256 bits as used in the crypto KEYBUF register. + */ +typedef uint32_t CRYPTO_KeyBuf_TypeDef[CRYPTO_KEYBUF_SIZE_IN_32BIT_WORDS]; + +/** + * CRYPTO Data registers. These register are used to load 128 bit values as + * input and output data for cryptographic and big integer arithmetic + * functions of the CRYPTO module. + */ +typedef enum +{ + cryptoRegDATA0 = (uint32_t) &CRYPTO->DATA0, /**< 128 bit DATA0 register */ + cryptoRegDATA1 = (uint32_t) &CRYPTO->DATA1, /**< 128 bit DATA1 register */ + cryptoRegDATA2 = (uint32_t) &CRYPTO->DATA2, /**< 128 bit DATA2 register */ + cryptoRegDATA3 = (uint32_t) &CRYPTO->DATA3, /**< 128 bit DATA3 register */ + cryptoRegDATA0XOR = (uint32_t) &CRYPTO->DATA0XOR, /**< 128 bit DATA0XOR register */ +} CRYPTO_DataReg_TypeDef; + +/** + * CRYPTO DData (Double Data) registers. These registers are used to load + * 256 bit values as input and output data for cryptographic and big integer + * arithmetic functions of the CRYPTO module. + */ +typedef enum +{ + cryptoRegDDATA0 = (uint32_t) &CRYPTO->DDATA0, /**< 256 bit DDATA0 register */ + cryptoRegDDATA1 = (uint32_t) &CRYPTO->DDATA1, /**< 256 bit DDATA1 register */ + cryptoRegDDATA2 = (uint32_t) &CRYPTO->DDATA2, /**< 256 bit DDATA2 register */ + cryptoRegDDATA3 = (uint32_t) &CRYPTO->DDATA3, /**< 256 bit DDATA3 register */ + cryptoRegDDATA4 = (uint32_t) &CRYPTO->DDATA4, /**< 256 bit DDATA4 register */ + cryptoRegDDATA0BIG = (uint32_t) &CRYPTO->DDATA0BIG, /**< 256 bit DDATA0BIG register, big endian access to DDATA0 */ +} CRYPTO_DDataReg_TypeDef; + +/** + * CRYPTO QData (Quad data) registers. These registers are used to load 512 bit + * values as input and output data for cryptographic and big integer arithmetic + * functions of the CRYPTO module. + */ +typedef enum +{ + cryptoRegQDATA0 = (uint32_t) &CRYPTO->QDATA0, /**< 512 bit QDATA0 register */ + cryptoRegQDATA1 = (uint32_t) &CRYPTO->QDATA1, /**< 512 bit QDATA1 register */ + cryptoRegQDATA1BIG = (uint32_t) &CRYPTO->QDATA1BIG, /**< 512 bit QDATA1BIG register, big-endian access to QDATA1 */ +} CRYPTO_QDataReg_TypeDef; + +/** CRYPTO modulus types. */ +typedef enum +{ + cryptoModulusBin256 = CRYPTO_WAC_MODULUS_BIN256, /**< Generic 256 bit modulus 2^256 */ + cryptoModulusBin128 = CRYPTO_WAC_MODULUS_BIN128, /**< Generic 128 bit modulus 2^128 */ + cryptoModulusGcmBin128 = CRYPTO_WAC_MODULUS_GCMBIN128, /**< GCM 128 bit modulus = 2^128 + 2^7 + 2^2 + 2 + 1 */ + cryptoModulusEccB233 = CRYPTO_WAC_MODULUS_ECCBIN233P, /**< ECC B233 prime modulus = 2^233 + 2^74 + 1 */ + cryptoModulusEccB163 = CRYPTO_WAC_MODULUS_ECCBIN163P, /**< ECC B163 prime modulus = 2^163 + 2^7 + 2^6 + 2^3 + 1 */ + cryptoModulusEccP256 = CRYPTO_WAC_MODULUS_ECCPRIME256P, /**< ECC P256 prime modulus = 2^256 - 2^224 + 2^192 + 2^96 - 1 */ + cryptoModulusEccP224 = CRYPTO_WAC_MODULUS_ECCPRIME224P, /**< ECC P224 prime modulus = 2^224 - 2^96 - 1 */ + cryptoModulusEccP192 = CRYPTO_WAC_MODULUS_ECCPRIME192P, /**< ECC P192 prime modulus = 2^192 - 2^64 - 1 */ + cryptoModulusEccB233Order = CRYPTO_WAC_MODULUS_ECCBIN233N, /**< ECC B233 order modulus */ + cryptoModulusEccB233KOrder = CRYPTO_WAC_MODULUS_ECCBIN233KN, /**< ECC B233K order modulus */ + cryptoModulusEccB163Order = CRYPTO_WAC_MODULUS_ECCBIN163N, /**< ECC B163 order modulus */ + cryptoModulusEccB163KOrder = CRYPTO_WAC_MODULUS_ECCBIN163KN, /**< ECC B163K order modulus */ + cryptoModulusEccP256Order = CRYPTO_WAC_MODULUS_ECCPRIME256N, /**< ECC P256 order modulus */ + cryptoModulusEccP224Order = CRYPTO_WAC_MODULUS_ECCPRIME224N, /**< ECC P224 order modulus */ + cryptoModulusEccP192Order = CRYPTO_WAC_MODULUS_ECCPRIME192N /**< ECC P192 order modulus */ +} CRYPTO_ModulusType_TypeDef; + +/** CRYPTO multiplication widths for wide arithmetic operations. */ +typedef enum +{ + cryptoMulOperand256Bits = CRYPTO_WAC_MULWIDTH_MUL256, /**< 256 bits operands */ + cryptoMulOperand128Bits = CRYPTO_WAC_MULWIDTH_MUL128, /**< 128 bits operands */ + cryptoMulOperandModulusBits = CRYPTO_WAC_MULWIDTH_MULMOD /**< MUL operand width + is specified by the + modulus type.*/ +} CRYPTO_MulOperandWidth_TypeDef; + +/** CRYPTO result widths for MUL operations. */ +typedef enum +{ + cryptoResult128Bits = CRYPTO_WAC_RESULTWIDTH_128BIT, /**< Multiplication result width is 128 bits*/ + cryptoResult256Bits = CRYPTO_WAC_RESULTWIDTH_256BIT, /**< Multiplication result width is 256 bits*/ + cryptoResult260Bits = CRYPTO_WAC_RESULTWIDTH_260BIT /**< Multiplication result width is 260 bits*/ +} CRYPTO_ResultWidth_TypeDef; + +/** CRYPTO result widths for MUL operations. */ +typedef enum +{ + cryptoInc1byte = CRYPTO_CTRL_INCWIDTH_INCWIDTH1, /**< inc width is 1 byte*/ + cryptoInc2byte = CRYPTO_CTRL_INCWIDTH_INCWIDTH2, /**< inc width is 2 byte*/ + cryptoInc3byte = CRYPTO_CTRL_INCWIDTH_INCWIDTH3, /**< inc width is 3 byte*/ + cryptoInc4byte = CRYPTO_CTRL_INCWIDTH_INCWIDTH4 /**< inc width is 4 byte*/ +} CRYPTO_IncWidth_TypeDef; + +/** CRYPTO key width. */ +typedef enum +{ + cryptoKey128Bits = 8, /**< Key width is 128 bits*/ + cryptoKey256Bits = 16, /**< Key width is 256 bits*/ +} CRYPTO_KeyWidth_TypeDef; + +/** + * The max number of crypto instructions in an instruction sequence + */ +#define CRYPTO_MAX_SEQUENCE_INSTRUCTIONS (20) + +/** + * Instruction sequence type. + * The user should fill in the desired operations from step1, then step2 etc. + * The CRYPTO_CMD_INSTR_END marks the end of the sequence. + * Bit fields are used to format the memory layout of the struct equal to the + * sequence registers in the CRYPTO module. + */ +typedef uint8_t CRYPTO_InstructionSequence_TypeDef[CRYPTO_MAX_SEQUENCE_INSTRUCTIONS]; + +/** Default instruction sequence consisting of all ENDs. The user can + initialize the instruction sequence with this default value set, and fill + in the desired operations from step 1. The first END instruction marks + the end of the sequence. */ +#define CRYPTO_INSTRUCTIONSEQUENSE_DEFAULT \ + {CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, \ + CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, \ + CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, \ + CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, \ + CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, \ + CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END, \ + CRYPTO_CMD_INSTR_END, CRYPTO_CMD_INSTR_END} + +/** SHA-1 Digest type. */ +typedef uint8_t CRYPTO_SHA1_Digest_TypeDef[CRYPTO_SHA1_DIGEST_SIZE_IN_BYTES]; + +/** SHA-256 Digest type. */ +typedef uint8_t CRYPTO_SHA256_Digest_TypeDef[CRYPTO_SHA256_DIGEST_SIZE_IN_BYTES]; + +/** + * @brief + * AES counter modification function pointer. + * + * @note + * This is defined in order for backwards compatibility with EFM32 em_aes.h. + * The CRYPTO implementation of Counter mode does not support counter update + * callbacks. + * + * @param[in] ctr Counter value to be modified. + */ +typedef void (*CRYPTO_AES_CtrFuncPtr_TypeDef)(uint8_t * ctr); + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Set the modulus type used for wide arithmetic operations. + * + * @details + * This function sets the modulus type to be used by the Modulus instructions + * of the CRYPTO module. + * + * @param[in] modType Modulus type. + ******************************************************************************/ +void CRYPTO_ModulusSet(CRYPTO_ModulusType_TypeDef modType); + +/***************************************************************************//** + * @brief + * Set the number of bits in the operands of the MUL instruction. + * + * @details + * This function sets the number of bits to be used in the operands of + * the MUL instruction. + * + * @param[in] mulOperandWidth Multiplication width in bits. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_MulOperandWidthSet(CRYPTO_MulOperandWidth_TypeDef mulOperandWidth) +{ + uint32_t temp = CRYPTO->WAC & (~_CRYPTO_WAC_MULWIDTH_MASK); + CRYPTO->WAC = temp | mulOperandWidth; +} + +/***************************************************************************//** + * @brief + * Set the width of the results of the non-modulus instructions. + * + * @details + * This function sets the result width of the non-modulus instructions. + * + * @param[in] resultWidth Result width of non-modulus instructions. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_ResultWidthSet(CRYPTO_ResultWidth_TypeDef resultWidth) +{ + uint32_t temp = CRYPTO->WAC & (~_CRYPTO_WAC_RESULTWIDTH_MASK); + CRYPTO->WAC = temp | resultWidth; +} + +/***************************************************************************//** + * @brief + * Set the width of the DATA1 increment instruction DATA1INC. + * + * @details + * This function sets the width of the DATA1 increment instruction + * @ref CRYPTO_CMD_INSTR_DATA1INC. + * + * @param[in] incWidth incrementation width. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_IncWidthSet(CRYPTO_IncWidth_TypeDef incWidth) +{ + uint32_t temp = CRYPTO->CTRL & (~_CRYPTO_CTRL_INCWIDTH_MASK); + CRYPTO->CTRL = temp | incWidth; +} + +/***************************************************************************//** + * @brief + * Write a 128 bit value into a crypto register. + * + * @note + * This function provide a low-level api for writing to the multi-word + * registers in the crypto peripheral. Applications should prefer to use + * @ref CRYPTO_DataWrite, @ref CRYPTO_DDataWrite or @ref CRYPTO_QDataWrite + * for writing to the DATA, DDATA and QDATA registers. + * + * @param[in] reg + * Pointer to the crypto register. + * + * @param[in] val + * This is a pointer to 4 32 bit integers that contains the 128 bit value + * which will be written to the crypto register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_BurstToCrypto(volatile uint32_t * reg, const uint32_t * val) +{ + /* Load data from memory into local registers. */ + register uint32_t v0 = val[0]; + register uint32_t v1 = val[1]; + register uint32_t v2 = val[2]; + register uint32_t v3 = val[3]; + /* Store data to CRYPTO */ + *reg = v0; + *reg = v1; + *reg = v2; + *reg = v3; +} + +/***************************************************************************//** + * @brief + * Read a 128 bit value from a crypto register. + * + * @note + * This function provide a low-level api for reading one of the multi-word + * registers in the crypto peripheral. Applications should prefer to use + * @ref CRYPTO_DataRead, @ref CRYPTO_DDataRead or @ref CRYPTO_QDataRead + * for reading the value of the DATA, DDATA and QDATA registers. + * + * @param[in] reg + * Pointer to the crypto register. + * + * @param[out] val + * This is a pointer to an array that is capable of holding 4 32 bit integers + * that will be filled with the 128 bit value from the crypto register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_BurstFromCrypto(volatile uint32_t * reg, uint32_t * val) +{ + /* Load data from CRYPTO into local registers. */ + register uint32_t v0 = *reg; + register uint32_t v1 = *reg; + register uint32_t v2 = *reg; + register uint32_t v3 = *reg; + /* Store data to memory */ + val[0] = v0; + val[1] = v1; + val[2] = v2; + val[3] = v3; +} + +/***************************************************************************//** + * @brief + * Write 128 bits of data to a DATAX register in the CRYPTO module. + * + * @details + * Write 128 bits of data to a DATAX register in the crypto module. The data + * value is typically input to a big integer operation (see crypto + * instructions). + * + * @param[in] dataReg The 128 bit DATA register. + * @param[in] val Value of the data to write to the DATA register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_DataWrite(CRYPTO_DataReg_TypeDef dataReg, + const CRYPTO_Data_TypeDef val) +{ + CRYPTO_BurstToCrypto((volatile uint32_t *)dataReg, val); +} + +/***************************************************************************//** + * @brief + * Read 128 bits of data from a DATAX register in the CRYPTO module. + * + * @details + * Read 128 bits of data from a DATAX register in the crypto module. The data + * value is typically output from a big integer operation (see crypto + * instructions) + * + * @param[in] dataReg The 128 bit DATA register. + * @param[out] val Location where to store the value in memory. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_DataRead(CRYPTO_DataReg_TypeDef dataReg, + CRYPTO_Data_TypeDef val) +{ + CRYPTO_BurstFromCrypto((volatile uint32_t *)dataReg, val); +} + +/***************************************************************************//** + * @brief + * Write 256 bits of data to a DDATAX register in the CRYPTO module. + * + * @details + * Write 256 bits of data into a DDATAX (Double Data) register in the crypto + * module. The data value is typically input to a big integer operation (see + * crypto instructions). + * + * @param[in] ddataReg The 256 bit DDATA register. + * @param[in] val Value of the data to write to the DDATA register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_DDataWrite(CRYPTO_DDataReg_TypeDef ddataReg, + const CRYPTO_DData_TypeDef val) +{ + CRYPTO_BurstToCrypto((volatile uint32_t *)ddataReg, &val[0]); + CRYPTO_BurstToCrypto((volatile uint32_t *)ddataReg, &val[4]); +} + +/***************************************************************************//** + * @brief + * Read 256 bits of data from a DDATAX register in the CRYPTO module. + * + * @details + * Read 256 bits of data from a DDATAX (Double Data) register in the crypto + * module. The data value is typically output from a big integer operation + * (see crypto instructions). + * + * @param[in] ddataReg The 256 bit DDATA register. + * @param[out] val Location where to store the value in memory. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_DDataRead(CRYPTO_DDataReg_TypeDef ddataReg, + CRYPTO_DData_TypeDef val) +{ + CRYPTO_BurstFromCrypto((volatile uint32_t *)ddataReg, &val[0]); + CRYPTO_BurstFromCrypto((volatile uint32_t *)ddataReg, &val[4]); +} + +/***************************************************************************//** + * @brief + * Write 512 bits of data to a QDATAX register in the CRYPTO module. + * + * @details + * Write 512 bits of data into a QDATAX (Quad Data) register in the crypto module + * The data value is typically input to a big integer operation (see crypto + * instructions). + * + * @param[in] qdataReg The 512 bits QDATA register. + * @param[in] val Value of the data to write to the QDATA register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_QDataWrite(CRYPTO_QDataReg_TypeDef qdataReg, + CRYPTO_QData_TypeDef val) +{ + CRYPTO_BurstToCrypto((volatile uint32_t *)qdataReg, &val[0]); + CRYPTO_BurstToCrypto((volatile uint32_t *)qdataReg, &val[4]); + CRYPTO_BurstToCrypto((volatile uint32_t *)qdataReg, &val[8]); + CRYPTO_BurstToCrypto((volatile uint32_t *)qdataReg, &val[12]); +} + +/***************************************************************************//** + * @brief + * Read 512 bits of data from a QDATAX register in the CRYPTO module. + * + * @details + * Read 512 bits of data from a QDATAX register in the crypto module. The data + * value is typically input to a big integer operation (see crypto + * instructions). + * + * @param[in] qdataReg The 512 bits QDATA register. + * @param[in] val Value of the data to write to the QDATA register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_QDataRead(CRYPTO_QDataReg_TypeDef qdataReg, + CRYPTO_QData_TypeDef val) +{ + CRYPTO_BurstFromCrypto((volatile uint32_t *)qdataReg, &val[0]); + CRYPTO_BurstFromCrypto((volatile uint32_t *)qdataReg, &val[4]); + CRYPTO_BurstFromCrypto((volatile uint32_t *)qdataReg, &val[8]); + CRYPTO_BurstFromCrypto((volatile uint32_t *)qdataReg, &val[12]); +} + +/***************************************************************************//** + * @brief + * Set the key value to be used by the CRYPTO module. + * + * @details + * Write 128 or 256 bit key to the KEYBUF register in the crypto module. + * + * @param[in] val Value of the data to write to the KEYBUF register. + * @param[in] keyWidth Key width - 128 or 256 bits + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_KeyBufWrite(CRYPTO_KeyBuf_TypeDef val, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + if (keyWidth == cryptoKey256Bits) + { + /* Set AES-256 mode */ + BUS_RegBitWrite(&CRYPTO->CTRL, _CRYPTO_CTRL_AES_SHIFT, _CRYPTO_CTRL_AES_AES256); + /* Load key in KEYBUF register (= DDATA4) */ + CRYPTO_DDataWrite(cryptoRegDDATA4, (uint32_t *)val); + } + else + { + /* Set AES-128 mode */ + BUS_RegBitWrite(&CRYPTO->CTRL, _CRYPTO_CTRL_AES_SHIFT, _CRYPTO_CTRL_AES_AES128); + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &val[0]); + } +} + +void CRYPTO_KeyRead(CRYPTO_KeyBuf_TypeDef val, + CRYPTO_KeyWidth_TypeDef keyWidth); + +/***************************************************************************//** + * @brief + * Quick write 128 bit key to the CRYPTO module. + * + * @details + * Quick write 128 bit key to the KEYBUF register in the CRYPTO module. + * + * @param[in] val Value of the data to write to the KEYBUF register. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_KeyBuf128Write(const uint32_t * val) +{ + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, val); +} + +/***************************************************************************//** + * @brief + * Quick read access of the Carry bit from arithmetic operations. + * + * @details + * This function reads the carry bit of the CRYPTO ALU. + * + * @return + * Returns 'true' if carry is 1, and 'false' if carry is 0. + ******************************************************************************/ +__STATIC_INLINE bool CRYPTO_CarryIsSet(void) +{ + return (CRYPTO->DSTATUS & _CRYPTO_DSTATUS_CARRY_MASK) + >> _CRYPTO_DSTATUS_CARRY_SHIFT; +} + +/***************************************************************************//** + * @brief + * Quick read access of the 4 LSbits of the DDATA0 register. + * + * @details + * This function quickly retrieves the 4 least significant bits of the + * DDATA0 register via the DDATA0LSBS bit field in the DSTATUS register. + * + * @return + * Returns the 4 LSbits of DDATA0. + ******************************************************************************/ +__STATIC_INLINE uint8_t CRYPTO_DData0_4LSBitsRead(void) +{ + return (CRYPTO->DSTATUS & _CRYPTO_DSTATUS_DDATA0LSBS_MASK) + >> _CRYPTO_DSTATUS_DDATA0LSBS_SHIFT; +} + +/***************************************************************************//** + * @brief + * Read 260 bits from the DDATA0 register. + * + * @details + * This functions reads 260 bits from the DDATA0 register in the CRYPTO + * module. The data value is typically output from a big integer operation + * (see crypto instructions) when the result width is set to 260 bits by + * calling @ref CRYPTO_ResultWidthSet(cryptoResult260Bits); + * + * @param[out] val Location where to store the value in memory. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_DData0Read260(CRYPTO_Data260_TypeDef val) +{ + CRYPTO_DDataRead(cryptoRegDDATA0, val); + val[8] = (CRYPTO->DSTATUS & _CRYPTO_DSTATUS_DDATA0MSBS_MASK) + >> _CRYPTO_DSTATUS_DDATA0MSBS_SHIFT; +} + +/***************************************************************************//** + * @brief + * Write 260 bits to the DDATA0 register. + * + * @details + * This functions writes 260 bits to the DDATA0 register in the CRYPTO + * module. The data value is typically input to a big integer operation + * (see crypto instructions) when the result width is set to 260 bits by + * calling @ref CRYPTO_ResultWidthSet(cryptoResult260Bits); + * + * @param[out] val Location where of the value in memory. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_DData0Write260(const CRYPTO_Data260_TypeDef val) +{ + CRYPTO_DDataWrite(cryptoRegDDATA0, val); + CRYPTO->DDATA0BYTE32 = val[8] & _CRYPTO_DDATA0BYTE32_DDATA0BYTE32_MASK; +} + +/***************************************************************************//** + * @brief + * Quick read the MSbit of the DDATA1 register. + * + * @details + * This function reads the most significant bit (bit 255) of the DDATA1 + * register via the DDATA1MSB bit field in the DSTATUS register. This can + * be used to quickly check the signedness of a big integer resident in the + * CRYPTO module. + * + * @return + * Returns 'true' if MSbit is 1, and 'false' if MSbit is 0. + ******************************************************************************/ +__STATIC_INLINE bool CRYPTO_DData1_MSBitRead(void) +{ + return (CRYPTO->DSTATUS & _CRYPTO_DSTATUS_DDATA1MSB_MASK) + >> _CRYPTO_DSTATUS_DDATA1MSB_SHIFT; +} + +/***************************************************************************//** + * @brief + * Load a sequence of instructions to be executed on the current values in + * the data registers. + * + * @details + * This function loads a sequence of instructions to the crypto module. The + * instructions will be executed when the CRYPTO_InstructionSequenceExecute + * function is called. The first END marks the end of the sequence. + * + * @param[in] instructionSequence Instruction sequence to load. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_InstructionSequenceLoad(const CRYPTO_InstructionSequence_TypeDef instructionSequence) +{ + const uint32_t * pas = (const uint32_t *) instructionSequence; + + CRYPTO->SEQ0 = pas[0]; + CRYPTO->SEQ1 = pas[1]; + CRYPTO->SEQ2 = pas[2]; + CRYPTO->SEQ3 = pas[3]; + CRYPTO->SEQ4 = pas[4]; +} + +/***************************************************************************//** + * @brief + * Execute the current programmed instruction sequence. + * + * @details + * This function starts the execution of the current instruction sequence + * in the CRYPTO module. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_InstructionSequenceExecute(void) +{ + /* Start the command sequence. */ + CRYPTO->CMD = CRYPTO_CMD_SEQSTART; +} + +/***************************************************************************//** + * @brief + * Check whether the execution of an instruction sequence has completed. + * + * @details + * This function checks whether an instruction sequence has completed. + * + * @return + * Returns 'true' if the instruction sequence is done, and 'false' if not. + ******************************************************************************/ +__STATIC_INLINE bool CRYPTO_InstructionSequenceDone(void) +{ + /* Return true if operation has completed. */ + return !(CRYPTO->STATUS + & (CRYPTO_STATUS_INSTRRUNNING | CRYPTO_STATUS_SEQRUNNING)); +} + +/***************************************************************************//** + * @brief + * Wait for completion of the current sequence of instructions. + * + * @details + * This function "busy"-waits until the execution of the ongoing instruction + * sequence has completed. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_InstructionSequenceWait(void) +{ + while (!CRYPTO_InstructionSequenceDone()) + ; +} + +/***************************************************************************//** + * @brief + * Wait for completion of the current command. + * + * @details + * This function "busy"-waits until the execution of the ongoing instruction + * has completed. + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_InstructionWait(void) +{ + /* Wait for completion */ + while (!(CRYPTO->IF & CRYPTO_IF_INSTRDONE)) + ; + CRYPTO->IFC = CRYPTO_IF_INSTRDONE; +} + +void CRYPTO_SHA_1(const uint8_t * msg, + uint64_t msgLen, + CRYPTO_SHA1_Digest_TypeDef digest); + +void CRYPTO_SHA_256(const uint8_t * msg, + uint64_t msgLen, + CRYPTO_SHA256_Digest_TypeDef digest); + +void CRYPTO_Mul(uint32_t * A, int aSize, + uint32_t * B, int bSize, + uint32_t * R, int rSize); + +void CRYPTO_AES_CBC128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt); + +void CRYPTO_AES_CBC256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt); + +void CRYPTO_AES_CFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt); + +void CRYPTO_AES_CFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt); + +void CRYPTO_AES_CTR128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc); + +void CRYPTO_AES_CTR256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc); + +void CRYPTO_AES_CTRUpdate32Bit(uint8_t * ctr); +void CRYPTO_AES_DecryptKey128(uint8_t * out, const uint8_t * in); +void CRYPTO_AES_DecryptKey256(uint8_t * out, const uint8_t * in); + +void CRYPTO_AES_ECB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt); + +void CRYPTO_AES_ECB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt); + +void CRYPTO_AES_OFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv); + +void CRYPTO_AES_OFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv); + +/***************************************************************************//** + * @brief + * Clear one or more pending CRYPTO interrupts. + * + * @param[in] flags + * Pending CRYPTO interrupt source to clear. Use a bitwise logic OR combination of + * valid interrupt flags for the CRYPTO module (CRYPTO_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_IntClear(uint32_t flags) +{ + CRYPTO->IFC = flags; +} + +/***************************************************************************//** + * @brief + * Disable one or more CRYPTO interrupts. + * + * @param[in] flags + * CRYPTO interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the CRYPTO module (CRYPTO_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_IntDisable(uint32_t flags) +{ + CRYPTO->IEN &= ~(flags); +} + +/***************************************************************************//** + * @brief + * Enable one or more CRYPTO interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using CRYPTO_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * CRYPTO interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the CRYPTO module (CRYPTO_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_IntEnable(uint32_t flags) +{ + CRYPTO->IEN |= flags; +} + +/***************************************************************************//** + * @brief + * Get pending CRYPTO interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * CRYPTO interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the CRYPTO module (CRYPTO_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t CRYPTO_IntGet(void) +{ + return CRYPTO->IF; +} + +/***************************************************************************//** + * @brief + * Get enabled and pending CRYPTO interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled CRYPTO interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in CRYPTO_IEN and + * - the pending interrupt flags CRYPTO_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t CRYPTO_IntGetEnabled(void) +{ + return CRYPTO->IF & CRYPTO->IEN; +} + +/***************************************************************************//** + * @brief + * Set one or more pending CRYPTO interrupts from SW. + * + * @param[in] flags + * CRYPTO interrupt sources to set to pending. Use a bitwise logic OR combination + * of valid interrupt flags for the CRYPTO module (CRYPTO_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void CRYPTO_IntSet(uint32_t flags) +{ + CRYPTO->IFS = flags; +} + +/******************************************************************************* + ***** Static inline wrappers for CRYPTO AES functions in order to ***** + ***** preserve backwards compatibility with AES module API functions. ***** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * AES Cipher-block chaining (CBC) cipher mode encryption/decryption, + * 128 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CBC128 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CBC128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO_AES_CBC128(out, in, len, key, iv, encrypt); +} + +/***************************************************************************//** + * @brief + * AES Cipher-block chaining (CBC) cipher mode encryption/decryption, 256 bit + * key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CBC256 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CBC256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO_AES_CBC256(out, in, len, key, iv, encrypt); +} + +/***************************************************************************//** + * @brief + * AES Cipher feedback (CFB) cipher mode encryption/decryption, 128 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CFB128 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO_AES_CFB128(out, in, len, key, iv, encrypt); +} + +/***************************************************************************//** + * @brief + * AES Cipher feedback (CFB) cipher mode encryption/decryption, 256 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CFB256 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO_AES_CFB256(out, in, len, key, iv, encrypt); +} + +/***************************************************************************//** + * @brief + * AES Counter (CTR) cipher mode encryption/decryption, 128 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CTR128 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CTR128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc) +{ + CRYPTO_AES_CTR128(out, in, len, key, ctr, ctrFunc); +} + +/***************************************************************************//** + * @brief + * AES Counter (CTR) cipher mode encryption/decryption, 256 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CTR256 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CTR256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc) +{ + CRYPTO_AES_CTR256(out, in, len, key, ctr, ctrFunc); +} + +/***************************************************************************//** + * @brief + * Update last 32 bits of 128 bit counter, by incrementing with 1. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_CTRUpdate32Bit instead. + ******************************************************************************/ +__STATIC_INLINE void AES_CTRUpdate32Bit(uint8_t * ctr) +{ + CRYPTO_AES_CTRUpdate32Bit(ctr); +} + +/***************************************************************************//** + * @brief + * Generate 128 bit AES decryption key from 128 bit encryption key. The + * decryption key is used for some cipher modes when decrypting. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_DecryptKey128 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_DecryptKey128(uint8_t * out, const uint8_t * in) +{ + CRYPTO_AES_DecryptKey128(out, in); +} + +/***************************************************************************//** + * @brief + * Generate 256 bit AES decryption key from 256 bit encryption key. The + * decryption key is used for some cipher modes when decrypting. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_DecryptKey256 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_DecryptKey256(uint8_t * out, const uint8_t * in) +{ + CRYPTO_AES_DecryptKey256(out, in); +} + +/***************************************************************************//** + * @brief + * AES Electronic Codebook (ECB) cipher mode encryption/decryption, + * 128 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_ECB128 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_ECB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt) +{ + CRYPTO_AES_ECB128(out, in, len, key, encrypt); +} + +/***************************************************************************//** + * @brief + * AES Electronic Codebook (ECB) cipher mode encryption/decryption, + * 256 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_ECB256 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_ECB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt) +{ + CRYPTO_AES_ECB256(out, in, len, key, encrypt); +} + +/***************************************************************************//** + * @brief + * AES Output feedback (OFB) cipher mode encryption/decryption, 128 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_OFB128 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_OFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv) +{ + CRYPTO_AES_OFB128(out, in, len, key, iv); +} + +/***************************************************************************//** + * @brief + * AES Output feedback (OFB) cipher mode encryption/decryption, 256 bit key. + * + * @deprecated + * This function is present to preserve backwards compatibility. Use + * @ref CRYPTO_AES_OFB256 instead. + ******************************************************************************/ +__STATIC_INLINE void AES_OFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv) +{ + CRYPTO_AES_OFB256(out, in, len, key, iv); +} + +#ifdef __cplusplus +} +#endif + +/** @} (end addtogroup CRYPTO) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(CRYPTO_COUNT) && (CRYPTO_COUNT > 0) */ + +#endif /* __SILICON_LABS_EM_CRYPTO_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_dac.h b/cpu/efm32_common/emlib/inc/em_dac.h new file mode 100644 index 0000000000000..51f3f96d867d0 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_dac.h @@ -0,0 +1,424 @@ +/***************************************************************************//** + * @file em_dac.h + * @brief Digital to Analog Converter (DAC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_DAC_H__ +#define __SILICON_LABS_EM_DAC_H__ + +#include "em_device.h" + +#if defined(DAC_COUNT) && (DAC_COUNT > 0) + +#include "em_assert.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup DAC + * @{ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of DAC register block pointer reference for assert statements. */ +#define DAC_REF_VALID(ref) ((ref) == DAC0) + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Conversion mode. */ +typedef enum +{ + dacConvModeContinuous = _DAC_CTRL_CONVMODE_CONTINUOUS, /**< Continuous mode. */ + dacConvModeSampleHold = _DAC_CTRL_CONVMODE_SAMPLEHOLD, /**< Sample/hold mode. */ + dacConvModeSampleOff = _DAC_CTRL_CONVMODE_SAMPLEOFF /**< Sample/shut off mode. */ +} DAC_ConvMode_TypeDef; + +/** Output mode. */ +typedef enum +{ + dacOutputDisable = _DAC_CTRL_OUTMODE_DISABLE, /**< Output to pin and ADC disabled. */ + dacOutputPin = _DAC_CTRL_OUTMODE_PIN, /**< Output to pin only. */ + dacOutputADC = _DAC_CTRL_OUTMODE_ADC, /**< Output to ADC only */ + dacOutputPinADC = _DAC_CTRL_OUTMODE_PINADC /**< Output to pin and ADC. */ +} DAC_Output_TypeDef; + + +/** Peripheral Reflex System signal used to trigger single sample. */ +typedef enum +{ + dacPRSSELCh0 = _DAC_CH0CTRL_PRSSEL_PRSCH0, /**< PRS channel 0. */ + dacPRSSELCh1 = _DAC_CH0CTRL_PRSSEL_PRSCH1, /**< PRS channel 1. */ + dacPRSSELCh2 = _DAC_CH0CTRL_PRSSEL_PRSCH2, /**< PRS channel 2. */ + dacPRSSELCh3 = _DAC_CH0CTRL_PRSSEL_PRSCH3, /**< PRS channel 3. */ +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH4 ) + dacPRSSELCh4 = _DAC_CH0CTRL_PRSSEL_PRSCH4, /**< PRS channel 4. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH5 ) + dacPRSSELCh5 = _DAC_CH0CTRL_PRSSEL_PRSCH5, /**< PRS channel 5. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH6 ) + dacPRSSELCh6 = _DAC_CH0CTRL_PRSSEL_PRSCH6, /**< PRS channel 6. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH7 ) + dacPRSSELCh7 = _DAC_CH0CTRL_PRSSEL_PRSCH7, /**< PRS channel 7. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH8 ) + dacPRSSELCh8 = _DAC_CH0CTRL_PRSSEL_PRSCH8, /**< PRS channel 8. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH9 ) + dacPRSSELCh9 = _DAC_CH0CTRL_PRSSEL_PRSCH9, /**< PRS channel 9. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH10 ) + dacPRSSELCh10 = _DAC_CH0CTRL_PRSSEL_PRSCH10, /**< PRS channel 10. */ +#endif +#if defined( _DAC_CH0CTRL_PRSSEL_PRSCH11 ) + dacPRSSELCh11 = _DAC_CH0CTRL_PRSSEL_PRSCH11, /**< PRS channel 11. */ +#endif +} DAC_PRSSEL_TypeDef; + + +/** Reference voltage for DAC. */ +typedef enum +{ + dacRef1V25 = _DAC_CTRL_REFSEL_1V25, /**< Internal 1.25V bandgap reference. */ + dacRef2V5 = _DAC_CTRL_REFSEL_2V5, /**< Internal 2.5V bandgap reference. */ + dacRefVDD = _DAC_CTRL_REFSEL_VDD /**< VDD reference. */ +} DAC_Ref_TypeDef; + + +/** Refresh interval. */ +typedef enum +{ + dacRefresh8 = _DAC_CTRL_REFRSEL_8CYCLES, /**< Refresh every 8 prescaled cycles. */ + dacRefresh16 = _DAC_CTRL_REFRSEL_16CYCLES, /**< Refresh every 16 prescaled cycles. */ + dacRefresh32 = _DAC_CTRL_REFRSEL_32CYCLES, /**< Refresh every 32 prescaled cycles. */ + dacRefresh64 = _DAC_CTRL_REFRSEL_64CYCLES /**< Refresh every 64 prescaled cycles. */ +} DAC_Refresh_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** DAC init structure, common for both channels. */ +typedef struct +{ + /** Refresh interval. Only used if REFREN bit set for a DAC channel. */ + DAC_Refresh_TypeDef refresh; + + /** Reference voltage to use. */ + DAC_Ref_TypeDef reference; + + /** Output mode */ + DAC_Output_TypeDef outMode; + + /** Conversion mode. */ + DAC_ConvMode_TypeDef convMode; + + /** + * Prescaler used to get DAC clock. Derived as follows: + * DACclk=HFPERclk/(2^prescale). The DAC clock should be <= 1MHz. + */ + uint8_t prescale; + + /** Enable/disable use of low pass filter on output. */ + bool lpEnable; + + /** Enable/disable reset of prescaler on ch0 start. */ + bool ch0ResetPre; + + /** Enable/disable output enable control by CH1 PRS signal. */ + bool outEnablePRS; + + /** Enable/disable sine mode. */ + bool sineEnable; + + /** Select if single ended or differential mode. */ + bool diff; +} DAC_Init_TypeDef; + +/** Default config for DAC init structure. */ +#define DAC_INIT_DEFAULT \ +{ \ + dacRefresh8, /* Refresh every 8 prescaled cycles. */ \ + dacRef1V25, /* 1.25V internal reference. */ \ + dacOutputPin, /* Output to pin only. */ \ + dacConvModeContinuous, /* Continuous mode. */ \ + 0, /* No prescaling. */ \ + false, /* Do not enable low pass filter. */ \ + false, /* Do not reset prescaler on ch0 start. */ \ + false, /* DAC output enable always on. */ \ + false, /* Disable sine mode. */ \ + false /* Single ended mode. */ \ +} + + +/** DAC channel init structure. */ +typedef struct +{ + /** Enable channel. */ + bool enable; + + /** + * Peripheral reflex system trigger enable. If false, channel is triggered + * by writing to CHnDATA. + */ + bool prsEnable; + + /** + * Enable/disable automatic refresh of channel. Refresh interval must be + * defined in common control init, please see DAC_Init(). + */ + bool refreshEnable; + + /** + * Peripheral reflex system trigger selection. Only applicable if @p prsEnable + * is enabled. + */ + DAC_PRSSEL_TypeDef prsSel; +} DAC_InitChannel_TypeDef; + +/** Default config for DAC channel init structure. */ +#define DAC_INITCHANNEL_DEFAULT \ +{ \ + false, /* Leave channel disabled when init done. */ \ + false, /* Disable PRS triggering. */ \ + false, /* Channel not refreshed automatically. */ \ + dacPRSSELCh0 /* Select PRS ch0 (if PRS triggering enabled). */ \ +} + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void DAC_Enable(DAC_TypeDef *dac, unsigned int ch, bool enable); +void DAC_Init(DAC_TypeDef *dac, const DAC_Init_TypeDef *init); +void DAC_InitChannel(DAC_TypeDef *dac, + const DAC_InitChannel_TypeDef *init, + unsigned int ch); +void DAC_ChannelOutputSet(DAC_TypeDef *dac, + unsigned int channel, + uint32_t value); + +/***************************************************************************//** + * @brief + * Set the output signal of DAC channel 0 to a given value. + * + * @details + * This function sets the output signal of DAC channel 0 by writing @p value + * to the CH0DATA register. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] value + * Value to write to the channel 0 output register CH0DATA. + ******************************************************************************/ +__STATIC_INLINE void DAC_Channel0OutputSet( DAC_TypeDef *dac, + uint32_t value ) +{ + EFM_ASSERT(value<=_DAC_CH0DATA_MASK); + dac->CH0DATA = value; +} + + +/***************************************************************************//** + * @brief + * Set the output signal of DAC channel 1 to a given value. + * + * @details + * This function sets the output signal of DAC channel 1 by writing @p value + * to the CH1DATA register. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] value + * Value to write to the channel 1 output register CH1DATA. + ******************************************************************************/ +__STATIC_INLINE void DAC_Channel1OutputSet( DAC_TypeDef *dac, + uint32_t value ) +{ + EFM_ASSERT(value<=_DAC_CH1DATA_MASK); + dac->CH1DATA = value; +} + + +/***************************************************************************//** + * @brief + * Clear one or more pending DAC interrupts. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] flags + * Pending DAC interrupt source to clear. Use a bitwise logic OR combination of + * valid interrupt flags for the DAC module (DAC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void DAC_IntClear(DAC_TypeDef *dac, uint32_t flags) +{ + dac->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more DAC interrupts. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] flags + * DAC interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the DAC module (DAC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void DAC_IntDisable(DAC_TypeDef *dac, uint32_t flags) +{ + dac->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more DAC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using DAC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] flags + * DAC interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the DAC module (DAC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void DAC_IntEnable(DAC_TypeDef *dac, uint32_t flags) +{ + dac->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending DAC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @return + * DAC interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the DAC module (DAC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t DAC_IntGet(DAC_TypeDef *dac) +{ + return dac->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending DAC interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled DAC interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in DACx_IEN_nnn + * register (DACx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the DAC module + * (DACx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t DAC_IntGetEnabled(DAC_TypeDef *dac) +{ + uint32_t ien; + + /* Store DAC->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = dac->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return dac->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending DAC interrupts from SW. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] flags + * DAC interrupt sources to set to pending. Use a bitwise logic OR combination + * of valid interrupt flags for the DAC module (DAC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void DAC_IntSet(DAC_TypeDef *dac, uint32_t flags) +{ + dac->IFS = flags; +} + +uint8_t DAC_PrescaleCalc(uint32_t dacFreq, uint32_t hfperFreq); +void DAC_Reset(DAC_TypeDef *dac); + +/** @} (end addtogroup DAC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(DAC_COUNT) && (DAC_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_DAC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_dbg.h b/cpu/efm32_common/emlib/inc/em_dbg.h new file mode 100644 index 0000000000000..aacc838ab8425 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_dbg.h @@ -0,0 +1,93 @@ +/***************************************************************************//** + * @file em_dbg.h + * @brief Debug (DBG) API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#ifndef __SILICON_LABS_EM_DBG_H__ +#define __SILICON_LABS_EM_DBG_H__ + +#include +#include "em_device.h" + +#if defined( CoreDebug_DHCSR_C_DEBUGEN_Msk ) + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup DBG + * @{ + ******************************************************************************/ + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +#if defined( GPIO_ROUTE_SWCLKPEN ) || defined( GPIO_ROUTEPEN_SWCLKTCKPEN ) +/***************************************************************************//** + * @brief + * Check if a debugger is connected (and debug session activated) + * + * @details + * Used to make run-time decisions depending on whether a debug session + * has been active since last reset, ie using a debug probe or similar. In + * some cases special handling is required in that scenario. + * + * @return + * true if a debug session is active since last reset, otherwise false. + ******************************************************************************/ +__STATIC_INLINE bool DBG_Connected(void) +{ + return (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) ? true : false; +} +#endif + + +#if defined( GPIO_ROUTE_SWOPEN ) || defined( GPIO_ROUTEPEN_SWVPEN ) +void DBG_SWOEnable(unsigned int location); +#endif + +/** @} (end addtogroup DBG) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( CoreDebug_DHCSR_C_DEBUGEN_Msk ) */ + +#endif /* __SILICON_LABS_EM_DBG_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_dma.h b/cpu/efm32_common/emlib/inc/em_dma.h new file mode 100644 index 0000000000000..8590871cb6f58 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_dma.h @@ -0,0 +1,561 @@ +/***************************************************************************//** + * @file em_dma.h + * @brief Direct memory access (DMA) API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_DMA_H__ +#define __SILICON_LABS_EM_DMA_H__ + +#include "em_device.h" +#if defined( DMA_PRESENT ) + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup DMA + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** + * Amount source/destination address should be incremented for each data + * transfer. + */ +typedef enum +{ + dmaDataInc1 = _DMA_CTRL_SRC_INC_BYTE, /**< Increment address 1 byte. */ + dmaDataInc2 = _DMA_CTRL_SRC_INC_HALFWORD, /**< Increment address 2 bytes. */ + dmaDataInc4 = _DMA_CTRL_SRC_INC_WORD, /**< Increment address 4 bytes. */ + dmaDataIncNone = _DMA_CTRL_SRC_INC_NONE /**< Do not increment address. */ +} DMA_DataInc_TypeDef; + + +/** Data sizes (in number of bytes) to be read/written by DMA transfer. */ +typedef enum +{ + dmaDataSize1 = _DMA_CTRL_SRC_SIZE_BYTE, /**< 1 byte DMA transfer size. */ + dmaDataSize2 = _DMA_CTRL_SRC_SIZE_HALFWORD, /**< 2 byte DMA transfer size. */ + dmaDataSize4 = _DMA_CTRL_SRC_SIZE_WORD /**< 4 byte DMA transfer size. */ +} DMA_DataSize_TypeDef; + + +/** Type of DMA transfer. */ +typedef enum +{ + /** Basic DMA cycle. */ + dmaCycleCtrlBasic = _DMA_CTRL_CYCLE_CTRL_BASIC, + /** Auto-request DMA cycle. */ + dmaCycleCtrlAuto = _DMA_CTRL_CYCLE_CTRL_AUTO, + /** Ping-pong DMA cycle. */ + dmaCycleCtrlPingPong = _DMA_CTRL_CYCLE_CTRL_PINGPONG, + /** Memory scatter-gather DMA cycle. */ + dmaCycleCtrlMemScatterGather = _DMA_CTRL_CYCLE_CTRL_MEM_SCATTER_GATHER, + /** Peripheral scatter-gather DMA cycle. */ + dmaCycleCtrlPerScatterGather = _DMA_CTRL_CYCLE_CTRL_PER_SCATTER_GATHER +} DMA_CycleCtrl_TypeDef; + + +/** Number of transfers before controller does new arbitration. */ +typedef enum +{ + dmaArbitrate1 = _DMA_CTRL_R_POWER_1, /**< Arbitrate after 1 DMA transfer. */ + dmaArbitrate2 = _DMA_CTRL_R_POWER_2, /**< Arbitrate after 2 DMA transfers. */ + dmaArbitrate4 = _DMA_CTRL_R_POWER_4, /**< Arbitrate after 4 DMA transfers. */ + dmaArbitrate8 = _DMA_CTRL_R_POWER_8, /**< Arbitrate after 8 DMA transfers. */ + dmaArbitrate16 = _DMA_CTRL_R_POWER_16, /**< Arbitrate after 16 DMA transfers. */ + dmaArbitrate32 = _DMA_CTRL_R_POWER_32, /**< Arbitrate after 32 DMA transfers. */ + dmaArbitrate64 = _DMA_CTRL_R_POWER_64, /**< Arbitrate after 64 DMA transfers. */ + dmaArbitrate128 = _DMA_CTRL_R_POWER_128, /**< Arbitrate after 128 DMA transfers. */ + dmaArbitrate256 = _DMA_CTRL_R_POWER_256, /**< Arbitrate after 256 DMA transfers. */ + dmaArbitrate512 = _DMA_CTRL_R_POWER_512, /**< Arbitrate after 512 DMA transfers. */ + dmaArbitrate1024 = _DMA_CTRL_R_POWER_1024 /**< Arbitrate after 1024 DMA transfers. */ +} DMA_ArbiterConfig_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** + * @brief + * DMA interrupt callback function pointer. + * @details + * Parameters: + * @li channel - The DMA channel the callback function is invoked for. + * @li primary - Indicates if callback is invoked for completion of primary + * (true) or alternate (false) descriptor. This is mainly useful for + * ping-pong DMA cycles, in order to know which descriptor to refresh. + * @li user - User definable reference that may be used to pass information + * to be used by the callback handler. If used, the referenced data must be + * valid at the point when the interrupt handler invokes the callback. + * If callback changes any data in the provided user structure, remember + * that those changes are done in interrupt context, and proper protection + * of data may be required. + */ +typedef void (*DMA_FuncPtr_TypeDef)(unsigned int channel, bool primary, void *user); + + +/** + * @brief + * Callback structure that can be used to define DMA complete actions. + * @details + * A reference to this structure is only stored in the primary descriptor + * for a channel (if callback feature is used). If callback is required + * for both primary and alternate descriptor completion, this must be + * handled by one common callback, using the provided 'primary' parameter + * with the callback function. + */ +typedef struct +{ + /** + * Pointer to callback function to invoke when DMA transfer cycle done. + * Notice that this function is invoked in interrupt context, and therefore + * should be short and non-blocking. + */ + DMA_FuncPtr_TypeDef cbFunc; + + /** User defined pointer to provide with callback function. */ + void *userPtr; + + /** + * For internal use only: Indicates if next callback applies to primary + * or alternate descriptor completion. Mainly useful for ping-pong DMA + * cycles. Set this value to 0 prior to configuring callback handling. + */ + uint8_t primary; +} DMA_CB_TypeDef; + + +/** Configuration structure for a channel. */ +typedef struct +{ + /** + * Select if channel priority is in the high or default priority group + * with respect to arbitration. Within a priority group, lower numbered + * channels have higher priority than higher numbered channels. + */ + bool highPri; + + /** + * Select if interrupt shall be enabled for channel (triggering interrupt + * handler when dma_done signal is asserted). It should normally be + * enabled if using the callback feature for a channel, and disabled if + * not using the callback feature. + */ + bool enableInt; + + /** + * Channel control specifying the source of DMA signals. If accessing + * peripherals, use one of the DMAREQ_nnn defines available for the + * peripheral. Set it to 0 for memory-to-memory DMA cycles. + */ + uint32_t select; + + /** + * @brief + * User definable callback handling configuration. + * @details + * Please refer to structure definition for details. The callback + * is invoked when the specified DMA cycle is complete (when dma_done + * signal asserted). The callback is invoked in interrupt context, + * and should be efficient and non-blocking. Set to NULL to not + * use the callback feature. + * @note + * The referenced structure is used by the interrupt handler, and must + * be available until no longer used. Thus, in most cases it should + * not be located on the stack. + */ + DMA_CB_TypeDef *cb; +} DMA_CfgChannel_TypeDef; + + +/** + * Configuration structure for primary or alternate descriptor + * (not used for scatter-gather DMA cycles). + */ +typedef struct +{ + /** Destination increment size for each DMA transfer */ + DMA_DataInc_TypeDef dstInc; + + /** Source increment size for each DMA transfer */ + DMA_DataInc_TypeDef srcInc; + + /** DMA transfer unit size. */ + DMA_DataSize_TypeDef size; + + /** + * Arbitration rate, ie number of DMA transfers done before rearbitration + * takes place. + */ + DMA_ArbiterConfig_TypeDef arbRate; + + /** + * HPROT signal state, please refer to reference manual, DMA chapter for + * further details. Normally set to 0 if protection is not an issue. + * The following bits are available: + * @li bit 0 - HPROT[1] control for source read accesses, + * privileged/non-privileged access + * @li bit 3 - HPROT[1] control for destination write accesses, + * privileged/non-privileged access + */ + uint8_t hprot; +} DMA_CfgDescr_TypeDef; + + +#if defined( _DMA_LOOP0_MASK ) && defined( _DMA_LOOP1_MASK ) +/** + * Configuration structure for loop mode + */ +typedef struct +{ + /** Enable repeated loop */ + bool enable; + /** Width of transfer, reload value for nMinus1 */ + uint16_t nMinus1; +} DMA_CfgLoop_TypeDef; +#endif + + +#if defined( _DMA_RECT0_MASK ) +/** + * Configuration structure for rectangular copy + */ +typedef struct +{ + /** DMA channel destination stride (width of destination image, distance between lines) */ + uint16_t dstStride; + /** DMA channel source stride (width of source image, distance between lines) */ + uint16_t srcStride; + /** 2D copy height */ + uint16_t height; +} DMA_CfgRect_TypeDef; +#endif + + +/** Configuration structure for alternate scatter-gather descriptor. */ +typedef struct +{ + /** Pointer to location to transfer data from. */ + void *src; + + /** Pointer to location to transfer data to. */ + void *dst; + + /** Destination increment size for each DMA transfer */ + DMA_DataInc_TypeDef dstInc; + + /** Source increment size for each DMA transfer */ + DMA_DataInc_TypeDef srcInc; + + /** DMA transfer unit size. */ + DMA_DataSize_TypeDef size; + + /** + * Arbitration rate, ie number of DMA transfers done before rearbitration + * takes place. + */ + DMA_ArbiterConfig_TypeDef arbRate; + + /** Number of DMA transfers minus 1 to do. Must be <= 1023. */ + uint16_t nMinus1; + + /** + * HPROT signal state, please refer to reference manual, DMA chapter for + * further details. Normally set to 0 if protection is not an issue. + * The following bits are available: + * @li bit 0 - HPROT[1] control for source read accesses, + * privileged/non-privileged access + * @li bit 3 - HPROT[1] control for destination write accesses, + * privileged/non-privileged access + */ + uint8_t hprot; + + /** Specify if a memory or peripheral scatter-gather DMA cycle. Notice + * that this parameter should be the same for all alternate + * descriptors. + * @li true - this is a peripheral scatter-gather cycle + * @li false - this is a memory scatter-gather cycle + */ + bool peripheral; +} DMA_CfgDescrSGAlt_TypeDef; + + +/** DMA init structure */ +typedef struct +{ + /** + * HPROT signal state when accessing the primary/alternate + * descriptors. Normally set to 0 if protection is not an issue. + * The following bits are available: + * @li bit 0 - HPROT[1] control for descriptor accesses (ie when + * the DMA controller accesses the channel control block itself), + * privileged/non-privileged access + */ + uint8_t hprot; + + /** + * Pointer to the controlblock in memory holding descriptors (channel + * control data structures). This memory must be properly aligned + * at a 256 bytes. I.e. the 8 least significant bits must be zero. + * + * Please refer to the reference manual, DMA chapter for more details. + * + * It is possible to provide a smaller memory block, only covering + * those channels actually used, if not all available channels are used. + * Ie, if only using 4 channels (0-3), both primary and alternate + * structures, then only 16*2*4 = 128 bytes must be provided. This + * implementation has however no check if later exceeding such a limit + * by configuring for instance channel 4, in which case memory overwrite + * of some other data will occur. + */ + DMA_DESCRIPTOR_TypeDef *controlBlock; +} DMA_Init_TypeDef; + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void DMA_ActivateAuto(unsigned int channel, + bool primary, + void *dst, + void *src, + unsigned int nMinus1); +void DMA_ActivateBasic(unsigned int channel, + bool primary, + bool useBurst, + void *dst, + void *src, + unsigned int nMinus1); +void DMA_ActivatePingPong(unsigned int channel, + bool useBurst, + void *primDst, + void *primSrc, + unsigned int primNMinus1, + void *altDst, + void *altSrc, + unsigned int altNMinus1); +void DMA_ActivateScatterGather(unsigned int channel, + bool useBurst, + DMA_DESCRIPTOR_TypeDef *altDescr, + unsigned int count); +void DMA_CfgChannel(unsigned int channel, DMA_CfgChannel_TypeDef *cfg); +void DMA_CfgDescr(unsigned int channel, + bool primary, + DMA_CfgDescr_TypeDef *cfg); +#if defined( _DMA_LOOP0_MASK ) && defined( _DMA_LOOP1_MASK ) +void DMA_CfgLoop(unsigned int channel, DMA_CfgLoop_TypeDef *cfg); +#endif + +#if defined( _DMA_RECT0_MASK ) +void DMA_CfgRect(unsigned int channel, DMA_CfgRect_TypeDef *cfg); +#endif + +#if defined( _DMA_LOOP0_MASK ) && defined( _DMA_LOOP1_MASK ) +/***************************************************************************//** + * @brief + * Clear Loop configuration for channel + * + * @param[in] channel + * Channel to reset loop configuration for + ******************************************************************************/ +__STATIC_INLINE void DMA_ResetLoop(unsigned int channel) +{ + /* Clean loop copy operation */ + switch(channel) + { + case 0: + DMA->LOOP0 = _DMA_LOOP0_RESETVALUE; + break; + case 1: + DMA->LOOP1 = _DMA_LOOP1_RESETVALUE; + break; + default: + break; + } +} +#endif + + +#if defined( _DMA_RECT0_MASK ) +/***************************************************************************//** + * @brief + * Clear Rect/2D DMA configuration for channel + * + * @param[in] channel + * Channel to reset loop configuration for + ******************************************************************************/ +__STATIC_INLINE void DMA_ResetRect(unsigned int channel) +{ + (void) channel; + + /* Clear rect copy operation */ + DMA->RECT0 = _DMA_RECT0_RESETVALUE; +} +#endif +void DMA_CfgDescrScatterGather(DMA_DESCRIPTOR_TypeDef *descr, + unsigned int indx, + DMA_CfgDescrSGAlt_TypeDef *cfg); +void DMA_ChannelEnable(unsigned int channel, bool enable); +bool DMA_ChannelEnabled(unsigned int channel); +void DMA_Init(DMA_Init_TypeDef *init); +void DMA_IRQHandler(void); +void DMA_RefreshPingPong(unsigned int channel, + bool primary, + bool useBurst, + void *dst, + void *src, + unsigned int nMinus1, + bool last); +void DMA_Reset(void); + +/***************************************************************************//** + * @brief + * Clear one or more pending DMA interrupts. + * + * @param[in] flags + * Pending DMA interrupt sources to clear. Use one or more valid + * interrupt flags for the DMA module (DMA_IFC_nnn). + ******************************************************************************/ +__STATIC_INLINE void DMA_IntClear(uint32_t flags) +{ + DMA->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more DMA interrupts. + * + * @param[in] flags + * DMA interrupt sources to disable. Use one or more valid + * interrupt flags for the DMA module (DMA_IEN_nnn). + ******************************************************************************/ +__STATIC_INLINE void DMA_IntDisable(uint32_t flags) +{ + DMA->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more DMA interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using DMA_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * DMA interrupt sources to enable. Use one or more valid + * interrupt flags for the DMA module (DMA_IEN_nnn). + ******************************************************************************/ +__STATIC_INLINE void DMA_IntEnable(uint32_t flags) +{ + DMA->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending DMA interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * DMA interrupt sources pending. Returns one or more valid + * interrupt flags for the DMA module (DMA_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t DMA_IntGet(void) +{ + return DMA->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending DMA interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled DMA interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in DMA_IEN and + * - the pending interrupt flags DMA_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t DMA_IntGetEnabled(void) +{ + uint32_t ien; + + ien = DMA->IEN; + return DMA->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending DMA interrupts + * + * @param[in] flags + * DMA interrupt sources to set to pending. Use one or more valid + * interrupt flags for the DMA module (DMA_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void DMA_IntSet(uint32_t flags) +{ + DMA->IFS = flags; +} + +/** @} (end addtogroup DMA) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( DMA_PRESENT ) */ +#endif /* __SILICON_LABS_EM_DMA_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_ebi.h b/cpu/efm32_common/emlib/inc/em_ebi.h new file mode 100644 index 0000000000000..84dc9a3da8f90 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_ebi.h @@ -0,0 +1,844 @@ +/***************************************************************************//** + * @file em_ebi.h + * @brief External Bus Iterface (EBI) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_EBI_H__ +#define __SILICON_LABS_EM_EBI_H__ + +#include "em_device.h" +#if defined(EBI_COUNT) && (EBI_COUNT > 0) + +#include +#include +#include "em_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup EBI + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @verbatim + * + * --------- --------- + * | | /| |\ | Ext. | + * | EBI | / --------- \ | Async | + * | | \ --------- / | Device| + * | | \| |/ | | + * --------- --------- + * Parallel interface + * + * @endverbatim + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +#define EBI_BANK0 (uint32_t)(1 << 1) /**< EBI address bank 0 */ +#define EBI_BANK1 (uint32_t)(1 << 2) /**< EBI address bank 1 */ +#define EBI_BANK2 (uint32_t)(1 << 3) /**< EBI address bank 2 */ +#define EBI_BANK3 (uint32_t)(1 << 4) /**< EBI address bank 3 */ + +#define EBI_CS0 (uint32_t)(1 << 1) /**< EBI chip select line 0 */ +#define EBI_CS1 (uint32_t)(1 << 2) /**< EBI chip select line 1 */ +#define EBI_CS2 (uint32_t)(1 << 3) /**< EBI chip select line 2 */ +#define EBI_CS3 (uint32_t)(1 << 4) /**< EBI chip select line 3 */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** EBI Mode of operation */ +typedef enum +{ + /** 8 data bits, 8 address bits */ + ebiModeD8A8 = EBI_CTRL_MODE_D8A8, + /** 16 data bits, 16 address bits, using address latch enable */ + ebiModeD16A16ALE = EBI_CTRL_MODE_D16A16ALE, + /** 8 data bits, 24 address bits, using address latch enable */ + ebiModeD8A24ALE = EBI_CTRL_MODE_D8A24ALE, +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** Mode D16 */ + ebiModeD16 = EBI_CTRL_MODE_D16, +#endif +} EBI_Mode_TypeDef; + +/** EBI Polarity configuration */ +typedef enum +{ + /** Active Low */ + ebiActiveLow = 0, + /** Active High */ + ebiActiveHigh = 1 +} EBI_Polarity_TypeDef; + +/** EBI Pin Line types */ +typedef enum +{ + /** Address Ready line */ + ebiLineARDY, + /** Address Latch Enable line */ + ebiLineALE, + /** Write Enable line */ + ebiLineWE, + /** Read Enable line */ + ebiLineRE, + /** Chip Select line */ + ebiLineCS, +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** BL line */ + ebiLineBL, +#endif +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** TFT VSYNC line */ + ebiLineTFTVSync, + /** TFT HSYNC line */ + ebiLineTFTHSync, + /** TFT Data enable line */ + ebiLineTFTDataEn, + /** TFT DCLK line */ + ebiLineTFTDClk, + /** TFT Chip select line */ + ebiLineTFTCS, +#endif +} EBI_Line_TypeDef; + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +/** Address Pin Enable, lower limit - lower range of pins to enable */ +typedef enum +{ + /** Adress lines EBI_A[0] and upwards are enabled by APEN */ + ebiALowA0 = EBI_ROUTE_ALB_A0, + /** Adress lines EBI_A[8] and upwards are enabled by APEN */ + ebiALowA8 = EBI_ROUTE_ALB_A8, + /** Adress lines EBI_A[16] and upwards are enabled by APEN */ + ebiALowA16 = EBI_ROUTE_ALB_A16, + /** Adress lines EBI_A[24] and upwards are enabled by APEN */ + ebiALowA24 = EBI_ROUTE_ALB_A24, +} EBI_ALow_TypeDef; + +/** Adress Pin Enable, high limit - higher limit of pins to enable */ +typedef enum +{ + /** All EBI_A pins are disabled */ + ebiAHighA0 = EBI_ROUTE_APEN_A0, + /** All EBI_A[4:ALow] are enabled */ + ebiAHighA5 = EBI_ROUTE_APEN_A5, + /** All EBI_A[5:ALow] are enabled */ + ebiAHighA6 = EBI_ROUTE_APEN_A6, + /** All EBI_A[6:ALow] are enabled */ + ebiAHighA7 = EBI_ROUTE_APEN_A7, + /** All EBI_A[7:ALow] are enabled */ + ebiAHighA8 = EBI_ROUTE_APEN_A8, + /** All EBI_A[8:ALow] are enabled */ + ebiAHighA9 = EBI_ROUTE_APEN_A9, + /** All EBI_A[9:ALow] are enabled */ + ebiAHighA10 = EBI_ROUTE_APEN_A10, + /** All EBI_A[10:ALow] are enabled */ + ebiAHighA11 = EBI_ROUTE_APEN_A11, + /** All EBI_A[11:ALow] are enabled */ + ebiAHighA12 = EBI_ROUTE_APEN_A12, + /** All EBI_A[12:ALow] are enabled */ + ebiAHighA13 = EBI_ROUTE_APEN_A13, + /** All EBI_A[13:ALow] are enabled */ + ebiAHighA14 = EBI_ROUTE_APEN_A14, + /** All EBI_A[14:ALow] are enabled */ + ebiAHighA15 = EBI_ROUTE_APEN_A15, + /** All EBI_A[15:ALow] are enabled */ + ebiAHighA16 = EBI_ROUTE_APEN_A16, + /** All EBI_A[16:ALow] are enabled */ + ebiAHighA17 = EBI_ROUTE_APEN_A17, + /** All EBI_A[17:ALow] are enabled */ + ebiAHighA18 = EBI_ROUTE_APEN_A18, + /** All EBI_A[18:ALow] are enabled */ + ebiAHighA19 = EBI_ROUTE_APEN_A19, + /** All EBI_A[19:ALow] are enabled */ + ebiAHighA20 = EBI_ROUTE_APEN_A20, + /** All EBI_A[20:ALow] are enabled */ + ebiAHighA21 = EBI_ROUTE_APEN_A21, + /** All EBI_A[21:ALow] are enabled */ + ebiAHighA22 = EBI_ROUTE_APEN_A22, + /** All EBI_A[22:ALow] are enabled */ + ebiAHighA23 = EBI_ROUTE_APEN_A23, + /** All EBI_A[23:ALow] are enabled */ + ebiAHighA24 = EBI_ROUTE_APEN_A24, + /** All EBI_A[24:ALow] are enabled */ + ebiAHighA25 = EBI_ROUTE_APEN_A25, + /** All EBI_A[25:ALow] are enabled */ + ebiAHighA26 = EBI_ROUTE_APEN_A26, + /** All EBI_A[26:ALow] are enabled */ + ebiAHighA27 = EBI_ROUTE_APEN_A27, + /** All EBI_A[27:ALow] are enabled */ + ebiAHighA28 = EBI_ROUTE_APEN_A28, +} EBI_AHigh_TypeDef; + +/** EBI I/O Alternate Pin Location */ +typedef enum { + /** EBI PIN I/O Location 0 */ + ebiLocation0 = EBI_ROUTE_LOCATION_LOC0, + /** EBI PIN I/O Location 1 */ + ebiLocation1 = EBI_ROUTE_LOCATION_LOC1, + /** EBI PIN I/O Location 2 */ + ebiLocation2 = EBI_ROUTE_LOCATION_LOC2 +} EBI_Location_TypeDef; +#endif + +/* TFT support */ +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +/** EBI TFT Graphics Bank Select */ +typedef enum +{ + /** Memory BANK0 contains frame buffer */ + ebiTFTBank0 = EBI_TFTCTRL_BANKSEL_BANK0, + /** Memory BANK1 contains frame buffer */ + ebiTFTBank1 = EBI_TFTCTRL_BANKSEL_BANK1, + /** Memory BANK2 contains frame buffer */ + ebiTFTBank2 = EBI_TFTCTRL_BANKSEL_BANK2, + /** Memory BANK3 contains frame buffer */ + ebiTFTBank3 = EBI_TFTCTRL_BANKSEL_BANK3 +} EBI_TFTBank_TypeDef; + +/** Masking and Alpha blending source color*/ +typedef enum +{ + /** Use memory as source color for masking/alpha blending */ + ebiTFTColorSrcMem = EBI_TFTCTRL_COLOR1SRC_MEM, + /** Use PIXEL1 register as source color for masking/alpha blending */ + ebiTFTColorSrcPixel1 = EBI_TFTCTRL_COLOR1SRC_PIXEL1, +} EBI_TFTColorSrc_TypeDef; + +/** Bus Data Interleave Mode */ +typedef enum +{ + /** Unlimited interleaved accesses per EBI_DCLK period. Can cause jitter */ + ebiTFTInterleaveUnlimited = EBI_TFTCTRL_INTERLEAVE_UNLIMITED, + /** Allow 1 interleaved access per EBI_DCLK period */ + ebiTFTInterleaveOnePerDClk = EBI_TFTCTRL_INTERLEAVE_ONEPERDCLK, + /** Only allow accesses during porch periods */ + ebiTFTInterleavePorch = EBI_TFTCTRL_INTERLEAVE_PORCH, +} EBI_TFTInterleave_TypeDef; + +/** Control frame base pointer copy */ +typedef enum +{ + /** Trigger update of frame buffer pointer on vertical sync */ + ebiTFTFrameBufTriggerVSync = EBI_TFTCTRL_FBCTRIG_VSYNC, + /** Trigger update of frame buffer pointer on horizontal sync */ + ebiTFTFrameBufTriggerHSync = EBI_TFTCTRL_FBCTRIG_HSYNC, +} EBI_TFTFrameBufTrigger_TypeDef; + +/** Control of mask and alpha blending mode */ +typedef enum +{ + /** Masking and blending are disabled */ + ebiTFTMBDisabled = EBI_TFTCTRL_MASKBLEND_DISABLED, + /** Internal masking */ + ebiTFTMBIMask = EBI_TFTCTRL_MASKBLEND_IMASK, + /** Internal alpha blending */ + ebiTFTMBIAlpha = EBI_TFTCTRL_MASKBLEND_IALPHA, + /** Internal masking and alpha blending are enabled */ + ebiTFTMBIMaskAlpha = EBI_TFTCTRL_MASKBLEND_IMASKIALPHA, + /** External masking */ + ebiTFTMBEMask = EBI_TFTCTRL_MASKBLEND_EMASK, + /** External alpha blending */ + ebiTFTMBEAlpha = EBI_TFTCTRL_MASKBLEND_EALPHA, + /** External masking and alpha blending */ + ebiTFTMBEMaskAlpha = EBI_TFTCTRL_MASKBLEND_EMASKEALPHA, +} EBI_TFTMaskBlend_TypeDef; + +/** TFT Direct Drive mode */ +typedef enum +{ + /** Disabled */ + ebiTFTDDModeDisabled = EBI_TFTCTRL_DD_DISABLED, + /** Direct Drive from internal memory */ + ebiTFTDDModeInternal = EBI_TFTCTRL_DD_INTERNAL, + /** Direct Drive from external memory */ + ebiTFTDDModeExternal = EBI_TFTCTRL_DD_EXTERNAL, +} EBI_TFTDDMode_TypeDef; + +/** TFT Data Increment Width */ +typedef enum +{ + /** Pixel increments are 1 byte at a time */ + ebiTFTWidthByte = EBI_TFTCTRL_WIDTH_BYTE, + /** Pixel increments are 2 bytes (half word) */ + ebiTFTWidthHalfWord = EBI_TFTCTRL_WIDTH_HALFWORD, +} EBI_TFTWidth_TypeDef; + +#endif + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** EBI Initialization structure */ +typedef struct +{ + /** EBI operation mode, data and address limits */ + EBI_Mode_TypeDef mode; + /** Address Ready pin polarity, active high or low */ + EBI_Polarity_TypeDef ardyPolarity; + /** Address Latch Enable pin polarity, active high or low */ + EBI_Polarity_TypeDef alePolarity; + /** Write Enable pin polarity, active high or low */ + EBI_Polarity_TypeDef wePolarity; + /** Read Enable pin polarity, active high or low */ + EBI_Polarity_TypeDef rePolarity; + /** Chip Select pin polarity, active high or low */ + EBI_Polarity_TypeDef csPolarity; +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** Byte Lane pin polaritym, active high or low */ + EBI_Polarity_TypeDef blPolarity; + /** Flag to enable or disable Byte Lane support */ + bool blEnable; + /** Flag to enable or disable idle state insertion between transfers */ + bool noIdle; +#endif + /** Flag to enable or disable Address Ready support */ + bool ardyEnable; + /** Set to turn off 32 cycle timeout ability */ + bool ardyDisableTimeout; + /** Mask of flags which selects address banks to configure EBI_BANK<0-3> */ + uint32_t banks; + /** Mask of flags which selects chip select lines to configure EBI_CS<0-3> */ + uint32_t csLines; + /** Number of cycles address is held after Adress Latch Enable is asserted */ + int addrSetupCycles; + /** Number of cycles address is driven onto the ADDRDAT bus before ALE is asserted */ + int addrHoldCycles; +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** Enable or disables half cycle duration of the ALE strobe in the last address setup cycle */ + bool addrHalfALE; +#endif + /** Number of cycles for address setup before REn is asserted */ + int readSetupCycles; + /** Number of cycles REn is held active */ + int readStrobeCycles; + /** Number of cycles CSn is held active after REn is deasserted */ + int readHoldCycles; +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** Enable or disable page mode reads */ + bool readPageMode; + /** Enables or disable prefetching from sequential addresses */ + bool readPrefetch; + /** Enabled or disables half cycle duration of the REn signal in the last strobe cycle */ + bool readHalfRE; +#endif + /** Number of cycles for address setup before WEn is asserted */ + int writeSetupCycles; + /** Number of cycles WEn is held active */ + int writeStrobeCycles; + /** Number of cycles CSn is held active after WEn is deasserted */ + int writeHoldCycles; +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** Enable or disable the write buffer */ + bool writeBufferDisable; + /** Enables or disables half cycle duration of the WEn signal in the last strobe cycle */ + bool writeHalfWE; + /** Lower address pin limit to enable */ + EBI_ALow_TypeDef aLow; + /** High address pin limit to enable */ + EBI_AHigh_TypeDef aHigh; + /** Pin Location */ + EBI_Location_TypeDef location; +#endif + /** Flag, if EBI should be enabled after configuration */ + bool enable; +} EBI_Init_TypeDef; + +/** Default config for EBI init structures */ +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +#define EBI_INIT_DEFAULT \ +{ \ + ebiModeD8A8, /* 8 bit address, 8 bit data */ \ + ebiActiveLow, /* ARDY polarity */ \ + ebiActiveLow, /* ALE polarity */ \ + ebiActiveLow, /* WE polarity */ \ + ebiActiveLow, /* RE polarity */ \ + ebiActiveLow, /* CS polarity */ \ + ebiActiveLow, /* BL polarity */ \ + false, /* enable BL */ \ + false, /* enable NOIDLE */ \ + false, /* enable ARDY */ \ + false, /* don't disable ARDY timeout */ \ + EBI_BANK0, /* enable bank 0 */ \ + EBI_CS0, /* enable chip select 0 */ \ + 0, /* addr setup cycles */ \ + 1, /* addr hold cycles */ \ + false, /* do not enable half cycle ALE strobe */ \ + 0, /* read setup cycles */ \ + 0, /* read strobe cycles */ \ + 0, /* read hold cycles */ \ + false, /* disable page mode */ \ + false, /* disable prefetch */ \ + false, /* do not enable half cycle REn strobe */ \ + 0, /* write setup cycles */ \ + 0, /* write strobe cycles */ \ + 1, /* write hold cycles */ \ + false, /* do not disable the write buffer */ \ + false, /* do not enable halc cycle WEn strobe */ \ + ebiALowA0, /* ALB - Low bound, address lines */ \ + ebiAHighA0, /* APEN - High bound, address lines */ \ + ebiLocation0, /* Use Location 0 */ \ + true, /* enable EBI */ \ +} +#else +#define EBI_INIT_DEFAULT \ +{ \ + ebiModeD8A8, /* 8 bit address, 8 bit data */ \ + ebiActiveLow, /* ARDY polarity */ \ + ebiActiveLow, /* ALE polarity */ \ + ebiActiveLow, /* WE polarity */ \ + ebiActiveLow, /* RE polarity */ \ + ebiActiveLow, /* CS polarity */ \ + false, /* enable ARDY */ \ + false, /* don't disable ARDY timeout */ \ + EBI_BANK0, /* enable bank 0 */ \ + EBI_CS0, /* enable chip select 0 */ \ + 0, /* addr setup cycles */ \ + 1, /* addr hold cycles */ \ + 0, /* read setup cycles */ \ + 0, /* read strobe cycles */ \ + 0, /* read hold cycles */ \ + 0, /* write setup cycles */ \ + 0, /* write strobe cycles */ \ + 1, /* write hold cycles */ \ + true, /* enable EBI */ \ +} +#endif + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + +/** TFT Initialization structure */ +typedef struct +{ + /** External memory bank for driving display */ + EBI_TFTBank_TypeDef bank; + /** Width */ + EBI_TFTWidth_TypeDef width; + /** Color source for masking and alpha blending */ + EBI_TFTColorSrc_TypeDef colSrc; + /** Bus Interleave mode */ + EBI_TFTInterleave_TypeDef interleave; + /** Trigger for updating frame buffer pointer */ + EBI_TFTFrameBufTrigger_TypeDef fbTrigger; + /** Drive DCLK from negative clock edge of internal clock */ + bool shiftDClk; + /** Masking and alpha blending mode */ + EBI_TFTMaskBlend_TypeDef maskBlend; + /** TFT Direct Drive mode */ + EBI_TFTDDMode_TypeDef driveMode; + /** TFT Polarity for Chip Select (CS) Line */ + EBI_Polarity_TypeDef csPolarity; + /** TFT Polarity for Data Clock (DCLK) Line */ + EBI_Polarity_TypeDef dclkPolarity; + /** TFT Polarity for Data Enable (DATAEN) Line */ + EBI_Polarity_TypeDef dataenPolarity; + /** TFT Polarity for Horizontal Sync (HSYNC) Line */ + EBI_Polarity_TypeDef hsyncPolarity; + /** TFT Polarity for Vertical Sync (VSYNC) Line */ + EBI_Polarity_TypeDef vsyncPolarity; + /** Horizontal size in pixels */ + int hsize; + /** Horizontal Front Porch Size */ + int hPorchFront; + /** Horizontal Back Porch Size */ + int hPorchBack; + /** Horizontal Synchronization Pulse Width */ + int hPulseWidth; + /** Vertical size in pixels */ + int vsize; + /** Vertical Front Porch Size */ + int vPorchFront; + /** Vertical Back Porch Size */ + int vPorchBack; + /** Vertical Synchronization Pulse Width */ + int vPulseWidth; + /** TFT Frame Buffer address, offset to EBI bank base address */ + uint32_t addressOffset; + /** TFT DCLK period in internal cycles */ + int dclkPeriod; + /** Starting position of External Direct Drive relative to DCLK inactive edge */ + int startPosition; + /** Number of cycles RGB data is driven before active edge of DCLK */ + int setupCycles; + /** Number of cycles RGB data is held after active edge of DCLK */ + int holdCycles; +} EBI_TFTInit_TypeDef; + +/** Default configuration for EBI TFT init structure */ +#define EBI_TFTINIT_DEFAULT \ +{ \ + ebiTFTBank0, /* Select EBI Bank 0 */ \ + ebiTFTWidthHalfWord, /* Select 2-byte increments */ \ + ebiTFTColorSrcMem, /* Use memory as source for mask/blending */ \ + ebiTFTInterleaveUnlimited, /* Unlimited interleaved accesses */ \ + ebiTFTFrameBufTriggerVSync, /* VSYNC as frame buffer update trigger */ \ + false, /* Drive DCLK from negative edge of internal clock */ \ + ebiTFTMBDisabled, /* No masking and alpha blending enabled */ \ + ebiTFTDDModeExternal, /* Drive from external memory */ \ + ebiActiveLow, /* CS Active Low polarity */ \ + ebiActiveLow, /* DCLK Active Low polarity */ \ + ebiActiveLow, /* DATAEN Active Low polarity */ \ + ebiActiveLow, /* HSYNC Active Low polarity */ \ + ebiActiveLow, /* VSYNC Active Low polarity */ \ + 320, /* Horizontal size in pixels */ \ + 1, /* Horizontal Front Porch */ \ + 29, /* Horizontal Back Porch */ \ + 2, /* Horizontal Synchronization Pulse Width */ \ + 240, /* Vertical size in pixels */ \ + 1, /* Vertical Front Porch */ \ + 4, /* Vertical Back Porch */ \ + 2, /* Vertical Synchronization Pulse Width */ \ + 0x0000, /* Address offset to EBI memory base */ \ + 5, /* DCLK Period */ \ + 2, /* DCLK Start */ \ + 1, /* DCLK Setup cycles */ \ + 1, /* DCLK Hold cycles */ \ +} + +#endif +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void EBI_Init(const EBI_Init_TypeDef *ebiInit); +void EBI_Disable(void); +uint32_t EBI_BankAddress(uint32_t bank); +void EBI_BankEnable(uint32_t banks, bool enable); + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +void EBI_TFTInit(const EBI_TFTInit_TypeDef *ebiTFTInit); +void EBI_TFTSizeSet(uint32_t horizontal, uint32_t vertical); +void EBI_TFTHPorchSet(int front, int back, int pulseWidth); +void EBI_TFTVPorchSet(int front, int back, int pulseWidth); +void EBI_TFTTimingSet(int dclkPeriod, int start, int setup, int hold); +#endif + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +/* This functionality is only available on devices with independent timing support */ +void EBI_BankReadTimingSet(uint32_t bank, int setupCycles, int strobeCycles, int holdCycles); +void EBI_BankReadTimingConfig(uint32_t bank, bool pageMode, bool prefetch, bool halfRE); + +void EBI_BankWriteTimingSet(uint32_t bank, int setupCycles, int strobeCycles, int holdCycles); +void EBI_BankWriteTimingConfig(uint32_t bank, bool writeBufDisable, bool halfWE); + +void EBI_BankAddressTimingSet(uint32_t bank, int setupCycles, int holdCycles); +void EBI_BankAddressTimingConfig(uint32_t bank, bool halfALE); + +void EBI_BankPolaritySet(uint32_t bank, EBI_Line_TypeDef line, EBI_Polarity_TypeDef polarity); +void EBI_BankByteLaneEnable(uint32_t bank, bool enable); +void EBI_AltMapEnable(bool enable); + +/***************************************************************************//** + * @brief + * Enable or disable TFT Direct Drive + * + * @param[in] mode + * Drive from Internal or External memory, or Disable Direct Drive + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTEnable(EBI_TFTDDMode_TypeDef mode) +{ + EBI->TFTCTRL = (EBI->TFTCTRL & ~(_EBI_TFTCTRL_DD_MASK)) | (uint32_t) mode; +} + + +/***************************************************************************//** + * @brief + * Configure frame buffer pointer + * + * @param[in] address + * Frame pointer address, as offset by EBI base address + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTFrameBaseSet(uint32_t address) +{ + EBI->TFTFRAMEBASE = (uint32_t) address; +} + + +/***************************************************************************//** + * @brief Set TFT Pixel Color 0 or 1 + * + * @param[in] pixel + * Which pixel instance to set + * @param[in] color + * Color of pixel, 16-bit value + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTPixelSet(int pixel, uint32_t color) +{ + EFM_ASSERT(pixel == 0 || pixel == 1); + + if (pixel == 0) + { + EBI->TFTPIXEL0 = color; + } + if (pixel == 1) + { + EBI->TFTPIXEL1 = color; + } +} + + +/***************************************************************************//** + * @brief Masking and Blending Mode Set + * + * @param[in] maskBlend + * Masking and alpha blending mode + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTMaskBlendMode(EBI_TFTMaskBlend_TypeDef maskBlend) +{ + EBI->TFTCTRL = (EBI->TFTCTRL & (~_EBI_TFTCTRL_MASKBLEND_MASK))|maskBlend; +} + + +/***************************************************************************//** + * @brief Set TFT Alpha Blending Factor + * + * @param[in] alpha + * 8-bit value indicating blending factor + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTAlphaBlendSet(uint8_t alpha) +{ + EBI->TFTALPHA = alpha; +} + + +/***************************************************************************//** + * @brief Set TFT mask value + * Data accesses that matches this value are suppressed + * @param[in] mask + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTMaskSet(uint32_t mask) +{ + EBI->TFTMASK = mask; +} + + +/***************************************************************************//** + * @brief Get current vertical position counter + * @return + * Returns the current line position for the visible part of a frame + ******************************************************************************/ +__STATIC_INLINE uint32_t EBI_TFTVCount(void) +{ + return((EBI->TFTSTATUS & _EBI_TFTSTATUS_VCNT_MASK) >> _EBI_TFTSTATUS_VCNT_SHIFT); +} + + +/***************************************************************************//** + * @brief Get current horizontal position counter + * @return + * Returns the current horizontal pixel position within a visible line + ******************************************************************************/ +__STATIC_INLINE uint32_t EBI_TFTHCount(void) +{ + return((EBI->TFTSTATUS & _EBI_TFTSTATUS_HCNT_MASK) >> _EBI_TFTSTATUS_HCNT_SHIFT); +} + + +/***************************************************************************//** + * @brief Set Frame Buffer Trigger + * + * @details + * Frame buffer pointer will be updated either on each horizontal line (hsync) + * or vertical update (vsync). + * + * @param[in] sync + * Trigger update of frame buffer pointer on vertical or horisontal sync. + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTFBTriggerSet(EBI_TFTFrameBufTrigger_TypeDef sync) +{ + EBI->TFTCTRL = ((EBI->TFTCTRL & ~_EBI_TFTCTRL_FBCTRIG_MASK)|sync); +} + + +/***************************************************************************//** + * @brief Set horizontal TFT stride value in number of bytes + * + * @param[in] nbytes + * Number of bytes to add to frame buffer pointer after each horizontal line + * update + ******************************************************************************/ +__STATIC_INLINE void EBI_TFTHStrideSet(uint32_t nbytes) +{ + EFM_ASSERT(nbytes < 0x1000); + + EBI->TFTSTRIDE = (EBI->TFTSTRIDE & ~(_EBI_TFTSTRIDE_HSTRIDE_MASK))| + (nbytes<<_EBI_TFTSTRIDE_HSTRIDE_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Clear one or more pending EBI interrupts. + * @param[in] flags + * Pending EBI interrupt source to clear. Use a logical OR combination + * of valid interrupt flags for the EBI module (EBI_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void EBI_IntClear(uint32_t flags) +{ + EBI->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending EBI interrupts. + * + * @param[in] flags + * EBI interrupt sources to set to pending. Use a logical OR combination of + * valid interrupt flags for the EBI module (EBI_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void EBI_IntSet(uint32_t flags) +{ + EBI->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more EBI interrupts. + * + * @param[in] flags + * EBI interrupt sources to disable. Use logical OR combination of valid + * interrupt flags for the EBI module (EBI_IF_nnn) + ******************************************************************************/ +__STATIC_INLINE void EBI_IntDisable(uint32_t flags) +{ + EBI->IEN &= ~(flags); +} + + +/***************************************************************************//** + * @brief + * Enable one or more EBI interrupts. + * + * @param[in] flags + * EBI interrupt sources to enable. Use logical OR combination of valid + * interrupt flags for the EBI module (EBI_IF_nnn) + ******************************************************************************/ +__STATIC_INLINE void EBI_IntEnable(uint32_t flags) +{ + EBI->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending EBI interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function + * + * @return + * EBI interrupt sources pending, a logical combination of valid EBI + * interrupt flags, EBI_IF_nnn + ******************************************************************************/ +__STATIC_INLINE uint32_t EBI_IntGet(void) +{ + return EBI->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending EBI interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled EBI interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in EBI_IEN and + * - the pending interrupt flags EBI_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t EBI_IntGetEnabled(void) +{ + uint32_t ien; + + ien = EBI->IEN; + return EBI->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Start ECC generator on NAND flash transfers. + ******************************************************************************/ +__STATIC_INLINE void EBI_StartNandEccGen(void) +{ + EBI->CMD = EBI_CMD_ECCSTART | EBI_CMD_ECCCLEAR; +} + + +/***************************************************************************//** + * @brief + * Stop NAND flash ECC generator and return generated ECC. + * + * @return + * The generated ECC. + ******************************************************************************/ +__STATIC_INLINE uint32_t EBI_StopNandEccGen( void ) +{ + EBI->CMD = EBI_CMD_ECCSTOP; + return EBI->ECCPARITY; +} +#endif + +void EBI_ChipSelectEnable(uint32_t banks, bool enable); +void EBI_ReadTimingSet(int setupCycles, int strobeCycles, int holdCycles); +void EBI_WriteTimingSet(int setupCycles, int strobeCycles, int holdCycles); +void EBI_AddressTimingSet(int setupCycles, int holdCycles); +void EBI_PolaritySet(EBI_Line_TypeDef line, EBI_Polarity_TypeDef polarity); + +/** @} (end addtogroup EBI) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(EBI_COUNT) && (EBI_COUNT > 0) */ + +#endif /* __SILICON_LABS_EM_EBI_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_emu.h b/cpu/efm32_common/emlib/inc/em_emu.h new file mode 100644 index 0000000000000..7b8e7cc53e764 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_emu.h @@ -0,0 +1,731 @@ +/***************************************************************************//** + * @file em_emu.h + * @brief Energy management unit (EMU) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_EMU_H__ +#define __SILICON_LABS_EM_EMU_H__ + +#include "em_device.h" +#if defined( EMU_PRESENT ) + +#include +#include "em_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup EMU + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +#if defined( _EMU_EM4CONF_OSC_MASK ) +/** EM4 duty oscillator */ +typedef enum +{ + /** Select ULFRCO as duty oscillator in EM4 */ + emuEM4Osc_ULFRCO = EMU_EM4CONF_OSC_ULFRCO, + /** Select LFXO as duty oscillator in EM4 */ + emuEM4Osc_LFXO = EMU_EM4CONF_OSC_LFXO, + /** Select LFRCO as duty oscillator in EM4 */ + emuEM4Osc_LFRCO = EMU_EM4CONF_OSC_LFRCO +} EMU_EM4Osc_TypeDef; +#endif + +#if defined( _EMU_BUCTRL_PROBE_MASK ) +/** Backup Power Voltage Probe types */ +typedef enum +{ + /** Disable voltage probe */ + emuProbe_Disable = EMU_BUCTRL_PROBE_DISABLE, + /** Connect probe to VDD_DREG */ + emuProbe_VDDDReg = EMU_BUCTRL_PROBE_VDDDREG, + /** Connect probe to BU_IN */ + emuProbe_BUIN = EMU_BUCTRL_PROBE_BUIN, + /** Connect probe to BU_OUT */ + emuProbe_BUOUT = EMU_BUCTRL_PROBE_BUOUT +} EMU_Probe_TypeDef; +#endif + +#if defined( _EMU_PWRCONF_PWRRES_MASK ) +/** Backup Power Domain resistor selection */ +typedef enum +{ + /** Main power and backup power connected with RES0 series resistance */ + emuRes_Res0 = EMU_PWRCONF_PWRRES_RES0, + /** Main power and backup power connected with RES1 series resistance */ + emuRes_Res1 = EMU_PWRCONF_PWRRES_RES1, + /** Main power and backup power connected with RES2 series resistance */ + emuRes_Res2 = EMU_PWRCONF_PWRRES_RES2, + /** Main power and backup power connected with RES3 series resistance */ + emuRes_Res3 = EMU_PWRCONF_PWRRES_RES3, +} EMU_Resistor_TypeDef; +#endif + +#if defined( BU_PRESENT ) +/** Backup Power Domain power connection */ +typedef enum +{ + /** No connection between main and backup power */ + emuPower_None = EMU_BUINACT_PWRCON_NONE, + /** Main power and backup power connected through diode, + allowing current from backup to main only */ + emuPower_BUMain = EMU_BUINACT_PWRCON_BUMAIN, + /** Main power and backup power connected through diode, + allowing current from main to backup only */ + emuPower_MainBU = EMU_BUINACT_PWRCON_MAINBU, + /** Main power and backup power connected without diode */ + emuPower_NoDiode = EMU_BUINACT_PWRCON_NODIODE, +} EMU_Power_TypeDef; +#endif + +/** BOD threshold setting selector, active or inactive mode */ +typedef enum +{ + /** Configure BOD threshold for active mode */ + emuBODMode_Active, + /** Configure BOD threshold for inactive mode */ + emuBODMode_Inactive, +} EMU_BODMode_TypeDef; + +#if defined( _EMU_EM4CTRL_EM4STATE_MASK ) +/** EM4 modes */ +typedef enum +{ + /** EM4 Hibernate */ + emuEM4Hibernate = EMU_EM4CTRL_EM4STATE_EM4H, + /** EM4 Shutoff */ + emuEM4Shutoff = EMU_EM4CTRL_EM4STATE_EM4S, +} EMU_EM4State_TypeDef; +#endif + + +#if defined( _EMU_EM4CTRL_EM4IORETMODE_MASK ) +typedef enum +{ + /** No Retention: Pads enter reset state when entering EM4 */ + emuPinRetentionDisable = EMU_EM4CTRL_EM4IORETMODE_DISABLE, + /** Retention through EM4: Pads enter reset state when exiting EM4 */ + emuPinRetentionEm4Exit = EMU_EM4CTRL_EM4IORETMODE_EM4EXIT, + /** Retention through EM4 and wakeup: call EMU_UnlatchPinRetention() to + release pins from retention after EM4 wakeup */ + emuPinRetentionLatch = EMU_EM4CTRL_EM4IORETMODE_SWUNLATCH, +} EMU_EM4PinRetention_TypeDef; +#endif + + +#if defined( _EMU_PWRCFG_MASK ) +/** Power configurations */ +typedef enum +{ + /** DCDC is connected to DVDD */ + emuPowerConfig_DcdcToDvdd = EMU_PWRCFG_PWRCFG_DCDCTODVDD, +} EMU_PowerConfig_TypeDef; +#endif + +#if defined( _EMU_DCDCCTRL_MASK ) +/** DCDC operating modes */ +typedef enum +{ + /** DCDC regulator bypass */ + emuDcdcMode_Bypass = EMU_DCDCCTRL_DCDCMODE_BYPASS, + /** DCDC low-noise mode */ + emuDcdcMode_LowNoise = EMU_DCDCCTRL_DCDCMODE_LOWNOISE, +} EMU_DcdcMode_TypeDef; +#endif + +#if defined( _EMU_PWRCTRL_MASK ) +/** DCDC to DVDD mode analog peripheral power supply select */ +typedef enum +{ + /** Select AVDD as analog power supply. Typically lower noise, but less energy efficient. */ + emuDcdcAnaPeripheralPower_AVDD = EMU_PWRCTRL_ANASW_AVDD, + /** Select DCDC (DVDD) as analog power supply. Typically more energy efficient, but more noise. */ + emuDcdcAnaPeripheralPower_DCDC = EMU_PWRCTRL_ANASW_DVDD +} EMU_DcdcAnaPeripheralPower_TypeDef; +#endif + +#if defined( _EMU_DCDCMISCCTRL_MASK ) +/** DCDC Low-noise efficiency mode */ +typedef enum +{ +#if defined( _EFM_DEVICE ) + /** High efficiency mode */ + emuDcdcLnHighEfficiency = 0, +#endif + /** Fast transient response mode */ + emuDcdcLnFastTransient = EMU_DCDCMISCCTRL_LNFORCECCM, +} EMU_DcdcLnTransientMode_TypeDef; +#endif + +#if defined( _EMU_DCDCCTRL_MASK ) +/** DCDC Low-noise RCO band select */ +typedef enum +{ + /** Set RCO to 3MHz */ + EMU_DcdcLnRcoBand_3MHz = 0, + /** Set RCO to 4MHz */ + EMU_DcdcLnRcoBand_4MHz = 1, + /** Set RCO to 5MHz */ + EMU_DcdcLnRcoBand_5MHz = 2, + /** Set RCO to 6MHz */ + EMU_DcdcLnRcoBand_6MHz = 3, + /** Set RCO to 7MHz */ + EMU_DcdcLnRcoBand_7MHz = 4, + /** Set RCO to 8MHz */ + EMU_DcdcLnRcoBand_8MHz = 5, + /** Set RCO to 9MHz */ + EMU_DcdcLnRcoBand_9MHz = 6, + /** Set RCO to 10MHz */ + EMU_DcdcLnRcoBand_10MHz = 7, +} EMU_DcdcLnRcoBand_TypeDef; + +#endif + +#if defined( EMU_STATUS_VMONRDY ) +/** VMON channels */ +typedef enum +{ + emuVmonChannel_AVDD, + emuVmonChannel_ALTAVDD, + emuVmonChannel_DVDD, + emuVmonChannel_IOVDD0 +} EMU_VmonChannel_TypeDef; +#endif /* EMU_STATUS_VMONRDY */ + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Energy Mode 2 and 3 initialization structure */ +typedef struct +{ + bool em23VregFullEn; /**< Enable full VREG drive strength in EM2/3 */ +} EMU_EM23Init_TypeDef; + +/** Default initialization of EM2 and 3 configuration */ +#define EMU_EM23INIT_DEFAULT \ +{ false } /* Reduced voltage regulator drive strength in EM2 and EM3 */ + + +#if defined( _EMU_EM4CONF_MASK ) || defined( _EMU_EM4CTRL_MASK ) +/** Energy Mode 4 initialization structure */ +typedef struct +{ +#if defined( _EMU_EM4CONF_MASK ) + /* Init parameters for platforms with EMU->EM4CONF register */ + bool lockConfig; /**< Lock configuration of regulator, BOD and oscillator */ + bool buBodRstDis; /**< When set, no reset will be asserted due to Brownout when in EM4 */ + EMU_EM4Osc_TypeDef osc; /**< EM4 duty oscillator */ + bool buRtcWakeup; /**< Wake up on EM4 BURTC interrupt */ + bool vreg; /**< Enable EM4 voltage regulator */ + +#elif defined( _EMU_EM4CTRL_MASK ) + /* Init parameters for platforms with EMU->EM4CTRL register */ + bool retainLfxo; /**< Disable the LFXO upon EM4 entry */ + bool retainLfrco; /**< Disable the LFRCO upon EM4 entry */ + bool retainUlfrco; /**< Disable the ULFRCO upon EM4 entry */ + EMU_EM4State_TypeDef em4State; /**< Hibernate or shutoff EM4 state */ + EMU_EM4PinRetention_TypeDef pinRetentionMode; /**< EM4 pin retention mode */ +#endif +} EMU_EM4Init_TypeDef; +#endif + +/** Default initialization of EM4 configuration */ +#if defined( _EMU_EM4CONF_MASK ) +#define EMU_EM4INIT_DEFAULT \ +{ \ + false, /* Dont't lock configuration after it's been set */ \ + false, /* No reset will be asserted due to Brownout when in EM4 */ \ + emuEM4Osc_ULFRCO, /* Use default ULFRCO oscillator */ \ + true, /* Wake up on EM4 BURTC interrupt */ \ + true, /* Enable VREG */ \ +} +#endif +#if defined( _EMU_EM4CTRL_MASK ) +#define EMU_EM4INIT_DEFAULT \ +{ \ + false, /* Retain LFXO configuration upon EM4 entry */ \ + false, /* Retain LFRCO configuration upon EM4 entry */ \ + false, /* Retain ULFRCO configuration upon EM4 entry */ \ + emuEM4Shutoff, /* Use EM4 shutoff state */ \ + emuPinRetentionDisable, /* Do not retain pins in EM4 */ \ +} +#endif + +#if defined( BU_PRESENT ) +/** Backup Power Domain Initialization structure */ +typedef struct +{ + /* Backup Power Domain power configuration */ + + /** Voltage probe select, selects ADC voltage */ + EMU_Probe_TypeDef probe; + /** Enable BOD calibration mode */ + bool bodCal; + /** Enable BU_STAT status pin for active BU mode */ + bool statusPinEnable; + + /* Backup Power Domain connection configuration */ + /** Power domain resistor */ + EMU_Resistor_TypeDef resistor; + /** BU_VOUT strong enable */ + bool voutStrong; + /** BU_VOUT medium enable */ + bool voutMed; + /** BU_VOUT weak enable */ + bool voutWeak; + /** Power connection, when not in Backup Mode */ + EMU_Power_TypeDef inactivePower; + /** Power connection, when in Backup Mode */ + EMU_Power_TypeDef activePower; + /** Enable backup power domain, and release reset, enable BU_VIN pin */ + bool enable; +} EMU_BUPDInit_TypeDef; + +/** Default Backup Power Domain configuration */ +#define EMU_BUPDINIT_DEFAULT \ +{ \ + emuProbe_Disable, /* Do not enable voltage probe */ \ + false, /* Disable BOD calibration mode */ \ + false, /* Disable BU_STAT pin for backup mode indication */ \ + \ + emuRes_Res0, /* RES0 series resistance between main and backup power */ \ + false, /* Don't enable strong switch */ \ + false, /* Don't enable medium switch */ \ + false, /* Don't enable weak switch */ \ + \ + emuPower_None, /* No connection between main and backup power (inactive mode) */ \ + emuPower_None, /* No connection between main and backup power (active mode) */ \ + true /* Enable BUPD enter on BOD, enable BU_VIN pin, release BU reset */ \ +} +#endif + +#if defined( _EMU_DCDCCTRL_MASK ) +/** DCDC initialization structure */ +typedef struct +{ + EMU_PowerConfig_TypeDef powerConfig; /**< Device external power configuration */ + EMU_DcdcMode_TypeDef dcdcMode; /**< DCDC regulator operating mode in EM0 */ + uint16_t mVout; /**< Target output voltage (mV) */ + uint16_t em01LoadCurrent_mA; /**< Estimated average load current in EM0 (mA). + This estimate is also used for EM1 optimization, + so if EM1 current is expected to be higher than EM0, + then this parameter should hold the higher EM1 current. */ + uint16_t em234LoadCurrent_uA; /**< Estimated average load current in EM2 (uA). + This estimate is also used for EM3 and 4 optimization, + so if EM3 or 4 current is expected to be higher than EM2, + then this parameter should hold the higher EM3 or 4 current. */ + uint16_t maxCurrent_mA; /**< Maximum peak DCDC output current (mA). + This can be set to the maximum for the power source, + for example the maximum for a battery. */ + EMU_DcdcAnaPeripheralPower_TypeDef anaPeripheralPower;/**< Select analog peripheral power in DCDC-to-DVDD mode */ + EMU_DcdcLnTransientMode_TypeDef lnTransientMode; /**< Low-noise transient mode */ + +} EMU_DCDCInit_TypeDef; + +/** Default DCDC initialization */ +#if defined( _EFM_DEVICE ) +#define EMU_DCDCINIT_DEFAULT \ +{ \ + emuPowerConfig_DcdcToDvdd, /* DCDC to DVDD */ \ + emuDcdcMode_LowNoise, /* Low-niose mode in EM0 (can be set to LowPower on EFM32PG revB0) */ \ + 1800, /* Nominal output voltage for DVDD mode, 1.8V */ \ + 5, /* Nominal EM0 load current of less than 5mA */ \ + 10, /* Nominal EM2/3 load current less than 10uA */ \ + 160, /* Maximum peak current of 160mA */ \ + emuDcdcAnaPeripheralPower_DCDC,/* Select DCDC as analog power supply (lower power) */ \ + emuDcdcLnHighEfficiency, /* Use low-noise high-efficiency mode (ignored if emuDcdcMode_LowPower) */ \ +} +#else /* EFR32 device */ +#define EMU_DCDCINIT_DEFAULT \ +{ \ + emuPowerConfig_DcdcToDvdd, /* DCDC to DVDD */ \ + emuDcdcMode_LowNoise, /* Low-niose mode in EM0 */ \ + 1800, /* Nominal output voltage for DVDD mode, 1.8V */ \ + 15, /* Nominal EM0 load current of less than 5mA */ \ + 10, /* Nominal EM2/3 load current less than 10uA */ \ + 160, /* Maximum peak current of 160mA */ \ + emuDcdcAnaPeripheralPower_AVDD,/* Select AVDD as analog power supply (less noise) */ \ + emuDcdcLnFastTransient, /* Use low-noise fast-transient mode */ \ +} +#endif + +#endif + +#if defined( EMU_STATUS_VMONRDY ) +/** VMON initialization structure */ +typedef struct +{ + EMU_VmonChannel_TypeDef channel; /**< VMON channel to configure */ + int threshold; /**< Trigger threshold (mV) */ + bool riseWakeup; /**< Wake up from EM4H on rising edge */ + bool fallWakeup; /**< Wake up from EM4H on falling edge */ + bool enable; /**< Enable VMON channel */ + bool retDisable; /**< Disable IO0 retention when voltage drops below threshold (IOVDD only) */ +} EMU_VmonInit_TypeDef; + +/** Default VMON initialization structure */ +#define EMU_VMONINIT_DEFAULT \ +{ \ + emuVmonChannel_AVDD, /* AVDD VMON channel */ \ + 3200, /* 3.2 V threshold */ \ + false, /* Don't wake from EM4H on rising edge */ \ + false, /* Don't wake from EM4H on falling edge */ \ + true, /* Enable VMON channel */ \ + false /* Don't disable IO0 retention */ \ +} + +/** VMON Hysteresis initialization structure */ +typedef struct +{ + EMU_VmonChannel_TypeDef channel; /**< VMON channel to configure */ + int riseThreshold; /**< Rising threshold (mV) */ + int fallThreshold; /**< Falling threshold (mV) */ + bool riseWakeup; /**< Wake up from EM4H on rising edge */ + bool fallWakeup; /**< Wake up from EM4H on falling edge */ + bool enable; /**< Enable VMON channel */ +} EMU_VmonHystInit_TypeDef; + +/** Default VMON Hysteresis initialization structure */ +#define EMU_VMONHYSTINIT_DEFAULT \ +{ \ + emuVmonChannel_AVDD, /* AVDD VMON channel */ \ + 3200, /* 3.2 V rise threshold */ \ + 3200, /* 3.2 V fall threshold */ \ + false, /* Don't wake from EM4H on rising edge */ \ + false, /* Don't wake from EM4H on falling edge */ \ + true /* Enable VMON channel */ \ +} +#endif /* EMU_STATUS_VMONRDY */ + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Enter energy mode 1 (EM1). + ******************************************************************************/ +__STATIC_INLINE void EMU_EnterEM1(void) +{ + /* Enter sleep mode */ + SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; + __WFI(); +} + +void EMU_EM23Init(EMU_EM23Init_TypeDef *em23Init); +#if defined( _EMU_EM4CONF_MASK ) || defined( _EMU_EM4CTRL_MASK ) +void EMU_EM4Init(EMU_EM4Init_TypeDef *em4Init); +#endif +void EMU_EnterEM2(bool restore); +void EMU_EnterEM3(bool restore); +void EMU_EnterEM4(void); +void EMU_MemPwrDown(uint32_t blocks); +void EMU_UpdateOscConfig(void); +#if defined( BU_PRESENT ) +void EMU_BUPDInit(EMU_BUPDInit_TypeDef *bupdInit); +void EMU_BUThresholdSet(EMU_BODMode_TypeDef mode, uint32_t value); +void EMU_BUThresRangeSet(EMU_BODMode_TypeDef mode, uint32_t value); +#endif +#if defined( _EMU_DCDCCTRL_MASK ) +bool EMU_DCDCInit(EMU_DCDCInit_TypeDef *dcdcInit); +void EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode); +bool EMU_DCDCOutputVoltageSet(uint32_t mV, bool setLpVoltage, bool setLnVoltage); +void EMU_DCDCOptimizeSlice(uint32_t mALoadCurrent); +void EMU_DCDCLnRcoBandSet(EMU_DcdcLnRcoBand_TypeDef band); +bool EMU_DCDCPowerOff(void); +#endif +#if defined( EMU_STATUS_VMONRDY ) +void EMU_VmonInit(EMU_VmonInit_TypeDef *vmonInit); +void EMU_VmonHystInit(EMU_VmonHystInit_TypeDef *vmonInit); +void EMU_VmonEnable(EMU_VmonChannel_TypeDef channel, bool enable); +bool EMU_VmonChannelStatusGet(EMU_VmonChannel_TypeDef channel); + +/***************************************************************************//** + * @brief + * Get the status of the voltage monitor (VMON). + * + * @return + * Status of the VMON. True if all the enabled channels are ready, false if + * one or more of the enabled channels are not ready. + ******************************************************************************/ +__STATIC_INLINE bool EMU_VmonStatusGet(void) +{ + return BUS_RegBitRead(&EMU->STATUS, _EMU_STATUS_VMONRDY_SHIFT); +} +#endif /* EMU_STATUS_VMONRDY */ + +#if defined( _EMU_IF_MASK ) +/***************************************************************************//** + * @brief + * Clear one or more pending EMU interrupts. + * + * @param[in] flags + * Pending EMU interrupt sources to clear. Use one or more valid + * interrupt flags for the EMU module (EMU_IFC_nnn). + ******************************************************************************/ +__STATIC_INLINE void EMU_IntClear(uint32_t flags) +{ + EMU->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more EMU interrupts. + * + * @param[in] flags + * EMU interrupt sources to disable. Use one or more valid + * interrupt flags for the EMU module (EMU_IEN_nnn). + ******************************************************************************/ +__STATIC_INLINE void EMU_IntDisable(uint32_t flags) +{ + EMU->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more EMU interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using EMU_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * EMU interrupt sources to enable. Use one or more valid + * interrupt flags for the EMU module (EMU_IEN_nnn). + ******************************************************************************/ +__STATIC_INLINE void EMU_IntEnable(uint32_t flags) +{ + EMU->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending EMU interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * EMU interrupt sources pending. Returns one or more valid + * interrupt flags for the EMU module (EMU_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t EMU_IntGet(void) +{ + return EMU->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending EMU interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled EMU interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in EMU_IEN and + * - the pending interrupt flags EMU_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t EMU_IntGetEnabled(void) +{ + uint32_t ien; + + ien = EMU->IEN; + return EMU->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending EMU interrupts + * + * @param[in] flags + * EMU interrupt sources to set to pending. Use one or more valid + * interrupt flags for the EMU module (EMU_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void EMU_IntSet(uint32_t flags) +{ + EMU->IFS = flags; +} +#endif /* _EMU_IF_MASK */ + + +#if defined( _EMU_EM4CONF_LOCKCONF_MASK ) +/***************************************************************************//** + * @brief + * Enable or disable EM4 lock configuration + * @param[in] enable + * If true, locks down EM4 configuration + ******************************************************************************/ +__STATIC_INLINE void EMU_EM4Lock(bool enable) +{ + BUS_RegBitWrite(&(EMU->EM4CONF), _EMU_EM4CONF_LOCKCONF_SHIFT, enable); +} +#endif + +#if defined( _EMU_STATUS_BURDY_MASK ) +/***************************************************************************//** + * @brief + * Halts until backup power functionality is ready + ******************************************************************************/ +__STATIC_INLINE void EMU_BUReady(void) +{ + while(!(EMU->STATUS & EMU_STATUS_BURDY)) + ; +} +#endif + +#if defined( _EMU_ROUTE_BUVINPEN_MASK ) +/***************************************************************************//** + * @brief + * Disable BU_VIN support + * @param[in] enable + * If true, enables BU_VIN input pin support, if false disables it + ******************************************************************************/ +__STATIC_INLINE void EMU_BUPinEnable(bool enable) +{ + BUS_RegBitWrite(&(EMU->ROUTE), _EMU_ROUTE_BUVINPEN_SHIFT, enable); +} +#endif + +/***************************************************************************//** + * @brief + * Lock the EMU in order to protect its registers against unintended + * modification. + * + * @note + * If locking the EMU registers, they must be unlocked prior to using any + * EMU API functions modifying EMU registers, excluding interrupt control + * and regulator control if the architecture has a EMU_PWRCTRL register. + * An exception to this is the energy mode entering API (EMU_EnterEMn()), + * which can be used when the EMU registers are locked. + ******************************************************************************/ +__STATIC_INLINE void EMU_Lock(void) +{ + EMU->LOCK = EMU_LOCK_LOCKKEY_LOCK; +} + + +/***************************************************************************//** + * @brief + * Unlock the EMU so that writing to locked registers again is possible. + ******************************************************************************/ +__STATIC_INLINE void EMU_Unlock(void) +{ + EMU->LOCK = EMU_LOCK_LOCKKEY_UNLOCK; +} + + +#if defined( _EMU_PWRLOCK_MASK ) +/***************************************************************************//** + * @brief + * Lock the EMU regulator control registers in order to protect against + * unintended modification. + ******************************************************************************/ +__STATIC_INLINE void EMU_PowerLock(void) +{ + EMU->PWRLOCK = EMU_PWRLOCK_LOCKKEY_LOCK; +} + + +/***************************************************************************//** + * @brief + * Unlock the EMU power control registers so that writing to + * locked registers again is possible. + ******************************************************************************/ +__STATIC_INLINE void EMU_PowerUnlock(void) +{ + EMU->PWRLOCK = EMU_PWRLOCK_LOCKKEY_UNLOCK; +} +#endif + + +/***************************************************************************//** + * @brief + * Block entering EM2 or higher number energy modes. + ******************************************************************************/ +__STATIC_INLINE void EMU_EM2Block(void) +{ + BUS_RegBitWrite(&(EMU->CTRL), _EMU_CTRL_EM2BLOCK_SHIFT, 1U); +} + +/***************************************************************************//** + * @brief + * Unblock entering EM2 or higher number energy modes. + ******************************************************************************/ +__STATIC_INLINE void EMU_EM2UnBlock(void) +{ + BUS_RegBitWrite(&(EMU->CTRL), _EMU_CTRL_EM2BLOCK_SHIFT, 0U); +} + +#if defined( _EMU_EM4CTRL_EM4IORETMODE_MASK ) +/***************************************************************************//** + * @brief + * When EM4 pin retention is set to emuPinRetentionLatch, then pins are retained + * through EM4 entry and wakeup. The pin state is released by calling this function. + * The feature allows peripherals or GPIO to be re-initialized after EM4 exit (reset), + * and when the initialization is done, this function can release pins and return control + * to the peripherals or GPIO. + ******************************************************************************/ +__STATIC_INLINE void EMU_UnlatchPinRetention(void) +{ + EMU->CMD = EMU_CMD_EM4UNLATCH; +} +#endif + +/** @} (end addtogroup EMU) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( EMU_PRESENT ) */ +#endif /* __SILICON_LABS_EM_EMU_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_gpio.h b/cpu/efm32_common/emlib/inc/em_gpio.h new file mode 100644 index 0000000000000..a703994b450ff --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_gpio.h @@ -0,0 +1,932 @@ +/***************************************************************************//** + * @file em_gpio.h + * @brief General Purpose IO (GPIO) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#ifndef __SILICON_LABS_EM_GPIO_H__ +#define __SILICON_LABS_EM_GPIO_H__ + +#include "em_device.h" +#if defined(GPIO_COUNT) && (GPIO_COUNT > 0) + +#include +#include "em_bus.h" +#include "em_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup GPIO + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +#if defined( _EFM32_TINY_FAMILY ) || defined( _EFM32_ZERO_FAMILY ) + +#define _GPIO_PORT_A_PIN_COUNT 14 +#define _GPIO_PORT_B_PIN_COUNT 10 +#define _GPIO_PORT_C_PIN_COUNT 16 +#define _GPIO_PORT_D_PIN_COUNT 9 +#define _GPIO_PORT_E_PIN_COUNT 12 +#define _GPIO_PORT_F_PIN_COUNT 6 + +#define _GPIO_PORT_A_PIN_MASK 0xF77F +#define _GPIO_PORT_B_PIN_MASK 0x79F8 +#define _GPIO_PORT_C_PIN_MASK 0xFFFF +#define _GPIO_PORT_D_PIN_MASK 0x01FF +#define _GPIO_PORT_E_PIN_MASK 0xFFF0 +#define _GPIO_PORT_F_PIN_MASK 0x003F + +#elif defined( _EFM32_HAPPY_FAMILY ) + +#define _GPIO_PORT_A_PIN_COUNT 6 +#define _GPIO_PORT_B_PIN_COUNT 5 +#define _GPIO_PORT_C_PIN_COUNT 12 +#define _GPIO_PORT_D_PIN_COUNT 4 +#define _GPIO_PORT_E_PIN_COUNT 4 +#define _GPIO_PORT_F_PIN_COUNT 6 + +#define _GPIO_PORT_A_PIN_MASK 0x0707 +#define _GPIO_PORT_B_PIN_MASK 0x6980 +#define _GPIO_PORT_C_PIN_MASK 0xEF1F +#define _GPIO_PORT_D_PIN_MASK 0x00F0 +#define _GPIO_PORT_E_PIN_MASK 0x3C00 +#define _GPIO_PORT_F_PIN_MASK 0x003F + +#elif defined( _EFM32_GIANT_FAMILY ) \ + || defined( _EFM32_WONDER_FAMILY ) + +#define _GPIO_PORT_A_PIN_COUNT 16 +#define _GPIO_PORT_B_PIN_COUNT 16 +#define _GPIO_PORT_C_PIN_COUNT 16 +#define _GPIO_PORT_D_PIN_COUNT 16 +#define _GPIO_PORT_E_PIN_COUNT 16 +#define _GPIO_PORT_F_PIN_COUNT 13 + +#define _GPIO_PORT_A_PIN_MASK 0xFFFF +#define _GPIO_PORT_B_PIN_MASK 0xFFFF +#define _GPIO_PORT_C_PIN_MASK 0xFFFF +#define _GPIO_PORT_D_PIN_MASK 0xFFFF +#define _GPIO_PORT_E_PIN_MASK 0xFFFF +#define _GPIO_PORT_F_PIN_MASK 0x1FFF + +#elif defined( _EFM32_GECKO_FAMILY ) + +#define _GPIO_PORT_A_PIN_COUNT 16 +#define _GPIO_PORT_B_PIN_COUNT 16 +#define _GPIO_PORT_C_PIN_COUNT 16 +#define _GPIO_PORT_D_PIN_COUNT 16 +#define _GPIO_PORT_E_PIN_COUNT 16 +#define _GPIO_PORT_F_PIN_COUNT 10 + +#define _GPIO_PORT_A_PIN_MASK 0xFFFF +#define _GPIO_PORT_B_PIN_MASK 0xFFFF +#define _GPIO_PORT_C_PIN_MASK 0xFFFF +#define _GPIO_PORT_D_PIN_MASK 0xFFFF +#define _GPIO_PORT_E_PIN_MASK 0xFFFF +#define _GPIO_PORT_F_PIN_MASK 0x03FF + +#elif defined( _EFR32_MIGHTY_FAMILY ) \ + || defined( _EFR32_BLUE_FAMILY ) \ + || defined( _EFR32_FLEX_FAMILY ) \ + || defined( _EFR32_ZAPPY_FAMILY ) + +#define _GPIO_PORT_A_PIN_COUNT 6 +#define _GPIO_PORT_B_PIN_COUNT 5 +#define _GPIO_PORT_C_PIN_COUNT 6 +#define _GPIO_PORT_D_PIN_COUNT 3 +#define _GPIO_PORT_E_PIN_COUNT 0 +#define _GPIO_PORT_F_PIN_COUNT 8 + +#define _GPIO_PORT_A_PIN_MASK 0x003F +#define _GPIO_PORT_B_PIN_MASK 0xF800 +#define _GPIO_PORT_C_PIN_MASK 0x0FC0 +#define _GPIO_PORT_D_PIN_MASK 0xE000 +#define _GPIO_PORT_E_PIN_MASK 0x0000 +#define _GPIO_PORT_F_PIN_MASK 0x00FF + +#elif defined( _EFM32_PEARL_FAMILY ) \ + || defined( _EFM32_JADE_FAMILY ) + +#define _GPIO_PORT_A_PIN_COUNT 6 +#define _GPIO_PORT_B_PIN_COUNT 5 +#define _GPIO_PORT_C_PIN_COUNT 6 +#define _GPIO_PORT_D_PIN_COUNT 7 +#define _GPIO_PORT_E_PIN_COUNT 0 +#define _GPIO_PORT_F_PIN_COUNT 8 + +#define _GPIO_PORT_A_PIN_MASK 0x003F +#define _GPIO_PORT_B_PIN_MASK 0xF800 +#define _GPIO_PORT_C_PIN_MASK 0x0FC0 +#define _GPIO_PORT_D_PIN_MASK 0xFE00 +#define _GPIO_PORT_E_PIN_MASK 0x0000 +#define _GPIO_PORT_F_PIN_MASK 0x00FF + +#else +#warning "Port and pin masks are not defined for this family." +#endif + +#if defined( _GPIO_PORT_G_PIN_COUNT ) && defined( _GPIO_PORT_H_PIN_COUNT ) +#define _GPIO_PORT_SIZE(port) ( \ + (port) == 0 ? _GPIO_PORT_A_PIN_COUNT : \ + (port) == 1 ? _GPIO_PORT_B_PIN_COUNT : \ + (port) == 2 ? _GPIO_PORT_C_PIN_COUNT : \ + (port) == 3 ? _GPIO_PORT_D_PIN_COUNT : \ + (port) == 4 ? _GPIO_PORT_E_PIN_COUNT : \ + (port) == 5 ? _GPIO_PORT_F_PIN_COUNT : \ + (port) == 6 ? _GPIO_PORT_G_PIN_COUNT : \ + (port) == 7 ? _GPIO_PORT_H_PIN_COUNT : \ + 0) +#else +#define _GPIO_PORT_SIZE(port) ( \ + (port) == 0 ? _GPIO_PORT_A_PIN_COUNT : \ + (port) == 1 ? _GPIO_PORT_B_PIN_COUNT : \ + (port) == 2 ? _GPIO_PORT_C_PIN_COUNT : \ + (port) == 3 ? _GPIO_PORT_D_PIN_COUNT : \ + (port) == 4 ? _GPIO_PORT_E_PIN_COUNT : \ + (port) == 5 ? _GPIO_PORT_F_PIN_COUNT : \ + 0) +#endif + +#if defined( _GPIO_PORT_G_PIN_MASK ) && defined( _GPIO_PORT_H_PIN_MASK ) +#define _GPIO_PORT_MASK(port) ( \ + (port) == 0 ? _GPIO_PORT_A_PIN_MASK : \ + (port) == 1 ? _GPIO_PORT_B_PIN_MASK : \ + (port) == 2 ? _GPIO_PORT_C_PIN_MASK : \ + (port) == 3 ? _GPIO_PORT_D_PIN_MASK : \ + (port) == 4 ? _GPIO_PORT_E_PIN_MASK : \ + (port) == 5 ? _GPIO_PORT_F_PIN_MASK : \ + (port) == 6 ? _GPIO_PORT_G_PIN_MASK : \ + (port) == 7 ? _GPIO_PORT_H_PIN_MASK : \ + 0) +#else +#define _GPIO_PORT_MASK(port) ( \ + (port) == 0 ? _GPIO_PORT_A_PIN_MASK : \ + (port) == 1 ? _GPIO_PORT_B_PIN_MASK : \ + (port) == 2 ? _GPIO_PORT_C_PIN_MASK : \ + (port) == 3 ? _GPIO_PORT_D_PIN_MASK : \ + (port) == 4 ? _GPIO_PORT_E_PIN_MASK : \ + (port) == 5 ? _GPIO_PORT_F_PIN_MASK : \ + 0) +#endif + +/** Validation of port and pin */ +#define GPIO_PORT_VALID(port) ( _GPIO_PORT_MASK(port) ) +#define GPIO_PORT_PIN_VALID(port, pin) ((( _GPIO_PORT_MASK(port)) >> (pin)) & 0x1 ) + +/** Highest GPIO pin number */ +#define GPIO_PIN_MAX 15 + +/** Highest GPIO port number */ +#if defined( _GPIO_PORT_G_PIN_COUNT ) && defined( _GPIO_PORT_H_PIN_COUNT ) +#define GPIO_PORT_MAX 7 +#else +#define GPIO_PORT_MAX 5 +#endif +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** GPIO ports ids. */ +typedef enum +{ +#if ( _GPIO_PORT_A_PIN_COUNT > 0 ) + gpioPortA = 0, +#endif +#if ( _GPIO_PORT_B_PIN_COUNT > 0 ) + gpioPortB = 1, +#endif +#if ( _GPIO_PORT_C_PIN_COUNT > 0 ) + gpioPortC = 2, +#endif +#if ( _GPIO_PORT_D_PIN_COUNT > 0 ) + gpioPortD = 3, +#endif +#if ( _GPIO_PORT_E_PIN_COUNT > 0 ) + gpioPortE = 4, +#endif +#if ( _GPIO_PORT_F_PIN_COUNT > 0 ) + gpioPortF = 5 +#endif +#if defined( _GPIO_PORT_G_PIN_COUNT ) && ( _GPIO_PORT_G_PIN_COUNT > 0 ) + gpioPortG = 6 +#endif +#if defined( _GPIO_PORT_H_PIN_COUNT ) && ( _GPIO_PORT_H_PIN_COUNT > 0 ) + gpioPortH = 7 +#endif +} GPIO_Port_TypeDef; + +#if defined( _GPIO_P_CTRL_DRIVEMODE_MASK ) +/** GPIO drive mode. */ +typedef enum +{ + /** Default 6mA */ + gpioDriveModeStandard = GPIO_P_CTRL_DRIVEMODE_STANDARD, + /** 0.5 mA */ + gpioDriveModeLowest = GPIO_P_CTRL_DRIVEMODE_LOWEST, + /** 20 mA */ + gpioDriveModeHigh = GPIO_P_CTRL_DRIVEMODE_HIGH, + /** 2 mA */ + gpioDriveModeLow = GPIO_P_CTRL_DRIVEMODE_LOW +} GPIO_DriveMode_TypeDef; +#endif + +#if defined( _GPIO_P_CTRL_DRIVESTRENGTH_MASK ) && defined( _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK ) +/** GPIO drive strength. */ +typedef enum +{ + /** GPIO weak 1mA and alternate function weak 1mA */ + gpioDriveStrengthWeakAlternateWeak = GPIO_P_CTRL_DRIVESTRENGTH_WEAK | GPIO_P_CTRL_DRIVESTRENGTHALT_WEAK, + + /** GPIO weak 1mA and alternate function strong 10mA */ + gpioDriveStrengthWeakAlternateStrong = GPIO_P_CTRL_DRIVESTRENGTH_WEAK | GPIO_P_CTRL_DRIVESTRENGTHALT_STRONG, + + /** GPIO strong 10mA and alternate function weak 1mA */ + gpioDriveStrengthStrongAlternateWeak = GPIO_P_CTRL_DRIVESTRENGTH_STRONG | GPIO_P_CTRL_DRIVESTRENGTHALT_WEAK, + + /** GPIO strong 10mA and alternate function strong 10mA */ + gpioDriveStrengthStrongAlternateStrong = GPIO_P_CTRL_DRIVESTRENGTH_STRONG | GPIO_P_CTRL_DRIVESTRENGTHALT_STRONG, +} GPIO_DriveStrength_TypeDef; +/* For legacy support */ +#define gpioDriveStrengthStrong gpioDriveStrengthStrongAlternateStrong +#define gpioDriveStrengthWeak gpioDriveStrengthWeakAlternateWeak +#endif + +/** Pin mode. For more details on each mode, please refer to the + * reference manual. */ +typedef enum +{ + /** Input disabled. Pullup if DOUT is set. */ + gpioModeDisabled = _GPIO_P_MODEL_MODE0_DISABLED, + /** Input enabled. Filter if DOUT is set */ + gpioModeInput = _GPIO_P_MODEL_MODE0_INPUT, + /** Input enabled. DOUT determines pull direction */ + gpioModeInputPull = _GPIO_P_MODEL_MODE0_INPUTPULL, + /** Input enabled with filter. DOUT determines pull direction */ + gpioModeInputPullFilter = _GPIO_P_MODEL_MODE0_INPUTPULLFILTER, + /** Push-pull output */ + gpioModePushPull = _GPIO_P_MODEL_MODE0_PUSHPULL, +#if defined( _GPIO_P_MODEL_MODE0_PUSHPULLDRIVE ) + /** Push-pull output with drive-strength set by DRIVEMODE */ + gpioModePushPullDrive = _GPIO_P_MODEL_MODE0_PUSHPULLDRIVE, +#endif +#if defined( _GPIO_P_MODEL_MODE0_PUSHPULLALT ) + /** Push-pull using alternate control */ + gpioModePushPullAlternate = _GPIO_P_MODEL_MODE0_PUSHPULLALT, +#endif + /** Wired-or output */ + gpioModeWiredOr = _GPIO_P_MODEL_MODE0_WIREDOR, + /** Wired-or output with pull-down */ + gpioModeWiredOrPullDown = _GPIO_P_MODEL_MODE0_WIREDORPULLDOWN, + /** Open-drain output */ + gpioModeWiredAnd = _GPIO_P_MODEL_MODE0_WIREDAND, + /** Open-drain output with filter */ + gpioModeWiredAndFilter = _GPIO_P_MODEL_MODE0_WIREDANDFILTER, + /** Open-drain output with pullup */ + gpioModeWiredAndPullUp = _GPIO_P_MODEL_MODE0_WIREDANDPULLUP, + /** Open-drain output with filter and pullup */ + gpioModeWiredAndPullUpFilter = _GPIO_P_MODEL_MODE0_WIREDANDPULLUPFILTER, +#if defined( _GPIO_P_MODEL_MODE0_WIREDANDDRIVE ) + /** Open-drain output with drive-strength set by DRIVEMODE */ + gpioModeWiredAndDrive = _GPIO_P_MODEL_MODE0_WIREDANDDRIVE, + /** Open-drain output with filter and drive-strength set by DRIVEMODE */ + gpioModeWiredAndDriveFilter = _GPIO_P_MODEL_MODE0_WIREDANDDRIVEFILTER, + /** Open-drain output with pullup and drive-strength set by DRIVEMODE */ + gpioModeWiredAndDrivePullUp = _GPIO_P_MODEL_MODE0_WIREDANDDRIVEPULLUP, + /** Open-drain output with filter, pullup and drive-strength set by DRIVEMODE */ + gpioModeWiredAndDrivePullUpFilter = _GPIO_P_MODEL_MODE0_WIREDANDDRIVEPULLUPFILTER +#endif +#if defined( _GPIO_P_MODEL_MODE0_WIREDANDALT ) + /** Open-drain output using alternate control */ + gpioModeWiredAndAlternate = _GPIO_P_MODEL_MODE0_WIREDANDALT, + /** Open-drain output using alternate control with filter */ + gpioModeWiredAndAlternateFilter = _GPIO_P_MODEL_MODE0_WIREDANDALTFILTER, + /** Open-drain output using alternate control with pullup */ + gpioModeWiredAndAlternatePullUp = _GPIO_P_MODEL_MODE0_WIREDANDALTPULLUP, + /** Open-drain output uisng alternate control with filter and pullup */ + gpioModeWiredAndAlternatePullUpFilter = _GPIO_P_MODEL_MODE0_WIREDANDALTPULLUPFILTER, +#endif +} GPIO_Mode_TypeDef; + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void GPIO_DbgLocationSet(unsigned int location); + +void GPIO_IntConfig(GPIO_Port_TypeDef port, + unsigned int pin, + bool risingEdge, + bool fallingEdge, + bool enable); + +void GPIO_PinModeSet(GPIO_Port_TypeDef port, + unsigned int pin, + GPIO_Mode_TypeDef mode, + unsigned int out); + +# if defined( _GPIO_EM4WUEN_MASK ) +void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask); +#endif + +/***************************************************************************//** + * @brief + * Enable/disable serial wire clock pin. + * + * @note + * Disabling SWDClk will disable the debug interface, which may result in + * a lockout if done early in startup (before debugger is able to halt core). + * + * @param[in] enable + * @li false - disable serial wire clock. + * @li true - enable serial wire clock (default after reset). + ******************************************************************************/ +__STATIC_INLINE void GPIO_DbgSWDClkEnable(bool enable) +{ +#if defined( _GPIO_ROUTE_SWCLKPEN_MASK ) + BUS_RegBitWrite(&(GPIO->ROUTE), _GPIO_ROUTE_SWCLKPEN_SHIFT, enable); +#elif defined( _GPIO_ROUTEPEN_SWCLKTCKPEN_MASK ) + BUS_RegBitWrite(&(GPIO->ROUTEPEN), _GPIO_ROUTEPEN_SWCLKTCKPEN_SHIFT, enable); +#else +#warning "ROUTE enable for SWCLK pin is not defined." +#endif +} + + +/***************************************************************************//** + * @brief + * Enable/disable serial wire data I/O pin. + * + * @note + * Disabling SWDClk will disable the debug interface, which may result in + * a lockout if done early in startup (before debugger is able to halt core). + * + * @param[in] enable + * @li false - disable serial wire data pin. + * @li true - enable serial wire data pin (default after reset). + ******************************************************************************/ +__STATIC_INLINE void GPIO_DbgSWDIOEnable(bool enable) +{ +#if defined( _GPIO_ROUTE_SWDIOPEN_MASK ) + BUS_RegBitWrite(&(GPIO->ROUTE), _GPIO_ROUTE_SWDIOPEN_SHIFT, enable); +#elif defined( _GPIO_ROUTEPEN_SWDIOTMSPEN_MASK ) + BUS_RegBitWrite(&(GPIO->ROUTEPEN), _GPIO_ROUTEPEN_SWDIOTMSPEN_SHIFT, enable); +#else +#warning "ROUTE enable for SWDIO pin is not defined." +#endif +} + + +#if defined( _GPIO_ROUTE_SWOPEN_MASK ) || defined( _GPIO_ROUTEPEN_SWVPEN_MASK ) +/***************************************************************************//** + * @brief + * Enable/Disable serial wire output pin. + * + * @note + * Enabling this pin is not sufficient to fully enable serial wire output + * which is also dependent on issues outside the GPIO module. Please refer to + * DBG_SWOEnable(). + * + * @param[in] enable + * @li false - disable serial wire viewer pin (default after reset). + * @li true - enable serial wire viewer pin. + ******************************************************************************/ +__STATIC_INLINE void GPIO_DbgSWOEnable(bool enable) +{ +#if defined( _GPIO_ROUTE_SWOPEN_MASK ) + BUS_RegBitWrite(&(GPIO->ROUTE), _GPIO_ROUTE_SWOPEN_SHIFT, enable); +#elif defined( _GPIO_ROUTEPEN_SWVPEN_MASK ) + BUS_RegBitWrite(&(GPIO->ROUTEPEN), _GPIO_ROUTEPEN_SWVPEN_SHIFT, enable); +#else +#warning "ROUTE enable for SWO/SWV pin is not defined." +#endif +} +#endif + +#if defined (_GPIO_P_CTRL_DRIVEMODE_MASK) +void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode); +#endif + +#if defined( _GPIO_P_CTRL_DRIVESTRENGTH_MASK ) +void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port, GPIO_DriveStrength_TypeDef strength); +#endif + +# if defined( _GPIO_EM4WUEN_MASK ) +/**************************************************************************//** + * @brief + * Disable GPIO pin wake-up from EM4. + * + * @param[in] pinmask + * Bitmask containing the bitwise logic OR of which GPIO pin(s) to disable. + * Refer to Reference Manuals for pinmask to GPIO port/pin mapping. + *****************************************************************************/ +__STATIC_INLINE void GPIO_EM4DisablePinWakeup(uint32_t pinmask) +{ + EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0); + + GPIO->EM4WUEN &= ~pinmask; +} +#endif + + +#if defined( _GPIO_EM4WUCAUSE_MASK ) || defined( _RMU_RSTCAUSE_EM4RST_MASK ) +/**************************************************************************//** + * @brief + * Check which GPIO pin(s) that caused a wake-up from EM4. + * + * @return + * Bitmask containing the bitwise logic OR of which GPIO pin(s) caused the + * wake-up. Refer to Reference Manuals for pinmask to GPIO port/pin mapping. + *****************************************************************************/ +__STATIC_INLINE uint32_t GPIO_EM4GetPinWakeupCause(void) +{ +#if defined( _GPIO_EM4WUCAUSE_MASK ) + return GPIO->EM4WUCAUSE & _GPIO_EM4WUCAUSE_MASK; +#else + return RMU->RSTCAUSE & _RMU_RSTCAUSE_EM4RST_MASK; +#endif +} +#endif + + +#if defined( GPIO_CTRL_EM4RET ) || defined( _EMU_EM4CTRL_EM4IORETMODE_MASK ) +/**************************************************************************//** + * @brief + * Enable GPIO pin retention of output enable, output value, pull enable and + * pull direction in EM4. + * + * @note + * For platform 2 parts, EMU_EM4Init() and EMU_UnlatchPinRetention() offers + * more pin retention features. This function implements the EM4EXIT retention + * mode on platform 2. + * + * @param[in] enable + * @li true - enable EM4 pin retention. + * @li false - disable EM4 pin retention. + *****************************************************************************/ +__STATIC_INLINE void GPIO_EM4SetPinRetention(bool enable) +{ + if (enable) + { +#if defined( GPIO_CTRL_EM4RET ) + GPIO->CTRL |= GPIO_CTRL_EM4RET; +#else + EMU->EM4CTRL = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4IORETMODE_MASK) + | EMU_EM4CTRL_EM4IORETMODE_EM4EXIT; +#endif + } + else + { +#if defined( GPIO_CTRL_EM4RET ) + GPIO->CTRL &= ~GPIO_CTRL_EM4RET; +#else + EMU->EM4CTRL = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4IORETMODE_MASK) + | EMU_EM4CTRL_EM4IORETMODE_DISABLE; +#endif + } +} +#endif + + +/***************************************************************************//** + * @brief + * Enable/disable input sensing. + * + * @details + * Disabling input sensing if not used, can save some energy consumption. + * + * @param[in] val + * Bitwise logic OR of one or more of: + * @li GPIO_INSENSE_INT - interrupt input sensing. + * @li GPIO_INSENSE_PRS - peripheral reflex system input sensing. + * + * @param[in] mask + * Mask containing bitwise logic OR of bits similar as for @p val used to + * indicate which input sense options to disable/enable. + ******************************************************************************/ +__STATIC_INLINE void GPIO_InputSenseSet(uint32_t val, uint32_t mask) +{ + GPIO->INSENSE = (GPIO->INSENSE & ~mask) | (val & mask); +} + + +/***************************************************************************//** + * @brief + * Clear one or more pending GPIO interrupts. + * + * @param[in] flags + * Bitwise logic OR of GPIO interrupt sources to clear. + ******************************************************************************/ +__STATIC_INLINE void GPIO_IntClear(uint32_t flags) +{ + GPIO->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more GPIO interrupts. + * + * @param[in] flags + * GPIO interrupt sources to disable. + ******************************************************************************/ +__STATIC_INLINE void GPIO_IntDisable(uint32_t flags) +{ + GPIO->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more GPIO interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using GPIO_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * GPIO interrupt sources to enable. + ******************************************************************************/ +__STATIC_INLINE void GPIO_IntEnable(uint32_t flags) +{ + GPIO->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending GPIO interrupts. + * + * @return + * GPIO interrupt sources pending. + ******************************************************************************/ +__STATIC_INLINE uint32_t GPIO_IntGet(void) +{ + return GPIO->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending GPIO interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled GPIO interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in GPIO_IEN register + * and + * - the OR combination of valid interrupt flags in GPIO_IF register. + ******************************************************************************/ +__STATIC_INLINE uint32_t GPIO_IntGetEnabled(void) +{ + uint32_t tmp; + + /* Store GPIO->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + tmp = GPIO->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return GPIO->IF & tmp; +} + + +/**************************************************************************//** + * @brief + * Set one or more pending GPIO interrupts from SW. + * + * @param[in] flags + * GPIO interrupt sources to set to pending. + *****************************************************************************/ +__STATIC_INLINE void GPIO_IntSet(uint32_t flags) +{ + GPIO->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Locks the GPIO configuration. + ******************************************************************************/ +__STATIC_INLINE void GPIO_Lock(void) +{ + GPIO->LOCK = GPIO_LOCK_LOCKKEY_LOCK; +} + + +/***************************************************************************//** + * @brief + * Read the pad value for a single pin in a GPIO port. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pin + * The pin number to read. + * + * @return + * The pin value, 0 or 1. + ******************************************************************************/ +__STATIC_INLINE unsigned int GPIO_PinInGet(GPIO_Port_TypeDef port, + unsigned int pin) +{ + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); + return BUS_RegBitRead(&GPIO->P[port].DIN, pin); +} + + +/***************************************************************************//** + * @brief + * Set a single pin in GPIO data out port register to 0. + * + * @note + * In order for the setting to take effect on the output pad, the pin must + * have been configured properly. If not, it will take effect whenever the + * pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pin + * The pin to set. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PinOutClear(GPIO_Port_TypeDef port, unsigned int pin) +{ + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); +#if defined( _GPIO_P_DOUTCLR_MASK ) + GPIO->P[port].DOUTCLR = 1 << pin; +#else + BUS_RegBitWrite(&GPIO->P[port].DOUT, pin, 0); +#endif +} + + +/***************************************************************************//** + * @brief + * Get current setting for a pin in a GPIO port data out register. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pin + * The pin to get setting for. + * + * @return + * The DOUT setting for the requested pin, 0 or 1. + ******************************************************************************/ +__STATIC_INLINE unsigned int GPIO_PinOutGet(GPIO_Port_TypeDef port, + unsigned int pin) +{ + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); + return BUS_RegBitRead(&GPIO->P[port].DOUT, pin); +} + + +/***************************************************************************//** + * @brief + * Set a single pin in GPIO data out register to 1. + * + * @note + * In order for the setting to take effect on the output pad, the pin must + * have been configured properly. If not, it will take effect whenever the + * pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pin + * The pin to set. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PinOutSet(GPIO_Port_TypeDef port, unsigned int pin) +{ + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); +#if defined( _GPIO_P_DOUTSET_MASK ) + GPIO->P[port].DOUTSET = 1 << pin; +#else + BUS_RegBitWrite(&GPIO->P[port].DOUT, pin, 1); +#endif +} + + +/***************************************************************************//** + * @brief + * Toggle a single pin in GPIO port data out register. + * + * @note + * In order for the setting to take effect on the output pad, the pin must + * have been configured properly. If not, it will take effect whenever the + * pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pin + * The pin to toggle. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PinOutToggle(GPIO_Port_TypeDef port, unsigned int pin) +{ + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); + + GPIO->P[port].DOUTTGL = 1 << pin; +} + + +/***************************************************************************//** + * @brief + * Read the pad values for GPIO port. + * + * @param[in] port + * The GPIO port to access. + ******************************************************************************/ +__STATIC_INLINE uint32_t GPIO_PortInGet(GPIO_Port_TypeDef port) +{ + EFM_ASSERT(GPIO_PORT_VALID(port)); + + return GPIO->P[port].DIN; +} + + +/***************************************************************************//** + * @brief + * Set bits in DOUT register for a port to 0. + * + * @note + * In order for the setting to take effect on the output pad, the pin must + * have been configured properly. If not, it will take effect whenever the + * pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pins + * Bit mask for bits to clear in DOUT register. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PortOutClear(GPIO_Port_TypeDef port, uint32_t pins) +{ + EFM_ASSERT(GPIO_PORT_VALID(port)); +#if defined( _GPIO_P_DOUTCLR_MASK ) + GPIO->P[port].DOUTCLR = pins; +#else + BUS_RegMaskedClear(&GPIO->P[port].DOUT, pins); +#endif +} + + +/***************************************************************************//** + * @brief + * Get current setting for a GPIO port data out register. + * + * @param[in] port + * The GPIO port to access. + * + * @return + * The data out setting for the requested port. + ******************************************************************************/ +__STATIC_INLINE uint32_t GPIO_PortOutGet(GPIO_Port_TypeDef port) +{ + EFM_ASSERT(GPIO_PORT_VALID(port)); + + return GPIO->P[port].DOUT; +} + + +/***************************************************************************//** + * @brief + * Set bits GPIO data out register to 1. + * + * @note + * In order for the setting to take effect on the respective output pads, the + * pins must have been configured properly. If not, it will take effect + * whenever the pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pins + * Bit mask for bits to set to 1 in DOUT register. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PortOutSet(GPIO_Port_TypeDef port, uint32_t pins) +{ + EFM_ASSERT(GPIO_PORT_VALID(port)); +#if defined( _GPIO_P_DOUTSET_MASK ) + GPIO->P[port].DOUTSET = pins; +#else + BUS_RegMaskedSet(&GPIO->P[port].DOUT, pins); +#endif +} + + +/***************************************************************************//** + * @brief + * Set GPIO port data out register. + * + * @note + * In order for the setting to take effect on the respective output pads, the + * pins must have been configured properly. If not, it will take effect + * whenever the pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] val + * Value to write to port data out register. + * + * @param[in] mask + * Mask indicating which bits to modify. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PortOutSetVal(GPIO_Port_TypeDef port, + uint32_t val, + uint32_t mask) +{ + EFM_ASSERT(GPIO_PORT_VALID(port)); + + GPIO->P[port].DOUT = (GPIO->P[port].DOUT & ~mask) | (val & mask); +} + + +/***************************************************************************//** + * @brief + * Toggle pins in GPIO port data out register. + * + * @note + * In order for the setting to take effect on the output pad, the pin must + * have been configured properly. If not, it will take effect whenever the + * pin has been properly configured. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pins + * Bitmask with pins to toggle. + ******************************************************************************/ +__STATIC_INLINE void GPIO_PortOutToggle(GPIO_Port_TypeDef port, uint32_t pins) +{ + EFM_ASSERT(GPIO_PORT_VALID(port)); + + GPIO->P[port].DOUTTGL = pins; +} + + +/***************************************************************************//** + * @brief + * Unlocks the GPIO configuration. + ******************************************************************************/ +__STATIC_INLINE void GPIO_Unlock(void) +{ + GPIO->LOCK = GPIO_LOCK_LOCKKEY_UNLOCK; +} + +/** @} (end addtogroup GPIO) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_GPIO_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_i2c.h b/cpu/efm32_common/emlib/inc/em_i2c.h new file mode 100644 index 0000000000000..8423e275001b7 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_i2c.h @@ -0,0 +1,531 @@ +/***************************************************************************//** + * @file em_i2c.h + * @brief Inter-intergrated circuit (I2C) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_I2C_H__ +#define __SILICON_LABS_EM_I2C_H__ + +#include "em_device.h" +#if defined(I2C_COUNT) && (I2C_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup I2C + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** + * @brief + * Standard mode max frequency assuming using 4:4 ratio for Nlow:Nhigh. + * @details + * From I2C specification: Min Tlow = 4.7us, min Thigh = 4.0us, + * max Trise=1.0us, max Tfall=0.3us. Since ratio is 4:4, have to use + * worst case value of Tlow or Thigh as base. + * + * 1/(Tlow + Thigh + 1us + 0.3us) = 1/(4.7 + 4.7 + 1.3)us = 93458Hz + * @note + * Due to chip characteristics, the max value is somewhat reduced. + */ +#if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_TINY_FAMILY) \ + || defined(_EFM32_ZERO_FAMILY) || defined(_EFM32_HAPPY_FAMILY) +#define I2C_FREQ_STANDARD_MAX 93000 +#elif defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +#define I2C_FREQ_STANDARD_MAX 92000 +#elif defined(_SILICON_LABS_32B_PLATFORM_2) +// None of the chips on this platform has been characterized on this parameter. +// Use same value as on Wonder until further notice. +#define I2C_FREQ_STANDARD_MAX 92000 +#else +#error "Unknown device family." +#endif + +/** + * @brief + * Fast mode max frequency assuming using 6:3 ratio for Nlow:Nhigh. + * @details + * From I2C specification: Min Tlow = 1.3us, min Thigh = 0.6us, + * max Trise=0.3us, max Tfall=0.3us. Since ratio is 6:3, have to use + * worst case value of Tlow or 2xThigh as base. + * + * 1/(Tlow + Thigh + 0.3us + 0.3us) = 1/(1.3 + 0.65 + 0.6)us = 392157Hz + */ +#define I2C_FREQ_FAST_MAX 392157 + + +/** + * @brief + * Fast mode+ max frequency assuming using 11:6 ratio for Nlow:Nhigh. + * @details + * From I2C specification: Min Tlow = 0.5us, min Thigh = 0.26us, + * max Trise=0.12us, max Tfall=0.12us. Since ratio is 11:6, have to use + * worst case value of Tlow or (11/6)xThigh as base. + * + * 1/(Tlow + Thigh + 0.12us + 0.12us) = 1/(0.5 + 0.273 + 0.24)us = 987167Hz + */ +#define I2C_FREQ_FASTPLUS_MAX 987167 + + +/** + * @brief + * Indicate plain write sequence: S+ADDR(W)+DATA0+P. + * @details + * @li S - Start + * @li ADDR(W) - address with W/R bit cleared + * @li DATA0 - Data taken from buffer with index 0 + * @li P - Stop + */ +#define I2C_FLAG_WRITE 0x0001 + +/** + * @brief + * Indicate plain read sequence: S+ADDR(R)+DATA0+P. + * @details + * @li S - Start + * @li ADDR(R) - address with W/R bit set + * @li DATA0 - Data read into buffer with index 0 + * @li P - Stop + */ +#define I2C_FLAG_READ 0x0002 + +/** + * @brief + * Indicate combined write/read sequence: S+ADDR(W)+DATA0+Sr+ADDR(R)+DATA1+P. + * @details + * @li S - Start + * @li Sr - Repeated start + * @li ADDR(W) - address with W/R bit cleared + * @li ADDR(R) - address with W/R bit set + * @li DATAn - Data written from/read into buffer with index n + * @li P - Stop + */ +#define I2C_FLAG_WRITE_READ 0x0004 + +/** + * @brief + * Indicate write sequence using two buffers: S+ADDR(W)+DATA0+DATA1+P. + * @details + * @li S - Start + * @li ADDR(W) - address with W/R bit cleared + * @li DATAn - Data written from buffer with index n + * @li P - Stop + */ +#define I2C_FLAG_WRITE_WRITE 0x0008 + +/** Use 10 bit address. */ +#define I2C_FLAG_10BIT_ADDR 0x0010 + + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Clock low to high ratio settings. */ +typedef enum +{ + i2cClockHLRStandard = _I2C_CTRL_CLHR_STANDARD, /**< Ratio is 4:4 */ + i2cClockHLRAsymetric = _I2C_CTRL_CLHR_ASYMMETRIC, /**< Ratio is 6:3 */ + i2cClockHLRFast = _I2C_CTRL_CLHR_FAST /**< Ratio is 11:3 */ +} I2C_ClockHLR_TypeDef; + + +/** Return codes for single master mode transfer function. */ +typedef enum +{ + /* In progress code (>0) */ + i2cTransferInProgress = 1, /**< Transfer in progress. */ + + /* Complete code (=0) */ + i2cTransferDone = 0, /**< Transfer completed successfully. */ + + /* Transfer error codes (<0) */ + i2cTransferNack = -1, /**< NACK received during transfer. */ + i2cTransferBusErr = -2, /**< Bus error during transfer (misplaced START/STOP). */ + i2cTransferArbLost = -3, /**< Arbitration lost during transfer. */ + i2cTransferUsageFault = -4, /**< Usage fault. */ + i2cTransferSwFault = -5 /**< SW fault. */ +} I2C_TransferReturn_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** I2C initialization structure. */ +typedef struct +{ + /** Enable I2C peripheral when init completed. */ + bool enable; + + /** Set to master (true) or slave (false) mode */ + bool master; + + /** + * I2C reference clock assumed when configuring bus frequency setup. + * Set it to 0 if currently configurated reference clock shall be used + * This parameter is only applicable if operating in master mode. + */ + uint32_t refFreq; + + /** + * (Max) I2C bus frequency to use. This parameter is only applicable + * if operating in master mode. + */ + uint32_t freq; + + /** Clock low/high ratio control. */ + I2C_ClockHLR_TypeDef clhr; +} I2C_Init_TypeDef; + +/** Suggested default config for I2C init structure. */ +#define I2C_INIT_DEFAULT \ +{ \ + true, /* Enable when init done */ \ + true, /* Set to master mode */ \ + 0, /* Use currently configured reference clock */ \ + I2C_FREQ_STANDARD_MAX, /* Set to standard rate assuring being */ \ + /* within I2C spec */ \ + i2cClockHLRStandard /* Set to use 4:4 low/high duty cycle */ \ +} + + +/** + * @brief + * Master mode transfer message structure used to define a complete + * I2C transfer sequence (from start to stop). + * @details + * The structure allows for defining the following types of sequences, + * please refer to defines for sequence details. + * @li #I2C_FLAG_READ - data read into buf[0].data + * @li #I2C_FLAG_WRITE - data written from buf[0].data + * @li #I2C_FLAG_WRITE_READ - data written from buf[0].data and read + * into buf[1].data + * @li #I2C_FLAG_WRITE_WRITE - data written from buf[0].data and + * buf[1].data + */ +typedef struct +{ + /** + * @brief + * Address to use after (repeated) start. + * @details + * Layout details, A = address bit, X = don't care bit (set to 0): + * @li 7 bit address - use format AAAA AAAX. + * @li 10 bit address - use format XXXX XAAX AAAA AAAA + */ + uint16_t addr; + + /** Flags defining sequence type and details, see I2C_FLAG_... defines. */ + uint16_t flags; + + /** + * Buffers used to hold data to send from or receive into depending + * on sequence type. + */ + struct + { + /** Buffer used for data to transmit/receive, must be @p len long. */ + uint8_t *data; + + /** + * Number of bytes in @p data to send or receive. Notice that when + * receiving data to this buffer, at least 1 byte must be received. + * Setting @p len to 0 in the receive case is considered a usage fault. + * Transmitting 0 bytes is legal, in which case only the address + * is transmitted after the start condition. + */ + uint16_t len; + } buf[2]; +} I2C_TransferSeq_TypeDef; + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c); +void I2C_BusFreqSet(I2C_TypeDef *i2c, + uint32_t freqRef, + uint32_t freqScl, + I2C_ClockHLR_TypeDef i2cMode); +void I2C_Enable(I2C_TypeDef *i2c, bool enable); +void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init); + +/***************************************************************************//** + * @brief + * Clear one or more pending I2C interrupts. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] flags + * Pending I2C interrupt source to clear. Use a bitwse logic OR combination of + * valid interrupt flags for the I2C module (I2C_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void I2C_IntClear(I2C_TypeDef *i2c, uint32_t flags) +{ + i2c->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more I2C interrupts. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] flags + * I2C interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the I2C module (I2C_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void I2C_IntDisable(I2C_TypeDef *i2c, uint32_t flags) +{ + i2c->IEN &= ~(flags); +} + + +/***************************************************************************//** + * @brief + * Enable one or more I2C interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using I2C_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] flags + * I2C interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the I2C module (I2C_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void I2C_IntEnable(I2C_TypeDef *i2c, uint32_t flags) +{ + i2c->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending I2C interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * I2C interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the I2C module (I2C_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t I2C_IntGet(I2C_TypeDef *i2c) +{ + return i2c->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending I2C interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * Pending and enabled I2C interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in I2Cn_IEN and + * - the pending interrupt flags I2Cn_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t I2C_IntGetEnabled(I2C_TypeDef *i2c) +{ + uint32_t ien; + + ien = i2c->IEN; + return i2c->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending I2C interrupts from SW. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] flags + * I2C interrupt sources to set to pending. Use a bitwise logic OR combination + * of valid interrupt flags for the I2C module (I2C_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void I2C_IntSet(I2C_TypeDef *i2c, uint32_t flags) +{ + i2c->IFS = flags; +} + +void I2C_Reset(I2C_TypeDef *i2c); + +/***************************************************************************//** + * @brief + * Get slave address used for I2C peripheral (when operating in slave mode). + * + * @details + * For 10 bit addressing mode, the address is split in two bytes, and only + * the first byte setting is fetched, effectively only controlling the 2 most + * significant bits of the 10 bit address. Full handling of 10 bit addressing + * in slave mode requires additional SW handling. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * I2C slave address in use. The 7 most significant bits define the actual + * address, the least significant bit is reserved and always returned as 0. + ******************************************************************************/ +__STATIC_INLINE uint8_t I2C_SlaveAddressGet(I2C_TypeDef *i2c) +{ + return ((uint8_t)(i2c->SADDR)); +} + + +/***************************************************************************//** + * @brief + * Set slave address to use for I2C peripheral (when operating in slave mode). + * + * @details + * For 10 bit addressing mode, the address is split in two bytes, and only + * the first byte is set, effectively only controlling the 2 most significant + * bits of the 10 bit address. Full handling of 10 bit addressing in slave + * mode requires additional SW handling. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] addr + * I2C slave address to use. The 7 most significant bits define the actual + * address, the least significant bit is reserved and always set to 0. + ******************************************************************************/ +__STATIC_INLINE void I2C_SlaveAddressSet(I2C_TypeDef *i2c, uint8_t addr) +{ + i2c->SADDR = (uint32_t)addr & 0xfe; +} + + +/***************************************************************************//** + * @brief + * Get slave address mask used for I2C peripheral (when operating in slave + * mode). + * + * @details + * The address mask defines how the comparator works. A bit position with + * value 0 means that the corresponding slave address bit is ignored during + * comparison (don't care). A bit position with value 1 means that the + * corresponding slave address bit must match. + * + * For 10 bit addressing mode, the address is split in two bytes, and only + * the mask for the first address byte is fetched, effectively only + * controlling the 2 most significant bits of the 10 bit address. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * I2C slave address mask in use. The 7 most significant bits define the + * actual address mask, the least significant bit is reserved and always + * returned as 0. + ******************************************************************************/ +__STATIC_INLINE uint8_t I2C_SlaveAddressMaskGet(I2C_TypeDef *i2c) +{ + return ((uint8_t)(i2c->SADDRMASK)); +} + + +/***************************************************************************//** + * @brief + * Set slave address mask used for I2C peripheral (when operating in slave + * mode). + * + * @details + * The address mask defines how the comparator works. A bit position with + * value 0 means that the corresponding slave address bit is ignored during + * comparison (don't care). A bit position with value 1 means that the + * corresponding slave address bit must match. + * + * For 10 bit addressing mode, the address is split in two bytes, and only + * the mask for the first address byte is set, effectively only controlling + * the 2 most significant bits of the 10 bit address. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] mask + * I2C slave address mask to use. The 7 most significant bits define the + * actual address mask, the least significant bit is reserved and should + * be 0. + ******************************************************************************/ +__STATIC_INLINE void I2C_SlaveAddressMaskSet(I2C_TypeDef *i2c, uint8_t mask) +{ + i2c->SADDRMASK = (uint32_t)mask & 0xfe; +} + + +I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c); +I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c, + I2C_TransferSeq_TypeDef *seq); + +/** @} (end addtogroup I2C) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_I2C_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_idac.h b/cpu/efm32_common/emlib/inc/em_idac.h new file mode 100644 index 0000000000000..0ba4aa172c2d9 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_idac.h @@ -0,0 +1,435 @@ +/***************************************************************************//** + * @file em_idac.h + * @brief Current Digital to Analog Converter (IDAC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_IDAC_H__ +#define __SILICON_LABS_EM_IDAC_H__ + +#include "em_device.h" + +#if defined(IDAC_COUNT) && (IDAC_COUNT > 0) +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup IDAC + * @{ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of IDAC register block pointer reference for assert statements. */ +#define IDAC_REF_VALID(ref) ((ref) == IDAC0) + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Output mode. */ +typedef enum +{ +#if defined( _IDAC_CTRL_OUTMODE_MASK ) + idacOutputPin = IDAC_CTRL_OUTMODE_PIN, /**< Output to IDAC OUT pin */ + idacOutputADC = IDAC_CTRL_OUTMODE_ADC /**< Output to ADC */ +#elif ( _IDAC_CTRL_APORTOUTSEL_MASK ) + idacOutputAPORT1XCH0 = IDAC_CTRL_APORTOUTSEL_APORT1XCH0, /**< Output to APORT 1X CH0 */ + idacOutputAPORT1YCH1 = IDAC_CTRL_APORTOUTSEL_APORT1YCH1, /**< Output to APORT 1Y CH1 */ + idacOutputAPORT1XCH2 = IDAC_CTRL_APORTOUTSEL_APORT1XCH2, /**< Output to APORT 1X CH2 */ + idacOutputAPORT1YCH3 = IDAC_CTRL_APORTOUTSEL_APORT1YCH3, /**< Output to APORT 1Y CH3 */ + idacOutputAPORT1XCH4 = IDAC_CTRL_APORTOUTSEL_APORT1XCH4, /**< Output to APORT 1X CH4 */ + idacOutputAPORT1YCH5 = IDAC_CTRL_APORTOUTSEL_APORT1YCH5, /**< Output to APORT 1Y CH5 */ + idacOutputAPORT1XCH6 = IDAC_CTRL_APORTOUTSEL_APORT1XCH6, /**< Output to APORT 1X CH6 */ + idacOutputAPORT1YCH7 = IDAC_CTRL_APORTOUTSEL_APORT1YCH7, /**< Output to APORT 1Y CH7 */ + idacOutputAPORT1XCH8 = IDAC_CTRL_APORTOUTSEL_APORT1XCH8, /**< Output to APORT 1X CH8 */ + idacOutputAPORT1YCH9 = IDAC_CTRL_APORTOUTSEL_APORT1YCH9, /**< Output to APORT 1Y CH9 */ + idacOutputAPORT1XCH10 = IDAC_CTRL_APORTOUTSEL_APORT1XCH10, /**< Output to APORT 1X CH10 */ + idacOutputAPORT1YCH11 = IDAC_CTRL_APORTOUTSEL_APORT1YCH11, /**< Output to APORT 1Y CH11 */ + idacOutputAPORT1XCH12 = IDAC_CTRL_APORTOUTSEL_APORT1XCH12, /**< Output to APORT 1X CH12 */ + idacOutputAPORT1YCH13 = IDAC_CTRL_APORTOUTSEL_APORT1YCH13, /**< Output to APORT 1Y CH13 */ + idacOutputAPORT1XCH14 = IDAC_CTRL_APORTOUTSEL_APORT1XCH14, /**< Output to APORT 1X CH14 */ + idacOutputAPORT1YCH15 = IDAC_CTRL_APORTOUTSEL_APORT1YCH15, /**< Output to APORT 1Y CH15 */ + idacOutputAPORT1XCH16 = IDAC_CTRL_APORTOUTSEL_APORT1XCH16, /**< Output to APORT 1X CH16 */ + idacOutputAPORT1YCH17 = IDAC_CTRL_APORTOUTSEL_APORT1YCH17, /**< Output to APORT 1Y CH17 */ + idacOutputAPORT1XCH18 = IDAC_CTRL_APORTOUTSEL_APORT1XCH18, /**< Output to APORT 1X CH18 */ + idacOutputAPORT1YCH19 = IDAC_CTRL_APORTOUTSEL_APORT1YCH19, /**< Output to APORT 1Y CH19 */ + idacOutputAPORT1XCH20 = IDAC_CTRL_APORTOUTSEL_APORT1XCH20, /**< Output to APORT 1X CH20 */ + idacOutputAPORT1YCH21 = IDAC_CTRL_APORTOUTSEL_APORT1YCH21, /**< Output to APORT 1Y CH21 */ + idacOutputAPORT1XCH22 = IDAC_CTRL_APORTOUTSEL_APORT1XCH22, /**< Output to APORT 1X CH22 */ + idacOutputAPORT1YCH23 = IDAC_CTRL_APORTOUTSEL_APORT1YCH23, /**< Output to APORT 1Y CH23 */ + idacOutputAPORT1XCH24 = IDAC_CTRL_APORTOUTSEL_APORT1XCH24, /**< Output to APORT 1X CH24 */ + idacOutputAPORT1YCH25 = IDAC_CTRL_APORTOUTSEL_APORT1YCH25, /**< Output to APORT 1Y CH25 */ + idacOutputAPORT1XCH26 = IDAC_CTRL_APORTOUTSEL_APORT1XCH26, /**< Output to APORT 1X CH26 */ + idacOutputAPORT1YCH27 = IDAC_CTRL_APORTOUTSEL_APORT1YCH27, /**< Output to APORT 1Y CH27 */ + idacOutputAPORT1XCH28 = IDAC_CTRL_APORTOUTSEL_APORT1XCH28, /**< Output to APORT 1X CH28 */ + idacOutputAPORT1YCH29 = IDAC_CTRL_APORTOUTSEL_APORT1YCH29, /**< Output to APORT 1Y CH29 */ + idacOutputAPORT1XCH30 = IDAC_CTRL_APORTOUTSEL_APORT1XCH30, /**< Output to APORT 1X CH30 */ + idacOutputAPORT1YCH31 = IDAC_CTRL_APORTOUTSEL_APORT1YCH31, /**< Output to APORT 1Y CH31 */ +#endif +} IDAC_OutMode_TypeDef; + + +/** Selects which Peripheral Reflex System (PRS) signal to use when + PRS is set to control the IDAC output. */ +typedef enum +{ + idacPRSSELCh0 = IDAC_CTRL_PRSSEL_PRSCH0, /**< PRS channel 0. */ + idacPRSSELCh1 = IDAC_CTRL_PRSSEL_PRSCH1, /**< PRS channel 1. */ + idacPRSSELCh2 = IDAC_CTRL_PRSSEL_PRSCH2, /**< PRS channel 2. */ + idacPRSSELCh3 = IDAC_CTRL_PRSSEL_PRSCH3, /**< PRS channel 3. */ +#if defined( IDAC_CTRL_PRSSEL_PRSCH4 ) + idacPRSSELCh4 = IDAC_CTRL_PRSSEL_PRSCH4, /**< PRS channel 4. */ + idacPRSSELCh5 = IDAC_CTRL_PRSSEL_PRSCH5, /**< PRS channel 5. */ +#endif +#if defined( IDAC_CTRL_PRSSEL_PRSCH6 ) + idacPRSSELCh6 = IDAC_CTRL_PRSSEL_PRSCH6, /**< PRS channel 6. */ + idacPRSSELCh7 = IDAC_CTRL_PRSSEL_PRSCH7, /**< PRS channel 7. */ + idacPRSSELCh8 = IDAC_CTRL_PRSSEL_PRSCH8, /**< PRS channel 8. */ + idacPRSSELCh9 = IDAC_CTRL_PRSSEL_PRSCH9, /**< PRS channel 9. */ + idacPRSSELCh10 = IDAC_CTRL_PRSSEL_PRSCH10, /**< PRS channel 10 */ + idacPRSSELCh11 = IDAC_CTRL_PRSSEL_PRSCH11, /**< PRS channel 11 */ +#endif +} IDAC_PRSSEL_TypeDef; + + +/** Selects which current range to use. */ +typedef enum +{ + idacCurrentRange0 = IDAC_CURPROG_RANGESEL_RANGE0, /**< current range 0. */ + idacCurrentRange1 = IDAC_CURPROG_RANGESEL_RANGE1, /**< current range 1. */ + idacCurrentRange2 = IDAC_CURPROG_RANGESEL_RANGE2, /**< current range 2. */ + idacCurrentRange3 = IDAC_CURPROG_RANGESEL_RANGE3, /**< current range 3. */ +} IDAC_Range_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** IDAC init structure, common for both channels. */ +typedef struct +{ + /** Enable IDAC. */ + bool enable; + + /** Output mode */ + IDAC_OutMode_TypeDef outMode; + + /** + * Enable Peripheral reflex system (PRS) to control IDAC output. If false, + * the IDAC output is controlled by writing to IDAC_OUTEN in IDAC_CTRL or + * by calling IDAC_OutEnable(). + */ + bool prsEnable; + + /** + * Peripheral reflex system channel selection. Only applicable if @p prsEnable + * is enabled. + */ + IDAC_PRSSEL_TypeDef prsSel; + + /** Enable/disable current sink mode. */ + bool sinkEnable; + +} IDAC_Init_TypeDef; + +/** Default config for IDAC init structure. */ +#if defined( _IDAC_CTRL_OUTMODE_MASK ) +#define IDAC_INIT_DEFAULT \ +{ \ + false, /**< Leave IDAC disabled when init done. */ \ + idacOutputPin, /**< Output to IDAC output pin. */ \ + false, /**< Disable PRS triggering. */ \ + idacPRSSELCh0, /**< Select PRS ch0 (if PRS triggering enabled). */ \ + false /**< Disable current sink mode. */ \ +} +#elif ( _IDAC_CTRL_APORTOUTSEL_MASK ) +#define IDAC_INIT_DEFAULT \ +{ \ + false, /**< Leave IDAC disabled when init done. */ \ + idacOutputAPORT1XCH0, /**< Output to APORT. */ \ + false, /**< Disable PRS triggering. */ \ + idacPRSSELCh0, /**< Select PRS ch0 (if PRS triggering enabled). */ \ + false /**< Disable current sink mode. */ \ +} +#endif + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initialize IDAC. + * + * @details + * Initializes IDAC according to the initialization structure parameter, and + * sets the default calibration value stored in the DEVINFO structure. + * + * @note + * This function will disable the IDAC prior to configuration. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] init + * Pointer to IDAC initialization structure. + ******************************************************************************/ +void IDAC_Init(IDAC_TypeDef *idac, const IDAC_Init_TypeDef *init); + + +/***************************************************************************//** + * @brief + * Enable/disable IDAC. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] enable + * true to enable IDAC, false to disable. + ******************************************************************************/ +void IDAC_Enable(IDAC_TypeDef *idac, bool enable); + + +/***************************************************************************//** + * @brief + * Reset IDAC to same state as after a HW reset. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + ******************************************************************************/ +void IDAC_Reset(IDAC_TypeDef *idac); + + +/***************************************************************************//** + * @brief + * Enable/disable Minimal Output Transition mode. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] enable + * true to enable Minimal Output Transition mode, false to disable. + ******************************************************************************/ +void IDAC_MinimalOutputTransitionMode(IDAC_TypeDef *idac, bool enable); + + +/***************************************************************************//** + * @brief + * Set the current range of the IDAC output. + * + * @details + * This function sets the current range of the IDAC output. The function + * also updates the IDAC calibration register (IDAC_CAL) with the default + * calibration value (from DEVINFO, factory setting) corresponding to the + * specified range. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] range + * Current range value. + ******************************************************************************/ +void IDAC_RangeSet(IDAC_TypeDef *idac, const IDAC_Range_TypeDef range); + + +/***************************************************************************//** + * @brief + * Set the current step of the IDAC output. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] step + * Step value for IDAC output. Valid range is 0-31. + ******************************************************************************/ +void IDAC_StepSet(IDAC_TypeDef *idac, const uint32_t step); + + +/***************************************************************************//** + * @brief + * Enable/disable the IDAC OUT pin. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] enable + * true to enable the IDAC OUT pin, false to disable. + ******************************************************************************/ +void IDAC_OutEnable(IDAC_TypeDef *idac, bool enable); + + +#if defined( _IDAC_IEN_MASK ) +/***************************************************************************//** + * @brief + * Clear one or more pending IDAC interrupts. + * + * @param[in] IDAC + * Pointer to IDAC peripheral register block. + * + * @param[in] flags + * Pending IDAC interrupt source(s) to clear. Use one or more valid + * interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void IDAC_IntClear(IDAC_TypeDef *idac, uint32_t flags) +{ + idac->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more IDAC interrupts. + * + * @param[in] IDAC + * Pointer to IDAC peripheral register block. + * + * @param[in] flags + * IDAC interrupt source(s) to disable. Use one or more valid + * interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void IDAC_IntDisable(IDAC_TypeDef *idac, uint32_t flags) +{ + idac->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more IDAC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using IDAC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] IDAC + * Pointer to IDAC peripheral register block. + * + * @param[in] flags + * IDAC interrupt source(s) to enable. Use one or more valid + * interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void IDAC_IntEnable(IDAC_TypeDef *idac, uint32_t flags) +{ + idac->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending IDAC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] IDAC + * Pointer to IDAC peripheral register block. + * + * @return + * IDAC interrupt source(s) pending. Returns one or more valid + * interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE uint32_t IDAC_IntGet(IDAC_TypeDef *idac) +{ + return idac->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending IDAC interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] IDAC + * Pointer to IDAC peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled IDAC interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in IDACx_IEN_nnn + * register (IDACx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the IDAC module + * (IDACx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t IDAC_IntGetEnabled(IDAC_TypeDef *idac) +{ + uint32_t ien; + + /* Store flags in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = idac->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return idac->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending IDAC interrupts from SW. + * + * @param[in] IDAC + * Pointer to IDAC peripheral register block. + * + * @param[in] flags + * IDAC interrupt source(s) to set to pending. Use one or more valid + * interrupt flags for the IDAC module (IDAC_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void IDAC_IntSet(IDAC_TypeDef *idac, uint32_t flags) +{ + idac->IFS = flags; +} +#endif + + +/** @} (end addtogroup IDAC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(IDAC_COUNT) && (IDAC_COUNT > 0) */ + +#endif /* __SILICON_LABS_EM_IDAC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_int.h b/cpu/efm32_common/emlib/inc/em_int.h new file mode 100644 index 0000000000000..2a306d728fca7 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_int.h @@ -0,0 +1,121 @@ +/***************************************************************************//** + * @file em_int.h + * @brief Interrupt enable/disable unit API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_INT_H__ +#define __SILICON_LABS_EM_INT_H__ + +#include "em_device.h" + +extern uint32_t INT_LockCnt; + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +#ifndef UINT32_MAX +#define UINT32_MAX ((uint32_t)(0xFFFFFFFF)) +#endif +/** @endcond */ + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup INT + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Disable interrupts. + * + * @details + * Disable interrupts and increment lock level counter. + * + * @return + * The resulting interrupt disable nesting level. + * + ******************************************************************************/ +__STATIC_INLINE uint32_t INT_Disable(void) +{ + __disable_irq(); + if (INT_LockCnt < UINT32_MAX) + { + INT_LockCnt++; + } + + return INT_LockCnt; +} + +/***************************************************************************//** + * @brief + * Enable interrupts. + * + * @return + * The resulting interrupt disable nesting level. + * + * @details + * Decrement interrupt lock level counter and enable interrupts if counter + * reached zero. + * + ******************************************************************************/ +__STATIC_INLINE uint32_t INT_Enable(void) +{ + uint32_t retVal; + + if (INT_LockCnt > 0) + { + INT_LockCnt--; + retVal = INT_LockCnt; + if (retVal == 0) + { + __enable_irq(); + } + return retVal; + } + else + { + return 0; + } +} + +/** @} (end addtogroup INT) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_INT_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_lcd.h b/cpu/efm32_common/emlib/inc/em_lcd.h new file mode 100644 index 0000000000000..090a05df568ef --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_lcd.h @@ -0,0 +1,612 @@ +/***************************************************************************//** + * @file em_lcd.h + * @brief Liquid Crystal Display (LCD) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_LCD_H__ +#define __SILICON_LABS_EM_LCD_H__ + +#include "em_device.h" + +#if defined(LCD_COUNT) && (LCD_COUNT > 0) +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LCD + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** MUX setting */ +typedef enum +{ + /** Static (segments can be multiplexed with LCD_COM[0]) */ + lcdMuxStatic = LCD_DISPCTRL_MUX_STATIC, + /** Duplex / 1/2 Duty cycle (segments can be multiplexed with LCD_COM[0:1]) */ + lcdMuxDuplex = LCD_DISPCTRL_MUX_DUPLEX, + /** Triplex / 1/3 Duty cycle (segments can be multiplexed with LCD_COM[0:2]) */ + lcdMuxTriplex = LCD_DISPCTRL_MUX_TRIPLEX, + /** Quadruplex / 1/4 Duty cycle (segments can be multiplexed with LCD_COM[0:3]) */ + lcdMuxQuadruplex = LCD_DISPCTRL_MUX_QUADRUPLEX, +#if defined(LCD_DISPCTRL_MUXE_MUXE) + /** Sextaplex / 1/6 Duty cycle (segments can be multiplexed with LCD_COM[0:5]) */ + lcdMuxSextaplex = LCD_DISPCTRL_MUXE_MUXE | LCD_DISPCTRL_MUX_DUPLEX, + /** Octaplex / 1/6 Duty cycle (segments can be multiplexed with LCD_COM[0:5]) */ + lcdMuxOctaplex = LCD_DISPCTRL_MUXE_MUXE | LCD_DISPCTRL_MUX_QUADRUPLEX +#endif +} LCD_Mux_TypeDef; + +/** Bias setting */ +typedef enum +{ + /** Static (2 levels) */ + lcdBiasStatic = LCD_DISPCTRL_BIAS_STATIC, + /** 1/2 Bias (3 levels) */ + lcdBiasOneHalf = LCD_DISPCTRL_BIAS_ONEHALF, + /** 1/3 Bias (4 levels) */ + lcdBiasOneThird = LCD_DISPCTRL_BIAS_ONETHIRD, +#if defined(LCD_DISPCTRL_BIAS_ONEFOURTH) + /** 1/4 Bias (5 levels) */ + lcdBiasOneFourth = LCD_DISPCTRL_BIAS_ONEFOURTH, +#endif +} LCD_Bias_TypeDef; + +/** Wave type */ +typedef enum +{ + /** Low power optimized waveform output */ + lcdWaveLowPower = LCD_DISPCTRL_WAVE_LOWPOWER, + /** Regular waveform output */ + lcdWaveNormal = LCD_DISPCTRL_WAVE_NORMAL +} LCD_Wave_TypeDef; + +/** VLCD Voltage Source */ +typedef enum +{ + /** VLCD Powered by VDD */ + lcdVLCDSelVDD = LCD_DISPCTRL_VLCDSEL_VDD, + /** VLCD Powered by external VDD / Voltage Boost */ + lcdVLCDSelVExtBoost = LCD_DISPCTRL_VLCDSEL_VEXTBOOST +} LCD_VLCDSel_TypeDef; + +/** Contrast Configuration */ +typedef enum +{ + /** Contrast is adjusted relative to VDD (VLCD) */ + lcdConConfVLCD = LCD_DISPCTRL_CONCONF_VLCD, + /** Contrast is adjusted relative to Ground */ + lcdConConfGND = LCD_DISPCTRL_CONCONF_GND +} LCD_ConConf_TypeDef; + +/** Voltage Boost Level - Datasheets document setting for each part number */ +typedef enum +{ + lcdVBoostLevel0 = LCD_DISPCTRL_VBLEV_LEVEL0, /**< Voltage boost LEVEL0 */ + lcdVBoostLevel1 = LCD_DISPCTRL_VBLEV_LEVEL1, /**< Voltage boost LEVEL1 */ + lcdVBoostLevel2 = LCD_DISPCTRL_VBLEV_LEVEL2, /**< Voltage boost LEVEL2 */ + lcdVBoostLevel3 = LCD_DISPCTRL_VBLEV_LEVEL3, /**< Voltage boost LEVEL3 */ + lcdVBoostLevel4 = LCD_DISPCTRL_VBLEV_LEVEL4, /**< Voltage boost LEVEL4 */ + lcdVBoostLevel5 = LCD_DISPCTRL_VBLEV_LEVEL5, /**< Voltage boost LEVEL5 */ + lcdVBoostLevel6 = LCD_DISPCTRL_VBLEV_LEVEL6, /**< Voltage boost LEVEL6 */ + lcdVBoostLevel7 = LCD_DISPCTRL_VBLEV_LEVEL7 /**< Voltage boost LEVEL7 */ +} LCD_VBoostLevel_TypeDef; + +/** Frame Counter Clock Prescaler, FC-CLK = FrameRate (Hz) / this factor */ +typedef enum +{ + /** Prescale Div 1 */ + lcdFCPrescDiv1 = LCD_BACTRL_FCPRESC_DIV1, + /** Prescale Div 2 */ + lcdFCPrescDiv2 = LCD_BACTRL_FCPRESC_DIV2, + /** Prescale Div 4 */ + lcdFCPrescDiv4 = LCD_BACTRL_FCPRESC_DIV4, + /** Prescale Div 8 */ + lcdFCPrescDiv8 = LCD_BACTRL_FCPRESC_DIV8 +} LCD_FCPreScale_TypeDef; + +/** Segment selection */ +typedef enum +{ + /** Select segment lines 0 to 3 */ + lcdSegment0_3 = (1 << 0), + /** Select segment lines 4 to 7 */ + lcdSegment4_7 = (1 << 1), + /** Select segment lines 8 to 11 */ + lcdSegment8_11 = (1 << 2), + /** Select segment lines 12 to 15 */ + lcdSegment12_15 = (1 << 3), + /** Select segment lines 16 to 19 */ + lcdSegment16_19 = (1 << 4), + /** Select segment lines 20 to 23 */ + lcdSegment20_23 = (1 << 5), +#if defined(_LCD_SEGD0L_MASK) && (_LCD_SEGD0L_MASK == 0x00FFFFFFUL) + /** Select all segment lines */ + lcdSegmentAll = (0x003f) +#elif defined(_LCD_SEGD0H_MASK) && (_LCD_SEGD0H_MASK == 0x000000FFUL) + /** Select segment lines 24 to 27 */ + lcdSegment24_27 = (1 << 6), + /** Select segment lines 28 to 31 */ + lcdSegment28_31 = (1 << 7), + /** Select segment lines 32 to 35 */ + lcdSegment32_35 = (1 << 8), + /** Select segment lines 36 to 39 */ + lcdSegment36_39 = (1 << 9), + /** Select all segment lines */ + lcdSegmentAll = (0x03ff) +#endif +} LCD_SegmentRange_TypeDef; + +/** Update Data Control */ +typedef enum +{ + /** Regular update, data transfer done immediately */ + lcdUpdateCtrlRegular = LCD_CTRL_UDCTRL_REGULAR, + /** Data transfer done at Frame Counter event */ + lcdUpdateCtrlFCEvent = LCD_CTRL_UDCTRL_FCEVENT, + /** Data transfer done at Frame Start */ + lcdUpdateCtrlFrameStart = LCD_CTRL_UDCTRL_FRAMESTART +} LCD_UpdateCtrl_TypeDef; + +/** Animation Shift operation; none, left or right */ +typedef enum +{ + /** No shift */ + lcdAnimShiftNone = _LCD_BACTRL_AREGASC_NOSHIFT, + /** Shift segment bits left */ + lcdAnimShiftLeft = _LCD_BACTRL_AREGASC_SHIFTLEFT, + /** Shift segment bits right */ + lcdAnimShiftRight = _LCD_BACTRL_AREGASC_SHIFTRIGHT +} LCD_AnimShift_TypeDef; + +/** Animation Logic Control, how AReg and BReg should be combined */ +typedef enum +{ + /** Use bitwise logic AND to mix animation register A (AREGA) and B (AREGB) */ + lcdAnimLogicAnd = LCD_BACTRL_ALOGSEL_AND, + /** Use bitwise logic OR to mix animation register A (AREGA) and B (AREGB) */ + lcdAnimLogicOr = LCD_BACTRL_ALOGSEL_OR +} LCD_AnimLogic_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** LCD Animation Configuration */ +typedef struct +{ + /** Enable Animation at end of initialization */ + bool enable; + /** Initial Animation Register A Value */ + uint32_t AReg; + /** Shift operation of Animation Register A */ + LCD_AnimShift_TypeDef AShift; + /** Initial Animation Register B Value */ + uint32_t BReg; + /** Shift operation of Animation Register B */ + LCD_AnimShift_TypeDef BShift; + /** A and B Logical Operation to use for mixing and outputting resulting segments */ + LCD_AnimLogic_TypeDef animLogic; +#if defined(LCD_BACTRL_ALOC) + /** Number of first segment to animate. Options are 0 or 8 for Giant/Leopard. End is startSeg+7 */ + int startSeg; +#endif +} LCD_AnimInit_TypeDef; + +/** LCD Frame Control Initialization */ +typedef struct +{ + /** Enable at end */ + bool enable; + /** Frame Counter top value */ + uint32_t top; + /** Frame Counter clock prescaler */ + LCD_FCPreScale_TypeDef prescale; +} LCD_FrameCountInit_TypeDef; + +/** LCD Controller Initialization structure */ +typedef struct +{ + /** Enable controller at end of initialization */ + bool enable; + /** Mux configuration */ + LCD_Mux_TypeDef mux; + /** Bias configuration */ + LCD_Bias_TypeDef bias; + /** Wave configuration */ + LCD_Wave_TypeDef wave; + /** VLCD Select */ + LCD_VLCDSel_TypeDef vlcd; + /** Contrast Configuration */ + LCD_ConConf_TypeDef contrast; +} LCD_Init_TypeDef; + +/** Default config for LCD init structure, enables 160 segments */ +#define LCD_INIT_DEFAULT \ +{ \ + true, \ + lcdMuxQuadruplex, \ + lcdBiasOneThird, \ + lcdWaveLowPower, \ + lcdVLCDSelVDD, \ + lcdConConfVLCD \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void LCD_Init(const LCD_Init_TypeDef *lcdInit); +void LCD_VLCDSelect(LCD_VLCDSel_TypeDef vlcd); +void LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud); +void LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef *fcInit); +void LCD_AnimInit(const LCD_AnimInit_TypeDef *animInit); + +void LCD_SegmentRangeEnable(LCD_SegmentRange_TypeDef segment, bool enable); +void LCD_SegmentSet(int com, int bit, bool enable); +void LCD_SegmentSetLow(int com, uint32_t mask, uint32_t bits); +#if defined(_LCD_SEGD0H_MASK) +void LCD_SegmentSetHigh(int com, uint32_t mask, uint32_t bits); +#endif +void LCD_ContrastSet(int level); +void LCD_VBoostSet(LCD_VBoostLevel_TypeDef vboost); + +#if defined(LCD_CTRL_DSC) +void LCD_BiasSegmentSet(int segment, int biasLevel); +void LCD_BiasComSet(int com, int biasLevel); +#endif + +/***************************************************************************//** + * @brief + * Enable or disable LCD controller + * + * @param[in] enable + * If true, enables LCD controller with current configuration, if false + * disables LCD controller. CMU clock for LCD must be enabled for correct + * operation. + ******************************************************************************/ +__STATIC_INLINE void LCD_Enable(bool enable) +{ + if (enable) + { + LCD->CTRL |= LCD_CTRL_EN; + } + else + { + LCD->CTRL &= ~LCD_CTRL_EN; + } +} + + +/***************************************************************************//** + * @brief + * Enables or disables LCD Animation feature + * + * @param[in] enable + * Boolean true enables animation, false disables animation + ******************************************************************************/ +__STATIC_INLINE void LCD_AnimEnable(bool enable) +{ + if (enable) + { + LCD->BACTRL |= LCD_BACTRL_AEN; + } + else + { + LCD->BACTRL &= ~LCD_BACTRL_AEN; + } +} + + +/***************************************************************************//** + * @brief + * Enables or disables LCD blink + * + * @param[in] enable + * Boolean true enables blink, false disables blink + ******************************************************************************/ +__STATIC_INLINE void LCD_BlinkEnable(bool enable) +{ + if (enable) + { + LCD->BACTRL |= LCD_BACTRL_BLINKEN; + } + else + { + LCD->BACTRL &= ~LCD_BACTRL_BLINKEN; + } +} + + +/***************************************************************************//** + * @brief + * Disables all segments, while keeping segment state + * + * @param[in] enable + * Boolean true clears all segments, boolean false restores all segment lines + ******************************************************************************/ +__STATIC_INLINE void LCD_BlankEnable(bool enable) +{ + if (enable) + { + LCD->BACTRL |= LCD_BACTRL_BLANK; + } + else + { + LCD->BACTRL &= ~LCD_BACTRL_BLANK; + } +} + + +/***************************************************************************//** + * @brief + * Enables or disables LCD Frame Control + * + * @param[in] enable + * Boolean true enables frame counter, false disables frame counter + ******************************************************************************/ +__STATIC_INLINE void LCD_FrameCountEnable(bool enable) +{ + if (enable) + { + LCD->BACTRL |= LCD_BACTRL_FCEN; + } + else + { + LCD->BACTRL &= ~LCD_BACTRL_FCEN; + } +} + + +/***************************************************************************//** + * @brief + * Returns current animation state + * + * @return + * Animation state, in range 0-15 + ******************************************************************************/ +__STATIC_INLINE int LCD_AnimState(void) +{ + return (int)(LCD->STATUS & _LCD_STATUS_ASTATE_MASK) >> _LCD_STATUS_ASTATE_SHIFT; +} + + +/***************************************************************************//** + * @brief + * Returns current blink state + * + * @return + * Return value is 1 if segments are enabled, 0 if disabled + ******************************************************************************/ +__STATIC_INLINE int LCD_BlinkState(void) +{ + return (int)(LCD->STATUS & _LCD_STATUS_BLINK_MASK) >> _LCD_STATUS_BLINK_SHIFT; +} + + +/***************************************************************************//** + * @brief + * When set, LCD registers will not be updated until cleared, + * + * @param[in] enable + * When enable is true, update is stopped, when false all registers are + * updated + ******************************************************************************/ +__STATIC_INLINE void LCD_FreezeEnable(bool enable) +{ + if (enable) + { + LCD->FREEZE = LCD_FREEZE_REGFREEZE_FREEZE; + } + else + { + LCD->FREEZE = LCD_FREEZE_REGFREEZE_UPDATE; + } +} + + +/***************************************************************************//** + * @brief + * Returns SYNCBUSY bits, indicating which registers have pending updates + * + * @return + * Bit fields for LCD registers which have pending updates + ******************************************************************************/ +__STATIC_INLINE uint32_t LCD_SyncBusyGet(void) +{ + return LCD->SYNCBUSY; +} + + +/***************************************************************************//** + * @brief + * Polls LCD SYNCBUSY flags, until flag has been cleared + * + * @param[in] flags + * Bit fields for LCD registers that shall be updated before we continue + ******************************************************************************/ +__STATIC_INLINE void LCD_SyncBusyDelay(uint32_t flags) +{ + while (LCD->SYNCBUSY & flags) + ; +} + + +/***************************************************************************//** + * @brief + * Get pending LCD interrupt flags + * + * @return + * Pending LCD interrupt sources. Returns a set of interrupt flags OR-ed + * together for multiple interrupt sources in the LCD module (LCD_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LCD_IntGet(void) +{ + return LCD->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending LCD interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending and enabled LCD interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in LCD_IEN_nnn + * register (LCD_IEN_nnn) and + * - the bitwise OR combination of valid interrupt flags of the LCD module + * (LCD_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LCD_IntGetEnabled(void) +{ + uint32_t ien; + + /* Store LCD->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = LCD->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return LCD->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending LCD interrupts from SW. + * + * @param[in] flags + * LCD interrupt sources to set to pending. Use a set of interrupt flags + * OR-ed together to set multiple interrupt sources for the LCD module + * (LCD_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void LCD_IntSet(uint32_t flags) +{ + LCD->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Enable LCD interrupts + * + * @param[in] flags + * LCD interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to set multiple interrupt sources for the LCD module + * (LCD_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void LCD_IntEnable(uint32_t flags) +{ + LCD->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Disable LCD interrupts + * + * @param[in] flags + * LCD interrupt sources to disable. Use a set of interrupt flags OR-ed + * together to disable multiple interrupt sources for the LCD module + * (LCD_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void LCD_IntDisable(uint32_t flags) +{ + LCD->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Clear one or more interrupt flags + * + * @param[in] flags + * LCD interrupt sources to clear. Use a set of interrupt flags OR-ed + * together to clear multiple interrupt sources for the LCD module + * (LCD_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void LCD_IntClear(uint32_t flags) +{ + LCD->IFC = flags; +} + + +#if defined(LCD_CTRL_DSC) +/***************************************************************************//** + * @brief + * Enable or disable LCD Direct Segment Control + * + * @param[in] enable + * If true, enables LCD controller Direct Segment Control + * Segment and COM line bias levels needs to be set explicitly with the + * LCD_BiasSegmentSet() and LCD_BiasComSet() function calls. + ******************************************************************************/ +__STATIC_INLINE void LCD_DSCEnable(bool enable) +{ + if (enable) + { + LCD->CTRL |= LCD_CTRL_DSC; + } + else + { + LCD->CTRL &= ~LCD_CTRL_DSC; + } +} +#endif + +/** @} (end addtogroup LCD) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(LCD_COUNT) && (LCD_COUNT > 0) */ + +#endif /* __SILICON_LABS_EM_LCD_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_ldma.h b/cpu/efm32_common/emlib/inc/em_ldma.h new file mode 100644 index 0000000000000..b1cedb03fc7e6 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_ldma.h @@ -0,0 +1,1330 @@ +/***************************************************************************//** + * @file em_ldma.h + * @brief Direct memory access (LDMA) API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_LDMA_H__ +#define __SILICON_LABS_EM_LDMA_H__ + +#include "em_device.h" + +#if defined( LDMA_PRESENT ) && ( LDMA_COUNT == 1 ) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LDMA + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** + * This value controls the number of unit data transfers per arbitration + * cycle, providing a means to balance DMA channels' load on the controller. + */ +typedef enum +{ + ldmaCtrlBlockSizeUnit1 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT1, /**< One transfer per arbitration. */ + ldmaCtrlBlockSizeUnit2 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT2, /**< Two transfers per arbitration. */ + ldmaCtrlBlockSizeUnit3 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT3, /**< Three transfers per arbitration. */ + ldmaCtrlBlockSizeUnit4 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT4, /**< Four transfers per arbitration. */ + ldmaCtrlBlockSizeUnit6 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT6, /**< Six transfers per arbitration. */ + ldmaCtrlBlockSizeUnit8 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT8, /**< Eight transfers per arbitration. */ + ldmaCtrlBlockSizeUnit16 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT16, /**< 16 transfers per arbitration. */ + ldmaCtrlBlockSizeUnit32 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT32, /**< 32 transfers per arbitration. */ + ldmaCtrlBlockSizeUnit64 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT64, /**< 64 transfers per arbitration. */ + ldmaCtrlBlockSizeUnit128 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT128, /**< 128 transfers per arbitration. */ + ldmaCtrlBlockSizeUnit256 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT256, /**< 256 transfers per arbitration. */ + ldmaCtrlBlockSizeUnit512 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT512, /**< 512 transfers per arbitration. */ + ldmaCtrlBlockSizeUnit1024 = _LDMA_CH_CTRL_BLOCKSIZE_UNIT1024, /**< 1024 transfers per arbitration. */ + ldmaCtrlBlockSizeAll = _LDMA_CH_CTRL_BLOCKSIZE_ALL /**< Lock arbitration during transfer. */ +} LDMA_CtrlBlockSize_t; + +/** DMA structure type. */ +typedef enum +{ + ldmaCtrlStructTypeXfer = _LDMA_CH_CTRL_STRUCTTYPE_TRANSFER, /**< TRANSFER transfer type. */ + ldmaCtrlStructTypeSync = _LDMA_CH_CTRL_STRUCTTYPE_SYNCHRONIZE, /**< SYNCHRONIZE transfer type. */ + ldmaCtrlStructTypeWrite = _LDMA_CH_CTRL_STRUCTTYPE_WRITE /**< WRITE transfer type. */ +} LDMA_CtrlStructType_t; + +/** DMA transfer block or cycle selector. */ +typedef enum +{ + ldmaCtrlReqModeBlock = _LDMA_CH_CTRL_REQMODE_BLOCK, /**< Each DMA request trigger transfer of one block. */ + ldmaCtrlReqModeAll = _LDMA_CH_CTRL_REQMODE_ALL /**< A DMA request trigger transfer of a complete cycle. */ +} LDMA_CtrlReqMode_t; + +/** Source address increment unit size. */ +typedef enum +{ + ldmaCtrlSrcIncOne = _LDMA_CH_CTRL_SRCINC_ONE, /**< Increment source address by one unit data size. */ + ldmaCtrlSrcIncTwo = _LDMA_CH_CTRL_SRCINC_TWO, /**< Increment source address by two unit data sizes. */ + ldmaCtrlSrcIncFour = _LDMA_CH_CTRL_SRCINC_FOUR, /**< Increment source address by four unit data sizes. */ + ldmaCtrlSrcIncNone = _LDMA_CH_CTRL_SRCINC_NONE /**< Do not increment the source address. */ +} LDMA_CtrlSrcInc_t; + +/** DMA transfer unit size. */ +typedef enum +{ + ldmaCtrlSizeByte = _LDMA_CH_CTRL_SIZE_BYTE, /**< Each unit transfer is a byte. */ + ldmaCtrlSizeHalf = _LDMA_CH_CTRL_SIZE_HALFWORD, /**< Each unit transfer is a half-word. */ + ldmaCtrlSizeWord = _LDMA_CH_CTRL_SIZE_WORD /**< Each unit transfer is a word. */ +} LDMA_CtrlSize_t; + +/** Destination address increment unit size. */ +typedef enum +{ + ldmaCtrlDstIncOne = _LDMA_CH_CTRL_DSTINC_ONE, /**< Increment destination address by one unit data size. */ + ldmaCtrlDstIncTwo = _LDMA_CH_CTRL_DSTINC_TWO, /**< Increment destination address by two unit data sizes. */ + ldmaCtrlDstIncFour = _LDMA_CH_CTRL_DSTINC_FOUR, /**< Increment destination address by four unit data sizes. */ + ldmaCtrlDstIncNone = _LDMA_CH_CTRL_DSTINC_NONE /**< Do not increment the destination address. */ +} LDMA_CtrlDstInc_t; + +/** Source addressing mode. */ +typedef enum +{ + ldmaCtrlSrcAddrModeAbs = _LDMA_CH_CTRL_SRCMODE_ABSOLUTE, /**< Address fetched from a linked structure is absolute. */ + ldmaCtrlSrcAddrModeRel = _LDMA_CH_CTRL_SRCMODE_RELATIVE /**< Address fetched from a linked structure is relative. */ +} LDMA_CtrlSrcAddrMode_t; + +/** Destination addressing mode. */ +typedef enum +{ + ldmaCtrlDstAddrModeAbs = _LDMA_CH_CTRL_DSTMODE_ABSOLUTE, /**< Address fetched from a linked structure is absolute. */ + ldmaCtrlDstAddrModeRel = _LDMA_CH_CTRL_DSTMODE_RELATIVE /**< Address fetched from a linked structure is relative. */ +} LDMA_CtrlDstAddrMode_t; + +/** DMA linkload address mode. */ +typedef enum +{ + ldmaLinkModeAbs = _LDMA_CH_LINK_LINKMODE_ABSOLUTE, /**< Link address is an absolute address value. */ + ldmaLinkModeRel = _LDMA_CH_LINK_LINKMODE_RELATIVE /**< Link address is a two's complement releative address. */ +} LDMA_LinkMode_t; + +/** Insert extra arbitration slots to increase channel arbitration priority. */ +typedef enum +{ + ldmaCfgArbSlotsAs1 = _LDMA_CH_CFG_ARBSLOTS_ONE, /**< One arbitration slot selected. */ + ldmaCfgArbSlotsAs2 = _LDMA_CH_CFG_ARBSLOTS_TWO, /**< Two arbitration slots selected. */ + ldmaCfgArbSlotsAs4 = _LDMA_CH_CFG_ARBSLOTS_FOUR, /**< Four arbitration slots selected. */ + ldmaCfgArbSlotsAs8 = _LDMA_CH_CFG_ARBSLOTS_EIGHT /**< Eight arbitration slots selected. */ +} LDMA_CfgArbSlots_t; + +/** Source address increment sign. */ +typedef enum +{ + ldmaCfgSrcIncSignPos = _LDMA_CH_CFG_SRCINCSIGN_POSITIVE, /**< Increment source address. */ + ldmaCfgSrcIncSignNeg = _LDMA_CH_CFG_SRCINCSIGN_NEGATIVE /**< Decrement source address. */ +} LDMA_CfgSrcIncSign_t; + +/** Destination address increment sign. */ +typedef enum +{ + ldmaCfgDstIncSignPos = _LDMA_CH_CFG_DSTINCSIGN_POSITIVE, /**< Increment destination address. */ + ldmaCfgDstIncSignNeg = _LDMA_CH_CFG_DSTINCSIGN_NEGATIVE /**< Decrement destination address. */ +} LDMA_CfgDstIncSign_t; + +/** Peripherals that can trigger LDMA transfers. */ +typedef enum +{ + ldmaPeripheralSignal_NONE = LDMA_CH_REQSEL_SOURCESEL_NONE, ///< No peripheral selected for DMA triggering. + #if defined( LDMA_CH_REQSEL_SIGSEL_ADC0SCAN ) + ldmaPeripheralSignal_ADC0_SCAN = LDMA_CH_REQSEL_SIGSEL_ADC0SCAN | LDMA_CH_REQSEL_SOURCESEL_ADC0, ///< Trig on ADC0_SCAN. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_ADC0SINGLE ) + ldmaPeripheralSignal_ADC0_SINGLE = LDMA_CH_REQSEL_SIGSEL_ADC0SINGLE | LDMA_CH_REQSEL_SOURCESEL_ADC0, ///< Trig on ADC0_SINGLE. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_AGCRSSI ) + ldmaPeripheralSignal_AGC_RSSI = LDMA_CH_REQSEL_SIGSEL_AGCRSSI | LDMA_CH_REQSEL_SOURCESEL_AGC, ///< Trig on AGC_RSSI. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_CRYPTODATA0RD ) + ldmaPeripheralSignal_CRYPTO_DATA0RD = LDMA_CH_REQSEL_SIGSEL_CRYPTODATA0RD | LDMA_CH_REQSEL_SOURCESEL_CRYPTO, ///< Trig on CRYPTO_DATA0RD. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_CRYPTODATA0WR ) + ldmaPeripheralSignal_CRYPTO_DATA0WR = LDMA_CH_REQSEL_SIGSEL_CRYPTODATA0WR | LDMA_CH_REQSEL_SOURCESEL_CRYPTO, ///< Trig on CRYPTO_DATA0WR. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_CRYPTODATA0XWR ) + ldmaPeripheralSignal_CRYPTO_DATA0XWR = LDMA_CH_REQSEL_SIGSEL_CRYPTODATA0XWR | LDMA_CH_REQSEL_SOURCESEL_CRYPTO, ///< Trig on CRYPTO_DATA0XWR. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_CRYPTODATA1RD ) + ldmaPeripheralSignal_CRYPTO_DATA1RD = LDMA_CH_REQSEL_SIGSEL_CRYPTODATA1RD | LDMA_CH_REQSEL_SOURCESEL_CRYPTO, ///< Trig on CRYPTO_DATA1RD. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_CRYPTODATA1WR ) + ldmaPeripheralSignal_CRYPTO_DATA1WR = LDMA_CH_REQSEL_SIGSEL_CRYPTODATA1WR | LDMA_CH_REQSEL_SOURCESEL_CRYPTO, ///< Trig on CRYPTO_DATA1WR. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_I2C0RXDATAV ) + ldmaPeripheralSignal_I2C0_RXDATAV = LDMA_CH_REQSEL_SIGSEL_I2C0RXDATAV | LDMA_CH_REQSEL_SOURCESEL_I2C0, ///< Trig on I2C0_RXDATAV. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_I2C0TXBL ) + ldmaPeripheralSignal_I2C0_TXBL = LDMA_CH_REQSEL_SIGSEL_I2C0TXBL | LDMA_CH_REQSEL_SOURCESEL_I2C0, ///< Trig on I2C0_TXBL. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_LEUART0RXDATAV ) + ldmaPeripheralSignal_LEUART0_RXDATAV = LDMA_CH_REQSEL_SIGSEL_LEUART0RXDATAV | LDMA_CH_REQSEL_SOURCESEL_LEUART0, ///< Trig on LEUART0_RXDATAV. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_LEUART0TXBL ) + ldmaPeripheralSignal_LEUART0_TXBL = LDMA_CH_REQSEL_SIGSEL_LEUART0TXBL | LDMA_CH_REQSEL_SOURCESEL_LEUART0, ///< Trig on LEUART0_TXBL. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_LEUART0TXEMPTY ) + ldmaPeripheralSignal_LEUART0_TXEMPTY = LDMA_CH_REQSEL_SIGSEL_LEUART0TXEMPTY | LDMA_CH_REQSEL_SOURCESEL_LEUART0, ///< Trig on LEUART0_TXEMPTY. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_MODEMDEBUG ) + ldmaPeripheralSignal_MODEM_DEBUG = LDMA_CH_REQSEL_SIGSEL_MODEMDEBUG | LDMA_CH_REQSEL_SOURCESEL_MODEM, ///< Trig on MODEM_DEBUG. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_MSCWDATA ) + ldmaPeripheralSignal_MSC_WDATA = LDMA_CH_REQSEL_SIGSEL_MSCWDATA | LDMA_CH_REQSEL_SOURCESEL_MSC, ///< Trig on MSC_WDATA. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERBOF ) + ldmaPeripheralSignal_PROTIMER_BOF = LDMA_CH_REQSEL_SIGSEL_PROTIMERBOF | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_BOF. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERCC0 ) + ldmaPeripheralSignal_PROTIMER_CC0 = LDMA_CH_REQSEL_SIGSEL_PROTIMERCC0 | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_CC0. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERCC1 ) + ldmaPeripheralSignal_PROTIMER_CC1 = LDMA_CH_REQSEL_SIGSEL_PROTIMERCC1 | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_CC1. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERCC2 ) + ldmaPeripheralSignal_PROTIMER_CC2 = LDMA_CH_REQSEL_SIGSEL_PROTIMERCC2 | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_CC2. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERCC3 ) + ldmaPeripheralSignal_PROTIMER_CC3 = LDMA_CH_REQSEL_SIGSEL_PROTIMERCC3 | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_CC3. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERCC4 ) + ldmaPeripheralSignal_PROTIMER_CC4 = LDMA_CH_REQSEL_SIGSEL_PROTIMERCC4 | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_CC4. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERPOF ) + ldmaPeripheralSignal_PROTIMER_POF = LDMA_CH_REQSEL_SIGSEL_PROTIMERPOF | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_POF. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PROTIMERWOF ) + ldmaPeripheralSignal_PROTIMER_WOF = LDMA_CH_REQSEL_SIGSEL_PROTIMERWOF | LDMA_CH_REQSEL_SOURCESEL_PROTIMER, ///< Trig on PROTIMER_WOF. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PRSREQ0 ) + ldmaPeripheralSignal_PRS_REQ0 = LDMA_CH_REQSEL_SIGSEL_PRSREQ0 | LDMA_CH_REQSEL_SOURCESEL_PRS, ///< Trig on PRS_REQ0. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_PRSREQ1 ) + ldmaPeripheralSignal_PRS_REQ1 = LDMA_CH_REQSEL_SIGSEL_PRSREQ1 | LDMA_CH_REQSEL_SOURCESEL_PRS, ///< Trig on PRS_REQ1. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER0CC0 ) + ldmaPeripheralSignal_TIMER0_CC0 = LDMA_CH_REQSEL_SIGSEL_TIMER0CC0 | LDMA_CH_REQSEL_SOURCESEL_TIMER0, ///< Trig on TIMER0_CC0. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER0CC1 ) + ldmaPeripheralSignal_TIMER0_CC1 = LDMA_CH_REQSEL_SIGSEL_TIMER0CC1 | LDMA_CH_REQSEL_SOURCESEL_TIMER0, ///< Trig on TIMER0_CC1. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER0CC2 ) + ldmaPeripheralSignal_TIMER0_CC2 = LDMA_CH_REQSEL_SIGSEL_TIMER0CC2 | LDMA_CH_REQSEL_SOURCESEL_TIMER0, ///< Trig on TIMER0_CC2. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER0UFOF ) + ldmaPeripheralSignal_TIMER0_UFOF = LDMA_CH_REQSEL_SIGSEL_TIMER0UFOF | LDMA_CH_REQSEL_SOURCESEL_TIMER0, ///< Trig on TIMER0_UFOF. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER1CC0 ) + ldmaPeripheralSignal_TIMER1_CC0 = LDMA_CH_REQSEL_SIGSEL_TIMER1CC0 | LDMA_CH_REQSEL_SOURCESEL_TIMER1, ///< Trig on TIMER1_CC0. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER1CC1 ) + ldmaPeripheralSignal_TIMER1_CC1 = LDMA_CH_REQSEL_SIGSEL_TIMER1CC1 | LDMA_CH_REQSEL_SOURCESEL_TIMER1, ///< Trig on TIMER1_CC1. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER1CC2 ) + ldmaPeripheralSignal_TIMER1_CC2 = LDMA_CH_REQSEL_SIGSEL_TIMER1CC2 | LDMA_CH_REQSEL_SOURCESEL_TIMER1, ///< Trig on TIMER1_CC2. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER1CC3 ) + ldmaPeripheralSignal_TIMER1_CC3 = LDMA_CH_REQSEL_SIGSEL_TIMER1CC3 | LDMA_CH_REQSEL_SOURCESEL_TIMER1, ///< Trig on TIMER1_CC3. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_TIMER1UFOF ) + ldmaPeripheralSignal_TIMER1_UFOF = LDMA_CH_REQSEL_SIGSEL_TIMER1UFOF | LDMA_CH_REQSEL_SOURCESEL_TIMER1, ///< Trig on TIMER1_UFOF. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART0RXDATAV ) + ldmaPeripheralSignal_USART0_RXDATAV = LDMA_CH_REQSEL_SIGSEL_USART0RXDATAV | LDMA_CH_REQSEL_SOURCESEL_USART0, ///< Trig on USART0_RXDATAV. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART0TXBL ) + ldmaPeripheralSignal_USART0_TXBL = LDMA_CH_REQSEL_SIGSEL_USART0TXBL | LDMA_CH_REQSEL_SOURCESEL_USART0, ///< Trig on USART0_TXBL. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART0TXEMPTY ) + ldmaPeripheralSignal_USART0_TXEMPTY = LDMA_CH_REQSEL_SIGSEL_USART0TXEMPTY | LDMA_CH_REQSEL_SOURCESEL_USART0, ///< Trig on USART0_TXEMPTY. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART1RXDATAV ) + ldmaPeripheralSignal_USART1_RXDATAV = LDMA_CH_REQSEL_SIGSEL_USART1RXDATAV | LDMA_CH_REQSEL_SOURCESEL_USART1, ///< Trig on USART1_RXDATAV. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART1RXDATAVRIGHT ) + ldmaPeripheralSignal_USART1_RXDATAVRIGHT = LDMA_CH_REQSEL_SIGSEL_USART1RXDATAVRIGHT | LDMA_CH_REQSEL_SOURCESEL_USART1, ///< Trig on USART1_RXDATAVRIGHT. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART1TXBL ) + ldmaPeripheralSignal_USART1_TXBL = LDMA_CH_REQSEL_SIGSEL_USART1TXBL | LDMA_CH_REQSEL_SOURCESEL_USART1, ///< Trig on USART1_TXBL. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART1TXBLRIGHT ) + ldmaPeripheralSignal_USART1_TXBLRIGHT = LDMA_CH_REQSEL_SIGSEL_USART1TXBLRIGHT | LDMA_CH_REQSEL_SOURCESEL_USART1, ///< Trig on USART1_TXBLRIGHT. + #endif + #if defined( LDMA_CH_REQSEL_SIGSEL_USART1TXEMPTY ) + ldmaPeripheralSignal_USART1_TXEMPTY = LDMA_CH_REQSEL_SIGSEL_USART1TXEMPTY | LDMA_CH_REQSEL_SOURCESEL_USART1 ///< Trig on USART1_TXEMPTY. + #endif +} LDMA_PeripheralSignal_t; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** + * @brief + * DMA descriptor. + * @details + * The LDMA DMA controller supports three different DMA descriptors. Each + * consist of four WORD's which map directly onto hw control registers for a + * given DMA channel. The three descriptor types are XFER, SYNC and WRI. + * Refer to the reference manual for further information. + */ +typedef union +{ + /** + * TRANSFER DMA descriptor, this is the only descriptor type which can be + * used to start a DMA transfer. + */ + struct + { + uint32_t structType : 2; /**< Set to 0 to select XFER descriptor type. */ + uint32_t reserved0 : 1; + uint32_t structReq : 1; /**< DMA transfer trigger during LINKLOAD. */ + uint32_t xferCnt : 11; /**< Transfer count minus one. */ + uint32_t byteSwap : 1; /**< Enable byte swapping transfers. */ + uint32_t blockSize : 4; /**< Number of unit transfers per arb. cycle. */ + uint32_t doneIfs : 1; /**< Generate interrupt when done. */ + uint32_t reqMode : 1; /**< Block or cycle transfer selector. */ + uint32_t decLoopCnt : 1; /**< Enable looped transfers. */ + uint32_t ignoreSrec : 1; /**< Ignore single requests. */ + uint32_t srcInc : 2; /**< Source address increment unit size. */ + uint32_t size : 2; /**< DMA transfer unit size. */ + uint32_t dstInc : 2; /**< Destination address increment unit size. */ + uint32_t srcAddrMode: 1; /**< Source addressing mode. */ + uint32_t dstAddrMode: 1; /**< Destination addressing mode. */ + + uint32_t srcAddr; /**< DMA source address. */ + uint32_t dstAddr; /**< DMA destination address. */ + + uint32_t linkMode : 1; /**< Select absolute or relative link address.*/ + uint32_t link : 1; /**< Enable LINKLOAD when transfer is done. */ + int32_t linkAddr : 30; /**< Address of next (linked) descriptor. */ + } xfer; + + /** SYNCHRONIZE DMA descriptor, used for intra channel transfer + * syncronization. + */ + struct + { + uint32_t structType : 2; /**< Set to 1 to select SYNC descriptor type. */ + uint32_t reserved0 : 1; + uint32_t structReq : 1; /**< DMA transfer trigger during LINKLOAD. */ + uint32_t xferCnt : 11; /**< Transfer count minus one. */ + uint32_t byteSwap : 1; /**< Enable byte swapping transfers. */ + uint32_t blockSize : 4; /**< Number of unit transfers per arb. cycle. */ + uint32_t doneIfs : 1; /**< Generate interrupt when done. */ + uint32_t reqMode : 1; /**< Block or cycle transfer selector. */ + uint32_t decLoopCnt : 1; /**< Enable looped transfers. */ + uint32_t ignoreSrec : 1; /**< Ignore single requests. */ + uint32_t srcInc : 2; /**< Source address increment unit size. */ + uint32_t size : 2; /**< DMA transfer unit size. */ + uint32_t dstInc : 2; /**< Destination address increment unit size. */ + uint32_t srcAddrMode: 1; /**< Source addressing mode. */ + uint32_t dstAddrMode: 1; /**< Destination addressing mode. */ + + uint32_t syncSet : 8; /**< Set bits in LDMA_CTRL.SYNCTRIG register. */ + uint32_t syncClr : 8; /**< Clear bits in LDMA_CTRL.SYNCTRIG register*/ + uint32_t reserved3 : 16; + uint32_t matchVal : 8; /**< Sync trig match value. */ + uint32_t matchEn : 8; /**< Sync trig match enable. */ + uint32_t reserved4 : 16; + + uint32_t linkMode : 1; /**< Select absolute or relative link address.*/ + uint32_t link : 1; /**< Enable LINKLOAD when transfer is done. */ + int32_t linkAddr : 30; /**< Address of next (linked) descriptor. */ + } sync; + + /** WRITE DMA descriptor, used for write immediate operations. */ + struct + { + uint32_t structType : 2; /**< Set to 2 to select WRITE descriptor type.*/ + uint32_t reserved0 : 1; + uint32_t structReq : 1; /**< DMA transfer trigger during LINKLOAD. */ + uint32_t xferCnt : 11; /**< Transfer count minus one. */ + uint32_t byteSwap : 1; /**< Enable byte swapping transfers. */ + uint32_t blockSize : 4; /**< Number of unit transfers per arb. cycle. */ + uint32_t doneIfs : 1; /**< Generate interrupt when done. */ + uint32_t reqMode : 1; /**< Block or cycle transfer selector. */ + uint32_t decLoopCnt : 1; /**< Enable looped transfers. */ + uint32_t ignoreSrec : 1; /**< Ignore single requests. */ + uint32_t srcInc : 2; /**< Source address increment unit size. */ + uint32_t size : 2; /**< DMA transfer unit size. */ + uint32_t dstInc : 2; /**< Destination address increment unit size. */ + uint32_t srcAddrMode: 1; /**< Source addressing mode. */ + uint32_t dstAddrMode: 1; /**< Destination addressing mode. */ + + uint32_t immVal; /**< Data to be written at dstAddr. */ + uint32_t dstAddr; /**< DMA write destination address. */ + + uint32_t linkMode : 1; /**< Select absolute or relative link address.*/ + uint32_t link : 1; /**< Enable LINKLOAD when transfer is done. */ + int32_t linkAddr : 30; /**< Address of next (linked) descriptor. */ + } wri; +} LDMA_Descriptor_t; + +/** @brief LDMA initialization configuration structure. */ +typedef struct +{ + uint8_t ldmaInitCtrlNumFixed; /**< Arbitration mode separator.*/ + uint8_t ldmaInitCtrlSyncPrsClrEn; /**< PRS Synctrig clear enable. */ + uint8_t ldmaInitCtrlSyncPrsSetEn; /**< PRS Synctrig set enable. */ + uint8_t ldmaInitIrqPriority; /**< LDMA IRQ priority (0..7). */ +} LDMA_Init_t; + +/** + * @brief + * DMA transfer configuration structure. + * @details + * This struct configures all aspects of a DMA transfer. + */ +typedef struct +{ + uint32_t ldmaReqSel; /**< Selects DMA trigger source. */ + uint8_t ldmaCtrlSyncPrsClrOff; /**< PRS Synctrig clear enables to clear. */ + uint8_t ldmaCtrlSyncPrsClrOn; /**< PRS Synctrig clear enables to set. */ + uint8_t ldmaCtrlSyncPrsSetOff; /**< PRS Synctrig set enables to clear. */ + uint8_t ldmaCtrlSyncPrsSetOn; /**< PRS Synctrig set enables to set. */ + bool ldmaReqDis; /**< Mask the PRS trigger input. */ + bool ldmaDbgHalt; /**< Dis. DMA trig when cpu is halted. */ + uint8_t ldmaCfgArbSlots; /**< Arbitration slot number. */ + uint8_t ldmaCfgSrcIncSign; /**< Source addr. increment sign. */ + uint8_t ldmaCfgDstIncSign; /**< Dest. addr. increment sign. */ + uint8_t ldmaLoopCnt; /**< Counter for looped transfers. */ +} LDMA_TransferCfg_t; + + +/******************************************************************************* + ************************** STRUCT INITIALIZERS **************************** + ******************************************************************************/ + + +/** @brief Default DMA initialization structure. */ +#define LDMA_INIT_DEFAULT \ +{ \ + .ldmaInitCtrlNumFixed = _LDMA_CTRL_NUMFIXED_DEFAULT, /* Fixed priority arbitration. */ \ + .ldmaInitCtrlSyncPrsClrEn = 0, /* No PRS Synctrig clear enable*/ \ + .ldmaInitCtrlSyncPrsSetEn = 0, /* No PRS Synctrig set enable. */ \ + .ldmaInitIrqPriority = 3 /* IRQ priority level 3. */ \ +} + +/** + * @brief + * Generic DMA transfer configuration for memory to memory transfers. + */ +#define LDMA_TRANSFER_CFG_MEMORY() \ +{ \ + 0, 0, 0, 0, 0, \ + false, false, ldmaCfgArbSlotsAs1, \ + ldmaCfgSrcIncSignPos, ldmaCfgDstIncSignPos, 0 \ +} + +/** + * @brief + * Generic DMA transfer configuration for looped memory to memory transfers. + */ +#define LDMA_TRANSFER_CFG_MEMORY_LOOP( loopCnt) \ +{ \ + 0, 0, 0, 0, 0, \ + false, false, ldmaCfgArbSlotsAs1, \ + ldmaCfgSrcIncSignPos, ldmaCfgDstIncSignPos, \ + loopCnt \ +} + +/** + * @brief + * Generic DMA transfer configuration for memory to/from peripheral transfers. + */ +#define LDMA_TRANSFER_CFG_PERIPHERAL( signal ) \ +{ \ + signal, 0, 0, 0, 0, \ + false, false, ldmaCfgArbSlotsAs1, \ + ldmaCfgSrcIncSignPos, ldmaCfgDstIncSignPos, 0 \ +} + +/** + * @brief + * Generic DMA transfer configuration for looped memory to/from peripheral transfers. + */ +#define LDMA_TRANSFER_CFG_PERIPHERAL_LOOP( signal, loopCnt ) \ +{ \ + signal, 0, 0, 0, 0, \ + false, false, ldmaCfgArbSlotsAs1, \ + ldmaCfgSrcIncSignPos, ldmaCfgDstIncSignPos, loopCnt \ +} + +/** + * @brief + * DMA descriptor initializer for single memory to memory word transfer. + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of words to transfer. + */ +#define LDMA_DESCRIPTOR_SINGLE_M2M_WORD( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeWord, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for single memory to memory half-word transfer. + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of half-words to transfer. + */ +#define LDMA_DESCRIPTOR_SINGLE_M2M_HALF( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeHalf, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for single memory to memory byte transfer. + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of bytes to transfer. + */ +#define LDMA_DESCRIPTOR_SINGLE_M2M_BYTE( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for linked memory to memory word transfer. + * + * The link address must be an absolute address. + * @note + * The linkAddr member of the transfer descriptor is not + * initialized. + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of words to transfer. + */ +#define LDMA_DESCRIPTOR_LINKABS_M2M_WORD( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 0, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeWord, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeAbs, \ + .link = 1, \ + .linkAddr = 0 /* Must be set runtime ! */ \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for linked memory to memory half-word transfer. + * + * The link address must be an absolute address. + * @note + * The linkAddr member of the transfer descriptor is not + * initialized. + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of half-words to transfer. + */ +#define LDMA_DESCRIPTOR_LINKABS_M2M_HALF( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 0, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeHalf, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeAbs, \ + .link = 1, \ + .linkAddr = 0 /* Must be set runtime ! */ \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for linked memory to memory byte transfer. + * + * The link address must be an absolute address. + * @note + * The linkAddr member of the transfer descriptor is not + * initialized. + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of bytes to transfer. + */ +#define LDMA_DESCRIPTOR_LINKABS_M2M_BYTE( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 0, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeAbs, \ + .link = 1, \ + .linkAddr = 0 /* Must be set runtime ! */ \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for linked memory to memory word transfer. + * + * The link address is a relative address. + * @note + * The linkAddr member of the transfer descriptor is + * initialized to 4, assuming that the next descriptor immediately follows + * this descriptor (in memory). + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of words to transfer. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_M2M_WORD( src, dest, count, linkjmp ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 0, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeWord, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for linked memory to memory half-word transfer. + * + * The link address is a relative address. + * @note + * The linkAddr member of the transfer descriptor is + * initialized to 4, assuming that the next descriptor immediately follows + * this descriptor (in memory). + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of half-words to transfer. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_M2M_HALF( src, dest, count, linkjmp ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 0, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeHalf, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for linked memory to memory byte transfer. + * + * The link address is a relative address. + * @note + * The linkAddr member of the transfer descriptor is + * initialized to 4, assuming that the next descriptor immediately follows + * this descriptor (in memory). + * @param[in] src Source data address. + * @param[in] dest Destination data address. + * @param[in] count Number of bytes to transfer. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_M2M_BYTE( src, dest, count, linkjmp ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 1, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 0, \ + .reqMode = ldmaCtrlReqModeAll, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for byte transfers from a peripheral to memory. + * @param[in] src Peripheral data source register address. + * @param[in] dest Destination data address. + * @param[in] count Number of bytes to transfer. + */ +#define LDMA_DESCRIPTOR_SINGLE_P2M_BYTE( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 0, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeBlock, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncNone, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for byte transfers from memory to a peripheral + * @param[in] src Source data address. + * @param[in] dest Peripheral data register destination address. + * @param[in] count Number of bytes to transfer. + */ +#define LDMA_DESCRIPTOR_SINGLE_M2P_BYTE( src, dest, count ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 0, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeBlock, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncNone, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for byte transfers from a peripheral to memory. + * @param[in] src Peripheral data source register address. + * @param[in] dest Destination data address. + * @param[in] count Number of bytes to transfer. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_P2M_BYTE( src, dest, count, linkjmp ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 0, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeBlock, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncNone, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncOne, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for byte transfers from memory to a peripheral + * @param[in] src Source data address. + * @param[in] dest Peripheral data register destination address. + * @param[in] count Number of bytes to transfer. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_M2P_BYTE( src, dest, count, linkjmp ) \ +{ \ + .xfer = \ + { \ + .structType = ldmaCtrlStructTypeXfer, \ + .structReq = 0, \ + .xferCnt = ( count ) - 1, \ + .byteSwap = 0, \ + .blockSize = ldmaCtrlBlockSizeUnit1, \ + .doneIfs = 1, \ + .reqMode = ldmaCtrlReqModeBlock, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = ldmaCtrlSrcIncOne, \ + .size = ldmaCtrlSizeByte, \ + .dstInc = ldmaCtrlDstIncNone, \ + .srcAddrMode = ldmaCtrlSrcAddrModeAbs, \ + .dstAddrMode = ldmaCtrlDstAddrModeAbs, \ + .srcAddr = (uint32_t)(src), \ + .dstAddr = (uint32_t)(dest), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for Immediate WRITE transfer + * @param[in] value Immediate value to write. + * @param[in] address Write sddress. + */ +#define LDMA_DESCRIPTOR_SINGLE_WRITE( value, address ) \ +{ \ + .wri = \ + { \ + .structType = ldmaCtrlStructTypeWrite, \ + .structReq = 1, \ + .xferCnt = 0, \ + .byteSwap = 0, \ + .blockSize = 0, \ + .doneIfs = 1, \ + .reqMode = 0, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = 0, \ + .size = 0, \ + .dstInc = 0, \ + .srcAddrMode = 0, \ + .dstAddrMode = 0, \ + .immVal = (value), \ + .dstAddr = (uint32_t)(address), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for Immediate WRITE transfer + * + * The link address must be an absolute address. + * @note + * The linkAddr member of the transfer descriptor is not + * initialized. + * @param[in] value Immediate value to write. + * @param[in] address Write sddress. + */ +#define LDMA_DESCRIPTOR_LINKABS_WRITE( value, address ) \ +{ \ + .wri = \ + { \ + .structType = ldmaCtrlStructTypeWrite, \ + .structReq = 1, \ + .xferCnt = 0, \ + .byteSwap = 0, \ + .blockSize = 0, \ + .doneIfs = 0, \ + .reqMode = 0, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = 0, \ + .size = 0, \ + .dstInc = 0, \ + .srcAddrMode = 0, \ + .dstAddrMode = 0, \ + .immVal = (value), \ + .dstAddr = (uint32_t)(address), \ + .linkMode = ldmaLinkModeAbs, \ + .link = 1, \ + .linkAddr = 0 /* Must be set runtime ! */ \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for Immediate WRITE transfer + * @param[in] value Immediate value to write. + * @param[in] address Write sddress. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_WRITE( value, address, linkjmp ) \ +{ \ + .wri = \ + { \ + .structType = ldmaCtrlStructTypeWrite, \ + .structReq = 1, \ + .xferCnt = 0, \ + .byteSwap = 0, \ + .blockSize = 0, \ + .doneIfs = 0, \ + .reqMode = 0, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = 0, \ + .size = 0, \ + .dstInc = 0, \ + .srcAddrMode = 0, \ + .dstAddrMode = 0, \ + .immVal = (value), \ + .dstAddr = (uint32_t)(address), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for SYNC transfer + * @param[in] set Sync pattern bits to set. + * @param[in] clr Sync pattern bits to clear. + * @param[in] matchValue Sync pattern to match. + * @param[in] matchEnable Sync pattern bits to enable for match. + */ +#define LDMA_DESCRIPTOR_SINGLE_SYNC( set, clr, matchValue, matchEnable ) \ +{ \ + .sync = \ + { \ + .structType = ldmaCtrlStructTypeSync, \ + .structReq = 1, \ + .xferCnt = 0, \ + .byteSwap = 0, \ + .blockSize = 0, \ + .doneIfs = 1, \ + .reqMode = 0, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = 0, \ + .size = 0, \ + .dstInc = 0, \ + .srcAddrMode = 0, \ + .dstAddrMode = 0, \ + .syncSet = (set), \ + .syncClr = (clr), \ + .matchVal = (matchValue), \ + .matchEn = (matchEnable), \ + .linkMode = 0, \ + .link = 0, \ + .linkAddr = 0 \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for SYNC transfer + * + * The link address must be an absolute address. + * @note + * The linkAddr member of the transfer descriptor is not + * initialized. + * @param[in] set Sync pattern bits to set. + * @param[in] clr Sync pattern bits to clear. + * @param[in] matchValue Sync pattern to match. + * @param[in] matchEnable Sync pattern bits to enable for match. + */ +#define LDMA_DESCRIPTOR_LINKABS_SYNC( set, clr, matchValue, matchEnable ) \ +{ \ + .sync = \ + { \ + .structType = ldmaCtrlStructTypeSync, \ + .structReq = 1, \ + .xferCnt = 0, \ + .byteSwap = 0, \ + .blockSize = 0, \ + .doneIfs = 0, \ + .reqMode = 0, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = 0, \ + .size = 0, \ + .dstInc = 0, \ + .srcAddrMode = 0, \ + .dstAddrMode = 0, \ + .syncSet = (set), \ + .syncClr = (clr), \ + .matchVal = (matchValue), \ + .matchEn = (matchEnable), \ + .linkMode = ldmaLinkModeAbs, \ + .link = 1, \ + .linkAddr = 0 /* Must be set runtime ! */ \ + } \ +} + +/** + * @brief + * DMA descriptor initializer for SYNC transfer + * @param[in] set Sync pattern bits to set. + * @param[in] clr Sync pattern bits to clear. + * @param[in] matchValue Sync pattern to match. + * @param[in] matchEnable Sync pattern bits to enable for match. + * @param[in] linkjmp Address of descriptor to link to expressed as a + * signed number of descriptors from "here". + * 1=one descriptor forward in memory, + * 0=one this descriptor, + * -1=one descriptor back in memory. + */ +#define LDMA_DESCRIPTOR_LINKREL_SYNC( set, clr, matchValue, matchEnable, linkjmp ) \ +{ \ + .sync = \ + { \ + .structType = ldmaCtrlStructTypeSync, \ + .structReq = 1, \ + .xferCnt = 0, \ + .byteSwap = 0, \ + .blockSize = 0, \ + .doneIfs = 0, \ + .reqMode = 0, \ + .decLoopCnt = 0, \ + .ignoreSrec = 0, \ + .srcInc = 0, \ + .size = 0, \ + .dstInc = 0, \ + .srcAddrMode = 0, \ + .dstAddrMode = 0, \ + .syncSet = (set), \ + .syncClr = (clr), \ + .matchVal = (matchValue), \ + .matchEn = (matchEnable), \ + .linkMode = ldmaLinkModeRel, \ + .link = 1, \ + .linkAddr = ( linkjmp ) * 4 \ + } \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void LDMA_DeInit( void ); +void LDMA_Init( LDMA_Init_t *init ); +void LDMA_StartTransfer( int ch, + LDMA_TransferCfg_t *transfer, + LDMA_Descriptor_t *descriptor ); +void LDMA_StopTransfer( int ch ); +bool LDMA_TransferDone( int ch ); +uint32_t LDMA_TransferRemainingCount( int ch ); + + +/***************************************************************************//** + * @brief + * Clear one or more pending LDMA interrupts. + * + * @param[in] flags + * Pending LDMA interrupt sources to clear. Use one or more valid + * interrupt flags for the LDMA module (LDMA_IFC_nnn). + ******************************************************************************/ +__STATIC_INLINE void LDMA_IntClear(uint32_t flags) +{ + LDMA->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more LDMA interrupts. + * + * @param[in] flags + * LDMA interrupt sources to disable. Use one or more valid + * interrupt flags for the LDMA module (LDMA_IEN_nnn). + ******************************************************************************/ +__STATIC_INLINE void LDMA_IntDisable(uint32_t flags) +{ + LDMA->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more LDMA interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using LDMA_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * LDMA interrupt sources to enable. Use one or more valid + * interrupt flags for the LDMA module (LDMA_IEN_nnn). + ******************************************************************************/ +__STATIC_INLINE void LDMA_IntEnable(uint32_t flags) +{ + LDMA->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending LDMA interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * LDMA interrupt sources pending. Returns one or more valid + * interrupt flags for the LDMA module (LDMA_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LDMA_IntGet(void) +{ + return LDMA->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending LDMA interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled LDMA interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in LDMA_IEN and + * - the pending interrupt flags LDMA_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t LDMA_IntGetEnabled(void) +{ + uint32_t ien; + + ien = LDMA->IEN; + return LDMA->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending LDMA interrupts + * + * @param[in] flags + * LDMA interrupt sources to set to pending. Use one or more valid + * interrupt flags for the LDMA module (LDMA_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void LDMA_IntSet(uint32_t flags) +{ + LDMA->IFS = flags; +} + +/** @} (end addtogroup LDMA) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( LDMA_PRESENT ) && ( LDMA_COUNT == 1 ) */ +#endif /* __SILICON_LABS_EM_LDMA_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_lesense.h b/cpu/efm32_common/emlib/inc/em_lesense.h new file mode 100644 index 0000000000000..e6e5000549dc8 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_lesense.h @@ -0,0 +1,1321 @@ +/***************************************************************************//** + * @file em_lesense.h + * @brief Low Energy Sensor (LESENSE) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_LESENSE_H__ +#define __SILICON_LABS_EM_LESENSE_H__ + +#include "em_device.h" + +#if defined(LESENSE_COUNT) && (LESENSE_COUNT > 0) +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LESENSE + * @{ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + + + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Clock divisors for controlling the prescaling factor of the period + * counter. + * Note: these enumeration values are being used for different clock division + * related configuration parameters (hfPresc, lfPresc, pcPresc). */ +typedef enum +{ + lesenseClkDiv_1 = 0, /**< Divide clock by 1. */ + lesenseClkDiv_2 = 1, /**< Divide clock by 2. */ + lesenseClkDiv_4 = 2, /**< Divide clock by 4. */ + lesenseClkDiv_8 = 3, /**< Divide clock by 8. */ + lesenseClkDiv_16 = 4, /**< Divide clock by 16. */ + lesenseClkDiv_32 = 5, /**< Divide clock by 32. */ + lesenseClkDiv_64 = 6, /**< Divide clock by 64. */ + lesenseClkDiv_128 = 7 /**< Divide clock by 128. */ +} LESENSE_ClkPresc_TypeDef; + + +/** Scan modes. */ +typedef enum +{ + /** New scan is started each time the period counter overflows. */ + lesenseScanStartPeriodic = LESENSE_CTRL_SCANMODE_PERIODIC, + + /** Single scan is performed when LESENSE_ScanStart() is called. */ + lesenseScanStartOneShot = LESENSE_CTRL_SCANMODE_ONESHOT, + + /** New scan is triggered by pulse on PRS channel. */ + lesenseScanStartPRS = LESENSE_CTRL_SCANMODE_PRS +} LESENSE_ScanMode_TypeDef; + + +/** PRS sources. + * Note: these enumeration values are being used for different PRS related + * configuration parameters. */ +typedef enum +{ + lesensePRSCh0 = 0, /**< PRS channel 0. */ + lesensePRSCh1 = 1, /**< PRS channel 1. */ + lesensePRSCh2 = 2, /**< PRS channel 2. */ + lesensePRSCh3 = 3, /**< PRS channel 3. */ +#if defined( LESENSE_CTRL_PRSSEL_PRSCH4 ) + lesensePRSCh4 = 4, /**< PRS channel 4. */ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH5 ) + lesensePRSCh5 = 5, /**< PRS channel 5. */ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH6 ) + lesensePRSCh6 = 6, /**< PRS channel 6. */ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH7 ) + lesensePRSCh7 = 7, /**< PRS channel 7. */ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH8 ) + lesensePRSCh8 = 8, /**< PRS channel 8. */ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH9 ) + lesensePRSCh9 = 9, /**< PRS channel 9. */ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH10 ) + lesensePRSCh10 = 10, /**< PRS channel 10.*/ +#endif +#if defined( LESENSE_CTRL_PRSSEL_PRSCH11 ) + lesensePRSCh11 = 11, /**< PRS channel 11.*/ +#endif +} LESENSE_PRSSel_TypeDef; + + +/** Locations of the alternate excitation function. */ +typedef enum +{ + /** Alternate excitation is mapped to the LES_ALTEX pins. */ + lesenseAltExMapALTEX = _LESENSE_CTRL_ALTEXMAP_ALTEX, + + /** Alternate excitation is mapped to the pins of the other ACMP. */ + lesenseAltExMapACMP = _LESENSE_CTRL_ALTEXMAP_ACMP +} LESENSE_AltExMap_TypeDef; + + +/** Result buffer interrupt and DMA trigger levels. */ +typedef enum +{ + /** DMA and interrupt flags are set when result buffer is halffull. */ + lesenseBufTrigHalf = LESENSE_CTRL_BUFIDL_HALFFULL, + + /** DMA and interrupt flags set when result buffer is full. */ + lesenseBufTrigFull = LESENSE_CTRL_BUFIDL_FULL +} LESENSE_BufTrigLevel_TypeDef; + + +/** Modes of operation for DMA wakeup from EM2. */ +typedef enum +{ + /** No DMA wakeup from EM2. */ + lesenseDMAWakeUpDisable = LESENSE_CTRL_DMAWU_DISABLE, + + /** DMA wakeup from EM2 when data is valid in the result buffer. */ + lesenseDMAWakeUpBufValid = LESENSE_CTRL_DMAWU_BUFDATAV, + + /** DMA wakeup from EM2 when the resultbuffer is full/halffull, depending on + * RESBIDL configuration in LESENSE_CTRL register (selected by + * resBufTrigLevel in LESENSE_ResBufTrigLevel_TypeDef descriptor structure). */ + lesenseDMAWakeUpBufLevel = LESENSE_CTRL_DMAWU_BUFLEVEL +} LESENSE_DMAWakeUp_TypeDef; + + +/** Bias modes. */ +typedef enum +{ + /** Duty cycle bias module between low power and high accuracy mode. */ + lesenseBiasModeDutyCycle = LESENSE_BIASCTRL_BIASMODE_DUTYCYCLE, + + /** Bias module is always in high accuracy mode. */ + lesenseBiasModeHighAcc = LESENSE_BIASCTRL_BIASMODE_HIGHACC, + + /** Bias module is controlled by the EMU and not affected by LESENSE. */ + lesenseBiasModeDontTouch = LESENSE_BIASCTRL_BIASMODE_DONTTOUCH +} LESENSE_BiasMode_TypeDef; + + +/** Scan configuration. */ +typedef enum +{ + /** The channel configuration registers (CHx_CONF) used are directly mapped to + * the channel number. */ + lesenseScanConfDirMap = LESENSE_CTRL_SCANCONF_DIRMAP, + + /** The channel configuration registers used are CHx+8_CONF for channels 0-7 + * and CHx-8_CONF for channels 8-15. */ + lesenseScanConfInvMap = LESENSE_CTRL_SCANCONF_INVMAP, + + /** The channel configuration registers used toggles between CHX_SCANCONF and + * CHX+8_SCANCONF when channel x triggers. */ + lesenseScanConfToggle = LESENSE_CTRL_SCANCONF_TOGGLE, + + /** The decoder state defines the channel configuration register (CHx_CONF) to + * be used. */ + lesenseScanConfDecDef = LESENSE_CTRL_SCANCONF_DECDEF +} LESENSE_ScanConfSel_TypeDef; + + +/** DAC CHx data control configuration. */ +typedef enum +{ + /** DAC channel x data is defined by DAC_CHxDATA register. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACIfData = _LESENSE_PERCTRL_DACCH0DATA_DACDATA, + + /** DAC channel x data is defined by ACMPTHRES in LESENSE_CHx_INTERACT. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseACMPThres = _LESENSE_PERCTRL_DACCH0DATA_ACMPTHRES +} LESENSE_ControlDACData_TypeDef; + + +/** DAC channel x conversion mode configuration. */ +typedef enum +{ + /** LESENSE doesn't control DAC channel x. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACConvModeDisable = _LESENSE_PERCTRL_DACCH0CONV_DISABLE, + + /** DAC channel x is driven in continuous mode. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACConvModeContinuous = _LESENSE_PERCTRL_DACCH0CONV_CONTINUOUS, + + /** DAC channel x is driven in sample hold mode. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACConvModeSampleHold = _LESENSE_PERCTRL_DACCH0CONV_SAMPLEHOLD, + + /** DAC channel x is driven in sample off mode. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACConvModeSampleOff = _LESENSE_PERCTRL_DACCH0CONV_SAMPLEOFF +} LESENSE_ControlDACConv_TypeDef; + + +/** DAC channel x output mode configuration. */ +typedef enum +{ + /** DAC CHx output to pin and ACMP/ADC disabled. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACOutModeDisable = _LESENSE_PERCTRL_DACCH0OUT_DISABLE, + + /** DAC CHx output to pin enabled, output to ADC and ACMP disabled. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACOutModePin = _LESENSE_PERCTRL_DACCH0OUT_PIN, + + /** DAC CHx output to pin disabled, output to ADC and ACMP enabled. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACOutModeADCACMP = _LESENSE_PERCTRL_DACCH0OUT_ADCACMP, + + /** DAC CHx output to pin, ADC, and ACMP enabled. + * Note: this value could be used for both DAC Ch0 and Ch1. */ + lesenseDACOutModePinADCACMP = _LESENSE_PERCTRL_DACCH0OUT_PINADCACMP +} LESENSE_ControlDACOut_TypeDef; + + +/** DAC reference configuration. */ +typedef enum +{ + /** DAC uses VDD reference. */ + lesenseDACRefVdd = LESENSE_PERCTRL_DACREF_VDD, + + /** DAC uses bandgap reference. */ + lesenseDACRefBandGap = LESENSE_PERCTRL_DACREF_BANDGAP +} LESENSE_DACRef_TypeDef; + + +/** ACMPx control configuration. */ +typedef enum +{ + /** LESENSE does not control the ACMPx. + * Note: this value could be used for both ACMP0 and ACMP1. */ + lesenseACMPModeDisable = _LESENSE_PERCTRL_ACMP0MODE_DISABLE, + + /** LESENSE controls the input mux of ACMPx. + * Note: this value could be used for both ACMP0 and ACMP1. */ + lesenseACMPModeMux = _LESENSE_PERCTRL_ACMP0MODE_MUX, + + /** LESENSE controls the input mux of and the threshold value of ACMPx. + * Note: this value could be used for both ACMP0 and ACMP1. */ + lesenseACMPModeMuxThres = _LESENSE_PERCTRL_ACMP0MODE_MUXTHRES +} LESENSE_ControlACMP_TypeDef; + + +/** Warm up modes. ACMP and DAC duty cycle mode configuration. */ +typedef enum +{ + /** ACMPs and DACs are shut down when LESENSE is idle. */ + lesenseWarmupModeNormal = LESENSE_PERCTRL_WARMUPMODE_NORMAL, + + /** ACMPs are kept powered up when LESENSE is idle. */ + lesenseWarmupModeACMP = LESENSE_PERCTRL_WARMUPMODE_KEEPACMPWARM, + + /** The DAC is kept powered up when LESENSE is idle. */ + lesenseWarmupModeDAC = LESENSE_PERCTRL_WARMUPMODE_KEEPDACWARM, + + /** ACMPs and the DAC are kept powered up when LESENSE is idle. */ + lesenseWarmupModeKeepWarm = LESENSE_PERCTRL_WARMUPMODE_KEEPACMPDACWARM +} LESENSE_WarmupMode_TypeDef; + + +/** Decoder input source configuration. */ +typedef enum +{ + /** The SENSORSTATE register is used as input to the decoder. */ + lesenseDecInputSensorSt = LESENSE_DECCTRL_INPUT_SENSORSTATE, + + /** PRS channels are used as input to the decoder. */ + lesenseDecInputPRS = LESENSE_DECCTRL_INPUT_PRS +} LESENSE_DecInput_TypeDef; + + +/** Compare source selection for sensor sampling. */ +typedef enum +{ + /** Counter output will be used in comparison. */ + lesenseSampleModeCounter = LESENSE_CH_INTERACT_SAMPLE_COUNTER, + + /** ACMP output will be used in comparison. */ + lesenseSampleModeACMP = LESENSE_CH_INTERACT_SAMPLE_ACMP +} LESENSE_ChSampleMode_TypeDef; + + +/** Interrupt generation setup for CHx interrupt flag. */ +typedef enum +{ + /** No interrupt is generated. */ + lesenseSetIntNone = LESENSE_CH_INTERACT_SETIF_NONE, + + /** Set interrupt flag if the sensor triggers. */ + lesenseSetIntLevel = LESENSE_CH_INTERACT_SETIF_LEVEL, + + /** Set interrupt flag on positive edge of the sensor state. */ + lesenseSetIntPosEdge = LESENSE_CH_INTERACT_SETIF_POSEDGE, + + /** Set interrupt flag on negative edge of the sensor state. */ + lesenseSetIntNegEdge = LESENSE_CH_INTERACT_SETIF_NEGEDGE +} LESENSE_ChIntMode_TypeDef; + + +/** Channel pin mode for the excitation phase of the scan sequence. */ +typedef enum +{ + /** Channel pin is disabled. */ + lesenseChPinExDis = LESENSE_CH_INTERACT_EXMODE_DISABLE, + + /** Channel pin is configured as push-pull, driven HIGH. */ + lesenseChPinExHigh = LESENSE_CH_INTERACT_EXMODE_HIGH, + + /** Channel pin is configured as push-pull, driven LOW. */ + lesenseChPinExLow = LESENSE_CH_INTERACT_EXMODE_LOW, + + /** DAC output (only available on channel 0, 1, 2, 3, 12, 13, 14 and 15) */ + lesenseChPinExDACOut = LESENSE_CH_INTERACT_EXMODE_DACOUT +} LESENSE_ChPinExMode_TypeDef; + + +/** Channel pin mode for the idle phase of the scan sequence. */ +typedef enum +{ + /** Channel pin is disabled in idle phase. + * Note: this value could be used for all channels. */ + lesenseChPinIdleDis = _LESENSE_IDLECONF_CH0_DISABLE, + + /** Channel pin is configured as push-pull, driven HIGH in idle phase. + * Note: this value could be used for all channels. */ + lesenseChPinIdleHigh = _LESENSE_IDLECONF_CH0_HIGH, + + /** Channel pin is configured as push-pull, driven LOW in idle phase. + * Note: this value could be used for all channels. */ + lesenseChPinIdleLow = _LESENSE_IDLECONF_CH0_LOW, + + /** Channel pin is connected to DAC CH0 output in idle phase. + * Note: only applies to channel 0, 1, 2, 3. */ + lesenseChPinIdleDACCh0 = _LESENSE_IDLECONF_CH0_DACCH0, + + /** Channel pin is connected to DAC CH1 output in idle phase. + * Note: only applies to channel 12, 13, 14, 15. */ + lesenseChPinIdleDACCh1 = _LESENSE_IDLECONF_CH12_DACCH1 +} LESENSE_ChPinIdleMode_TypeDef; + + +/** Clock used for excitation and sample delay timing. */ +typedef enum +{ + /** LFACLK (LF clock) is used. */ + lesenseClkLF = _LESENSE_CH_INTERACT_EXCLK_LFACLK, + + /** AUXHFRCO (HF clock) is used. */ + lesenseClkHF = _LESENSE_CH_INTERACT_EXCLK_AUXHFRCO +} LESENSE_ChClk_TypeDef; + + +/** Compare modes for counter comparison. */ +typedef enum +{ + /** Set interrupt flag if counter value is less than CTRTHRESHOLD, or if the + * ACMP output is 0. */ + lesenseCompModeLess = LESENSE_CH_EVAL_COMP_LESS, + + /** Set interrupt flag if counter value is greater than, or equal to + * CTRTHRESHOLD, or if the ACMP output is 1. */ + lesenseCompModeGreaterOrEq = LESENSE_CH_EVAL_COMP_GE +} LESENSE_ChCompMode_TypeDef; + + +/** Idle phase configuration of alternate excitation channels. */ +typedef enum +{ + /** ALTEX output is disabled in idle phase. + * Note: this value could be used for all alternate excitation channels. */ + lesenseAltExPinIdleDis = _LESENSE_ALTEXCONF_IDLECONF0_DISABLE, + + /** ALTEX output is high in idle phase. + * Note: this value could be used for all alternate excitation channels. */ + lesenseAltExPinIdleHigh = _LESENSE_ALTEXCONF_IDLECONF0_HIGH, + + /** ALTEX output is low in idle phase. + * Note: this value could be used for all alternate excitation channels. */ + lesenseAltExPinIdleLow = _LESENSE_ALTEXCONF_IDLECONF0_LOW +} LESENSE_AltExPinIdle_TypeDef; + + +/** Transition action modes. */ +typedef enum +{ + /** No PRS pulses generated (if PRSCOUNT == 0). + * Do not count (if PRSCOUNT == 1). */ + lesenseTransActNone = LESENSE_ST_TCONFA_PRSACT_NONE, + + /** Generate pulse on LESPRS0 (if PRSCOUNT == 0). */ + lesenseTransActPRS0 = LESENSE_ST_TCONFA_PRSACT_PRS0, + + /** Generate pulse on LESPRS1 (if PRSCOUNT == 0). */ + lesenseTransActPRS1 = LESENSE_ST_TCONFA_PRSACT_PRS1, + + /** Generate pulse on LESPRS0 and LESPRS1 (if PRSCOUNT == 0). */ + lesenseTransActPRS01 = LESENSE_ST_TCONFA_PRSACT_PRS01, + + /** Generate pulse on LESPRS2 (for both PRSCOUNT == 0 and PRSCOUNT == 1). */ + lesenseTransActPRS2 = LESENSE_ST_TCONFA_PRSACT_PRS2, + + /** Generate pulse on LESPRS0 and LESPRS2 (if PRSCOUNT == 0). */ + lesenseTransActPRS02 = LESENSE_ST_TCONFA_PRSACT_PRS02, + + /** Generate pulse on LESPRS1 and LESPRS2 (if PRSCOUNT == 0). */ + lesenseTransActPRS12 = LESENSE_ST_TCONFA_PRSACT_PRS12, + + /** Generate pulse on LESPRS0, LESPRS1 and LESPRS2 (if PRSCOUNT == 0). */ + lesenseTransActPRS012 = LESENSE_ST_TCONFA_PRSACT_PRS012, + + /** Count up (if PRSCOUNT == 1). */ + lesenseTransActUp = LESENSE_ST_TCONFA_PRSACT_UP, + + /** Count down (if PRSCOUNT == 1). */ + lesenseTransActDown = LESENSE_ST_TCONFA_PRSACT_DOWN, + + /** Count up and generate pulse on LESPRS2 (if PRSCOUNT == 1). */ + lesenseTransActUpAndPRS2 = LESENSE_ST_TCONFA_PRSACT_UPANDPRS2, + + /** Count down and generate pulse on LESPRS2 (if PRSCOUNT == 1). */ + lesenseTransActDownAndPRS2 = LESENSE_ST_TCONFA_PRSACT_DOWNANDPRS2 +} LESENSE_StTransAct_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Core control (LESENSE_CTRL) descriptor structure. */ +typedef struct +{ + /** Select scan start mode to control how the scan start is being triggered.*/ + LESENSE_ScanMode_TypeDef scanStart; + + /** Select PRS source for scan start if scanMode is set to lesensePrsPulse. */ + LESENSE_PRSSel_TypeDef prsSel; + + /** Select scan configuration register usage strategy. */ + LESENSE_ScanConfSel_TypeDef scanConfSel; + + /** Set to true to invert ACMP0 output. */ + bool invACMP0; + + /** Set to true to invert ACMP1 output. */ + bool invACMP1; + + /** Set to true to sample both ACMPs simultaneously. */ + bool dualSample; + + /** Set to true in order to to store SCANRES in RAM (accessible via RESDATA) + * after each scan. */ + bool storeScanRes; + + /** Set to true in order to always make LESENSE write to the result buffer, + * even if it is full. */ + bool bufOverWr; + + /** Select trigger conditions for interrupt and DMA. */ + LESENSE_BufTrigLevel_TypeDef bufTrigLevel; + + /** Configure trigger condition for DMA wakeup from EM2. */ + LESENSE_DMAWakeUp_TypeDef wakeupOnDMA; + + /** Select bias mode. */ + LESENSE_BiasMode_TypeDef biasMode; + + /** Set to true to keep LESENSE running in debug mode. */ + bool debugRun; +} LESENSE_CoreCtrlDesc_TypeDef; + +/** Default configuration for LESENSE_CtrlDesc_TypeDef structure. */ +#define LESENSE_CORECTRL_DESC_DEFAULT \ +{ \ + lesenseScanStartPeriodic, /* Start new scan each time the period counter overflows. */ \ + lesensePRSCh0, /* Default PRS channel is selected. */ \ + lesenseScanConfDirMap, /* Direct mapping SCANCONF register usage strategy. */ \ + false, /* Don't invert ACMP0 output. */ \ + false, /* Don't invert ACMP1 output. */ \ + false, /* Disable dual sampling. */ \ + true, /* Store scan result after each scan. */ \ + true, /* Overwrite result buffer register even if it is full. */ \ + lesenseBufTrigHalf, /* Trigger interrupt and DMA request if result buffer is half full. */ \ + lesenseDMAWakeUpDisable, /* Don't wake up on DMA from EM2. */ \ + lesenseBiasModeDontTouch, /* Don't touch bias configuration. */ \ + true /* Keep LESENSE running in debug mode. */ \ +} + + +/** LESENSE timing control descriptor structure. */ +typedef struct +{ + /** Set the number of LFACLK cycles to delay sensor interaction on + * each channel. Valid range: 0-3 (2 bit). */ + uint8_t startDelay; +} LESENSE_TimeCtrlDesc_TypeDef; + +/** Default configuration for LESENSE_TimeCtrlDesc_TypeDef structure. */ +#define LESENSE_TIMECTRL_DESC_DEFAULT \ +{ \ + 0U /* No sensor interaction delay. */ \ +} + + +/** LESENSE peripheral control descriptor structure. */ +typedef struct +{ + /** Configure DAC channel 0 data control. */ + LESENSE_ControlDACData_TypeDef dacCh0Data; + + /** Configure how LESENSE controls conversion on DAC channel 0. */ + LESENSE_ControlDACConv_TypeDef dacCh0ConvMode; + + /** Configure how LESENSE controls output on DAC channel 0. */ + LESENSE_ControlDACOut_TypeDef dacCh0OutMode; + + /** Configure DAC channel 1 data control. */ + LESENSE_ControlDACData_TypeDef dacCh1Data; + + /** Configure how LESENSE controls conversion on DAC channel 1. */ + LESENSE_ControlDACConv_TypeDef dacCh1ConvMode; + + /** Configure how LESENSE controls output on DAC channel 1. */ + LESENSE_ControlDACOut_TypeDef dacCh1OutMode; + + /** Configure the prescaling factor for the LESENSE - DAC interface. + * Valid range: 0-31 (5bit). */ + uint8_t dacPresc; + + /** Configure the DAC reference to be used. Set to #lesenseDACRefVdd to use + * VDD and set to #lesenseDACRefBandGap to use bandgap as reference. */ + LESENSE_DACRef_TypeDef dacRef; + + /** Configure how LESENSE controls ACMP 0. */ + LESENSE_ControlACMP_TypeDef acmp0Mode; + + /** Configure how LESENSE controls ACMP 1. */ + LESENSE_ControlACMP_TypeDef acmp1Mode; + + /** Configure how LESENSE controls ACMPs and the DAC in idle mode. */ + LESENSE_WarmupMode_TypeDef warmupMode; +} LESENSE_PerCtrlDesc_TypeDef; + +/** Default configuration for LESENSE_PerCtrl_TypeDef structure. */ +#define LESENSE_PERCTRL_DESC_DEFAULT \ +{ \ + lesenseDACIfData, /**/ \ + lesenseDACConvModeDisable, /**/ \ + lesenseDACOutModeDisable, /**/ \ + lesenseDACIfData, /**/ \ + lesenseDACConvModeDisable, /**/ \ + lesenseDACOutModeDisable, /**/ \ + 0U, /**/ \ + lesenseDACRefVdd, /**/ \ + lesenseACMPModeMuxThres, /**/ \ + lesenseACMPModeMuxThres, /**/ \ + lesenseWarmupModeKeepWarm, /**/ \ +} + + +/** LESENSE decoder control descriptor structure. */ +typedef struct +{ + /** Select the input to the LESENSE decoder. */ + LESENSE_DecInput_TypeDef decInput; + + /** Initial state of the LESENSE decoder. */ + uint32_t initState; + + /** Set to enable the decoder to check the present state in addition + * to the states defined in DECCONF. */ + bool chkState; + + /** When set, a transition from state x in the decoder will set interrupt flag + * CHx. */ + bool intMap; + + /** Set to enable hysteresis in the decoder for suppressing changes on PRS + * channel 0. */ + bool hystPRS0; + + /** Set to enable hysteresis in the decoder for suppressing changes on PRS + * channel 1. */ + bool hystPRS1; + + /** Set to enable hysteresis in the decoder for suppressing changes on PRS + * channel 2. */ + bool hystPRS2; + + /** Set to enable hysteresis in the decoder for suppressing interrupt + * requests. */ + bool hystIRQ; + + /** Set to enable count mode on decoder PRS channels 0 and 1 to produce + * outputs which can be used by a PCNT to count up or down. */ + bool prsCount; + + /** Select PRS channel input for bit 0 of the LESENSE decoder. */ + LESENSE_PRSSel_TypeDef prsChSel0; + + /** Select PRS channel input for bit 1 of the LESENSE decoder. */ + LESENSE_PRSSel_TypeDef prsChSel1; + + /** Select PRS channel input for bit 2 of the LESENSE decoder. */ + LESENSE_PRSSel_TypeDef prsChSel2; + + /** Select PRS channel input for bit 3 of the LESENSE decoder. */ + LESENSE_PRSSel_TypeDef prsChSel3; +} LESENSE_DecCtrlDesc_TypeDef; + +/** Default configuration for LESENSE_PerCtrl_TypeDef structure. */ +#define LESENSE_DECCTRL_DESC_DEFAULT \ +{ \ + lesenseDecInputSensorSt, /**/ \ + 0U, /**/ \ + false, /**/ \ + true, /**/ \ + true, /**/ \ + true, /**/ \ + true, /**/ \ + true, /**/ \ + false, /**/ \ + lesensePRSCh0, /**/ \ + lesensePRSCh1, /**/ \ + lesensePRSCh2, /**/ \ + lesensePRSCh3, /**/ \ +} + + +/** LESENSE module initialization structure. */ +typedef struct +{ + /** LESENSE core configuration parameters. */ + LESENSE_CoreCtrlDesc_TypeDef coreCtrl; + + /** LESENSE timing configuration parameters. */ + LESENSE_TimeCtrlDesc_TypeDef timeCtrl; + + /** LESENSE peripheral configuration parameters. */ + LESENSE_PerCtrlDesc_TypeDef perCtrl; + + /** LESENSE decoder configuration parameters. */ + LESENSE_DecCtrlDesc_TypeDef decCtrl; +} LESENSE_Init_TypeDef; + +/** Default configuration for LESENSE_Init_TypeDef structure. */ +#define LESENSE_INIT_DEFAULT \ +{ \ + .coreCtrl = LESENSE_CORECTRL_DESC_DEFAULT, /* Default core control parameters. */ \ + .timeCtrl = LESENSE_TIMECTRL_DESC_DEFAULT, /* Default time control parameters. */ \ + .perCtrl = LESENSE_PERCTRL_DESC_DEFAULT, /* Default peripheral control parameters. */ \ + .decCtrl = LESENSE_DECCTRL_DESC_DEFAULT /* Default decoder control parameters. */ \ +} + + +/** Channel descriptor structure. */ +typedef struct +{ + /** Set to enable scan channel CHx. */ + bool enaScanCh; + + /** Set to enable CHx pin. */ + bool enaPin; + + /** Enable/disable channel interrupts after configuring all the sensor channel + * parameters. */ + bool enaInt; + + /** Configure channel pin mode for the excitation phase of the scan sequence. + * Note: OPAOUT is only available on channels 2, 3, 4, and 5. */ + LESENSE_ChPinExMode_TypeDef chPinExMode; + + /** Configure channel pin idle setup in LESENSE idle phase. */ + LESENSE_ChPinIdleMode_TypeDef chPinIdleMode; + + /** Set to use alternate excite pin for excitation. */ + bool useAltEx; + + /** Set to enable the result from this channel being shifted into the decoder + * register. */ + bool shiftRes; + + /** Set to invert the result bit stored in SCANRES register. */ + bool invRes; + + /** Set to store the counter value in RAM (accessible via RESDATA) and make + * the comparison result available in the SCANRES register. */ + bool storeCntRes; + + /** Select clock used for excitation timing. */ + LESENSE_ChClk_TypeDef exClk; + + /** Select clock used for sample delay timing. */ + LESENSE_ChClk_TypeDef sampleClk; + + /** Configure excitation time. Excitation will last exTime+1 excitation clock + * cycles. Valid range: 0-63 (6 bits). */ + uint8_t exTime; + + /** Configure sample delay. Sampling will occur after sampleDelay+1 sample + * clock cycles. Valid range: 0-127 (7 bits). */ + uint8_t sampleDelay; + + /** Configure measure delay. Sensor measuring is delayed for measDelay + * excitation clock cycles. Valid range: 0-127 (7 bits). */ + uint8_t measDelay; + + /** Configure ACMP threshold. + * If perCtrl.dacCh0Data or perCtrl.dacCh1Data is set to #lesenseDACIfData, + * acmpThres defines the 12-bit DAC data in the corresponding data register + * of the DAC interface (DACn_CH0DATA and DACn_CH1DATA). + * In this case, the valid range is: 0-4095 (12 bits). + * If perCtrl.dacCh0Data or perCtrl.dacCh1Data is set to #lesenseACMPThres, + * acmpThres defines the 6-bit Vdd scaling factor of ACMP negative input + * (VDDLEVEL in ACMP_INPUTSEL register). + * In this case, the valid range is: 0-63 (6 bits). */ + uint16_t acmpThres; + + /** Select if ACMP output or counter output should be used in comparison. */ + LESENSE_ChSampleMode_TypeDef sampleMode; + + /** Configure interrupt generation mode for CHx interrupt flag. */ + LESENSE_ChIntMode_TypeDef intMode; + + /** Configure decision threshold for counter comparison. + * Valid range: 0-65535 (16 bits). */ + uint16_t cntThres; + + /** Select mode for counter comparison. */ + LESENSE_ChCompMode_TypeDef compMode; +} LESENSE_ChDesc_TypeDef; + + +/** Configuration structure for all scan channels. */ +typedef struct +{ + /** Channel descriptor for all 16 channels. */ + LESENSE_ChDesc_TypeDef Ch[16]; +} LESENSE_ChAll_TypeDef; + +/** Default configuration for scan channel. */ +#define LESENSE_CH_CONF_DEFAULT \ +{ \ + true, /* Enable scan channel. */ \ + true, /* Enable the assigned pin on scan channel. */ \ + true, /* Enable interrupts on channel. */ \ + lesenseChPinExHigh, /* Channel pin is high during the excitation period. */ \ + lesenseChPinIdleLow, /* Channel pin is low during the idle period. */ \ + false, /* Don't use alternate excitation pins for excitation. */ \ + false, /* Disabled to shift results from this channel to the decoder register. */ \ + false, /* Disabled to invert the scan result bit. */ \ + false, /* Disabled to store counter value in the result buffer. */ \ + lesenseClkLF, /* Use the LF clock for excitation timing. */ \ + lesenseClkLF, /* Use the LF clock for sample timing. */ \ + 0x03U, /* Excitation time is set to 3(+1) excitation clock cycles. */ \ + 0x09U, /* Sample delay is set to 9(+1) sample clock cycles. */ \ + 0x06U, /* Measure delay is set to 6 excitation clock cycles.*/ \ + 0x00U, /* ACMP threshold has been set to 0. */ \ + lesenseSampleModeACMP, /* ACMP output will be used in comparison. */ \ + lesenseSetIntNone, /* No interrupt is generated by the channel. */ \ + 0xFFU, /* Counter threshold has bee set to 0xFF. */ \ + lesenseCompModeLess /* Compare mode has been set to trigger interrupt on "less". */ \ +} + +/** Default configuration for all sensor channels. */ +#define LESENSE_SCAN_CONF_DEFAULT \ +{ \ + { \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 0. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 1. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 2. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 3. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 4. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 5. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 6. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 7. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 8. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 9. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 10. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 11. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 12. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 13. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 14. */ \ + LESENSE_CH_CONF_DEFAULT, /* Scan channel 15. */ \ + } \ +} + + +/** Alternate excitation descriptor structure. */ +typedef struct +{ + /** Configure alternate excitation pins. If set, the corresponding alternate + * excitation pin/signal is enabled. */ + bool enablePin; + + /** Configure idle phase setup of alternate excitation pins. + The idleConf parameter is not valid when altExMap==lesenseAltExMapACMP. */ + LESENSE_AltExPinIdle_TypeDef idleConf; + + /** Configure how to control the external alternate excitation pins. Only + * applies if altExMap has been set to lesenseAltExMapALTEX. + * If true, the excitation happens on the corresponding alternate excitation + * pin during the excitation periods of all enabled channels. + * If false, the excitation happens on the corresponding alternate excitation + * pin ONLY during the excitation period of the corresponding channel. + * The alwaysEx parameter is not valid when altExMap==lesenseAltExMapACMP. */ + bool alwaysEx; +} LESENSE_AltExDesc_TypeDef; + + +/** Configuration structure for alternate excitation. */ +typedef struct +{ + /** Select alternate excitation mapping. */ + LESENSE_AltExMap_TypeDef altExMap; + + /** Alternate excitation channel descriptors. + * When altExMap==lesenseAltExMapALTEX only the 8 first descriptors are used. + * In this mode they describe the configuration of the LES_ALTEX0-7 pins. + * When altExMap==lesenseAltExMapACMP all 16 descriptors are used. In this + * mode they describe the configuration of the 16 possible ACMP0-1 excitation + * channels. Please refer to the user manual for a complete mapping of the + * routing. + * NOTE: + * Some parameters in the descriptors are not valid when + * altExMap==lesenseAltExMapACMP. Please refer to the definition of the + * LESENSE_AltExDesc_TypeDef structure for details regarding which parameters + * are valid. */ + LESENSE_AltExDesc_TypeDef AltEx[16]; + +} LESENSE_ConfAltEx_TypeDef; + + +/** Default configuration for alternate excitation channel. */ +#define LESENSE_ALTEX_CH_CONF_DEFAULT \ +{ \ + true, /* Alternate excitation enabled.*/ \ + lesenseAltExPinIdleDis,/* Alternate excitation pin is disabled in idle. */ \ + false /* Excite only for corresponding channel. */ \ +} + +/** Default configuration for all alternate excitation channels. */ +#define LESENSE_ALTEX_CONF_DEFAULT \ +{ \ + lesenseAltExMapACMP, \ + { \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 0. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 1. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 2. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 3. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 4. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 5. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 6. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 7. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 8. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 9. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 10. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 11. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 12. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 13. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT, /* Alternate excitation channel 14. */ \ + LESENSE_ALTEX_CH_CONF_DEFAULT /* Alternate excitation channel 15. */ \ + } \ +} + + +/** Decoder state condition descriptor structure. */ +typedef struct +{ + /** Configure compare value. State transition is triggered when sensor state + * equals to this value. Valid range: 0-15 (4 bits). */ + uint8_t compVal; + + /** Configure compare mask. Set bit X to exclude sensor X from evaluation. + * Note: decoder can handle sensor inputs from up to 4 sensors, therefore + * this mask is 4 bit long. */ + uint8_t compMask; + + /** Configure index of state to be entered if the sensor state equals to + * compVal. Valid range: 0-15 (4 bits). */ + uint8_t nextState; + + /** Configure which PRS action to perform when sensor state equals to + * compVal. */ + LESENSE_StTransAct_TypeDef prsAct; + + /** If enabled, interrupt flag is set when sensor state equals to compVal. */ + bool setInt; +} LESENSE_DecStCond_TypeDef; + +/** Default configuration for decoder state condition. */ +#define LESENSE_ST_CONF_DEFAULT \ +{ \ + 0x0FU, /* Compare value set to 0x0F. */ \ + 0x00U, /* All decoder inputs masked. */ \ + 0U, /* Next state is state 0. */ \ + lesenseTransActNone, /* No PRS action performed on compare match. */ \ + false /* No interrupt triggered on compare match. */ \ +} + + +/** Decoder state x configuration structure. */ +typedef struct +{ + /** If enabled, the state descriptor pair in the next location will also be + * evaluated. */ + bool chainDesc; + + /** State condition descriptor A (high level descriptor of + * LESENSE_STx_DECCONFA). */ + LESENSE_DecStCond_TypeDef confA; + + /** State condition descriptor B (high level descriptor of + * LESENSE_STx_DECCONFB). */ + LESENSE_DecStCond_TypeDef confB; +} LESENSE_DecStDesc_TypeDef; + + +/** Configuration structure for the decoder. */ +typedef struct +{ + /** Descriptor of the 16 decoder states. */ + LESENSE_DecStDesc_TypeDef St[16]; +} LESENSE_DecStAll_TypeDef; + +/** Default configuration for all decoder states. */ +#define LESENSE_DECODER_CONF_DEFAULT \ +{ /* chain | Descriptor A | Descriptor B */ \ + { \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 0. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 1. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 2. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 3. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 4. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 5. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 6. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 7. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 8. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 9. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 10. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 11. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 12. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 13. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT }, /* Decoder state 14. */ \ + { false, LESENSE_ST_CONF_DEFAULT, LESENSE_ST_CONF_DEFAULT } /* Decoder state 15. */ \ + } \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ +void LESENSE_Init(LESENSE_Init_TypeDef const *init, bool const reqReset); +void LESENSE_Reset(void); + +uint32_t LESENSE_ScanFreqSet(uint32_t refFreq, uint32_t const scanFreq); +void LESENSE_ScanModeSet(LESENSE_ScanMode_TypeDef const scanMode, + bool const start); + +void LESENSE_StartDelaySet(uint8_t const startDelay); + +void LESENSE_ClkDivSet(LESENSE_ChClk_TypeDef const clk, + LESENSE_ClkPresc_TypeDef const clkDiv); + +void LESENSE_ChannelAllConfig(LESENSE_ChAll_TypeDef const *confChAll); +void LESENSE_ChannelConfig(LESENSE_ChDesc_TypeDef const *confCh, + uint32_t const chIdx); +void LESENSE_ChannelEnable(uint8_t const chIdx, + bool const enaScanCh, + bool const enaPin); +void LESENSE_ChannelEnableMask(uint16_t chMask, uint16_t pinMask); +void LESENSE_ChannelTimingSet(uint8_t const chIdx, + uint8_t const exTime, + uint8_t const sampleDelay, + uint8_t const measDelay); +void LESENSE_ChannelThresSet(uint8_t const chIdx, + uint16_t const acmpThres, + uint16_t const cntThres); + +void LESENSE_AltExConfig(LESENSE_ConfAltEx_TypeDef const *confAltEx); + +void LESENSE_DecoderStateAllConfig(LESENSE_DecStAll_TypeDef const *confDecStAll); +void LESENSE_DecoderStateConfig(LESENSE_DecStDesc_TypeDef const *confDecSt, + uint32_t const decSt); +void LESENSE_DecoderStateSet(uint32_t decSt); +uint32_t LESENSE_DecoderStateGet(void); + +void LESENSE_ScanStart(void); +void LESENSE_ScanStop(void); +void LESENSE_DecoderStart(void); +void LESENSE_ResultBufferClear(void); + + +/***************************************************************************//** + * @brief + * Stop LESENSE decoder. + * + * @details + * This function disables the LESENSE decoder by setting the command to the + * LESENSE_DECCTRL register. + ******************************************************************************/ +__STATIC_INLINE void LESENSE_DecoderStop(void) +{ + /* Stop the decoder */ + LESENSE->DECCTRL |= LESENSE_DECCTRL_DISABLE; +} + + +/***************************************************************************//** + * @brief + * Get the current status of LESENSE. + * + * @return + * This function returns the value of LESENSE_STATUS register that + * contains the OR combination of the following status bits: + * @li LESENSE_STATUS_RESV - Result data valid. Set when data is available + * in the result buffer. Cleared when the buffer is empty. + * @li LESENSE_STATUS_RESFULL - Result buffer full. Set when the result + * buffer is full. + * @li LESENSE_STATUS_RUNNING - LESENSE is active. + * @li LESENSE_STATUS_SCANACTIVE - LESENSE is currently interfacing sensors. + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_StatusGet(void) +{ + return LESENSE->STATUS; +} + + +/***************************************************************************//** + * @brief + * Wait until the status of LESENSE is equal to what requested. + * + * @details + * This function is polling the LESENSE_STATUS register and waits until the + * requested combination of flags are set. + * + * @param[in] flag + * The OR combination of the following status bits: + * @li LESENSE_STATUS_BUFDATAV - Result data valid. Set when data is available + * in the result buffer. Cleared when the buffer is empty. + * @li LESENSE_STATUS_BUFHALFFULL - Result buffer half full. Set when the + * result buffer is half full. + * @li LESENSE_STATUS_BUFFULL - Result buffer full. Set when the result + * buffer is full. + * @li LESENSE_STATUS_RUNNING - LESENSE is active. + * @li LESENSE_STATUS_SCANACTIVE - LESENSE is currently interfacing sensors. + * @li LESENSE_STATUS_DACACTIVE - The DAC interface is currently active. + ******************************************************************************/ +__STATIC_INLINE void LESENSE_StatusWait(uint32_t flag) +{ + while (!(LESENSE->STATUS & flag)) + ; +} + + +/***************************************************************************//** + * @brief + * Get the currently active channel index. + * + * @return + * This function returns the value of LESENSE_CHINDEX register that + * contains the index of the currently active channel (0-15). + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_ChannelActiveGet(void) +{ + return LESENSE->CURCH; +} + + +/***************************************************************************//** + * @brief + * Get the latest scan comparison result (1 bit / channel). + * + * @return + * This function returns the value of LESENSE_SCANRES register that + * contains the comparison result of the last scan on all channels. + * Bit x is set if a comparison triggered on channel x, which means that the + * LESENSE counter met the comparison criteria set in LESENSE_CHx_EVAL by + * COMPMODE and CNTTHRES. + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_ScanResultGet(void) +{ + return LESENSE->SCANRES; +} + + +/***************************************************************************//** + * @brief + * Get the oldest unread data from the result buffer. + * + * @note + * Make sure that the STORERES bit is set in LESENSE_CHx_EVAL, or + * STRSCANRES bit is set in LESENSE_CTRL, otherwise this function will return + * undefined value. + * + * @return + * This function returns the value of LESENSE_RESDATA register that + * contains the oldest unread counter result from the result buffer. + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_ScanResultDataGet(void) +{ + return LESENSE->BUFDATA; +} + + +/***************************************************************************//** + * @brief + * Get data from the result data buffer. + * + * @note + * Make sure that the STORERES bit is set in LESENSE_CHx_EVAL, or + * STRSCANRES bit is set in LESENSE_CTRL, otherwise this function will return + * undefined value. + * + * @param[in] idx + * Result data buffer index. Valid range: 0-15. + * + * @return + * This function returns the selected word from the result data buffer. + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_ScanResultDataBufferGet(uint32_t idx) +{ + /* Note: masking is needed to avoid over-indexing! */ + return LESENSE->BUF[idx & 0x0FU].DATA; +} + +/***************************************************************************//** + * @brief + * Get the current state of the LESENSE sensor. + * + * @return + * This function returns the value of LESENSE_SENSORSTATE register that + * represents the current state of the LESENSE sensor. + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_SensorStateGet(void) +{ + return LESENSE->SENSORSTATE; +} + + +/***************************************************************************//** + * @brief + * Shut off power to the LESENSE RAM, disables LESENSE. + * + * @details + * This function shuts off the LESENSE RAM in order to decrease the leakage + * current of the mcu if LESENSE is not used in your application. + * + * @note + * Warning! Once the LESENSE RAM is powered down, it cannot be powered up + * again. + ******************************************************************************/ +__STATIC_INLINE void LESENSE_RAMPowerDown(void) +{ + /* Power down LESENSE RAM */ + LESENSE->POWERDOWN = LESENSE_POWERDOWN_RAM; +} + + +/***************************************************************************//** + * @brief + * Clear one or more pending LESENSE interrupts. + * + * @param[in] flags + * Pending LESENSE interrupt sources to clear. Use a set of interrupt flags + * OR-ed together to clear multiple interrupt sources of the LESENSE module + * (LESENSE_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LESENSE_IntClear(uint32_t flags) +{ + LESENSE->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more LESENSE interrupts. + * + * @param[in] flags + * LESENSE interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to enable multiple interrupt sources of the LESENSE module + * (LESENSE_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LESENSE_IntEnable(uint32_t flags) +{ + LESENSE->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more LESENSE interrupts. + * + * @param[in] flags + * LESENSE interrupt sources to disable. Use a set of interrupt flags OR-ed + * together to disable multiple interrupt sources of the LESENSE module + * (LESENSE_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LESENSE_IntDisable(uint32_t flags) +{ + LESENSE->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending LESENSE interrupts from SW. + * + * @param[in] flags + * LESENSE interrupt sources to set to pending. Use a set of interrupt + * flags OR-ed together to set multiple interrupt sources of the LESENSE + * module (LESENSE_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void LESENSE_IntSet(uint32_t flags) +{ + LESENSE->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Get pending LESENSE interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending LESENSE interrupt sources. The OR combination of valid interrupt + * flags of the LESENSE module (LESENSE_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_IntGet(void) +{ + return LESENSE->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending LESENSE interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending and enabled LESENSE interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in LESENSE_IEN_nnn + * register (LESENSE_IEN_nnn) and + * - the OR combination of valid interrupt flags of the LESENSE module + * (LESENSE_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LESENSE_IntGetEnabled(void) +{ + uint32_t tmp; + + /* Store LESENSE->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + tmp = LESENSE->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return LESENSE->IF & tmp; +} + + +/** @} (end addtogroup LESENSE) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(LESENSE_COUNT) && (LESENSE_COUNT > 0) */ + +#endif /* __SILICON_LABS_EM_LESENSE_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_letimer.h b/cpu/efm32_common/emlib/inc/em_letimer.h new file mode 100644 index 0000000000000..24456647c341b --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_letimer.h @@ -0,0 +1,321 @@ +/***************************************************************************//** + * @file em_letimer.h + * @brief Low Energy Timer (LETIMER) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_LETIMER_H__ +#define __SILICON_LABS_EM_LETIMER_H__ + +#include +#include "em_device.h" +#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LETIMER + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Repeat mode. */ +typedef enum +{ + /** Count until stopped by SW. */ + letimerRepeatFree = _LETIMER_CTRL_REPMODE_FREE, + /** Count REP0 times. */ + letimerRepeatOneshot = _LETIMER_CTRL_REPMODE_ONESHOT, + /** + * Count REP0 times, if REP1 has been written to, it is loaded into + * REP0 when REP0 is about to be decremented to 0. + */ + letimerRepeatBuffered = _LETIMER_CTRL_REPMODE_BUFFERED, + /** + * Run as long as both REP0 and REP1 are not 0. Both REP0 and REP1 + * are decremented when counter underflows. + */ + letimerRepeatDouble = _LETIMER_CTRL_REPMODE_DOUBLE +} LETIMER_RepeatMode_TypeDef; + + +/** Underflow action on output. */ +typedef enum +{ + /** No output action. */ + letimerUFOANone = _LETIMER_CTRL_UFOA0_NONE, + /** Toggle output when counter underflows. */ + letimerUFOAToggle = _LETIMER_CTRL_UFOA0_TOGGLE, + /** Hold output one LETIMER clock cycle when counter underflows. */ + letimerUFOAPulse = _LETIMER_CTRL_UFOA0_PULSE, + /** Set output idle when counter underflows, and active when matching COMP1. */ + letimerUFOAPwm = _LETIMER_CTRL_UFOA0_PWM +} LETIMER_UFOA_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** LETIMER initialization structure. */ +typedef struct +{ + bool enable; /**< Start counting when init completed. */ + bool debugRun; /**< Counter shall keep running during debug halt. */ +#if defined(LETIMER_CTRL_RTCC0TEN) + bool rtcComp0Enable; /**< Start counting on RTC COMP0 match. */ + bool rtcComp1Enable; /**< Start counting on RTC COMP1 match. */ +#endif + bool comp0Top; /**< Load COMP0 register into CNT when counter underflows. */ + bool bufTop; /**< Load COMP1 into COMP0 when REP0 reaches 0. */ + uint8_t out0Pol; /**< Idle value for output 0. */ + uint8_t out1Pol; /**< Idle value for output 1. */ + LETIMER_UFOA_TypeDef ufoa0; /**< Underflow output 0 action. */ + LETIMER_UFOA_TypeDef ufoa1; /**< Underflow output 1 action. */ + LETIMER_RepeatMode_TypeDef repMode; /**< Repeat mode. */ +} LETIMER_Init_TypeDef; + +/** Default config for LETIMER init structure. */ +#if defined(LETIMER_CTRL_RTCC0TEN) +#define LETIMER_INIT_DEFAULT \ +{ \ + true, /* Enable timer when init complete. */ \ + false, /* Stop counter during debug halt. */ \ + false, /* Do not start counting on RTC COMP0 match. */ \ + false, /* Do not start counting on RTC COMP1 match. */ \ + false, /* Do not load COMP0 into CNT on underflow. */ \ + false, /* Do not load COMP1 into COMP0 when REP0 reaches 0. */ \ + 0, /* Idle value 0 for output 0. */ \ + 0, /* Idle value 0 for output 1. */ \ + letimerUFOANone, /* No action on underflow on output 0. */ \ + letimerUFOANone, /* No action on underflow on output 1. */ \ + letimerRepeatFree /* Count until stopped by SW. */ \ +} +#else +#define LETIMER_INIT_DEFAULT \ +{ \ + true, /* Enable timer when init complete. */ \ + false, /* Stop counter during debug halt. */ \ + false, /* Do not load COMP0 into CNT on underflow. */ \ + false, /* Do not load COMP1 into COMP0 when REP0 reaches 0. */ \ + 0, /* Idle value 0 for output 0. */ \ + 0, /* Idle value 0 for output 1. */ \ + letimerUFOANone, /* No action on underflow on output 0. */ \ + letimerUFOANone, /* No action on underflow on output 1. */ \ + letimerRepeatFree /* Count until stopped by SW. */ \ +} +#endif + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp); +void LETIMER_CompareSet(LETIMER_TypeDef *letimer, + unsigned int comp, + uint32_t value); + + +/***************************************************************************//** + * @brief + * Get LETIMER counter value. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @return + * Current LETIMER counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t LETIMER_CounterGet(LETIMER_TypeDef *letimer) +{ + return(letimer->CNT); +} + + +void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable); +#if defined(_LETIMER_FREEZE_MASK) +void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable); +#endif +void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init); + + +/***************************************************************************//** + * @brief + * Clear one or more pending LETIMER interrupts. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] flags + * Pending LETIMER interrupt source to clear. Use a bitwise logic OR + * combination of valid interrupt flags for the LETIMER module + * (LETIMER_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LETIMER_IntClear(LETIMER_TypeDef *letimer, uint32_t flags) +{ + letimer->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more LETIMER interrupts. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] flags + * LETIMER interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the LETIMER module (LETIMER_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LETIMER_IntDisable(LETIMER_TypeDef *letimer, uint32_t flags) +{ + letimer->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more LETIMER interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using LETIMER_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] flags + * LETIMER interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the LETIMER module (LETIMER_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LETIMER_IntEnable(LETIMER_TypeDef *letimer, uint32_t flags) +{ + letimer->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending LETIMER interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @return + * LETIMER interrupt sources pending. A bitwise logic OR combination of + * valid interrupt flags for the LETIMER module (LETIMER_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LETIMER_IntGet(LETIMER_TypeDef *letimer) +{ + return letimer->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending LETIMER interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @return + * Pending and enabled LETIMER interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in LETIMER_IEN_nnn + * register (LETIMER_IEN_nnn) and + * - the OR combination of valid interrupt flags of the LETIMER module + * (LETIMER_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LETIMER_IntGetEnabled(LETIMER_TypeDef *letimer) +{ + uint32_t ien; + + + /* Store flags in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = letimer->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return letimer->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending LETIMER interrupts from SW. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] flags + * LETIMER interrupt sources to set to pending. Use a bitwise logic OR + * combination of valid interrupt flags for the LETIMER module (LETIMER_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LETIMER_IntSet(LETIMER_TypeDef *letimer, uint32_t flags) +{ + letimer->IFS = flags; +} + + +uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep); +void LETIMER_RepeatSet(LETIMER_TypeDef *letimer, + unsigned int rep, + uint32_t value); +void LETIMER_Reset(LETIMER_TypeDef *letimer); + + +/** @} (end addtogroup LETIMER) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_LETIMER_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_leuart.h b/cpu/efm32_common/emlib/inc/em_leuart.h new file mode 100644 index 0000000000000..3c3db543e382b --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_leuart.h @@ -0,0 +1,382 @@ +/***************************************************************************//** + * @file em_leuart.h + * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) + * peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_LEUART_H__ +#define __SILICON_LABS_EM_LEUART_H__ + +#include "em_device.h" +#if defined(LEUART_COUNT) && (LEUART_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LEUART + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Databit selection. */ +typedef enum +{ + leuartDatabits8 = LEUART_CTRL_DATABITS_EIGHT, /**< 8 databits. */ + leuartDatabits9 = LEUART_CTRL_DATABITS_NINE /**< 9 databits. */ +} LEUART_Databits_TypeDef; + + +/** Enable selection. */ +typedef enum +{ + /** Disable both receiver and transmitter. */ + leuartDisable = 0x0, + + /** Enable receiver only, transmitter disabled. */ + leuartEnableRx = LEUART_CMD_RXEN, + + /** Enable transmitter only, receiver disabled. */ + leuartEnableTx = LEUART_CMD_TXEN, + + /** Enable both receiver and transmitter. */ + leuartEnable = (LEUART_CMD_RXEN | LEUART_CMD_TXEN) +} LEUART_Enable_TypeDef; + + +/** Parity selection. */ +typedef enum +{ + leuartNoParity = LEUART_CTRL_PARITY_NONE, /**< No parity. */ + leuartEvenParity = LEUART_CTRL_PARITY_EVEN, /**< Even parity. */ + leuartOddParity = LEUART_CTRL_PARITY_ODD /**< Odd parity. */ +} LEUART_Parity_TypeDef; + + +/** Stopbits selection. */ +typedef enum +{ + leuartStopbits1 = LEUART_CTRL_STOPBITS_ONE, /**< 1 stopbits. */ + leuartStopbits2 = LEUART_CTRL_STOPBITS_TWO /**< 2 stopbits. */ +} LEUART_Stopbits_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Init structure. */ +typedef struct +{ + /** Specifies whether TX and/or RX shall be enabled when init completed. */ + LEUART_Enable_TypeDef enable; + + /** + * LEUART reference clock assumed when configuring baudrate setup. Set + * it to 0 if currently configurated reference clock shall be used. + */ + uint32_t refFreq; + + /** Desired baudrate. */ + uint32_t baudrate; + + /** Number of databits in frame. */ + LEUART_Databits_TypeDef databits; + + /** Parity mode to use. */ + LEUART_Parity_TypeDef parity; + + /** Number of stopbits to use. */ + LEUART_Stopbits_TypeDef stopbits; +} LEUART_Init_TypeDef; + +/** Default config for LEUART init structure. */ +#define LEUART_INIT_DEFAULT \ +{ \ + leuartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 9600, /* 9600 bits/s. */ \ + leuartDatabits8, /* 8 databits. */ \ + leuartNoParity, /* No parity. */ \ + leuartStopbits1 /* 1 stopbit. */ \ +} + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv); +uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart); +void LEUART_BaudrateSet(LEUART_TypeDef *leuart, + uint32_t refFreq, + uint32_t baudrate); +void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable); +void LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable); +void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init); +void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable); +void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable); + +/***************************************************************************//** + * @brief + * Clear one or more pending LEUART interrupts. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] flags + * Pending LEUART interrupt source to clear. Use a bitwise logic OR + * combination of valid interrupt flags for the LEUART module (LEUART_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LEUART_IntClear(LEUART_TypeDef *leuart, uint32_t flags) +{ + leuart->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more LEUART interrupts. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] flags + * LEUART interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the LEUART module (LEUART_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LEUART_IntDisable(LEUART_TypeDef *leuart, uint32_t flags) +{ + leuart->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more LEUART interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using LEUART_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] flags + * LEUART interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the LEUART module (LEUART_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LEUART_IntEnable(LEUART_TypeDef *leuart, uint32_t flags) +{ + leuart->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending LEUART interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * LEUART interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the LEUART module (LEUART_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LEUART_IntGet(LEUART_TypeDef *leuart) +{ + return leuart->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending LEUART interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled LEUART interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in LEUARTx_IEN_nnn + * register (LEUARTx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the LEUART module + * (LEUARTx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t LEUART_IntGetEnabled(LEUART_TypeDef *leuart) +{ + uint32_t tmp; + + /* Store LEUARTx->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + tmp = leuart->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return leuart->IF & tmp; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending LEUART interrupts from SW. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] flags + * LEUART interrupt sources to set to pending. Use a bitwise logic OR + * combination of valid interrupt flags for the LEUART module (LEUART_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void LEUART_IntSet(LEUART_TypeDef *leuart, uint32_t flags) +{ + leuart->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Get LEUART STATUS register. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * STATUS register value. + * + ******************************************************************************/ +__STATIC_INLINE uint32_t LEUART_StatusGet(LEUART_TypeDef *leuart) +{ + return leuart->STATUS; +} + +void LEUART_Reset(LEUART_TypeDef *leuart); +uint8_t LEUART_Rx(LEUART_TypeDef *leuart); +uint16_t LEUART_RxExt(LEUART_TypeDef *leuart); +void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data); +void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data); + + +/***************************************************************************//** + * @brief + * Receive one 8 bit frame, (or part of a 9 bit frame). + * + * @details + * This function is used to quickly receive one 8 bit frame by reading the + * RXDATA register directly, without checking the STATUS register for the + * RXDATAV flag. This can be useful from the RXDATAV interrupt handler, + * i.e. waiting is superfluous, in order to quickly read the received data. + * Please refer to @ref LEUART_RxDataXGet() for reception of 9 bit frames. + * + * @note + * Since this function does not check whether the RXDATA register actually + * holds valid data, it should only be used in situations when it is certain + * that there is valid data, ensured by some external program routine, e.g. + * like when handling an RXDATAV interrupt. The @ref LEUART_Rx() is normally a + * better choice if the validity of the RXDATA register is not certain. + * + * @note + * Notice that possible parity/stop bits are not + * considered part of specified frame bit length. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +__STATIC_INLINE uint8_t LEUART_RxDataGet(LEUART_TypeDef *leuart) +{ + return (uint8_t)leuart->RXDATA; +} + + +/***************************************************************************//** + * @brief + * Receive one 8-9 bit frame, with extended information. + * + * @details + * This function is used to quickly receive one 8-9 bit frame with extended + * information by reading the RXDATAX register directly, without checking the + * STATUS register for the RXDATAV flag. This can be useful from the RXDATAV + * interrupt handler, i.e. waiting is superfluous, in order to quickly read + * the received data. + * + * @note + * Since this function does not check whether the RXDATAX register actually + * holds valid data, it should only be used in situations when it is certain + * that there is valid data, ensured by some external program routine, e.g. + * like when handling an RXDATAV interrupt. The @ref LEUART_RxExt() is normally + * a better choice if the validity of the RXDATAX register is not certain. + * + * @note + * Notice that possible parity/stop bits are not + * considered part of specified frame bit length. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +__STATIC_INLINE uint16_t LEUART_RxDataXGet(LEUART_TypeDef *leuart) +{ + return (uint16_t)leuart->RXDATAX; +} + + +/** @} (end addtogroup LEUART) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_LEUART_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_mpu.h b/cpu/efm32_common/emlib/inc/em_mpu.h new file mode 100644 index 0000000000000..689efa1e08dc6 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_mpu.h @@ -0,0 +1,241 @@ +/***************************************************************************//** + * @file em_mpu.h + * @brief Memory protection unit (MPU) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_MPU_H__ +#define __SILICON_LABS_EM_MPU_H__ + +#include "em_device.h" + +#if defined(__MPU_PRESENT) && (__MPU_PRESENT == 1) +#include "em_assert.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup MPU + * @{ + ******************************************************************************/ + +/** @anchor MPU_CTRL_PRIVDEFENA + * Argument to MPU_enable(). Enables priviledged + * access to default memory map. */ +#define MPU_CTRL_PRIVDEFENA MPU_CTRL_PRIVDEFENA_Msk + +/** @anchor MPU_CTRL_HFNMIENA + * Argument to MPU_enable(). Enables MPU during hard fault, + * NMI, and FAULTMASK handlers. */ +#define MPU_CTRL_HFNMIENA MPU_CTRL_HFNMIENA_Msk + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** + * Size of an MPU region. + */ +typedef enum +{ + mpuRegionSize32b = 4, /**< 32 byte region size. */ + mpuRegionSize64b = 5, /**< 64 byte region size. */ + mpuRegionSize128b = 6, /**< 128 byte region size. */ + mpuRegionSize256b = 7, /**< 256 byte region size. */ + mpuRegionSize512b = 8, /**< 512 byte region size. */ + mpuRegionSize1Kb = 9, /**< 1K byte region size. */ + mpuRegionSize2Kb = 10, /**< 2K byte region size. */ + mpuRegionSize4Kb = 11, /**< 4K byte region size. */ + mpuRegionSize8Kb = 12, /**< 8K byte region size. */ + mpuRegionSize16Kb = 13, /**< 16K byte region size. */ + mpuRegionSize32Kb = 14, /**< 32K byte region size. */ + mpuRegionSize64Kb = 15, /**< 64K byte region size. */ + mpuRegionSize128Kb = 16, /**< 128K byte region size. */ + mpuRegionSize256Kb = 17, /**< 256K byte region size. */ + mpuRegionSize512Kb = 18, /**< 512K byte region size. */ + mpuRegionSize1Mb = 19, /**< 1M byte region size. */ + mpuRegionSize2Mb = 20, /**< 2M byte region size. */ + mpuRegionSize4Mb = 21, /**< 4M byte region size. */ + mpuRegionSize8Mb = 22, /**< 8M byte region size. */ + mpuRegionSize16Mb = 23, /**< 16M byte region size. */ + mpuRegionSize32Mb = 24, /**< 32M byte region size. */ + mpuRegionSize64Mb = 25, /**< 64M byte region size. */ + mpuRegionSize128Mb = 26, /**< 128M byte region size. */ + mpuRegionSize256Mb = 27, /**< 256M byte region size. */ + mpuRegionSize512Mb = 28, /**< 512M byte region size. */ + mpuRegionSize1Gb = 29, /**< 1G byte region size. */ + mpuRegionSize2Gb = 30, /**< 2G byte region size. */ + mpuRegionSize4Gb = 31 /**< 4G byte region size. */ +} MPU_RegionSize_TypeDef; + +/** + * MPU region access permission attributes. + */ +typedef enum +{ + mpuRegionNoAccess = 0, /**< No access what so ever. */ + mpuRegionApPRw = 1, /**< Priviledged state R/W only. */ + mpuRegionApPRwURo = 2, /**< Priviledged state R/W, User state R only. */ + mpuRegionApFullAccess = 3, /**< R/W in Priviledged and User state. */ + mpuRegionApPRo = 5, /**< Priviledged R only. */ + mpuRegionApPRo_URo = 6 /**< R only in Priviledged and User state. */ +} MPU_RegionAp_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** MPU Region init structure. */ +typedef struct +{ + bool regionEnable; /**< MPU region enable. */ + uint8_t regionNo; /**< MPU region number. */ + uint32_t baseAddress; /**< Region baseaddress. */ + MPU_RegionSize_TypeDef size; /**< Memory region size. */ + MPU_RegionAp_TypeDef accessPermission; /**< Memory access permissions. */ + bool disableExec; /**< Disable execution. */ + bool shareable; /**< Memory shareable attribute. */ + bool cacheable; /**< Memory cacheable attribute. */ + bool bufferable; /**< Memory bufferable attribute. */ + uint8_t srd; /**< Memory subregion disable bits. */ + uint8_t tex; /**< Memory type extension attributes. */ +} MPU_RegionInit_TypeDef; + +/** Default configuration of MPU region init structure for flash memory. */ +#define MPU_INIT_FLASH_DEFAULT \ +{ \ + true, /* Enable MPU region. */ \ + 0, /* MPU Region number. */ \ + FLASH_MEM_BASE, /* Flash base address. */ \ + mpuRegionSize1Mb, /* Size - Set to max. */ \ + mpuRegionApFullAccess, /* Access permissions. */ \ + false, /* Execution allowed. */ \ + false, /* Not shareable. */ \ + true, /* Cacheable. */ \ + false, /* Not bufferable. */ \ + 0, /* No subregions. */ \ + 0 /* No TEX attributes. */ \ +} + + +/** Default configuration of MPU region init structure for sram memory. */ +#define MPU_INIT_SRAM_DEFAULT \ +{ \ + true, /* Enable MPU region. */ \ + 1, /* MPU Region number. */ \ + RAM_MEM_BASE, /* SRAM base address. */ \ + mpuRegionSize128Kb, /* Size - Set to max. */ \ + mpuRegionApFullAccess, /* Access permissions. */ \ + false, /* Execution allowed. */ \ + true, /* Shareable. */ \ + true, /* Cacheable. */ \ + false, /* Not bufferable. */ \ + 0, /* No subregions. */ \ + 0 /* No TEX attributes. */ \ +} + + +/** Default configuration of MPU region init structure for onchip peripherals.*/ +#define MPU_INIT_PERIPHERAL_DEFAULT \ +{ \ + true, /* Enable MPU region. */ \ + 0, /* MPU Region number. */ \ + 0, /* Region base address. */ \ + mpuRegionSize32b, /* Size - Set to minimum */ \ + mpuRegionApFullAccess, /* Access permissions. */ \ + true, /* Execution not allowed. */ \ + true, /* Shareable. */ \ + false, /* Not cacheable. */ \ + true, /* Bufferable. */ \ + 0, /* No subregions. */ \ + 0 /* No TEX attributes. */ \ +} + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + + +void MPU_ConfigureRegion(const MPU_RegionInit_TypeDef *init); + + +/***************************************************************************//** + * @brief + * Disable the MPU + * @details + * Disable MPU and MPU fault exceptions. + ******************************************************************************/ +__STATIC_INLINE void MPU_Disable(void) +{ + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; /* Disable fault exceptions */ + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; /* Disable the MPU */ +} + + +/***************************************************************************//** + * @brief + * Enable the MPU + * @details + * Enable MPU and MPU fault exceptions. + * @param[in] flags + * Use a logical OR of @ref MPU_CTRL_PRIVDEFENA and + * @ref MPU_CTRL_HFNMIENA as needed. + ******************************************************************************/ +__STATIC_INLINE void MPU_Enable(uint32_t flags) +{ + EFM_ASSERT(!(flags & ~(MPU_CTRL_PRIVDEFENA_Msk + | MPU_CTRL_HFNMIENA_Msk + | MPU_CTRL_ENABLE_Msk))); + + MPU->CTRL = flags | MPU_CTRL_ENABLE_Msk; /* Enable the MPU */ + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; /* Enable fault exceptions */ +} + + +/** @} (end addtogroup MPU) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(__MPU_PRESENT) && (__MPU_PRESENT == 1) */ + +#endif /* __SILICON_LABS_EM_MPU_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_msc.h b/cpu/efm32_common/emlib/inc/em_msc.h new file mode 100644 index 0000000000000..d0ff8a01d6ecc --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_msc.h @@ -0,0 +1,465 @@ +/***************************************************************************//** + * @file em_msc.h + * @brief Flash controller module (MSC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_MSC_H__ +#define __SILICON_LABS_EM_MSC_H__ + +#include "em_device.h" +#if defined(MSC_COUNT) && (MSC_COUNT > 0) + +#include +#include +#include "em_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup MSC + * @brief Flash controller (MSC) peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ************************* DEFINES ***************************************** + ******************************************************************************/ + +/** + * @brief + * The timeout used while waiting for the flash to become ready after + * a write. This number indicates the number of iterations to perform before + * issuing a timeout. + * @note + * This timeout is set very large (in the order of 100x longer than + * necessary). This is to avoid any corner cases. + * + */ +#define MSC_PROGRAM_TIMEOUT 10000000ul + +/** + * @brief + * By compiling with the define EM_MSC_RUN_FROM_FLASH the Flash + * controller (MSC) peripheral will remain in and execute from flash. + * This is useful for targets that don't want to allocate RAM space to + * hold the flash functions. Without this define the MSC peripheral + * functions will be copied into and run out of RAM. + * @note + * This define is commented out by default so the MSC controller API + * will run from RAM by default. + * + */ +#if defined( DOXY_DOC_ONLY ) +#define EM_MSC_RUN_FROM_FLASH +#else +//#define EM_MSC_RUN_FROM_FLASH +#endif + +/******************************************************************************* + ************************* TYPEDEFS **************************************** + ******************************************************************************/ + +/** Return codes for writing/erasing the flash */ +typedef enum +{ + mscReturnOk = 0, /**< Flash write/erase successful. */ + mscReturnInvalidAddr = -1, /**< Invalid address. Write to an address that is not flash. */ + mscReturnLocked = -2, /**< Flash address is locked. */ + mscReturnTimeOut = -3, /**< Timeout while writing to flash. */ + mscReturnUnaligned = -4 /**< Unaligned access to flash. */ +} MSC_Status_TypeDef; + + +#if defined( _MSC_READCTRL_BUSSTRATEGY_MASK ) +/** Strategy for prioritized bus access */ +typedef enum +{ + mscBusStrategyCPU = MSC_READCTRL_BUSSTRATEGY_CPU, /**< Prioritize CPU bus accesses */ + mscBusStrategyDMA = MSC_READCTRL_BUSSTRATEGY_DMA, /**< Prioritize DMA bus accesses */ + mscBusStrategyDMAEM1 = MSC_READCTRL_BUSSTRATEGY_DMAEM1, /**< Prioritize DMAEM1 for bus accesses */ + mscBusStrategyNone = MSC_READCTRL_BUSSTRATEGY_NONE /**< No unit has bus priority */ +} MSC_BusStrategy_Typedef; +#endif + +/** Code execution configuration */ +typedef struct +{ + bool scbtEn; /**< Enable Suppressed Conditional Branch Target Prefetch */ + bool prefetchEn; /**< Enable MSC prefetching */ + bool ifcDis; /**< Disable instruction cache */ + bool aiDis; /**< Disable automatic cache invalidation on write or erase */ + bool iccDis; /**< Disable automatic caching of fetches in interrupt context */ + bool useHprot; /**< Use ahb_hprot to determine if the instruction is cacheable or not */ +} MSC_ExecConfig_TypeDef; + +/** Default MSC ExecConfig initialization */ +#define MSC_EXECCONFIG_DEFAULT \ +{ \ + false, \ + true, \ + false, \ + false, \ + false, \ + false, \ +} + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/* Legacy type names */ +#define mscBusStrategy_Typedef MSC_BusStrategy_Typedef +#define msc_Return_TypeDef MSC_Status_TypeDef +/** @endcond */ + +/******************************************************************************* + ************************* PROTOTYPES ************************************** + ******************************************************************************/ + +void MSC_Init(void); +void MSC_Deinit(void); +#if !defined( _EFM32_GECKO_FAMILY ) +void MSC_ExecConfigSet(MSC_ExecConfig_TypeDef *execConfig); +#endif + +/***************************************************************************//** + * @brief + * Clear one or more pending MSC interrupts. + * + * @param[in] flags + * Pending MSC intterupt source to clear. Use a bitwise logic OR combination + * of valid interrupt flags for the MSC module (MSC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void MSC_IntClear(uint32_t flags) +{ + MSC->IFC = flags; +} + +/***************************************************************************//** + * @brief + * Disable one or more MSC interrupts. + * + * @param[in] flags + * MSC interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the MSC module (MSC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void MSC_IntDisable(uint32_t flags) +{ + MSC->IEN &= ~(flags); +} + + +/***************************************************************************//** + * @brief + * Enable one or more MSC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using MSC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * MSC interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the MSC module (MSC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void MSC_IntEnable(uint32_t flags) +{ + MSC->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending MSC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * MSC interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the MSC module (MSC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t MSC_IntGet(void) +{ + return(MSC->IF); +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending MSC interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled MSC interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in MSC_IEN and + * - the pending interrupt flags MSC_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t MSC_IntGetEnabled(void) +{ + uint32_t ien; + + ien = MSC->IEN; + return MSC->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending MSC interrupts from SW. + * + * @param[in] flags + * MSC interrupt sources to set to pending. Use a bitwise logic OR combination of + * valid interrupt flags for the MSC module (MSC_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void MSC_IntSet(uint32_t flags) +{ + MSC->IFS = flags; +} + + +#if defined( MSC_IF_CHOF ) && defined( MSC_IF_CMOF ) +/***************************************************************************//** + * @brief + * Starts measuring cache hit ratio. + * @details + * This function starts the performance counters. It is defined inline to + * minimize the impact of this code on the measurement itself. + ******************************************************************************/ +__STATIC_INLINE void MSC_StartCacheMeasurement(void) +{ + /* Clear CMOF and CHOF to catch these later */ + MSC->IFC = MSC_IF_CHOF | MSC_IF_CMOF; + + /* Start performance counters */ +#if defined( _MSC_CACHECMD_MASK ) + MSC->CACHECMD = MSC_CACHECMD_STARTPC; +#else + MSC->CMD = MSC_CMD_STARTPC; +#endif +} + + +/***************************************************************************//** + * @brief + * Stops measuring the hit rate. + * @note + * This function is defined inline to minimize the impact of this + * code on the measurement itself. + * This code only works for relatively short sections of code. If you wish + * to measure longer sections of code you need to implement a IRQ Handler for + * The CHOF and CMOF overflow interrupts. Theses overflows needs to be + * counted and included in the total. + * The functions can then be implemented as follows: + * @verbatim + * volatile uint32_t hitOverflows + * volatile uint32_t missOverflows + * + * void MSC_IRQHandler(void) + * { + * uint32_t flags; + * flags = MSC->IF; + * if (flags & MSC_IF_CHOF) + * { + * MSC->IFC = MSC_IF_CHOF; + * hitOverflows++; + * } + * if (flags & MSC_IF_CMOF) + * { + * MSC->IFC = MSC_IF_CMOF; + * missOverflows++; + * } + * } + * + * void startPerformanceCounters(void) + * { + * hitOverflows = 0; + * missOverflows = 0; + * + * MSC_IntEnable(MSC_IF_CHOF | MSC_IF_CMOF); + * NVIC_EnableIRQ(MSC_IRQn); + * + * MSC_StartCacheMeasurement(); + * } + * @endverbatim + * @return + * Returns -1 if there has been no cache accesses. + * Returns -2 if there has been an overflow in the performance counters. + * If not, it will return the percentage of hits versus misses. + ******************************************************************************/ +__STATIC_INLINE int32_t MSC_GetCacheMeasurement(void) +{ + int32_t total; + /* Stop the counter before computing the hit-rate */ +#if defined( _MSC_CACHECMD_MASK ) + MSC->CACHECMD = MSC_CACHECMD_STOPPC; +#else + MSC->CMD = MSC_CMD_STOPPC; +#endif + + /* Check for overflows in performance counters */ + if (MSC->IF & (MSC_IF_CHOF | MSC_IF_CMOF)) + return -2; + + /* Because the hits and misses are volatile, we need to split this up into + * two statements to avoid a compiler warning regarding the order of volatile + * accesses. */ + total = MSC->CACHEHITS; + total += MSC->CACHEMISSES; + + /* To avoid a division by zero. */ + if (total == 0) + return -1; + + return (MSC->CACHEHITS * 100) / total; +} + + +/***************************************************************************//** + * @brief + * Flush the contents of the instruction cache. + ******************************************************************************/ +__STATIC_INLINE void MSC_FlushCache(void) +{ +#if defined( _MSC_CACHECMD_MASK ) + MSC->CACHECMD = MSC_CACHECMD_INVCACHE; +#else + MSC->CMD = MSC_CMD_INVCACHE; +#endif +} + + +/***************************************************************************//** + * @brief + * Enable or disable instruction cache functionality + * @param[in] enable + * Enable instruction cache. Default is on. + ******************************************************************************/ +__STATIC_INLINE void MSC_EnableCache(bool enable) +{ + BUS_RegBitWrite(&(MSC->READCTRL), _MSC_READCTRL_IFCDIS_SHIFT, !enable); +} + + +#if defined( MSC_READCTRL_ICCDIS ) +/***************************************************************************//** + * @brief + * Enable or disable instruction cache functionality in IRQs + * @param[in] enable + * Enable instruction cache. Default is on. + ******************************************************************************/ +__STATIC_INLINE void MSC_EnableCacheIRQs(bool enable) +{ + BUS_RegBitWrite(&(MSC->READCTRL), _MSC_READCTRL_ICCDIS_SHIFT, !enable); +} +#endif + + +/***************************************************************************//** + * @brief + * Enable or disable instruction cache flushing when writing to flash + * @param[in] enable + * Enable automatic cache flushing. Default is on. + ******************************************************************************/ +__STATIC_INLINE void MSC_EnableAutoCacheFlush(bool enable) +{ + BUS_RegBitWrite(&(MSC->READCTRL), _MSC_READCTRL_AIDIS_SHIFT, !enable); +} +#endif /* defined( MSC_IF_CHOF ) && defined( MSC_IF_CMOF ) */ + + +#if defined( _MSC_READCTRL_BUSSTRATEGY_MASK ) +/***************************************************************************//** + * @brief + * Configure which unit should get priority on system bus. + * @param[in] mode + * Unit to prioritize bus accesses for. + ******************************************************************************/ +__STATIC_INLINE void MSC_BusStrategy(mscBusStrategy_Typedef mode) +{ + MSC->READCTRL = (MSC->READCTRL & ~(_MSC_READCTRL_BUSSTRATEGY_MASK)) | mode; +} +#endif + +#if defined(EM_MSC_RUN_FROM_FLASH) +#define MSC_FUNC_PREFIX +#define MSC_FUNC_POSTFIX +#elif defined(__CC_ARM) +#define MSC_FUNC_PREFIX +#define MSC_FUNC_POSTFIX +#elif defined(__ICCARM__) +#define MSC_FUNC_PREFIX __ramfunc +#define MSC_FUNC_POSTFIX +#elif defined(__GNUC__) && defined(__CROSSWORKS_ARM) +#define MSC_FUNC_PREFIX +#define MSC_FUNC_POSTFIX __attribute__ ((section(".fast"))) +#elif defined(__GNUC__) +#define MSC_FUNC_PREFIX +#define MSC_FUNC_POSTFIX __attribute__ ((section(".ram"))) +#endif + + +MSC_FUNC_PREFIX MSC_Status_TypeDef + MSC_WriteWord(uint32_t *address, + void const *data, + uint32_t numBytes) MSC_FUNC_POSTFIX; + +#if !defined( _EFM32_GECKO_FAMILY ) +MSC_FUNC_PREFIX MSC_Status_TypeDef + MSC_WriteWordFast(uint32_t *address, + void const *data, + uint32_t numBytes) MSC_FUNC_POSTFIX; + +#endif + +MSC_FUNC_PREFIX MSC_Status_TypeDef + MSC_ErasePage(uint32_t *startAddress) MSC_FUNC_POSTFIX; + +#if defined( _MSC_MASSLOCK_MASK ) +MSC_FUNC_PREFIX MSC_Status_TypeDef MSC_MassErase(void) MSC_FUNC_POSTFIX; +#endif + +/** @} (end addtogroup MSC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(MSC_COUNT) && (MSC_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_MSC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_opamp.h b/cpu/efm32_common/emlib/inc/em_opamp.h new file mode 100644 index 0000000000000..6657e058728bf --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_opamp.h @@ -0,0 +1,569 @@ +/**************************************************************************//** + * @file em_opamp.h + * @brief Operational Amplifier (OPAMP) peripheral API + * @version 4.2.1 + ****************************************************************************** + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_OPAMP_H__ +#define __SILICON_LABS_EM_OPAMP_H__ + +#include "em_device.h" +#if defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1) + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "em_dac.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup OPAMP + * @{ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of DAC OPA number for assert statements. */ +#define DAC_OPA_VALID(opa) ((opa) <= OPA2) + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** OPAMP selector values. */ +typedef enum +{ + OPA0 = 0, /**< Select OPA0. */ + OPA1 = 1, /**< Select OPA1. */ + OPA2 = 2 /**< Select OPA2. */ +} OPAMP_TypeDef; + +/** OPAMP negative terminal input selection values. */ +typedef enum +{ + opaNegSelDisable = DAC_OPA0MUX_NEGSEL_DISABLE, /**< Input disabled. */ + opaNegSelUnityGain = DAC_OPA0MUX_NEGSEL_UG, /**< Unity gain feedback path. */ + opaNegSelResTap = DAC_OPA0MUX_NEGSEL_OPATAP, /**< Feedback resistor ladder tap. */ + opaNegSelNegPad = DAC_OPA0MUX_NEGSEL_NEGPAD /**< Negative pad as input. */ +} OPAMP_NegSel_TypeDef; + +/** OPAMP positive terminal input selection values. */ +typedef enum +{ + opaPosSelDisable = DAC_OPA0MUX_POSSEL_DISABLE, /**< Input disabled. */ + opaPosSelDac = DAC_OPA0MUX_POSSEL_DAC, /**< DAC as input (not OPA2). */ + opaPosSelPosPad = DAC_OPA0MUX_POSSEL_POSPAD, /**< Positive pad as input. */ + opaPosSelOpaIn = DAC_OPA0MUX_POSSEL_OPA0INP, /**< Input from OPAx. */ + opaPosSelResTapOpa0 = DAC_OPA0MUX_POSSEL_OPATAP /**< Feedback resistor ladder tap from OPA0. */ +} OPAMP_PosSel_TypeDef; + +/** OPAMP output terminal selection values. */ +typedef enum +{ + opaOutModeDisable = DAC_OPA0MUX_OUTMODE_DISABLE, /**< OPA output disabled. */ + opaOutModeMain = DAC_OPA0MUX_OUTMODE_MAIN, /**< Main output to pin enabled. */ + opaOutModeAlt = DAC_OPA0MUX_OUTMODE_ALT, /**< Alternate output(s) enabled (not OPA2). */ + opaOutModeAll = DAC_OPA0MUX_OUTMODE_ALL /**< Both main and alternate enabled (not OPA2). */ +} OPAMP_OutMode_TypeDef; + +/** OPAMP gain values. */ +typedef enum +{ + opaResSelDefault = DAC_OPA0MUX_RESSEL_DEFAULT, /**< Default value when resistor ladder is unused. */ + opaResSelR2eq0_33R1 = DAC_OPA0MUX_RESSEL_RES0, /**< R2 = 0.33 * R1 */ + opaResSelR2eqR1 = DAC_OPA0MUX_RESSEL_RES1, /**< R2 = R1 */ + opaResSelR1eq1_67R1 = DAC_OPA0MUX_RESSEL_RES2, /**< R2 = 1.67 R1 */ + opaResSelR2eq2R1 = DAC_OPA0MUX_RESSEL_RES3, /**< R2 = 2 * R1 */ + opaResSelR2eq3R1 = DAC_OPA0MUX_RESSEL_RES4, /**< R2 = 3 * R1 */ + opaResSelR2eq4_33R1 = DAC_OPA0MUX_RESSEL_RES5, /**< R2 = 4.33 * R1 */ + opaResSelR2eq7R1 = DAC_OPA0MUX_RESSEL_RES6, /**< R2 = 7 * R1 */ + opaResSelR2eq15R1 = DAC_OPA0MUX_RESSEL_RES7 /**< R2 = 15 * R1 */ +} OPAMP_ResSel_TypeDef; + +/** OPAMP resistor ladder input selector values. */ +typedef enum +{ + opaResInMuxDisable = DAC_OPA0MUX_RESINMUX_DISABLE, /**< Resistor ladder disabled. */ + opaResInMuxOpaIn = DAC_OPA0MUX_RESINMUX_OPA0INP, /**< Input from OPAx. */ + opaResInMuxNegPad = DAC_OPA0MUX_RESINMUX_NEGPAD, /**< Input from negative pad. */ + opaResInMuxPosPad = DAC_OPA0MUX_RESINMUX_POSPAD, /**< Input from positive pad. */ + opaResInMuxVss = DAC_OPA0MUX_RESINMUX_VSS /**< Input connected to Vss. */ +} OPAMP_ResInMux_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** OPAMP init structure. */ +typedef struct +{ + OPAMP_NegSel_TypeDef negSel; /**< Select input source for negative terminal. */ + OPAMP_PosSel_TypeDef posSel; /**< Select input source for positive terminal. */ + OPAMP_OutMode_TypeDef outMode; /**< Output terminal connection. */ + OPAMP_ResSel_TypeDef resSel; /**< Select R2/R1 resistor ratio. */ + OPAMP_ResInMux_TypeDef resInMux; /**< Select input source for resistor ladder. */ + uint32_t outPen; /**< Alternate output enable bit mask. This value + should consist of one or more of the + DAC_OPA[opa#]MUX_OUTPEN_OUT[output#] flags + (defined in \_dac.h) OR'ed together. + @n @n + For OPA0: + @li DAC_OPA0MUX_OUTPEN_OUT0 + @li DAC_OPA0MUX_OUTPEN_OUT1 + @li DAC_OPA0MUX_OUTPEN_OUT2 + @li DAC_OPA0MUX_OUTPEN_OUT3 + @li DAC_OPA0MUX_OUTPEN_OUT4 + + For OPA1: + @li DAC_OPA1MUX_OUTPEN_OUT0 + @li DAC_OPA1MUX_OUTPEN_OUT1 + @li DAC_OPA1MUX_OUTPEN_OUT2 + @li DAC_OPA1MUX_OUTPEN_OUT3 + @li DAC_OPA1MUX_OUTPEN_OUT4 + + For OPA2: + @li DAC_OPA2MUX_OUTPEN_OUT0 + @li DAC_OPA2MUX_OUTPEN_OUT1 + + E.g: @n + init.outPen = DAC_OPA0MUX_OUTPEN_OUT0 | + DAC_OPA0MUX_OUTPEN_OUT2 | + DAC_OPA0MUX_OUTPEN_OUT4; */ + uint32_t bias; /**< Set OPAMP bias current. */ + bool halfBias; /**< Divide OPAMP bias current by 2. */ + bool lpfPosPadDisable; /**< Disable low pass filter on positive pad. */ + bool lpfNegPadDisable; /**< Disable low pass filter on negative pad. */ + bool nextOut; /**< Enable NEXTOUT signal source. */ + bool npEn; /**< Enable positive pad. */ + bool ppEn; /**< Enable negative pad. */ + bool shortInputs; /**< Short OPAMP input terminals. */ + bool hcmDisable; /**< Disable input rail-to-rail capability. */ + bool defaultOffset; /**< Use factory calibrated opamp offset value. */ + uint32_t offset; /**< Opamp offset value when @ref defaultOffset is false.*/ +} OPAMP_Init_TypeDef; + +/** Configuration of OPA0/1 in unity gain voltage follower mode. */ +#define OPA_INIT_UNITY_GAIN \ +{ \ + opaNegSelUnityGain, /* Unity gain. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelDefault, /* Resistor ladder is not used. */ \ + opaResInMuxDisable, /* Resistor ladder disabled. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA2 in unity gain voltage follower mode. */ +#define OPA_INIT_UNITY_GAIN_OPA2 \ +{ \ + opaNegSelUnityGain, /* Unity gain. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelDefault, /* Resistor ladder is not used. */ \ + opaResInMuxDisable, /* Resistor ladder disabled. */ \ + DAC_OPA0MUX_OUTPEN_OUT0, /* Alternate output 0 enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA0/1 in non-inverting amplifier mode. */ +#define OPA_INIT_NON_INVERTING \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eq0_33R1, /* R2 = 1/3 R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + true, /* Neg pad enabled, used as signal ground. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA2 in non-inverting amplifier mode. */ +#define OPA_INIT_NON_INVERTING_OPA2 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eq0_33R1, /* R2 = 1/3 R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + DAC_OPA0MUX_OUTPEN_OUT0, /* Alternate output 0 enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + true, /* Neg pad enabled, used as signal ground. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA0/1 in inverting amplifier mode. */ +#define OPA_INIT_INVERTING \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + true, /* Neg pad enabled, used as signal input. */ \ + true, /* Pos pad enabled, used as signal ground. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA2 in inverting amplifier mode. */ +#define OPA_INIT_INVERTING_OPA2 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + DAC_OPA0MUX_OUTPEN_OUT0, /* Alternate output 0 enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + true, /* Neg pad enabled, used as signal input. */ \ + true, /* Pos pad enabled, used as signal ground. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA0 in cascaded non-inverting amplifier mode. */ +#define OPA_INIT_CASCADED_NON_INVERTING_OPA0 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelR2eq0_33R1, /* R2 = 1/3 R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA1). */ \ + true, /* Neg pad enabled, used as signal ground. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA1 in cascaded non-inverting amplifier mode. */ +#define OPA_INIT_CASCADED_NON_INVERTING_OPA1 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelOpaIn, /* Pos input from OPA0 output. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelR2eq0_33R1, /* R2 = 1/3 R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA2). */ \ + true, /* Neg pad enabled, used as signal ground. */ \ + false, /* Pos pad disabled. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA2 in cascaded non-inverting amplifier mode. */ +#define OPA_INIT_CASCADED_NON_INVERTING_OPA2 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelOpaIn, /* Pos input from OPA1 output. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eq0_33R1, /* R2 = 1/3 R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + DAC_OPA0MUX_OUTPEN_OUT0, /* Alternate output 0 enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + true, /* Neg pad enabled, used as signal ground. */ \ + false, /* Pos pad disabled. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA0 in cascaded inverting amplifier mode. */ +#define OPA_INIT_CASCADED_INVERTING_OPA0 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA1). */ \ + true, /* Neg pad enabled, used as signal input. */ \ + true, /* Pos pad enabled, used as signal ground. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA1 in cascaded inverting amplifier mode. */ +#define OPA_INIT_CASCADED_INVERTING_OPA1 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxOpaIn, /* Resistor ladder input from OPA0. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA2). */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal ground. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA2 in cascaded inverting amplifier mode. */ +#define OPA_INIT_CASCADED_INVERTING_OPA2 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxOpaIn, /* Resistor ladder input from OPA1. */ \ + DAC_OPA0MUX_OUTPEN_OUT0, /* Alternate output 0 enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal ground. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA0 in two-opamp differential driver mode. */ +#define OPA_INIT_DIFF_DRIVER_OPA0 \ +{ \ + opaNegSelUnityGain, /* Unity gain. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelDefault, /* Resistor ladder is not used. */ \ + opaResInMuxDisable, /* Resistor ladder disabled. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA1). */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA1 in two-opamp differential driver mode. */ +#define OPA_INIT_DIFF_DRIVER_OPA1 \ +{ \ + opaNegSelResTap, /* Neg input from resistor ladder tap. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxOpaIn, /* Resistor ladder input from OPA0. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal ground. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA0 in three-opamp differential receiver mode. */ +#define OPA_INIT_DIFF_RECEIVER_OPA0 \ +{ \ + opaNegSelUnityGain, /* Unity gain. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxNegPad, /* Resistor ladder input from neg pad. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA2). */ \ + true, /* Neg pad enabled, used as signal ground. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA1 in three-opamp differential receiver mode. */ +#define OPA_INIT_DIFF_RECEIVER_OPA1 \ +{ \ + opaNegSelUnityGain, /* Unity gain. */ \ + opaPosSelPosPad, /* Pos input from pad. */ \ + opaOutModeAll, /* Both main and alternate outputs. */ \ + opaResSelDefault, /* Resistor ladder is not used. */ \ + opaResInMuxDisable, /* Disable resistor ladder. */ \ + 0, /* No alternate outputs enabled. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + true, /* Pass output to next stage (OPA2). */ \ + false, /* Neg pad disabled. */ \ + true, /* Pos pad enabled, used as signal input. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/** Configuration of OPA2 in three-opamp differential receiver mode. */ +#define OPA_INIT_DIFF_RECEIVER_OPA2 \ +{ \ + opaNegSelResTap, /* Input from resistor ladder tap. */ \ + opaPosSelResTapOpa0, /* Input from OPA0 resistor ladder tap. */ \ + opaOutModeMain, /* Main output enabled. */ \ + opaResSelR2eqR1, /* R2 = R1 */ \ + opaResInMuxOpaIn, /* Resistor ladder input from OPA1. */ \ + DAC_OPA0MUX_OUTPEN_OUT0, /* Enable alternate output 0. */ \ + _DAC_BIASPROG_BIASPROG_DEFAULT, /* Default bias setting. */ \ + _DAC_BIASPROG_HALFBIAS_DEFAULT, /* Default half-bias setting. */ \ + false, /* No low pass filter on pos pad. */ \ + false, /* No low pass filter on neg pad. */ \ + false, /* No nextout output enabled. */ \ + false, /* Neg pad disabled. */ \ + false, /* Pos pad disabled. */ \ + false, /* No shorting of inputs. */ \ + false, /* Rail-to-rail input enabled. */ \ + true, /* Use factory calibrated opamp offset. */ \ + 0 /* Opamp offset value (not used). */ \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void OPAMP_Disable(DAC_TypeDef *dac, OPAMP_TypeDef opa); +void OPAMP_Enable(DAC_TypeDef *dac, OPAMP_TypeDef opa, const OPAMP_Init_TypeDef *init); + +/** @} (end addtogroup OPAMP) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( OPAMP_PRESENT ) && ( OPAMP_COUNT == 1 ) */ +#endif /* __SILICON_LABS_EM_OPAMP_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_part.h b/cpu/efm32_common/emlib/inc/em_part.h new file mode 100644 index 0000000000000..660a9ef0e27dc --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_part.h @@ -0,0 +1,41 @@ +/***************************************************************************//** + * @file em_part.h + * @brief Verify that part specific main header files are supported and included + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_PART_H__ +#define __SILICON_LABS_EM_PART_H__ + +/* This file is kept for backwards compatibility. */ +#warning "Using em_part.h is deprecated. Please use em_device.h instead." + +#include "em_device.h" + +#endif /* __SILICON_LABS_EM_PART_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_pcnt.h b/cpu/efm32_common/emlib/inc/em_pcnt.h new file mode 100644 index 0000000000000..5744aed263bb2 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_pcnt.h @@ -0,0 +1,619 @@ +/***************************************************************************//** + * @file em_pcnt.h + * @brief Pulse Counter (PCNT) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_PCNT_H__ +#define __SILICON_LABS_EM_PCNT_H__ + +#include "em_device.h" +#if defined(PCNT_COUNT) && (PCNT_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup PCNT + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ +/** PCNT0 Counter register size. */ +#if defined(_EFM32_GECKO_FAMILY) +#define PCNT0_CNT_SIZE (8) /* PCNT0 counter is 8 bits. */ +#else +#define PCNT0_CNT_SIZE (16) /* PCNT0 counter is 16 bits. */ +#endif + +#ifdef PCNT1 +/** PCNT1 Counter register size. */ +#define PCNT1_CNT_SIZE (8) /* PCNT1 counter is 8 bits. */ +#endif + +#ifdef PCNT2 +/** PCNT2 Counter register size. */ +#define PCNT2_CNT_SIZE (8) /* PCNT2 counter is 8 bits. */ +#endif + + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Mode selection. */ +typedef enum +{ + /** Disable pulse counter. */ + pcntModeDisable = _PCNT_CTRL_MODE_DISABLE, + + /** Single input LFACLK oversampling mode (available in EM0-EM2). */ + pcntModeOvsSingle = _PCNT_CTRL_MODE_OVSSINGLE, + + /** Externally clocked single input counter mode (available in EM0-EM3). */ + pcntModeExtSingle = _PCNT_CTRL_MODE_EXTCLKSINGLE, + + /** Externally clocked quadrature decoder mode (available in EM0-EM3). */ + pcntModeExtQuad = _PCNT_CTRL_MODE_EXTCLKQUAD, + +#if defined(_PCNT_CTRL_MODE_OVSQUAD1X) + /** LFACLK oversampling quadrature decoder 1X mode (available in EM0-EM2). */ + pcntModeOvsQuad1 = _PCNT_CTRL_MODE_OVSQUAD1X, + + /** LFACLK oversampling quadrature decoder 2X mode (available in EM0-EM2). */ + pcntModeOvsQuad2 = _PCNT_CTRL_MODE_OVSQUAD2X, + + /** LFACLK oversampling quadrature decoder 4X mode (available in EM0-EM2). */ + pcntModeOvsQuad4 = _PCNT_CTRL_MODE_OVSQUAD4X, +#endif +} PCNT_Mode_TypeDef; + + +#if defined(_PCNT_CTRL_CNTEV_MASK) +/** Counter event selection. + * Note: unshifted values are being used for enumeration because multiple + * configuration structure members use this type definition. */ +typedef enum +{ + /** Counts up on up-count and down on down-count events. */ + pcntCntEventBoth = _PCNT_CTRL_CNTEV_BOTH, + + /** Only counts up on up-count events. */ + pcntCntEventUp = _PCNT_CTRL_CNTEV_UP, + + /** Only counts down on down-count events. */ + pcntCntEventDown = _PCNT_CTRL_CNTEV_DOWN, + + /** Never counts. */ + pcntCntEventNone = _PCNT_CTRL_CNTEV_NONE +} PCNT_CntEvent_TypeDef; +#endif + + +#if defined(_PCNT_INPUT_MASK) +/** PRS sources for @p s0PRS and @p s1PRS. */ +typedef enum +{ + pcntPRSCh0 = 0, /**< PRS channel 0. */ + pcntPRSCh1 = 1, /**< PRS channel 1. */ + pcntPRSCh2 = 2, /**< PRS channel 2. */ + pcntPRSCh3 = 3, /**< PRS channel 3. */ +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH4) + pcntPRSCh4 = 4, /**< PRS channel 4. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH5) + pcntPRSCh5 = 5, /**< PRS channel 5. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH6) + pcntPRSCh6 = 6, /**< PRS channel 6. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH7) + pcntPRSCh7 = 7, /**< PRS channel 7. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH8) + pcntPRSCh8 = 8, /**< PRS channel 8. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH9) + pcntPRSCh9 = 9, /**< PRS channel 9. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH10) + pcntPRSCh10 = 10, /**< PRS channel 10. */ +#endif +#if defined(PCNT_INPUT_S0PRSSEL_PRSCH11) + pcntPRSCh11 = 11 /**< PRS channel 11. */ +#endif +} PCNT_PRSSel_TypeDef; + + +/** PRS inputs of PCNT. */ +typedef enum +{ + pcntPRSInputS0 = 0, /** PRS input 0. */ + pcntPRSInputS1 = 1 /** PRS input 1. */ +} PCNT_PRSInput_TypeDef; +#endif + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Init structure. */ +typedef struct +{ + /** Mode to operate in. */ + PCNT_Mode_TypeDef mode; + + /** Initial counter value (refer to reference manual for max value allowed). + * Only used for #pcntModeOvsSingle (and possibly #pcntModeDisable) modes. + * If using #pcntModeExtSingle or #pcntModeExtQuad modes, the counter + * value is reset to HW reset value. */ + uint32_t counter; + + /** Initial top value (refer to reference manual for max value allowed). + * Only used for #pcntModeOvsSingle (and possibly #pcntModeDisable) modes. + * If using #pcntModeExtSingle or #pcntModeExtQuad modes, the top + * value is reset to HW reset value. */ + uint32_t top; + + /** Polarity of incoming edge. + * @li #pcntModeExtSingle mode - if false, positive edges are counted, + * otherwise negative edges. + * @li #pcntModeExtQuad mode - if true, counting direction is inverted. */ + bool negEdge; + + /** Counting direction, only applicable for #pcntModeOvsSingle and + * #pcntModeExtSingle modes. */ + bool countDown; + + /** Enable filter, only available in #pcntModeOvs* modes. */ + bool filter; + +#if defined(PCNT_CTRL_HYST) + /** Set to true to enable hysteresis. When its enabled, the PCNT will always + * overflow and underflow to TOP/2. */ + bool hyst; + + /** Set to true to enable S1 to determine the direction of counting in + * OVSSINGLE or EXTCLKSINGLE modes. @n + * When S1 is high, the count direction is given by CNTDIR, and when S1 is + * low, the count direction is the opposite. */ + bool s1CntDir; + + /** Selects whether the regular counter responds to up-count events, + * down-count events, both or none. */ + PCNT_CntEvent_TypeDef cntEvent; + + /** Selects whether the auxiliary counter responds to up-count events, + * down-count events, both or none. */ + PCNT_CntEvent_TypeDef auxCntEvent; + + /** Select PRS channel as input to S0IN in PCNTx_INPUT register. */ + PCNT_PRSSel_TypeDef s0PRS; + + /** Select PRS channel as input to S1IN in PCNTx_INPUT register. */ + PCNT_PRSSel_TypeDef s1PRS; +#endif +} PCNT_Init_TypeDef; + +#if !defined(PCNT_CTRL_HYST) +/** Default config for PCNT init structure. */ +#define PCNT_INIT_DEFAULT \ +{ \ + pcntModeDisable, /* Disabled by default. */ \ + _PCNT_CNT_RESETVALUE, /* Default counter HW reset value. */ \ + _PCNT_TOP_RESETVALUE, /* Default counter HW reset value. */ \ + false, /* Use positive edge. */ \ + false, /* Up-counting. */ \ + false /* Filter disabled. */ \ +} +#else +/** Default config for PCNT init structure. */ +#define PCNT_INIT_DEFAULT \ +{ \ + pcntModeDisable, /* Disabled by default. */ \ + _PCNT_CNT_RESETVALUE, /* Default counter HW reset value. */ \ + _PCNT_TOP_RESETVALUE, /* Default counter HW reset value. */ \ + false, /* Use positive edge. */ \ + false, /* Up-counting. */ \ + false, /* Filter disabled. */ \ + false, /* Hysteresis disabled. */ \ + true, /* Counter direction is given by CNTDIR. */ \ + pcntCntEventUp, /* Regular counter counts up on upcount events. */ \ + pcntCntEventNone, /* Auxiliary counter doesn't respond to events. */ \ + pcntPRSCh0, /* PRS channel 0 selected as S0IN. */ \ + pcntPRSCh0 /* PRS channel 0 selected as S1IN. */ \ +} +#endif + +#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) +/** Filter initialization structure */ +typedef struct +{ + /** Used only in OVSINGLE and OVSQUAD1X-4X modes. To use this, enable the filter through + * setting filter to true during PCNT_Init(). Filter length = (filtLen + 5) LFACLK cycles. */ + uint8_t filtLen; + + /** When set, removes flutter from Quaddecoder inputs S0IN and S1IN. + * Available only in OVSQUAD1X-4X modes. */ + bool flutterrm; +} PCNT_Filter_TypeDef; +#endif + +/** Default config for PCNT init structure. */ +#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) +#define PCNT_FILTER_DEFAULT \ +{ \ + 0, /* Default length is 5 LFACLK cycles */ \ + false /* No flutter removal */ \ +} +#endif + +#if defined(PCNT_CTRL_TCCMODE_DEFAULT) + +/** Modes for Triggered Compare and Clear module */ +typedef enum +{ + /** Triggered compare and clear not enabled. */ + tccModeDisabled = _PCNT_CTRL_TCCMODE_DISABLED, + + /** Compare and clear performed on each (optionally prescaled) LFA clock cycle. */ + tccModeLFA = _PCNT_CTRL_TCCMODE_LFA, + + /** Compare and clear performed on PRS edges. Polarity defined by prsPolarity. */ + tccModePRS = _PCNT_CTRL_TCCMODE_PRS +} PCNT_TCCMode_TypeDef; + +/** Prescaler values for LFA compare and clear events. Only has effect when TCC mode is LFA. */ +typedef enum +{ + /** Compare and clear event each LFA cycle. */ + tccPrescDiv1 = _PCNT_CTRL_TCCPRESC_DIV1, + + /** Compare and clear event every other LFA cycle. */ + tccPrescDiv2 = _PCNT_CTRL_TCCPRESC_DIV2, + + /** Compare and clear event every 4th LFA cycle. */ + tccPrescDiv4 = _PCNT_CTRL_TCCPRESC_DIV4, + + /** Compare and clear event every 8th LFA cycle. */ + tccPrescDiv8 = _PCNT_CTRL_TCCPRESC_DIV8 +} PCNT_TCCPresc_Typedef; + +/** Compare modes for TCC module */ +typedef enum +{ + /** Compare match if PCNT_CNT is less than, or equal to PCNT_TOP. */ + tccCompLTOE = _PCNT_CTRL_TCCCOMP_LTOE, + + /** Compare match if PCNT_CNT is greater than or equal to PCNT_TOP. */ + tccCompGTOE = _PCNT_CTRL_TCCCOMP_GTOE, + + /** Compare match if PCNT_CNT is less than, or equal to PCNT_TOP[15:8]], and greater + * than, or equal to PCNT_TOP[7:0]. */ + tccCompRange = _PCNT_CTRL_TCCCOMP_RANGE +} PCNT_TCCComp_Typedef; + +/** TCC initialization structure */ +typedef struct +{ + /** Mode to operate in. */ + PCNT_TCCMode_TypeDef mode; + + /** Prescaler value for LFACLK in LFA mode */ + PCNT_TCCPresc_Typedef prescaler; + + /** Choose the event that will trigger a clear */ + PCNT_TCCComp_Typedef compare; + + /** PRS input to TCC module, either for gating the PCNT clock, triggering the TCC comparison, or both. */ + PCNT_PRSSel_TypeDef tccPRS; + + /** TCC PRS input polarity. @n + * False = Rising edge for comparison trigger, and PCNT clock gated when the PRS signal is high. @n + * True = Falling edge for comparison trigger, and PCNT clock gated when the PRS signal is low. */ + bool prsPolarity; + + /** Enable gating PCNT input clock through TCC PRS signal. + * Polarity selection is done through prsPolarity. */ + bool prsGateEnable; +} PCNT_TCC_TypeDef; + +#define PCNT_TCC_DEFAULT \ +{ \ + tccModeDisabled, /* Disabled by default */ \ + tccPrescDiv1, /* Do not prescale LFA clock in LFA mode */ \ + tccCompLTOE, /* Clear when CNT <= TOP */ \ + pcntPRSCh0, /* Select PRS channel 0 as input to TCC */ \ + false, /* PRS polarity is rising edge, and gate when 1 */ \ + false /* Do not gate the PCNT counter input */ \ +} + +#endif +/* defined(PCNT_CTRL_TCCMODE_DEFAULT) */ + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get pulse counter value. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @return + * Current pulse counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t PCNT_CounterGet(PCNT_TypeDef *pcnt) +{ + return pcnt->CNT; +} + +#if defined(_PCNT_AUXCNT_MASK) +/***************************************************************************//** + * @brief + * Get auxiliary counter value. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @return + * Current auxiliary counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t PCNT_AuxCounterGet(PCNT_TypeDef *pcnt) +{ + return pcnt->AUXCNT; +} +#endif + +void PCNT_CounterReset(PCNT_TypeDef *pcnt); +void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top); + +/***************************************************************************//** + * @brief + * Set counter value. + * + * @details + * The pulse counter is disabled while changing counter value, and reenabled + * (if originally enabled) when counter value has been set. + * + * @note + * This function will stall until synchronization to low frequency domain is + * completed. For that reason, it should normally not be used when using + * an external clock to clock the PCNT module, since stall time may be + * undefined in that case. The counter should normally only be set when + * operating in (or about to enable) #pcntModeOvsSingle mode. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] count + * Value to set in counter register. + ******************************************************************************/ +__STATIC_INLINE void PCNT_CounterSet(PCNT_TypeDef *pcnt, uint32_t count) +{ + PCNT_CounterTopSet(pcnt, count, pcnt->TOP); +} + +void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode); +void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable); +void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init); + +#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) +void PCNT_FilterConfiguration(PCNT_TypeDef *pcnt, const PCNT_Filter_TypeDef *config, bool enable); +#endif + +#if defined(_PCNT_INPUT_MASK) +void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt, + PCNT_PRSInput_TypeDef prsInput, + bool enable); +#endif + +#if defined(PCNT_CTRL_TCCMODE_DEFAULT) +void PCNT_TCCConfiguration(PCNT_TypeDef *pcnt, const PCNT_TCC_TypeDef *config); +#endif +/***************************************************************************//** + * @brief + * Clear one or more pending PCNT interrupts. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] flags + * Pending PCNT interrupt source to clear. Use a bitwise logic OR combination + * of valid interrupt flags for the PCNT module (PCNT_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void PCNT_IntClear(PCNT_TypeDef *pcnt, uint32_t flags) +{ + pcnt->IFC = flags; +} + +/***************************************************************************//** + * @brief + * Disable one or more PCNT interrupts. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] flags + * PCNT interrupt sources to disable. Use a bitwise logic OR combination of + * valid interrupt flags for the PCNT module (PCNT_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void PCNT_IntDisable(PCNT_TypeDef *pcnt, uint32_t flags) +{ + pcnt->IEN &= ~flags; +} + +/***************************************************************************//** + * @brief + * Enable one or more PCNT interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using PCNT_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] flags + * PCNT interrupt sources to enable. Use a bitwise logic OR combination of + * valid interrupt flags for the PCNT module (PCNT_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void PCNT_IntEnable(PCNT_TypeDef *pcnt, uint32_t flags) +{ + pcnt->IEN |= flags; +} + +/***************************************************************************//** + * @brief + * Get pending PCNT interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @return + * PCNT interrupt sources pending. A bitwise logic OR combination of valid + * interrupt flags for the PCNT module (PCNT_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t PCNT_IntGet(PCNT_TypeDef *pcnt) +{ + return pcnt->IF; +} + +/***************************************************************************//** + * @brief + * Get enabled and pending PCNT interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @return + * Pending and enabled PCNT interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in PCNT_IEN_nnn + * register (PCNT_IEN_nnn) and + * - the OR combination of valid interrupt flags of the PCNT module + * (PCNT_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t PCNT_IntGetEnabled(PCNT_TypeDef *pcnt) +{ + uint32_t ien; + + + /* Store pcnt->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = pcnt->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return pcnt->IF & ien; +} + +/***************************************************************************//** + * @brief + * Set one or more pending PCNT interrupts from SW. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] flags + * PCNT interrupt sources to set to pending. Use a bitwise logic OR combination + * of valid interrupt flags for the PCNT module (PCNT_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE void PCNT_IntSet(PCNT_TypeDef *pcnt, uint32_t flags) +{ + pcnt->IFS = flags; +} + +void PCNT_Reset(PCNT_TypeDef *pcnt); + +/***************************************************************************//** + * @brief + * Get pulse counter top buffer value. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @return + * Current pulse counter top buffer value. + ******************************************************************************/ +__STATIC_INLINE uint32_t PCNT_TopBufferGet(PCNT_TypeDef *pcnt) +{ + return pcnt->TOPB; +} + +void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val); + +/***************************************************************************//** + * @brief + * Get pulse counter top value. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @return + * Current pulse counter top value. + ******************************************************************************/ +__STATIC_INLINE uint32_t PCNT_TopGet(PCNT_TypeDef *pcnt) +{ + return pcnt->TOP; +} + +void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val); + +/** @} (end addtogroup PCNT) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_PCNT_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_prs.h b/cpu/efm32_common/emlib/inc/em_prs.h new file mode 100644 index 0000000000000..31b71fef757e0 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_prs.h @@ -0,0 +1,130 @@ +/***************************************************************************//** + * @file em_prs.h + * @brief Peripheral Reflex System (PRS) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_PRS_H__ +#define __SILICON_LABS_EM_PRS_H__ + +#include "em_device.h" +#if defined(PRS_COUNT) && (PRS_COUNT > 0) + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup PRS + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Edge detection type. */ +typedef enum +{ + prsEdgeOff = PRS_CH_CTRL_EDSEL_OFF, /**< Leave signal as is. */ + prsEdgePos = PRS_CH_CTRL_EDSEL_POSEDGE, /**< Generate pules on positive edge. */ + prsEdgeNeg = PRS_CH_CTRL_EDSEL_NEGEDGE, /**< Generate pules on negative edge. */ + prsEdgeBoth = PRS_CH_CTRL_EDSEL_BOTHEDGES /**< Generate pules on both edges. */ +} PRS_Edge_TypeDef; + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Set level control bit for one or more channels. + * + * @details + * The level value for a channel is XORed with both the pulse possible issued + * by PRS_PulseTrigger() and the PRS input signal selected for the channel(s). + * + * @param[in] level + * Level to use for channels indicated by @p mask. Use logical OR combination + * of PRS_SWLEVEL_CHnLEVEL defines for channels to set high level, otherwise 0. + * + * @param[in] mask + * Mask indicating which channels to set level for. Use logical OR combination + * of PRS_SWLEVEL_CHnLEVEL defines. + ******************************************************************************/ +__STATIC_INLINE void PRS_LevelSet(uint32_t level, uint32_t mask) +{ + PRS->SWLEVEL = (PRS->SWLEVEL & ~mask) | (level & mask); +} + + +/***************************************************************************//** + * @brief + * Trigger a high pulse (one HFPERCLK) for one or more channels. + * + * @details + * Setting a bit for a channel causes the bit in the register to remain high + * for one HFPERCLK cycle. The pulse is XORed with both the corresponding bit + * in PRS SWLEVEL register and the PRS input signal selected for the + * channel(s). + * + * @param[in] channels + * Logical ORed combination of channels to trigger a pulse for. Use + * PRS_SWPULSE_CHnPULSE defines. + ******************************************************************************/ +__STATIC_INLINE void PRS_PulseTrigger(uint32_t channels) +{ + PRS->SWPULSE = channels & _PRS_SWPULSE_MASK; +} + +void PRS_SourceSignalSet(unsigned int ch, + uint32_t source, + uint32_t signal, + PRS_Edge_TypeDef edge); + +#if defined( PRS_CH_CTRL_ASYNC ) +void PRS_SourceAsyncSignalSet(unsigned int ch, + uint32_t source, + uint32_t signal); +#endif + +/** @} (end addtogroup PRS) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(PRS_COUNT) && (PRS_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_PRS_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_rmu.h b/cpu/efm32_common/emlib/inc/em_rmu.h new file mode 100644 index 0000000000000..16b6929bacf55 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_rmu.h @@ -0,0 +1,150 @@ +/***************************************************************************//** + * @file em_rmu.h + * @brief Reset Management Unit (RMU) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_RMU_H__ +#define __SILICON_LABS_EM_RMU_H__ + +#include "em_device.h" +#if defined(RMU_COUNT) && (RMU_COUNT > 0) +#include "em_assert.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup RMU + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** RMU reset modes */ +typedef enum +{ +#if defined(_RMU_CTRL_PINRMODE_MASK) + rmuResetModeDisabled = _RMU_CTRL_PINRMODE_DISABLED, + rmuResetModeLimited = _RMU_CTRL_PINRMODE_LIMITED, + rmuResetModeExtended = _RMU_CTRL_PINRMODE_EXTENDED, + rmuResetModeFull = _RMU_CTRL_PINRMODE_FULL, +#else + rmuResetModeClear = 0, + rmuResetModeSet = 1, +#endif +} RMU_ResetMode_TypeDef; + +/** RMU controlled peripheral reset control and reset source control */ +typedef enum +{ +#if defined(RMU_CTRL_BURSTEN) + rmuResetBU = _RMU_CTRL_BURSTEN_MASK, /**< Reset control over Backup Power domain select */ +#endif +#if defined(RMU_CTRL_LOCKUPRDIS) + rmuResetLockUp = _RMU_CTRL_LOCKUPRDIS_MASK, /**< Cortex lockup reset select */ +#elif defined(_RMU_CTRL_LOCKUPRMODE_MASK) + rmuResetLockUp = _RMU_CTRL_LOCKUPRMODE_MASK, /**< Cortex lockup reset select */ +#endif +#if defined(_RMU_CTRL_WDOGRMODE_MASK) + rmuResetWdog = _RMU_CTRL_WDOGRMODE_MASK, /**< WDOG reset select */ +#endif +#if defined(_RMU_CTRL_LOCKUPRMODE_MASK) + rmuResetCoreLockup = _RMU_CTRL_LOCKUPRMODE_MASK, /**< Cortex lockup reset select */ +#endif +#if defined(_RMU_CTRL_SYSRMODE_MASK) + rmuResetSys = _RMU_CTRL_SYSRMODE_MASK, /**< SYSRESET select */ +#endif +#if defined(_RMU_CTRL_PINRMODE_MASK) + rmuResetPin = _RMU_CTRL_PINRMODE_MASK, /**< Pin reset select */ +#endif +} RMU_Reset_TypeDef; + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/** RMU_LockupResetDisable kept for backwards compatibility */ +#define RMU_LockupResetDisable(A) RMU_ResetControl(rmuResetLockUp, A) + +void RMU_ResetControl(RMU_Reset_TypeDef reset, RMU_ResetMode_TypeDef mode); +void RMU_ResetCauseClear(void); +uint32_t RMU_ResetCauseGet(void); + +#if defined(_RMU_CTRL_RESETSTATE_MASK) +/***************************************************************************//** + * @brief + * Set user reset state. This state is reset only by a Power-on-reset and a + * pin reset. + * + * @param[in] userState User state to set + ******************************************************************************/ +__STATIC_INLINE void RMU_UserResetStateSet(uint32_t userState) +{ + EFM_ASSERT(!(userState + & ~(_RMU_CTRL_RESETSTATE_MASK >> _RMU_CTRL_RESETSTATE_SHIFT))); + RMU->CTRL = (RMU->CTRL & ~_RMU_CTRL_RESETSTATE_MASK) + | (userState << _RMU_CTRL_RESETSTATE_SHIFT); +} + +/***************************************************************************//** + * @brief + * Get user reset state. This state is reset only by a Power-on-reset and a + * pin reset. + * + * @return + * Reset surviving user state + ******************************************************************************/ +__STATIC_INLINE uint32_t RMU_UserResetStateGet(void) +{ + uint32_t userState = (RMU->CTRL & _RMU_CTRL_RESETSTATE_MASK) + >> _RMU_CTRL_RESETSTATE_SHIFT; + return userState; +} +#endif + +/** @} (end addtogroup RMU) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(RMU_COUNT) && (RMU_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_RMU_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_rtc.h b/cpu/efm32_common/emlib/inc/em_rtc.h new file mode 100644 index 0000000000000..5a4a79e29cf87 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_rtc.h @@ -0,0 +1,214 @@ +/***************************************************************************//** + * @file em_rtc.h + * @brief Real Time Counter (RTC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_RTC_H__ +#define __SILICON_LABS_EM_RTC_H__ + +#include "em_device.h" +#if defined(RTC_COUNT) && (RTC_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup RTC + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** RTC initialization structure. */ +typedef struct +{ + bool enable; /**< Start counting when init completed. */ + bool debugRun; /**< Counter shall keep running during debug halt. */ + bool comp0Top; /**< Use compare register 0 as max count value. */ +} RTC_Init_TypeDef; + +/** Suggested default config for RTC init structure. */ +#define RTC_INIT_DEFAULT \ +{ \ + true, /* Start counting when init done */ \ + false, /* Disable updating during debug halt */ \ + true /* Restart counting from 0 when reaching COMP0 */ \ +} + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +uint32_t RTC_CompareGet(unsigned int comp); +void RTC_CompareSet(unsigned int comp, uint32_t value); + +/***************************************************************************//** + * @brief + * Get RTC counter value. + * + * @return + * Current RTC counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTC_CounterGet(void) +{ + return RTC->CNT; +} + +void RTC_CounterReset(void); +void RTC_Enable(bool enable); +void RTC_FreezeEnable(bool enable); +void RTC_Init(const RTC_Init_TypeDef *init); + +/***************************************************************************//** + * @brief + * Clear one or more pending RTC interrupts. + * + * @param[in] flags + * RTC interrupt sources to clear. Use a set of interrupt flags OR-ed + * together to clear multiple interrupt sources for the RTC module + * (RTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void RTC_IntClear(uint32_t flags) +{ + RTC->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more RTC interrupts. + * + * @param[in] flags + * RTC interrupt sources to disable. Use a set of interrupt flags OR-ed + * together to disable multiple interrupt sources for the RTC module + * (RTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void RTC_IntDisable(uint32_t flags) +{ + RTC->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more RTC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using RTC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * RTC interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to set multiple interrupt sources for the RTC module + * (RTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void RTC_IntEnable(uint32_t flags) +{ + RTC->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending RTC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending RTC interrupt sources. Returns a set of interrupt flags OR-ed + * together for multiple interrupt sources in the RTC module (RTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t RTC_IntGet(void) +{ + return RTC->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending RTC interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled RTC interrupt sources + * The return value is the bitwise AND of + * - the enabled interrupt sources in RTC_IEN and + * - the pending interrupt flags RTC_IF + ******************************************************************************/ +__STATIC_INLINE uint32_t RTC_IntGetEnabled(void) +{ + uint32_t ien; + + ien = RTC->IEN; + return RTC->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending RTC interrupts from SW. + * + * @param[in] flags + * RTC interrupt sources to set to pending. Use a set of interrupt flags + * OR-ed together to set multiple interrupt sources for the RTC module + * (RTC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void RTC_IntSet(uint32_t flags) +{ + RTC->IFS = flags; +} + +void RTC_Reset(void); + +/** @} (end addtogroup RTC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(RTC_COUNT) && (RTC_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_RTC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_rtcc.h b/cpu/efm32_common/emlib/inc/em_rtcc.h new file mode 100644 index 0000000000000..72ca247a20395 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_rtcc.h @@ -0,0 +1,696 @@ +/***************************************************************************//** + * @file + * @brief Real Time Counter (RTCC) peripheral API. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_RTCC_H__ +#define __SILICON_LABS_EM_RTCC_H__ + +#include "em_device.h" +#if defined( RTCC_COUNT ) && ( RTCC_COUNT == 1 ) + +#include +#include "em_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup RTCC + * @{ + ******************************************************************************/ + +/******************************************************************************* + ********************************* ENUM ************************************ + ******************************************************************************/ + +/** Operational mode of the counter. */ +typedef enum +{ + /** Normal counter mode. The counter is incremented by 1 for each tick. */ + rtccCntModeNormal = _RTCC_CTRL_CNTTICK_PRESC, + + /** Calendar mode. Refer to the RTCC chapter of the Reference Manual for more + * details on the calendar mode. */ + rtccCntModeCalendar = _RTCC_CTRL_CNTTICK_CCV0MATCH +} RTCC_CntMode_TypeDef; + +/** Counter prescaler selection. */ +typedef enum +{ + rtccCntPresc_1 = _RTCC_CTRL_CNTPRESC_DIV1, /**< Divide clock by 1. */ + rtccCntPresc_2 = _RTCC_CTRL_CNTPRESC_DIV2, /**< Divide clock by 2. */ + rtccCntPresc_4 = _RTCC_CTRL_CNTPRESC_DIV4, /**< Divide clock by 4. */ + rtccCntPresc_8 = _RTCC_CTRL_CNTPRESC_DIV8, /**< Divide clock by 8. */ + rtccCntPresc_16 = _RTCC_CTRL_CNTPRESC_DIV16, /**< Divide clock by 16. */ + rtccCntPresc_32 = _RTCC_CTRL_CNTPRESC_DIV32, /**< Divide clock by 32. */ + rtccCntPresc_64 = _RTCC_CTRL_CNTPRESC_DIV64, /**< Divide clock by 64. */ + rtccCntPresc_128 = _RTCC_CTRL_CNTPRESC_DIV128, /**< Divide clock by 128. */ + rtccCntPresc_256 = _RTCC_CTRL_CNTPRESC_DIV256, /**< Divide clock by 256. */ + rtccCntPresc_512 = _RTCC_CTRL_CNTPRESC_DIV512, /**< Divide clock by 512. */ + rtccCntPresc_1024 = _RTCC_CTRL_CNTPRESC_DIV1024, /**< Divide clock by 1024. */ + rtccCntPresc_2048 = _RTCC_CTRL_CNTPRESC_DIV2048, /**< Divide clock by 2048. */ + rtccCntPresc_4096 = _RTCC_CTRL_CNTPRESC_DIV4096, /**< Divide clock by 4096. */ + rtccCntPresc_8192 = _RTCC_CTRL_CNTPRESC_DIV8192, /**< Divide clock by 8192. */ + rtccCntPresc_16384 = _RTCC_CTRL_CNTPRESC_DIV16384, /**< Divide clock by 16384. */ + rtccCntPresc_32768 = _RTCC_CTRL_CNTPRESC_DIV32768 /**< Divide clock by 32768. */ +} RTCC_CntPresc_TypeDef; + + +/** Prescaler mode of the RTCC counter. */ +typedef enum +{ + /** CNT register ticks according to the prescaler value. */ + rtccCntTickPresc = _RTCC_CTRL_CNTTICK_PRESC, + + /** CNT register ticks when PRECNT matches the 15 least significant bits of + * ch. 0 CCV register. */ + rtccCntTickCCV0Match = _RTCC_CTRL_CNTTICK_CCV0MATCH +} RTCC_PrescMode_TypeDef; + + +/** Capture/Compare channel mode. */ +typedef enum +{ + rtccCapComChModeOff = _RTCC_CC_CTRL_MODE_OFF, /**< Capture/Compare channel turned off. */ + rtccCapComChModeCapture = _RTCC_CC_CTRL_MODE_INPUTCAPTURE, /**< Capture mode. */ + rtccCapComChModeCompare = _RTCC_CC_CTRL_MODE_OUTPUTCOMPARE, /**< Compare mode. */ +} RTCC_CapComChMode_TypeDef; + +/** Compare match output action mode. */ +typedef enum +{ + rtccCompMatchOutActionPulse = _RTCC_CC_CTRL_CMOA_PULSE, /**< Generate a pulse. */ + rtccCompMatchOutActionToggle = _RTCC_CC_CTRL_CMOA_TOGGLE, /**< Toggle output. */ + rtccCompMatchOutActionClear = _RTCC_CC_CTRL_CMOA_CLEAR, /**< Clear output. */ + rtccCompMatchOutActionSet = _RTCC_CC_CTRL_CMOA_SET /**< Set output. */ +} RTCC_CompMatchOutAction_TypeDef; + + +/** PRS input sources. */ +typedef enum +{ + rtccPRSCh0 = _RTCC_CC_CTRL_PRSSEL_PRSCH0, /**< PRS channel 0. */ + rtccPRSCh1 = _RTCC_CC_CTRL_PRSSEL_PRSCH1, /**< PRS channel 1. */ + rtccPRSCh2 = _RTCC_CC_CTRL_PRSSEL_PRSCH2, /**< PRS channel 2. */ + rtccPRSCh3 = _RTCC_CC_CTRL_PRSSEL_PRSCH3, /**< PRS channel 3. */ + rtccPRSCh4 = _RTCC_CC_CTRL_PRSSEL_PRSCH4, /**< PRS channel 4. */ + rtccPRSCh5 = _RTCC_CC_CTRL_PRSSEL_PRSCH5, /**< PRS channel 5. */ + rtccPRSCh6 = _RTCC_CC_CTRL_PRSSEL_PRSCH6, /**< PRS channel 6. */ + rtccPRSCh7 = _RTCC_CC_CTRL_PRSSEL_PRSCH7, /**< PRS channel 7. */ + rtccPRSCh8 = _RTCC_CC_CTRL_PRSSEL_PRSCH8, /**< PRS channel 8. */ + rtccPRSCh9 = _RTCC_CC_CTRL_PRSSEL_PRSCH9, /**< PRS channel 9. */ + rtccPRSCh10 = _RTCC_CC_CTRL_PRSSEL_PRSCH10, /**< PRS channel 10. */ + rtccPRSCh11 = _RTCC_CC_CTRL_PRSSEL_PRSCH11 /**< PRS channel 11. */ +} RTCC_PRSSel_TypeDef; + + +/** Input edge select. */ +typedef enum +{ + rtccInEdgeRising = _RTCC_CC_CTRL_ICEDGE_RISING, /**< Rising edges detected. */ + rtccInEdgeFalling = _RTCC_CC_CTRL_ICEDGE_FALLING, /**< Falling edges detected. */ + rtccInEdgeBoth = _RTCC_CC_CTRL_ICEDGE_BOTH, /**< Both edges detected. */ + rtccInEdgeNone = _RTCC_CC_CTRL_ICEDGE_NONE /**< No edge detection, signal is left as is. */ +} RTCC_InEdgeSel_TypeDef; + + +/** Capture/Compare channel compare mode. */ +typedef enum +{ + /** CCVx is compared with the CNT register. */ + rtccCompBaseCnt = _RTCC_CC_CTRL_COMPBASE_CNT, + + /** CCVx is compared with a CNT[16:0] and PRECNT[14:0]. */ + rtccCompBasePreCnt = _RTCC_CC_CTRL_COMPBASE_PRECNT +} RTCC_CompBase_TypeDef; + + /** Day compare mode. */ +typedef enum +{ + rtccDayCompareModeMonth = _RTCC_CC_CTRL_DAYCC_MONTH, /**< Day of month is selected for Capture/Compare. */ + rtccDayCompareModeWeek = _RTCC_CC_CTRL_DAYCC_WEEK /**< Day of week is selected for Capture/Compare. */ +} RTCC_DayCompareMode_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** RTCC initialization structure. */ +typedef struct +{ + /** Enable/disable counting when initialization is completed. */ + bool enable; + + /** Enable/disable timer counting during debug halt. */ + bool debugRun; + + /** Enable/disable precounter wrap on ch. 0 CCV value. */ + bool precntWrapOnCCV0; + + /** Enable/disable counter wrap on ch. 1 CCV value. */ + bool cntWrapOnCCV1; + + /** Counter prescaler. */ + RTCC_CntPresc_TypeDef presc; + + /** Prescaler mode. */ + RTCC_PrescMode_TypeDef prescMode; + +#if defined(_RTCC_CTRL_BUMODETSEN_MASK) + /** Enable/disable storing RTCC counter value in RTCC_CCV2 upon backup mode + * entry. */ + bool enaBackupModeSet; +#endif + + /** Enable/disable the check that sets the OSCFFAIL interrupt flag if no + * LFCLK-RTCC ticks are detected within one ULFRCO cycles. */ + bool enaOSCFailDetect; + + /** Select the operational mode of the counter. */ + RTCC_CntMode_TypeDef cntMode; + + /** Disable leap year correction for the calendar mode. When this parameter is + * set to false, February has 29 days if (year % 4 == 0). If true, February + * always has 28 days. */ + bool disLeapYearCorr; +} RTCC_Init_TypeDef; + + +/** RTCC capture/compare channel configuration structure. */ +typedef struct +{ + /** Select the mode of the Capture/Compare channel. */ + RTCC_CapComChMode_TypeDef chMode; + + /** Compare mode channel match output action. */ + RTCC_CompMatchOutAction_TypeDef compMatchOutAction; + + /** Capture mode channel PRS input channel selection. */ + RTCC_PRSSel_TypeDef prsSel; + + /** Capture mode channel input edge selection. */ + RTCC_InEdgeSel_TypeDef inputEdgeSel; + + /** Comparison base of the channel in compare mode. */ + RTCC_CompBase_TypeDef compBase; + + /** The COMPMASK (5 bit) most significant bits of the compare value will not + * be subject to comparison. */ + uint8_t compMask; + + /** Day compare mode. */ + RTCC_DayCompareMode_TypeDef dayCompMode; +} RTCC_CCChConf_TypeDef; + + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** Default RTCC init structure. */ +#if defined(_RTCC_CTRL_BUMODETSEN_MASK) +#define RTCC_INIT_DEFAULT \ +{ \ + true, /* Start counting when init done. */ \ + false, /* Disable RTCC during debug halt. */ \ + false, /* Disable precounter wrap on ch. 0 CCV value. */ \ + false, /* Disable counter wrap on ch. 1 CCV value. */ \ + rtccCntPresc_32, /* 977 us per tick. */ \ + rtccCntTickPresc, /* Counter increments according to prescaler value. */ \ + false, /* No RTCC storage on backup mode entry. */ \ + false, /* No RTCC oscillator failure detection. */ \ + rtccCntModeNormal, /* Normal RTCC mode. */ \ + false, /* No leap year correction. */ \ +} +#else +#define RTCC_INIT_DEFAULT \ +{ \ + true, /* Start counting when init done. */ \ + false, /* Disable RTCC during debug halt. */ \ + false, /* Disable precounter wrap on ch. 0 CCV value. */ \ + false, /* Disable counter wrap on ch. 1 CCV value. */ \ + rtccCntPresc_32, /* 977 us per tick. */ \ + rtccCntTickPresc, /* Counter increments according to prescaler value. */ \ + false, /* No RTCC oscillator failure detection. */ \ + rtccCntModeNormal, /* Normal RTCC mode. */ \ + false, /* No leap year correction. */ \ +} +#endif + +/** Default RTCC channel output compare init structure. */ +#define RTCC_CH_INIT_COMPARE_DEFAULT \ +{ \ + rtccCapComChModeCompare, /* Select output compare mode. */ \ + rtccCompMatchOutActionPulse, /* Create pulse on compare match. */ \ + rtccPRSCh0, /* PRS channel 0 (not used). */ \ + rtccInEdgeNone, /* No edge detection. */ \ + rtccCompBaseCnt, /* Counter comparison base. */ \ + 0, /* No compare mask bits set. */ \ + rtccDayCompareModeMonth /* Don't care */ \ +} + +/** Default RTCC channel input capture init structure. */ +#define RTCC_CH_INIT_CAPTURE_DEFAULT \ +{ \ + rtccCapComChModeCapture, /* Select input capture mode. */ \ + rtccCompMatchOutActionPulse, /* Create pulse on capture. */ \ + rtccPRSCh0, /* PRS channel 0. */ \ + rtccInEdgeRising, /* Rising edge detection. */ \ + rtccCompBaseCnt, /* Don't care. */ \ + 0, /* Don't care. */ \ + rtccDayCompareModeMonth /* Don't care */ \ +} + +/** Validation of valid RTCC channel for assert statements. */ +#define RTCC_CH_VALID( ch ) ( ( ch ) < 3 ) + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get RTCC capture/compare register value (CCV) for selected channel. + * + * @param[in] ch + * Channel selector. + * + * @return + * Capture/compare register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_ChannelCCVGet( int ch ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + return RTCC->CC[ ch ].CCV; +} + +/***************************************************************************//** + * @brief + * Set RTCC capture/compare register value (CCV) for selected channel. + * + * @param[in] ch + * Channel selector. + * + * @param[in] value + * CCV value. + ******************************************************************************/ +__STATIC_INLINE void RTCC_ChannelCCVSet( int ch, uint32_t value ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + RTCC->CC[ ch ].CCV = value; +} + +/***************************************************************************//** + * @brief + * Get the calendar DATE register content for selected channel. + * + * @param[in] ch + * Channel selector. + * + * @return + * DATE register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_ChannelDateGet( int ch ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + return RTCC->CC[ ch ].DATE; +} + +/***************************************************************************//** + * @brief + * Set the calendar DATE register for selected channel. + * + * @param[in] ch + * Channel selector. + * + * @param[in] date + * DATE value. + ******************************************************************************/ +__STATIC_INLINE void RTCC_ChannelDateSet( int ch, uint32_t date ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + RTCC->CC[ ch ].DATE = date; +} + +void RTCC_ChannelInit( int ch, RTCC_CCChConf_TypeDef const *confPtr ); + +/***************************************************************************//** + * @brief + * Get the calendar TIME register content for selected channel. + * + * @param[in] ch + * Channel selector. + * + * @return + * TIME register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_ChannelTimeGet( int ch ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + return RTCC->CC[ ch ].TIME; +} + +/***************************************************************************//** + * @brief + * Set the calendar TIME register for selected channel. + * + * @param[in] ch + * Channel selector. + * + * @param[in] time + * TIME value. + ******************************************************************************/ +__STATIC_INLINE void RTCC_ChannelTimeSet( int ch, uint32_t time ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + RTCC->CC[ ch ].TIME = time; +} + +/***************************************************************************//** + * @brief + * Get the combined CNT/PRECNT register content. + * + * @return + * CNT/PRECNT register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_CombinedCounterGet( void ) +{ + return RTCC->COMBCNT; +} + +/***************************************************************************//** + * @brief + * Get RTCC counter value. + * + * @return + * Current RTCC counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_CounterGet( void ) +{ + return RTCC->CNT; +} + +/***************************************************************************//** + * @brief + * Set RTCC CNT counter. + * + * @param[in] value + * CNT value. + ******************************************************************************/ +__STATIC_INLINE void RTCC_CounterSet( uint32_t value ) +{ + RTCC->CNT = value; +} + +/***************************************************************************//** + * @brief + * Get DATE register value. + * + * @return + * Current DATE register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_DateGet( void ) +{ + return RTCC->DATE; +} + +/***************************************************************************//** + * @brief + * Set RTCC DATE register. + * + * @param[in] date + * DATE value. + ******************************************************************************/ +__STATIC_INLINE void RTCC_DateSet( uint32_t date ) +{ + RTCC->DATE = date; +} + +/***************************************************************************//** + * @brief + * Enable/disable EM4 wakeup capability. + * + * @param[in] enable + * True to enable EM4 wakeup, false to disable. + ******************************************************************************/ +__STATIC_INLINE void RTCC_EM4WakeupEnable( bool enable ) +{ + if ( enable ) + { + RTCC->EM4WUEN = RTCC_EM4WUEN_EM4WU; + } + else + { + RTCC->EM4WUEN = 0; + } +} + +void RTCC_Enable( bool enable ); + +void RTCC_Init( const RTCC_Init_TypeDef *init ); + +/***************************************************************************//** + * @brief + * Clear one or more pending RTCC interrupts. + * + * @param[in] flags + * RTCC interrupt sources to clear. Use a set of interrupt flags OR-ed + * together to clear multiple interrupt sources. + ******************************************************************************/ +__STATIC_INLINE void RTCC_IntClear( uint32_t flags ) +{ + RTCC->IFC = flags; +} + +/***************************************************************************//** + * @brief + * Disable one or more RTCC interrupts. + * + * @param[in] flags + * RTCC interrupt sources to disable. Use a set of interrupt flags OR-ed + * together to disable multiple interrupt. + ******************************************************************************/ +__STATIC_INLINE void RTCC_IntDisable( uint32_t flags ) +{ + RTCC->IEN &= ~flags; +} + +/***************************************************************************//** + * @brief + * Enable one or more RTCC interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using RTCC_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] flags + * RTCC interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to set multiple interrupt. + ******************************************************************************/ +__STATIC_INLINE void RTCC_IntEnable( uint32_t flags ) +{ + RTCC->IEN |= flags; +} + +/***************************************************************************//** + * @brief + * Get pending RTCC interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending RTCC interrupt sources. Returns a set of interrupt flags OR-ed + * together for the interrupt sources set. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_IntGet( void ) +{ + return RTCC->IF; +} + +/***************************************************************************//** + * @brief + * Get enabled and pending RTCC interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @return + * Pending and enabled RTCC interrupt sources. Returns a set of interrupt + * flags OR-ed together for the interrupt sources set. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_IntGetEnabled( void ) +{ + uint32_t tmp; + + tmp = RTCC->IEN; + + /* Bitwise AND of pending and enabled interrupt flags. */ + return RTCC->IF & tmp; +} + +/***************************************************************************//** + * @brief + * Set one or more pending RTCC interrupts from SW. + * + * @param[in] flags + * RTCC interrupt sources to set to pending. Use a set of interrupt flags + * (RTCC_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void RTCC_IntSet( uint32_t flags ) +{ + RTCC->IFS = flags; +} + +/***************************************************************************//** + * @brief + * Lock RTCC registers. + * + * @note + * When RTCC registers are locked, RTCC_CTRL, RTCC_PRECNT, RTCC_CNT, + * RTCC_TIME, RTCC_DATE, RTCC_IEN, RTCC_POWERDOWN and RTCC_CCx_XXX registers + * can not be written to. + ******************************************************************************/ +__STATIC_INLINE void RTCC_Lock( void ) +{ + RTCC->LOCK = RTCC_LOCK_LOCKKEY_LOCK; +} + +/***************************************************************************//** + * @brief + * Get RTCC pre-counter value. + * + * @return + * Current RTCC pre-counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_PreCounterGet( void ) +{ + return RTCC->PRECNT; +} + +/***************************************************************************//** + * @brief + * Set RTCC pre-counter value. + * + * @param[in] preCntVal + * RTCC pre-counter value to be set. + ******************************************************************************/ +__STATIC_INLINE void RTCC_PreCounterSet( uint32_t preCntVal ) +{ + RTCC->PRECNT = preCntVal; +} + +void RTCC_Reset( void ); + +/***************************************************************************//** + * @brief + * Power down the retention ram. + * + * @note + * Once retention ram is powered down, it cannot be powered up again. + ******************************************************************************/ +__STATIC_INLINE void RTCC_RetentionRamPowerDown( void ) +{ + RTCC->POWERDOWN = RTCC_POWERDOWN_RAM; +} + +void RTCC_StatusClear( void ); + +/***************************************************************************//** + * @brief + * Get STATUS register value. + * + * @return + * Current STATUS register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_StatusGet( void ) +{ + while ( RTCC->SYNCBUSY & RTCC_SYNCBUSY_CMD ) + { + // Wait for syncronization. + } + return RTCC->STATUS; +} + +/***************************************************************************//** + * @brief + * Get TIME register value. + * + * @return + * Current TIME register value. + ******************************************************************************/ +__STATIC_INLINE uint32_t RTCC_TimeGet( void ) +{ + return RTCC->TIME; +} + +/***************************************************************************//** + * @brief + * Set RTCC TIME register. + * + * @param[in] time + * TIME value. + ******************************************************************************/ +__STATIC_INLINE void RTCC_TimeSet( uint32_t time ) +{ + RTCC->TIME = time; +} + +/***************************************************************************//** + * @brief + * Unlock RTCC registers. + * + * @note + * When RTCC registers are locked, RTCC_CTRL, RTCC_PRECNT, RTCC_CNT, + * RTCC_TIME, RTCC_DATE, RTCC_IEN, RTCC_POWERDOWN and RTCC_CCx_XXX registers + * can not be written to. + ******************************************************************************/ +__STATIC_INLINE void RTCC_Unlock( void ) +{ + RTCC->LOCK = RTCC_LOCK_LOCKKEY_UNLOCK; +} + +/** @} (end addtogroup RTCC) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined( RTCC_COUNT ) && ( RTC_COUNT == 1 ) */ +#endif /* __SILICON_LABS_EM_RTCC_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_system.h b/cpu/efm32_common/emlib/inc/em_system.h new file mode 100644 index 0000000000000..a84843440c2ec --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_system.h @@ -0,0 +1,392 @@ +/***************************************************************************//** + * @file em_system.h + * @brief System API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_SYSTEM_H__ +#define __SILICON_LABS_EM_SYSTEM_H__ + +#include +#include "em_device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup SYSTEM + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Family identifiers. */ +typedef enum +{ +/* New style family #defines */ +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32G) + systemPartFamilyEfm32Gecko = _DEVINFO_PART_DEVICE_FAMILY_EFM32G, /**< EFM32 Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32GG) + systemPartFamilyEfm32Giant = _DEVINFO_PART_DEVICE_FAMILY_EFM32GG, /**< EFM32 Giant Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32TG) + systemPartFamilyEfm32Tiny = _DEVINFO_PART_DEVICE_FAMILY_EFM32TG, /**< EFM32 Tiny Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32LG) + systemPartFamilyEfm32Leopard = _DEVINFO_PART_DEVICE_FAMILY_EFM32LG, /**< EFM32 Leopard Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32WG) + systemPartFamilyEfm32Wonder = _DEVINFO_PART_DEVICE_FAMILY_EFM32WG, /**< EFM32 Wonder Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32ZG) + systemPartFamilyEfm32Zero = _DEVINFO_PART_DEVICE_FAMILY_EFM32ZG, /**< EFM32 Zero Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32HG) + systemPartFamilyEfm32Happy = _DEVINFO_PART_DEVICE_FAMILY_EFM32HG, /**< EFM32 Happy Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32PG1B) + systemPartFamilyEfm32Pearl1B = _DEVINFO_PART_DEVICE_FAMILY_EFM32PG1B, /**< EFM32 Pearl Gecko Gen1 Basic Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFM32JG1B) + systemPartFamilyEfm32Jade1B = _DEVINFO_PART_DEVICE_FAMILY_EFM32JG1B, /**< EFM32 Jade Gecko Gen1 Basic Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EZR32WG) + systemPartFamilyEzr32Wonder = _DEVINFO_PART_DEVICE_FAMILY_EZR32WG, /**< EZR32 Wonder Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EZR32LG) + systemPartFamilyEzr32Leopard = _DEVINFO_PART_DEVICE_FAMILY_EZR32LG, /**< EZR32 Leopard Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EZR32HG) + systemPartFamilyEzr32Happy = _DEVINFO_PART_DEVICE_FAMILY_EZR32HG, /**< EZR32 Happy Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG1P) + systemPartFamilyMighty1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG1P, /**< EFR32 Mighty Gecko Gen1 Premium Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG1B) + systemPartFamilyMighty1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG1B, /**< EFR32 Mighty Gecko Gen1 Basic Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32MG1V) + systemPartFamilyMighty1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32MG1V, /**< EFR32 Mighty Gecko Gen1 Value Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG1P) + systemPartFamilyBlue1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG1P, /**< EFR32 Blue Gecko Gen1 Premium Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG1B) + systemPartFamilyBlue1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG1B, /**< EFR32 Blue Gecko Gen1 Basic Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32BG1V) + systemPartFamilyBlue1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32BG1V, /**< EFR32 Blue Gecko Gen1 Value Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32SG1P) + systemPartFamilySnappy1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32SG1P, /**< EFR32 Snappy Gecko Gen1 Premium Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32SG1B) + systemPartFamilySnappy1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32SG1B, /**< EFR32 Snappy Gecko Gen1 Basic Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32SG1V) + systemPartFamilySnappy1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32SG1V, /**< EFR32 Snappy Gecko Gen1 Value Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG1P) + systemPartFamilyFlex1P = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG1P, /**< EFR32 Flex Gecko Gen1 Premium Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG1B) + systemPartFamilyFlex1B = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG1B, /**< EFR32 Flex Gecko Gen1 Basic Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_EFR32FG1V) + systemPartFamilyFlex1V = _DEVINFO_PART_DEVICE_FAMILY_EFR32FG1V, /**< EFR32 Flex Gecko Gen1 Value Device Family */ +#endif +/* Legacy family #defines */ +#if defined(_DEVINFO_PART_DEVICE_FAMILY_G) + systemPartFamilyGecko = _DEVINFO_PART_DEVICE_FAMILY_G, /**< Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_GG) + systemPartFamilyGiant = _DEVINFO_PART_DEVICE_FAMILY_GG, /**< Giant Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_TG) + systemPartFamilyTiny = _DEVINFO_PART_DEVICE_FAMILY_TG, /**< Tiny Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_LG) + systemPartFamilyLeopard = _DEVINFO_PART_DEVICE_FAMILY_LG, /**< Leopard Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_WG) + systemPartFamilyWonder = _DEVINFO_PART_DEVICE_FAMILY_WG, /**< Wonder Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_ZG) + systemPartFamilyZero = _DEVINFO_PART_DEVICE_FAMILY_ZG, /**< Zero Gecko Device Family */ +#endif +#if defined(_DEVINFO_PART_DEVICE_FAMILY_HG) + systemPartFamilyHappy = _DEVINFO_PART_DEVICE_FAMILY_HG, /**< Happy Gecko Device Family */ +#endif + systemPartFamilyUnknown = 0xFF /**< Unknown Device Family. + The family id is missing + on unprogrammed parts. */ +} SYSTEM_PartFamily_TypeDef; + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Chip revision details */ +typedef struct +{ + uint8_t minor; /**< Minor revision number */ + uint8_t major; /**< Major revision number */ + uint8_t family;/**< Device family number */ +} SYSTEM_ChipRevision_TypeDef; + +#if defined(__FPU_PRESENT) && (__FPU_PRESENT == 1) +/** Floating point coprocessor access modes. */ +typedef enum +{ + fpuAccessDenied = (0x0 << 20), /**< Access denied, any attempted access generates a NOCP UsageFault. */ + fpuAccessPrivilegedOnly = (0x5 << 20), /**< Privileged access only, an unprivileged access generates a NOCP UsageFault. */ + fpuAccessReserved = (0xA << 20), /**< Reserved. */ + fpuAccessFull = (0xF << 20) /**< Full access. */ +} SYSTEM_FpuAccess_TypeDef; +#endif + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void SYSTEM_ChipRevisionGet(SYSTEM_ChipRevision_TypeDef *rev); +uint32_t SYSTEM_GetCalibrationValue(volatile uint32_t *regAddress); + +#if defined(__FPU_PRESENT) && (__FPU_PRESENT == 1) +/***************************************************************************//** + * @brief + * Set floating point coprocessor (FPU) access mode. + * + * @param[in] accessMode + * Floating point coprocessor access mode. See @ref SYSTEM_FpuAccess_TypeDef + * for details. + ******************************************************************************/ +__STATIC_INLINE void SYSTEM_FpuAccessModeSet(SYSTEM_FpuAccess_TypeDef accessMode) +{ + SCB->CPACR = (SCB->CPACR & ~(0xF << 20)) | accessMode; +} +#endif + +/***************************************************************************//** + * @brief + * Get the unique number for this part. + * + * @return + * Unique number for this part. + ******************************************************************************/ +__STATIC_INLINE uint64_t SYSTEM_GetUnique(void) +{ + return (uint64_t)((uint64_t)DEVINFO->UNIQUEH << 32) | (uint64_t)DEVINFO->UNIQUEL; +} + +/***************************************************************************//** + * @brief + * Get the production revision for this part. + * + * @return + * Production revision for this part. + ******************************************************************************/ +__STATIC_INLINE uint8_t SYSTEM_GetProdRev(void) +{ + return (DEVINFO->PART & _DEVINFO_PART_PROD_REV_MASK) + >> _DEVINFO_PART_PROD_REV_SHIFT; +} + +/***************************************************************************//** + * @brief + * Get the SRAM size (in KB). + * + * @note + * This function retrievs the correct value by reading the chip device + * info structure. If your binary is made for one specific device only, + * the \#define SRAM_SIZE can be used instead. + * + * @return + * The size of the internal SRAM (in KB). + ******************************************************************************/ +__STATIC_INLINE uint16_t SYSTEM_GetSRAMSize(void) +{ +#if defined(_EFM32_GECKO_FAMILY) + /* Early Gecko devices had a bug where SRAM and Flash size were swapped. */ + if (SYSTEM_GetProdRev() < 5) + { + return (DEVINFO->MSIZE & _DEVINFO_MSIZE_FLASH_MASK) + >> _DEVINFO_MSIZE_FLASH_SHIFT; + } +#endif + return (DEVINFO->MSIZE & _DEVINFO_MSIZE_SRAM_MASK) + >> _DEVINFO_MSIZE_SRAM_SHIFT; +} + +/***************************************************************************//** + * @brief + * Get the flash size (in KB). + * + * @note + * This function retrievs the correct value by reading the chip device + * info structure. If your binary is made for one specific device only, + * the \#define FLASH_SIZE can be used instead. + * + * @return + * The size of the internal flash (in KB). + ******************************************************************************/ +__STATIC_INLINE uint16_t SYSTEM_GetFlashSize(void) +{ +#if defined(_EFM32_GECKO_FAMILY) + /* Early Gecko devices had a bug where SRAM and Flash size were swapped. */ + if (SYSTEM_GetProdRev() < 5) + { + return (DEVINFO->MSIZE & _DEVINFO_MSIZE_SRAM_MASK) + >> _DEVINFO_MSIZE_SRAM_SHIFT; + } +#endif + return (DEVINFO->MSIZE & _DEVINFO_MSIZE_FLASH_MASK) + >> _DEVINFO_MSIZE_FLASH_SHIFT; +} + + +/***************************************************************************//** + * @brief + * Get the flash page size in bytes. + * + * @note + * This function retrievs the correct value by reading the chip device + * info structure. If your binary is made for one specific device only, + * the \#define FLASH_PAGE_SIZE can be used instead. + * + * @return + * The page size of the internal flash in bytes. + ******************************************************************************/ +__STATIC_INLINE uint32_t SYSTEM_GetFlashPageSize(void) +{ + uint32_t tmp; + +#if defined(_EFM32_GIANT_FAMILY) + if (SYSTEM_GetProdRev() < 18) + { + /* Early Giant/Leopard devices did not have MEMINFO in DEVINFO. */ + return FLASH_PAGE_SIZE; + } +#elif defined(_EFM32_ZERO_FAMILY) + if (SYSTEM_GetProdRev() < 24) + { + /* Early Zero devices have an incorrect DEVINFO flash page size */ + return FLASH_PAGE_SIZE; + } +#endif + + tmp = (DEVINFO->MEMINFO & _DEVINFO_MEMINFO_FLASH_PAGE_SIZE_MASK) + >> _DEVINFO_MEMINFO_FLASH_PAGE_SIZE_SHIFT; + + return 1 << ((tmp + 10) & 0xFF); +} + + +#if defined( _DEVINFO_DEVINFOREV_DEVINFOREV_MASK ) +/***************************************************************************//** + * @brief + * Get DEVINFO revision. + * + * @return + * Revision of the DEVINFO contents. + ******************************************************************************/ +__STATIC_INLINE uint8_t SYSTEM_GetDevinfoRev(void) +{ + return (DEVINFO->DEVINFOREV & _DEVINFO_DEVINFOREV_DEVINFOREV_MASK) + >> _DEVINFO_DEVINFOREV_DEVINFOREV_SHIFT; +} +#endif + + +/***************************************************************************//** + * @brief + * Get part number of the MCU. + * + * @return + * The part number of the MCU. + ******************************************************************************/ +__STATIC_INLINE uint16_t SYSTEM_GetPartNumber(void) +{ + return (DEVINFO->PART & _DEVINFO_PART_DEVICE_NUMBER_MASK) + >> _DEVINFO_PART_DEVICE_NUMBER_SHIFT; +} + +/***************************************************************************//** + * @brief + * Get family identifier of the MCU. + * + * @note + * This function retrievs the family id by reading the chip's device info + * structure in flash memory. The user can retrieve the family id directly + * by reading the DEVINFO->PART item and decode with the mask and shift + * \#defines defined in \_devinfo.h (please refer to code + * below for details). + * + * @return + * The family identifier of the MCU. + ******************************************************************************/ +__STATIC_INLINE SYSTEM_PartFamily_TypeDef SYSTEM_GetFamily(void) +{ + return (SYSTEM_PartFamily_TypeDef) + ((DEVINFO->PART & _DEVINFO_PART_DEVICE_FAMILY_MASK) + >> _DEVINFO_PART_DEVICE_FAMILY_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Get the calibration temperature (in degrees Celsius). + * + * @return + * The calibration temperature in Celsius. + ******************************************************************************/ +__STATIC_INLINE uint8_t SYSTEM_GetCalibrationTemperature(void) +{ + return (DEVINFO->CAL & _DEVINFO_CAL_TEMP_MASK) + >> _DEVINFO_CAL_TEMP_SHIFT; +} + +/** @} (end addtogroup SYSTEM) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_SYSTEM_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_timer.h b/cpu/efm32_common/emlib/inc/em_timer.h new file mode 100644 index 0000000000000..a81b8814b4c5b --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_timer.h @@ -0,0 +1,931 @@ +/***************************************************************************//** + * @file em_timer.h + * @brief Timer/counter (TIMER) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_TIMER_H__ +#define __SILICON_LABS_EM_TIMER_H__ + +#include "em_device.h" +#if defined(TIMER_COUNT) && (TIMER_COUNT > 0) + +#include +#include "em_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup TIMER + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* 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 */ +#if defined(_SILICON_LABS_32B_PLATFORM_1) +#define TIMER_CH_VALID(ch) ((ch) < 3) +#elif defined(_SILICON_LABS_32B_PLATFORM_2) +#define TIMER_CH_VALID(ch) ((ch) < 4) +#else +#error "Unknown platform. Undefined number of channels." +#endif + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Timer compare/capture mode. */ +typedef enum +{ + timerCCModeOff = _TIMER_CC_CTRL_MODE_OFF, /**< Channel turned off. */ + timerCCModeCapture = _TIMER_CC_CTRL_MODE_INPUTCAPTURE, /**< Input capture. */ + timerCCModeCompare = _TIMER_CC_CTRL_MODE_OUTPUTCOMPARE, /**< Output compare. */ + timerCCModePWM = _TIMER_CC_CTRL_MODE_PWM /**< Pulse-Width modulation. */ +} TIMER_CCMode_TypeDef; + + +/** Clock select. */ +typedef enum +{ + /** Prescaled HFPER clock. */ + timerClkSelHFPerClk = _TIMER_CTRL_CLKSEL_PRESCHFPERCLK, + + /** Prescaled HFPER clock. */ + timerClkSelCC1 = _TIMER_CTRL_CLKSEL_CC1, + + /** + * Cascaded, clocked by underflow (down-counting) or overflow (up-counting) + * by lower numbered timer. + */ + timerClkSelCascade = _TIMER_CTRL_CLKSEL_TIMEROUF +} TIMER_ClkSel_TypeDef; + + +/** Input capture edge select. */ +typedef enum +{ + /** Rising edges detected. */ + timerEdgeRising = _TIMER_CC_CTRL_ICEDGE_RISING, + + /** Falling edges detected. */ + timerEdgeFalling = _TIMER_CC_CTRL_ICEDGE_FALLING, + + /** Both edges detected. */ + timerEdgeBoth = _TIMER_CC_CTRL_ICEDGE_BOTH, + + /** No edge detection, leave signal as is. */ + timerEdgeNone = _TIMER_CC_CTRL_ICEDGE_NONE +} TIMER_Edge_TypeDef; + + +/** Input capture event control. */ +typedef enum +{ + /** PRS output pulse, interrupt flag and DMA request set on every capture. */ + timerEventEveryEdge = _TIMER_CC_CTRL_ICEVCTRL_EVERYEDGE, + /** PRS output pulse, interrupt flag and DMA request set on every second capture. */ + timerEventEvery2ndEdge = _TIMER_CC_CTRL_ICEVCTRL_EVERYSECONDEDGE, + /** + * PRS output pulse, interrupt flag and DMA request set on rising edge (if + * input capture edge = BOTH). + */ + timerEventRising = _TIMER_CC_CTRL_ICEVCTRL_RISING, + /** + * PRS output pulse, interrupt flag and DMA request set on falling edge (if + * input capture edge = BOTH). + */ + timerEventFalling = _TIMER_CC_CTRL_ICEVCTRL_FALLING +} TIMER_Event_TypeDef; + + +/** Input edge action. */ +typedef enum +{ + /** No action taken. */ + timerInputActionNone = _TIMER_CTRL_FALLA_NONE, + + /** Start counter without reload. */ + timerInputActionStart = _TIMER_CTRL_FALLA_START, + + /** Stop counter without reload. */ + timerInputActionStop = _TIMER_CTRL_FALLA_STOP, + + /** Reload and start counter. */ + timerInputActionReloadStart = _TIMER_CTRL_FALLA_RELOADSTART +} TIMER_InputAction_TypeDef; + + +/** Timer mode. */ +typedef enum +{ + timerModeUp = _TIMER_CTRL_MODE_UP, /**< Up-counting. */ + timerModeDown = _TIMER_CTRL_MODE_DOWN, /**< Down-counting. */ + timerModeUpDown = _TIMER_CTRL_MODE_UPDOWN, /**< Up/down-counting. */ + timerModeQDec = _TIMER_CTRL_MODE_QDEC /**< Quadrature decoder. */ +} TIMER_Mode_TypeDef; + + +/** Compare/capture output action. */ +typedef enum +{ + /** No action. */ + timerOutputActionNone = _TIMER_CC_CTRL_CUFOA_NONE, + + /** Toggle on event. */ + timerOutputActionToggle = _TIMER_CC_CTRL_CUFOA_TOGGLE, + + /** Clear on event. */ + timerOutputActionClear = _TIMER_CC_CTRL_CUFOA_CLEAR, + + /** Set on event. */ + timerOutputActionSet = _TIMER_CC_CTRL_CUFOA_SET +} TIMER_OutputAction_TypeDef; + + +/** Prescaler. */ +typedef enum +{ + timerPrescale1 = _TIMER_CTRL_PRESC_DIV1, /**< Divide by 1. */ + timerPrescale2 = _TIMER_CTRL_PRESC_DIV2, /**< Divide by 2. */ + timerPrescale4 = _TIMER_CTRL_PRESC_DIV4, /**< Divide by 4. */ + timerPrescale8 = _TIMER_CTRL_PRESC_DIV8, /**< Divide by 8. */ + timerPrescale16 = _TIMER_CTRL_PRESC_DIV16, /**< Divide by 16. */ + timerPrescale32 = _TIMER_CTRL_PRESC_DIV32, /**< Divide by 32. */ + timerPrescale64 = _TIMER_CTRL_PRESC_DIV64, /**< Divide by 64. */ + timerPrescale128 = _TIMER_CTRL_PRESC_DIV128, /**< Divide by 128. */ + timerPrescale256 = _TIMER_CTRL_PRESC_DIV256, /**< Divide by 256. */ + timerPrescale512 = _TIMER_CTRL_PRESC_DIV512, /**< Divide by 512. */ + timerPrescale1024 = _TIMER_CTRL_PRESC_DIV1024 /**< Divide by 1024. */ +} TIMER_Prescale_TypeDef; + + +/** Peripheral Reflex System signal. */ +typedef enum +{ + timerPRSSELCh0 = _TIMER_CC_CTRL_PRSSEL_PRSCH0, /**< PRS channel 0. */ + timerPRSSELCh1 = _TIMER_CC_CTRL_PRSSEL_PRSCH1, /**< PRS channel 1. */ + timerPRSSELCh2 = _TIMER_CC_CTRL_PRSSEL_PRSCH2, /**< PRS channel 2. */ + timerPRSSELCh3 = _TIMER_CC_CTRL_PRSSEL_PRSCH3, /**< PRS channel 3. */ +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH4) + timerPRSSELCh4 = _TIMER_CC_CTRL_PRSSEL_PRSCH4, /**< PRS channel 4. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH5) + timerPRSSELCh5 = _TIMER_CC_CTRL_PRSSEL_PRSCH5, /**< PRS channel 5. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH6) + timerPRSSELCh6 = _TIMER_CC_CTRL_PRSSEL_PRSCH6, /**< PRS channel 6. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH7) + timerPRSSELCh7 = _TIMER_CC_CTRL_PRSSEL_PRSCH7, /**< PRS channel 7. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH8) + timerPRSSELCh8 = _TIMER_CC_CTRL_PRSSEL_PRSCH8, /**< PRS channel 8. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH9) + timerPRSSELCh9 = _TIMER_CC_CTRL_PRSSEL_PRSCH9, /**< PRS channel 9. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH10) + timerPRSSELCh10 = _TIMER_CC_CTRL_PRSSEL_PRSCH10, /**< PRS channel 10. */ +#endif +#if defined(_TIMER_CC_CTRL_PRSSEL_PRSCH11) + timerPRSSELCh11 = _TIMER_CC_CTRL_PRSSEL_PRSCH11, /**< PRS channel 11. */ +#endif +} TIMER_PRSSEL_TypeDef; + +#if defined(_TIMER_DTFC_DTFA_NONE) +/** DT (Dead Time) Fault Actions. */ +typedef enum +{ + timerDtiFaultActionNone = _TIMER_DTFC_DTFA_NONE, /**< No action on fault. */ + timerDtiFaultActionInactive = _TIMER_DTFC_DTFA_INACTIVE, /**< Set outputs inactive. */ + timerDtiFaultActionClear = _TIMER_DTFC_DTFA_CLEAR, /**< Clear outputs. */ + timerDtiFaultActionTristate = _TIMER_DTFC_DTFA_TRISTATE /**< Tristate outputs. */ +} TIMER_DtiFaultAction_TypeDef; +#endif + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** TIMER initialization structure. */ +typedef struct +{ + /** Start counting when init completed. */ + bool enable; + + /** Counter shall keep running during debug halt. */ + bool debugRun; + + /** Prescaling factor, if HFPER clock used. */ + TIMER_Prescale_TypeDef prescale; + + /** Clock selection. */ + TIMER_ClkSel_TypeDef clkSel; + +#if defined(TIMER_CTRL_X2CNT) && defined(TIMER_CTRL_ATI) + /** 2x Count mode, counter increments/decrements by 2, meant for PWN mode. */ + bool count2x; + + /** ATI (Always Track Inputs) makes CCPOL always track + * the polarity of the inputs. */ + bool ati; +#endif + + /** Action on falling input edge. */ + TIMER_InputAction_TypeDef fallAction; + + /** Action on rising input edge. */ + TIMER_InputAction_TypeDef riseAction; + + /** Counting mode. */ + TIMER_Mode_TypeDef mode; + + /** DMA request clear on active. */ + bool dmaClrAct; + + /** Select X2 or X4 quadrature decode mode (if used). */ + bool quadModeX4; + + /** Determines if only counting up or down once. */ + bool oneShot; + + /** Timer start/stop/reload by other timers. */ + bool sync; +} TIMER_Init_TypeDef; + +/** Default config for TIMER init structure. */ +#if defined(TIMER_CTRL_X2CNT) && defined(TIMER_CTRL_ATI) +#define TIMER_INIT_DEFAULT \ +{ \ + true, /* Enable timer when init complete. */ \ + false, /* Stop counter during debug halt. */ \ + timerPrescale1, /* No prescaling. */ \ + timerClkSelHFPerClk, /* Select HFPER clock. */ \ + false, /* Not 2x count mode. */ \ + false, /* No ATI. */ \ + timerInputActionNone, /* No action on falling input edge. */ \ + timerInputActionNone, /* No action on rising input edge. */ \ + timerModeUp, /* Up-counting. */ \ + false, /* Do not clear DMA requests when DMA channel is active. */ \ + false, /* Select X2 quadrature decode mode (if used). */ \ + false, /* Disable one shot. */ \ + false /* Not started/stopped/reloaded by other timers. */ \ +} +#else +#define TIMER_INIT_DEFAULT \ +{ \ + true, /* Enable timer when init complete. */ \ + false, /* Stop counter during debug halt. */ \ + timerPrescale1, /* No prescaling. */ \ + timerClkSelHFPerClk, /* Select HFPER clock. */ \ + timerInputActionNone, /* No action on falling input edge. */ \ + timerInputActionNone, /* No action on rising input edge. */ \ + timerModeUp, /* Up-counting. */ \ + false, /* Do not clear DMA requests when DMA channel is active. */ \ + false, /* Select X2 quadrature decode mode (if used). */ \ + false, /* Disable one shot. */ \ + false /* Not started/stopped/reloaded by other timers. */ \ +} +#endif + +/** TIMER compare/capture initialization structure. */ +typedef struct +{ + /** Input capture event control. */ + TIMER_Event_TypeDef eventCtrl; + + /** Input capture edge select. */ + TIMER_Edge_TypeDef edge; + + /** + * Peripheral reflex system trigger selection. Only applicable if @p prsInput + * is enabled. + */ + TIMER_PRSSEL_TypeDef prsSel; + + /** Counter underflow output action. */ + TIMER_OutputAction_TypeDef cufoa; + + /** Counter overflow output action. */ + TIMER_OutputAction_TypeDef cofoa; + + /** Counter match output action. */ + TIMER_OutputAction_TypeDef cmoa; + + /** Compare/capture channel mode. */ + TIMER_CCMode_TypeDef mode; + + /** Enable digital filter. */ + bool filter; + + /** Select TIMERnCCx (false) or PRS input (true). */ + bool prsInput; + + /** + * Compare output initial state. Only used in Output Compare and PWM mode. + * When true, the compare/PWM output is set high when the counter is + * disabled. When counting resumes, this value will represent the initial + * value for the compare/PWM output. If the bit is cleared, the output + * will be cleared when the counter is disabled. + */ + bool coist; + + /** Invert output from compare/capture channel. */ + bool outInvert; +} TIMER_InitCC_TypeDef; + +/** Default config for TIMER compare/capture init structure. */ +#define TIMER_INITCC_DEFAULT \ +{ \ + timerEventEveryEdge, /* Event on every capture. */ \ + timerEdgeRising, /* Input capture edge on rising edge. */ \ + timerPRSSELCh0, /* Not used by default, select PRS channel 0. */ \ + timerOutputActionNone, /* No action on underflow. */ \ + timerOutputActionNone, /* No action on overflow. */ \ + timerOutputActionNone, /* No action on match. */ \ + timerCCModeOff, /* Disable compare/capture channel. */ \ + false, /* Disable filter. */ \ + false, /* Select TIMERnCCx input. */ \ + false, /* Clear output when counter disabled. */ \ + false /* Do not invert output. */ \ +} + +#if defined(_TIMER_DTCTRL_MASK) +/** TIMER Dead Time Insertion (DTI) initialization structure. */ +typedef struct +{ + /** Enable DTI or leave it disabled until @ref TIMER_EnableDTI() is called */ + bool enable; + + /** DTI Output Polarity */ + bool activeLowOut; + + /** DTI Complementary Output Invert */ + bool invertComplementaryOut; + + /** Enable Automatic Start-up functionality (when debugger exits) */ + bool autoRestart; + + /** Enable/disable PRS as DTI input. */ + bool enablePrsSource; + + /** Select which PRS channel as DTI input. Only valid if @p enablePrsSource + is enabled. */ + TIMER_PRSSEL_TypeDef prsSel; + + /** DTI prescaling factor, if HFPER clock used. */ + TIMER_Prescale_TypeDef prescale; + + /** DTI Rise Time */ + unsigned int riseTime; + + /** DTI Fall Time */ + unsigned int fallTime; + + /** DTI outputs enable bit mask, consisting of one bit per DTI + output signal, i.e. CC0, CC1, CC2, CDTI0, CDTI1 and CDTI2. + This value should consist of one or more TIMER_DTOGEN_DTOGnnnEN flags + (defined in \_timer.h) OR'ed together. */ + uint32_t outputsEnableMask; + + /** Enable core lockup as a fault source. */ + bool enableFaultSourceCoreLockup; + + /** Enable debugger as a fault source. */ + bool enableFaultSourceDebugger; + + /** Enable PRS fault source 0 (@p faultSourcePrsSel0) */ + bool enableFaultSourcePrsSel0; + + /** Select which PRS signal to be PRS fault source 0. */ + TIMER_PRSSEL_TypeDef faultSourcePrsSel0; + + /** Enable PRS fault source 1 (@p faultSourcePrsSel1) */ + bool enableFaultSourcePrsSel1; + + /** Select which PRS signal to be PRS fault source 1. */ + TIMER_PRSSEL_TypeDef faultSourcePrsSel1; + + /** Fault Action */ + TIMER_DtiFaultAction_TypeDef faultAction; + +} TIMER_InitDTI_TypeDef; + + + /** Default config for TIMER DTI init structure. */ +#define TIMER_INITDTI_DEFAULT \ +{ \ + true, /* Enable the DTI. */ \ + false, /* CC[0|1|2] outputs are active high. */ \ + false, /* CDTI[0|1|2] outputs are not inverted. */ \ + false, /* No auto restart when debugger exits. */ \ + false, /* No PRS source selected. */ \ + timerPRSSELCh0, /* Not used by default, select PRS channel 0. */ \ + timerPrescale1, /* No prescaling. */ \ + 0, /* No rise time. */ \ + 0, /* No fall time. */ \ + TIMER_DTOGEN_DTOGCC0EN|TIMER_DTOGEN_DTOGCDTI0EN, /* Enable CC0 and CDTI0 */\ + true, /* Enable core lockup as fault source */ \ + true, /* Enable debugger as fault source */ \ + false, /* Disable PRS fault source 0 */ \ + timerPRSSELCh0, /* Not used by default, select PRS channel 0. */ \ + false, /* Disable PRS fault source 1 */ \ + timerPRSSELCh0, /* Not used by default, select PRS channel 0. */ \ + timerDtiFaultActionInactive, /* No fault action. */ \ +} +#endif /* _TIMER_DTCTRL_MASK */ + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get capture value for compare/capture channel when operating in capture + * mode. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] ch + * Compare/capture channel to access. + * + * @return + * Current capture value. + ******************************************************************************/ +__STATIC_INLINE uint32_t TIMER_CaptureGet(TIMER_TypeDef *timer, unsigned int ch) +{ + return timer->CC[ch].CCV; +} + + +/***************************************************************************//** + * @brief + * Set compare value buffer for compare/capture channel when operating in + * compare or PWM mode. + * + * @details + * The compare value buffer holds the value which will be written to + * TIMERn_CCx_CCV on an update event if the buffer has been updated since + * the last event. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] ch + * Compare/capture channel to access. + * + * @param[in] val + * Value to set in compare value buffer register. + ******************************************************************************/ +__STATIC_INLINE void TIMER_CompareBufSet(TIMER_TypeDef *timer, + unsigned int ch, + uint32_t val) +{ + timer->CC[ch].CCVB = val; +} + + +/***************************************************************************//** + * @brief + * Set compare value for compare/capture channel when operating in compare + * or PWM mode. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] ch + * Compare/capture channel to access. + * + * @param[in] val + * Value to set in compare value register. + ******************************************************************************/ +__STATIC_INLINE void TIMER_CompareSet(TIMER_TypeDef *timer, + unsigned int ch, + uint32_t val) +{ + timer->CC[ch].CCV = val; +} + + +/***************************************************************************//** + * @brief + * Get TIMER counter value. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @return + * Current TIMER counter value. + ******************************************************************************/ +__STATIC_INLINE uint32_t TIMER_CounterGet(TIMER_TypeDef *timer) +{ + return timer->CNT; +} + + +/***************************************************************************//** + * @brief + * Set TIMER counter value. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] val + * Value to set counter to. + ******************************************************************************/ +__STATIC_INLINE void TIMER_CounterSet(TIMER_TypeDef *timer, uint32_t val) +{ + timer->CNT = val; +} + + +/***************************************************************************//** + * @brief + * Start/stop TIMER. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] enable + * true to enable counting, false to disable. + ******************************************************************************/ +__STATIC_INLINE 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; + } +} + + +void TIMER_Init(TIMER_TypeDef *timer, const TIMER_Init_TypeDef *init); +void TIMER_InitCC(TIMER_TypeDef *timer, + unsigned int ch, + const TIMER_InitCC_TypeDef *init); + +#if defined(_TIMER_DTCTRL_MASK) +void TIMER_InitDTI(TIMER_TypeDef *timer, const TIMER_InitDTI_TypeDef *init); + +/***************************************************************************//** + * @brief + * Enable or disable DTI unit. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] enable + * true to enable DTI unit, false to disable. + ******************************************************************************/ +__STATIC_INLINE void TIMER_EnableDTI(TIMER_TypeDef *timer, bool enable) +{ + EFM_ASSERT(TIMER0 == timer); + + if (enable) + { + timer->DTCTRL |= TIMER_DTCTRL_DTEN; + } + else + { + timer->DTCTRL &= ~TIMER_DTCTRL_DTEN; + } +} + + +/***************************************************************************//** + * @brief + * Get DTI fault source flags status. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @return + * Status of the DTI fault source flags. Returns one or more valid + * DTI fault source flags (TIMER_DTFAULT_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE uint32_t TIMER_GetDTIFault(TIMER_TypeDef *timer) +{ + EFM_ASSERT(TIMER0 == timer); + return timer->DTFAULT; +} + + +/***************************************************************************//** + * @brief + * Clear DTI fault source flags. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] flags + * DTI fault source(s) to clear. Use one or more valid DTI fault + * source flags (TIMER_DTFAULT_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void TIMER_ClearDTIFault(TIMER_TypeDef *timer, uint32_t flags) + +{ + EFM_ASSERT(TIMER0 == timer); + timer->DTFAULTC = flags; +} +#endif /* _TIMER_DTCTRL_MASK */ + + +/***************************************************************************//** + * @brief + * Clear one or more pending TIMER interrupts. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] flags + * Pending TIMER interrupt source(s) to clear. Use one or more valid + * interrupt flags for the TIMER module (TIMER_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void TIMER_IntClear(TIMER_TypeDef *timer, uint32_t flags) +{ + timer->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more TIMER interrupts. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] flags + * TIMER interrupt source(s) to disable. Use one or more valid + * interrupt flags for the TIMER module (TIMER_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void TIMER_IntDisable(TIMER_TypeDef *timer, uint32_t flags) +{ + timer->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more TIMER interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using TIMER_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] flags + * TIMER interrupt source(s) to enable. Use one or more valid + * interrupt flags for the TIMER module (TIMER_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void TIMER_IntEnable(TIMER_TypeDef *timer, uint32_t flags) +{ + timer->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending TIMER interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @return + * TIMER interrupt source(s) pending. Returns one or more valid + * interrupt flags for the TIMER module (TIMER_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE uint32_t TIMER_IntGet(TIMER_TypeDef *timer) +{ + return timer->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending TIMER interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled TIMER interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in TIMERx_IEN_nnn + * register (TIMERx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the TIMER module + * (TIMERx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t TIMER_IntGetEnabled(TIMER_TypeDef *timer) +{ + uint32_t ien; + + /* Store TIMER->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = timer->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return timer->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending TIMER interrupts from SW. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] flags + * TIMER interrupt source(s) to set to pending. Use one or more valid + * interrupt flags for the TIMER module (TIMER_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void TIMER_IntSet(TIMER_TypeDef *timer, uint32_t flags) +{ + timer->IFS = flags; +} + +#if defined(_TIMER_DTLOCK_LOCKKEY_LOCK) +/***************************************************************************//** + * @brief + * Lock some of the TIMER registers in order to protect them from being + * modified. + * + * @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. + ******************************************************************************/ +__STATIC_INLINE void TIMER_Lock(TIMER_TypeDef *timer) +{ + EFM_ASSERT(TIMER0 == timer); + + timer->DTLOCK = TIMER_DTLOCK_LOCKKEY_LOCK; +} +#endif + +void TIMER_Reset(TIMER_TypeDef *timer); + +/***************************************************************************//** + * @brief + * Set top value buffer for timer. + * + * @details + * When the top value buffer register is updated, the value is loaded into + * the top value register at the next wrap around. This feature is useful + * in order to update the top value safely when the timer is running. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] val + * Value to set in top value buffer register. + ******************************************************************************/ +__STATIC_INLINE void TIMER_TopBufSet(TIMER_TypeDef *timer, uint32_t val) +{ + timer->TOPB = val; +} + + +/***************************************************************************//** + * @brief + * Get top value setting for timer. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @return + * Current top value. + ******************************************************************************/ +__STATIC_INLINE uint32_t TIMER_TopGet(TIMER_TypeDef *timer) +{ + return timer->TOP; +} + + +/***************************************************************************//** + * @brief + * Set top value for timer. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] val + * Value to set in top value register. + ******************************************************************************/ +__STATIC_INLINE void TIMER_TopSet(TIMER_TypeDef *timer, uint32_t val) +{ + timer->TOP = val; +} + + +#if defined(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. + ******************************************************************************/ +__STATIC_INLINE void TIMER_Unlock(TIMER_TypeDef *timer) +{ + EFM_ASSERT(TIMER0 == timer); + + timer->DTLOCK = TIMER_DTLOCK_LOCKKEY_UNLOCK; +} +#endif + + +/** @} (end addtogroup TIMER) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(TIMER_COUNT) && (TIMER_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_TIMER_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_usart.h b/cpu/efm32_common/emlib/inc/em_usart.h new file mode 100644 index 0000000000000..584c99cab1d35 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_usart.h @@ -0,0 +1,899 @@ +/***************************************************************************//** + * @file em_usart.h + * @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART) + * peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#ifndef __SILICON_LABS_EM_USART_H__ +#define __SILICON_LABS_EM_USART_H__ + +#include "em_device.h" +#if defined(USART_COUNT) && (USART_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup USART + * @brief Universal Synchronous/Asynchronous Receiver/Transmitter (USART) peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Databit selection. */ +typedef enum +{ + usartDatabits4 = USART_FRAME_DATABITS_FOUR, /**< 4 databits (not available for UART). */ + usartDatabits5 = USART_FRAME_DATABITS_FIVE, /**< 5 databits (not available for UART). */ + usartDatabits6 = USART_FRAME_DATABITS_SIX, /**< 6 databits (not available for UART). */ + usartDatabits7 = USART_FRAME_DATABITS_SEVEN, /**< 7 databits (not available for UART). */ + usartDatabits8 = USART_FRAME_DATABITS_EIGHT, /**< 8 databits. */ + usartDatabits9 = USART_FRAME_DATABITS_NINE, /**< 9 databits. */ + usartDatabits10 = USART_FRAME_DATABITS_TEN, /**< 10 databits (not available for UART). */ + usartDatabits11 = USART_FRAME_DATABITS_ELEVEN, /**< 11 databits (not available for UART). */ + usartDatabits12 = USART_FRAME_DATABITS_TWELVE, /**< 12 databits (not available for UART). */ + usartDatabits13 = USART_FRAME_DATABITS_THIRTEEN, /**< 13 databits (not available for UART). */ + usartDatabits14 = USART_FRAME_DATABITS_FOURTEEN, /**< 14 databits (not available for UART). */ + usartDatabits15 = USART_FRAME_DATABITS_FIFTEEN, /**< 15 databits (not available for UART). */ + usartDatabits16 = USART_FRAME_DATABITS_SIXTEEN /**< 16 databits (not available for UART). */ +} USART_Databits_TypeDef; + + +/** Enable selection. */ +typedef enum +{ + /** Disable both receiver and transmitter. */ + usartDisable = 0x0, + + /** Enable receiver only, transmitter disabled. */ + usartEnableRx = USART_CMD_RXEN, + + /** Enable transmitter only, receiver disabled. */ + usartEnableTx = USART_CMD_TXEN, + + /** Enable both receiver and transmitter. */ + usartEnable = (USART_CMD_RXEN | USART_CMD_TXEN) +} USART_Enable_TypeDef; + + +/** Oversampling selection, used for asynchronous operation. */ +typedef enum +{ + usartOVS16 = USART_CTRL_OVS_X16, /**< 16x oversampling (normal). */ + usartOVS8 = USART_CTRL_OVS_X8, /**< 8x oversampling. */ + usartOVS6 = USART_CTRL_OVS_X6, /**< 6x oversampling. */ + usartOVS4 = USART_CTRL_OVS_X4 /**< 4x oversampling. */ +} USART_OVS_TypeDef; + + +/** Parity selection, mainly used for asynchronous operation. */ +typedef enum +{ + usartNoParity = USART_FRAME_PARITY_NONE, /**< No parity. */ + usartEvenParity = USART_FRAME_PARITY_EVEN, /**< Even parity. */ + usartOddParity = USART_FRAME_PARITY_ODD /**< Odd parity. */ +} USART_Parity_TypeDef; + + +/** Stopbits selection, used for asynchronous operation. */ +typedef enum +{ + usartStopbits0p5 = USART_FRAME_STOPBITS_HALF, /**< 0.5 stopbits. */ + usartStopbits1 = USART_FRAME_STOPBITS_ONE, /**< 1 stopbits. */ + usartStopbits1p5 = USART_FRAME_STOPBITS_ONEANDAHALF, /**< 1.5 stopbits. */ + usartStopbits2 = USART_FRAME_STOPBITS_TWO /**< 2 stopbits. */ +} USART_Stopbits_TypeDef; + + +/** Clock polarity/phase mode. */ +typedef enum +{ + /** Clock idle low, sample on rising edge. */ + usartClockMode0 = USART_CTRL_CLKPOL_IDLELOW | USART_CTRL_CLKPHA_SAMPLELEADING, + + /** Clock idle low, sample on falling edge. */ + usartClockMode1 = USART_CTRL_CLKPOL_IDLELOW | USART_CTRL_CLKPHA_SAMPLETRAILING, + + /** Clock idle high, sample on falling edge. */ + usartClockMode2 = USART_CTRL_CLKPOL_IDLEHIGH | USART_CTRL_CLKPHA_SAMPLELEADING, + + /** Clock idle high, sample on rising edge. */ + usartClockMode3 = USART_CTRL_CLKPOL_IDLEHIGH | USART_CTRL_CLKPHA_SAMPLETRAILING +} USART_ClockMode_TypeDef; + + +/** Pulse width selection for IrDA mode. */ +typedef enum +{ + /** IrDA pulse width is 1/16 for OVS=0 and 1/8 for OVS=1 */ + usartIrDAPwONE = USART_IRCTRL_IRPW_ONE, + + /** IrDA pulse width is 2/16 for OVS=0 and 2/8 for OVS=1 */ + usartIrDAPwTWO = USART_IRCTRL_IRPW_TWO, + + /** IrDA pulse width is 3/16 for OVS=0 and 3/8 for OVS=1 */ + usartIrDAPwTHREE = USART_IRCTRL_IRPW_THREE, + + /** IrDA pulse width is 4/16 for OVS=0 and 4/8 for OVS=1 */ + usartIrDAPwFOUR = USART_IRCTRL_IRPW_FOUR +} USART_IrDAPw_Typedef; + + +/** PRS channel selection for IrDA mode. */ +typedef enum +{ + usartIrDAPrsCh0 = USART_IRCTRL_IRPRSSEL_PRSCH0, /**< PRS channel 0 */ + usartIrDAPrsCh1 = USART_IRCTRL_IRPRSSEL_PRSCH1, /**< PRS channel 1 */ + usartIrDAPrsCh2 = USART_IRCTRL_IRPRSSEL_PRSCH2, /**< PRS channel 2 */ + usartIrDAPrsCh3 = USART_IRCTRL_IRPRSSEL_PRSCH3, /**< PRS channel 3 */ +#if defined(USART_IRCTRL_IRPRSSEL_PRSCH4) + usartIrDAPrsCh4 = USART_IRCTRL_IRPRSSEL_PRSCH4, /**< PRS channel 4 */ +#endif +#if defined(USART_IRCTRL_IRPRSSEL_PRSCH5) + usartIrDAPrsCh5 = USART_IRCTRL_IRPRSSEL_PRSCH5, /**< PRS channel 5 */ +#endif +#if defined(USART_IRCTRL_IRPRSSEL_PRSCH6) + usartIrDAPrsCh6 = USART_IRCTRL_IRPRSSEL_PRSCH6, /**< PRS channel 6 */ +#endif +#if defined(USART_IRCTRL_IRPRSSEL_PRSCH7) + usartIrDAPrsCh7 = USART_IRCTRL_IRPRSSEL_PRSCH7, /**< PRS channel 7 */ +#endif +} USART_IrDAPrsSel_Typedef; + +#if defined(_USART_I2SCTRL_MASK) +/** I2S format selection. */ +typedef enum +{ + usartI2sFormatW32D32 = USART_I2SCTRL_FORMAT_W32D32, /**< 32-bit word, 32-bit data */ + usartI2sFormatW32D24M = USART_I2SCTRL_FORMAT_W32D24M, /**< 32-bit word, 32-bit data with 8 lsb masked */ + usartI2sFormatW32D24 = USART_I2SCTRL_FORMAT_W32D24, /**< 32-bit word, 24-bit data */ + usartI2sFormatW32D16 = USART_I2SCTRL_FORMAT_W32D16, /**< 32-bit word, 16-bit data */ + usartI2sFormatW32D8 = USART_I2SCTRL_FORMAT_W32D8, /**< 32-bit word, 8-bit data */ + usartI2sFormatW16D16 = USART_I2SCTRL_FORMAT_W16D16, /**< 16-bit word, 16-bit data */ + usartI2sFormatW16D8 = USART_I2SCTRL_FORMAT_W16D8, /**< 16-bit word, 8-bit data */ + usartI2sFormatW8D8 = USART_I2SCTRL_FORMAT_W8D8 /**< 8-bit word, 8-bit data */ +} USART_I2sFormat_TypeDef; + +/** I2S frame data justify. */ +typedef enum +{ + usartI2sJustifyLeft = USART_I2SCTRL_JUSTIFY_LEFT, /**< Data is left-justified within the frame */ + usartI2sJustifyRight = USART_I2SCTRL_JUSTIFY_RIGHT /**< Data is right-justified within the frame */ +} USART_I2sJustify_TypeDef; +#endif + +#if defined(_USART_INPUT_MASK) +/** USART Rx input PRS selection. */ +typedef enum +{ + usartPrsRxCh0 = USART_INPUT_RXPRSSEL_PRSCH0, /**< PRSCH0 selected as USART_INPUT */ + usartPrsRxCh1 = USART_INPUT_RXPRSSEL_PRSCH1, /**< PRSCH1 selected as USART_INPUT */ + usartPrsRxCh2 = USART_INPUT_RXPRSSEL_PRSCH2, /**< PRSCH2 selected as USART_INPUT */ + usartPrsRxCh3 = USART_INPUT_RXPRSSEL_PRSCH3, /**< PRSCH3 selected as USART_INPUT */ + +#if defined(USART_INPUT_RXPRSSEL_PRSCH7) + usartPrsRxCh4 = USART_INPUT_RXPRSSEL_PRSCH4, /**< PRSCH4 selected as USART_INPUT */ + usartPrsRxCh5 = USART_INPUT_RXPRSSEL_PRSCH5, /**< PRSCH5 selected as USART_INPUT */ + usartPrsRxCh6 = USART_INPUT_RXPRSSEL_PRSCH6, /**< PRSCH6 selected as USART_INPUT */ + usartPrsRxCh7 = USART_INPUT_RXPRSSEL_PRSCH7, /**< PRSCH7 selected as USART_INPUT */ +#endif + +#if defined(USART_INPUT_RXPRSSEL_PRSCH11) + usartPrsRxCh8 = USART_INPUT_RXPRSSEL_PRSCH8, /**< PRSCH8 selected as USART_INPUT */ + usartPrsRxCh9 = USART_INPUT_RXPRSSEL_PRSCH9, /**< PRSCH9 selected as USART_INPUT */ + usartPrsRxCh10 = USART_INPUT_RXPRSSEL_PRSCH10, /**< PRSCH10 selected as USART_INPUT */ + usartPrsRxCh11 = USART_INPUT_RXPRSSEL_PRSCH11 /**< PRSCH11 selected as USART_INPUT */ +#endif +} USART_PrsRxCh_TypeDef; +#endif + +/** USART PRS Transmit Trigger Channels */ +typedef enum +{ + usartPrsTriggerCh0 = USART_TRIGCTRL_TSEL_PRSCH0, /**< PRSCH0 selected as USART Trigger */ + usartPrsTriggerCh1 = USART_TRIGCTRL_TSEL_PRSCH1, /**< PRSCH0 selected as USART Trigger */ + usartPrsTriggerCh2 = USART_TRIGCTRL_TSEL_PRSCH2, /**< PRSCH0 selected as USART Trigger */ + usartPrsTriggerCh3 = USART_TRIGCTRL_TSEL_PRSCH3, /**< PRSCH0 selected as USART Trigger */ + +#if defined(USART_TRIGCTRL_TSEL_PRSCH7) + usartPrsTriggerCh4 = USART_TRIGCTRL_TSEL_PRSCH4, /**< PRSCH0 selected as USART Trigger */ + usartPrsTriggerCh5 = USART_TRIGCTRL_TSEL_PRSCH5, /**< PRSCH0 selected as USART Trigger */ + usartPrsTriggerCh6 = USART_TRIGCTRL_TSEL_PRSCH6, /**< PRSCH0 selected as USART Trigger */ + usartPrsTriggerCh7 = USART_TRIGCTRL_TSEL_PRSCH7, /**< PRSCH0 selected as USART Trigger */ +#endif +} USART_PrsTriggerCh_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Asynchronous mode init structure. */ +typedef struct +{ + /** Specifies whether TX and/or RX shall be enabled when init completed. */ + USART_Enable_TypeDef enable; + + /** + * USART/UART reference clock assumed when configuring baudrate setup. Set + * it to 0 if currently configurated reference clock shall be used. + */ + uint32_t refFreq; + + /** Desired baudrate. */ + uint32_t baudrate; + + /** Oversampling used. */ + USART_OVS_TypeDef oversampling; + + /** Number of databits in frame. Notice that UART modules only support 8 or + * 9 databits. */ + USART_Databits_TypeDef databits; + + /** Parity mode to use. */ + USART_Parity_TypeDef parity; + + /** Number of stopbits to use. */ + USART_Stopbits_TypeDef stopbits; + +#if defined(USART_INPUT_RXPRS) && defined(USART_CTRL_MVDIS) + /** Majority Vote Disable for 16x, 8x and 6x oversampling modes. */ + bool mvdis; + + /** Enable USART Rx via PRS. */ + bool prsRxEnable; + + /** Select PRS channel for USART Rx. (Only valid if prsRxEnable is true). */ + USART_PrsRxCh_TypeDef prsRxCh; +#endif +#if defined(_USART_TIMING_CSHOLD_MASK) + /** Auto CS enabling */ + bool autoCsEnable; + /** Auto CS hold time in baud cycles */ + uint8_t autoCsHold; + /** Auto CS setup time in baud cycles */ + uint8_t autoCsSetup; +#endif +} USART_InitAsync_TypeDef; + +/** USART PRS trigger enable */ +typedef struct +{ +#if defined(USART_TRIGCTRL_AUTOTXTEN) + /** Enable AUTOTX */ + bool autoTxTriggerEnable; +#endif + /** Trigger receive via PRS channel */ + bool rxTriggerEnable; + /** Trigger transmit via PRS channel */ + bool txTriggerEnable; + /** PRS channel to be used to trigger auto transmission */ + USART_PrsTriggerCh_TypeDef prsTriggerChannel; +} USART_PrsTriggerInit_TypeDef; + +/** Default config for USART async init structure. */ +#if defined(_USART_TIMING_CSHOLD_MASK) && defined(USART_CTRL_MVDIS) +#define USART_INITASYNC_DEFAULT \ +{ \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 115200, /* 115200 bits/s. */ \ + usartOVS16, /* 16x oversampling. */ \ + usartDatabits8, /* 8 databits. */ \ + usartNoParity, /* No parity. */ \ + usartStopbits1, /* 1 stopbit. */ \ + false, /* Do not disable majority vote. */ \ + false, /* Not USART PRS input mode. */ \ + usartPrsRxCh0, /* PRS channel 0. */ \ + false, /* Auto CS functionality enable/disable switch */ \ + 0, /* Auto CS Hold cycles */ \ + 0 /* Auto CS Setup cycles */ \ +} +#elif defined(USART_INPUT_RXPRS) && defined(USART_CTRL_MVDIS) +#define USART_INITASYNC_DEFAULT \ +{ \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 115200, /* 115200 bits/s. */ \ + usartOVS16, /* 16x oversampling. */ \ + usartDatabits8, /* 8 databits. */ \ + usartNoParity, /* No parity. */ \ + usartStopbits1, /* 1 stopbit. */ \ + false, /* Do not disable majority vote. */ \ + false, /* Not USART PRS input mode. */ \ + usartPrsRxCh0 /* PRS channel 0. */ \ +} +#else +#define USART_INITASYNC_DEFAULT \ +{ \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 115200, /* 115200 bits/s. */ \ + usartOVS16, /* 16x oversampling. */ \ + usartDatabits8, /* 8 databits. */ \ + usartNoParity, /* No parity. */ \ + usartStopbits1 /* 1 stopbit. */ \ +} +#endif + +/** Default config for USART PRS triggering structure. */ +#if defined(USART_TRIGCTRL_AUTOTXTEN) +#define USART_INITPRSTRIGGER_DEFAULT \ +{ \ + false, /* Do not enable autoTX triggering. */ \ + false, /* Do not enable receive triggering. */ \ + false, /* Do not enable transmit triggering. */ \ + usartPrsTriggerCh0 /* Set default channel to zero. */ \ +} +#else +#define USART_INITPRSTRIGGER_DEFAULT \ +{ \ + false, /* Do not enable receive triggering. */ \ + false, /* Do not enable transmit triggering. */ \ + usartPrsTriggerCh0 /* Set default channel to zero. */ \ +} +#endif + +/** Synchronous mode init structure. */ +typedef struct +{ + /** Specifies whether TX and/or RX shall be enabled when init completed. */ + USART_Enable_TypeDef enable; + + /** + * USART/UART reference clock assumed when configuring baudrate setup. Set + * it to 0 if currently configurated reference clock shall be used. + */ + uint32_t refFreq; + + /** Desired baudrate. */ + uint32_t baudrate; + + /** Number of databits in frame. */ + USART_Databits_TypeDef databits; + + /** Select if to operate in master or slave mode. */ + bool master; + + /** Select if to send most or least significant bit first. */ + bool msbf; + + /** Clock polarity/phase mode. */ + USART_ClockMode_TypeDef clockMode; + +#if defined(USART_INPUT_RXPRS) && defined(USART_TRIGCTRL_AUTOTXTEN) + /** Enable USART Rx via PRS. */ + bool prsRxEnable; + + /** Select PRS channel for USART Rx. (Only valid if prsRxEnable is true). */ + USART_PrsRxCh_TypeDef prsRxCh; + + /** Enable AUTOTX mode. Transmits as long as RX is not full. + * If TX is empty, underflows are generated. */ + bool autoTx; +#endif +#if defined(_USART_TIMING_CSHOLD_MASK) + /** Auto CS enabling */ + bool autoCsEnable; + /** Auto CS hold time in baud cycles */ + uint8_t autoCsHold; + /** Auto CS setup time in baud cycles */ + uint8_t autoCsSetup; +#endif +} USART_InitSync_TypeDef; + +/** Default config for USART sync init structure. */ +#if defined(_USART_TIMING_CSHOLD_MASK) +#define USART_INITSYNC_DEFAULT \ +{ \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 1000000, /* 1 Mbits/s. */ \ + usartDatabits8, /* 8 databits. */ \ + true, /* Master mode. */ \ + false, /* Send least significant bit first. */ \ + usartClockMode0, /* Clock idle low, sample on rising edge. */ \ + false, /* Not USART PRS input mode. */ \ + usartPrsRxCh0, /* PRS channel 0. */ \ + false, /* No AUTOTX mode. */ \ + false, /* No AUTOCS mode */ \ + 0, /* Auto CS Hold cycles */ \ + 0 /* Auto CS Setup cycles */ \ +} +#elif defined(USART_INPUT_RXPRS) && defined(USART_TRIGCTRL_AUTOTXTEN) +#define USART_INITSYNC_DEFAULT \ +{ \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 1000000, /* 1 Mbits/s. */ \ + usartDatabits8, /* 8 databits. */ \ + true, /* Master mode. */ \ + false, /* Send least significant bit first. */ \ + usartClockMode0, /* Clock idle low, sample on rising edge. */ \ + false, /* Not USART PRS input mode. */ \ + usartPrsRxCh0, /* PRS channel 0. */ \ + false /* No AUTOTX mode. */ \ +} +#else +#define USART_INITSYNC_DEFAULT \ +{ \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 1000000, /* 1 Mbits/s. */ \ + usartDatabits8, /* 8 databits. */ \ + true, /* Master mode. */ \ + false, /* Send least significant bit first. */ \ + usartClockMode0 /* Clock idle low, sample on rising edge. */ \ +} +#endif + + +/** IrDA mode init structure. Inherited from asynchronous mode init structure */ +typedef struct +{ + /** General Async initialization structure. */ + USART_InitAsync_TypeDef async; + + /** Set to invert Rx signal before IrDA demodulator. */ + bool irRxInv; + + /** Set to enable filter on IrDA demodulator. */ + bool irFilt; + + /** Configure the pulse width generated by the IrDA modulator as a fraction + * of the configured USART bit period. */ + USART_IrDAPw_Typedef irPw; + + /** Enable the PRS channel selected by irPrsSel as input to IrDA module + * instead of TX. */ + bool irPrsEn; + + /** A PRS can be used as input to the pulse modulator instead of TX. + * This value selects the channel to use. */ + USART_IrDAPrsSel_Typedef irPrsSel; +} USART_InitIrDA_TypeDef; + + +/** Default config for IrDA mode init structure. */ +#define USART_INITIRDA_DEFAULT \ +{ \ + { \ + usartEnable, /* Enable RX/TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 115200, /* 115200 bits/s. */ \ + usartOVS16, /* 16x oversampling. */ \ + usartDatabits8, /* 8 databits. */ \ + usartEvenParity, /* Even parity. */ \ + usartStopbits1 /* 1 stopbit. */ \ + }, \ + false, /* Rx invert disabled. */ \ + false, /* Filtering disabled. */ \ + usartIrDAPwTHREE, /* Pulse width is set to ONE. */ \ + false, /* Routing to PRS is disabled. */ \ + usartIrDAPrsCh0 /* PRS channel 0. */ \ +} + + +#if defined(_USART_I2SCTRL_MASK) +/** I2S mode init structure. Inherited from synchronous mode init structure */ +typedef struct +{ + /** General Sync initialization structure. */ + USART_InitSync_TypeDef sync; + + /** I2S mode. */ + USART_I2sFormat_TypeDef format; + + /** Delay on I2S data. Set to add a one-cycle delay between a transition + * on the word-clock and the start of the I2S word. + * Should be set for standard I2S format. */ + bool delay; + + /** Separate DMA Request For Left/Right Data. */ + bool dmaSplit; + + /** Justification of I2S data within the frame */ + USART_I2sJustify_TypeDef justify; + + /** Stero or Mono, set to true for mono. */ + bool mono; +} USART_InitI2s_TypeDef; + + +/** Default config for I2S mode init structure. */ +#define USART_INITI2S_DEFAULT \ +{ \ + { \ + usartEnableTx, /* Enable TX when init completed. */ \ + 0, /* Use current configured reference clock for configuring baudrate. */ \ + 1000000, /* Baudrate 1M bits/s. */ \ + usartDatabits16, /* 16 databits. */ \ + true, /* Operate as I2S master. */ \ + true, /* Most significant bit first. */ \ + usartClockMode0, /* Clock idle low, sample on rising edge. */ \ + false, /* Don't enable USARTRx via PRS. */ \ + usartPrsRxCh0, /* PRS channel selection (dummy). */ \ + false /* Disable AUTOTX mode. */ \ + }, \ + usartI2sFormatW16D16, /* 16-bit word, 16-bit data */ \ + true, /* Delay on I2S data. */ \ + false, /* No DMA split. */ \ + usartI2sJustifyLeft, /* Data is left-justified within the frame */ \ + false /* Stereo mode. */ \ +} +#endif + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void USART_BaudrateAsyncSet(USART_TypeDef *usart, + uint32_t refFreq, + uint32_t baudrate, + USART_OVS_TypeDef ovs); +uint32_t USART_BaudrateCalc(uint32_t refFreq, + uint32_t clkdiv, + bool syncmode, + USART_OVS_TypeDef ovs); +uint32_t USART_BaudrateGet(USART_TypeDef *usart); +void USART_BaudrateSyncSet(USART_TypeDef *usart, + uint32_t refFreq, + uint32_t baudrate); +void USART_Enable(USART_TypeDef *usart, USART_Enable_TypeDef enable); + +void USART_InitAsync(USART_TypeDef *usart, const USART_InitAsync_TypeDef *init); +void USART_InitSync(USART_TypeDef *usart, const USART_InitSync_TypeDef *init); +#if defined(USART0) || ((USART_COUNT == 1) && defined(USART1)) +void USART_InitIrDA(const USART_InitIrDA_TypeDef *init); +#endif + +#if defined(_USART_I2SCTRL_MASK) +void USART_InitI2s(USART_TypeDef *usart, USART_InitI2s_TypeDef *init); +#endif +void USART_InitPrsTrigger(USART_TypeDef *usart, const USART_PrsTriggerInit_TypeDef *init); + + +/***************************************************************************//** + * @brief + * Clear one or more pending USART interrupts. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] flags + * Pending USART/UART interrupt source(s) to clear. Use one or more valid + * interrupt flags for the USART module (USART_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void USART_IntClear(USART_TypeDef *usart, uint32_t flags) +{ + usart->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more USART interrupts. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] flags + * USART/UART interrupt source(s) to disable. Use one or more valid + * interrupt flags for the USART module (USART_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void USART_IntDisable(USART_TypeDef *usart, uint32_t flags) +{ + usart->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more USART interrupts. + * + * @note + * Depending on the use, a pending interrupt may already be set prior to + * enabling the interrupt. Consider using USART_IntClear() prior to enabling + * if such a pending interrupt should be ignored. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] flags + * USART/UART interrupt source(s) to enable. Use one or more valid + * interrupt flags for the USART module (USART_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void USART_IntEnable(USART_TypeDef *usart, uint32_t flags) +{ + usart->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending USART interrupt flags. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * USART/UART interrupt source(s) pending. Returns one or more valid + * interrupt flags for the USART module (USART_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE uint32_t USART_IntGet(USART_TypeDef *usart) +{ + return usart->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending USART interrupt flags. + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @note + * Interrupt flags are not cleared by the use of this function. + * + * @return + * Pending and enabled USART interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in USARTx_IEN_nnn + * register (USARTx_IEN_nnn) and + * - the OR combination of valid interrupt flags of the USART module + * (USARTx_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t USART_IntGetEnabled(USART_TypeDef *usart) +{ + uint32_t ien; + + /* Store USARTx->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + ien = usart->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return usart->IF & ien; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending USART interrupts from SW. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @param[in] flags + * USART/UART interrupt source(s) to set to pending. Use one or more valid + * interrupt flags for the USART module (USART_IF_nnn) OR'ed together. + ******************************************************************************/ +__STATIC_INLINE void USART_IntSet(USART_TypeDef *usart, uint32_t flags) +{ + usart->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Get USART STATUS register. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * STATUS register value. + * + ******************************************************************************/ +__STATIC_INLINE uint32_t USART_StatusGet(USART_TypeDef *usart) +{ + return usart->STATUS; +} + +void USART_Reset(USART_TypeDef *usart); +uint8_t USART_Rx(USART_TypeDef *usart); +uint16_t USART_RxDouble(USART_TypeDef *usart); +uint32_t USART_RxDoubleExt(USART_TypeDef *usart); +uint16_t USART_RxExt(USART_TypeDef *usart); + + +/***************************************************************************//** + * @brief + * Receive one 4-8 bit frame, (or part of 10-16 bit frame). + * + * @details + * This function is used to quickly receive one 4-8 bits frame by reading the + * RXDATA register directly, without checking the STATUS register for the + * RXDATAV flag. This can be useful from the RXDATAV interrupt handler, + * i.e. waiting is superfluous, in order to quickly read the received data. + * Please refer to @ref USART_RxDataXGet() for reception of 9 bit frames. + * + * @note + * Since this function does not check whether the RXDATA register actually + * holds valid data, it should only be used in situations when it is certain + * that there is valid data, ensured by some external program routine, e.g. + * like when handling an RXDATAV interrupt. The @ref USART_Rx() is normally a + * better choice if the validity of the RXDATA register is not certain. + * + * @note + * Notice that possible parity/stop bits in asynchronous mode are not + * considered part of specified frame bit length. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +__STATIC_INLINE uint8_t USART_RxDataGet(USART_TypeDef *usart) +{ + return (uint8_t)usart->RXDATA; +} + + +/***************************************************************************//** + * @brief + * Receive two 4-8 bit frames, or one 10-16 bit frame. + * + * @details + * This function is used to quickly receive one 10-16 bits frame or two 4-8 + * bit frames by reading the RXDOUBLE register directly, without checking + * the STATUS register for the RXDATAV flag. This can be useful from the + * RXDATAV interrupt handler, i.e. waiting is superfluous, in order to + * quickly read the received data. + * This function is normally used to receive one frame when operating with + * frame length 10-16 bits. Please refer to @ref USART_RxDoubleXGet() + * for reception of two 9 bit frames. + * + * @note + * Since this function does not check whether the RXDOUBLE register actually + * holds valid data, it should only be used in situations when it is certain + * that there is valid data, ensured by some external program routine, e.g. + * like when handling an RXDATAV interrupt. The @ref USART_RxDouble() is + * normally a better choice if the validity of the RXDOUBLE register is not + * certain. + * + * @note + * Notice that possible parity/stop bits in asynchronous mode are not + * considered part of specified frame bit length. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +__STATIC_INLINE uint16_t USART_RxDoubleGet(USART_TypeDef *usart) +{ + 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 used to quickly receive one 10-16 bits frame or two 4-9 + * bit frames by reading the RXDOUBLEX register directly, without checking + * the STATUS register for the RXDATAV flag. This can be useful from the + * RXDATAV interrupt handler, i.e. waiting is superfluous, in order to + * quickly read the received data. + * + * @note + * Since this function does not check whether the RXDOUBLEX register actually + * holds valid data, it should only be used in situations when it is certain + * that there is valid data, ensured by some external program routine, e.g. + * like when handling an RXDATAV interrupt. The @ref USART_RxDoubleExt() is + * normally a better choice if the validity of the RXDOUBLEX register is not + * certain. + * + * @note + * Notice that possible parity/stop bits in asynchronous mode are not + * considered part of specified frame bit length. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +__STATIC_INLINE uint32_t USART_RxDoubleXGet(USART_TypeDef *usart) +{ + return usart->RXDOUBLEX; +} + + +/***************************************************************************//** + * @brief + * Receive one 4-9 bit frame, (or part of 10-16 bit frame) with extended + * information. + * + * @details + * This function is used to quickly receive one 4-9 bit frame, (or part of + * 10-16 bit frame) with extended information by reading the RXDATAX register + * directly, without checking the STATUS register for the RXDATAV flag. This + * can be useful from the RXDATAV interrupt handler, i.e. waiting is + * superfluous, in order to quickly read the received data. + * + * @note + * Since this function does not check whether the RXDATAX register actually + * holds valid data, it should only be used in situations when it is certain + * that there is valid data, ensured by some external program routine, e.g. + * like when handling an RXDATAV interrupt. The @ref USART_RxExt() is normally + * a better choice if the validity of the RXDATAX register is not certain. + * + * @note + * Notice that possible parity/stop bits in asynchronous mode are not + * considered part of specified frame bit length. + * + * @param[in] usart + * Pointer to USART/UART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +__STATIC_INLINE uint16_t USART_RxDataXGet(USART_TypeDef *usart) +{ + return (uint16_t)usart->RXDATAX; +} + +uint8_t USART_SpiTransfer(USART_TypeDef *usart, uint8_t data); +void USART_Tx(USART_TypeDef *usart, uint8_t data); +void USART_TxDouble(USART_TypeDef *usart, uint16_t data); +void USART_TxDoubleExt(USART_TypeDef *usart, uint32_t data); +void USART_TxExt(USART_TypeDef *usart, uint16_t data); + + +/** @} (end addtogroup USART) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(USART_COUNT) && (USART_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_USART_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_vcmp.h b/cpu/efm32_common/emlib/inc/em_vcmp.h new file mode 100644 index 0000000000000..4311a7f46dc01 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_vcmp.h @@ -0,0 +1,349 @@ +/***************************************************************************//** + * @file em_vcmp.h + * @brief Voltage Comparator (VCMP) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_VCMP_H__ +#define __SILICON_LABS_EM_VCMP_H__ + +#include "em_device.h" +#if defined(VCMP_COUNT) && (VCMP_COUNT > 0) + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup VCMP + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Warm-up Time in High Frequency Peripheral Clock cycles */ +typedef enum +{ + /** 4 cycles */ + vcmpWarmTime4Cycles = _VCMP_CTRL_WARMTIME_4CYCLES, + /** 8 cycles */ + vcmpWarmTime8Cycles = _VCMP_CTRL_WARMTIME_8CYCLES, + /** 16 cycles */ + vcmpWarmTime16Cycles = _VCMP_CTRL_WARMTIME_16CYCLES, + /** 32 cycles */ + vcmpWarmTime32Cycles = _VCMP_CTRL_WARMTIME_32CYCLES, + /** 64 cycles */ + vcmpWarmTime64Cycles = _VCMP_CTRL_WARMTIME_64CYCLES, + /** 128 cycles */ + vcmpWarmTime128Cycles = _VCMP_CTRL_WARMTIME_128CYCLES, + /** 256 cycles */ + vcmpWarmTime256Cycles = _VCMP_CTRL_WARMTIME_256CYCLES, + /** 512 cycles */ + vcmpWarmTime512Cycles = _VCMP_CTRL_WARMTIME_512CYCLES +} VCMP_WarmTime_TypeDef; + +/** Hyseresis configuration */ +typedef enum +{ + /** Normal operation, no hysteresis */ + vcmpHystNone, + /** Digital output will not toggle until positive edge is at least + * 20mV above or below negative input voltage */ + vcmpHyst20mV +} VCMP_Hysteresis_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** VCMP Initialization structure */ +typedef struct +{ + /** If set to true, will reduce by half the bias current */ + bool halfBias; + /** BIAS current configuration, depends on halfBias setting, + * above, see reference manual */ + int biasProg; + /** Enable interrupt for falling edge */ + bool irqFalling; + /** Enable interrupt for rising edge */ + bool irqRising; + /** Warm-up time in clock cycles */ + VCMP_WarmTime_TypeDef warmup; + /** Hysteresis configuration */ + VCMP_Hysteresis_TypeDef hyst; + /** Output value when comparator is inactive, should be 0 or 1 */ + int inactive; + /** Enable low power mode for VDD and bandgap reference */ + bool lowPowerRef; + /** Trigger level, according to formula + * VDD Trigger Level = 1.667V + 0.034V x triggerLevel */ + int triggerLevel; + /** Enable VCMP after configuration */ + bool enable; +} VCMP_Init_TypeDef; + +/** Default VCMP initialization structure */ +#define VCMP_INIT_DEFAULT \ +{ \ + true, /** Half Bias enabled */ \ + 0x7, /** Bias curernt 0.7 uA when half bias enabled */ \ + false, /** Falling edge sense not enabled */ \ + false, /** Rising edge sense not enabled */ \ + vcmpWarmTime4Cycles, /** 4 clock cycles warm-up time */ \ + vcmpHystNone, /** No hysteresis */ \ + 0, /** 0 in digital ouput when inactive */ \ + true, /** Do not use low power reference */ \ + 39, /** Trigger level just below 3V */ \ + true, /** Enable after init */ \ +} + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void VCMP_Init(const VCMP_Init_TypeDef *vcmpInit); +void VCMP_LowPowerRefSet(bool enable); +void VCMP_TriggerSet(int level); + +/***************************************************************************//** + * @brief + * Enable Voltage Comparator + ******************************************************************************/ +__STATIC_INLINE void VCMP_Enable(void) +{ + VCMP->CTRL |= VCMP_CTRL_EN; +} + + +/***************************************************************************//** + * @brief + * Disable Voltage Comparator + ******************************************************************************/ +__STATIC_INLINE void VCMP_Disable(void) +{ + VCMP->CTRL &= ~VCMP_CTRL_EN; +} + + +/***************************************************************************//** + * @brief + * Calculate voltage to trigger level + * + * @note + * You need soft float support for this function to be working + * + * @param[in] v + * Voltage Level for trigger + ******************************************************************************/ +__STATIC_INLINE uint32_t VCMP_VoltageToLevel(float v) +{ + return (uint32_t)((v - (float)1.667) / (float)0.034); +} + + +/***************************************************************************//** + * @brief + * Returns true, if Voltage Comparator indicated VDD < trigger level, else + * false + ******************************************************************************/ +__STATIC_INLINE bool VCMP_VDDLower(void) +{ + if (VCMP->STATUS & VCMP_STATUS_VCMPOUT) + { + return false; + } + else + { + return true; + } +} + + +/***************************************************************************//** + * @brief + * Returns true, if Voltage Comparator indicated VDD > trigger level, else + * false + ******************************************************************************/ +__STATIC_INLINE bool VCMP_VDDHigher(void) +{ + if (VCMP->STATUS & VCMP_STATUS_VCMPOUT) + { + return true; + } + else + { + return false; + } +} + + +/***************************************************************************//** + * @brief + * VCMP output is ready + ******************************************************************************/ +__STATIC_INLINE bool VCMP_Ready(void) +{ + if (VCMP->STATUS & VCMP_STATUS_VCMPACT) + { + return true; + } + else + { + return false; + } +} + + +/***************************************************************************//** + * @brief + * Clear one or more pending VCMP interrupts. + * + * @param[in] flags + * VCMP interrupt sources to clear. Use a set of interrupt flags OR-ed + * together to clear multiple interrupt sources for the VCMP module + * (VCMP_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void VCMP_IntClear(uint32_t flags) +{ + VCMP->IFC = flags; +} + + +/***************************************************************************//** + * @brief + * Set one or more pending VCMP interrupts from SW. + * + * @param[in] flags + * VCMP interrupt sources to set to pending. Use a set of interrupt flags + * OR-ed together to set multiple interrupt sources for the VCMP module + * (VCMP_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void VCMP_IntSet(uint32_t flags) +{ + VCMP->IFS = flags; +} + + +/***************************************************************************//** + * @brief + * Disable one or more VCMP interrupts + * + * @param[in] flags + * VCMP interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to set multiple interrupt sources for the VCMP module + * (VCMP_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void VCMP_IntDisable(uint32_t flags) +{ + VCMP->IEN &= ~flags; +} + + +/***************************************************************************//** + * @brief + * Enable one or more VCMP interrupts + * + * @param[in] flags + * VCMP interrupt sources to enable. Use a set of interrupt flags OR-ed + * together to set multiple interrupt sources for the VCMP module + * (VCMP_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE void VCMP_IntEnable(uint32_t flags) +{ + VCMP->IEN |= flags; +} + + +/***************************************************************************//** + * @brief + * Get pending VCMP interrupt flags + * + * @note + * The event bits are not cleared by the use of this function + * + * @return + * Pending VCMP interrupt sources. Returns a set of interrupt flags OR-ed + * together for multiple interrupt sources in the VCMP module (VCMP_IFS_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t VCMP_IntGet(void) +{ + return VCMP->IF; +} + + +/***************************************************************************//** + * @brief + * Get enabled and pending VCMP interrupt flags. + * + * @details + * Useful for handling more interrupt sources in the same interrupt handler. + * + * @note + * The event bits are not cleared by the use of this function. + * + * @return + * Pending and enabled VCMP interrupt sources. + * The return value is the bitwise AND combination of + * - the OR combination of enabled interrupt sources in VCMP_IEN_nnn + * register (VCMP_IEN_nnn) and + * - the OR combination of valid interrupt flags of the VCMP module + * (VCMP_IF_nnn). + ******************************************************************************/ +__STATIC_INLINE uint32_t VCMP_IntGetEnabled(void) +{ + uint32_t tmp = 0U; + + /* Store VCMP->IEN in temporary variable in order to define explicit order + * of volatile accesses. */ + tmp = VCMP->IEN; + + /* Bitwise AND of pending and enabled interrupts */ + return VCMP->IF & tmp; +} + +/** @} (end addtogroup VCMP) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(VCMP_COUNT) && (VCMP_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_VCMP_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_version.h b/cpu/efm32_common/emlib/inc/em_version.h new file mode 100644 index 0000000000000..b9583c7dc613c --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_version.h @@ -0,0 +1,86 @@ +/***************************************************************************//** + * @file em_version.h + * @brief Assign correct part number for include file + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#ifndef __SILICON_LABS_EM_VERSION_H__ +#define __SILICON_LABS_EM_VERSION_H__ + +#include "em_device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup VERSION + * @{ + ******************************************************************************/ + +/** Version number of emlib peripheral API. */ +#define _EMLIB_VERSION 4.2.1 + +/** Major version of emlib. Bumped when incompatible API changes introduced. */ +#define _EMLIB_VERSION_MAJOR 4 + +/** Minor version of emlib. Bumped when functionality is added in a backwards- + compatible manner. */ +#define _EMLIB_VERSION_MINOR 2 + +/** Patch revision of emlib. Bumped when adding backwards-compatible bug + fixes.*/ +#define _EMLIB_VERSION_PATCH 1 + + +/** Version number of targeted CMSIS package. */ +#define _CMSIS_VERSION 4.2.0 + +/** Major version of CMSIS. */ +#define _CMSIS_VERSION_MAJOR 4 + +/** Minor version of CMSIS. */ +#define _CMSIS_VERSION_MINOR 2 + +/** Patch revision of CMSIS. */ +#define _CMSIS_VERSION_PATCH 0 + +/** @} (end addtogroup Version) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SILICON_LABS_EM_VERSION_H__ */ diff --git a/cpu/efm32_common/emlib/inc/em_wdog.h b/cpu/efm32_common/emlib/inc/em_wdog.h new file mode 100644 index 0000000000000..d9153c14d33c5 --- /dev/null +++ b/cpu/efm32_common/emlib/inc/em_wdog.h @@ -0,0 +1,156 @@ +/***************************************************************************//** + * @file em_wdog.h + * @brief Watchdog (WDOG) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#ifndef __SILICON_LABS_EM_WDOG_H__ +#define __SILICON_LABS_EM_WDOG_H__ + +#include "em_device.h" +#if defined(WDOG_COUNT) && (WDOG_COUNT > 0) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup WDOG + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** Watchdog clock selection. */ +typedef enum +{ + wdogClkSelULFRCO = _WDOG_CTRL_CLKSEL_ULFRCO, /**< Ultra low frequency (1 kHz) clock */ + wdogClkSelLFRCO = _WDOG_CTRL_CLKSEL_LFRCO, /**< Low frequency RC oscillator */ + wdogClkSelLFXO = _WDOG_CTRL_CLKSEL_LFXO /**< Low frequency crystal oscillator */ +} WDOG_ClkSel_TypeDef; + +/** Watchdog period selection. */ +typedef enum +{ + wdogPeriod_9 = 0x0, /**< 9 clock periods */ + wdogPeriod_17 = 0x1, /**< 17 clock periods */ + wdogPeriod_33 = 0x2, /**< 33 clock periods */ + wdogPeriod_65 = 0x3, /**< 65 clock periods */ + wdogPeriod_129 = 0x4, /**< 129 clock periods */ + wdogPeriod_257 = 0x5, /**< 257 clock periods */ + wdogPeriod_513 = 0x6, /**< 513 clock periods */ + wdogPeriod_1k = 0x7, /**< 1025 clock periods */ + wdogPeriod_2k = 0x8, /**< 2049 clock periods */ + wdogPeriod_4k = 0x9, /**< 4097 clock periods */ + wdogPeriod_8k = 0xA, /**< 8193 clock periods */ + wdogPeriod_16k = 0xB, /**< 16385 clock periods */ + wdogPeriod_32k = 0xC, /**< 32769 clock periods */ + wdogPeriod_64k = 0xD, /**< 65537 clock periods */ + wdogPeriod_128k = 0xE, /**< 131073 clock periods */ + wdogPeriod_256k = 0xF /**< 262145 clock periods */ +} WDOG_PeriodSel_TypeDef; + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Watchdog initialization structure. */ +typedef struct +{ + /** Enable watchdog when init completed. */ + bool enable; + + /** Counter shall keep running during debug halt. */ + bool debugRun; + + /** Counter shall keep running when in EM2. */ + bool em2Run; + + /** Counter shall keep running when in EM3. */ + bool em3Run; + + /** Block EMU from entering EM4. */ + bool em4Block; + + /** Block SW from disabling LFRCO/LFXO oscillators. */ + bool swoscBlock; + + /** Block SW from modifying the configuration (a reset is needed to reconfigure). */ + bool lock; + + /** Clock source to use for watchdog. */ + WDOG_ClkSel_TypeDef clkSel; + + /** Watchdog timeout period. */ + WDOG_PeriodSel_TypeDef perSel; +} WDOG_Init_TypeDef; + +/** Suggested default config for WDOG init structure. */ +#define WDOG_INIT_DEFAULT \ +{ \ + true, /* Start watchdog when init done */ \ + false, /* WDOG not counting during debug halt */ \ + false, /* WDOG not counting when in EM2 */ \ + false, /* WDOG not counting when in EM3 */ \ + false, /* EM4 can be entered */ \ + false, /* Do not block disabling LFRCO/LFXO in CMU */ \ + false, /* Do not lock WDOG configuration (if locked, reset needed to unlock) */ \ + wdogClkSelULFRCO, /* Select 1kHZ WDOG oscillator */ \ + wdogPeriod_256k /* Set longest possible timeout period */ \ +} + + +/******************************************************************************* + ***************************** PROTOTYPES ********************************** + ******************************************************************************/ + +void WDOG_Enable(bool enable); +void WDOG_Feed(void); +void WDOG_Init(const WDOG_Init_TypeDef *init); +void WDOG_Lock(void); + +/** @} (end addtogroup WDOG) */ +/** @} (end addtogroup EM_Library) */ + +#ifdef __cplusplus +} +#endif + +#endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */ +#endif /* __SILICON_LABS_EM_WDOG_H__ */ diff --git a/cpu/efm32_common/emlib/src/Makefile b/cpu/efm32_common/emlib/src/Makefile new file mode 100644 index 0000000000000..11f547d32f429 --- /dev/null +++ b/cpu/efm32_common/emlib/src/Makefile @@ -0,0 +1,3 @@ +MODULE = emlib + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/efm32_common/emlib/src/em_acmp.c b/cpu/efm32_common/emlib/src/em_acmp.c new file mode 100644 index 0000000000000..b9d5cf4d2348e --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_acmp.c @@ -0,0 +1,456 @@ +/***************************************************************************//** + * @file em_acmp.c + * @brief Analog Comparator (ACMP) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#include "em_acmp.h" +#if defined(ACMP_COUNT) && (ACMP_COUNT > 0) + +#include +#include "em_bus.h" +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup ACMP + * @brief Analog comparator (ACMP) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + + +/** Validation of ACMP register block pointer reference + * for assert statements. */ +#if (ACMP_COUNT == 1) +#define ACMP_REF_VALID(ref) ((ref) == ACMP0) +#elif (ACMP_COUNT == 2) +#define ACMP_REF_VALID(ref) (((ref) == ACMP0) || ((ref) == ACMP1)) +#else +#error Undefined number of analog comparators (ACMP). +#endif + +/** The maximum value that can be inserted in the route location register + * for the specific device. */ +#if defined(_ACMP_ROUTE_LOCATION_LOC3) +#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC3 +#elif defined(_ACMP_ROUTE_LOCATION_LOC2) +#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC2 +#elif defined(_ACMP_ROUTE_LOCATION_LOC1) +#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTE_LOCATION_LOC1 +#elif defined(_ACMP_ROUTELOC0_OUTLOC_LOC31) +#define _ACMP_ROUTE_LOCATION_MAX _ACMP_ROUTELOC0_OUTLOC_LOC31 +#else +#error Undefined max route locations +#endif + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Sets up the ACMP for use in capacative sense applications. + * + * @details + * This function sets up the ACMP for use in capacacitve sense applications. + * To use the capacative sense functionality in the ACMP you need to use + * the PRS output of the ACMP module to count the number of oscillations + * in the capacative sense circuit (possibly using a TIMER). + * + * @note + * A basic example of capacative sensing can be found in the STK BSP + * (capsense demo). + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @param[in] init + * Pointer to initialization structure used to configure ACMP for capacative + * sensing operation. + ******************************************************************************/ +void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + /* Make sure that vddLevel is within bounds */ +#if defined(_ACMP_INPUTSEL_VDDLEVEL_MASK) + EFM_ASSERT(init->vddLevel < 64); +#else + EFM_ASSERT(init->vddLevelLow < 64); + EFM_ASSERT(init->vddLevelHigh < 64); +#endif + + /* Make sure biasprog is within bounds */ + EFM_ASSERT(init->biasProg <= + (_ACMP_CTRL_BIASPROG_MASK >> _ACMP_CTRL_BIASPROG_SHIFT)); + + /* Set control register. No need to set interrupt modes */ + acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT) +#if defined(_ACMP_CTRL_HALFBIAS_MASK) + | (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT) +#endif + | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT) +#if defined(_ACMP_CTRL_WARMTIME_MASK) + | (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT) +#endif +#if defined(_ACMP_CTRL_HYSTSEL_MASK) + | (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT) +#endif +#if defined(_ACMP_CTRL_ACCURACY_MASK) + | ACMP_CTRL_ACCURACY_HIGH +#endif + ; + +#if defined(_ACMP_HYSTERESIS0_MASK) + acmp->HYSTERESIS0 = (init->vddLevelHigh << _ACMP_HYSTERESIS0_DIVVA_SHIFT) + | (init->hysteresisLevel_0 << _ACMP_HYSTERESIS0_HYST_SHIFT); + acmp->HYSTERESIS1 = (init->vddLevelLow << _ACMP_HYSTERESIS1_DIVVA_SHIFT) + | (init->hysteresisLevel_1 << _ACMP_HYSTERESIS1_HYST_SHIFT); +#endif + + /* Select capacative sensing mode by selecting a resistor and enabling it */ + acmp->INPUTSEL = (init->resistor << _ACMP_INPUTSEL_CSRESSEL_SHIFT) + | ACMP_INPUTSEL_CSRESEN +#if defined(_ACMP_INPUTSEL_LPREF_MASK) + | (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT) +#endif +#if defined(_ACMP_INPUTSEL_VDDLEVEL_MASK) + | (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT) +#endif +#if defined(ACMP_INPUTSEL_NEGSEL_CAPSENSE) + | ACMP_INPUTSEL_NEGSEL_CAPSENSE +#else + | ACMP_INPUTSEL_VASEL_VDD + | ACMP_INPUTSEL_NEGSEL_VADIV +#endif + ; + + /* Enable ACMP if requested. */ + BUS_RegBitWrite(&(acmp->CTRL), _ACMP_CTRL_EN_SHIFT, init->enable); +} + +/***************************************************************************//** + * @brief + * Sets the ACMP channel used for capacative sensing. + * + * @note + * A basic example of capacative sensing can be found in the STK BSP + * (capsense demo). + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + * + * @param[in] channel + * The ACMP channel to use for capacative sensing (Possel). + ******************************************************************************/ +void ACMP_CapsenseChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef channel) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + +#if defined(_ACMP_INPUTSEL_POSSEL_CH7) + /* Make sure that only external channels are used */ + EFM_ASSERT(channel <= _ACMP_INPUTSEL_POSSEL_CH7); +#elif defined(_ACMP_INPUTSEL_POSSEL_BUS4XCH31) + /* Make sure that only external channels are used */ + EFM_ASSERT(channel <= _ACMP_INPUTSEL_POSSEL_BUS4XCH31); +#endif + + /* Set channel as positive channel in ACMP */ + BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_POSSEL_MASK, + channel << _ACMP_INPUTSEL_POSSEL_SHIFT); +} + +/***************************************************************************//** + * @brief + * Disables the ACMP. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + ******************************************************************************/ +void ACMP_Disable(ACMP_TypeDef *acmp) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + acmp->CTRL &= ~ACMP_CTRL_EN; +} + +/***************************************************************************//** + * @brief + * Enables the ACMP. + * + * @param[in] acmp + * Pointer to ACMP peripheral register block. + ******************************************************************************/ +void ACMP_Enable(ACMP_TypeDef *acmp) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + acmp->CTRL |= ACMP_CTRL_EN; +} + +/***************************************************************************//** + * @brief + * Reset ACMP 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] acmp + * Pointer to the ACMP peripheral register block. + ******************************************************************************/ +void ACMP_Reset(ACMP_TypeDef *acmp) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + acmp->CTRL = _ACMP_CTRL_RESETVALUE; + acmp->INPUTSEL = _ACMP_INPUTSEL_RESETVALUE; +#if defined(_ACMP_HYSTERESIS0_HYST_MASK) + acmp->HYSTERESIS0 = _ACMP_HYSTERESIS0_RESETVALUE; + acmp->HYSTERESIS1 = _ACMP_HYSTERESIS1_RESETVALUE; +#endif + acmp->IEN = _ACMP_IEN_RESETVALUE; + acmp->IFC = _ACMP_IF_MASK; +} + +/***************************************************************************//** + * @brief + * Sets up GPIO output from the ACMP. + * + * @note + * GPIO must be enabled in the CMU before this function call, i.e. + * @verbatim CMU_ClockEnable(cmuClock_GPIO, true); @endverbatim + * + * @param[in] acmp + * Pointer to the ACMP peripheral register block. + * + * @param location + * The pin location to use. See the datasheet for location to pin mappings. + * + * @param enable + * Enable or disable pin output. + * + * @param invert + * Invert output. + ******************************************************************************/ +void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + /* Sanity checking of location */ + EFM_ASSERT(location <= _ACMP_ROUTE_LOCATION_MAX); + + /* Set GPIO inversion */ + BUS_RegMaskedWrite(&acmp->CTRL, _ACMP_CTRL_GPIOINV_MASK, + invert << _ACMP_CTRL_GPIOINV_SHIFT); + +#if defined(_ACMP_ROUTE_MASK) + acmp->ROUTE = (location << _ACMP_ROUTE_LOCATION_SHIFT) + | (enable << _ACMP_ROUTE_ACMPPEN_SHIFT); +#endif +#if defined(_ACMP_ROUTELOC0_MASK) + acmp->ROUTELOC0 = location << _ACMP_ROUTELOC0_OUTLOC_SHIFT; + acmp->ROUTEPEN = enable ? ACMP_ROUTEPEN_OUTPEN : 0; +#endif +} + +/***************************************************************************//** + * @brief + * Sets which channels should be used in ACMP comparisons. + * + * @param[in] acmp + * Pointer to the ACMP peripheral register block. + * + * @param negSel + * Channel to use on the negative input to the ACMP. + * + * @param posSel + * Channel to use on the positive input to the ACMP. + ******************************************************************************/ +void ACMP_ChannelSet(ACMP_TypeDef *acmp, ACMP_Channel_TypeDef negSel, + ACMP_Channel_TypeDef posSel) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + /* Make sure that posSel and negSel channel selectors are valid. */ +#if defined(_ACMP_INPUTSEL_NEGSEL_DAC0CH1) + EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_DAC0CH1); +#elif defined(_ACMP_INPUTSEL_NEGSEL_CAPSENSE) + EFM_ASSERT(negSel <= _ACMP_INPUTSEL_NEGSEL_CAPSENSE); +#endif + +#if defined(_ACMP_INPUTSEL_POSSEL_CH7) + EFM_ASSERT(posSel <= _ACMP_INPUTSEL_POSSEL_CH7); +#endif + + acmp->INPUTSEL = (acmp->INPUTSEL & ~(_ACMP_INPUTSEL_POSSEL_MASK + | _ACMP_INPUTSEL_NEGSEL_MASK)) + | (negSel << _ACMP_INPUTSEL_NEGSEL_SHIFT) + | (posSel << _ACMP_INPUTSEL_POSSEL_SHIFT); +} + +/***************************************************************************//** + * @brief + * Initialize ACMP. + * + * @param[in] acmp + * Pointer to the ACMP peripheral register block. + * + * @param[in] init + * Pointer to initialization structure used to configure ACMP for capacative + * sensing operation. + ******************************************************************************/ +void ACMP_Init(ACMP_TypeDef *acmp, const ACMP_Init_TypeDef *init) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(ACMP_REF_VALID(acmp)); + + /* Make sure biasprog is within bounds */ + EFM_ASSERT(init->biasProg < 16); + + /* Make sure the ACMP is disable since we might be changing the + * ACMP power source */ + BUS_RegBitWrite(&acmp->CTRL, _ACMP_CTRL_EN_SHIFT, 0); + + /* Set control register. No need to set interrupt modes */ + acmp->CTRL = (init->fullBias << _ACMP_CTRL_FULLBIAS_SHIFT) +#if defined(_ACMP_CTRL_HALFBIAS_MASK) + | (init->halfBias << _ACMP_CTRL_HALFBIAS_SHIFT) +#endif + | (init->biasProg << _ACMP_CTRL_BIASPROG_SHIFT) + | (init->interruptOnFallingEdge << _ACMP_CTRL_IFALL_SHIFT) + | (init->interruptOnRisingEdge << _ACMP_CTRL_IRISE_SHIFT) +#if defined(_ACMP_CTRL_INPUTRANGE_MASK) + | (init->inputRange << _ACMP_CTRL_INPUTRANGE_SHIFT) +#endif +#if defined(_ACMP_CTRL_ACCURACY_MASK) + | (init->accuracy << _ACMP_CTRL_ACCURACY_SHIFT) +#endif +#if defined(_ACMP_CTRL_PWRSEL_MASK) + | (init->powerSource << _ACMP_CTRL_PWRSEL_SHIFT) +#endif +#if defined(_ACMP_CTRL_WARMTIME_MASK) + | (init->warmTime << _ACMP_CTRL_WARMTIME_SHIFT) +#endif +#if defined(_ACMP_CTRL_HYSTSEL_MASK) + | (init->hysteresisLevel << _ACMP_CTRL_HYSTSEL_SHIFT) +#endif + | (init->inactiveValue << _ACMP_CTRL_INACTVAL_SHIFT); + + acmp->INPUTSEL = (0) +#if defined(_ACMP_INPUTSEL_VLPSEL_MASK) + | (init->vlpInput << _ACMP_INPUTSEL_VLPSEL_SHIFT) +#endif +#if defined(_ACMP_INPUTSEL_LPREF_MASK) + | (init->lowPowerReferenceEnabled << _ACMP_INPUTSEL_LPREF_SHIFT) +#endif +#if defined(_ACMP_INPUTSEL_VDDLEVEL_MASK) + | (init->vddLevel << _ACMP_INPUTSEL_VDDLEVEL_SHIFT) +#endif + ; + + /* Enable ACMP if requested. */ + BUS_RegBitWrite(&(acmp->CTRL), _ACMP_CTRL_EN_SHIFT, init->enable); +} + +#if defined(_ACMP_INPUTSEL_VASEL_MASK) +/***************************************************************************//** + * @brief + * Setup the VA Source. + * + * @param[in] acmp + * Pointer to the ACMP peripheral register block. + * + * @param[in] vaconfig + * Pointer to the structure used to configure the VA source. This structure + * contains the input source as well as the 2 divider values. + ******************************************************************************/ +void ACMP_VASetup(ACMP_TypeDef *acmp, const ACMP_VAConfig_TypeDef *vaconfig) +{ + EFM_ASSERT(vaconfig->div0 < 64); + EFM_ASSERT(vaconfig->div1 < 64); + + BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VASEL_MASK, + vaconfig->input << _ACMP_INPUTSEL_VASEL_SHIFT); + BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVA_MASK, + vaconfig->div0 << _ACMP_HYSTERESIS0_DIVVA_SHIFT); + BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVA_MASK, + vaconfig->div1 << _ACMP_HYSTERESIS1_DIVVA_SHIFT); +} +#endif + +#if defined(_ACMP_INPUTSEL_VBSEL_MASK) +/***************************************************************************//** + * @brief + * Setup the VB Source. + * + * @param[in] acmp + * Pointer to the ACMP peripheral register block. + * + * @param[in] vbconfig + * Pointer to the structure used to configure the VB source. This structure + * contains the input source as well as the 2 divider values. + ******************************************************************************/ +void ACMP_VBSetup(ACMP_TypeDef *acmp, const ACMP_VBConfig_TypeDef *vbconfig) +{ + EFM_ASSERT(vbconfig->div0 < 64); + EFM_ASSERT(vbconfig->div1 < 64); + + BUS_RegMaskedWrite(&acmp->INPUTSEL, _ACMP_INPUTSEL_VBSEL_MASK, + vbconfig->input << _ACMP_INPUTSEL_VBSEL_SHIFT); + BUS_RegMaskedWrite(&acmp->HYSTERESIS0, _ACMP_HYSTERESIS0_DIVVB_MASK, + vbconfig->div0 << _ACMP_HYSTERESIS0_DIVVB_SHIFT); + BUS_RegMaskedWrite(&acmp->HYSTERESIS1, _ACMP_HYSTERESIS1_DIVVB_MASK, + vbconfig->div1 << _ACMP_HYSTERESIS1_DIVVB_SHIFT); +} +#endif + +/** @} (end addtogroup ACMP) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(ACMP_COUNT) && (ACMP_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_adc.c b/cpu/efm32_common/emlib/src/em_adc.c new file mode 100644 index 0000000000000..e812ed00d5746 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_adc.c @@ -0,0 +1,1082 @@ +/***************************************************************************//** + * @file em_adc.c + * @brief Analog to Digital Converter (ADC) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_adc.h" +#if defined( ADC_COUNT ) && ( ADC_COUNT > 0 ) + +#include "em_cmu.h" +#include "em_assert.h" +#include + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup ADC + * @brief Analog to Digital Converter (ADC) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of ADC register block pointer reference for assert statements. */ +#define ADC_REF_VALID(ref) ((ref) == ADC0) + +/** Max ADC clock */ +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) +#define ADC_MAX_CLOCK 13000000 +#else +#define ADC_MAX_CLOCK 16000000 +#endif + +/** Min ADC clock */ +#define ADC_MIN_CLOCK 32000 + +/** Helper defines for selecting ADC calibration and DEVINFO register fields. */ +#if defined( _DEVINFO_ADC0CAL0_1V25_GAIN_MASK ) +#define DEVINFO_ADC0_GAIN1V25_MASK _DEVINFO_ADC0CAL0_1V25_GAIN_MASK +#elif defined( _DEVINFO_ADC0CAL0_GAIN1V25_MASK ) +#define DEVINFO_ADC0_GAIN1V25_MASK _DEVINFO_ADC0CAL0_GAIN1V25_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL0_1V25_GAIN_SHIFT ) +#define DEVINFO_ADC0_GAIN1V25_SHIFT _DEVINFO_ADC0CAL0_1V25_GAIN_SHIFT +#elif defined( _DEVINFO_ADC0CAL0_GAIN1V25_SHIFT ) +#define DEVINFO_ADC0_GAIN1V25_SHIFT _DEVINFO_ADC0CAL0_GAIN1V25_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL0_1V25_OFFSET_MASK ) +#define DEVINFO_ADC0_OFFSET1V25_MASK _DEVINFO_ADC0CAL0_1V25_OFFSET_MASK +#elif defined( _DEVINFO_ADC0CAL0_OFFSET1V25_MASK ) +#define DEVINFO_ADC0_OFFSET1V25_MASK _DEVINFO_ADC0CAL0_OFFSET1V25_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL0_1V25_OFFSET_SHIFT ) +#define DEVINFO_ADC0_OFFSET1V25_SHIFT _DEVINFO_ADC0CAL0_1V25_OFFSET_SHIFT +#elif defined( _DEVINFO_ADC0CAL0_OFFSET1V25_SHIFT ) +#define DEVINFO_ADC0_OFFSET1V25_SHIFT _DEVINFO_ADC0CAL0_OFFSET1V25_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL0_2V5_GAIN_MASK ) +#define DEVINFO_ADC0_GAIN2V5_MASK _DEVINFO_ADC0CAL0_2V5_GAIN_MASK +#elif defined( _DEVINFO_ADC0CAL0_GAIN2V5_MASK ) +#define DEVINFO_ADC0_GAIN2V5_MASK _DEVINFO_ADC0CAL0_GAIN2V5_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL0_2V5_GAIN_SHIFT ) +#define DEVINFO_ADC0_GAIN2V5_SHIFT _DEVINFO_ADC0CAL0_2V5_GAIN_SHIFT +#elif defined( _DEVINFO_ADC0CAL0_GAIN2V5_SHIFT ) +#define DEVINFO_ADC0_GAIN2V5_SHIFT _DEVINFO_ADC0CAL0_GAIN2V5_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL0_2V5_OFFSET_MASK ) +#define DEVINFO_ADC0_OFFSET2V5_MASK _DEVINFO_ADC0CAL0_2V5_OFFSET_MASK +#elif defined( _DEVINFO_ADC0CAL0_OFFSET2V5_MASK ) +#define DEVINFO_ADC0_OFFSET2V5_MASK _DEVINFO_ADC0CAL0_OFFSET2V5_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL0_2V5_OFFSET_SHIFT ) +#define DEVINFO_ADC0_OFFSET2V5_SHIFT _DEVINFO_ADC0CAL0_2V5_OFFSET_SHIFT +#elif defined( _DEVINFO_ADC0CAL0_OFFSET2V5_SHIFT ) +#define DEVINFO_ADC0_OFFSET2V5_SHIFT _DEVINFO_ADC0CAL0_OFFSET2V5_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL1_VDD_GAIN_MASK ) +#define DEVINFO_ADC0_GAINVDD_MASK _DEVINFO_ADC0CAL1_VDD_GAIN_MASK +#elif defined( _DEVINFO_ADC0CAL1_GAINVDD_MASK ) +#define DEVINFO_ADC0_GAINVDD_MASK _DEVINFO_ADC0CAL1_GAINVDD_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL1_VDD_GAIN_SHIFT ) +#define DEVINFO_ADC0_GAINVDD_SHIFT _DEVINFO_ADC0CAL1_VDD_GAIN_SHIFT +#elif defined( _DEVINFO_ADC0CAL1_GAINVDD_SHIFT ) +#define DEVINFO_ADC0_GAINVDD_SHIFT _DEVINFO_ADC0CAL1_GAINVDD_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL1_VDD_OFFSET_MASK ) +#define DEVINFO_ADC0_OFFSETVDD_MASK _DEVINFO_ADC0CAL1_VDD_OFFSET_MASK +#elif defined( _DEVINFO_ADC0CAL1_OFFSETVDD_MASK ) +#define DEVINFO_ADC0_OFFSETVDD_MASK _DEVINFO_ADC0CAL1_OFFSETVDD_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL1_VDD_OFFSET_SHIFT ) +#define DEVINFO_ADC0_OFFSETVDD_SHIFT _DEVINFO_ADC0CAL1_VDD_OFFSET_SHIFT +#elif defined( _DEVINFO_ADC0CAL1_OFFSETVDD_SHIFT ) +#define DEVINFO_ADC0_OFFSETVDD_SHIFT _DEVINFO_ADC0CAL1_OFFSETVDD_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL1_5VDIFF_GAIN_MASK ) +#define DEVINFO_ADC0_GAIN5VDIFF_MASK _DEVINFO_ADC0CAL1_5VDIFF_GAIN_MASK +#elif defined( _DEVINFO_ADC0CAL1_GAIN5VDIFF_MASK ) +#define DEVINFO_ADC0_GAIN5VDIFF_MASK _DEVINFO_ADC0CAL1_GAIN5VDIFF_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL1_5VDIFF_GAIN_SHIFT ) +#define DEVINFO_ADC0_GAIN5VDIFF_SHIFT _DEVINFO_ADC0CAL1_5VDIFF_GAIN_SHIFT +#elif defined( _DEVINFO_ADC0CAL1_GAIN5VDIFF_SHIFT ) +#define DEVINFO_ADC0_GAIN5VDIFF_SHIFT _DEVINFO_ADC0CAL1_GAIN5VDIFF_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_MASK ) +#define DEVINFO_ADC0_OFFSET5VDIFF_MASK _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_MASK +#elif defined( _DEVINFO_ADC0CAL1_OFFSET5VDIFF_MASK ) +#define DEVINFO_ADC0_OFFSET5VDIFF_MASK _DEVINFO_ADC0CAL1_OFFSET5VDIFF_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_SHIFT ) +#define DEVINFO_ADC0_OFFSET5VDIFF_SHIFT _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_SHIFT +#elif defined( _DEVINFO_ADC0CAL1_OFFSET5VDIFF_SHIFT ) +#define DEVINFO_ADC0_OFFSET5VDIFF_SHIFT _DEVINFO_ADC0CAL1_OFFSET5VDIFF_SHIFT +#endif + +#if defined( _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_MASK ) +#define DEVINFO_ADC0_OFFSET2XVDD_MASK _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_MASK +#elif defined( _DEVINFO_ADC0CAL2_OFFSET2XVDD_MASK ) +#define DEVINFO_ADC0_OFFSET2XVDD_MASK _DEVINFO_ADC0CAL2_OFFSET2XVDD_MASK +#endif + +#if defined( _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_SHIFT ) +#define DEVINFO_ADC0_OFFSET2XVDD_SHIFT _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_SHIFT +#elif defined( _DEVINFO_ADC0CAL2_OFFSET2XVDD_SHIFT ) +#define DEVINFO_ADC0_OFFSET2XVDD_SHIFT _DEVINFO_ADC0CAL2_OFFSET2XVDD_SHIFT +#endif + +/** @endcond */ + + +/******************************************************************************* + *************************** LOCAL FUNCTIONS ******************************* + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/***************************************************************************//** + * @brief + * Load ADC calibration register for a selected reference and conversion mode. + * + * @details + * During production, calibration values are stored in the device + * information page for internal references. Notice that for external references, + * calibration values must be determined explicitly, and this function + * will not modify the calibration register for external references. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] ref + * Reference to load calibrated values for. No values are loaded for + * external references. + * + * @param[in] setScanCal + * Select scan mode (true) or single mode (false) calibration load. + ******************************************************************************/ +static void ADC_LoadDevinfoCal(ADC_TypeDef *adc, + ADC_Ref_TypeDef ref, + bool setScanCal) +{ + uint32_t calReg; + uint32_t newCal; + uint32_t mask; + uint32_t shift; + + if (setScanCal) + { + shift = _ADC_CAL_SCANOFFSET_SHIFT; + mask = ~(_ADC_CAL_SCANOFFSET_MASK +#if defined( _ADC_CAL_SCANOFFSETINV_MASK ) + | _ADC_CAL_SCANOFFSETINV_MASK +#endif + | _ADC_CAL_SCANGAIN_MASK); + } + else + { + shift = _ADC_CAL_SINGLEOFFSET_SHIFT; + mask = ~(_ADC_CAL_SINGLEOFFSET_MASK +#if defined( _ADC_CAL_SINGLEOFFSETINV_MASK ) + | _ADC_CAL_SINGLEOFFSETINV_MASK +#endif + | _ADC_CAL_SINGLEGAIN_MASK); + } + + calReg = adc->CAL & mask; + newCal = 0; + + switch (ref) + { + case adcRef1V25: + newCal |= ((DEVINFO->ADC0CAL0 & DEVINFO_ADC0_GAIN1V25_MASK) + >> DEVINFO_ADC0_GAIN1V25_SHIFT) + << _ADC_CAL_SINGLEGAIN_SHIFT; + newCal |= ((DEVINFO->ADC0CAL0 & DEVINFO_ADC0_OFFSET1V25_MASK) + >> DEVINFO_ADC0_OFFSET1V25_SHIFT) + << _ADC_CAL_SINGLEOFFSET_SHIFT; +#if defined( _ADC_CAL_SINGLEOFFSETINV_MASK ) + newCal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_NEGSEOFFSET1V25_MASK) + >> _DEVINFO_ADC0CAL0_NEGSEOFFSET1V25_SHIFT) + << _ADC_CAL_SINGLEOFFSETINV_SHIFT; +#endif + break; + + case adcRef2V5: + newCal |= ((DEVINFO->ADC0CAL0 & DEVINFO_ADC0_GAIN2V5_MASK) + >> DEVINFO_ADC0_GAIN2V5_SHIFT) + << _ADC_CAL_SINGLEGAIN_SHIFT; + newCal |= ((DEVINFO->ADC0CAL0 & DEVINFO_ADC0_OFFSET2V5_MASK) + >> DEVINFO_ADC0_OFFSET2V5_SHIFT) + << _ADC_CAL_SINGLEOFFSET_SHIFT; +#if defined( _ADC_CAL_SINGLEOFFSETINV_MASK ) + newCal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_NEGSEOFFSET2V5_MASK) + >> _DEVINFO_ADC0CAL0_NEGSEOFFSET2V5_SHIFT) + << _ADC_CAL_SINGLEOFFSETINV_SHIFT; +#endif + break; + + case adcRefVDD: + newCal |= ((DEVINFO->ADC0CAL1 & DEVINFO_ADC0_GAINVDD_MASK) + >> DEVINFO_ADC0_GAINVDD_SHIFT) + << _ADC_CAL_SINGLEGAIN_SHIFT; + newCal |= ((DEVINFO->ADC0CAL1 & DEVINFO_ADC0_OFFSETVDD_MASK) + >> DEVINFO_ADC0_OFFSETVDD_SHIFT) + << _ADC_CAL_SINGLEOFFSET_SHIFT; +#if defined( _ADC_CAL_SINGLEOFFSETINV_MASK ) + newCal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_NEGSEOFFSETVDD_MASK) + >> _DEVINFO_ADC0CAL1_NEGSEOFFSETVDD_SHIFT) + << _ADC_CAL_SINGLEOFFSETINV_SHIFT; +#endif + break; + + case adcRef5VDIFF: + newCal |= ((DEVINFO->ADC0CAL1 & DEVINFO_ADC0_GAIN5VDIFF_MASK) + >> DEVINFO_ADC0_GAIN5VDIFF_SHIFT) + << _ADC_CAL_SINGLEGAIN_SHIFT; + newCal |= ((DEVINFO->ADC0CAL1 & DEVINFO_ADC0_OFFSET5VDIFF_MASK) + >> DEVINFO_ADC0_OFFSET5VDIFF_SHIFT) + << _ADC_CAL_SINGLEOFFSET_SHIFT; +#if defined( _ADC_CAL_SINGLEOFFSETINV_MASK ) + newCal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_NEGSEOFFSET5VDIFF_MASK) + >> _DEVINFO_ADC0CAL1_NEGSEOFFSET5VDIFF_SHIFT) + << _ADC_CAL_SINGLEOFFSETINV_SHIFT; +#endif + break; + + case adcRef2xVDD: + /* There is no gain calibration for this reference */ + newCal |= ((DEVINFO->ADC0CAL2 & DEVINFO_ADC0_OFFSET2XVDD_MASK) + >> DEVINFO_ADC0_OFFSET2XVDD_SHIFT) + << _ADC_CAL_SINGLEOFFSET_SHIFT; +#if defined( _ADC_CAL_SINGLEOFFSETINV_MASK ) + newCal |= ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_NEGSEOFFSET2XVDD_MASK) + >> _DEVINFO_ADC0CAL2_NEGSEOFFSET2XVDD_SHIFT) + << _ADC_CAL_SINGLEOFFSETINV_SHIFT; +#endif + break; + +#if defined( _ADC_SINGLECTRLX_VREFSEL_VDDXWATT ) + case adcRefVddxAtt: + newCal |= ((DEVINFO->ADC0CAL1 & DEVINFO_ADC0_GAINVDD_MASK) + >> DEVINFO_ADC0_GAINVDD_SHIFT) + << _ADC_CAL_SINGLEGAIN_SHIFT; + newCal |= ((DEVINFO->ADC0CAL1 & DEVINFO_ADC0_OFFSETVDD_MASK) + >> DEVINFO_ADC0_OFFSETVDD_SHIFT) + << _ADC_CAL_SINGLEOFFSET_SHIFT; + newCal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_NEGSEOFFSETVDD_MASK) + >> _DEVINFO_ADC0CAL1_NEGSEOFFSETVDD_SHIFT) + << _ADC_CAL_SINGLEOFFSETINV_SHIFT; + break; +#endif + + /* For external references, the calibration must be determined for the + specific application and set by the user. Calibration data is also not + available for the internal references adcRefVBGR, adcRefVEntropy and + adcRefVBGRlow. */ + default: + newCal = 0; + break; + } + + adc->CAL = calReg | (newCal << shift); +} + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initialize ADC. + * + * @details + * Initializes common parts for both single conversion and scan sequence. + * In addition, single and/or scan control configuration must be done, please + * refer to ADC_InitSingle() and ADC_InitScan() respectively. + * + * On ADC architectures with the ADCn->SCANCHCONF register, + * ADC_ScanSingleEndedInit() and ADC_ScanDifferentialInit() can be used to + * assist scan conversion input setup. + * + * @note + * This function will stop any ongoing conversion. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] init + * Pointer to ADC initialization structure. + ******************************************************************************/ +void ADC_Init(ADC_TypeDef *adc, const ADC_Init_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(ADC_REF_VALID(adc)); + + /* Make sure conversion is not in progress */ + adc->CMD = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP; + + tmp = ((uint32_t)(init->ovsRateSel) << _ADC_CTRL_OVSRSEL_SHIFT) + | (((uint32_t)(init->timebase) << _ADC_CTRL_TIMEBASE_SHIFT) + & _ADC_CTRL_TIMEBASE_MASK) + | (((uint32_t)(init->prescale) << _ADC_CTRL_PRESC_SHIFT) + & _ADC_CTRL_PRESC_MASK) +#if defined ( _ADC_CTRL_LPFMODE_MASK ) + | ((uint32_t)(init->lpfMode) << _ADC_CTRL_LPFMODE_SHIFT) +#endif + | ((uint32_t)(init->warmUpMode) << _ADC_CTRL_WARMUPMODE_SHIFT); + + if (init->tailgate) + { + tmp |= ADC_CTRL_TAILGATE; + } + adc->CTRL = tmp; + + /* Set ADC EM2 clock configuration */ +#if defined( _ADC_CTRL_ADCCLKMODE_MASK ) + BUS_RegMaskedWrite(&ADC0->CTRL, + _ADC_CTRL_ADCCLKMODE_MASK | _ADC_CTRL_ASYNCCLKEN_MASK, + init->em2ClockConfig << _ADC_CTRL_ASYNCCLKEN_SHIFT); +#endif + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + /* Fix for errata ADC_EXXX */ + ADC_IntClear(adc, ADC_IFC_SCANUF); +#endif +} + + +#if defined( _ADC_SCANINPUTSEL_MASK ) +/***************************************************************************//** + * @brief + * Clear ADC scan input configuration. + * + * @param[in] scanInit + * Struct to hold the scan configuration, input configuration. + ******************************************************************************/ +void ADC_ScanInputClear(ADC_InitScan_TypeDef *scanInit) +{ + /* Clear input configuration */ + + /* Select none */ + scanInit->scanInputConfig.scanInputSel = 0xFFFFFFFF; + scanInit->scanInputConfig.scanInputEn = 0; + + /* Default alternative negative inputs */ + scanInit->scanInputConfig.scanNegSel = _ADC_SCANNEGSEL_RESETVALUE; +} + + +/***************************************************************************//** + * @brief + * Initialize ADC scan single-ended input configuration. + * + * @details + * Set configuration for ADC scan conversion with single-ended inputs. The + * ADC_InitScan_TypeDef struct updated from this function should be passed to + * ADC_InitScan(). + * + * @param[in] inputGroup + * ADC scan input group. See section 25.3.4 in the reference manual for + * more information. + * + * @param[in] singleEndedSel + * APORT select. + * + * @return + * Scan ID of selected ADC input. ee section 25.3.4 in the reference manual for + * more information. Note that the returned integer represents the bit position + * in ADCn_SCANMASK set by this function. The accumulated mask is stored in + * scanInit->scanInputConfig->scanInputEn. + ******************************************************************************/ +uint32_t ADC_ScanSingleEndedInputAdd(ADC_InitScan_TypeDef *scanInit, + ADC_ScanInputGroup_TypeDef inputGroup, + ADC_PosSel_TypeDef singleEndedSel) +{ + uint32_t currentSel; + uint32_t newSel; + uint32_t scanId; + + scanInit->diff = false; + + /* Check for unsupported APORTs */ + EFM_ASSERT((singleEndedSel <= adcPosSelAPORT0YCH0) || (singleEndedSel >= adcPosSelAPORT0YCH15)); + + /* Decode the input group select by shifting right by 3 */ + newSel = singleEndedSel >> 3; + + currentSel = (scanInit->scanInputConfig.scanInputSel >> (inputGroup * 8)) & 0xFF; + + /* If none selected */ + if (currentSel == 0xFF) + { + scanInit->scanInputConfig.scanInputSel &= ~(0xFF << (inputGroup * 8)); + scanInit->scanInputConfig.scanInputSel |= (newSel << (inputGroup * 8)); + } + else if (currentSel == newSel) + { + /* Ok, but do nothing. */ + } + else + { + /* Invalid channel range. A range is already selected for this group. */ + EFM_ASSERT(false); + } + + /* Update and return scan input enable mask (SCANMASK) */ + scanId = (inputGroup * 8) + (singleEndedSel & 0x7); + EFM_ASSERT(scanId < 32); + scanInit->scanInputConfig.scanInputEn |= 0x1 << scanId; + return scanId; +} + + +/***************************************************************************//** + * @brief + * Initialize ADC scan differential input configuration. + * + * @details + * Set configuration for ADC scan conversion with differential inputs. The + * ADC_InitScan_TypeDef struct updated by this function should be passed to + * ADC_InitScan(). + * + * @param[in] scanInit + * Struct to hold the scan and input configuration. + * + * @param[in] inputGroup + * ADC scan input group. See section 25.3.4 in the reference manual for + * more information. + * + * @param[in] posSel + * APORT bus pair select. The negative terminal is implicitly selected by + * the positive terminal. + * + * @param[in] negInput + * ADC scan alternative negative input. Set to adcScanNegInputDefault to select + * default negative input (implicit from posSel). + * + * @return + * Scan ID of selected ADC input. ee section 25.3.4 in the reference manual for + * more information. Note that the returned integer represents the bit position + * in ADCn_SCANMASK set by this function. The accumulated mask is stored in + * scanInit->scanInputConfig->scanInputEn. + ******************************************************************************/ +uint32_t ADC_ScanDifferentialInputAdd(ADC_InitScan_TypeDef *scanInit, + ADC_ScanInputGroup_TypeDef inputGroup, + ADC_PosSel_TypeDef posSel, + ADC_ScanNegInput_TypeDef negInput) +{ + uint32_t negInputRegMask = 0; + uint32_t negInputRegShift = 0; + uint32_t negInputRegVal = 0; + uint32_t scanId = 0; + + /* Do a single ended init, then update for differential scan. */ + scanId = ADC_ScanSingleEndedInputAdd(scanInit, inputGroup, posSel); + + /* Reset to differential mode */ + scanInit->diff = true; + + /* Set negative ADC input, unless the default is selected. */ + if (negInput != adcScanNegInputDefault) + { + if (scanId == 0) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT0NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT0NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 0); + } + else if (scanId == 2) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT2NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT2NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 0); + } + else if (scanId == 4) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT4NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT4NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 0); + } + else if (scanId == 6) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT6NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT6NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 0); + } + else if (scanId == 9) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT9NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT9NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 1); + } + else if (scanId == 11) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT11NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT11NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 1); + } + else if (scanId == 13) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT13NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT13NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 1); + } + else if (scanId == 15) + { + negInputRegMask = _ADC_SCANNEGSEL_INPUT15NEGSEL_MASK; + negInputRegShift = _ADC_SCANNEGSEL_INPUT15NEGSEL_SHIFT; + EFM_ASSERT(inputGroup == 1); + } + else + { + /* There is not negative input option for this positive input (negInput is posInput + 1). */ + EFM_ASSERT(false); + } + + /* Find ADC_SCANNEGSEL_CHxNSEL value for positive input 0, 2, 4 and 6 */ + if (inputGroup == 0) + { + switch (negInput) + { + case adcScanNegInput1: + negInputRegVal = _ADC_SCANNEGSEL_INPUT0NEGSEL_INPUT1; + break; + + case adcScanNegInput3: + negInputRegVal = _ADC_SCANNEGSEL_INPUT0NEGSEL_INPUT3; + break; + + case adcScanNegInput5: + negInputRegVal = _ADC_SCANNEGSEL_INPUT0NEGSEL_INPUT5; + break; + + case adcScanNegInput7: + negInputRegVal = _ADC_SCANNEGSEL_INPUT0NEGSEL_INPUT7; + break; + + default: + /* Invalid selection. Options are input 1, 3, 5 and 7. */ + EFM_ASSERT(false); + break; + } + } + else if (inputGroup == 1) + { + /* Find ADC_SCANNEGSEL_CHxNSEL value for positive input 9, 11, 13 and 15 */ + switch (negInput) + { + case adcScanNegInput8: + negInputRegVal = _ADC_SCANNEGSEL_INPUT9NEGSEL_INPUT8; + break; + + case adcScanNegInput10: + negInputRegVal = _ADC_SCANNEGSEL_INPUT9NEGSEL_INPUT10; + break; + + case adcScanNegInput12: + negInputRegVal = _ADC_SCANNEGSEL_INPUT9NEGSEL_INPUT12; + break; + + case adcScanNegInput14: + negInputRegVal = _ADC_SCANNEGSEL_INPUT9NEGSEL_INPUT14; + break; + + default: + /* Invalid selection. Options are input 8, 10, 12 and 14. */ + EFM_ASSERT(false); + break; + } + } + else + { + /* No alternative negative input for input group > 1 */ + EFM_ASSERT(false); + } + + /* Update config */ + scanInit->scanInputConfig.scanNegSel &= ~negInputRegMask; + scanInit->scanInputConfig.scanNegSel |= negInputRegVal << negInputRegShift; + } + return scanId; +} +#endif + + +/***************************************************************************//** + * @brief + * Initialize ADC scan sequence. + * + * @details + * Please refer to ADC_Start() for starting scan sequence. + * + * When selecting an external reference, the gain and offset calibration + * must be set explicitly (CAL register). For other references, the + * calibration is updated with values defined during manufacturing. + * + * @note + * This function will stop any ongoing scan sequence. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] init + * Pointer to ADC initialization structure. + ******************************************************************************/ +void ADC_InitScan(ADC_TypeDef *adc, const ADC_InitScan_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(ADC_REF_VALID(adc)); + + /* Make sure scan sequence is not in progress */ + adc->CMD = ADC_CMD_SCANSTOP; + + /* Load calibration data for selected reference */ + ADC_LoadDevinfoCal(adc, init->reference, true); + + tmp = 0 +#if defined ( _ADC_SCANCTRL_PRSSEL_MASK ) + | (init->prsSel << _ADC_SCANCTRL_PRSSEL_SHIFT) +#endif + | (init->acqTime << _ADC_SCANCTRL_AT_SHIFT) +#if defined ( _ADC_SCANCTRL_INPUTMASK_MASK ) + | init->input +#endif + | (init->resolution << _ADC_SCANCTRL_RES_SHIFT); + + if (init->prsEnable) + { + tmp |= ADC_SCANCTRL_PRSEN; + } + + if (init->leftAdjust) + { + tmp |= ADC_SCANCTRL_ADJ_LEFT; + } + +#if defined( _ADC_SCANCTRL_INPUTMASK_MASK ) + if (init->diff) +#elif defined( _ADC_SCANINPUTSEL_MASK ) + if (init->diff) +#endif + { + tmp |= ADC_SCANCTRL_DIFF; + } + + if (init->rep) + { +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + /* Scan repeat mode does not work on platform 2 as described in errata ADC_EXXX. */ + EFM_ASSERT(false); +#endif + tmp |= ADC_SCANCTRL_REP; + } + + /* Set scan reference. Check if reference configuraion is extended to SCANCTRLX. */ +#if defined ( _ADC_SCANCTRLX_VREFSEL_MASK ) + if (init->reference & ADC_CTRLX_VREFSEL_REG) + { + /* Select extension register */ + tmp |= ADC_SCANCTRL_REF_CONF; + } + else + { + tmp |= init->reference << _ADC_SCANCTRL_REF_SHIFT; + } +#else + tmp |= init->reference << _ADC_SCANCTRL_REF_SHIFT; +#endif + +#if defined( _ADC_SCANCTRL_INPUTMASK_MASK ) + tmp |= init->input; +#endif + + adc->SCANCTRL = tmp; + + /* Update SINGLECTRLX for reference select and PRS select */ +#if defined ( _ADC_SCANCTRLX_MASK ) + tmp = adc->SCANCTRLX & ~(_ADC_SCANCTRLX_VREFSEL_MASK + | _ADC_SCANCTRLX_PRSSEL_MASK + | _ADC_SCANCTRLX_FIFOOFACT_MASK); + if (init->reference & ADC_CTRLX_VREFSEL_REG) + { + tmp |= (init->reference & ~ADC_CTRLX_VREFSEL_REG) << _ADC_SCANCTRLX_VREFSEL_SHIFT; + } + + tmp |= init->prsSel << _ADC_SCANCTRLX_PRSSEL_SHIFT; + + if (init->fifoOverwrite) + { + tmp |= ADC_SCANCTRLX_FIFOOFACT_OVERWRITE; + } + + adc->SCANCTRLX = tmp; +#endif + +#if defined( _ADC_CTRL_SCANDMAWU_MASK ) + BUS_RegBitWrite(&adc->CTRL, _ADC_CTRL_SCANDMAWU_SHIFT, init->scanDmaEm2Wu); +#endif + + /* Write scan input configuration */ +#if defined( _ADC_SCANINPUTSEL_MASK ) + adc->SCANINPUTSEL = init->scanInputConfig.scanInputSel; + adc->SCANMASK = init->scanInputConfig.scanInputEn; + adc->SCANNEGSEL = init->scanInputConfig.scanNegSel; +#endif + + /* Assert for any APORT bus conflicts programming errors */ +#if defined( _ADC_BUSCONFLICT_MASK ) + tmp = adc->BUSREQ; + EFM_ASSERT(!(tmp & adc->BUSCONFLICT)); + EFM_ASSERT(!(adc->STATUS & _ADC_STATUS_PROGERR_MASK)); +#endif +} + + +/***************************************************************************//** + * @brief + * Initialize single ADC sample conversion. + * + * @details + * Please refer to ADC_Start() for starting single conversion. + * + * When selecting an external reference, the gain and offset calibration + * must be set explicitly (CAL register). For other references, the + * calibration is updated with values defined during manufacturing. + * + * @note + * This function will stop any ongoing single conversion. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] init + * Pointer to ADC initialization structure. + ******************************************************************************/ +void ADC_InitSingle(ADC_TypeDef *adc, const ADC_InitSingle_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(ADC_REF_VALID(adc)); + + /* Make sure single conversion is not in progress */ + adc->CMD = ADC_CMD_SINGLESTOP; + + /* Load calibration data for selected reference */ + ADC_LoadDevinfoCal(adc, init->reference, false); + + tmp = 0 +#if defined( _ADC_SINGLECTRL_PRSSEL_MASK ) + | (init->prsSel << _ADC_SINGLECTRL_PRSSEL_SHIFT) +#endif + | (init->acqTime << _ADC_SINGLECTRL_AT_SHIFT) +#if defined( _ADC_SINGLECTRL_INPUTSEL_MASK ) + | (init->input << _ADC_SINGLECTRL_INPUTSEL_SHIFT) +#endif +#if defined( _ADC_SINGLECTRL_POSSEL_MASK ) + | (init->posSel << _ADC_SINGLECTRL_POSSEL_SHIFT) +#endif +#if defined( _ADC_SINGLECTRL_NEGSEL_MASK ) + | (init->negSel << _ADC_SINGLECTRL_NEGSEL_SHIFT) +#endif + | ((uint32_t)(init->resolution) << _ADC_SINGLECTRL_RES_SHIFT); + + if (init->prsEnable) + { + tmp |= ADC_SINGLECTRL_PRSEN; + } + + if (init->leftAdjust) + { + tmp |= ADC_SINGLECTRL_ADJ_LEFT; + } + + if (init->diff) + { + tmp |= ADC_SINGLECTRL_DIFF; + } + + if (init->rep) + { + tmp |= ADC_SINGLECTRL_REP; + } + + /* Set single reference. Check if reference configuraion is extended to SINGLECTRLX. */ +#if defined ( _ADC_SINGLECTRLX_MASK ) + if (init->reference & ADC_CTRLX_VREFSEL_REG) + { + /* Select extension register */ + tmp |= ADC_SINGLECTRL_REF_CONF; + } + else + { + tmp |= (init->reference << _ADC_SINGLECTRL_REF_SHIFT); + } +#else + tmp |= (init->reference << _ADC_SINGLECTRL_REF_SHIFT); +#endif + adc->SINGLECTRL = tmp; + + /* Update SINGLECTRLX for reference select and PRS select */ +#if defined ( _ADC_SINGLECTRLX_VREFSEL_MASK ) + tmp = adc->SINGLECTRLX & (_ADC_SINGLECTRLX_VREFSEL_MASK + | _ADC_SINGLECTRLX_PRSSEL_MASK + | _ADC_SINGLECTRLX_FIFOOFACT_MASK); + if (init->reference & ADC_CTRLX_VREFSEL_REG) + { + tmp |= ((init->reference & ~ADC_CTRLX_VREFSEL_REG) << _ADC_SINGLECTRLX_VREFSEL_SHIFT); + } + + tmp |= ((init->prsSel << _ADC_SINGLECTRLX_PRSSEL_SHIFT)); + + if (init->fifoOverwrite) + { + tmp |= ADC_SINGLECTRLX_FIFOOFACT_OVERWRITE; + } + + adc->SINGLECTRLX = tmp; +#endif + + /* Set DMA availability in EM2 */ +#if defined( _ADC_CTRL_SINGLEDMAWU_MASK ) + BUS_RegBitWrite(&ADC0->CTRL, _ADC_CTRL_SINGLEDMAWU_SHIFT, init->singleDmaEm2Wu); +#endif + + /* Assert for any APORT bus conflicts programming errors */ +#if defined( _ADC_BUSCONFLICT_MASK ) + tmp = adc->BUSREQ; + EFM_ASSERT(!(tmp & adc->BUSCONFLICT)); + EFM_ASSERT(!(adc->STATUS & _ADC_STATUS_PROGERR_MASK)); +#endif +} + + +#if defined( _ADC_SCANDATAX_MASK ) +/***************************************************************************//** + * @brief + * Get scan result and scan select ID. + * + * @note + * Only use if scan data valid. This function does not check the DV flag. + * The return value is intended to be used as a index for the scan select ID. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[out] scanId + * Scan select ID of first data in scan FIFO. + * + * @return + * First scan data in scan FIFO. + ******************************************************************************/ +uint32_t ADC_DataIdScanGet(ADC_TypeDef *adc, uint32_t *scanId) +{ + uint32_t scanData; + + /* Pop data FIFO with scan ID */ + scanData = adc->SCANDATAX; + *scanId = (scanData & _ADC_SCANDATAX_SCANINPUTID_MASK) >> _ADC_SCANDATAX_SCANINPUTID_SHIFT; + return (scanData & _ADC_SCANDATAX_DATA_MASK) >> _ADC_SCANDATAX_DATA_SHIFT; +} +#endif + + +/***************************************************************************//** + * @brief + * Calculate prescaler value used to determine ADC clock. + * + * @details + * The ADC clock is given by: HFPERCLK / (prescale + 1). + * + * @param[in] adcFreq ADC frequency wanted. The frequency will automatically + * be adjusted to be within valid range according to reference manual. + * + * @param[in] hfperFreq Frequency in Hz of reference HFPER clock. Set to 0 to + * use currently defined HFPER clock setting. + * + * @return + * Prescaler value to use for ADC in order to achieve a clock value + * <= @p adcFreq. + ******************************************************************************/ +uint8_t ADC_PrescaleCalc(uint32_t adcFreq, uint32_t hfperFreq) +{ + uint32_t ret; + + /* Make sure selected ADC clock is within valid range */ + if (adcFreq > ADC_MAX_CLOCK) + { + adcFreq = ADC_MAX_CLOCK; + } + else if (adcFreq < ADC_MIN_CLOCK) + { + adcFreq = ADC_MIN_CLOCK; + } + + /* Use current HFPER frequency? */ + if (!hfperFreq) + { + hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER); + } + + ret = (hfperFreq + adcFreq - 1) / adcFreq; + if (ret) + { + ret--; + } + + return (uint8_t)ret; +} + + +/***************************************************************************//** + * @brief + * Reset ADC 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] adc + * Pointer to ADC peripheral register block. + ******************************************************************************/ +void ADC_Reset(ADC_TypeDef *adc) +{ + /* Stop conversions, before resetting other registers. */ + adc->CMD = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP; + adc->SINGLECTRL = _ADC_SINGLECTRL_RESETVALUE; +#if defined( _ADC_SINGLECTRLX_MASK ) + adc->SINGLECTRLX = _ADC_SINGLECTRLX_RESETVALUE; +#endif + adc->SCANCTRL = _ADC_SCANCTRL_RESETVALUE; +#if defined( _ADC_SCANCTRLX_MASK ) + adc->SCANCTRLX = _ADC_SCANCTRLX_RESETVALUE; +#endif + adc->CTRL = _ADC_CTRL_RESETVALUE; + adc->IEN = _ADC_IEN_RESETVALUE; + adc->IFC = _ADC_IFC_MASK; + adc->BIASPROG = _ADC_BIASPROG_RESETVALUE; +#if defined( _ADC_SCANMASK_MASK ) + adc->SCANMASK = _ADC_SCANMASK_RESETVALUE; +#endif +#if defined( _ADC_SCANINPUTSEL_MASK ) + adc->SCANINPUTSEL = _ADC_SCANINPUTSEL_RESETVALUE; +#endif +#if defined( _ADC_SCANNEGSEL_MASK ) + adc->SCANNEGSEL = _ADC_SCANNEGSEL_RESETVALUE; +#endif + + /* Clear data FIFOs */ +#if defined( _ADC_SINGLEFIFOCLEAR_MASK ) + adc->SINGLEFIFOCLEAR |= ADC_SINGLEFIFOCLEAR_SINGLEFIFOCLEAR; + adc->SCANFIFOCLEAR |= ADC_SCANFIFOCLEAR_SCANFIFOCLEAR; +#endif + + /* Load calibration values for the 1V25 internal reference. */ + ADC_LoadDevinfoCal(adc, adcRef1V25, false); + ADC_LoadDevinfoCal(adc, adcRef1V25, true); + +#if defined( _ADC_SCANINPUTSEL_MASK ) + /* Do not reset route register, setting should be done independently */ +#endif +} + + +/***************************************************************************//** + * @brief + * Calculate timebase value in order to get a timebase providing at least 1us. + * + * @param[in] hfperFreq Frequency in Hz of reference HFPER clock. Set to 0 to + * use currently defined HFPER clock setting. + * + * @return + * Timebase value to use for ADC in order to achieve at least 1 us. + ******************************************************************************/ +uint8_t ADC_TimebaseCalc(uint32_t hfperFreq) +{ + if (!hfperFreq) + { + hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER); + + /* Just in case, make sure we get non-zero freq for below calculation */ + if (!hfperFreq) + { + hfperFreq = 1; + } + } +#if defined( _EFM32_GIANT_FAMILY ) || defined( _EFM32_WONDER_FAMILY ) + /* Handle errata on Giant Gecko, max TIMEBASE is 5 bits wide or max 0x1F */ + /* cycles. This will give a warmp up time of e.g. 0.645us, not the */ + /* required 1us when operating at 48MHz. One must also increase acqTime */ + /* to compensate for the missing clock cycles, adding up to 1us in total.*/ + /* See reference manual for details. */ + if ( hfperFreq > 32000000 ) + { + hfperFreq = 32000000; + } +#endif + /* Determine number of HFPERCLK cycle >= 1us */ + hfperFreq += 999999; + hfperFreq /= 1000000; + + /* Return timebase value (N+1 format) */ + return (uint8_t)(hfperFreq - 1); +} + + +/** @} (end addtogroup ADC) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(ADC_COUNT) && (ADC_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_aes.c b/cpu/efm32_common/emlib/src/em_aes.c new file mode 100644 index 0000000000000..da2cd9f1cea1f --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_aes.c @@ -0,0 +1,1387 @@ +/***************************************************************************//** + * @file em_aes.c + * @brief Advanced Encryption Standard (AES) accelerator peripheral API. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_aes.h" +#if defined(AES_COUNT) && (AES_COUNT > 0) + +#include "em_assert.h" +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup AES + * @brief Advanced Encryption Standard Accelerator (AES) Peripheral API. + * @details + * This API is intended for use on Silicon Labs target devices, and the + * following input/output notations should be noted: + * + * @li Input/output data (plaintext, ciphertext, key etc) are treated as + * byte arrays, starting with most significant byte. Ie, 32 bytes of + * plaintext (B0...B31) is located in memory in the same order, with B0 at + * the lower address and B31 at the higher address. + * + * @li Byte arrays must always be a multiple of AES block size, ie a multiple + * of 16. Padding, if required, is done at the end of the byte array. + * + * @li Byte arrays should be word (32 bit) aligned for performance + * considerations, since the array is accessed with 32 bit access type. + * The Cortex-M supports unaligned accesses, but with a performance penalty. + * + * @li It is possible to specify the same output buffer as input buffer + * as long as they point to the same address. In that case the provided input + * buffer is replaced with the encrypted/decrypted output. Notice that the + * buffers must be exactly overlapping. If partly overlapping, the + * behaviour is undefined. + * + * It is up to the user to use a cipher mode according to its requirements + * in order to not break security. Please refer to specific cipher mode + * theory for details. + * + * References: + * @li Wikipedia - Cipher modes, http://en.wikipedia.org/wiki/Cipher_modes + * + * @li Recommendation for Block Cipher Modes of Operation, + * NIST Special Publication 800-38A, 2001 Edition, + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#define AES_BLOCKSIZE 16 + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Cipher-block chaining (CBC) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Plaintext Plaintext + * | | + * V V + * InitVector ->XOR +-------------->XOR + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * |---------+ | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Ciphertext Ciphertext + * |----------+ | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | decryption | | | decryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * InitVector ->XOR +-------------->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 128 bit encryption key. When doing + * decryption, this is the 128 bit decryption key. The decryption key may + * be generated from the encryption key with AES_DecryptKey128(). + * On devices supporting key buffering this argument can be null, if so, the + * key will not be loaded, as it is assumed the key has been loaded + * into KEYHA previously. + * + * @param[in] iv + * 128 bit initalization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void AES_CBC128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt) +{ + int i; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + const uint32_t *_iv = (const uint32_t *)iv; + /* Need to buffer one block when decrypting in case 'out' replaces 'in' */ + uint32_t prev[4]; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + /* Number of blocks to process */ + len /= AES_BLOCKSIZE; + + #if defined( AES_CTRL_KEYBUFEN ) + if (key) + { + /* Load key into high key for key buffer usage */ + for (i = 3; i >= 0; i--) + { + AES->KEYHA = __REV(_key[i]); + } + } + #endif + + if (encrypt) + { + /* Enable encryption with auto start using XOR */ + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_XORSTART; + #else + AES->CTRL = AES_CTRL_XORSTART; + #endif + + /* Load initialization vector, since writing to DATA, it will */ + /* not trigger encryption. */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(_iv[i]); + } + + /* Encrypt data */ + while (len--) + { + #if !defined( AES_CTRL_KEYBUFEN ) + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_key[i]); + } + #endif + + /* Load data and trigger encryption */ + for (i = 3; i >= 0; i--) + { + AES->XORDATA = __REV(_in[i]); + } + _in += 4; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA); + } + _out += 4; + } + } + else + { + /* Select decryption mode */ + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART; + #else + AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_DATASTART; + #endif + + /* Copy init vector to previous buffer to avoid special handling */ + for (i = 0; i < 4; i++) + { + prev[i] = _iv[i]; + } + + /* Decrypt data */ + while (len--) + { + #if !defined( AES_CTRL_KEYBUFEN ) + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_key[i]); + } + #endif + + /* Load data and trigger decryption */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(_in[i]); + } + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* In order to avoid additional buffer, we use HW directly for XOR and buffer */ + /* (Writing to XORDATA will not trigger encoding, triggering enabled on DATA.) */ + for (i = 3; i >= 0; i--) + { + AES->XORDATA = __REV(prev[i]); + prev[i] = _in[i]; + } + _in += 4; + + /* Then fetch decrypted data, we have to do it in a separate loop */ + /* due to internal auto-shifting of words */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA); + } + _out += 4; + } + } +} + + +#if defined( AES_CTRL_AES256 ) +/***************************************************************************//** + * @brief + * Cipher-block chaining (CBC) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see AES_CBC128() for CBC figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with AES_DecryptKey256(). + * + * @param[in] iv + * 128 bit initalization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void AES_CBC256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt) +{ + int i; + int j; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + const uint32_t *_iv = (const uint32_t *)iv; + /* Need to buffer one block when decrypting in case output replaces input */ + uint32_t prev[4]; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + /* Number of blocks to process */ + len /= AES_BLOCKSIZE; + + if (encrypt) + { + /* Enable encryption with auto start using XOR */ + AES->CTRL = AES_CTRL_AES256 | AES_CTRL_XORSTART; + + /* Load initialization vector, since writing to DATA, it will */ + /* not trigger encryption. */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(_iv[i]); + } + + /* Encrypt data */ + while (len--) + { + /* Load key and data and trigger encryption */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_key[j]); + AES->KEYHA = __REV(_key[i]); + /* Write data last, since will trigger encryption on last iteration */ + AES->XORDATA = __REV(_in[i]); + } + _in += 4; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA); + } + _out += 4; + } + } + else + { + /* Select decryption mode */ + AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DECRYPT | AES_CTRL_DATASTART; + + /* Copy init vector to previous buffer to avoid special handling */ + for (i = 0; i < 4; i++) + { + prev[i] = _iv[i]; + } + + /* Decrypt data */ + while (len--) + { + /* Load key and data and trigger decryption */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_key[j]); + AES->KEYHA = __REV(_key[i]); + /* Write data last, since will trigger encryption on last iteration */ + AES->DATA = __REV(_in[i]); + } + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* In order to avoid additional buffer, we use HW directly for XOR and buffer */ + for (i = 3; i >= 0; i--) + { + AES->XORDATA = __REV(prev[i]); + prev[i] = _in[i]; + } + _in += 4; + + /* Then fetch decrypted data, we have to do it in a separate loop */ + /* due to internal auto-shifting of words */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA); + } + _out += 4; + } + } +} +#endif + + +/***************************************************************************//** + * @brief + * Cipher feedback (CFB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * Plaintext ->XOR | Plaintext ->XOR + * |---------+ | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * XOR<- Ciphertext XOR<- Ciphertext + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initalization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void AES_CFB128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt) +{ + int i; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + const uint32_t *_iv = (const uint32_t *)iv; + const uint32_t *data; + uint32_t tmp[4]; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART; + #else + AES->CTRL = AES_CTRL_DATASTART; + #endif + + #if defined( AES_CTRL_KEYBUFEN ) + /* Load key into high key for key buffer usage */ + for (i = 3; i >= 0; i--) + { + AES->KEYHA = __REV(_key[i]); + } + #endif + + /* Encrypt/decrypt data */ + data = _iv; + len /= AES_BLOCKSIZE; + while (len--) + { + #if !defined( AES_CTRL_KEYBUFEN ) + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_key[i]); + } + #endif + + /* Load data and trigger encryption */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(data[i]); + } + + /* Do some required processing before waiting for completion */ + if (encrypt) + { + data = _out; + } + else + { + /* Must copy current ciphertext block since it may be overwritten */ + for (i = 0; i < 4; i++) + { + tmp[i] = _in[i]; + } + data = tmp; + } + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA) ^ _in[i]; + } + _out += 4; + _in += 4; + } +} + + +#if defined( AES_CTRL_AES256 ) +/***************************************************************************//** + * @brief + * Cipher feedback (CFB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see AES_CFB128() for CFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initalization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void AES_CFB256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv, + bool encrypt) +{ + int i; + int j; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + const uint32_t *_iv = (const uint32_t *)iv; + const uint32_t *data; + uint32_t tmp[4]; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + /* Select encryption mode */ + AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DATASTART; + + /* Encrypt/decrypt data */ + data = _iv; + len /= AES_BLOCKSIZE; + while (len--) + { + /* Load key and block to be encrypted/decrypted */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_key[j]); + AES->KEYHA = __REV(_key[i]); + /* Write data last, since will trigger encryption on last iteration */ + AES->DATA = __REV(data[i]); + } + + /* Do some required processing before waiting for completion */ + if (encrypt) + { + data = _out; + } + else + { + /* Must copy current ciphertext block since it may be overwritten */ + for (i = 0; i < 4; i++) + { + tmp[i] = _in[i]; + } + data = tmp; + } + + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA) ^ _in[i]; + } + _out += 4; + _in += 4; + } +} +#endif + + +/***************************************************************************//** + * @brief + * Counter (CTR) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Counter Counter + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * Plaintext ->XOR Plaintext ->XOR + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Counter Counter + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * Ciphertext ->XOR Ciphertext ->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key. + * On devices supporting key buffering this argument can be null, if so, the + * key will not be loaded, as it is assumed the key has been loaded + * into KEYHA previously. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. + ******************************************************************************/ +void AES_CTR128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + uint8_t *ctr, + AES_CtrFuncPtr_TypeDef ctrFunc) +{ + int i; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + uint32_t *_ctr = (uint32_t *)ctr; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + EFM_ASSERT(ctrFunc); + + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART; + #else + AES->CTRL = AES_CTRL_DATASTART; + #endif + + #if defined( AES_CTRL_KEYBUFEN ) + if (key) + { + /* Load key into high key for key buffer usage */ + for (i = 3; i >= 0; i--) + { + AES->KEYHA = __REV(_key[i]); + } + } + #endif + + /* Encrypt/decrypt data */ + len /= AES_BLOCKSIZE; + while (len--) + { + #if !defined( AES_CTRL_KEYBUFEN ) + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_key[i]); + } + #endif + + /* Load ctr to be encrypted/decrypted */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(_ctr[i]); + } + /* Increment ctr for next use */ + ctrFunc(ctr); + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA) ^ _in[i]; + } + _out += 4; + _in += 4; + } +} + + +#if defined( AES_CTRL_AES256 ) +/***************************************************************************//** + * @brief + * Counter (CTR) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see AES_CTR128() for CTR figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. + ******************************************************************************/ +void AES_CTR256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + uint8_t *ctr, + AES_CtrFuncPtr_TypeDef ctrFunc) +{ + int i; + int j; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + uint32_t *_ctr = (uint32_t *)ctr; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + EFM_ASSERT(ctrFunc); + + /* Select encryption mode, with auto trigger */ + AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DATASTART; + + /* Encrypt/decrypt data */ + len /= AES_BLOCKSIZE; + while (len--) + { + /* Load key and block to be encrypted/decrypted */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_key[j]); + AES->KEYHA = __REV(_key[i]); + /* Write data last, since will trigger encryption on last iteration */ + AES->DATA = __REV(_ctr[i]); + } + /* Increment ctr for next use */ + ctrFunc(ctr); + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA) ^ _in[i]; + } + _out += 4; + _in += 4; + } +} +#endif + + +/***************************************************************************//** + * @brief + * Update last 32 bits of 128 bit counter, by incrementing with 1. + * + * @details + * Notice that no special consideration is given to possible wrap around. If + * 32 least significant bits are 0xFFFFFFFF, they will be updated to 0x00000000, + * ignoring overflow. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[in,out] ctr + * Buffer holding 128 bit counter to be updated. + ******************************************************************************/ +void AES_CTRUpdate32Bit(uint8_t *ctr) +{ + uint32_t *_ctr = (uint32_t *)ctr; + + _ctr[3] = __REV(__REV(_ctr[3]) + 1); +} + + +/***************************************************************************//** + * @brief + * Generate 128 bit decryption key from 128 bit encryption key. The decryption + * key is used for some cipher modes when decrypting. + * + * @details + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place 128 bit decryption key. Must be at least 16 bytes long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding 128 bit encryption key. Must be at least 16 bytes long. + ******************************************************************************/ +void AES_DecryptKey128(uint8_t *out, const uint8_t *in) +{ + int i; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_in[i]); + } + + /* Do dummy encryption to generate decrypt key */ + AES->CTRL = 0; + AES_IntClear(AES_IF_DONE); + AES->CMD = AES_CMD_START; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save decryption key */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->KEYLA); + } +} + + +#if defined( AES_CTRL_AES256 ) +/***************************************************************************//** + * @brief + * Generate 256 bit decryption key from 256 bit encryption key. The decryption + * key is used for some cipher modes when decrypting. + * + * @details + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place 256 bit decryption key. Must be at least 32 bytes long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding 256 bit encryption key. Must be at least 32 bytes long. + ******************************************************************************/ +void AES_DecryptKey256(uint8_t *out, const uint8_t *in) +{ + int i; + int j; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + + /* Load key */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_in[j]); + AES->KEYHA = __REV(_in[i]); + } + + /* Do dummy encryption to generate decrypt key */ + AES->CTRL = AES_CTRL_AES256; + AES->CMD = AES_CMD_START; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save decryption key */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + _out[j] = __REV(AES->KEYLA); + _out[i] = __REV(AES->KEYHA); + } +} +#endif + + +/***************************************************************************//** + * @brief + * Electronic Codebook (ECB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Plaintext Plaintext + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Ciphertext Ciphertext + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | decryption | | decryption | + * +--------------+ +--------------+ + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 128 bit encryption key. When doing + * decryption, this is the 128 bit decryption key. The decryption key may + * be generated from the encryption key with AES_DecryptKey128(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void AES_ECB128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + bool encrypt) +{ + int i; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + #if defined( AES_CTRL_KEYBUFEN ) + /* Load key into high key for key buffer usage */ + for (i = 3; i >= 0; i--) + { + AES->KEYHA = __REV(_key[i]); + } + #endif + + if (encrypt) + { + /* Select encryption mode */ + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART; + #else + AES->CTRL = AES_CTRL_DATASTART; + #endif + } + else + { + /* Select decryption mode */ + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_KEYBUFEN | AES_CTRL_DATASTART; + #else + AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_DATASTART; + #endif + } + + /* Encrypt/decrypt data */ + len /= AES_BLOCKSIZE; + while (len--) + { + #if !defined( AES_CTRL_KEYBUFEN ) + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_key[i]); + } + #endif + + /* Load block to be encrypted/decrypted */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(_in[i]); + } + _in += 4; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA); + } + _out += 4; + } +} + + +#if defined( AES_CTRL_AES256 ) +/***************************************************************************//** + * @brief + * Electronic Codebook (ECB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see AES_ECB128() for ECB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with AES_DecryptKey256(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void AES_ECB256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + bool encrypt) +{ + int i; + int j; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + if (encrypt) + { + /* Select encryption mode */ + AES->CTRL = AES_CTRL_AES256 | AES_CTRL_DATASTART; + } + else + { + /* Select decryption mode */ + AES->CTRL = AES_CTRL_DECRYPT | AES_CTRL_AES256 | AES_CTRL_DATASTART; + } + + /* Encrypt/decrypt data */ + len /= AES_BLOCKSIZE; + while (len--) + { + /* Load key and block to be encrypted/decrypted */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_key[j]); + AES->KEYHA = __REV(_key[i]); + /* Write data last, since will trigger encryption on last iteration */ + AES->DATA = __REV(_in[i]); + } + _in += 4; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA); + } + _out += 4; + } +} +#endif + + +/***************************************************************************//** + * @brief + * Output feedback (OFB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * |---------+ | + * V V + * Plaintext ->XOR Plaintext ->XOR + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * |---------+ | + * V V + * Ciphertext ->XOR Ciphertext ->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key. + * + * @param[in] iv + * 128 bit initalization vector to use. + ******************************************************************************/ +void AES_OFB128(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv) +{ + int i; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + const uint32_t *_iv = (const uint32_t *)iv; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + /* Select encryption mode, trigger explicitly by command */ + #if defined( AES_CTRL_KEYBUFEN ) + AES->CTRL = AES_CTRL_KEYBUFEN; + #else + AES->CTRL = 0; + #endif + + /* Load key into high key for key buffer usage */ + /* Load initialization vector */ + for (i = 3; i >= 0; i--) + { + #if defined( AES_CTRL_KEYBUFEN ) + AES->KEYHA = __REV(_key[i]); + #endif + AES->DATA = __REV(_iv[i]); + } + + /* Encrypt/decrypt data */ + len /= AES_BLOCKSIZE; + while (len--) + { + #if !defined( AES_CTRL_KEYBUFEN ) + /* Load key */ + for (i = 3; i >= 0; i--) + { + AES->KEYLA = __REV(_key[i]); + } + #endif + + AES->CMD = AES_CMD_START; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA) ^ _in[i]; + } + _out += 4; + _in += 4; + } +} + + +#if defined( AES_CTRL_AES256 ) +/***************************************************************************//** + * @brief + * Output feedback (OFB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see AES_OFB128() for OFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in] iv + * 128 bit initalization vector to use. + ******************************************************************************/ +void AES_OFB256(uint8_t *out, + const uint8_t *in, + unsigned int len, + const uint8_t *key, + const uint8_t *iv) +{ + int i; + int j; + uint32_t *_out = (uint32_t *)out; + const uint32_t *_in = (const uint32_t *)in; + const uint32_t *_key = (const uint32_t *)key; + const uint32_t *_iv = (const uint32_t *)iv; + + EFM_ASSERT(!(len % AES_BLOCKSIZE)); + + /* Select encryption mode, trigger explicitly by command */ + AES->CTRL = AES_CTRL_AES256; + + /* Load initialization vector */ + for (i = 3; i >= 0; i--) + { + AES->DATA = __REV(_iv[i]); + } + + /* Encrypt/decrypt data */ + len /= AES_BLOCKSIZE; + while (len--) + { + /* Load key */ + for (i = 3, j = 7; i >= 0; i--, j--) + { + AES->KEYLA = __REV(_key[j]); + AES->KEYHA = __REV(_key[i]); + } + + AES->CMD = AES_CMD_START; + + /* Wait for completion */ + while (AES->STATUS & AES_STATUS_RUNNING) + ; + + /* Save encrypted/decrypted data */ + for (i = 3; i >= 0; i--) + { + _out[i] = __REV(AES->DATA) ^ _in[i]; + } + _out += 4; + _in += 4; + } +} +#endif + + +/** @} (end addtogroup AES) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(AES_COUNT) && (AES_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_assert.c b/cpu/efm32_common/emlib/src/em_assert.c new file mode 100644 index 0000000000000..9e9d99fad41cf --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_assert.c @@ -0,0 +1,69 @@ +/***************************************************************************//** + * @file em_assert.c + * @brief Assert API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#include "em_assert.h" + +#if defined(DEBUG_EFM) + +/***************************************************************************//** + * @brief + * EFM internal assert handling. + * + * This function is invoked through EFM_ASSERT() macro usage only, it should + * not be used explicitly. + * + * Currently this implementation only enters an indefinite loop, allowing + * the use of a debugger to determine cause of failure. By defining + * DEBUG_EFM_USER to the preprocessor for all files, a user defined version + * of this function must be defined and will be invoked instead, possibly + * providing output of assertion location. + * + * Please notice that this function is not used unless DEBUG_EFM is defined + * during preprocessing of EFM_ASSERT() usage. + * + * @par file + * Name of source file where assertion failed. + * + * @par line + * Line number in source file where assertion failed. + ******************************************************************************/ +void assertEFM(const char *file, int line) +{ + (void)file; /* Unused parameter */ + (void)line; /* Unused parameter */ + + while (1) + ; +} + +#endif /* DEBUG_EFM */ diff --git a/cpu/efm32_common/emlib/src/em_burtc.c b/cpu/efm32_common/emlib/src/em_burtc.c new file mode 100644 index 0000000000000..15e9803aa266f --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_burtc.c @@ -0,0 +1,314 @@ +/***************************************************************************//** + * @file em_burtc.c + * @brief Backup Real Time Counter (BURTC) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#include "em_burtc.h" +#if defined(BURTC_PRESENT) + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup BURTC + * @brief Backup Real Time Counter (BURTC) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/***************************************************************************//** + * @brief Convert dividend to prescaler logarithmic value. Only works for even + * numbers equal to 2^n + * @param[in] div Unscaled dividend, + * @return Base 2 logarithm of input, as used by fixed prescalers + ******************************************************************************/ +__STATIC_INLINE uint32_t divToLog2(uint32_t div) +{ + uint32_t log2; + + /* Prescaler accepts an argument of 128 or less, valid values being 2^n */ + EFM_ASSERT((div > 0) && (div <= 32768)); + + /* Count leading zeroes and "reverse" result, Cortex-M3 intrinsic */ + log2 = (31 - __CLZ(div)); + + return log2; +} + + +/***************************************************************************//** + * @brief + * Wait for ongoing sync of register(s) to low frequency domain to complete. + * + * @param[in] mask + * Bitmask corresponding to SYNCBUSY register defined bits, indicating + * registers that must complete any ongoing synchronization. + ******************************************************************************/ +__STATIC_INLINE void regSync(uint32_t mask) +{ + /* Avoid deadlock if modifying the same register twice when freeze mode is + activated, or when no clock is selected for the BURTC. If no clock is + selected, then the sync is done once the clock source is set. */ + if ((BURTC->FREEZE & BURTC_FREEZE_REGFREEZE) + || ((BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK) != _BURTC_CTRL_CLKSEL_NONE)) + { + 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 (BURTC->SYNCBUSY & mask) + ; +} +/** @endcond */ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief Initialize BURTC + * + * @details + * Configures the BURTC peripheral. + * + * @note + * Before initialization, BURTC module must first be enabled by clearing the + * reset bit in the RMU, i.e. + * @verbatim + * RMU_ResetControl(rmuResetBU, rmuResetModeClear); + * @endverbatim + * Compare channel 0 must be configured outside this function, before + * initialization if enable is set to true. The counter will always be reset. + * + * @param[in] burtcInit + * Pointer to BURTC initialization structure + ******************************************************************************/ +void BURTC_Init(const BURTC_Init_TypeDef *burtcInit) +{ + uint32_t ctrl; + uint32_t presc; + + /* Check initializer structure integrity */ + EFM_ASSERT(burtcInit != (BURTC_Init_TypeDef *) 0); + /* Clock divider must be between 1 and 128, really on the form 2^n */ + EFM_ASSERT((burtcInit->clkDiv >= 1) && (burtcInit->clkDiv <= 128)); + /* Ignored compare bits during low power operation must be less than 7 */ + /* Note! Giant Gecko revision C errata, do NOT use LPCOMP=7 */ + EFM_ASSERT(burtcInit->lowPowerComp <= 6); + /* You cannot enable the BURTC if mode is set to disabled */ + EFM_ASSERT((burtcInit->enable == false) || + ((burtcInit->enable == true) + && (burtcInit->mode != burtcModeDisable))); + /* Low power mode is only available with LFRCO or LFXO as clock source */ + EFM_ASSERT((burtcInit->clkSel != burtcClkSelULFRCO) + || ((burtcInit->clkSel == burtcClkSelULFRCO) + && (burtcInit->lowPowerMode == burtcLPDisable))); + + /* Calculate prescaler value from clock divider input */ + /* Note! If clock select (clkSel) is ULFRCO, a clock divisor (clkDiv) of + value 1 will select a 2kHz ULFRCO clock, while any other value will + select a 1kHz ULFRCO clock source. */ + presc = divToLog2(burtcInit->clkDiv); + + /* Make sure all registers are updated simultaneously */ + if (burtcInit->enable) + { + BURTC_FreezeEnable(true); + } + + /* Modification of LPMODE register requires sync with potential ongoing + * register updates in LF domain. */ + regSync(BURTC_SYNCBUSY_LPMODE); + + /* Configure low power mode */ + BURTC->LPMODE = (uint32_t) (burtcInit->lowPowerMode); + + /* New configuration */ + ctrl = (BURTC_CTRL_RSTEN + | (burtcInit->mode) + | (burtcInit->debugRun << _BURTC_CTRL_DEBUGRUN_SHIFT) + | (burtcInit->compare0Top << _BURTC_CTRL_COMP0TOP_SHIFT) + | (burtcInit->lowPowerComp << _BURTC_CTRL_LPCOMP_SHIFT) + | (presc << _BURTC_CTRL_PRESC_SHIFT) + | (burtcInit->clkSel) + | (burtcInit->timeStamp << _BURTC_CTRL_BUMODETSEN_SHIFT)); + + /* Clear interrupts */ + BURTC_IntClear(0xFFFFFFFF); + + /* Set new configuration */ + BURTC->CTRL = ctrl; + + /* Enable BURTC and counter */ + if (burtcInit->enable) + { + /* To enable BURTC counter, we need to disable reset */ + BURTC_Enable(true); + + /* Clear freeze */ + BURTC_FreezeEnable(false); + } +} + + +/***************************************************************************//** + * @brief Set BURTC compare channel + * + * @param[in] comp Compare channel index, must be 0 for Giant / Leopard Gecko + * + * @param[in] value New compare value + ******************************************************************************/ +void BURTC_CompareSet(unsigned int comp, uint32_t value) +{ + (void) comp; /* Unused parameter when EFM_ASSERT is undefined. */ + + EFM_ASSERT(comp == 0); + + /* Modification of COMP0 register requires sync with potential ongoing + * register updates in LF domain. */ + regSync(BURTC_SYNCBUSY_COMP0); + + /* Configure compare channel 0 */ + BURTC->COMP0 = value; +} + + +/***************************************************************************//** + * @brief Get BURTC compare value + * + * @param[in] comp Compare channel index value, must be 0 for Giant/Leopard. + * + * @return Currently configured value for this compare channel + ******************************************************************************/ +uint32_t BURTC_CompareGet(unsigned int comp) +{ + (void) comp; /* Unused parameter when EFM_ASSERT is undefined. */ + + EFM_ASSERT(comp == 0); + + return BURTC->COMP0; +} + + +/***************************************************************************//** + * @brief Reset counter + ******************************************************************************/ +void BURTC_CounterReset(void) +{ + /* Set and clear reset bit */ + BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 1); + BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 0); +} + + +/***************************************************************************//** + * @brief + * Restore BURTC to reset state + * @note + * Before accessing the BURTC, BURSTEN in RMU->CTRL must be cleared. + * LOCK will not be reset to default value, as this will disable access + * to core BURTC registers. + ******************************************************************************/ +void BURTC_Reset(void) +{ + bool buResetState; + + /* Read reset state, set reset and restore state */ + buResetState = BUS_RegBitRead(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT); + BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, 1); + BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, buResetState); +} + + +/***************************************************************************//** + * @brief + * Get clock frequency of the BURTC. + * + * @return + * The current frequency in Hz. + ******************************************************************************/ +uint32_t BURTC_ClockFreqGet(void) +{ + uint32_t clkSel; + uint32_t clkDiv; + uint32_t frequency; + + clkSel = BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK; + clkDiv = (BURTC->CTRL & _BURTC_CTRL_PRESC_MASK) >> _BURTC_CTRL_PRESC_SHIFT; + + switch (clkSel) + { + /** Ultra low frequency (1 kHz) clock */ + case BURTC_CTRL_CLKSEL_ULFRCO: + if (_BURTC_CTRL_PRESC_DIV1 == clkDiv) + { + frequency = 2000; /* 2KHz when clock divisor is 1. */ + } + else + { + frequency = SystemULFRCOClockGet(); /* 1KHz when divisor is different + from 1. */ + } + break; + + /** Low frequency RC oscillator */ + case BURTC_CTRL_CLKSEL_LFRCO: + frequency = SystemLFRCOClockGet() / (1 << clkDiv); /* freq=32768/2^clkDiv */ + break; + + /** Low frequency crystal osciallator */ + case BURTC_CTRL_CLKSEL_LFXO: + frequency = SystemLFXOClockGet() / (1 << clkDiv); /* freq=32768/2^clkDiv */ + break; + + default: + /* No clock selected for BURTC. */ + frequency = 0; + } + return frequency; +} + + +/** @} (end addtogroup BURTC) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* BURTC_PRESENT */ diff --git a/cpu/efm32_common/emlib/src/em_cmu.c b/cpu/efm32_common/emlib/src/em_cmu.c new file mode 100644 index 0000000000000..93473ba485c6f --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_cmu.c @@ -0,0 +1,3786 @@ +/***************************************************************************//** + * @file em_cmu.c + * @brief Clock management unit (CMU) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ +#include "em_cmu.h" +#if defined( CMU_PRESENT ) + +#include +#include +#include "em_assert.h" +#include "em_bus.h" +#include "em_emu.h" +#include "em_system.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CMU + * @brief Clock management unit (CMU) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ****************************** DEFINES ************************************ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +/** Maximum allowed core frequency when using 0 wait-states on flash access. */ +#define CMU_MAX_FREQ_0WS 26000000 +/** Maximum allowed core frequency when using 1 wait-states on flash access */ +#define CMU_MAX_FREQ_1WS 40000000 +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) +/** Maximum allowed core frequency when using 0 wait-states on flash access. */ +#define CMU_MAX_FREQ_0WS 16000000 +/** Maximum allowed core frequency when using 1 wait-states on flash access */ +#define CMU_MAX_FREQ_1WS 32000000 +#else +#error "Unkown MCU platform." +#endif + +#if defined( CMU_CTRL_HFLE ) +/** Maximum frequency for HFLE needs to be enabled on Giant, Leopard and + Wonder. */ +#if defined( _EFM32_WONDER_FAMILY ) \ + || defined( _EZR32_LEOPARD_FAMILY ) \ + || defined( _EZR32_WONDER_FAMILY ) +#define CMU_MAX_FREQ_HFLE() 24000000 +#elif defined ( _EFM32_GIANT_FAMILY ) +#define CMU_MAX_FREQ_HFLE() (maxFreqHfle()) +#else +#error Invalid part/device. +#endif +#endif + +/******************************************************************************* + ************************** LOCAL VARIABLES ******************************** + ******************************************************************************/ + +#if defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) +static CMU_AUXHFRCOFreq_TypeDef auxHfrcoFreq = cmuAUXHFRCOFreq_19M0Hz; +#endif + +/** @endcond */ + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/***************************************************************************//** + * @brief + * Get the AUX clock frequency. Used by MSC flash programming and LESENSE, + * by default also as debug clock. + * + * @return + * AUX Frequency in Hz + ******************************************************************************/ +static uint32_t auxClkGet(void) +{ + uint32_t ret; + +#if defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) + ret = auxHfrcoFreq; + +#elif defined( _CMU_AUXHFRCOCTRL_BAND_MASK ) + /* All Geckos from TG and newer */ + switch(CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_BAND_MASK) + { + case CMU_AUXHFRCOCTRL_BAND_1MHZ: + ret = 1000000; + break; + + case CMU_AUXHFRCOCTRL_BAND_7MHZ: + ret = 7000000; + break; + + case CMU_AUXHFRCOCTRL_BAND_11MHZ: + ret = 11000000; + break; + + case CMU_AUXHFRCOCTRL_BAND_14MHZ: + ret = 14000000; + break; + + case CMU_AUXHFRCOCTRL_BAND_21MHZ: + ret = 21000000; + break; + +#if defined( _CMU_AUXHFRCOCTRL_BAND_28MHZ ) + case CMU_AUXHFRCOCTRL_BAND_28MHZ: + ret = 28000000; + break; +#endif + + default: + EFM_ASSERT(0); + ret = 0; + break; + } + +#else + /* Gecko has a fixed 14Mhz AUXHFRCO clock */ + ret = 14000000; + +#endif + + return ret; +} + + +/***************************************************************************//** + * @brief + * Get the Debug Trace clock frequency + * + * @return + * Debug Trace frequency in Hz + ******************************************************************************/ +static uint32_t dbgClkGet(void) +{ + uint32_t ret; + CMU_Select_TypeDef clk; + + /* Get selected clock source */ + clk = CMU_ClockSelectGet(cmuClock_DBG); + + switch(clk) + { + case cmuSelect_HFCLK: + ret = SystemHFClockGet(); +#if defined( _CMU_CTRL_HFCLKDIV_MASK ) + /* Family with an additional divider. */ + ret = ret / (1 + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK) + >> _CMU_CTRL_HFCLKDIV_SHIFT)); +#endif + break; + + case cmuSelect_AUXHFRCO: + ret = auxClkGet(); + break; + + default: + EFM_ASSERT(0); + ret = 0; + break; + } + return ret; +} + + +/***************************************************************************//** + * @brief + * Configure flash access wait states in order to support given core clock + * frequency. + * + * @param[in] coreFreq + * Core clock frequency to configure flash wait-states for + ******************************************************************************/ +static void flashWaitStateControl(uint32_t coreFreq) +{ + uint32_t mode; + bool mscLocked; +#if defined( MSC_READCTRL_MODE_WS0SCBTP ) + bool scbtpEn; /* Suppressed Conditional Branch Target Prefetch setting. */ +#endif + + /* Make sure the MSC is unlocked */ + mscLocked = MSC->LOCK; + MSC->LOCK = MSC_UNLOCK_CODE; + + /* Get mode and SCBTP enable */ + mode = MSC->READCTRL & _MSC_READCTRL_MODE_MASK; +#if defined( MSC_READCTRL_MODE_WS0SCBTP ) + switch(mode) + { + case MSC_READCTRL_MODE_WS0: + case MSC_READCTRL_MODE_WS1: +#if defined( MSC_READCTRL_MODE_WS2 ) + case MSC_READCTRL_MODE_WS2: +#endif + scbtpEn = false; + break; + + default: /* WSxSCBTP */ + scbtpEn = true; + break; + } +#endif + + + /* Set mode based on the core clock frequency and SCBTP enable */ +#if defined( MSC_READCTRL_MODE_WS0SCBTP ) + if (false) + { + } +#if defined( MSC_READCTRL_MODE_WS2 ) + else if (coreFreq > CMU_MAX_FREQ_1WS) + { + mode = (scbtpEn ? MSC_READCTRL_MODE_WS2SCBTP : MSC_READCTRL_MODE_WS2); + } +#endif + else if ((coreFreq <= CMU_MAX_FREQ_1WS) && (coreFreq > CMU_MAX_FREQ_0WS)) + { + mode = (scbtpEn ? MSC_READCTRL_MODE_WS1SCBTP : MSC_READCTRL_MODE_WS1); + } + else + { + mode = (scbtpEn ? MSC_READCTRL_MODE_WS0SCBTP : MSC_READCTRL_MODE_WS0); + } + +#else /* If MODE and SCBTP is in separate register fields */ + + if (false) + { + } +#if defined( MSC_READCTRL_MODE_WS2 ) + else if (coreFreq > CMU_MAX_FREQ_1WS) + { + mode = MSC_READCTRL_MODE_WS2; + } +#endif + else if ((coreFreq <= CMU_MAX_FREQ_1WS) && (coreFreq > CMU_MAX_FREQ_0WS)) + { + mode = MSC_READCTRL_MODE_WS1; + } + else + { + mode = MSC_READCTRL_MODE_WS0; + } +#endif + + /* BUS_RegMaskedWrite cannot be used here as it would temporarely set the + mode field to WS0 */ + MSC->READCTRL = (MSC->READCTRL &~_MSC_READCTRL_MODE_MASK) | mode; + + if (mscLocked) + { + MSC->LOCK = 0; + } +} + + +/***************************************************************************//** + * @brief + * Configure flash access wait states to most conservative setting for + * this target. Retain SCBTP (Suppressed Conditional Branch Target Prefetch) + * setting. + ******************************************************************************/ +static void flashWaitStateMax(void) +{ + flashWaitStateControl(SystemMaxCoreClockGet()); +} + + +/***************************************************************************//** + * @brief + * Get the LFnCLK frequency based on current configuration. + * + * @param[in] lfClkBranch + * Selected LF branch + * + * @return + * The LFnCLK frequency in Hz. If no LFnCLK is selected (disabled), 0 is + * returned. + ******************************************************************************/ +static uint32_t lfClkGet(CMU_Clock_TypeDef lfClkBranch) +{ + uint32_t sel; + uint32_t ret = 0; + + switch (lfClkBranch) + { + case cmuClock_LFA: + case cmuClock_LFB: +#if defined( _CMU_LFCCLKEN0_MASK ) + case cmuClock_LFC: +#endif +#if defined( _CMU_LFECLKSEL_MASK ) + case cmuClock_LFE: +#endif + break; + + default: + EFM_ASSERT(0); + break; + } + + sel = CMU_ClockSelectGet(lfClkBranch); + + /* Get clock select field */ + switch (lfClkBranch) + { + case cmuClock_LFA: +#if defined( _CMU_LFCLKSEL_MASK ) + sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) >> _CMU_LFCLKSEL_LFA_SHIFT; +#elif defined( _CMU_LFACLKSEL_MASK ) + sel = (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) >> _CMU_LFACLKSEL_LFA_SHIFT; +#else + EFM_ASSERT(0); +#endif + break; + + case cmuClock_LFB: +#if defined( _CMU_LFCLKSEL_MASK ) + sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) >> _CMU_LFCLKSEL_LFB_SHIFT; +#elif defined( _CMU_LFBCLKSEL_MASK ) + sel = (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) >> _CMU_LFBCLKSEL_LFB_SHIFT; +#else + EFM_ASSERT(0); +#endif + break; + +#if defined( _CMU_LFCCLKEN0_MASK ) + case cmuClock_LFC: + sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) >> _CMU_LFCLKSEL_LFC_SHIFT; + break; +#endif + +#if defined( _CMU_LFECLKSEL_MASK ) + case cmuClock_LFE: + sel = (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) >> _CMU_LFECLKSEL_LFE_SHIFT; + break; +#endif + + default: + EFM_ASSERT(0); + break; + } + + /* Get clock frequency */ +#if defined( _CMU_LFCLKSEL_MASK ) + switch (sel) + { + case _CMU_LFCLKSEL_LFA_LFRCO: + ret = SystemLFRCOClockGet(); + break; + + case _CMU_LFCLKSEL_LFA_LFXO: + ret = SystemLFXOClockGet(); + break; + +#if defined( _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2 ) + case _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2: +#if defined( CMU_CTRL_HFLE ) + /* Family which can use an extra div 4 divider */ + /* (and must if >32MHz) or HFLE is set. */ + if(((CMU->HFCORECLKDIV & _CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK) + == CMU_HFCORECLKDIV_HFCORECLKLEDIV_DIV4) + || (CMU->CTRL & CMU_CTRL_HFLE)) + { + ret = SystemCoreClockGet() / 4U; + } + else + { + ret = SystemCoreClockGet() / 2U; + } +#else + ret = SystemCoreClockGet() / 2U; +#endif + break; +#endif + + case _CMU_LFCLKSEL_LFA_DISABLED: + ret = 0; +#if defined( CMU_LFCLKSEL_LFAE ) + /* Check LF Extended bit setting for LFA or LFB ULFRCO clock */ + if ((lfClkBranch == cmuClock_LFA) || (lfClkBranch == cmuClock_LFB)) + { + if (CMU->LFCLKSEL >> (lfClkBranch == cmuClock_LFA + ? _CMU_LFCLKSEL_LFAE_SHIFT + : _CMU_LFCLKSEL_LFBE_SHIFT)) + { + ret = SystemULFRCOClockGet(); + } + } +#endif + break; + + default: + EFM_ASSERT(0); + ret = 0U; + break; + } +#endif /* _CMU_LFCLKSEL_MASK */ + +#if defined( _CMU_LFACLKSEL_MASK ) + switch (sel) + { + case _CMU_LFACLKSEL_LFA_LFRCO: + ret = SystemLFRCOClockGet(); + break; + + case _CMU_LFACLKSEL_LFA_LFXO: + ret = SystemLFXOClockGet(); + break; + + case _CMU_LFACLKSEL_LFA_ULFRCO: + ret = SystemULFRCOClockGet(); + break; + +#if defined( _CMU_LFACLKSEL_LFA_HFCLKLE ) + case _CMU_LFACLKSEL_LFA_HFCLKLE: + ret = ((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) + == CMU_HFPRESC_HFCLKLEPRESC_DIV4) + ? SystemCoreClockGet() / 4U + : SystemCoreClockGet() / 2U; + break; +#elif defined( _CMU_LFBCLKSEL_LFB_HFCLKLE ) + case _CMU_LFBCLKSEL_LFB_HFCLKLE: + ret = ((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) + == CMU_HFPRESC_HFCLKLEPRESC_DIV4) + ? SystemCoreClockGet() / 4U + : SystemCoreClockGet() / 2U; + break; +#endif + + case _CMU_LFACLKSEL_LFA_DISABLED: + ret = 0; + break; + } +#endif + + return ret; +} + + +#if defined( CMU_CTRL_HFLE ) \ + && !defined( _EFM32_WONDER_FAMILY ) \ + && !defined( _EZR32_LEOPARD_FAMILY ) \ + && !defined( _EZR32_WONDER_FAMILY ) +/***************************************************************************//** + * @brief + * Return max allowed frequency for low energy peripherals. + ******************************************************************************/ +static uint32_t maxFreqHfle(void) +{ + uint16_t majorMinorRev; + + switch (SYSTEM_GetFamily()) + { + case systemPartFamilyEfm32Leopard: + /* CHIP MAJOR bit [5:0] */ + majorMinorRev = (((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) + >> _ROMTABLE_PID0_REVMAJOR_SHIFT) << 8); + /* CHIP MINOR bit [7:4] */ + majorMinorRev |= (((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK) + >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4); + /* CHIP MINOR bit [3:0] */ + majorMinorRev |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) + >> _ROMTABLE_PID3_REVMINORLSB_SHIFT); + + if (majorMinorRev >= 0x0204) + return 24000000; + else + return 32000000; + + case systemPartFamilyEfm32Giant: + return 32000000; + + default: + /* Invalid device family. */ + EFM_ASSERT(false); + return 0; + } +} +#endif + + +/***************************************************************************//** + * @brief + * Wait for ongoing sync of register(s) to low frequency domain to complete. + * + * @param[in] mask + * Bitmask corresponding to SYNCBUSY register defined bits, indicating + * registers that must complete any ongoing synchronization. + ******************************************************************************/ +__STATIC_INLINE void syncReg(uint32_t mask) +{ + /* Avoid deadlock if modifying the same register twice when freeze mode is */ + /* activated. */ + if (CMU->FREEZE & CMU_FREEZE_REGFREEZE) + return; + + /* Wait for any pending previous write operation to have been completed */ + /* in low frequency domain */ + while (CMU->SYNCBUSY & mask) + { + } +} + + +#if defined(USB_PRESENT) +/***************************************************************************//** + * @brief + * Get the USBC frequency + * + * @return + * USBC frequency in Hz + ******************************************************************************/ +static uint32_t usbCClkGet(void) +{ + uint32_t ret; + CMU_Select_TypeDef clk; + + /* Get selected clock source */ + clk = CMU_ClockSelectGet(cmuClock_USBC); + + switch(clk) + { + case cmuSelect_LFXO: + ret = SystemLFXOClockGet(); + break; + case cmuSelect_LFRCO: + ret = SystemLFRCOClockGet(); + break; + case cmuSelect_HFCLK: + ret = SystemHFClockGet(); + break; + default: + /* Clock is not enabled */ + ret = 0; + break; + } + return ret; +} +#endif + + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +#if defined( _CMU_AUXHFRCOCTRL_BAND_MASK ) +/***************************************************************************//** + * @brief + * Get AUXHFRCO band in use. + * + * @return + * AUXHFRCO band in use. + ******************************************************************************/ +CMU_AUXHFRCOBand_TypeDef CMU_AUXHFRCOBandGet(void) +{ + return (CMU_AUXHFRCOBand_TypeDef)((CMU->AUXHFRCOCTRL + & _CMU_AUXHFRCOCTRL_BAND_MASK) + >> _CMU_AUXHFRCOCTRL_BAND_SHIFT); +} +#endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */ + + +#if defined( _CMU_AUXHFRCOCTRL_BAND_MASK ) +/***************************************************************************//** + * @brief + * Set AUXHFRCO band and the tuning value based on the value in the + * calibration table made during production. + * + * @param[in] band + * AUXHFRCO band to activate. + ******************************************************************************/ +void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOBand_TypeDef band) +{ + uint32_t tuning; + + /* Read tuning value from calibration table */ + switch (band) + { + case cmuAUXHFRCOBand_1MHz: + tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND1_MASK) + >> _DEVINFO_AUXHFRCOCAL0_BAND1_SHIFT; + break; + + case cmuAUXHFRCOBand_7MHz: + tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND7_MASK) + >> _DEVINFO_AUXHFRCOCAL0_BAND7_SHIFT; + break; + + case cmuAUXHFRCOBand_11MHz: + tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND11_MASK) + >> _DEVINFO_AUXHFRCOCAL0_BAND11_SHIFT; + break; + + case cmuAUXHFRCOBand_14MHz: + tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND14_MASK) + >> _DEVINFO_AUXHFRCOCAL0_BAND14_SHIFT; + break; + + case cmuAUXHFRCOBand_21MHz: + tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND21_MASK) + >> _DEVINFO_AUXHFRCOCAL1_BAND21_SHIFT; + break; + +#if defined( _CMU_AUXHFRCOCTRL_BAND_28MHZ ) + case cmuAUXHFRCOBand_28MHz: + tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND28_MASK) + >> _DEVINFO_AUXHFRCOCAL1_BAND28_SHIFT; + break; +#endif + + default: + EFM_ASSERT(0); + return; + } + + /* Set band/tuning */ + CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & + ~(_CMU_AUXHFRCOCTRL_BAND_MASK + | _CMU_AUXHFRCOCTRL_TUNING_MASK)) + | (band << _CMU_AUXHFRCOCTRL_BAND_SHIFT) + | (tuning << _CMU_AUXHFRCOCTRL_TUNING_SHIFT); + +} +#endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */ + + +#if defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) +/**************************************************************************//** + * @brief + * Get a pointer to the AUXHFRCO frequency calibration word in DEVINFO + * + * @param[in] freq + * Frequency in Hz + * + * @return + * AUXHFRCO calibration word for a given frequency + *****************************************************************************/ +static uint32_t CMU_AUXHFRCODevinfoGet(CMU_AUXHFRCOFreq_TypeDef freq) +{ + switch (freq) + { + /* 1, 2 and 4MHz share the same calibration word */ + case cmuAUXHFRCOFreq_1M0Hz: + case cmuAUXHFRCOFreq_2M0Hz: + case cmuAUXHFRCOFreq_4M0Hz: + return DEVINFO->AUXHFRCOCAL0; + + case cmuAUXHFRCOFreq_7M0Hz: + return DEVINFO->AUXHFRCOCAL3; + + case cmuAUXHFRCOFreq_13M0Hz: + return DEVINFO->AUXHFRCOCAL6; + + case cmuAUXHFRCOFreq_16M0Hz: + return DEVINFO->AUXHFRCOCAL7; + + case cmuAUXHFRCOFreq_19M0Hz: + return DEVINFO->AUXHFRCOCAL8; + + case cmuAUXHFRCOFreq_26M0Hz: + return DEVINFO->AUXHFRCOCAL10; + + case cmuAUXHFRCOFreq_32M0Hz: + return DEVINFO->AUXHFRCOCAL11; + + case cmuAUXHFRCOFreq_38M0Hz: + return DEVINFO->AUXHFRCOCAL12; + + default: /* cmuAUXHFRCOFreq_UserDefined */ + return 0; + } +} +#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ + + +#if defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) +/***************************************************************************//** + * @brief + * Get AUXHFRCO frequency enumeration in use + * + * @return + * AUXHFRCO frequency enumeration in use + ******************************************************************************/ +CMU_AUXHFRCOFreq_TypeDef CMU_AUXHFRCOFreqGet(void) +{ + return auxHfrcoFreq; +} +#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ + + +#if defined( _CMU_AUXHFRCOCTRL_FREQRANGE_MASK ) +/***************************************************************************//** + * @brief + * Set AUXHFRCO calibration for the selected target frequency + * + * @param[in] frequency + * AUXHFRCO frequency to set + ******************************************************************************/ +void CMU_AUXHFRCOFreqSet(CMU_AUXHFRCOFreq_TypeDef freq) +{ + uint32_t freqCal; + + /* Get DEVINFO index, set global auxHfrcoFreq */ + freqCal = CMU_AUXHFRCODevinfoGet(freq); + EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX)); + auxHfrcoFreq = freq; + + /* Wait for any previous sync to complete, and then set calibration data + for the selected frequency. */ + while(BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)); + + /* Set divider in AUXHFRCOCTRL for 1, 2 and 4MHz */ + switch(freq) + { + case cmuAUXHFRCOFreq_1M0Hz: + freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) + | CMU_AUXHFRCOCTRL_CLKDIV_DIV4; + break; + + case cmuAUXHFRCOFreq_2M0Hz: + freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) + | CMU_AUXHFRCOCTRL_CLKDIV_DIV2; + break; + + case cmuAUXHFRCOFreq_4M0Hz: + freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) + | CMU_AUXHFRCOCTRL_CLKDIV_DIV1; + break; + + default: + break; + } + CMU->AUXHFRCOCTRL = freqCal; +} +#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ + + +/***************************************************************************//** + * @brief + * Calibrate clock. + * + * @details + * Run a calibration for HFCLK against a selectable reference clock. Please + * refer to the reference manual, CMU chapter, for further details. + * + * @note + * This function will not return until calibration measurement is completed. + * + * @param[in] HFCycles + * The number of HFCLK cycles to run calibration. Increasing this number + * increases precision, but the calibration will take more time. + * + * @param[in] ref + * The reference clock used to compare HFCLK with. + * + * @return + * The number of ticks the reference clock after HFCycles ticks on the HF + * clock. + ******************************************************************************/ +uint32_t CMU_Calibrate(uint32_t HFCycles, CMU_Osc_TypeDef ref) +{ + EFM_ASSERT(HFCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT)); + + /* Set reference clock source */ + switch (ref) + { + case cmuOsc_LFXO: + CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFXO; + break; + + case cmuOsc_LFRCO: + CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFRCO; + break; + + case cmuOsc_HFXO: + CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFXO; + break; + + case cmuOsc_HFRCO: + CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFRCO; + break; + + case cmuOsc_AUXHFRCO: + CMU->CALCTRL = CMU_CALCTRL_UPSEL_AUXHFRCO; + break; + + default: + EFM_ASSERT(0); + return 0; + } + + /* Set top value */ + CMU->CALCNT = HFCycles; + + /* Start calibration */ + CMU->CMD = CMU_CMD_CALSTART; + +#if defined( CMU_STATUS_CALRDY ) + /* Wait until calibration completes */ + while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) + { + } +#else + /* Wait until calibration completes */ + while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) + { + } +#endif + + return CMU->CALCNT; +} + + +#if defined( _CMU_CALCTRL_UPSEL_MASK ) && defined( _CMU_CALCTRL_DOWNSEL_MASK ) +/***************************************************************************//** + * @brief + * Configure clock calibration + * + * @details + * Configure a calibration for a selectable clock source against another + * selectable reference clock. + * Refer to the reference manual, CMU chapter, for further details. + * + * @note + * After configuration, a call to CMU_CalibrateStart() is required, and + * the resulting calibration value can be read out with the + * CMU_CalibrateCountGet() function call. + * + * @param[in] downCycles + * The number of downSel clock cycles to run calibration. Increasing this + * number increases precision, but the calibration will take more time. + * + * @param[in] downSel + * The clock which will be counted down downCycles + * + * @param[in] upSel + * The reference clock, the number of cycles generated by this clock will + * be counted and added up, the result can be given with the + * CMU_CalibrateCountGet() function call. + ******************************************************************************/ +void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel, + CMU_Osc_TypeDef upSel) +{ + /* Keep untouched configuration settings */ + uint32_t calCtrl = CMU->CALCTRL + & ~(_CMU_CALCTRL_UPSEL_MASK | _CMU_CALCTRL_DOWNSEL_MASK); + + /* 20 bits of precision to calibration count register */ + EFM_ASSERT(downCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT)); + + /* Set down counting clock source - down counter */ + switch (downSel) + { + case cmuOsc_LFXO: + calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO; + break; + + case cmuOsc_LFRCO: + calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO; + break; + + case cmuOsc_HFXO: + calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO; + break; + + case cmuOsc_HFRCO: + calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCO; + break; + + case cmuOsc_AUXHFRCO: + calCtrl |= CMU_CALCTRL_DOWNSEL_AUXHFRCO; + break; + + default: + EFM_ASSERT(0); + break; + } + + /* Set top value to be counted down by the downSel clock */ + CMU->CALCNT = downCycles; + + /* Set reference clock source - up counter */ + switch (upSel) + { + case cmuOsc_LFXO: + calCtrl |= CMU_CALCTRL_UPSEL_LFXO; + break; + + case cmuOsc_LFRCO: + calCtrl |= CMU_CALCTRL_UPSEL_LFRCO; + break; + + case cmuOsc_HFXO: + calCtrl |= CMU_CALCTRL_UPSEL_HFXO; + break; + + case cmuOsc_HFRCO: + calCtrl |= CMU_CALCTRL_UPSEL_HFRCO; + break; + + case cmuOsc_AUXHFRCO: + calCtrl |= CMU_CALCTRL_UPSEL_AUXHFRCO; + break; + + default: + EFM_ASSERT(0); + break; + } + + CMU->CALCTRL = calCtrl; +} +#endif + + +/***************************************************************************//** + * @brief + * Get calibration count register + * @note + * If continuous calibrartion mode is active, calibration busy will almost + * always be off, and we just need to read the value, where the normal case + * would be that this function call has been triggered by the CALRDY + * interrupt flag. + * @return + * Calibration count, the number of UPSEL clocks (see CMU_CalibrateConfig) + * in the period of DOWNSEL oscillator clock cycles configured by a previous + * write operation to CMU->CALCNT + ******************************************************************************/ +uint32_t CMU_CalibrateCountGet(void) +{ + /* Wait until calibration completes, UNLESS continuous calibration mode is */ + /* active */ +#if defined( CMU_CALCTRL_CONT ) + if (!BUS_RegBitRead(&CMU->CALCTRL, _CMU_CALCTRL_CONT_SHIFT)) + { +#if defined( CMU_STATUS_CALRDY ) + /* Wait until calibration completes */ + while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) + { + } +#else + /* Wait until calibration completes */ + while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) + { + } +#endif + } +#else + while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) + { + } +#endif + return CMU->CALCNT; +} + + +/***************************************************************************//** + * @brief + * Get clock divisor/prescaler. + * + * @param[in] clock + * Clock point to get divisor/prescaler for. Notice that not all clock points + * have a divisor/prescaler. Please refer to CMU overview in reference manual. + * + * @return + * The current clock point divisor/prescaler. 1 is returned + * if @p clock specifies a clock point without a divisor/prescaler. + ******************************************************************************/ +CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock) +{ +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + return 1 + (uint32_t)CMU_ClockPrescGet(clock); + +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + uint32_t divReg; + CMU_ClkDiv_TypeDef ret; + + /* Get divisor reg id */ + divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK; + + switch (divReg) + { +#if defined( _CMU_CTRL_HFCLKDIV_MASK ) + case CMU_HFCLKDIV_REG: + ret = 1 + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK) + >> _CMU_CTRL_HFCLKDIV_SHIFT); + break; +#endif + + case CMU_HFPERCLKDIV_REG: + ret = (CMU_ClkDiv_TypeDef)((CMU->HFPERCLKDIV + & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) + >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; + + case CMU_HFCORECLKDIV_REG: + ret = (CMU_ClkDiv_TypeDef)((CMU->HFCORECLKDIV + & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK) + >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; + + case CMU_LFAPRESC0_REG: + switch (clock) + { + case cmuClock_RTC: + ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK) + >> _CMU_LFAPRESC0_RTC_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; + +#if defined(_CMU_LFAPRESC0_LETIMER0_MASK) + case cmuClock_LETIMER0: + ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) + >> _CMU_LFAPRESC0_LETIMER0_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; +#endif + +#if defined(_CMU_LFAPRESC0_LCD_MASK) + case cmuClock_LCDpre: + ret = (CMU_ClkDiv_TypeDef)(((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) + >> _CMU_LFAPRESC0_LCD_SHIFT) + + CMU_DivToLog2(cmuClkDiv_16)); + ret = CMU_Log2ToDiv(ret); + break; +#endif + +#if defined(_CMU_LFAPRESC0_LESENSE_MASK) + case cmuClock_LESENSE: + ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK) + >> _CMU_LFAPRESC0_LESENSE_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; +#endif + + default: + EFM_ASSERT(0); + ret = cmuClkDiv_1; + break; + } + break; + + case CMU_LFBPRESC0_REG: + switch (clock) + { +#if defined(_CMU_LFBPRESC0_LEUART0_MASK) + case cmuClock_LEUART0: + ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) + >> _CMU_LFBPRESC0_LEUART0_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; +#endif + +#if defined(_CMU_LFBPRESC0_LEUART1_MASK) + case cmuClock_LEUART1: + ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) + >> _CMU_LFBPRESC0_LEUART1_SHIFT); + ret = CMU_Log2ToDiv(ret); + break; +#endif + + default: + EFM_ASSERT(0); + ret = cmuClkDiv_1; + break; + } + break; + + default: + EFM_ASSERT(0); + ret = cmuClkDiv_1; + break; + } + + return ret; +#endif +} + + +/***************************************************************************//** + * @brief + * Set clock divisor/prescaler. + * + * @note + * If setting a LF clock prescaler, synchronization into the low frequency + * domain is required. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. Please refer to CMU_FreezeEnable() for + * a suggestion on how to reduce stalling time in some use cases. + * + * @param[in] clock + * Clock point to set divisor/prescaler for. Notice that not all clock points + * have a divisor/prescaler, please refer to CMU overview in the reference + * manual. + * + * @param[in] div + * The clock divisor to use (<= cmuClkDiv_512). + ******************************************************************************/ +void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div) +{ +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + CMU_ClockPrescSet(clock, (CMU_ClkPresc_TypeDef)(div - 1)); + +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + uint32_t freq; + uint32_t divReg; + + /* Get divisor reg id */ + divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK; + + switch (divReg) + { +#if defined( _CMU_CTRL_HFCLKDIV_MASK ) + case CMU_HFCLKDIV_REG: + EFM_ASSERT((div>=cmuClkDiv_1) && (div<=cmuClkDiv_8)); + + /* Configure worst case wait states for flash access before setting divisor */ + flashWaitStateMax(); + + /* Set divider */ + CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFCLKDIV_MASK) + | ((div-1) << _CMU_CTRL_HFCLKDIV_SHIFT); + + /* Update CMSIS core clock variable */ + /* (The function will update the global variable) */ + freq = SystemCoreClockGet(); + + /* Optimize flash access wait state setting for current core clk */ + flashWaitStateControl(freq); + break; +#endif + + case CMU_HFPERCLKDIV_REG: + EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512)); + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + CMU->HFPERCLKDIV = (CMU->HFPERCLKDIV & ~_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) + | (div << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT); + break; + + case CMU_HFCORECLKDIV_REG: + EFM_ASSERT(div <= cmuClkDiv_512); + + /* Configure worst case wait states for flash access before setting divisor */ + flashWaitStateMax(); + +#if defined( CMU_CTRL_HFLE ) + /* Clear HFLE and set DIV2 factor for peripheral clock + when running at frequencies lower than or equal to CMU_MAX_FREQ_HFLE. */ + if ((CMU_ClockFreqGet(cmuClock_HF) / div) <= CMU_MAX_FREQ_HFLE()) + { + /* Clear CMU HFLE */ + BUS_RegBitWrite(&CMU->CTRL, _CMU_CTRL_HFLE_SHIFT, 0); + + /* Set DIV2 factor for peripheral clock */ + BUS_RegBitWrite(&CMU->HFCORECLKDIV, + _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT, 0); + } + else + { + /* Set CMU HFLE */ + BUS_RegBitWrite(&CMU->CTRL, _CMU_CTRL_HFLE_SHIFT, 1); + + /* Set DIV4 factor for peripheral clock */ + BUS_RegBitWrite(&CMU->HFCORECLKDIV, + _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT, 1); + } +#endif + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV + & ~_CMU_HFCORECLKDIV_HFCORECLKDIV_MASK) + | (div << _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT); + + /* Update CMSIS core clock variable */ + /* (The function will update the global variable) */ + freq = SystemCoreClockGet(); + + /* Optimize flash access wait state setting for current core clk */ + flashWaitStateControl(freq); + break; + + case CMU_LFAPRESC0_REG: + switch (clock) + { + case cmuClock_RTC: + EFM_ASSERT(div <= cmuClkDiv_32768); + + /* LF register about to be modified require sync. busy check */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK) + | (div << _CMU_LFAPRESC0_RTC_SHIFT); + break; + +#if defined(_CMU_LFAPRESC0_LETIMER0_MASK) + case cmuClock_LETIMER0: + EFM_ASSERT(div <= cmuClkDiv_32768); + + /* LF register about to be modified require sync. busy check */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK) + | (div << _CMU_LFAPRESC0_LETIMER0_SHIFT); + break; +#endif + +#if defined(LCD_PRESENT) + case cmuClock_LCDpre: + EFM_ASSERT((div >= cmuClkDiv_16) && (div <= cmuClkDiv_128)); + + /* LF register about to be modified require sync. busy check */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK) + | ((div - CMU_DivToLog2(cmuClkDiv_16)) + << _CMU_LFAPRESC0_LCD_SHIFT); + break; +#endif /* defined(LCD_PRESENT) */ + +#if defined(LESENSE_PRESENT) + case cmuClock_LESENSE: + EFM_ASSERT(div <= cmuClkDiv_8); + + /* LF register about to be modified require sync. busy check */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK) + | (div << _CMU_LFAPRESC0_LESENSE_SHIFT); + break; +#endif /* defined(LESENSE_PRESENT) */ + + default: + EFM_ASSERT(0); + break; + } + break; + + case CMU_LFBPRESC0_REG: + switch (clock) + { +#if defined(_CMU_LFBPRESC0_LEUART0_MASK) + case cmuClock_LEUART0: + EFM_ASSERT(div <= cmuClkDiv_8); + + /* LF register about to be modified require sync. busy check */ + syncReg(CMU_SYNCBUSY_LFBPRESC0); + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK) + | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART0_SHIFT); + break; +#endif + +#if defined(_CMU_LFBPRESC0_LEUART1_MASK) + case cmuClock_LEUART1: + EFM_ASSERT(div <= cmuClkDiv_8); + + /* LF register about to be modified require sync. busy check */ + syncReg(CMU_SYNCBUSY_LFBPRESC0); + + /* Convert to correct scale */ + div = CMU_DivToLog2(div); + + CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK) + | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART1_SHIFT); + break; +#endif + + default: + EFM_ASSERT(0); + break; + } + break; + + default: + EFM_ASSERT(0); + break; + } +#endif +} + + +/***************************************************************************//** + * @brief + * Enable/disable a clock. + * + * @details + * In general, module clocking is disabled after a reset. If a module + * clock is disabled, the registers of that module are not accessible and + * reading from such registers may return undefined values. Writing to + * registers of clock disabled modules have no effect. One should normally + * avoid accessing module registers of a module with a disabled clock. + * + * @note + * If enabling/disabling a LF clock, synchronization into the low frequency + * domain is required. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. Please refer to CMU_FreezeEnable() for + * a suggestion on how to reduce stalling time in some use cases. + * + * @param[in] clock + * The clock to enable/disable. Notice that not all defined clock + * points have separate enable/disable control, please refer to CMU overview + * in reference manual. + * + * @param[in] enable + * @li true - enable specified clock. + * @li false - disable specified clock. + ******************************************************************************/ +void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable) +{ + volatile uint32_t *reg; + uint32_t bit; + uint32_t sync = 0; + + /* Identify enable register */ + switch ((clock >> CMU_EN_REG_POS) & CMU_EN_REG_MASK) + { +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + case CMU_CTRL_EN_REG: + reg = &CMU->CTRL; + break; +#endif + +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + case CMU_HFCORECLKEN0_EN_REG: + reg = &CMU->HFCORECLKEN0; +#if defined( CMU_CTRL_HFLE ) + /* Set HFLE and DIV4 factor for peripheral clock when + running at frequencies higher than or equal to CMU_MAX_FREQ_HFLE. */ + if ( CMU_ClockFreqGet(cmuClock_CORE) > CMU_MAX_FREQ_HFLE()) + { + /* Enable CMU HFLE */ + BUS_RegBitWrite(&CMU->CTRL, _CMU_CTRL_HFLE_SHIFT, 1); + + /* Set DIV4 factor for peripheral clock */ + BUS_RegBitWrite(&CMU->HFCORECLKDIV, + _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT, 1); + } +#endif + break; +#endif + +#if defined( _CMU_HFBUSCLKEN0_MASK ) + case CMU_HFBUSCLKEN0_EN_REG: + reg = &CMU->HFBUSCLKEN0; + break; +#endif + +#if defined( _CMU_HFRADIOCLKEN0_MASK ) + case CMU_HFRADIOCLKEN0_EN_REG: + reg = &CMU->HFRADIOCLKEN0; + break; +#endif + +#if defined( _CMU_HFPERCLKDIV_MASK ) + case CMU_HFPERCLKDIV_EN_REG: + reg = &CMU->HFPERCLKDIV; + break; +#endif + + case CMU_HFPERCLKEN0_EN_REG: + reg = &CMU->HFPERCLKEN0; + break; + + case CMU_LFACLKEN0_EN_REG: + reg = &CMU->LFACLKEN0; + sync = CMU_SYNCBUSY_LFACLKEN0; + break; + + case CMU_LFBCLKEN0_EN_REG: + reg = &CMU->LFBCLKEN0; + sync = CMU_SYNCBUSY_LFBCLKEN0; + break; + +#if defined( _CMU_LFCCLKEN0_MASK ) + case CMU_LFCCLKEN0_EN_REG: + reg = &CMU->LFCCLKEN0; + sync = CMU_SYNCBUSY_LFCCLKEN0; + break; +#endif + +#if defined( _CMU_LFECLKEN0_MASK ) + case CMU_LFECLKEN0_EN_REG: + reg = &CMU->LFECLKEN0; + sync = CMU_SYNCBUSY_LFECLKEN0; + break; +#endif + + case CMU_PCNT_EN_REG: + reg = &CMU->PCNTCTRL; + break; + + default: /* Cannot enable/disable clock point */ + EFM_ASSERT(0); + return; + } + + /* Get bit position used to enable/disable */ + bit = (clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK; + + /* LF synchronization required? */ + if (sync) + { + syncReg(sync); + } + + /* Set/clear bit as requested */ + BUS_RegBitWrite(reg, bit, enable); +} + + +/***************************************************************************//** + * @brief + * Get clock frequency for a clock point. + * + * @param[in] clock + * Clock point to fetch frequency for. + * + * @return + * The current frequency in Hz. + ******************************************************************************/ +uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock) +{ + uint32_t ret; + + switch(clock & (CMU_CLK_BRANCH_MASK << CMU_CLK_BRANCH_POS)) + { + case (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = SystemHFClockGet(); +#if defined( _CMU_CTRL_HFCLKDIV_MASK ) + /* Family with an additional divider. */ + ret = ret / (1U + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK) + >> _CMU_CTRL_HFCLKDIV_SHIFT)); +#endif +#if defined( _CMU_HFPRESC_MASK ) + ret = ret / (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK) + >> _CMU_HFPRESC_PRESC_SHIFT)); +#endif + break; + + case (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = SystemHFClockGet(); +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) +#if defined( _CMU_CTRL_HFCLKDIV_MASK ) + /* Family with an additional divider. */ + ret = ret / (1U + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK) + >> _CMU_CTRL_HFCLKDIV_SHIFT)); +#endif + ret >>= (CMU->HFPERCLKDIV & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) + >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + ret /= 1U + ((CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK) + >> _CMU_HFPERPRESC_PRESC_SHIFT); +#endif + break; + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +#if defined( _CMU_HFRADIOPRESC_PRESC_MASK ) + case (CMU_HFRADIO_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = SystemHFClockGet(); + ret /= 1U + ((CMU->HFRADIOPRESC & _CMU_HFRADIOPRESC_PRESC_MASK) + >> _CMU_HFRADIOPRESC_PRESC_SHIFT); + break; +#endif + +#if defined( CRYPTO_PRESENT ) \ + || defined( LDMA_PRESENT ) \ + || defined( GPCRC_PRESENT ) \ + || defined( PRS_PRESENT ) \ + || defined( GPIO_PRESENT ) + case (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = SystemHFClockGet(); + break; +#endif + + case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = SystemHFClockGet(); + ret /= 1U + ((CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK) + >> _CMU_HFCOREPRESC_PRESC_SHIFT); + break; + + case (CMU_HFEXP_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = SystemHFClockGet(); + ret /= 1U + ((CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK) + >> _CMU_HFEXPPRESC_PRESC_SHIFT); + break; +#endif + +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) +#if defined(AES_PRESENT) \ + || defined(DMA_PRESENT) \ + || defined(EBI_PRESENT) \ + || defined(USB_PRESENT) + case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS): + { + ret = SystemCoreClockGet(); + } break; +#endif +#endif + + case (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFA); + break; + +#if defined( _CMU_LFACLKEN0_RTC_MASK ) + case (CMU_RTC_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFA); + ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK) + >> _CMU_LFAPRESC0_RTC_SHIFT; + break; +#endif + +#if defined( _CMU_LFECLKEN0_RTCC_MASK ) + case (CMU_RTCC_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFE); + break; +#endif + +#if defined( _CMU_LFACLKEN0_LETIMER0_MASK ) + case (CMU_LETIMER0_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFA); +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) + >> _CMU_LFAPRESC0_LETIMER0_SHIFT; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) + >> _CMU_LFAPRESC0_LETIMER0_SHIFT); +#endif + break; +#endif + +#if defined(_CMU_LFACLKEN0_LCD_MASK) + case (CMU_LCDPRE_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFA); + ret >>= ((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) + >> _CMU_LFAPRESC0_LCD_SHIFT) + + CMU_DivToLog2(cmuClkDiv_16); + break; + + case (CMU_LCD_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFA); + ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) + >> _CMU_LFAPRESC0_LCD_SHIFT; + ret /= 1U + ((CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) + >> _CMU_LCDCTRL_FDIV_SHIFT); + break; +#endif + +#if defined(_CMU_LFACLKEN0_LESENSE_MASK) + case (CMU_LESENSE_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFA); + ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK) + >> _CMU_LFAPRESC0_LESENSE_SHIFT; + break; +#endif + + case (CMU_LFB_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFB); + break; + +#if defined( _CMU_LFBCLKEN0_LEUART0_MASK ) + case (CMU_LEUART0_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFB); +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) + >> _CMU_LFBPRESC0_LEUART0_SHIFT; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) + >> _CMU_LFBPRESC0_LEUART0_SHIFT); +#endif + break; +#endif + +#if defined( _CMU_LFBCLKEN0_LEUART1_MASK ) + case (CMU_LEUART1_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFB); +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) + >> _CMU_LFBPRESC0_LEUART1_SHIFT; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) + >> _CMU_LFBPRESC0_LEUART1_SHIFT); +#endif + break; +#endif + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + case (CMU_LFE_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = lfClkGet(cmuClock_LFE); + break; +#endif + + case (CMU_DBG_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = dbgClkGet(); + break; + + case (CMU_AUX_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = auxClkGet(); + break; + +#if defined(USB_PRESENT) + case (CMU_USBC_CLK_BRANCH << CMU_CLK_BRANCH_POS): + ret = usbCClkGet(); + break; +#endif + + default: + EFM_ASSERT(0); + ret = 0; + break; + } + + return ret; +} + + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +/***************************************************************************//** + * @brief + * Get clock prescaler. + * + * @param[in] clock + * Clock point to get the prescaler for. Notice that not all clock points + * have a prescaler. Please refer to CMU overview in reference manual. + * + * @return + * The prescaler value of the current clock point. 0 is returned + * if @p clock specifies a clock point without a prescaler. + ******************************************************************************/ +uint32_t CMU_ClockPrescGet(CMU_Clock_TypeDef clock) +{ + uint32_t prescReg; + uint32_t ret; + + /* Get prescaler register id. */ + prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK; + + switch (prescReg) + { + case CMU_HFPRESC_REG: + ret = ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK) + >> _CMU_HFPRESC_PRESC_SHIFT); + break; + + case CMU_HFEXPPRESC_REG: + ret = ((CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK) + >> _CMU_HFEXPPRESC_PRESC_SHIFT); + break; + + case CMU_HFCLKLEPRESC_REG: + ret = ((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) + >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT); + break; + + case CMU_HFPERPRESC_REG: + ret = ((CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK) + >> _CMU_HFPERPRESC_PRESC_SHIFT); + break; + +#if defined( _CMU_HFRADIOPRESC_PRESC_MASK ) + case CMU_HFRADIOPRESC_REG: + ret = ((CMU->HFRADIOPRESC & _CMU_HFRADIOPRESC_PRESC_MASK) + >> _CMU_HFRADIOPRESC_PRESC_SHIFT); + break; +#endif + + case CMU_HFCOREPRESC_REG: + ret = ((CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK) + >> _CMU_HFCOREPRESC_PRESC_SHIFT); + break; + + case CMU_LFAPRESC0_REG: + switch (clock) + { +#if defined( _CMU_LFAPRESC0_LETIMER0_MASK ) + case cmuClock_LETIMER0: + ret = (((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) + >> _CMU_LFAPRESC0_LETIMER0_SHIFT)); + /* Convert the exponent to prescaler value. */ + ret = CMU_Log2ToDiv(ret) - 1U; + break; +#endif + + default: + EFM_ASSERT(0); + ret = 0U; + break; + } + break; + + case CMU_LFBPRESC0_REG: + switch (clock) + { +#if defined( _CMU_LFBPRESC0_LEUART0_MASK ) + case cmuClock_LEUART0: + ret = (((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) + >> _CMU_LFBPRESC0_LEUART0_SHIFT)); + /* Convert the exponent to prescaler value. */ + ret = CMU_Log2ToDiv(ret) - 1U; + break; +#endif + +#if defined( _CMU_LFBPRESC0_LEUART1_MASK ) + case cmuClock_LEUART1: + ret = (((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) + >> _CMU_LFBPRESC0_LEUART1_SHIFT)); + /* Convert the exponent to prescaler value. */ + ret = CMU_Log2ToDiv(ret) - 1U; + break; +#endif + + default: + EFM_ASSERT(0); + ret = 0U; + break; + } + break; + + case CMU_LFEPRESC0_REG: + switch (clock) + { +#if defined( RTCC_PRESENT ) + case cmuClock_RTCC: + /* No need to compute with LFEPRESC0_RTCC - DIV1 is the only */ + /* allowed value. Convert the exponent to prescaler value. */ + ret = _CMU_LFEPRESC0_RTCC_DIV1; + break; + + default: + EFM_ASSERT(0); + ret = 0U; + break; +#endif + } + break; + + default: + EFM_ASSERT(0); + ret = 0U; + break; + } + + return ret; +} +#endif + + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) +/***************************************************************************//** + * @brief + * Set clock prescaler. + * + * @note + * If setting a LF clock prescaler, synchronization into the low frequency + * domain is required. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. Please refer to CMU_FreezeEnable() for + * a suggestion on how to reduce stalling time in some use cases. + * + * @param[in] clock + * Clock point to set prescaler for. Notice that not all clock points + * have a prescaler, please refer to CMU overview in the reference manual. + * + * @param[in] presc + * The clock prescaler to use. + ******************************************************************************/ +void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, CMU_ClkPresc_TypeDef presc) +{ + uint32_t freq; + uint32_t prescReg; + + /* Get divisor reg id */ + prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK; + + switch (prescReg) + { + case CMU_HFPRESC_REG: + EFM_ASSERT(presc < 32U); + + CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_PRESC_MASK) + | (presc << _CMU_HFPRESC_PRESC_SHIFT); + break; + + case CMU_HFEXPPRESC_REG: + EFM_ASSERT(presc < 32U); + + CMU->HFEXPPRESC = (CMU->HFEXPPRESC & ~_CMU_HFEXPPRESC_PRESC_MASK) + | (presc << _CMU_HFEXPPRESC_PRESC_SHIFT); + break; + + case CMU_HFCLKLEPRESC_REG: + EFM_ASSERT(presc < 2U); + + /* Specifies the clock divider for HFCLKLE. When running at frequencies + * higher than 32 MHz, this must be set to DIV4. */ + CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_HFCLKLEPRESC_MASK) + | (presc << _CMU_HFPRESC_HFCLKLEPRESC_SHIFT); + break; + + case CMU_HFPERPRESC_REG: + EFM_ASSERT(presc < 512U); + + CMU->HFPERPRESC = (CMU->HFPERPRESC & ~_CMU_HFPERPRESC_PRESC_MASK) + | (presc << _CMU_HFPERPRESC_PRESC_SHIFT); + break; + +#if defined( _CMU_HFRADIOPRESC_PRESC_MASK ) + case CMU_HFRADIOPRESC_REG: + EFM_ASSERT(presc < 512U); + + CMU->HFRADIOPRESC = (CMU->HFRADIOPRESC & ~_CMU_HFRADIOPRESC_PRESC_MASK) + | (presc << _CMU_HFRADIOPRESC_PRESC_SHIFT); + break; +#endif + + case CMU_HFCOREPRESC_REG: + EFM_ASSERT(presc < 512U); + + /* Configure worst case wait states for flash access before setting + * the prescaler. */ + flashWaitStateControl(CMU_MAX_FREQ_0WS + 1); + + CMU->HFCOREPRESC = (CMU->HFCOREPRESC & ~_CMU_HFCOREPRESC_PRESC_MASK) + | (presc << _CMU_HFCOREPRESC_PRESC_SHIFT); + + /* Update CMSIS core clock variable */ + /* (The function will update the global variable) */ + freq = SystemCoreClockGet(); + + /* Optimize flash access wait state setting for current core clk */ + flashWaitStateControl(freq); + break; + + case CMU_LFAPRESC0_REG: + switch (clock) + { +#if defined( RTC_PRESENT ) + case cmuClock_RTC: + EFM_ASSERT(presc <= 32768U); + + /* Convert prescaler value to DIV exponent scale. */ + presc = CMU_PrescToLog2(presc); + + /* LF register about to be modified require sync. Busy check. */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK) + | (presc << _CMU_LFAPRESC0_RTC_SHIFT); + break; +#endif + +#if defined( RTCC_PRESENT ) + case cmuClock_RTCC: +#if defined( _CMU_LFEPRESC0_RTCC_MASK ) + /* DIV1 is the only accepted value. */ + EFM_ASSERT(presc <= 0U); + + /* LF register about to be modified require sync. Busy check.. */ + syncReg(CMU_SYNCBUSY_LFEPRESC0); + + CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK) + | (presc << _CMU_LFEPRESC0_RTCC_SHIFT); +#else + EFM_ASSERT(presc <= 32768U); + + /* Convert prescaler value to DIV exponent scale. */ + presc = CMU_PrescToLog2(presc); + + /* LF register about to be modified require sync. Busy check. */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTCC_MASK) + | (presc << _CMU_LFAPRESC0_RTCC_SHIFT); +#endif + break; +#endif + +#if defined( _CMU_LFAPRESC0_LETIMER0_MASK ) + case cmuClock_LETIMER0: + EFM_ASSERT(presc <= 32768U); + + /* Convert prescaler value to DIV exponent scale. */ + presc = CMU_PrescToLog2(presc); + + /* LF register about to be modified require sync. Busy check. */ + syncReg(CMU_SYNCBUSY_LFAPRESC0); + + CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK) + | (presc << _CMU_LFAPRESC0_LETIMER0_SHIFT); + break; +#endif + + default: + EFM_ASSERT(0); + break; + } + break; + + case CMU_LFBPRESC0_REG: + switch (clock) + { +#if defined( _CMU_LFBPRESC0_LEUART0_MASK ) + case cmuClock_LEUART0: + EFM_ASSERT(presc <= 8U); + + /* Convert prescaler value to DIV exponent scale. */ + presc = CMU_PrescToLog2(presc); + + /* LF register about to be modified require sync. Busy check. */ + syncReg(CMU_SYNCBUSY_LFBPRESC0); + + CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK) + | (presc << _CMU_LFBPRESC0_LEUART0_SHIFT); + break; +#endif + +#if defined( _CMU_LFBPRESC0_LEUART1_MASK ) + case cmuClock_LEUART1: + EFM_ASSERT(presc <= 8U); + + /* Convert prescaler value to DIV exponent scale. */ + presc = CMU_PrescToLog2(presc); + + /* LF register about to be modified require sync. Busy check. */ + syncReg(CMU_SYNCBUSY_LFBPRESC0); + + CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK) + | (presc << _CMU_LFBPRESC0_LEUART1_SHIFT); + break; +#endif + + default: + EFM_ASSERT(0); + break; + } + break; + + case CMU_LFEPRESC0_REG: + switch (clock) + { +#if defined( _CMU_LFEPRESC0_RTCC_MASK ) + case cmuClock_RTCC: + EFM_ASSERT(presc <= 0U); + + /* LF register about to be modified require sync. Busy check. */ + syncReg(CMU_SYNCBUSY_LFEPRESC0); + + CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK) + | (presc << _CMU_LFEPRESC0_RTCC_SHIFT); + break; +#endif + + default: + EFM_ASSERT(0); + break; + } + break; + + default: + EFM_ASSERT(0); + break; + } +} +#endif + + +/***************************************************************************//** + * @brief + * Get currently selected reference clock used for a clock branch. + * + * @param[in] clock + * Clock branch to fetch selected ref. clock for. One of: + * @li #cmuClock_HF + * @li #cmuClock_LFA + * @li #cmuClock_LFB @if _CMU_LFCLKSEL_LFAE_ULFRCO + * @li #cmuClock_LFC + * @endif @if _SILICON_LABS_32B_PLATFORM_2 + * @li #cmuClock_LFE + * @endif + * @li #cmuClock_DBG @if DOXYDOC_USB_PRESENT + * @li #cmuClock_USBC + * @endif + * + * @return + * Reference clock used for clocking selected branch, #cmuSelect_Error if + * invalid @p clock provided. + ******************************************************************************/ +CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock) +{ + CMU_Select_TypeDef ret = cmuSelect_Disabled; + uint32_t selReg; + + selReg = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK; + + switch (selReg) + { + case CMU_HFCLKSEL_REG: +#if defined( _CMU_HFCLKSEL_HF_MASK ) + switch (CMU->HFCLKSEL & _CMU_HFCLKSEL_HF_MASK) + { + case CMU_HFCLKSEL_HF_LFXO: + ret = cmuSelect_LFXO; + break; + + case CMU_HFCLKSEL_HF_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_HFCLKSEL_HF_HFXO: + ret = cmuSelect_HFXO; + break; + + default: + ret = cmuSelect_HFRCO; + break; + } +#else + switch (CMU->STATUS + & (CMU_STATUS_HFRCOSEL + | CMU_STATUS_HFXOSEL + | CMU_STATUS_LFRCOSEL +#if defined( CMU_STATUS_USHFRCODIV2SEL ) + | CMU_STATUS_USHFRCODIV2SEL +#endif + | CMU_STATUS_LFXOSEL)) + { + case CMU_STATUS_LFXOSEL: + ret = cmuSelect_LFXO; + break; + + case CMU_STATUS_LFRCOSEL: + ret = cmuSelect_LFRCO; + break; + + case CMU_STATUS_HFXOSEL: + ret = cmuSelect_HFXO; + break; + +#if defined( CMU_STATUS_USHFRCODIV2SEL ) + case CMU_STATUS_USHFRCODIV2SEL: + ret = cmuSelect_USHFRCODIV2; + break; +#endif + + default: + ret = cmuSelect_HFRCO; + break; + } +#endif + break; + + case CMU_LFACLKSEL_REG: +#if defined( _CMU_LFCLKSEL_MASK ) + switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) + { + case CMU_LFCLKSEL_LFA_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_LFCLKSEL_LFA_LFXO: + ret = cmuSelect_LFXO; + break; + +#if defined( CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2 ) + case CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2: + ret = cmuSelect_CORELEDIV2; + break; +#endif + + default: +#if defined( CMU_LFCLKSEL_LFAE ) + if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFAE_MASK) + { + ret = cmuSelect_ULFRCO; + break; + } +#else + ret = cmuSelect_Disabled; +#endif + break; + } +#endif /* _CMU_LFCLKSEL_MASK */ + +#if defined( _CMU_LFACLKSEL_MASK ) + switch (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) + { + case CMU_LFACLKSEL_LFA_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_LFACLKSEL_LFA_LFXO: + ret = cmuSelect_LFXO; + break; + + case CMU_LFACLKSEL_LFA_ULFRCO: + ret = cmuSelect_ULFRCO; + break; + +#if defined( _CMU_LFACLKSEL_LFA_HFCLKLE ) + case CMU_LFACLKSEL_LFA_HFCLKLE: + ret = cmuSelect_HFCLKLE; + break; +#endif + + default: + ret = cmuSelect_Disabled; + break; + } +#endif + break; + + case CMU_LFBCLKSEL_REG: +#if defined( _CMU_LFCLKSEL_MASK ) + switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) + { + case CMU_LFCLKSEL_LFB_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_LFCLKSEL_LFB_LFXO: + ret = cmuSelect_LFXO; + break; + +#if defined( CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2 ) + case CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2: + ret = cmuSelect_CORELEDIV2; + break; +#endif + +#if defined( CMU_LFCLKSEL_LFB_HFCLKLE ) + case CMU_LFCLKSEL_LFB_HFCLKLE: + ret = cmuSelect_HFCLKLE; + break; +#endif + + default: +#if defined( CMU_LFCLKSEL_LFBE ) + if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFBE_MASK) + { + ret = cmuSelect_ULFRCO; + break; + } +#else + ret = cmuSelect_Disabled; +#endif + break; + } +#endif /* _CMU_LFCLKSEL_MASK */ + +#if defined( _CMU_LFBCLKSEL_MASK ) + switch (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) + { + case CMU_LFBCLKSEL_LFB_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_LFBCLKSEL_LFB_LFXO: + ret = cmuSelect_LFXO; + break; + + case CMU_LFBCLKSEL_LFB_ULFRCO: + ret = cmuSelect_ULFRCO; + break; + + case CMU_LFBCLKSEL_LFB_HFCLKLE: + ret = cmuSelect_HFCLKLE; + break; + + default: + ret = cmuSelect_Disabled; + break; + } +#endif + break; + +#if defined( _CMU_LFCLKSEL_LFC_MASK ) + case CMU_LFCCLKSEL_REG: + switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) + { + case CMU_LFCLKSEL_LFC_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_LFCLKSEL_LFC_LFXO: + ret = cmuSelect_LFXO; + break; + + default: + ret = cmuSelect_Disabled; + break; + } + break; +#endif + +#if defined( _CMU_LFECLKSEL_LFE_MASK ) + case CMU_LFECLKSEL_REG: + switch (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) + { + case CMU_LFECLKSEL_LFE_LFRCO: + ret = cmuSelect_LFRCO; + break; + + case CMU_LFECLKSEL_LFE_LFXO: + ret = cmuSelect_LFXO; + break; + + case CMU_LFECLKSEL_LFE_ULFRCO: + ret = cmuSelect_ULFRCO; + break; + +#if defined ( _CMU_LFECLKSEL_LFE_HFCLKLE ) + case CMU_LFECLKSEL_LFE_HFCLKLE: + ret = cmuSelect_HFCLKLE; + break; +#endif + + default: + ret = cmuSelect_Disabled; + break; + } + break; +#endif /* CMU_LFECLKSEL_REG */ + + case CMU_DBGCLKSEL_REG: +#if defined( _CMU_DBGCLKSEL_DBG_MASK ) + switch (CMU->DBGCLKSEL & _CMU_DBGCLKSEL_DBG_MASK) + { + case CMU_DBGCLKSEL_DBG_HFCLK: + ret = cmuSelect_HFCLK; + break; + + case CMU_DBGCLKSEL_DBG_AUXHFRCO: + ret = cmuSelect_AUXHFRCO; + break; + } +#else + ret = cmuSelect_AUXHFRCO; +#endif /* CMU_DBGCLKSEL_DBG */ + +#if defined( _CMU_CTRL_DBGCLK_MASK ) + switch(CMU->CTRL & _CMU_CTRL_DBGCLK_MASK) + { + case CMU_CTRL_DBGCLK_AUXHFRCO: + ret = cmuSelect_AUXHFRCO; + break; + + case CMU_CTRL_DBGCLK_HFCLK: + ret = cmuSelect_HFCLK; + break; + } +#else + ret = cmuSelect_AUXHFRCO; +#endif + break; + + +#if defined( USB_PRESENT ) + case CMU_USBCCLKSEL_REG: + switch (CMU->STATUS + & (CMU_STATUS_USBCLFXOSEL +#if defined(_CMU_STATUS_USBCHFCLKSEL_MASK) + | CMU_STATUS_USBCHFCLKSEL +#endif +#if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK) + | CMU_STATUS_USBCUSHFRCOSEL +#endif + | CMU_STATUS_USBCLFRCOSEL)) + { +#if defined(_CMU_STATUS_USBCHFCLKSEL_MASK) + case CMU_STATUS_USBCHFCLKSEL: + ret = cmuSelect_HFCLK; + break; +#endif + +#if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK) + case CMU_STATUS_USBCUSHFRCOSEL: + ret = cmuSelect_USHFRCO; + break; +#endif + + case CMU_STATUS_USBCLFXOSEL: + ret = cmuSelect_LFXO; + break; + + case CMU_STATUS_USBCLFRCOSEL: + ret = cmuSelect_LFRCO; + break; + + default: + ret = cmuSelect_Disabled; + break; + } + break; +#endif + + default: + EFM_ASSERT(0); + ret = cmuSelect_Error; + break; + } + + return ret; +} + + +/***************************************************************************//** + * @brief + * Select reference clock/oscillator used for a clock branch. + * + * @details + * Notice that if a selected reference is not enabled prior to selecting its + * use, it will be enabled, and this function will wait for the selected + * oscillator to be stable. It will however NOT be disabled if another + * reference clock is selected later. + * + * This feature is particularly important if selecting a new reference + * clock for the clock branch clocking the core, otherwise the system + * may halt. + * + * @param[in] clock + * Clock branch to select reference clock for. One of: + * @li #cmuClock_HF + * @li #cmuClock_LFA + * @li #cmuClock_LFB @if _CMU_LFCLKSEL_LFAE_ULFRCO + * @li #cmuClock_LFC + * @endif @if _SILICON_LABS_32B_PLATFORM_2 + * @li #cmuClock_LFE + * @endif + * @li #cmuClock_DBG @if DOXYDOC_USB_PRESENT + * @li #cmuClock_USBC + * @endif + * + * @param[in] ref + * Reference selected for clocking, please refer to reference manual for + * for details on which reference is available for a specific clock branch. + * @li #cmuSelect_HFRCO + * @li #cmuSelect_LFRCO + * @li #cmuSelect_HFXO + * @li #cmuSelect_LFXO + * @li #cmuSelect_CORELEDIV2 + * @li #cmuSelect_AUXHFRCO + * @li #cmuSelect_HFCLK @ifnot DOXYDOC_EFM32_GECKO_FAMILY + * @li #cmuSelect_ULFRCO + * @endif + ******************************************************************************/ +void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref) +{ + uint32_t select = cmuOsc_HFRCO; + CMU_Osc_TypeDef osc = cmuOsc_HFRCO; + uint32_t freq; + uint32_t tmp; + uint32_t selRegId; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + volatile uint32_t *selReg = NULL; +#endif +#if defined( CMU_LFCLKSEL_LFAE_ULFRCO ) + uint32_t lfExtended = 0; +#endif + + selRegId = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK; + + switch (selRegId) + { + case CMU_HFCLKSEL_REG: + switch (ref) + { + case cmuSelect_LFXO: +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + select = CMU_HFCLKSEL_HF_LFXO; +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + select = CMU_CMD_HFCLKSEL_LFXO; +#endif + osc = cmuOsc_LFXO; + break; + + case cmuSelect_LFRCO: +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + select = CMU_HFCLKSEL_HF_LFRCO; +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + select = CMU_CMD_HFCLKSEL_LFRCO; +#endif + osc = cmuOsc_LFRCO; + break; + + case cmuSelect_HFXO: + osc = cmuOsc_HFXO; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + select = CMU_HFCLKSEL_HF_HFXO; + /* Adjust HFXO buffer current for high frequencies, */ + /* enable WSHFLE for frequencies above 32MHz. */ + if (SystemHFXOClockGet() > 32000000) + { + CMU->CTRL |= CMU_CTRL_WSHFLE; + } +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + select = CMU_CMD_HFCLKSEL_HFXO; +#if defined( CMU_CTRL_HFLE ) + /* Adjust HFXO buffer current for high frequencies, */ + /* enable HFLE for frequencies above CMU_MAX_FREQ_HFLE. */ + if(SystemHFXOClockGet() > CMU_MAX_FREQ_HFLE()) + { + CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK) + | CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ + /* Must have HFLE enabled to access some LE peripherals >=32MHz */ + | CMU_CTRL_HFLE; + + /* Set HFLE and DIV4 factor for peripheral clock if HFCORE */ + /* clock for LE is enabled. */ + if (CMU->HFCORECLKEN0 & CMU_HFCORECLKEN0_LE) + { + BUS_RegBitWrite(&CMU->HFCORECLKDIV, + _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT, 1); + } + } + else + { + /* This can happen if the user configures the EFM32_HFXO_FREQ to */ + /* use another oscillator frequency */ + CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK) + | CMU_CTRL_HFXOBUFCUR_BOOSTUPTO32MHZ; + } +#endif +#endif + break; + + case cmuSelect_HFRCO: +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + select = CMU_HFCLKSEL_HF_HFRCO; +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + select = CMU_CMD_HFCLKSEL_HFRCO; +#endif + osc = cmuOsc_HFRCO; + break; + +#if defined( CMU_CMD_HFCLKSEL_USHFRCODIV2 ) + case cmuSelect_USHFRCODIV2: + select = CMU_CMD_HFCLKSEL_USHFRCODIV2; + osc = cmuOsc_USHFRCO; + break; +#endif + +#if defined( CMU_LFCLKSEL_LFAE_ULFRCO ) || defined( CMU_LFACLKSEL_LFA_ULFRCO ) + case cmuSelect_ULFRCO: + /* ULFRCO cannot be used as HFCLK */ + EFM_ASSERT(0); + return; +#endif + + default: + EFM_ASSERT(0); + return; + } + + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(osc, true, true); + + /* Configure worst case wait states for flash access before selecting */ + flashWaitStateMax(); + + /* Switch to selected oscillator */ +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + CMU->HFCLKSEL = select; +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + CMU->CMD = select; +#endif + + /* Keep EMU module informed */ + EMU_UpdateOscConfig(); + + /* Update CMSIS core clock variable */ + /* (The function will update the global variable) */ + freq = SystemCoreClockGet(); + + /* Optimize flash access wait state setting for currently selected core clk */ + flashWaitStateControl(freq); + break; + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + case CMU_LFACLKSEL_REG: + selReg = (selReg == NULL) ? &CMU->LFACLKSEL : selReg; +#if !defined( _CMU_LFACLKSEL_LFA_HFCLKLE ) + /* HFCLKCLE can not be used as LFACLK */ + EFM_ASSERT(ref != cmuSelect_HFCLKLE); +#endif + case CMU_LFECLKSEL_REG: + selReg = (selReg == NULL) ? &CMU->LFECLKSEL : selReg; +#if !defined( _CMU_LFECLKSEL_LFE_HFCLKLE ) + /* HFCLKCLE can not be used as LFECLK */ + EFM_ASSERT(ref != cmuSelect_HFCLKLE); +#endif + case CMU_LFBCLKSEL_REG: + selReg = (selReg == NULL) ? &CMU->LFBCLKSEL : selReg; + switch (ref) + { + case cmuSelect_Disabled: + tmp = _CMU_LFACLKSEL_LFA_DISABLED; + break; + + case cmuSelect_LFXO: + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFXO, true, true); + tmp = _CMU_LFACLKSEL_LFA_LFXO; + break; + + case cmuSelect_LFRCO: + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); + tmp = _CMU_LFACLKSEL_LFA_LFRCO; + break; + + case cmuSelect_HFCLKLE: + /* Ensure HFCORE to LE clocking is enabled */ + BUS_RegBitWrite(&CMU->HFBUSCLKEN0, _CMU_HFBUSCLKEN0_LE_SHIFT, 1); + tmp = _CMU_LFBCLKSEL_LFB_HFCLKLE; + + /* If core frequency is > 32MHz enable WSHFLE */ + freq = SystemCoreClockGet(); + if (freq > 32000000U) + { + /* Enable CMU HFLE */ + BUS_RegBitWrite(&CMU->CTRL, _CMU_CTRL_WSHFLE_SHIFT, 1); + + /* Enable DIV4 factor for peripheral clock */ + BUS_RegBitWrite(&CMU->HFPRESC, _CMU_HFPRESC_HFCLKLEPRESC_SHIFT, 1); + } + break; + + case cmuSelect_ULFRCO: + /* ULFRCO is always on, there is no need to enable it. */ + tmp = _CMU_LFACLKSEL_LFA_ULFRCO; + break; + + default: + EFM_ASSERT(0); + return; + } + *selReg = tmp; + break; + +#elif defined( _SILICON_LABS_32B_PLATFORM_1 ) + case CMU_LFACLKSEL_REG: + case CMU_LFBCLKSEL_REG: + switch (ref) + { + case cmuSelect_Disabled: + tmp = _CMU_LFCLKSEL_LFA_DISABLED; + break; + + case cmuSelect_LFXO: + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFXO, true, true); + tmp = _CMU_LFCLKSEL_LFA_LFXO; + break; + + case cmuSelect_LFRCO: + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); + tmp = _CMU_LFCLKSEL_LFA_LFRCO; + break; + + case cmuSelect_CORELEDIV2: + /* Ensure HFCORE to LE clocking is enabled */ + BUS_RegBitWrite(&(CMU->HFCORECLKEN0), _CMU_HFCORECLKEN0_LE_SHIFT, 1); + tmp = _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2; +#if defined( CMU_CTRL_HFLE ) + /* If core frequency is higher than CMU_MAX_FREQ_HFLE on + Giant/Leopard/Wonder, enable HFLE and DIV4. */ + freq = SystemCoreClockGet(); + if(freq > CMU_MAX_FREQ_HFLE()) + { + /* Enable CMU HFLE */ + BUS_RegBitWrite(&CMU->CTRL, _CMU_CTRL_HFLE_SHIFT, 1); + + /* Enable DIV4 factor for peripheral clock */ + BUS_RegBitWrite(&CMU->HFCORECLKDIV, + _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT, 1); + } +#endif + break; + +#if defined( CMU_LFCLKSEL_LFAE_ULFRCO ) + case cmuSelect_ULFRCO: + /* ULFRCO is always enabled */ + tmp = _CMU_LFCLKSEL_LFA_DISABLED; + lfExtended = 1; + break; +#endif + + default: + /* Illegal clock source for LFA/LFB selected */ + EFM_ASSERT(0); + return; + } + + /* Apply select */ + if (selRegId == CMU_LFACLKSEL_REG) + { +#if defined( _CMU_LFCLKSEL_LFAE_MASK ) + CMU->LFCLKSEL = (CMU->LFCLKSEL + & ~(_CMU_LFCLKSEL_LFA_MASK | _CMU_LFCLKSEL_LFAE_MASK)) + | (tmp << _CMU_LFCLKSEL_LFA_SHIFT) + | (lfExtended << _CMU_LFCLKSEL_LFAE_SHIFT); +#else + CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFA_MASK) + | (tmp << _CMU_LFCLKSEL_LFA_SHIFT); +#endif + } + else + { +#if defined( _CMU_LFCLKSEL_LFBE_MASK ) + CMU->LFCLKSEL = (CMU->LFCLKSEL + & ~(_CMU_LFCLKSEL_LFB_MASK | _CMU_LFCLKSEL_LFBE_MASK)) + | (tmp << _CMU_LFCLKSEL_LFB_SHIFT) + | (lfExtended << _CMU_LFCLKSEL_LFBE_SHIFT); +#else + CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFB_MASK) + | (tmp << _CMU_LFCLKSEL_LFB_SHIFT); +#endif + } + break; + +#if defined( _CMU_LFCLKSEL_LFC_MASK ) + case CMU_LFCCLKSEL_REG: + switch(ref) + { + case cmuSelect_Disabled: + tmp = _CMU_LFCLKSEL_LFA_DISABLED; + break; + + case cmuSelect_LFXO: + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFXO, true, true); + tmp = _CMU_LFCLKSEL_LFC_LFXO; + break; + + case cmuSelect_LFRCO: + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); + tmp = _CMU_LFCLKSEL_LFC_LFRCO; + break; + + default: + /* Illegal clock source for LFC selected */ + EFM_ASSERT(0); + return; + } + + /* Apply select */ + CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFC_MASK) + | (tmp << _CMU_LFCLKSEL_LFC_SHIFT); + break; +#endif +#endif + +#if defined( CMU_DBGCLKSEL_DBG ) || defined( CMU_CTRL_DBGCLK ) + case CMU_DBGCLKSEL_REG: + switch(ref) + { +#if defined( CMU_DBGCLKSEL_DBG ) + case cmuSelect_AUXHFRCO: + /* Select AUXHFRCO as debug clock */ + CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_AUXHFRCO; + break; + + case cmuSelect_HFCLK: + /* Select divided HFCLK as debug clock */ + CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK; + break; +#endif + +#if defined( CMU_CTRL_DBGCLK ) + case cmuSelect_AUXHFRCO: + /* Select AUXHFRCO as debug clock */ + CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK)) + | CMU_CTRL_DBGCLK_AUXHFRCO; + break; + + case cmuSelect_HFCLK: + /* Select divided HFCLK as debug clock */ + CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK)) + | CMU_CTRL_DBGCLK_HFCLK; + break; +#endif + + default: + /* Illegal clock source for debug selected */ + EFM_ASSERT(0); + return; + } + break; +#endif + +#if defined(USB_PRESENT) + case CMU_USBCCLKSEL_REG: + switch(ref) + { + case cmuSelect_LFXO: + /* Select LFXO as clock source for USB, can only be used in sleep mode */ + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFXO, true, true); + + /* Switch oscillator */ + CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO; + + /* Wait until clock is activated */ + while((CMU->STATUS & CMU_STATUS_USBCLFXOSEL)==0) + { + } + break; + + case cmuSelect_LFRCO: + /* Select LFRCO as clock source for USB, can only be used in sleep mode */ + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); + + /* Switch oscillator */ + CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO; + + /* Wait until clock is activated */ + while((CMU->STATUS & CMU_STATUS_USBCLFRCOSEL)==0) + { + } + break; + +#if defined( CMU_STATUS_USBCHFCLKSEL ) + case cmuSelect_HFCLK: + /* Select undivided HFCLK as clock source for USB */ + /* Oscillator must already be enabled to avoid a core lockup */ + CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV; + /* Wait until clock is activated */ + while((CMU->STATUS & CMU_STATUS_USBCHFCLKSEL)==0) + { + } + break; +#endif + +#if defined( CMU_CMD_USBCCLKSEL_USHFRCO ) + case cmuSelect_USHFRCO: + /* Select USHFRCO as clock source for USB */ + /* Ensure selected oscillator is enabled, waiting for it to stabilize */ + CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); + + /* Switch oscillator */ + CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO; + + /* Wait until clock is activated */ + while((CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL)==0) + { + } + break; +#endif + + default: + /* Illegal clock source for USB */ + EFM_ASSERT(0); + return; + } + break; +#endif + + default: + EFM_ASSERT(0); + break; + } +} + + +/**************************************************************************//** + * @brief + * CMU low frequency register synchronization freeze control. + * + * @details + * Some CMU registers requires 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). + * + * Another usage scenario of this feature, is when using an API (such + * as the CMU API) for modifying several bit fields consecutively in the + * same register. If freeze mode is enabled during this sequence, stalling + * can be avoided. + * + * @note + * When enabling freeze mode, this function will wait for all current + * ongoing CMU 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] enable + * @li true - enable freeze, modified registers are not propagated to the + * LF domain + * @li false - disable freeze, modified registers are propagated to LF + * domain + *****************************************************************************/ +void CMU_FreezeEnable(bool enable) +{ + 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 (CMU->SYNCBUSY) + { + } + + CMU->FREEZE = CMU_FREEZE_REGFREEZE; + } + else + { + CMU->FREEZE = 0; + } +} + + +#if defined( _CMU_HFRCOCTRL_BAND_MASK ) +/***************************************************************************//** + * @brief + * Get HFRCO band in use. + * + * @return + * HFRCO band in use. + ******************************************************************************/ +CMU_HFRCOBand_TypeDef CMU_HFRCOBandGet(void) +{ + return (CMU_HFRCOBand_TypeDef)((CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK) + >> _CMU_HFRCOCTRL_BAND_SHIFT); +} +#endif /* _CMU_HFRCOCTRL_BAND_MASK */ + + +#if defined( _CMU_HFRCOCTRL_BAND_MASK ) +/***************************************************************************//** + * @brief + * Set HFRCO band and the tuning value based on the value in the calibration + * table made during production. + * + * @param[in] band + * HFRCO band to activate. + ******************************************************************************/ +void CMU_HFRCOBandSet(CMU_HFRCOBand_TypeDef band) +{ + uint32_t tuning; + uint32_t freq; + CMU_Select_TypeDef osc; + + /* Read tuning value from calibration table */ + switch (band) + { + case cmuHFRCOBand_1MHz: + tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND1_MASK) + >> _DEVINFO_HFRCOCAL0_BAND1_SHIFT; + break; + + case cmuHFRCOBand_7MHz: + tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND7_MASK) + >> _DEVINFO_HFRCOCAL0_BAND7_SHIFT; + break; + + case cmuHFRCOBand_11MHz: + tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND11_MASK) + >> _DEVINFO_HFRCOCAL0_BAND11_SHIFT; + break; + + case cmuHFRCOBand_14MHz: + tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND14_MASK) + >> _DEVINFO_HFRCOCAL0_BAND14_SHIFT; + break; + + case cmuHFRCOBand_21MHz: + tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND21_MASK) + >> _DEVINFO_HFRCOCAL1_BAND21_SHIFT; + break; + +#if defined( _CMU_HFRCOCTRL_BAND_28MHZ ) + case cmuHFRCOBand_28MHz: + tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND28_MASK) + >> _DEVINFO_HFRCOCAL1_BAND28_SHIFT; + break; +#endif + + default: + EFM_ASSERT(0); + return; + } + + /* If HFRCO is used for core clock, we have to consider flash access WS. */ + osc = CMU_ClockSelectGet(cmuClock_HF); + if (osc == cmuSelect_HFRCO) + { + /* Configure worst case wait states for flash access before setting divider */ + flashWaitStateMax(); + } + + /* Set band/tuning */ + CMU->HFRCOCTRL = (CMU->HFRCOCTRL & + ~(_CMU_HFRCOCTRL_BAND_MASK | _CMU_HFRCOCTRL_TUNING_MASK)) + | (band << _CMU_HFRCOCTRL_BAND_SHIFT) + | (tuning << _CMU_HFRCOCTRL_TUNING_SHIFT); + + /* If HFRCO is used for core clock, optimize flash WS */ + if (osc == cmuSelect_HFRCO) + { + /* Update CMSIS core clock variable and get current core clock */ + /* (The function will update the global variable) */ + /* NOTE! We need at least 21 cycles before setting zero wait state to flash */ + /* (i.e. WS0) when going from the 28MHz to 1MHz in the HFRCO band */ + freq = SystemCoreClockGet(); + + /* Optimize flash access wait state setting for current core clk */ + flashWaitStateControl(freq); + } +} +#endif /* _CMU_HFRCOCTRL_BAND_MASK */ + + +#if defined( _CMU_HFRCOCTRL_FREQRANGE_MASK ) +/**************************************************************************//** + * @brief + * Get a pointer to the HFRCO frequency calibration word in DEVINFO + * + * @param[in] freq + * Frequency in Hz + * + * @return + * HFRCO calibration word for a given frequency + *****************************************************************************/ +static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq) +{ + switch (freq) + { + /* 1, 2 and 4MHz share the same calibration word */ + case cmuHFRCOFreq_1M0Hz: + case cmuHFRCOFreq_2M0Hz: + case cmuHFRCOFreq_4M0Hz: + return DEVINFO->HFRCOCAL0; + + case cmuHFRCOFreq_7M0Hz: + return DEVINFO->HFRCOCAL3; + + case cmuHFRCOFreq_13M0Hz: + return DEVINFO->HFRCOCAL6; + + case cmuHFRCOFreq_16M0Hz: + return DEVINFO->HFRCOCAL7; + + case cmuHFRCOFreq_19M0Hz: + return DEVINFO->HFRCOCAL8; + + case cmuHFRCOFreq_26M0Hz: + return DEVINFO->HFRCOCAL10; + + case cmuHFRCOFreq_32M0Hz: + return DEVINFO->HFRCOCAL11; + + case cmuHFRCOFreq_38M0Hz: + return DEVINFO->HFRCOCAL12; + + default: /* cmuHFRCOFreq_UserDefined */ + return 0; + } +} + + +/***************************************************************************//** + * @brief + * Get HFRCO frequency enumeration in use + * + * @return + * HFRCO frequency enumeration in use + ******************************************************************************/ +CMU_HFRCOFreq_TypeDef CMU_HFRCOFreqGet(void) +{ + return (CMU_HFRCOFreq_TypeDef)SystemHfrcoFreq; +} + + +/***************************************************************************//** + * @brief + * Set HFRCO calibration for the selected target frequency + * + * @param[in] freq + * HFRCO frequency band to set + ******************************************************************************/ +void CMU_HFRCOFreqSet(CMU_HFRCOFreq_TypeDef freq) +{ + uint32_t freqCal; + + /* Get DEVINFO index, set CMSIS frequency SystemHfrcoFreq */ + freqCal = CMU_HFRCODevinfoGet(freq); + EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX)); + SystemHfrcoFreq = (uint32_t)freq; + + /* Set max wait-states while changing core clock */ + if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) + { + flashWaitStateMax(); + } + + /* Wait for any previous sync to complete, and then set calibration data + for the selected frequency. */ + while(BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)); + + /* Check for valid calibration data */ + EFM_ASSERT(freqCal != UINT_MAX); + + /* Set divider in HFRCOCTRL for 1, 2 and 4MHz */ + switch(freq) + { + case cmuHFRCOFreq_1M0Hz: + freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) + | CMU_HFRCOCTRL_CLKDIV_DIV4; + break; + + case cmuHFRCOFreq_2M0Hz: + freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) + | CMU_HFRCOCTRL_CLKDIV_DIV2; + break; + + case cmuHFRCOFreq_4M0Hz: + freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) + | CMU_HFRCOCTRL_CLKDIV_DIV1; + break; + + default: + break; + } + CMU->HFRCOCTRL = freqCal; + + /* Optimize flash access wait-state configuration for this frequency, */ + /* if HFRCO is reference for core clock. */ + if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) + { + flashWaitStateControl((uint32_t)freq); + } +} +#endif /* _CMU_HFRCOCTRL_FREQRANGE_MASK */ + +#if defined( _CMU_HFRCOCTRL_SUDELAY_MASK ) +/***************************************************************************//** + * @brief + * Get the HFRCO startup delay. + * + * @details + * Please refer to the reference manual for further details. + * + * @return + * The startup delay in use. + ******************************************************************************/ +uint32_t CMU_HFRCOStartupDelayGet(void) +{ + return (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_SUDELAY_MASK) + >> _CMU_HFRCOCTRL_SUDELAY_SHIFT; +} + + +/***************************************************************************//** + * @brief + * Set the HFRCO startup delay. + * + * @details + * Please refer to the reference manual for further details. + * + * @param[in] delay + * The startup delay to set (<= 31). + ******************************************************************************/ +void CMU_HFRCOStartupDelaySet(uint32_t delay) +{ + EFM_ASSERT(delay <= 31); + + delay &= _CMU_HFRCOCTRL_SUDELAY_MASK >> _CMU_HFRCOCTRL_SUDELAY_SHIFT; + CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_SUDELAY_MASK)) + | (delay << _CMU_HFRCOCTRL_SUDELAY_SHIFT); +} +#endif + + +#if defined( _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK ) +/***************************************************************************//** + * @brief + * Enable or disable HFXO autostart + * + * @param[in] enRACStartSel + * If true, HFXO is automatically started and selected upon RAC wakeup. + * If false, HFXO is not started or selected automatically upon RAC wakeup. + * + * @param[in] enEM0EM1Start + * If true, HFXO is automatically started upon entering EM0/EM1 entry from + * EM2/EM3. HFXO selection has to be handled by the user. + * If false, HFXO is not started automatically when entering EM0/EM1. + * + * @param[in] enEM0EM1StartSel + * If true, HFXO is automatically started and immediately selected upon + * entering EM0/EM1 entry from EM2/EM3. Note that this option stalls the use of + * HFSRCCLK until HFXO becomes ready. + * If false, HFXO is not started or selected automatically when entering + * EM0/EM1. + ******************************************************************************/ +void CMU_HFXOAutostartEnable(bool enRACStartSel, + bool enEM0EM1Start, + bool enEM0EM1StartSel) +{ + uint32_t hfxoCtrl; + hfxoCtrl = CMU->HFXOCTRL & ~(_CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK + | _CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK + | _CMU_HFXOCTRL_AUTOSTARTSELEM0EM1_MASK); + + hfxoCtrl |= (enRACStartSel ? CMU_HFXOCTRL_AUTOSTARTRDYSELRAC : 0) + | (enEM0EM1Start ? CMU_HFXOCTRL_AUTOSTARTEM0EM1 : 0) + | (enEM0EM1StartSel ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0); + + CMU->HFXOCTRL = hfxoCtrl; +} +#endif /* _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK */ + + +#if defined( _CMU_HFXOCTRL_MASK ) +/**************************************************************************//** + * @brief + * Set HFXO control registers + * + * @note + * HFXO configuration should be obtained from a configuration tool, + * app note or xtal datasheet. This function disables the HFXO to ensure + * a valid state before update. + * + * @param[in] hfxoInit + * HFXO setup parameters + *****************************************************************************/ +void CMU_HFXOInit(CMU_HFXOInit_TypeDef *hfxoInit) +{ + uint32_t ishReg; + uint32_t ishMax; + + /* Do not disable HFXO if it is currently selected as HF/Core clock */ + EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_HFXO); + + /* HFXO must be disabled before reconfiguration */ + CMU_OscillatorEnable(cmuOsc_HFXO, false, false); + + /* Apply control settings */ + BUS_RegMaskedWrite(&CMU->HFXOCTRL, + _CMU_HFXOCTRL_LOWPOWER_MASK +#if defined( _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK ) + | _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK +#endif + | _CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK + | _CMU_HFXOCTRL_AUTOSTARTSELEM0EM1_MASK, + (hfxoInit->lowPowerMode + ? CMU_HFXOCTRL_LOWPOWER : 0) +#if defined( _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK ) + | (hfxoInit->autoStartSelOnRacWakeup + ? CMU_HFXOCTRL_AUTOSTARTRDYSELRAC : 0) +#endif + | (hfxoInit->autoStartEm01 + ? CMU_HFXOCTRL_AUTOSTARTEM0EM1 : 0) + | (hfxoInit->autoSelEm01 + ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0)); + + /* Set XTAL tuning parameters */ + + /* Set peak detection threshold in CMU_HFXOCTRL1_PEAKDETTHR[2:0] (hidden). */ + BUS_RegMaskedWrite((volatile uint32_t *)0x400E4028, 0x7, hfxoInit->thresholdPeakDetect); + + /* Set tuning for startup and steady state */ + BUS_RegMaskedWrite(&CMU->HFXOSTARTUPCTRL, + _CMU_HFXOSTARTUPCTRL_CTUNE_MASK + | _CMU_HFXOSTARTUPCTRL_REGISHWARM_MASK + | _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_MASK + | _CMU_HFXOSTARTUPCTRL_IBTRIMXOCOREWARM_MASK, + (hfxoInit->ctuneStartup + << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT) + | (hfxoInit->regIshStartup + << _CMU_HFXOSTARTUPCTRL_REGISHWARM_SHIFT) + | (hfxoInit->xoCoreBiasTrimStartup + << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT) + | 0x4 /* Recommended tuning */ + << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCOREWARM_SHIFT); + + /* Adjust CMU_HFXOSTEADYSTATECTRL_REGISHUPPER according to regIshSteadyState. + Saturate at max value. Please see the reference manual page 433 and Section + 12.5.10 CMU_HFXOSTEADYSTATECTRL for more details. */ + ishReg = hfxoInit->regIshSteadyState + 3; + ishMax = _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK + >> _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT; + ishReg = ishReg > ishMax ? ishMax : ishReg; + ishReg <<= _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT; + + BUS_RegMaskedWrite(&CMU->HFXOSTEADYSTATECTRL, + _CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK + | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK + | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK + | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK, + (hfxoInit->ctuneSteadyState + << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT) + | (hfxoInit->regIshSteadyState + << _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT) + | (hfxoInit->xoCoreBiasTrimSteadyState + << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT) + | ishReg); + + /* Set timeouts */ + BUS_RegMaskedWrite(&CMU->HFXOTIMEOUTCTRL, + _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_MASK + | _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_MASK + | _CMU_HFXOTIMEOUTCTRL_WARMSTEADYTIMEOUT_MASK + | _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_MASK + | _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_MASK, + (hfxoInit->timeoutShuntOptimization + << _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_SHIFT) + | (hfxoInit->timeoutPeakDetect + << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT) + | (hfxoInit->timeoutWarmSteady + << _CMU_HFXOTIMEOUTCTRL_WARMSTEADYTIMEOUT_SHIFT) + | (hfxoInit->timeoutSteady + << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT) + | (hfxoInit->timeoutStartup + << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT)); +} +#endif + + +/***************************************************************************//** + * @brief + * Get the LCD framerate divisor (FDIV) setting. + * + * @return + * The LCD framerate divisor. + ******************************************************************************/ +uint32_t CMU_LCDClkFDIVGet(void) +{ +#if defined( LCD_PRESENT ) + return (CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) >> _CMU_LCDCTRL_FDIV_SHIFT; +#else + return 0; +#endif /* defined(LCD_PRESENT) */ +} + + +/***************************************************************************//** + * @brief + * Set the LCD framerate divisor (FDIV) setting. + * + * @note + * The FDIV field (CMU LCDCTRL register) should only be modified while the + * LCD module is clock disabled (CMU LFACLKEN0.LCD bit is 0). This function + * will NOT modify FDIV if the LCD module clock is enabled. Please refer to + * CMU_ClockEnable() for disabling/enabling LCD clock. + * + * @param[in] div + * The FDIV setting to use. + ******************************************************************************/ +void CMU_LCDClkFDIVSet(uint32_t div) +{ +#if defined( LCD_PRESENT ) + EFM_ASSERT(div <= cmuClkDiv_128); + + /* Do not allow modification if LCD clock enabled */ + if (CMU->LFACLKEN0 & CMU_LFACLKEN0_LCD) + { + return; + } + + div <<= _CMU_LCDCTRL_FDIV_SHIFT; + div &= _CMU_LCDCTRL_FDIV_MASK; + CMU->LCDCTRL = (CMU->LCDCTRL & ~_CMU_LCDCTRL_FDIV_MASK) | div; +#else + (void)div; /* Unused parameter */ +#endif /* defined(LCD_PRESENT) */ +} + + +#if defined( _CMU_LFXOCTRL_MASK ) +/**************************************************************************//** + * @brief + * Set LFXO control registers + * + * @note + * LFXO configuration should be obtained from a configuration tool, + * app note or xtal datasheet. This function disables the LFXO to ensure + * a valid state before update. + * + * @param[in] lfxoInit + * LFXO setup parameters + *****************************************************************************/ +void CMU_LFXOInit(CMU_LFXOInit_TypeDef *lfxoInit) +{ + /* Do not disable LFXO if it is currently selected as HF/Core clock */ + EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO); + + /* LFXO must be disabled before reconfiguration */ + CMU_OscillatorEnable(cmuOsc_LFXO, false, false); + + BUS_RegMaskedWrite(&CMU->LFXOCTRL, + _CMU_LFXOCTRL_TUNING_MASK + | _CMU_LFXOCTRL_GAIN_MASK + | _CMU_LFXOCTRL_TIMEOUT_MASK, + (lfxoInit->ctune << _CMU_LFXOCTRL_TUNING_SHIFT) + | (lfxoInit->gain << _CMU_LFXOCTRL_GAIN_SHIFT) + | (lfxoInit->timeout << _CMU_LFXOCTRL_TIMEOUT_SHIFT)); +} +#endif + + +/***************************************************************************//** + * @brief + * Enable/disable oscillator. + * + * @note + * WARNING: When this function is called to disable either cmuOsc_LFXO or + * cmuOsc_HFXO the LFXOMODE or HFXOMODE fields of the CMU_CTRL register + * are reset to the reset value. I.e. if external clock sources are selected + * in either LFXOMODE or HFXOMODE fields, the configuration will be cleared + * and needs to be reconfigured if needed later. + * + * @param[in] osc + * The oscillator to enable/disable. + * + * @param[in] enable + * @li true - enable specified oscillator. + * @li false - disable specified oscillator. + * + * @param[in] wait + * Only used if @p enable is true. + * @li true - wait for oscillator start-up time to timeout before returning. + * @li false - do not wait for oscillator start-up time to timeout before + * returning. + ******************************************************************************/ +void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait) +{ + uint32_t rdyBitPos; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + uint32_t ensBitPos; +#endif + uint32_t enBit; + uint32_t disBit; + + switch (osc) + { + case cmuOsc_HFRCO: + enBit = CMU_OSCENCMD_HFRCOEN; + disBit = CMU_OSCENCMD_HFRCODIS; + rdyBitPos = _CMU_STATUS_HFRCORDY_SHIFT; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + ensBitPos = _CMU_STATUS_HFRCOENS_SHIFT; +#endif + break; + + case cmuOsc_HFXO: + enBit = CMU_OSCENCMD_HFXOEN; + disBit = CMU_OSCENCMD_HFXODIS; + rdyBitPos = _CMU_STATUS_HFXORDY_SHIFT; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + ensBitPos = _CMU_STATUS_HFXOENS_SHIFT; +#endif + break; + + case cmuOsc_AUXHFRCO: + enBit = CMU_OSCENCMD_AUXHFRCOEN; + disBit = CMU_OSCENCMD_AUXHFRCODIS; + rdyBitPos = _CMU_STATUS_AUXHFRCORDY_SHIFT; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + ensBitPos = _CMU_STATUS_AUXHFRCOENS_SHIFT; +#endif + break; + + case cmuOsc_LFRCO: + enBit = CMU_OSCENCMD_LFRCOEN; + disBit = CMU_OSCENCMD_LFRCODIS; + rdyBitPos = _CMU_STATUS_LFRCORDY_SHIFT; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + ensBitPos = _CMU_STATUS_LFRCOENS_SHIFT; +#endif + break; + + case cmuOsc_LFXO: + enBit = CMU_OSCENCMD_LFXOEN; + disBit = CMU_OSCENCMD_LFXODIS; + rdyBitPos = _CMU_STATUS_LFXORDY_SHIFT; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + ensBitPos = _CMU_STATUS_LFXOENS_SHIFT; +#endif + break; + +#if defined( _CMU_STATUS_USHFRCOENS_MASK ) + case cmuOsc_USHFRCO: + enBit = CMU_OSCENCMD_USHFRCOEN; + disBit = CMU_OSCENCMD_USHFRCODIS; + rdyBitPos = _CMU_STATUS_USHFRCORDY_SHIFT; +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + ensBitPos = _CMU_STATUS_USHFRCOENS_SHIFT; +#endif + break; +#endif + +#if defined( CMU_LFCLKSEL_LFAE_ULFRCO ) + case cmuOsc_ULFRCO: + /* ULFRCO is always enabled, and cannot be turned off */ + return; +#endif + + default: + /* Undefined clock source */ + EFM_ASSERT(0); + return; + } + + if (enable) + { + CMU->OSCENCMD = enBit; + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + /* Always wait for ENS to go high */ + while (!BUS_RegBitRead(&CMU->STATUS, ensBitPos)) + { + } +#endif + + /* Wait for clock to become ready after enable */ + if (wait) + { + while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)); +#if defined( _CMU_STATUS_HFXOSHUNTOPTRDY_MASK ) + /* Wait for shunt current optimization to complete */ + if ((osc == cmuOsc_HFXO) + && (BUS_RegMaskedRead(&CMU->HFXOCTRL, + _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) + == CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_AUTOCMD)) + { + while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_HFXOSHUNTOPTRDY_SHIFT)) + { + } + /* Assert on failed peak detection. Incorrect HFXO initialization parameters + caused startup to fail. Please review parameters. */ + EFM_ASSERT(BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_HFXOPEAKDETRDY_SHIFT)); + } +#endif + } + } + else + { + CMU->OSCENCMD = disBit; + +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + /* Always wait for ENS to go low */ + while (BUS_RegBitRead(&CMU->STATUS, ensBitPos)) + { + } +#endif + } + + /* Keep EMU module informed */ + EMU_UpdateOscConfig(); +} + + +/***************************************************************************//** + * @brief + * Get oscillator frequency tuning setting. + * + * @param[in] osc + * Oscillator to get tuning value for, one of: + * @li #cmuOsc_LFRCO + * @li #cmuOsc_HFRCO + * @li #cmuOsc_AUXHFRCO + * + * @return + * The oscillator frequency tuning setting in use. + ******************************************************************************/ +uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc) +{ + uint32_t ret; + + switch (osc) + { + case cmuOsc_LFRCO: + ret = (CMU->LFRCOCTRL & _CMU_LFRCOCTRL_TUNING_MASK) + >> _CMU_LFRCOCTRL_TUNING_SHIFT; + break; + + case cmuOsc_HFRCO: + ret = (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_TUNING_MASK) + >> _CMU_HFRCOCTRL_TUNING_SHIFT; + break; + + case cmuOsc_AUXHFRCO: + ret = (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_TUNING_MASK) + >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT; + break; + + default: + EFM_ASSERT(0); + ret = 0; + break; + } + + return ret; +} + + +/***************************************************************************//** + * @brief + * Set the oscillator frequency tuning control. + * + * @note + * Oscillator tuning is done during production, and the tuning value is + * automatically loaded after a reset. Changing the tuning value from the + * calibrated value is for more advanced use. + * + * @param[in] osc + * Oscillator to set tuning value for, one of: + * @li #cmuOsc_LFRCO + * @li #cmuOsc_HFRCO + * @li #cmuOsc_AUXHFRCO + * + * @param[in] val + * The oscillator frequency tuning setting to use. + ******************************************************************************/ +void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val) +{ + switch (osc) + { + case cmuOsc_LFRCO: + EFM_ASSERT(val <= (_CMU_LFRCOCTRL_TUNING_MASK + >> _CMU_LFRCOCTRL_TUNING_SHIFT)); + val &= (_CMU_LFRCOCTRL_TUNING_MASK >> _CMU_LFRCOCTRL_TUNING_SHIFT); +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + while(BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_LFRCOBSY_SHIFT)); +#endif + CMU->LFRCOCTRL = (CMU->LFRCOCTRL & ~(_CMU_LFRCOCTRL_TUNING_MASK)) + | (val << _CMU_LFRCOCTRL_TUNING_SHIFT); + break; + + case cmuOsc_HFRCO: + EFM_ASSERT(val <= (_CMU_HFRCOCTRL_TUNING_MASK + >> _CMU_HFRCOCTRL_TUNING_SHIFT)); + val &= (_CMU_HFRCOCTRL_TUNING_MASK >> _CMU_HFRCOCTRL_TUNING_SHIFT); +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + while(BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) + { + } +#endif + CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_TUNING_MASK)) + | (val << _CMU_HFRCOCTRL_TUNING_SHIFT); + break; + + case cmuOsc_AUXHFRCO: + EFM_ASSERT(val <= (_CMU_AUXHFRCOCTRL_TUNING_MASK + >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT)); + val &= (_CMU_AUXHFRCOCTRL_TUNING_MASK >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT); +#if defined( _SILICON_LABS_32B_PLATFORM_2 ) + while(BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) + { + } +#endif + CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & ~(_CMU_AUXHFRCOCTRL_TUNING_MASK)) + | (val << _CMU_AUXHFRCOCTRL_TUNING_SHIFT); + break; + + default: + EFM_ASSERT(0); + break; + } +} + + +/**************************************************************************//** + * @brief + * Determine if currently selected PCNTn clock used is external or LFBCLK. + * + * @param[in] instance + * PCNT instance number to get currently selected clock source for. + * + * @return + * @li true - selected clock is external clock. + * @li false - selected clock is LFBCLK. + *****************************************************************************/ +bool CMU_PCNTClockExternalGet(unsigned int instance) +{ + uint32_t setting; + + switch (instance) + { +#if defined( _CMU_PCNTCTRL_PCNT0CLKEN_MASK ) + case 0: + setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT0CLKSEL_PCNT0S0; + break; + +#if defined( _CMU_PCNTCTRL_PCNT1CLKEN_MASK ) + case 1: + setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT1CLKSEL_PCNT1S0; + break; + +#if defined( _CMU_PCNTCTRL_PCNT2CLKEN_MASK ) + case 2: + setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT2CLKSEL_PCNT2S0; + break; +#endif +#endif +#endif + + default: + setting = 0; + break; + } + return (setting ? true : false); +} + + +/**************************************************************************//** + * @brief + * Select PCNTn clock. + * + * @param[in] instance + * PCNT instance number to set selected clock source for. + * + * @param[in] external + * Set to true to select external clock, false to select LFBCLK. + *****************************************************************************/ +void CMU_PCNTClockExternalSet(unsigned int instance, bool external) +{ +#if defined( PCNT_PRESENT ) + uint32_t setting = 0; + + EFM_ASSERT(instance < PCNT_COUNT); + + if (external) + { + setting = 1; + } + + BUS_RegBitWrite(&(CMU->PCNTCTRL), (instance * 2) + 1, setting); + +#else + (void)instance; /* Unused parameter */ + (void)external; /* Unused parameter */ +#endif +} + + +#if defined( _CMU_USHFRCOCONF_BAND_MASK ) +/***************************************************************************//** + * @brief + * Get USHFRCO band in use. + * + * @return + * USHFRCO band in use. + ******************************************************************************/ +CMU_USHFRCOBand_TypeDef CMU_USHFRCOBandGet(void) +{ + return (CMU_USHFRCOBand_TypeDef)((CMU->USHFRCOCONF + & _CMU_USHFRCOCONF_BAND_MASK) + >> _CMU_USHFRCOCONF_BAND_SHIFT); +} +#endif + +#if defined( _CMU_USHFRCOCONF_BAND_MASK ) +/***************************************************************************//** + * @brief + * Set USHFRCO band to use. + * + * @param[in] band + * USHFRCO band to activate. + ******************************************************************************/ +void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band) +{ + uint32_t tuning; + uint32_t fineTuning; + CMU_Select_TypeDef osc; + + /* Cannot switch band if USHFRCO is already selected as HF clock. */ + osc = CMU_ClockSelectGet(cmuClock_HF); + EFM_ASSERT((CMU_USHFRCOBandGet() != band) && (osc != cmuSelect_USHFRCO)); + + /* Read tuning value from calibration table */ + switch (band) + { + case cmuUSHFRCOBand_24MHz: + tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND24_TUNING_MASK) + >> _DEVINFO_USHFRCOCAL0_BAND24_TUNING_SHIFT; + fineTuning = (DEVINFO->USHFRCOCAL0 + & _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_MASK) + >> _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_SHIFT; + break; + + case cmuUSHFRCOBand_48MHz: + tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND48_TUNING_MASK) + >> _DEVINFO_USHFRCOCAL0_BAND48_TUNING_SHIFT; + fineTuning = (DEVINFO->USHFRCOCAL0 + & _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_MASK) + >> _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_SHIFT; + /* Enable the clock divider before switching the band from 24 to 48MHz */ + BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 0); + break; + + default: + EFM_ASSERT(0); + return; + } + + /* Set band and tuning */ + CMU->USHFRCOCONF = (CMU->USHFRCOCONF & ~_CMU_USHFRCOCONF_BAND_MASK) + | (band << _CMU_USHFRCOCONF_BAND_SHIFT); + CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~_CMU_USHFRCOCTRL_TUNING_MASK) + | (tuning << _CMU_USHFRCOCTRL_TUNING_SHIFT); + CMU->USHFRCOTUNE = (CMU->USHFRCOTUNE & ~_CMU_USHFRCOTUNE_FINETUNING_MASK) + | (fineTuning << _CMU_USHFRCOTUNE_FINETUNING_SHIFT); + + /* Disable the clock divider after switching the band from 48 to 24MHz */ + if (band == cmuUSHFRCOBand_24MHz) + { + BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 1); + } +} +#endif + + + +/** @} (end addtogroup CMU) */ +/** @} (end addtogroup EM_Library) */ +#endif /* __EM_CMU_H */ diff --git a/cpu/efm32_common/emlib/src/em_crc.c b/cpu/efm32_common/emlib/src/em_crc.c new file mode 100644 index 0000000000000..5ae7057b84103 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_crc.c @@ -0,0 +1,122 @@ +/***************************************************************************//** + * @file + * @brief Cyclic Redundancy Check (CRC) API. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_crc.h" +#include "em_assert.h" + +#if defined(CRC_COUNT) && (CRC_COUNT > 0) + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CRC + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + + +/******************************************************************************* + *************************** GLOBAL FUNCTIONS ****************************** + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initialize the Cyclic Redundancy Check (CRC) module of EFR. + * + * @details + * Use this function to configure the main operational parameters of the CRC + * such as CRC bytes, number of valid input bits, input/output bit- and bit + * order reversing. + * Refer to EFR Reference Manual Chapter 14 and the configuration structure + * CRC_Init_TypeDef for more details. + * + * @note + * Internal notes: + * - Initialize the CRC in the Init() function or let users use the separate + * command function? + * + * @param[in] init + * Pointer to initialization structure used to configure the CRC. + ******************************************************************************/ +void CRC_Init(CRC_Init_TypeDef const *init) +{ + /* Sanity check of bitsPerWord. */ + EFM_ASSERT(init->bitsPerWord < 16U); + + /* Set CRC control configuration parameters such as CRC width, byte and bit + * bit order, the number of bits per word, inverting input/output, etc. */ + CRC->CTRL = (uint32_t)init->crcWidth + | (uint32_t)init->byteReverse + | (uint32_t)init->inputBitOrder + | (uint32_t)init->bitReverse + | ((uint32_t)init->bitsPerWord >> _CRC_CTRL_BITSPERWORD_SHIFT) + | ((uint32_t)init->inputPadding >> _CRC_CTRL_PADCRCINPUT_SHIFT) + | ((uint32_t)init->invInput >> _CRC_CTRL_INPUTINV_SHIFT) + | ((uint32_t)init->invOutput >> _CRC_CTRL_OUTPUTINV_SHIFT); + + /* Set CRC polynomial value. */ + CRC->POLY = init->crcPoly; + + /* Load CRC initialization value to CRC_INIT. Please note, that the + * initialization is not performed here! */ + CRC->INIT = init->initValue; +} + + +/***************************************************************************//** + * @brief + * Reset CRC registers to the hardware reset state. + ******************************************************************************/ +void CRC_Reset(void) +{ + /* Reset CRC registers to their default value. */ + CRC->CTRL = _CRC_CTRL_RESETVALUE; + CRC->POLY = _CRC_POLY_RESETVALUE; + CRC->INIT = _CRC_INIT_RESETVALUE; +} + + +/** @} (end addtogroup CRC) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(CRC_COUNT) && (CRC_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_cryotimer.c b/cpu/efm32_common/emlib/src/em_cryotimer.c new file mode 100644 index 0000000000000..fae9db049319c --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_cryotimer.c @@ -0,0 +1,61 @@ +/***************************************************************************//** + * @file em_cryotimer.c + * @brief Ultra Low Energy Timer/Counter (CRYOTIMER) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_cryotimer.h" +#include "em_bus.h" + +#if defined(CRYOTIMER_PRESENT) && (CRYOTIMER_COUNT == 1) + +/***************************************************************************//** + * @brief + * Initialize the CRYOTIMER. + * + * @details + * Use this function to initialize the CRYOTIMER. + * Select prescaler setting and select low frequency oscillator. + * Refer to the configuration structure @ref CRYOTIMER_Init_TypeDef for more + * details. + * + * @param[in] init + * Pointer to initialization structure. + ******************************************************************************/ +void CRYOTIMER_Init(const CRYOTIMER_Init_TypeDef *init) +{ + CRYOTIMER->PERIODSEL = (uint32_t)init->period & _CRYOTIMER_PERIODSEL_MASK; + CRYOTIMER->CTRL = ((uint32_t)init->enable << _CRYOTIMER_CTRL_EN_SHIFT) + | ((uint32_t)init->debugRun << _CRYOTIMER_CTRL_DEBUGRUN_SHIFT) + | ((uint32_t)init->osc << _CRYOTIMER_CTRL_OSCSEL_SHIFT) + | ((uint32_t)init->presc << _CRYOTIMER_CTRL_PRESC_SHIFT); + CRYOTIMER_EM4WakeupEnable(init->em4Wakeup); +} + +#endif /* defined(CRYOTIMER_PRESENT) && (CRYOTIMER_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_crypto.c b/cpu/efm32_common/emlib/src/em_crypto.c new file mode 100644 index 0000000000000..a556cd7aba701 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_crypto.c @@ -0,0 +1,1847 @@ +/***************************************************************************//** + * @file em_crypto.c + * @brief Cryptography accelerator peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ +#include "em_device.h" + +#if defined(CRYPTO_COUNT) && (CRYPTO_COUNT > 0) + +#include "em_crypto.h" +#include "em_assert.h" +#include "em_bitband.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup CRYPTO + * + * @brief Cryptography accelerator peripheral API + * + * @details + * This API is intended for use on Silicon Labs target devices, and provides + * a thin software interface for the functions of the crypto module, including + * @li AES (Advanced Encryption Standard) @ref crypto_aes + * @li SHA (Secure Hash Algorithm) @ref crypto_sha + * @li Big Integer multiplier @ref crypto_mul + * @li Functions for loading data and executing instruction sequences @ref crypto_exec + * + * @n @section crypto_aes AES + * The AES APIs include support for AES-128 and AES-256 with block cipher + * modes: + * @li CBC - Cipher Block Chaining mode + * @li CFB - Cipher Feedback mode + * @li CTR - Counter mode + * @li ECB - Electronic Code Book mode + * @li OFB - Output Feedback mode + * + * For the AES APIs Input/output data (plaintext, ciphertext, key etc) are + * treated as byte arrays, starting with most significant byte. Ie, 32 bytes + * of plaintext (B0...B31) is located in memory in the same order, with B0 at + * the lower address and B31 at the higher address. + * + * Byte arrays must always be a multiple of AES block size, ie. a multiple + * of 16. Padding, if required, is done at the end of the byte array. + * + * Byte arrays should be word (32 bit) aligned for performance + * considerations, since the array is accessed with 32 bit access type. + * The core MCUs supports unaligned accesses, but with a performance penalty. + * + * It is possible to specify the same output buffer as input buffer as long + * as they point to the same address. In that case the provided input buffer + * is replaced with the encrypted/decrypted output. Notice that the buffers + * must be exactly overlapping. If partly overlapping, the behavior is + * undefined. + * + * It is up to the user to use a cipher mode according to its requirements + * in order to not break security. Please refer to specific cipher mode + * theory for details. + * + * References: + * @li Wikipedia - Cipher modes, http://en.wikipedia.org/wiki/Cipher_modes + * + * @li Recommendation for Block Cipher Modes of Operation, + * NIST Special Publication 800-38A, 2001 Edition, + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * + * @li Recommendation for Block Cipher Modes of Operation, + * http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + * + * @n @section crypto_sha SHA + * The SHA APIs include support for + * @li SHA-1 @ref CRYPTO_SHA_1 + * @li SHA-256 @ref CRYPTO_SHA_256 + * + * The SHA-1 implementation is FIPS-180-1 compliant, ref: + * @li Wikipedia - SHA-1, https://en.wikipedia.org/wiki/SHA-1 + * @li SHA-1 spec - http://www.itl.nist.gov/fipspubs/fip180-1.htm + * + * The SHA-256 implementation is FIPS-180-2 compliant, ref: + * @li Wikipedia - SHA-2, https://en.wikipedia.org/wiki/SHA-2 + * @li SHA-2 spec - http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + * + * @n @section crypto_mul CRYPTO_Mul + * @ref CRYPTO_Mul is a function for multiplying big integers that are + * bigger than the operand size of the MUL instruction which is 128 bits. + * CRYPTO_Mul multiplies all partial operands of the input operands using + * MUL to form a resulting number which may be twice the size of + * the operands. + * + * CRPYTO_Mul is typically used by RSA implementations which perform a + * huge amount of multiplication and square operations in order to + * implement modular exponentiation. + * Some RSA implementations use a number representation including arrays + * of 32bit words of variable size. The user should compile with + * -D USE_VARIABLE_SIZED_DATA_LOADS in order to load these numbers + * directly into CRYPTO without converting the number representation. + * + * @n @section crypto_exec Load and Execute Instruction Sequences + * The functions for loading data and executing instruction sequences can + * be used to implement complex algorithms like elliptic curve cryptography + * (ECC)) and authenticated encryption algorithms. There are two typical + * modes of operation: + * @li Multi sequence operation + * @li Single static instruction sequence operation + * + * In multi sequence mode the software starts by loading input data, then + * an instruction sequence, execute, and finally read the result. This + * process is repeated until the full crypto operation is complete. + * + * When using a single static instruction sequence, there is just one + * instruction sequence which is loaded initially. The sequence can be setup + * to run multiple times. The data can be loaded during the execution of the + * sequence by using DMA, BUFC and/or programmed I/O directly from the MCU + * core. For details on how to program the instruction sequences please refer + * to the reference manual of the particular Silicon Labs device. + * + * In order to load input data to the CRYPTO module use any of the following + * functions: + * @li @ref CRYPTO_DataWrite - Write 128 bits to a DATA register. + * @li @ref CRYPTO_DDataWrite - Write 256 bits to a DDATA register. + * @li @ref CRYPTO_QDataWrite - Write 512 bits to a QDATA register. + * + * In order to read output data from the CRYPTO module use any of the + * following functions: + * @li @ref CRYPTO_DataRead - Read 128 bits from a DATA register. + * @li @ref CRYPTO_DDataRead - Read 256 bits from a DDATA register. + * @li @ref CRYPTO_QDataRead - Read 512 bits from a QDATA register. + * + * In order to load an instruction sequence to the CRYPTO module use + * @ref CRYPTO_InstructionSequenceLoad. + * + * In order to execute the current instruction sequence in the CRYPTO module + * use @ref CRYPTO_InstructionSequenceExecute. + * + * In order to check whether an instruction sequence has completed + * use @ref CRYPTO_InstructionSequenceDone. + * + * In order to wait for an instruction sequence to complete + * use @ref CRYPTO_InstructionSequenceWait. + * + * In order to optimally load (with regards to speed) and execute an + * instruction sequence use any of the CRYPTO_EXECUTE_X macros (where X is + * in the range 1-20) defined in @ref em_crypto.h. E.g. CRYPTO_EXECUTE_19. + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +#define CRYPTO_INSTRUCTIONS_PER_REG (4) +#define CRYPTO_INSTRUCTIONS_MAX (12) +#define CRYPTO_INSTRUCTION_REGS (CRYPTO_INSTRUCTIONS_MAX/CRYPTO_INSTRUCTIONS_PER_REG) + +#define CRYPTO_SHA1_BLOCK_SIZE_IN_BITS (512) +#define CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES (CRYPTO_SHA1_BLOCK_SIZE_IN_BITS/8) +#define CRYPTO_SHA1_BLOCK_SIZE_IN_32BIT_WORDS (CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES/sizeof(uint32_t)) +#define CRYPTO_SHA1_DIGEST_SIZE_IN_32BIT_WORDS (CRYPTO_SHA1_DIGEST_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_SHA256_BLOCK_SIZE_IN_BITS (512) +#define CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES (CRYPTO_SHA256_BLOCK_SIZE_IN_BITS/8) +#define CRYPTO_SHA256_BLOCK_SIZE_IN_32BIT_WORDS (CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define CRYPTO_SHA256_DIGEST_SIZE_IN_32BIT_WORDS (CRYPTO_SHA256_DIGEST_SIZE_IN_BYTES/sizeof(uint32_t)) + +#define PARTIAL_OPERAND_WIDTH_LOG2 (7) /* 2^7 = 128 */ +#define PARTIAL_OPERAND_WIDTH (1<WAC & (~(_CRYPTO_WAC_MODULUS_MASK | _CRYPTO_WAC_MODOP_MASK)); + + switch (modType) + { + case cryptoModulusBin256: + case cryptoModulusBin128: + case cryptoModulusGcmBin128: + case cryptoModulusEccB233: + case cryptoModulusEccB163: +#ifdef _CRYPTO_WAC_MODULUS_ECCBIN233N + case cryptoModulusEccB233Order: + case cryptoModulusEccB233KOrder: + case cryptoModulusEccB163Order: + case cryptoModulusEccB163KOrder: +#endif + CRYPTO->WAC = temp | modType | CRYPTO_WAC_MODOP_BINARY; + break; + + case cryptoModulusEccP256: + case cryptoModulusEccP224: + case cryptoModulusEccP192: +#ifdef _CRYPTO_WAC_MODULUS_ECCPRIME256P + case cryptoModulusEccP256Order: + case cryptoModulusEccP224Order: + case cryptoModulusEccP192Order: +#endif + CRYPTO->WAC = temp | modType | CRYPTO_WAC_MODOP_REGULAR; + break; + + default: + /* Unknown modulus type. */ + EFM_ASSERT(0); + } +} + +/***************************************************************************//** + * @brief + * Read the key value currently used by the CRYPTO module. + * + * @details + * Read 128 bits or 256 bits from KEY register in the CRYPTO module. + * + * @param[in] val Value of the data to write to the KEYBUF register. + * @param[in] keyWidth Key width - 128 or 256 bits + ******************************************************************************/ +void CRYPTO_KeyRead(CRYPTO_KeyBuf_TypeDef val, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(val); + + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &val[0]); + if (keyWidth == cryptoKey256Bits) + { + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &val[4]); + } +} + +/***************************************************************************//** + * @brief + * Perform a SHA-1 hash operation on a message. + * + * @details + * This function performs a SHA-1 hash operation on the message specified by + * msg with length msgLen, and returns the message digest in msgDigest. + * + * @param[in] msg Message to hash. + * @param[in] msgLen Length of message in bytes. + * @param[out] msgDigest Message digest. + ******************************************************************************/ +void CRYPTO_SHA_1(const uint8_t * msg, + uint64_t msgLen, + CRYPTO_SHA1_Digest_TypeDef msgDigest) +{ + uint32_t temp; + int len; + int blockLen; + uint32_t shaBlock[CRYPTO_SHA1_BLOCK_SIZE_IN_32BIT_WORDS]= + { + /* Initial value */ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 + }; + uint8_t * p8ShaBlock = (uint8_t *) shaBlock; + + /* Initialize crypto module to do SHA-1. */ + CRYPTO->CTRL = CRYPTO_CTRL_SHA_SHA1; + CRYPTO->SEQCTRL = 0; + CRYPTO->SEQCTRLB = 0; + + /* Set result width of MADD32 operation. */ + CRYPTO_ResultWidthSet(cryptoResult256Bits); + + /* Write init value to DDATA1. */ + CRYPTO_DDataWrite(cryptoRegDDATA1, shaBlock); + + /* Copy data to DDATA0 and select DDATA0 and DDATA1 for SHA operation. */ + CRYPTO_EXECUTE_2(CRYPTO_CMD_INSTR_DDATA1TODDATA0, + CRYPTO_CMD_INSTR_SELDDATA0DDATA1); + + len = msgLen; + + while (len >= CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES) + { + /* Write block to QDATA1. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, (uint32_t *) msg); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + len -= CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES; + msg += CRYPTO_SHA1_BLOCK_SIZE_IN_BYTES; + } + + blockLen = 0; + + /* Build the last (or second to last) block */ + for (; len; len--) + p8ShaBlock[blockLen++] = *msg++; + + /* append the '1' bit */ + p8ShaBlock[blockLen++] = 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (blockLen > 56) + { + while (blockLen < 64) + p8ShaBlock[blockLen++] = 0; + + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + blockLen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (blockLen < 56) + p8ShaBlock[blockLen++] = 0; + + /* And finally, encode the message length. */ + { + uint64_t msgLenInBits = msgLen << 3; + temp = msgLenInBits >> 32; + *(uint32_t*)&p8ShaBlock[56] = SWAP32(temp); + temp = msgLenInBits & 0xFFFFFFFF; + *(uint32_t*)&p8ShaBlock[60] = SWAP32(temp); + } + + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + /* Read resulting message digest from DDATA0BIG. */ + ((uint32_t*)msgDigest)[0] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[1] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[2] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[3] = CRYPTO->DDATA0BIG; + ((uint32_t*)msgDigest)[4] = CRYPTO->DDATA0BIG; + temp = CRYPTO->DDATA0BIG; + temp = CRYPTO->DDATA0BIG; + temp = CRYPTO->DDATA0BIG; +} + +/***************************************************************************//** + * @brief + * Perform a SHA-256 hash operation on a message. + * + * @details + * This function performs a SHA-256 hash operation on the message specified + * by msg with length msgLen, and returns the message digest in msgDigest. + * + * @param[in] msg Message to hash. + * @param[in] msgLen Length of message in bytes. + * @param[out] msgDigest Message digest. + ******************************************************************************/ +void CRYPTO_SHA_256(const uint8_t * msg, + uint64_t msgLen, + CRYPTO_SHA256_Digest_TypeDef msgDigest) +{ + uint32_t temp; + int len; + int blockLen; + uint32_t shaBlock[CRYPTO_SHA256_BLOCK_SIZE_IN_32BIT_WORDS]= + { + /* Initial value */ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + }; + uint8_t * p8ShaBlock = (uint8_t *) shaBlock; + + /* Initialize crypyo module to do SHA-256 (SHA-2). */ + CRYPTO->CTRL = CRYPTO_CTRL_SHA_SHA2; + CRYPTO->SEQCTRL = 0; + CRYPTO->SEQCTRLB = 0; + + /* Set result width of MADD32 operation. */ + CRYPTO_ResultWidthSet(cryptoResult256Bits); + + /* Write init value to DDATA1. */ + CRYPTO_DDataWrite(cryptoRegDDATA1, shaBlock); + + /* Copy data ot DDATA0 and select DDATA0 and DDATA1 for SHA operation. */ + CRYPTO_EXECUTE_2(CRYPTO_CMD_INSTR_DDATA1TODDATA0, + CRYPTO_CMD_INSTR_SELDDATA0DDATA1); + len = msgLen; + + while (len >= CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES) + { + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, (uint32_t *) msg); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + len -= CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES; + msg += CRYPTO_SHA256_BLOCK_SIZE_IN_BYTES; + } + + blockLen = 0; + + /* Build the last (or second to last) block */ + for (; len; len--) + p8ShaBlock[blockLen++] = *msg++; + + /* append the '1' bit */ + p8ShaBlock[blockLen++] = 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (blockLen > 56) + { + while (blockLen < 64) + p8ShaBlock[blockLen++] = 0; + + /* Write block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + blockLen = 0; + } + + /* Pad upto 56 bytes of zeroes */ + while (blockLen < 56) + p8ShaBlock[blockLen++] = 0; + + /* And finally, encode the message length. */ + { + uint64_t msgLenInBits = msgLen << 3; + temp = msgLenInBits >> 32; + *(uint32_t *)&p8ShaBlock[56] = SWAP32(temp); + temp = msgLenInBits & 0xFFFFFFFF; + *(uint32_t *)&p8ShaBlock[60] = SWAP32(temp); + } + + /* Write the final block to QDATA1BIG. */ + CRYPTO_QDataWrite(cryptoRegQDATA1BIG, shaBlock); + + /* Execute SHA */ + CRYPTO_EXECUTE_3(CRYPTO_CMD_INSTR_SHA, + CRYPTO_CMD_INSTR_MADD32, + CRYPTO_CMD_INSTR_DDATA0TODDATA1); + + /* Read resulting message digest from DDATA0BIG. */ + CRYPTO_DDataRead(cryptoRegDDATA0BIG, (uint32_t *)msgDigest); +} + +/***************************************************************************//** + * @brief + * Set 32bit word array to zero. + * + * @param[in] words32bits Pointer to 32bit word array + * @param[in] num32bitWords Number of 32bit words in array + ******************************************************************************/ +__STATIC_INLINE void cryptoBigintZeroize(uint32_t * words32bits, + int num32bitWords) +{ + while (num32bitWords--) + *words32bits++ = 0; +} + +/***************************************************************************//** + * @brief + * Increment value of 32bit word array by one. + * + * @param[in] words32bits Pointer to 32bit word array + * @param[in] num32bitWords Number of 32bit words in array + ******************************************************************************/ +__STATIC_INLINE void cryptoBigintIncrement(uint32_t * words32bits, + int num32bitWords) +{ + int i; + for (i=0; i>5; + int numPartialOperandsA = numWordsLastOperandA ? + (aSize >> PARTIAL_OPERAND_WIDTH_LOG2) + 1 : + aSize >> PARTIAL_OPERAND_WIDTH_LOG2; + int numWordsLastOperandB = (bSize&PARTIAL_OPERAND_WIDTH_MASK)>>5; + int numPartialOperandsB = numWordsLastOperandB ? + (bSize >> PARTIAL_OPERAND_WIDTH_LOG2) + 1 : + bSize >> PARTIAL_OPERAND_WIDTH_LOG2; + int numWordsLastOperandR = (rSize&PARTIAL_OPERAND_WIDTH_MASK)>>5; + int numPartialOperandsR = numWordsLastOperandR ? + (rSize >> PARTIAL_OPERAND_WIDTH_LOG2) + 1 : + rSize >> PARTIAL_OPERAND_WIDTH_LOG2; + EFM_ASSERT(numPartialOperandsA + numPartialOperandsB <= numPartialOperandsR); +#else + int numPartialOperandsA = aSize >> PARTIAL_OPERAND_WIDTH_LOG2; + int numPartialOperandsB = bSize >> PARTIAL_OPERAND_WIDTH_LOG2; + EFM_ASSERT((aSize & PARTIAL_OPERAND_WIDTH_MASK) == 0); + EFM_ASSERT((bSize & PARTIAL_OPERAND_WIDTH_MASK) == 0); +#endif + EFM_ASSERT(aSize + bSize <= rSize); + + /* Set R to zero. */ + cryptoBigintZeroize(R, rSize >> 5); + + /* Set multiplication width. */ + CRYPTO->WAC = CRYPTO_WAC_MULWIDTH_MUL128 | CRYPTO_WAC_RESULTWIDTH_256BIT; + + /* Setup DMA request signalling in order for MCU to run in parallel with + CRYPTO instruction sequence execution, and prepare data loading which + can take place immediately when CRYPTO is ready inside the instruction + sequence. */ + CRYPTO->CTRL = + CRYPTO_CTRL_DMA0RSEL_DATA0 | CRYPTO_CTRL_DMA0MODE_FULL | + CRYPTO_CTRL_DMA1RSEL_DATA1 | CRYPTO_CTRL_DMA1MODE_FULL; + + CRYPTO_EXECUTE_4( + CRYPTO_CMD_INSTR_CCLR, /* Carry = 0 */ + CRYPTO_CMD_INSTR_CLR, /* DDATA0 = 0 */ + /* clear result accumulation register */ + CRYPTO_CMD_INSTR_DDATA0TODDATA2, + CRYPTO_CMD_INSTR_SELDDATA1DDATA3); + /* + register map: + DDATA0: working register + DDATA1: B(j) + DDATA2: R(i+j+1) and R(i+j), combined with DMA entry for B(j) + DDATA3: A(i) + */ + + CRYPTO_SEQ_LOAD_10( + /* Temporarily load partial operand B(j) to DATA0. */ + /* R(i+j+1) is still in DATA1 */ + CRYPTO_CMD_INSTR_DMA0TODATA, + /* Move B(j) to DDATA1 */ + CRYPTO_CMD_INSTR_DDATA2TODDATA1, + + /* Restore previous partial result (now R(i+j)) */ + CRYPTO_CMD_INSTR_DATA1TODATA0, + + /* Load next partial result R(i+j+1) */ + CRYPTO_CMD_INSTR_DMA1TODATA, + + /* Execute partial multiplication A(i)inDDATA1 * B(j)inDDATA3*/ + CRYPTO_CMD_INSTR_MULO, + + /* Add the result to the previous partial result */ + /* AND take the previous carry value into account */ + /* at the right place (bit 128, ADDIC instruction */ + CRYPTO_CMD_INSTR_SELDDATA0DDATA2, + CRYPTO_CMD_INSTR_ADDIC, + + /* Save the new partial result (lower half) */ + CRYPTO_CMD_INSTR_DDATA0TODDATA2, + CRYPTO_CMD_INSTR_DATATODMA0, + /* Reset the operand selector for next*/ + CRYPTO_CMD_INSTR_SELDDATA2DDATA3 + ); + + /**************** End Initializations ******************/ + + for(i=0; i>(i*PARTIAL_OPERAND_WIDTH) to DDATA1. */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandA != 0) && ( i == numPartialOperandsA-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA2, + &A[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandA); + else + CRYPTO_DataWrite(cryptoRegDATA2, &A[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA2, &A[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + + /* Load partial result in R>>(i*PARTIAL_OPERAND_WIDTH) to DATA1. */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandR != 0) && ( i == numPartialOperandsR-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA1, + &R[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandR); + else + CRYPTO_DataWrite(cryptoRegDATA1, &R[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA1, &R[i*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + + /* Clear carry */ + CRYPTO->CMD = CRYPTO_CMD_INSTR_CCLR; + + /* Setup number of sequence iterations and block size. */ + CRYPTO->SEQCTRL = CRYPTO_SEQCTRL_BLOCKSIZE_16BYTES + | (PARTIAL_OPERAND_WIDTH_IN_BYTES * numPartialOperandsB); + + /* Execute the MULtiply instruction sequence. */ + CRYPTO_InstructionSequenceExecute(); + + for (j=0; j>(j*`PARTIAL_OPERAND_WIDTH) to DDATA2 + (via DATA0). */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandB != 0) && ( j == numPartialOperandsB-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA0, + &B[j*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandB); + else + CRYPTO_DataWrite(cryptoRegDATA0, + &B[j*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA0, + &B[j*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + + /* Load most significant partial result + R>>((i+j+1)*`PARTIAL_OPERAND_WIDTH) into DATA1. */ +#ifdef USE_VARIABLE_SIZED_DATA_LOADS + if ( (numWordsLastOperandR != 0) && ( (i+j+1) == numPartialOperandsR-1 ) ) + CRYPTO_DataWriteVariableSize(cryptoRegDATA1, + &R[(i+j+1)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS], + numWordsLastOperandR); + else + CRYPTO_DataWrite(cryptoRegDATA1, + &R[(i+j+1)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#else + CRYPTO_DataWrite(cryptoRegDATA1, + &R[(i+j+1)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); +#endif + /* Store least significant partial result */ + CRYPTO_DataRead(cryptoRegDATA0, + &R[(i+j)*PARTIAL_OPERAND_WIDTH_IN_32BIT_WORDS]); + + } /* for (j=0; jXOR +-------------->XOR + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * |---------+ | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Ciphertext Ciphertext + * |----------+ | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | decryption | | | decryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * InitVector ->XOR +-------------->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 128 bit encryption key. When doing + * decryption, this is the 128 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey128(). + * If this argument is null, the key will not be loaded, as it is assumed + * the key has been loaded into KEYHA previously. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CBC128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_CBCx(out, in, len, key, iv, encrypt, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Cipher-block chaining (CBC) cipher mode encryption/decryption, 256 bit + * key. + * + * @details + * Please see CRYPTO_AES_CBC128() for CBC figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CBC256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_CBCx(out, in, len, key, iv, encrypt, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * AES Cipher feedback (CFB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * Plaintext ->XOR | Plaintext ->XOR + * |---------+ | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * V | V + * XOR<- Ciphertext XOR<- Ciphertext + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_CFBx(out, in, len, key, iv, encrypt, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Cipher feedback (CFB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see CRYPTO_AES_CFB128() for CFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_CFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_CFBx(out, in, len, key, iv, encrypt, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * AES Counter (CTR) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Counter Counter + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * Plaintext ->XOR Plaintext ->XOR + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Counter Counter + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * Ciphertext ->XOR Ciphertext ->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key. + * If this argument is null, the key will not be loaded, as it is assumed + * the key has been loaded into KEYHA previously. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. Not supported by CRYPTO. + * This parameter is included in order for backwards compatibility with + * the EFM32 em_aes.h API. + ******************************************************************************/ +void CRYPTO_AES_CTR128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_CTRx(out, in, len, key, ctr, ctrFunc, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Counter (CTR) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see CRYPTO_AES_CTR128() for CTR figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. Not supported by CRYPTO. + * This parameter is included in order for backwards compatibility with + * the EFM32 em_aes.h API. + ******************************************************************************/ +void CRYPTO_AES_CTR256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_CTRx(out, in, len, key, ctr, ctrFunc, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * Update last 32 bits of 128 bit counter, by incrementing with 1. + * + * @details + * Notice that no special consideration is given to possible wrap around. If + * 32 least significant bits are 0xFFFFFFFF, they will be updated to 0x00000000, + * ignoring overflow. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[in,out] ctr + * Buffer holding 128 bit counter to be updated. + ******************************************************************************/ +void CRYPTO_AES_CTRUpdate32Bit(uint8_t * ctr) +{ + uint32_t * _ctr = (uint32_t *) ctr; + + _ctr[3] = __REV(__REV(_ctr[3]) + 1); +} + +/***************************************************************************//** + * @brief + * Generate 128 bit AES decryption key from 128 bit encryption key. The + * decryption key is used for some cipher modes when decrypting. + * + * @details + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place 128 bit decryption key. Must be at least 16 bytes long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding 128 bit encryption key. Must be at least 16 bytes long. + ******************************************************************************/ +void CRYPTO_AES_DecryptKey128(uint8_t * out, const uint8_t * in) +{ + uint32_t * _out = (uint32_t *) out; + const uint32_t * _in = (const uint32_t *) in; + + /* Load key */ + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &_in[0]); + + /* Do dummy encryption to generate decrypt key */ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_IntClear(CRYPTO_IF_INSTRDONE); + CRYPTO->CMD = CRYPTO_CMD_INSTR_AESENC; + + /* Save decryption key */ + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &_out[0]); +} + +/***************************************************************************//** + * @brief + * Generate 256 bit AES decryption key from 256 bit encryption key. The + * decryption key is used for some cipher modes when decrypting. + * + * @details + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place 256 bit decryption key. Must be at least 32 bytes long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding 256 bit encryption key. Must be at least 32 bytes long. + ******************************************************************************/ +void CRYPTO_AES_DecryptKey256(uint8_t * out, const uint8_t * in) +{ + uint32_t * _out = (uint32_t *) out; + const uint32_t * _in = (const uint32_t *) in; + + /* Load key */ + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &_in[0]); + CRYPTO_BurstToCrypto(&CRYPTO->KEYBUF, &_in[4]); + + /* Do dummy encryption to generate decrypt key */ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO->CMD = CRYPTO_CMD_INSTR_AESENC; + + /* Save decryption key */ + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &_out[0]); + CRYPTO_BurstFromCrypto(&CRYPTO->KEY, &_out[4]); +} + +/***************************************************************************//** + * @brief + * AES Electronic Codebook (ECB) cipher mode encryption/decryption, + * 128 bit key. + * + * @details + * Encryption: + * @verbatim + * Plaintext Plaintext + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | encryption | | encryption | + * +--------------+ +--------------+ + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * Ciphertext Ciphertext + * | | + * V V + * +--------------+ +--------------+ + * Key ->| Block cipher | Key ->| Block cipher | + * | decryption | | decryption | + * +--------------+ +--------------+ + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 128 bit encryption key. When doing + * decryption, this is the 128 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey128(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_ECB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_ECBx(out, in, len, key, encrypt, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Electronic Codebook (ECB) cipher mode encryption/decryption, + * 256 bit key. + * + * @details + * Please see CRYPTO_AES_ECB128() for ECB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + ******************************************************************************/ +void CRYPTO_AES_ECB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_ECBx(out, in, len, key, encrypt, cryptoKey256Bits); +} + +/***************************************************************************//** + * @brief + * AES Output feedback (OFB) cipher mode encryption/decryption, 128 bit key. + * + * @details + * Encryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * |---------+ | + * V V + * Plaintext ->XOR Plaintext ->XOR + * | | + * V V + * Ciphertext Ciphertext + * @endverbatim + * Decryption: + * @verbatim + * InitVector +----------------+ + * | | | + * V | V + * +--------------+ | +--------------+ + * Key ->| Block cipher | | Key ->| Block cipher | + * | encryption | | | encryption | + * +--------------+ | +--------------+ + * | | | + * |---------+ | + * V V + * Ciphertext ->XOR Ciphertext ->XOR + * | | + * V V + * Plaintext Plaintext + * @endverbatim + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 128 bit encryption key. + * + * @param[in] iv + * 128 bit initialization vector to use. + ******************************************************************************/ +void CRYPTO_AES_OFB128(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES128; + CRYPTO_AES_OFBx(out, in, len, key, iv, cryptoKey128Bits); +} + +/***************************************************************************//** + * @brief + * AES Output feedback (OFB) cipher mode encryption/decryption, 256 bit key. + * + * @details + * Please see CRYPTO_AES_OFB128() for OFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in] iv + * 128 bit initialization vector to use. + ******************************************************************************/ +void CRYPTO_AES_OFB256(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv) +{ + CRYPTO->CTRL = CRYPTO_CTRL_AES_AES256; + CRYPTO_AES_OFBx(out, in, len, key, iv, cryptoKey256Bits); +} + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Cipher-block chaining (CBC) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_CBC128() for CBC figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_CBCx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + /* Initialize control registers. */ + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + if (encrypt) + { + CRYPTO_DataWrite(cryptoRegDATA0, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR1_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *) in, + cryptoRegDATA0, (uint32_t *) out); + } + else + { + CRYPTO_DataWrite(cryptoRegDATA2, (uint32_t *) iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA1TODATA0 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESDEC << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_DATA2TODATA0XOR << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA2 << _CRYPTO_SEQ0_INSTR3_SHIFT; + + CRYPTO->SEQ1 = 0; + + /* The following call is equivalent to the last call in the + 'if( encrypt )' branch. However moving this + call outside the conditional scope results in slightly poorer + performance for some compiler optimizations. */ + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *) in, + cryptoRegDATA0, (uint32_t *) out); + } +} + +/***************************************************************************//** + * @brief + * Cipher feedback (CFB) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_CFB128() for CFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key is used for both encryption and decryption modes. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_CFBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + /* Initialize control registers. */ + CRYPTO->WAC = 0; + + /* Load Key */ + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + /* Load instructions to CRYPTO sequencer. */ + if (encrypt) + { + /* Load IV */ + CRYPTO_DataWrite(cryptoRegDATA0, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ0_INSTR1_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *)in, + cryptoRegDATA0, (uint32_t *)out + ); + } + else + { + /* Load IV */ + CRYPTO_DataWrite(cryptoRegDATA2, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA2TODATA0 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA1TODATA2 << _CRYPTO_SEQ0_INSTR3_SHIFT; + CRYPTO->SEQ1 = 0; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA1, (uint32_t *)in, + cryptoRegDATA0, (uint32_t *)out + ); + } +} + +/***************************************************************************//** + * @brief + * Counter (CTR) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_CTR128() for CTR figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in,out] ctr + * 128 bit initial counter value. The counter is updated after each AES + * block encoding through use of @p ctrFunc. + * + * @param[in] ctrFunc + * Function used to update counter value. Not supported by CRYPTO. + * This parameter is included in order for backwards compatibility with + * the EFM32 em_aes.h API. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_CTRx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + uint8_t * ctr, + CRYPTO_AES_CtrFuncPtr_TypeDef ctrFunc, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + (void) ctrFunc; + + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + /* Initialize control registers. */ + CRYPTO->CTRL |= CRYPTO_CTRL_INCWIDTH_INCWIDTH4; + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + CRYPTO_DataWrite(cryptoRegDATA1, (uint32_t *) ctr); + + CRYPTO->SEQ0 = CRYPTO_CMD_INSTR_DATA1TODATA0 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA3 << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA1INC << _CRYPTO_SEQ0_INSTR3_SHIFT; + + CRYPTO->SEQ1 = CRYPTO_CMD_INSTR_DATA2TODATA0XOR << _CRYPTO_SEQ1_INSTR4_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA2, (uint32_t *) in, + cryptoRegDATA0, (uint32_t *) out); + + CRYPTO_DataRead(cryptoRegDATA1, (uint32_t *) ctr); +} + +/***************************************************************************//** + * @brief + * Electronic Codebook (ECB) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_ECB128() for ECB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * When doing encryption, this is the 256 bit encryption key. When doing + * decryption, this is the 256 bit decryption key. The decryption key may + * be generated from the encryption key with CRYPTO_AES_DecryptKey256(). + * + * @param[in] encrypt + * Set to true to encrypt, false to decrypt. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_ECBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + bool encrypt, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + if (encrypt) + { + CRYPTO->SEQ0 = + (CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ0_INSTR1_SHIFT); + } + else + { + CRYPTO->SEQ0 = + (CRYPTO_CMD_INSTR_AESDEC << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ0_INSTR1_SHIFT); + } + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA0, (uint32_t *) in, + cryptoRegDATA1, (uint32_t *) out); +} + +/***************************************************************************//** + * @brief + * Output feedback (OFB) cipher mode encryption/decryption, 128/256 bit key. + * + * @details + * Please see CRYPTO_AES_OFB128() for OFB figure. + * + * Please refer to general comments on layout and byte ordering of parameters. + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] key + * 256 bit encryption key. + * + * @param[in] iv + * 128 bit initialization vector to use. + * + * @param[in] keyWidth + * Set to cryptoKey128Bits or cryptoKey256Bits. + ******************************************************************************/ +static void CRYPTO_AES_OFBx(uint8_t * out, + const uint8_t * in, + unsigned int len, + const uint8_t * key, + const uint8_t * iv, + CRYPTO_KeyWidth_TypeDef keyWidth) +{ + EFM_ASSERT(!(len % CRYPTO_AES_BLOCKSIZE)); + + CRYPTO->WAC = 0; + + CRYPTO_KeyBufWrite((uint32_t *)key, keyWidth); + + CRYPTO_DataWrite(cryptoRegDATA2, (uint32_t *)iv); + + CRYPTO->SEQ0 = + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ0_INSTR0_SHIFT | + CRYPTO_CMD_INSTR_DATA2TODATA0 << _CRYPTO_SEQ0_INSTR1_SHIFT | + CRYPTO_CMD_INSTR_AESENC << _CRYPTO_SEQ0_INSTR2_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA2 << _CRYPTO_SEQ0_INSTR3_SHIFT; + CRYPTO->SEQ1 = + CRYPTO_CMD_INSTR_DATA1TODATA0XOR << _CRYPTO_SEQ1_INSTR4_SHIFT | + CRYPTO_CMD_INSTR_DATA0TODATA1 << _CRYPTO_SEQ1_INSTR5_SHIFT; + + CRYPTO_AES_ProcessLoop(len, + cryptoRegDATA0, (uint32_t *) in, + cryptoRegDATA1, (uint32_t *) out); +} + +/***************************************************************************//** + * @brief + * Function performs generic AES loop. + * + * @details + * Function loads given register with provided input data. Triggers CRYPTO to + * perform sequence of instructions and read specified output register to + * output buffer. + * + * @param[in] len + * Number of bytes to encrypt/decrypt. Must be a multiple of 16. + * + * @param[in] inReg + * Input register - one of DATA0,DATA1,DATA2,DATA3 + * + * @param[in] in + * Buffer holding data to encrypt/decrypt. Must be at least @p len long. + * + * @param[in] outReg + * Output register - one of DATA0,DATA1,DATA2,DATA3 + * + * @param[out] out + * Buffer to place encrypted/decrypted data. Must be at least @p len long. It + * may be set equal to @p in, in which case the input buffer is overwritten. + ******************************************************************************/ +static inline void CRYPTO_AES_ProcessLoop(uint32_t len, + CRYPTO_DataReg_TypeDef inReg, + uint32_t * in, + CRYPTO_DataReg_TypeDef outReg, + uint32_t * out) +{ + len /= CRYPTO_AES_BLOCKSIZE; + CRYPTO->SEQCTRL = 16 << _CRYPTO_SEQCTRL_LENGTHA_SHIFT; + + while (len--) + { + /* Load data and trigger encryption */ + CRYPTO_DataWrite(inReg, (uint32_t *)in); + + CRYPTO->CMD = CRYPTO_CMD_SEQSTART; + + /* Save encrypted/decrypted data */ + CRYPTO_DataRead(outReg, (uint32_t *)out); + + out += 4; + in += 4; + } +} + +/** @} (end addtogroup CRYPTO) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(CRYPTO_COUNT) && (CRYPTO_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_dac.c b/cpu/efm32_common/emlib/src/em_dac.c new file mode 100644 index 0000000000000..ab5d03aad570e --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_dac.c @@ -0,0 +1,341 @@ +/***************************************************************************//** + * @file em_dac.c + * @brief Digital to Analog Coversion (DAC) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_dac.h" +#if defined(DAC_COUNT) && (DAC_COUNT > 0) +#include "em_cmu.h" +#include "em_assert.h" +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup DAC + * @brief Digital to Analog Coversion (DAC) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of DAC channel for assert statements. */ +#define DAC_CH_VALID(ch) ((ch) <= 1) + +/** Max DAC clock */ +#define DAC_MAX_CLOCK 1000000 + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Enable/disable DAC channel. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] ch + * Channel to enable/disable. + * + * @param[in] enable + * true to enable DAC channel, false to disable. + ******************************************************************************/ +void DAC_Enable(DAC_TypeDef *dac, unsigned int ch, bool enable) +{ + volatile uint32_t *reg; + + EFM_ASSERT(DAC_REF_VALID(dac)); + EFM_ASSERT(DAC_CH_VALID(ch)); + + if (!ch) + { + reg = &(dac->CH0CTRL); + } + else + { + reg = &(dac->CH1CTRL); + } + + BUS_RegBitWrite(reg, _DAC_CH0CTRL_EN_SHIFT, enable); +} + + +/***************************************************************************//** + * @brief + * Initialize DAC. + * + * @details + * Initializes common parts for both channels. In addition, channel control + * configuration must be done, please refer to DAC_InitChannel(). + * + * @note + * This function will disable both channels prior to configuration. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] init + * Pointer to DAC initialization structure. + ******************************************************************************/ +void DAC_Init(DAC_TypeDef *dac, const DAC_Init_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(DAC_REF_VALID(dac)); + + /* Make sure both channels are disabled. */ + BUS_RegBitWrite(&(dac->CH0CTRL), _DAC_CH0CTRL_EN_SHIFT, 0); + BUS_RegBitWrite(&(dac->CH1CTRL), _DAC_CH0CTRL_EN_SHIFT, 0); + + /* Load proper calibration data depending on selected reference */ + switch (init->reference) + { + case dacRef2V5: + dac->CAL = DEVINFO->DAC0CAL1; + break; + + case dacRefVDD: + dac->CAL = DEVINFO->DAC0CAL2; + break; + + default: /* 1.25V */ + dac->CAL = DEVINFO->DAC0CAL0; + break; + } + + tmp = ((uint32_t)(init->refresh) << _DAC_CTRL_REFRSEL_SHIFT) + | (((uint32_t)(init->prescale) << _DAC_CTRL_PRESC_SHIFT) + & _DAC_CTRL_PRESC_MASK) + | ((uint32_t)(init->reference) << _DAC_CTRL_REFSEL_SHIFT) + | ((uint32_t)(init->outMode) << _DAC_CTRL_OUTMODE_SHIFT) + | ((uint32_t)(init->convMode) << _DAC_CTRL_CONVMODE_SHIFT); + + if (init->ch0ResetPre) + { + tmp |= DAC_CTRL_CH0PRESCRST; + } + + if (init->outEnablePRS) + { + tmp |= DAC_CTRL_OUTENPRS; + } + + if (init->sineEnable) + { + tmp |= DAC_CTRL_SINEMODE; + } + + if (init->diff) + { + tmp |= DAC_CTRL_DIFF; + } + + dac->CTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Initialize DAC channel. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] init + * Pointer to DAC initialization structure. + * + * @param[in] ch + * Channel number to initialize. + ******************************************************************************/ +void DAC_InitChannel(DAC_TypeDef *dac, + const DAC_InitChannel_TypeDef *init, + unsigned int ch) +{ + uint32_t tmp; + + EFM_ASSERT(DAC_REF_VALID(dac)); + EFM_ASSERT(DAC_CH_VALID(ch)); + + tmp = (uint32_t)(init->prsSel) << _DAC_CH0CTRL_PRSSEL_SHIFT; + + if (init->enable) + { + tmp |= DAC_CH0CTRL_EN; + } + + if (init->prsEnable) + { + tmp |= DAC_CH0CTRL_PRSEN; + } + + if (init->refreshEnable) + { + tmp |= DAC_CH0CTRL_REFREN; + } + + if (ch) + { + dac->CH1CTRL = tmp; + } + else + { + dac->CH0CTRL = tmp; + } +} + + +/***************************************************************************//** + * @brief + * Set the output signal of a DAC channel to a given value. + * + * @details + * This function sets the output signal of a DAC channel by writing @p value + * to the corresponding CHnDATA register. + * + * @param[in] dac + * Pointer to DAC peripheral register block. + * + * @param[in] channel + * Channel number to set output of. + * + * @param[in] value + * Value to write to the channel output register CHnDATA. + ******************************************************************************/ +void DAC_ChannelOutputSet( DAC_TypeDef *dac, + unsigned int channel, + uint32_t value ) +{ + switch(channel) + { + case 0: + DAC_Channel0OutputSet(dac, value); + break; + case 1: + DAC_Channel1OutputSet(dac, value); + break; + default: + EFM_ASSERT(0); + break; + } +} + + +/***************************************************************************//** + * @brief + * Calculate prescaler value used to determine DAC clock. + * + * @details + * The DAC clock is given by: HFPERCLK / (prescale ^ 2). If the requested + * DAC frequency is low and the max prescaler value can not adjust the + * actual DAC frequency lower than the requested DAC frequency, then the + * max prescaler value is returned, resulting in a higher DAC frequency + * than requested. + * + * @param[in] dacFreq DAC frequency wanted. The frequency will automatically + * be adjusted to be below max allowed DAC clock. + * + * @param[in] hfperFreq Frequency in Hz of reference HFPER clock. Set to 0 to + * use currently defined HFPER clock setting. + * + * @return + * Prescaler value to use for DAC in order to achieve a clock value + * <= @p dacFreq. + ******************************************************************************/ +uint8_t DAC_PrescaleCalc(uint32_t dacFreq, uint32_t hfperFreq) +{ + uint32_t ret; + + /* Make sure selected DAC clock is below max value */ + if (dacFreq > DAC_MAX_CLOCK) + { + dacFreq = DAC_MAX_CLOCK; + } + + /* Use current HFPER frequency? */ + if (!hfperFreq) + { + hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER); + } + + /* Iterate in order to determine best prescale value. Only a few possible */ + /* values. We start with lowest prescaler value in order to get first */ + /* equal or below wanted DAC frequency value. */ + for (ret = 0; ret <= (_DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT); ret++) + { + if ((hfperFreq >> ret) <= dacFreq) + break; + } + + /* If ret is higher than the max prescaler value, make sure to return + the max value. */ + if (ret > (_DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT)) + { + ret = _DAC_CTRL_PRESC_MASK >> _DAC_CTRL_PRESC_SHIFT; + } + + return (uint8_t)ret; +} + + +/***************************************************************************//** + * @brief + * Reset DAC to same state as after a HW reset. + * + * @param[in] dac + * Pointer to ADC peripheral register block. + ******************************************************************************/ +void DAC_Reset(DAC_TypeDef *dac) +{ + /* Disable channels, before resetting other registers. */ + dac->CH0CTRL = _DAC_CH0CTRL_RESETVALUE; + dac->CH1CTRL = _DAC_CH1CTRL_RESETVALUE; + dac->CTRL = _DAC_CTRL_RESETVALUE; + dac->IEN = _DAC_IEN_RESETVALUE; + dac->IFC = _DAC_IFC_MASK; + dac->CAL = DEVINFO->DAC0CAL0; + dac->BIASPROG = _DAC_BIASPROG_RESETVALUE; + /* Do not reset route register, setting should be done independently */ +} + + +/** @} (end addtogroup DAC) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(DAC_COUNT) && (DAC_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_dbg.c b/cpu/efm32_common/emlib/src/em_dbg.c new file mode 100644 index 0000000000000..a37939a84b3b5 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_dbg.c @@ -0,0 +1,126 @@ +/***************************************************************************//** + * @file em_dbg.c + * @brief Debug (DBG) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_dbg.h" + +#if defined( CoreDebug_DHCSR_C_DEBUGEN_Msk ) + +#include "em_assert.h" +#include "em_cmu.h" +#include "em_gpio.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup DBG + * @brief Debug (DBG) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +#if defined( GPIO_ROUTE_SWOPEN ) || defined( GPIO_ROUTEPEN_SWVPEN ) +/***************************************************************************//** + * @brief + * Enable Serial Wire Output (SWO) pin. + * + * @details + * The SWO pin (sometimes denoted SWV, serial wire viewer) allows for + * miscellaneous output to be passed from the Cortex-M3 debug trace module to + * an external debug probe. By default, the debug trace module and pin output + * may be disabled. + * + * Since the SWO pin is only useful when using a debugger, a suggested use + * of this function during startup may be: + * @verbatim + * if (DBG_Connected()) + * { + * DBG_SWOEnable(1); + * } + * @endverbatim + * By checking if debugger is attached, some setup leading to higher energy + * consumption when debugger is attached, can be avoided when not using + * a debugger. + * + * Another alternative may be to set the debugger tool chain to configure + * the required setup (similar to the content of this function) by some + * sort of toolchain scripting during its attach/reset procedure. In that + * case, the above suggested code for enabling the SWO pin is not required + * in the application. + * + * @param[in] location + * Pin location used for SWO pin on the application in use. + ******************************************************************************/ +void DBG_SWOEnable(unsigned int location) +{ + int port; + int pin; + + EFM_ASSERT(location < AFCHANLOC_MAX); + +#if defined ( AF_DBG_SWO_PORT ) + port = AF_DBG_SWO_PORT(location); + pin = AF_DBG_SWO_PIN(location); +#elif defined (AF_DBG_SWV_PORT ) + port = AF_DBG_SWV_PORT(location); + pin = AF_DBG_SWV_PIN(location); +#else +#warning "AF debug port is not defined." +#endif + + /* Port/pin location not defined for device? */ + if ((pin < 0) || (port < 0)) + { + EFM_ASSERT(0); + return; + } + + /* Ensure auxiliary clock going to the Cortex debug trace module is enabled */ + CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, false); + + /* Set selected pin location for SWO pin and enable it */ + GPIO_DbgLocationSet(location); + GPIO_DbgSWOEnable(true); + + /* Configure SWO pin for output */ + GPIO_PinModeSet((GPIO_Port_TypeDef)port, pin, gpioModePushPull, 0); +} +#endif + +/** @} (end addtogroup DBG) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined( CoreDebug_DHCSR_C_DEBUGEN_Msk ) */ diff --git a/cpu/efm32_common/emlib/src/em_dma.c b/cpu/efm32_common/emlib/src/em_dma.c new file mode 100644 index 0000000000000..37675dec00452 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_dma.c @@ -0,0 +1,1231 @@ +/***************************************************************************//** + * @file em_dma.c + * @brief Direct memory access (DMA) module peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_dma.h" +#if defined( DMA_PRESENT ) + +#include "em_cmu.h" +#include "em_assert.h" +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup DMA + * @brief Direct Memory Access (DMA) Peripheral API + * @details + * These DMA access functions provide basic support for the following + * types of DMA cycles: + * + * @li @b Basic, used for transferring data between memory and peripherals. + * @li @b Auto-request, used for transferring data between memory locations. + * @li @b Ping-pong, used for for continuous transfer of data between memory + * and peripherals, automatically toggling between primary and alternate + * descriptors. + * @li @b Memory @b scatter-gather, used for transferring a number of buffers + * between memory locations. + * @li @b Peripheral @b scatter-gather, used for transferring a number of + * buffers between memory and peripherals. + * + * A basic understanding of the DMA controller is assumed. Please refer to + * the reference manual for further details. + * + * The term 'descriptor' is used as a synonym to the 'channel control data + * structure' term. + * + * In order to use the DMA controller, the initialization function must have + * been executed once (normally during system init): + * @verbatim + * DMA_Init(); + * @endverbatim + * + * Then, normally a user of a DMA channel configures the channel: + * @verbatim + * DMA_CfgChannel(); + * @endverbatim + * + * The channel configuration only has to be done once, if reusing the channel + * for the same purpose later. + * + * In order to set up a DMA cycle, the primary and/or alternate descriptor + * has to be set up as indicated below. + * + * For basic or auto-request cycles, use once on either primary or alternate + * descriptor: + * @verbatim + * DMA_CfgDescr(); + * @endverbatim + * + * For ping-pong cycles, configure both primary or alternate descriptors: + * @verbatim + * DMA_CfgDescr(); // Primary descriptor config + * DMA_CfgDescr(); // Alternate descriptor config + * @endverbatim + * + * For scatter-gather cycles, the alternate descriptor array must be programmed: + * @verbatim + * // 'n' is the number of scattered buffers + * // 'descr' points to the start of the alternate descriptor array + * + * // Fill in 'cfg' + * DMA_CfgDescrScatterGather(descr, 0, cfg); + * // Fill in 'cfg' + * DMA_CfgDescrScatterGather(descr, 1, cfg); + * : + * // Fill in 'cfg' + * DMA_CfgDescrScatterGather(descr, n - 1, cfg); + * @endverbatim + * + * In many cases, the descriptor configuration only has to be done once, if + * re-using the channel for the same type of DMA cycles later. + * + * In order to activate the DMA cycle, use the respective DMA_Activate...() + * function. + * + * For ping-pong DMA cycles, use DMA_RefreshPingPong() from the callback to + * prepare the completed descriptor for reuse. Notice that the refresh must + * be done prior to the other active descriptor completes, otherwise the + * ping-pong DMA cycle will halt. + * @{ + ******************************************************************************/ + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/***************************************************************************//** + * @brief + * Prepare descriptor for DMA cycle. + * + * @details + * This function prepares the last pieces of configuration required to start a + * DMA cycle. Since the DMA controller itself modifies some parts of the + * descriptor during use, those parts need to be refreshed if reusing a + * descriptor configuration. + * + * @note + * If using this function on a descriptor already activated and in use by the + * DMA controller, the behaviour is undefined. + * + * @param[in] channel + * DMA channel to prepare for DMA cycle. + * + * @param[in] cycleCtrl + * DMA cycle type to prepare for. + * + * @param[in] primary + * @li true - prepare primary descriptor + * @li false - prepare alternate descriptor + * + * @param[in] useBurst + * The burst feature is only used on peripherals supporting DMA bursts. + * Bursts must not be used if the total length (as given by nMinus1) is + * less than the arbitration rate configured for the descriptor. Please + * refer to the reference manual for further details on burst usage. + * + * @param[in] dst + * Address to start location to transfer data to. If NULL, leave setting in + * descriptor as is. + * + * @param[in] src + * Address to start location to transfer data from. If NULL, leave setting in + * descriptor as is. + * + * @param[in] nMinus1 + * Number of elements (minus 1) to transfer (<= 1023). + ******************************************************************************/ +static void DMA_Prepare(unsigned int channel, + DMA_CycleCtrl_TypeDef cycleCtrl, + bool primary, + bool useBurst, + void *dst, + void *src, + unsigned int nMinus1) +{ + DMA_DESCRIPTOR_TypeDef *descr; + DMA_DESCRIPTOR_TypeDef *primDescr; + DMA_CB_TypeDef *cb; + uint32_t inc; + uint32_t chBit; + uint32_t tmp; + + primDescr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE)) + channel; + + /* Find descriptor to configure */ + if (primary) + { + descr = primDescr; + } + else + { + descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE)) + channel; + } + + /* If callback defined, update info on whether callback is issued */ + /* for primary or alternate descriptor. Mainly needed for ping-pong */ + /* cycles. */ + cb = (DMA_CB_TypeDef *)(primDescr->USER); + if (cb) + { + cb->primary = (uint8_t)primary; + } + + if (src) + { + inc = (descr->CTRL & _DMA_CTRL_SRC_INC_MASK) >> _DMA_CTRL_SRC_INC_SHIFT; + if (inc == _DMA_CTRL_SRC_INC_NONE) + { + descr->SRCEND = src; + } + else + { + descr->SRCEND = (void *)((uint32_t)src + (nMinus1 << inc)); + } + } + + if (dst) + { + inc = (descr->CTRL & _DMA_CTRL_DST_INC_MASK) >> _DMA_CTRL_DST_INC_SHIFT; + if (inc == _DMA_CTRL_DST_INC_NONE) + { + descr->DSTEND = dst; + } + else + { + descr->DSTEND = (void *)((uint32_t)dst + (nMinus1 << inc)); + } + } + + chBit = 1 << channel; + if (useBurst) + { + DMA->CHUSEBURSTS = chBit; + } + else + { + DMA->CHUSEBURSTC = chBit; + } + + if (primary) + { + DMA->CHALTC = chBit; + } + else + { + DMA->CHALTS = chBit; + } + + /* Set cycle control */ + tmp = descr->CTRL & ~(_DMA_CTRL_CYCLE_CTRL_MASK | _DMA_CTRL_N_MINUS_1_MASK); + tmp |= nMinus1 << _DMA_CTRL_N_MINUS_1_SHIFT; + tmp |= (uint32_t)cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT; + descr->CTRL = tmp; +} + +/** @endcond */ + +/******************************************************************************* + ************************ INTERRUPT FUNCTIONS ****************************** + ******************************************************************************/ + +#ifndef EXCLUDE_DEFAULT_DMA_IRQ_HANDLER + +/***************************************************************************//** + * @brief + * Interrupt handler for DMA cycle completion handling. + * + * @details + * Clears any pending flags and calls registered callback (if any). + * + * If using the default interrupt vector table setup provided, this function + * is automatically placed in the IRQ table due to weak linking. If taking + * control over the interrupt vector table in some other way, this interrupt + * handler must be installed in order to be able to support callback actions. + * + * In order for the user to implement a custom IRQ handler or run without + * a DMA IRQ handler, the user can define EXCLUDE_DEFAULT_DMA_IRQ_HANDLER + * with a \#define statement or with the compiler option -D. + * + ******************************************************************************/ +void DMA_IRQHandler(void) +{ + int channel; + DMA_CB_TypeDef *cb; + uint32_t pending; + uint32_t pendingPrio; + uint32_t prio; + uint32_t primaryCpy; + int i; + + /* Get all pending and enabled interrupts */ + pending = DMA->IF; + pending &= DMA->IEN; + + /* Check for bus error */ + if (pending & DMA_IF_ERR) + { + /* Loop here to enable the debugger to see what has happened */ + while (1) + ; + } + + /* Process all pending channel interrupts. First process channels */ + /* defined with high priority, then those with default priority. */ + prio = DMA->CHPRIS; + pendingPrio = pending & prio; + for (i = 0; i < 2; i++) + { + channel = 0; + /* Process pending interrupts within high/default priority group */ + /* honouring priority within group. */ + while (pendingPrio) + { + if (pendingPrio & 1) + { + DMA_DESCRIPTOR_TypeDef *descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE); + uint32_t chmask = 1 << channel; + + /* Clear pending interrupt prior to invoking callback, in case it */ + /* sets up another DMA cycle. */ + DMA->IFC = chmask; + + /* Normally, no point in enabling interrupt without callback, but */ + /* check if callback is defined anyway. Callback info is always */ + /* located in primary descriptor. */ + cb = (DMA_CB_TypeDef *)(descr[channel].USER); + if (cb) + { + /* Toggle next-descriptor indicator always prior to invoking */ + /* callback (in case callback reconfigurs something) */ + primaryCpy = cb->primary; + cb->primary ^= 1; + if (cb->cbFunc) + { + cb->cbFunc(channel, (bool)primaryCpy, cb->userPtr); + } + } + } + + pendingPrio >>= 1; + channel++; + } + + /* On second iteration, process default priority channels */ + pendingPrio = pending & ~prio; + } +} + +#endif /* EXCLUDE_DEFAULT_DMA_IRQ_HANDLER */ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Activate DMA auto-request cycle (used for memory-memory transfers). + * + * @details + * Prior to activating the DMA cycle, the channel and descriptor to be used + * must have been properly configured. + * + * @note + * If using this function on a channel already activated and in use by the + * DMA controller, the behaviour is undefined. + * + * @param[in] channel + * DMA channel to activate DMA cycle for. + * + * @param[in] primary + * @li true - activate using primary descriptor + * @li false - activate using alternate descriptor + * + * @param[in] dst + * Address to start location to transfer data to. If NULL, leave setting in + * descriptor as is from a previous activation. + * + * @param[in] src + * Address to start location to transfer data from. If NULL, leave setting in + * descriptor as is from a previous activation. + * + * @param[in] nMinus1 + * Number of DMA transfer elements (minus 1) to transfer (<= 1023). The + * size of the DMA transfer element (1, 2 or 4 bytes) is configured with + * DMA_CfgDescr(). + ******************************************************************************/ +void DMA_ActivateAuto(unsigned int channel, + bool primary, + void *dst, + void *src, + unsigned int nMinus1) +{ + uint32_t chBit; + + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT)); + + DMA_Prepare(channel, + dmaCycleCtrlAuto, + primary, + false, + dst, + src, + nMinus1); + + chBit = 1 << channel; + DMA->CHENS = chBit; /* Enable channel */ + DMA->CHSWREQ = chBit; /* Activate with SW request */ +} + + +/***************************************************************************//** + * @brief + * Activate DMA basic cycle (used for memory-peripheral transfers). + * + * @details + * Prior to activating the DMA cycle, the channel and descriptor to be used + * must have been properly configured. + * + * @note + * If using this function on a channel already activated and in use by the + * DMA controller, the behaviour is undefined. + * + * @param[in] channel + * DMA channel to activate DMA cycle for. + * + * @param[in] primary + * @li true - activate using primary descriptor + * @li false - activate using alternate descriptor + * + * @param[in] useBurst + * The burst feature is only used on peripherals supporting DMA bursts. + * Bursts must not be used if the total length (as given by nMinus1) is + * less than the arbitration rate configured for the descriptor. Please + * refer to the reference manual for further details on burst usage. + * + * @param[in] dst + * Address to start location to transfer data to. If NULL, leave setting in + * descriptor as is from a previous activation. + * + * @param[in] src + * Address to start location to transfer data from. If NULL, leave setting in + * descriptor as is from a previous activation. + * + * @param[in] nMinus1 + * Number of DMA transfer elements (minus 1) to transfer (<= 1023). The + * size of the DMA transfer element (1, 2 or 4 bytes) is configured with + * DMA_CfgDescr(). + ******************************************************************************/ +void DMA_ActivateBasic(unsigned int channel, + bool primary, + bool useBurst, + void *dst, + void *src, + unsigned int nMinus1) +{ + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT)); + + DMA_Prepare(channel, + dmaCycleCtrlBasic, + primary, + useBurst, + dst, + src, + nMinus1); + + /* Enable channel, request signal is provided by peripheral device */ + DMA->CHENS = 1 << channel; +} + + +/***************************************************************************//** + * @brief + * Activate DMA ping-pong cycle (used for memory-peripheral transfers). + * + * @details + * Prior to activating the DMA cycle, the channel and both descriptors must + * have been properly configured. The primary descriptor is always the first + * descriptor to be used by the DMA controller. + * + * @note + * If using this function on a channel already activated and in use by the + * DMA controller, the behaviour is undefined. + * + * @param[in] channel + * DMA channel to activate DMA cycle for. + * + * @param[in] useBurst + * The burst feature is only used on peripherals supporting DMA bursts. + * Bursts must not be used if the total length (as given by nMinus1) is + * less than the arbitration rate configured for the descriptors. Please + * refer to the reference manual for further details on burst usage. Notice + * that this setting is used for both the primary and alternate descriptors. + * + * @param[in] primDst + * Address to start location to transfer data to, for primary descriptor. + * If NULL, leave setting in descriptor as is from a previous activation. + * + * @param[in] primSrc + * Address to start location to transfer data from, for primary descriptor. + * If NULL, leave setting in descriptor as is from a previous activation. + * + * @param[in] primNMinus1 + * Number of DMA transfer elements (minus 1) to transfer (<= 1023), for + * primary descriptor. The size of the DMA transfer element (1, 2 or 4 bytes) + * is configured with DMA_CfgDescr(). + * + * @param[in] altDst + * Address to start location to transfer data to, for alternate descriptor. + * If NULL, leave setting in descriptor as is from a previous activation. + * + * @param[in] altSrc + * Address to start location to transfer data from, for alternate descriptor. + * If NULL, leave setting in descriptor as is from a previous activation. + * + * @param[in] altNMinus1 + * Number of DMA transfer elements (minus 1) to transfer (<= 1023), for + * alternate descriptor. The size of the DMA transfer element (1, 2 or 4 bytes) + * is configured with DMA_CfgDescr(). + ******************************************************************************/ +void DMA_ActivatePingPong(unsigned int channel, + bool useBurst, + void *primDst, + void *primSrc, + unsigned int primNMinus1, + void *altDst, + void *altSrc, + unsigned int altNMinus1) +{ + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(primNMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT)); + EFM_ASSERT(altNMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT)); + + /* Prepare alternate descriptor first */ + DMA_Prepare(channel, + dmaCycleCtrlPingPong, + false, + useBurst, + altDst, + altSrc, + altNMinus1); + + /* Prepare primary descriptor last in order to start cycle using it */ + DMA_Prepare(channel, + dmaCycleCtrlPingPong, + true, + useBurst, + primDst, + primSrc, + primNMinus1); + + /* Enable channel, request signal is provided by peripheral device */ + DMA->CHENS = 1 << channel; +} + + +/***************************************************************************//** + * @brief + * Activate DMA scatter-gather cycle (used for either memory-peripheral + * or memory-memory transfers). + * + * @details + * Prior to activating the DMA cycle, the array with alternate descriptors + * must have been properly configured. This function can be reused without + * reconfiguring the alternate descriptors, as long as @p count is the same. + * + * @note + * If using this function on a channel already activated and in use by the + * DMA controller, the behaviour is undefined. + * + * @param[in] channel + * DMA channel to activate DMA cycle for. + * + * @param[in] useBurst + * The burst feature is only used on peripherals supporting DMA bursts + * (and thus this parameter is ignored for memory scatter-gather cycles). + * This parameter determines if bursts should be enabled during DMA transfers + * using the alternate descriptors. Bursts must not be used if the total + * length (as given by nMinus1 for the alternate descriptor) is + * less than the arbitration rate configured for the descriptor. Please + * refer to the reference manual for further details on burst usage. + * + * @param[in,out] altDescr + * Pointer to start of array with prepared alternate descriptors. The last + * descriptor will have its cycle control type reprogrammed to basic type. + * + * @param[in] count + * Number of alternate descriptors in @p altDescr array. Maximum number of + * alternate descriptors is 256. + ******************************************************************************/ +void DMA_ActivateScatterGather(unsigned int channel, + bool useBurst, + DMA_DESCRIPTOR_TypeDef *altDescr, + unsigned int count) +{ + DMA_DESCRIPTOR_TypeDef *descr; + DMA_CB_TypeDef *cb; + uint32_t cycleCtrl; + uint32_t chBit; + + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(altDescr); + EFM_ASSERT(count && (count <= 256)); + + /* We have to configure the primary descriptor properly in order to */ + /* transfer one complete alternate descriptor from the alternate */ + /* descriptor table into the actual alternate descriptor. */ + descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE) + channel; + + /* Set source end address to point to alternate descriptor array */ + descr->SRCEND = (uint32_t *)altDescr + (count * 4) - 1; + + /* The destination end address in the primary descriptor MUST point */ + /* to the corresponding alternate descriptor in scatter-gather mode. */ + descr->DSTEND = (uint32_t *)((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE) + + channel + 1) - 1; + + /* The user field of the descriptor is used for callback configuration, */ + /* and already configured when channel is configured. Do not modify it. */ + + /* Determine from alternate configuration whether this is a memory or */ + /* peripheral scatter-gather, by looking at the first alternate descriptor. */ + cycleCtrl = altDescr->CTRL & _DMA_CTRL_CYCLE_CTRL_MASK; + cycleCtrl &= ~(1 << _DMA_CTRL_CYCLE_CTRL_SHIFT); + + EFM_ASSERT((cycleCtrl == dmaCycleCtrlMemScatterGather) + || (cycleCtrl == dmaCycleCtrlPerScatterGather)); + + /* Set last alternate descriptor to basic or auto-request cycle type in */ + /* order to have dma_done signal asserted when complete. Otherwise interrupt */ + /* will not be triggered when done. */ + altDescr[count - 1].CTRL &= ~_DMA_CTRL_CYCLE_CTRL_MASK; + if (cycleCtrl == dmaCycleCtrlMemScatterGather) + { + altDescr[count - 1].CTRL |= (uint32_t)dmaCycleCtrlAuto + << _DMA_CTRL_CYCLE_CTRL_SHIFT; + } + else + { + altDescr[count - 1].CTRL |= (uint32_t)dmaCycleCtrlBasic + << _DMA_CTRL_CYCLE_CTRL_SHIFT; + } + + /* If callback defined, update info on whether callback is issued for */ + /* primary or alternate descriptor. Not really useful for scatter-gather, */ + /* but do for consistency. Always set to alternate, since that is the last */ + /* descriptor actually used. */ + cb = (DMA_CB_TypeDef *)(descr->USER); + if (cb) + { + cb->primary = false; + } + + /* Configure primary descriptor control word */ + descr->CTRL =((uint32_t)dmaDataInc4 << _DMA_CTRL_DST_INC_SHIFT) + | ((uint32_t)dmaDataSize4 << _DMA_CTRL_DST_SIZE_SHIFT) + | ((uint32_t)dmaDataInc4 << _DMA_CTRL_SRC_INC_SHIFT) + | ((uint32_t)dmaDataSize4 << _DMA_CTRL_SRC_SIZE_SHIFT) + /* Use same protection scheme as for alternate descriptors */ + | (altDescr->CTRL & _DMA_CTRL_SRC_PROT_CTRL_MASK) + | ((uint32_t)dmaArbitrate4 << _DMA_CTRL_R_POWER_SHIFT) + | (((count * 4) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT) + | (((uint32_t)useBurst & 1) << _DMA_CTRL_NEXT_USEBURST_SHIFT) + | cycleCtrl; + + chBit = 1 << channel; + + /* Start with primary descriptor */ + DMA->CHALTC = chBit; + + /* Enable channel */ + DMA->CHENS = chBit; + + /* Send request if memory scatter-gather, otherwise request signal is */ + /* provided by peripheral. */ + if (cycleCtrl == dmaCycleCtrlMemScatterGather) + { + DMA->CHSWREQ = chBit; + } +} + + +/***************************************************************************//** + * @brief + * Configure a DMA channel. + * + * @details + * Configure miscellaneous issues for a DMA channel. This function is typically + * used once to setup a channel for a certain type of use. + * + * @note + * If using this function on a channel already in use by the DMA controller, + * the behaviour is undefined. + * + * @param[in] channel + * DMA channel to configure. + * + * @param[in] cfg + * Configuration to use. + ******************************************************************************/ +void DMA_CfgChannel(unsigned int channel, DMA_CfgChannel_TypeDef *cfg) +{ + DMA_DESCRIPTOR_TypeDef *descr; + + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(cfg); + + /* Always keep callback configuration reference in primary descriptor */ + descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE); + descr[channel].USER = (uint32_t)(cfg->cb); + + /* Set to specified priority for channel */ + if (cfg->highPri) + { + DMA->CHPRIS = 1 << channel; + } + else + { + DMA->CHPRIC = 1 << channel; + } + + /* Set DMA signal source select */ + DMA->CH[channel].CTRL = cfg->select; + + /* Enable/disable interrupt as specified */ + if (cfg->enableInt) + { + DMA->IFC = (1 << channel); + BUS_RegBitWrite(&(DMA->IEN), channel, 1); + } + else + { + BUS_RegBitWrite(&(DMA->IEN), channel, 0); + } +} + + +/***************************************************************************//** + * @brief + * Configure DMA descriptor for auto-request, basic or ping-pong DMA cycles. + * + * @details + * This function is used for configuration of a descriptor for the following + * DMA cycle types: + * + * @li auto-request - used for memory/memory transfer + * @li basic - used for a peripheral/memory transfer + * @li ping-pong - used for a ping-pong based peripheral/memory transfer + * style providing time to refresh one descriptor while the other is + * in use. + * + * The DMA cycle is not activated, please see DMA_ActivateAuto(), + * DMA_ActivateBasic() or DMA_ActivatePingPong() to activate the DMA cycle. + * In many cases, the configuration only has to be done once, and all + * subsequent cycles may be activated with the activate function. + * + * For ping-pong DMA cycles, this function must be used both on the primary + * and the alternate descriptor prior to activating the DMA cycle. + * + * Notice that the DMA channel must also be configured, see DMA_CfgChannel(). + * + * @note + * If using this function on a descriptor already activated and in use by + * the DMA controller, the behaviour is undefined. + * + * @param[in] channel + * DMA channel to configure for. + * + * @param[in] primary + * @li true - configure primary descriptor + * @li false - configure alternate descriptor + * + * @param[in] cfg + * Configuration to use. + ******************************************************************************/ +void DMA_CfgDescr(unsigned int channel, + bool primary, + DMA_CfgDescr_TypeDef *cfg) +{ + DMA_DESCRIPTOR_TypeDef *descr; + + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(cfg); + + /* Find descriptor to configure */ + if (primary) + { + descr = (DMA_DESCRIPTOR_TypeDef *)DMA->CTRLBASE; + } + else + { + descr = (DMA_DESCRIPTOR_TypeDef *)DMA->ALTCTRLBASE; + } + descr += channel; + + /* Prepare the descriptor */ + /* Source/destination end addresses set when started */ + descr->CTRL = (cfg->dstInc << _DMA_CTRL_DST_INC_SHIFT) + | (cfg->size << _DMA_CTRL_DST_SIZE_SHIFT) + | (cfg->srcInc << _DMA_CTRL_SRC_INC_SHIFT) + | (cfg->size << _DMA_CTRL_SRC_SIZE_SHIFT) + | ((uint32_t)(cfg->hprot) << _DMA_CTRL_SRC_PROT_CTRL_SHIFT) + | (cfg->arbRate << _DMA_CTRL_R_POWER_SHIFT) + | (0 << _DMA_CTRL_N_MINUS_1_SHIFT) /* Set when activated */ + | (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) /* Set when activated */ + | DMA_CTRL_CYCLE_CTRL_INVALID; /* Set when activated */ +} + + +#if defined( _DMA_LOOP0_MASK ) && defined( _DMA_LOOP1_MASK ) +/***************************************************************************//** + * @brief Configure DMA channel for Loop mode or 2D transfer. + * + * @details + * For 2D transfer, set cfg->enable to "false", and only configure nMinus1 + * to same width as channel descriptor. + * + * @param[in] channel + * DMA channel to configure for. + * + * @param[in] cfg + * Configuration to use. + ******************************************************************************/ +void DMA_CfgLoop(unsigned int channel, DMA_CfgLoop_TypeDef *cfg) +{ + EFM_ASSERT(channel <= 1); + EFM_ASSERT(cfg->nMinus1 <= 1023); + + /* Configure LOOP setting */ + switch( channel ) + { + case 0: + DMA->LOOP0 = (cfg->enable << _DMA_LOOP0_EN_SHIFT) + | (cfg->nMinus1 << _DMA_LOOP0_WIDTH_SHIFT); + break; + case 1: + DMA->LOOP1 = (cfg->enable << _DMA_LOOP1_EN_SHIFT) + | (cfg->nMinus1 << _DMA_LOOP1_WIDTH_SHIFT); + break; + } +} +#endif + + +#if defined( _DMA_RECT0_MASK ) +/***************************************************************************//** + * @brief Configure DMA channel 2D transfer properties. + * + * @param[in] channel + * DMA channel to configure for. + * + * @param[in] cfg + * Configuration to use. + ******************************************************************************/ +void DMA_CfgRect(unsigned int channel, DMA_CfgRect_TypeDef *cfg) +{ + (void)channel; /* Unused parameter */ + + EFM_ASSERT(channel == 0); + EFM_ASSERT(cfg->dstStride <= 2047); + EFM_ASSERT(cfg->srcStride <= 2047); + EFM_ASSERT(cfg->height <= 1023); + + /* Configure rectangular/2D copy */ + DMA->RECT0 = (cfg->dstStride << _DMA_RECT0_DSTSTRIDE_SHIFT) + | (cfg->srcStride << _DMA_RECT0_SRCSTRIDE_SHIFT) + | (cfg->height << _DMA_RECT0_HEIGHT_SHIFT); +} +#endif + + +/***************************************************************************//** + * @brief + * Configure an alternate DMA descriptor for use with scatter-gather DMA + * cycles. + * + * @details + * In scatter-gather mode, the alternate descriptors are located in one + * contiguous memory area. Each of the alternate descriptor must be fully + * configured prior to starting the scatter-gather DMA cycle. + * + * The DMA cycle is not activated by this function, please see + * DMA_ActivateScatterGather() to activate the DMA cycle. In some cases, the + * alternate configuration only has to be done once, and all subsequent + * transfers may be activated with the activate function. + * + * Notice that the DMA channel must also be configured, see DMA_CfgChannel(). + * + * @param[in] descr + * Points to start of memory area holding the alternate descriptors. + * + * @param[in] indx + * Alternate descriptor index number to configure (numbered from 0). + * + * @param[in] cfg + * Configuration to use. + ******************************************************************************/ +void DMA_CfgDescrScatterGather(DMA_DESCRIPTOR_TypeDef *descr, + unsigned int indx, + DMA_CfgDescrSGAlt_TypeDef *cfg) +{ + uint32_t cycleCtrl; + + EFM_ASSERT(descr); + EFM_ASSERT(cfg); + + /* Point to selected entry in alternate descriptor table */ + descr += indx; + + if (cfg->srcInc == dmaDataIncNone) + { + descr->SRCEND = cfg->src; + } + else + { + descr->SRCEND = (void *)((uint32_t)(cfg->src) + + ((uint32_t)(cfg->nMinus1) << cfg->srcInc)); + } + + if (cfg->dstInc == dmaDataIncNone) + { + descr->DSTEND = cfg->dst; + } + else + { + descr->DSTEND = (void *)((uint32_t)(cfg->dst) + + ((uint32_t)(cfg->nMinus1) << cfg->dstInc)); + } + + /* User definable part not used */ + descr->USER = 0; + + if (cfg->peripheral) + { + cycleCtrl = (uint32_t)dmaCycleCtrlPerScatterGather + 1; + } + else + { + cycleCtrl = (uint32_t)dmaCycleCtrlMemScatterGather + 1; + } + + descr->CTRL =(cfg->dstInc << _DMA_CTRL_DST_INC_SHIFT) + | (cfg->size << _DMA_CTRL_DST_SIZE_SHIFT) + | (cfg->srcInc << _DMA_CTRL_SRC_INC_SHIFT) + | (cfg->size << _DMA_CTRL_SRC_SIZE_SHIFT) + | ((uint32_t)(cfg->hprot) << _DMA_CTRL_SRC_PROT_CTRL_SHIFT) + | (cfg->arbRate << _DMA_CTRL_R_POWER_SHIFT) + | ((uint32_t)(cfg->nMinus1) << _DMA_CTRL_N_MINUS_1_SHIFT) + /* Never set next useburst bit, since the descriptor used after the */ + /* alternate descriptor is the primary descriptor which operates on */ + /* memory. If the alternate descriptors need to have useBurst set, this */ + /* done when setting up the primary descriptor, ie when activating. */ + | (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) + | (cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Enable or disable a DMA channel. + * + * @details + * Use this function to explicitly enable or disable a DMA channel. A DMA + * channel is automatically disabled when the DMA controller has finished a + * transaction. + * + * @param[in] channel + * DMA channel to enable or disable. + * + * @param[in] enable + * If 'true' the channel will be enabled. If 'false' the channel will be + * disabled. + ******************************************************************************/ +void DMA_ChannelEnable(unsigned int channel, bool enable) +{ + EFM_ASSERT(channel < DMA_CHAN_COUNT); + + if (enable) + { + DMA->CHENS = 1<CHENC = 1<CHENS >> channel) & 1); +} + + +/***************************************************************************//** + * @brief + * Initializes DMA controller. + * + * @details + * This function will reset and prepare the DMA controller for use. Although + * it may be used several times, it is normally only used during system + * init. If reused during normal operation, notice that any ongoing DMA + * transfers will be aborted. When completed, the DMA controller is in + * an enabled state. + * + * @note + * Must be invoked before using the DMA controller. + * + * @param[in] init + * Pointer to a structure containing DMA init information. + ******************************************************************************/ +void DMA_Init(DMA_Init_TypeDef *init) +{ + EFM_ASSERT(init); + + /* Make sure control block is properly aligned */ +#if (DMA_CHAN_COUNT <= 4) + EFM_ASSERT(!((uint32_t)(init->controlBlock) & (128 - 1))); +#elif (DMA_CHAN_COUNT <= 8) || (DMA_CHAN_COUNT <= 12) + EFM_ASSERT(!((uint32_t)(init->controlBlock) & (256 - 1))); +#else +#error "Unsupported DMA channel count (em_dma.c)." +#endif + + /* Make sure DMA clock is enabled prior to accessing DMA module */ + CMU_ClockEnable(cmuClock_DMA, true); + + /* Make sure DMA controller is set to a known reset state */ + DMA_Reset(); + + /* Clear/enable DMA interrupts */ + NVIC_ClearPendingIRQ(DMA_IRQn); + NVIC_EnableIRQ(DMA_IRQn); + + /* Enable bus error interrupt */ + DMA->IEN = DMA_IEN_ERR; + + /* Set pointer to control block, notice that this ptr must have been */ + /* properly aligned, according to requirements defined in the reference */ + /* manual. */ + DMA->CTRLBASE = (uint32_t)(init->controlBlock); + + /* Configure and enable the DMA controller */ + DMA->CONFIG = ((uint32_t)(init->hprot) << _DMA_CONFIG_CHPROT_SHIFT) + | DMA_CONFIG_EN; +} + + +/***************************************************************************//** + * @brief + * Refresh a descriptor used in a DMA ping-pong cycle. + * + * @details + * During a ping-pong DMA cycle, the DMA controller automatically alternates + * between primary and alternate descriptors, when completing use of a + * descriptor. While the other descriptor is in use by the DMA controller, + * the SW should refresh the completed descriptor. This is typically done from + * the callback defined for the ping-pong cycle. + * + * @param[in] channel + * DMA channel to refresh ping-pong descriptor for. + * + * @param[in] primary + * @li true - refresh primary descriptor + * @li false - refresh alternate descriptor + * + * @param[in] useBurst + * The burst feature is only used on peripherals supporting DMA bursts. + * Bursts must not be used if the total length (as given by nMinus1) is + * less than the arbitration rate configured for the descriptor. Please + * refer to the reference manual for further details on burst usage. + * + * @param[in] dst + * Address to start location to transfer data to. If NULL, leave setting in + * descriptor as is. + * + * @param[in] src + * Address to start location to transfer data from. If NULL, leave setting in + * descriptor as is. + * + * @param[in] nMinus1 + * Number of DMA transfer elements (minus 1) to transfer (<= 1023). The + * size of the DMA transfer element (1, 2 or 4 bytes) is configured with + * DMA_CfgDescr(). + * + * @param[in] stop + * Indicate that the DMA ping-pong cycle shall stop @b after completing use + * of this descriptor. + ******************************************************************************/ +void DMA_RefreshPingPong(unsigned int channel, + bool primary, + bool useBurst, + void *dst, + void *src, + unsigned int nMinus1, + bool stop) +{ + DMA_CycleCtrl_TypeDef cycleCtrl; + DMA_DESCRIPTOR_TypeDef *descr; + uint32_t inc; + uint32_t chBit; + uint32_t tmp; + + EFM_ASSERT(channel < DMA_CHAN_COUNT); + EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT)); + + /* The ping-pong DMA cycle may be stopped by issuing a basic cycle type */ + if (stop) + { + cycleCtrl = dmaCycleCtrlBasic; + } + else + { + cycleCtrl = dmaCycleCtrlPingPong; + } + + /* Find descriptor to configure */ + if (primary) + { + descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE)) + channel; + } + else + { + descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE)) + channel; + } + + if (src) + { + inc = (descr->CTRL & _DMA_CTRL_SRC_INC_MASK) >> _DMA_CTRL_SRC_INC_SHIFT; + if (inc == _DMA_CTRL_SRC_INC_NONE) + { + descr->SRCEND = src; + } + else + { + descr->SRCEND = (void *)((uint32_t)src + (nMinus1 << inc)); + } + } + + if (dst) + { + inc = (descr->CTRL & _DMA_CTRL_DST_INC_MASK) >> _DMA_CTRL_DST_INC_SHIFT; + if (inc == _DMA_CTRL_DST_INC_NONE) + { + descr->DSTEND = dst; + } + else + { + descr->DSTEND = (void *)((uint32_t)dst + (nMinus1 << inc)); + } + } + + chBit = 1 << channel; + if (useBurst) + { + DMA->CHUSEBURSTS = chBit; + } + else + { + DMA->CHUSEBURSTC = chBit; + } + + /* Set cycle control */ + tmp = descr->CTRL & ~(_DMA_CTRL_CYCLE_CTRL_MASK | _DMA_CTRL_N_MINUS_1_MASK); + tmp |= nMinus1 << _DMA_CTRL_N_MINUS_1_SHIFT; + tmp |= cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT; + descr->CTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Reset the DMA controller. + * + * @details + * This functions will disable the DMA controller and set it to a reset + * state. + * + * @note + * Notice that any ongoing transfers will be aborted. + ******************************************************************************/ +void DMA_Reset(void) +{ + int i; + + /* Disable DMA interrupts */ + NVIC_DisableIRQ(DMA_IRQn); + + /* Put the DMA controller into a known state, first disabling it. */ + DMA->CONFIG = _DMA_CONFIG_RESETVALUE; + DMA->CHUSEBURSTC = _DMA_CHUSEBURSTC_MASK; + DMA->CHREQMASKC = _DMA_CHREQMASKC_MASK; + DMA->CHENC = _DMA_CHENC_MASK; + DMA->CHALTC = _DMA_CHALTC_MASK; + DMA->CHPRIC = _DMA_CHPRIC_MASK; + DMA->ERRORC = DMA_ERRORC_ERRORC; + DMA->IEN = _DMA_IEN_RESETVALUE; + DMA->IFC = _DMA_IFC_MASK; + + /* Clear channel control flags */ + for (i = 0; i < DMA_CHAN_COUNT; i++) + { + DMA->CH[i].CTRL = _DMA_CH_CTRL_RESETVALUE; + } +} + + +/** @} (end addtogroup DMA) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined( DMA_PRESENT ) */ diff --git a/cpu/efm32_common/emlib/src/em_ebi.c b/cpu/efm32_common/emlib/src/em_ebi.c new file mode 100644 index 0000000000000..f034633e33e1b --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_ebi.c @@ -0,0 +1,1187 @@ +/***************************************************************************//** + * @file em_ebi.c + * @brief External Bus Interface (EBI) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_ebi.h" +#if defined(EBI_COUNT) && (EBI_COUNT > 0) +#include "em_assert.h" +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup EBI + * @brief EBI External Bus Interface (EBI) Peripheral API + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Configure and enable External Bus Interface + * + * @param[in] ebiInit + * EBI configuration structure + * + * @note + * GPIO lines must be configured as PUSH_PULL for correct operation + * GPIO and EBI clocks must be enabled in the CMU + ******************************************************************************/ +void EBI_Init(const EBI_Init_TypeDef *ebiInit) +{ + uint32_t ctrl = EBI->CTRL; + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /* Enable Independent Timing for devices that supports it */ + ctrl |= EBI_CTRL_ITS; + + /* Set polarity of address ready */ + EBI_BankPolaritySet(ebiInit->banks, ebiLineARDY, ebiInit->ardyPolarity); + /* Set polarity of address latch enable */ + EBI_BankPolaritySet(ebiInit->banks, ebiLineALE, ebiInit->alePolarity); + /* Set polarity of write enable */ + EBI_BankPolaritySet(ebiInit->banks, ebiLineWE, ebiInit->wePolarity); + /* Set polarity of read enable */ + EBI_BankPolaritySet(ebiInit->banks, ebiLineRE, ebiInit->rePolarity); + /* Set polarity of chip select lines */ + EBI_BankPolaritySet(ebiInit->banks, ebiLineCS, ebiInit->csPolarity); + /* Set polarity of byte lane line */ + EBI_BankPolaritySet(ebiInit->banks, ebiLineBL, ebiInit->blPolarity); +#else + /* Set polarity of address ready */ + EBI_PolaritySet(ebiLineARDY, ebiInit->ardyPolarity); + /* Set polarity of address latch enable */ + EBI_PolaritySet(ebiLineALE, ebiInit->alePolarity); + /* Set polarity of write enable */ + EBI_PolaritySet(ebiLineWE, ebiInit->wePolarity); + /* Set polarity of read enable */ + EBI_PolaritySet(ebiLineRE, ebiInit->rePolarity); + /* Set polarity of chip select lines */ + EBI_PolaritySet(ebiLineCS, ebiInit->csPolarity); +#endif + + /* Configure EBI mode and control settings */ +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + if (ebiInit->banks & EBI_BANK0) + { + ctrl &= ~(_EBI_CTRL_MODE_MASK + | _EBI_CTRL_ARDYEN_MASK + | _EBI_CTRL_ARDYTODIS_MASK + | _EBI_CTRL_BL_MASK + | _EBI_CTRL_NOIDLE_MASK + | _EBI_CTRL_BANK0EN_MASK); + ctrl |= (ebiInit->mode << _EBI_CTRL_MODE_SHIFT); + ctrl |= (ebiInit->ardyEnable << _EBI_CTRL_ARDYEN_SHIFT); + ctrl |= (ebiInit->ardyDisableTimeout << _EBI_CTRL_ARDYTODIS_SHIFT); + ctrl |= (ebiInit->blEnable << _EBI_CTRL_BL_SHIFT); + ctrl |= (ebiInit->noIdle << _EBI_CTRL_NOIDLE_SHIFT); + if ( ebiInit->enable) + { + ctrl |= EBI_CTRL_BANK0EN; + } + } + if (ebiInit->banks & EBI_BANK1) + { + ctrl &= ~(_EBI_CTRL_BL1_MASK + | _EBI_CTRL_MODE1_MASK + | _EBI_CTRL_ARDY1EN_MASK + | _EBI_CTRL_ARDYTO1DIS_MASK + | _EBI_CTRL_NOIDLE1_MASK + | _EBI_CTRL_BANK1EN_MASK); + ctrl |= (ebiInit->mode << _EBI_CTRL_MODE1_SHIFT); + ctrl |= (ebiInit->ardyEnable << _EBI_CTRL_ARDY1EN_SHIFT); + ctrl |= (ebiInit->ardyDisableTimeout << _EBI_CTRL_ARDYTO1DIS_SHIFT); + ctrl |= (ebiInit->blEnable << _EBI_CTRL_BL1_SHIFT); + ctrl |= (ebiInit->noIdle << _EBI_CTRL_NOIDLE1_SHIFT); + if ( ebiInit->enable) + { + ctrl |= EBI_CTRL_BANK1EN; + } + } + if (ebiInit->banks & EBI_BANK2) + { + ctrl &= ~(_EBI_CTRL_BL2_MASK + | _EBI_CTRL_MODE2_MASK + | _EBI_CTRL_ARDY2EN_MASK + | _EBI_CTRL_ARDYTO2DIS_MASK + | _EBI_CTRL_NOIDLE2_MASK + | _EBI_CTRL_BANK2EN_MASK); + ctrl |= (ebiInit->mode << _EBI_CTRL_MODE2_SHIFT); + ctrl |= (ebiInit->ardyEnable << _EBI_CTRL_ARDY2EN_SHIFT); + ctrl |= (ebiInit->ardyDisableTimeout << _EBI_CTRL_ARDYTO2DIS_SHIFT); + ctrl |= (ebiInit->blEnable << _EBI_CTRL_BL2_SHIFT); + ctrl |= (ebiInit->noIdle << _EBI_CTRL_NOIDLE2_SHIFT); + if ( ebiInit->enable) + { + ctrl |= EBI_CTRL_BANK2EN; + } + } + if (ebiInit->banks & EBI_BANK3) + { + ctrl &= ~(_EBI_CTRL_BL3_MASK + | _EBI_CTRL_MODE3_MASK + | _EBI_CTRL_ARDY3EN_MASK + | _EBI_CTRL_ARDYTO3DIS_MASK + | _EBI_CTRL_NOIDLE3_MASK + | _EBI_CTRL_BANK3EN_MASK); + ctrl |= (ebiInit->mode << _EBI_CTRL_MODE3_SHIFT); + ctrl |= (ebiInit->ardyEnable << _EBI_CTRL_ARDY3EN_SHIFT); + ctrl |= (ebiInit->ardyDisableTimeout << _EBI_CTRL_ARDYTO3DIS_SHIFT); + ctrl |= (ebiInit->blEnable << _EBI_CTRL_BL3_SHIFT); + ctrl |= (ebiInit->noIdle << _EBI_CTRL_NOIDLE3_SHIFT); + if ( ebiInit->enable) + { + ctrl |= EBI_CTRL_BANK3EN; + } + } +#else + ctrl &= ~(_EBI_CTRL_MODE_MASK + | _EBI_CTRL_ARDYEN_MASK + | _EBI_CTRL_ARDYTODIS_MASK + | _EBI_CTRL_BANK0EN_MASK + | _EBI_CTRL_BANK1EN_MASK + | _EBI_CTRL_BANK2EN_MASK + | _EBI_CTRL_BANK3EN_MASK); + if ( ebiInit->enable) + { + if ( ebiInit->banks & EBI_BANK0 ) + { + ctrl |= EBI_CTRL_BANK0EN; + } + if ( ebiInit->banks & EBI_BANK1 ) + { + ctrl |= EBI_CTRL_BANK1EN; + } + if ( ebiInit->banks & EBI_BANK2 ) + { + ctrl |= EBI_CTRL_BANK2EN; + } + if ( ebiInit->banks & EBI_BANK3 ) + { + ctrl |= EBI_CTRL_BANK3EN; + } + } + ctrl |= ebiInit->mode; + ctrl |= (ebiInit->ardyEnable << _EBI_CTRL_ARDYEN_SHIFT); + ctrl |= (ebiInit->ardyDisableTimeout << _EBI_CTRL_ARDYTODIS_SHIFT); +#endif + + /* Configure timing */ +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + EBI_BankReadTimingSet(ebiInit->banks, + ebiInit->readSetupCycles, + ebiInit->readStrobeCycles, + ebiInit->readHoldCycles); + EBI_BankReadTimingConfig(ebiInit->banks, + ebiInit->readPageMode, + ebiInit->readPrefetch, + ebiInit->readHalfRE); + EBI_BankWriteTimingSet(ebiInit->banks, + ebiInit->writeSetupCycles, + ebiInit->writeStrobeCycles, + ebiInit->writeHoldCycles); + EBI_BankWriteTimingConfig(ebiInit->banks, + ebiInit->writeBufferDisable, + ebiInit->writeHalfWE); + EBI_BankAddressTimingSet(ebiInit->banks, + ebiInit->addrSetupCycles, + ebiInit->addrHoldCycles); + EBI_BankAddressTimingConfig(ebiInit->banks, + ebiInit->addrHalfALE); +#else + EBI_ReadTimingSet(ebiInit->readSetupCycles, + ebiInit->readStrobeCycles, + ebiInit->readHoldCycles); + EBI_WriteTimingSet(ebiInit->writeSetupCycles, + ebiInit->writeStrobeCycles, + ebiInit->writeHoldCycles); + EBI_AddressTimingSet(ebiInit->addrSetupCycles, + ebiInit->addrHoldCycles); +#endif + + /* Activate new configuration */ + EBI->CTRL = ctrl; + + /* Configure Adress Latch Enable */ + switch (ebiInit->mode) + { + case ebiModeD16A16ALE: + case ebiModeD8A24ALE: + /* Address Latch Enable */ + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_ALEPEN_SHIFT, 1); + break; +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + case ebiModeD16: +#endif + case ebiModeD8A8: + /* Make sure Address Latch is disabled */ + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_ALEPEN_SHIFT, 0); + break; + } +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /* Limit pin enable */ + EBI->ROUTE = (EBI->ROUTE & ~_EBI_ROUTE_ALB_MASK) | ebiInit->aLow; + EBI->ROUTE = (EBI->ROUTE & ~_EBI_ROUTE_APEN_MASK) | ebiInit->aHigh; + /* Location */ + EBI->ROUTE = (EBI->ROUTE & ~_EBI_ROUTE_LOCATION_MASK) | ebiInit->location; + + /* Enable EBI BL pin if necessary */ + if(ctrl & (_EBI_CTRL_BL_MASK|_EBI_CTRL_BL1_MASK|_EBI_CTRL_BL2_MASK|_EBI_CTRL_BL3_MASK)) + { + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_BLPEN_SHIFT, ebiInit->blEnable); + } +#endif + /* Enable EBI pins EBI_WEn and EBI_REn */ + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_EBIPEN_SHIFT, 1); + + /* Enable chip select lines */ + EBI_ChipSelectEnable(ebiInit->csLines, true); +} + + +/***************************************************************************//** + * @brief + * Disable External Bus Interface + ******************************************************************************/ +void EBI_Disable(void) +{ + /* Disable pins */ + EBI->ROUTE = _EBI_ROUTE_RESETVALUE; + /* Disable banks */ + EBI->CTRL = _EBI_CTRL_RESETVALUE; +} + + +/***************************************************************************//** + * @brief + * Enable or disable EBI Bank + * + * @param[in] banks + * Banks to reconfigure, mask of EBI_BANK flags + * + * @param[in] enable + * True to enable, false to disable + ******************************************************************************/ +void EBI_BankEnable(uint32_t banks, bool enable) +{ + if (banks & EBI_BANK0) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BANK0EN_SHIFT, enable); + } + if (banks & EBI_BANK1) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BANK1EN_SHIFT, enable); + } + if (banks & EBI_BANK2) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BANK2EN_SHIFT, enable); + } + if (banks & EBI_BANK3) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BANK3EN_SHIFT, enable); + } +} + + +/***************************************************************************//** + * @brief + * Return base address of EBI bank + * + * @param[in] bank + * Bank to return start address for + * + * @return + * Absolute address of bank + ******************************************************************************/ +uint32_t EBI_BankAddress(uint32_t bank) +{ +#if defined (_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + if(EBI->CTRL & EBI_CTRL_ALTMAP) + { + switch (bank) + { + case EBI_BANK0: + return(EBI_MEM_BASE); + + case EBI_BANK1: + return(EBI_MEM_BASE + 0x10000000UL); + + case EBI_BANK2: + return(EBI_MEM_BASE + 0x20000000UL); + + case EBI_BANK3: + return(EBI_MEM_BASE + 0x30000000UL); + + default: + EFM_ASSERT(0); + break; + } + } +#endif + switch (bank) + { + case EBI_BANK0: + return(EBI_MEM_BASE); + + case EBI_BANK1: + return(EBI_MEM_BASE + 0x04000000UL); + + case EBI_BANK2: + return(EBI_MEM_BASE + 0x08000000UL); + + case EBI_BANK3: + return(EBI_MEM_BASE + 0x0C000000UL); + + default: + EFM_ASSERT(0); + break; + } + return 0; +} + + +/***************************************************************************//** + * @brief + * Enable or disable EBI Chip Select + * + * @param[in] cs + * ChipSelect lines to reconfigure, mask of EBI_CS flags + * + * @param[in] enable + * True to enable, false to disable + ******************************************************************************/ +void EBI_ChipSelectEnable(uint32_t cs, bool enable) +{ + if (cs & EBI_CS0) + { + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_CS0PEN_SHIFT, enable); + } + if (cs & EBI_CS1) + { + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_CS1PEN_SHIFT, enable); + } + if (cs & EBI_CS2) + { + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_CS2PEN_SHIFT, enable); + } + if (cs & EBI_CS3) + { + BUS_RegBitWrite(&(EBI->ROUTE), _EBI_ROUTE_CS3PEN_SHIFT, enable); + } +} + + +/***************************************************************************//** + * @brief + * Configure EBI pin polarity + * + * @param[in] line + * Which pin/line to configure + * + * @param[in] polarity + * Active high, or active low + ******************************************************************************/ +void EBI_PolaritySet(EBI_Line_TypeDef line, EBI_Polarity_TypeDef polarity) +{ + switch (line) + { + case ebiLineARDY: + BUS_RegBitWrite(&(EBI->POLARITY), _EBI_POLARITY_ARDYPOL_SHIFT, polarity); + break; + case ebiLineALE: + BUS_RegBitWrite(&(EBI->POLARITY), _EBI_POLARITY_ALEPOL_SHIFT, polarity); + break; + case ebiLineWE: + BUS_RegBitWrite(&(EBI->POLARITY), _EBI_POLARITY_WEPOL_SHIFT, polarity); + break; + case ebiLineRE: + BUS_RegBitWrite(&(EBI->POLARITY), _EBI_POLARITY_REPOL_SHIFT, polarity); + break; + case ebiLineCS: + BUS_RegBitWrite(&(EBI->POLARITY), _EBI_POLARITY_CSPOL_SHIFT, polarity); + break; +#if defined (_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + case ebiLineBL: + BUS_RegBitWrite(&(EBI->POLARITY), _EBI_POLARITY_BLPOL_SHIFT, polarity); + break; + case ebiLineTFTVSync: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_VSYNCPOL_SHIFT, polarity); + break; + case ebiLineTFTHSync: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_HSYNCPOL_SHIFT, polarity); + break; + case ebiLineTFTDataEn: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_DATAENPOL_SHIFT, polarity); + break; + case ebiLineTFTDClk: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_DCLKPOL_SHIFT, polarity); + break; + case ebiLineTFTCS: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_CSPOL_SHIFT, polarity); + break; +#endif + default: + EFM_ASSERT(0); + break; + } +} + + +/***************************************************************************//** + * @brief + * Configure timing values of read bus accesses + * + * @param[in] setupCycles + * Number of clock cycles for address setup before REn is asserted + * + * @param[in] strobeCycles + * The number of cycles the REn is held active. After the specified number of + * cycles, data is read. If set to 0, 1 cycle is inserted by HW + * + * @param[in] holdCycles + * The number of cycles CSn is held active after the REn is dessarted + ******************************************************************************/ +void EBI_ReadTimingSet(int setupCycles, int strobeCycles, int holdCycles) +{ + uint32_t readTiming; + + /* Check that timings are within limits */ + EFM_ASSERT(setupCycles < 4); + EFM_ASSERT(strobeCycles < 16); + EFM_ASSERT(holdCycles < 4); + + /* Configure timing values */ + readTiming = (setupCycles << _EBI_RDTIMING_RDSETUP_SHIFT) + | (strobeCycles << _EBI_RDTIMING_RDSTRB_SHIFT) + | (holdCycles << _EBI_RDTIMING_RDHOLD_SHIFT); + + + EBI->RDTIMING = (EBI->RDTIMING + & ~(_EBI_RDTIMING_RDSETUP_MASK + | _EBI_RDTIMING_RDSTRB_MASK + | _EBI_RDTIMING_RDHOLD_MASK)) + | readTiming; +} + + +/***************************************************************************//** + * @brief + * Configure timing values of write bus accesses + * + * @param[in] setupCycles + * Number of clock cycles for address setup before WEn is asserted + * + * @param[in] strobeCycles + * Number of cycles WEn is held active. If set to 0, 1 cycle is inserted by HW + * + * @param[in] holdCycles + * Number of cycles CSn is held active after the WEn is deasserted + ******************************************************************************/ +void EBI_WriteTimingSet(int setupCycles, int strobeCycles, int holdCycles) +{ + uint32_t writeTiming; + + /* Check that timings are within limits */ + EFM_ASSERT(setupCycles < 4); + EFM_ASSERT(strobeCycles < 16); + EFM_ASSERT(holdCycles < 4); + + /* Configure timing values */ + writeTiming = (setupCycles << _EBI_WRTIMING_WRSETUP_SHIFT) + | (strobeCycles << _EBI_WRTIMING_WRSTRB_SHIFT) + | (holdCycles << _EBI_WRTIMING_WRHOLD_SHIFT); + + EBI->WRTIMING = (EBI->WRTIMING + & ~(_EBI_WRTIMING_WRSETUP_MASK + | _EBI_WRTIMING_WRSTRB_MASK + | _EBI_WRTIMING_WRHOLD_MASK)) + | writeTiming; +} + + +/***************************************************************************//** + * @brief + * Configure timing values of address latch bus accesses + * + * @param[in] setupCycles + * Sets the number of cycles the address is held after ALE is asserted + * + * @param[in] holdCycles + * Sets the number of cycles the address is driven onto the ADDRDAT bus before + * ALE is asserted. If set 0, 1 cycle is inserted by HW + ******************************************************************************/ +void EBI_AddressTimingSet(int setupCycles, int holdCycles) +{ + uint32_t addressLatchTiming; + + /* Check that timing values are within limits */ + EFM_ASSERT(setupCycles < 4); + EFM_ASSERT(holdCycles < 4); + + /* Configure address latch timing values */ + addressLatchTiming = (setupCycles << _EBI_ADDRTIMING_ADDRSETUP_SHIFT) + | (holdCycles << _EBI_ADDRTIMING_ADDRHOLD_SHIFT); + + EBI->ADDRTIMING = (EBI->ADDRTIMING + & ~(_EBI_ADDRTIMING_ADDRSETUP_MASK + | _EBI_ADDRTIMING_ADDRHOLD_MASK)) + | addressLatchTiming; +} + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +/***************************************************************************//** + * @brief + * Configure and initialize TFT Direct Drive + * + * @param[in] ebiTFTInit + * TFT Initialization structure + ******************************************************************************/ +void EBI_TFTInit(const EBI_TFTInit_TypeDef *ebiTFTInit) +{ + uint32_t ctrl; + + /* Configure base address for frame buffer offset to EBI bank */ + EBI_TFTFrameBaseSet(ebiTFTInit->addressOffset); + + /* Configure display size and porch areas */ + EBI_TFTSizeSet(ebiTFTInit->hsize, + ebiTFTInit->vsize); + EBI_TFTHPorchSet(ebiTFTInit->hPorchFront, + ebiTFTInit->hPorchBack, + ebiTFTInit->hPulseWidth); + EBI_TFTVPorchSet(ebiTFTInit->vPorchFront, + ebiTFTInit->vPorchBack, + ebiTFTInit->vPulseWidth); + + /* Configure timing settings */ + EBI_TFTTimingSet(ebiTFTInit->dclkPeriod, + ebiTFTInit->startPosition, + ebiTFTInit->setupCycles, + ebiTFTInit->holdCycles); + + /* Configure line polarity settings */ + EBI_PolaritySet(ebiLineTFTCS, ebiTFTInit->csPolarity); + EBI_PolaritySet(ebiLineTFTDClk, ebiTFTInit->dclkPolarity); + EBI_PolaritySet(ebiLineTFTDataEn, ebiTFTInit->dataenPolarity); + EBI_PolaritySet(ebiLineTFTVSync, ebiTFTInit->vsyncPolarity); + EBI_PolaritySet(ebiLineTFTHSync, ebiTFTInit->hsyncPolarity); + + /* Main control, EBI bank select, mask and blending configuration */ + ctrl = (uint32_t)ebiTFTInit->bank + | (uint32_t)ebiTFTInit->width + | (uint32_t)ebiTFTInit->colSrc + | (uint32_t)ebiTFTInit->interleave + | (uint32_t)ebiTFTInit->fbTrigger + | (uint32_t)(ebiTFTInit->shiftDClk == true + ? (1 << _EBI_TFTCTRL_SHIFTDCLKEN_SHIFT) : 0) + | (uint32_t)ebiTFTInit->maskBlend + | (uint32_t)ebiTFTInit->driveMode; + + EBI->TFTCTRL = ctrl; + + /* Enable TFT pins */ + if (ebiTFTInit->driveMode != ebiTFTDDModeDisabled) + { + EBI->ROUTE |= EBI_ROUTE_TFTPEN; + } +} + + +/***************************************************************************//** + * @brief + * Configure and initialize TFT size settings + * + * @param[in] horizontal + * TFT display horizontal size in pixels + * @param[in] vertical + * TFT display vertical size in pixels + ******************************************************************************/ +void EBI_TFTSizeSet(uint32_t horizontal, uint32_t vertical) +{ + EFM_ASSERT((horizontal-1) < 1024); + EFM_ASSERT((vertical-1) < 1024); + + EBI->TFTSIZE = ((horizontal-1) << _EBI_TFTSIZE_HSZ_SHIFT) + | ((vertical-1) << _EBI_TFTSIZE_VSZ_SHIFT); +} + +/***************************************************************************//** + * @brief + * Configure and initialize Horizontal Porch Settings + * + * @param[in] front + * Horizontal front porch size in pixels + * @param[in] back + * Horizontal back porch size in pixels + * @param[in] pulseWidth + * Horizontal synchronization pulse width. Set to required -1. + ******************************************************************************/ +void EBI_TFTHPorchSet(int front, int back, int pulseWidth) +{ + EFM_ASSERT(front < 256); + EFM_ASSERT(back < 256); + EFM_ASSERT((pulseWidth-1) < 128); + + EBI->TFTHPORCH = (front << _EBI_TFTHPORCH_HFPORCH_SHIFT) + | (back << _EBI_TFTHPORCH_HBPORCH_SHIFT) + | ((pulseWidth-1) << _EBI_TFTHPORCH_HSYNC_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Configure Vertical Porch Settings + * + * @param[in] front + * Vertical front porch size in pixels + * @param[in] back + * Vertical back porch size in pixels + * @param[in] pulseWidth + * Vertical synchronization pulse width. Set to required -1. + ******************************************************************************/ +void EBI_TFTVPorchSet(int front, int back, int pulseWidth) +{ + EFM_ASSERT(front < 256); + EFM_ASSERT(back < 256); + EFM_ASSERT((pulseWidth-1) < 128); + + EBI->TFTVPORCH = (front << _EBI_TFTVPORCH_VFPORCH_SHIFT) + | (back << _EBI_TFTVPORCH_VBPORCH_SHIFT) + | ((pulseWidth-1) << _EBI_TFTVPORCH_VSYNC_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Configure TFT Direct Drive Timing Settings + * + * @param[in] dclkPeriod + * DCLK period in internal cycles + * + * @param[in] start + * Starting position of external direct drive, relative to DCLK inactive edge + * + * @param[in] setup + * Number of cycles RGB data is driven before active edge of DCLK + * + * @param[in] hold + * Number of cycles RGB data is held after active edge of DCLK + ******************************************************************************/ +void EBI_TFTTimingSet(int dclkPeriod, int start, int setup, int hold) +{ + EFM_ASSERT(dclkPeriod < 2048); + EFM_ASSERT(start < 2048); + EFM_ASSERT(setup < 4); + EFM_ASSERT(hold < 4); + + EBI->TFTTIMING = (dclkPeriod << _EBI_TFTTIMING_DCLKPERIOD_SHIFT) + | (start << _EBI_TFTTIMING_TFTSTART_SHIFT) + | (setup << _EBI_TFTTIMING_TFTSETUP_SHIFT) + | (hold << _EBI_TFTTIMING_TFTHOLD_SHIFT); +} +#endif + +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) +/***************************************************************************//** + * @brief + * Configure read operation parameters for selected bank + * + * @param[in] banks + * Mask of memory bank(s) to configure write timing for + * + * @param[in] pageMode + * Enables or disables half cycle WE strobe in last strobe cycle + * + * @param[in] prefetch + * Enables or disables half cycle WE strobe in last strobe cycle + * + * @param[in] halfRE + * Enables or disables half cycle WE strobe in last strobe cycle + ******************************************************************************/ +void EBI_BankReadTimingConfig(uint32_t banks, bool pageMode, bool prefetch, bool halfRE) +{ + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + /* Configure read operation parameters */ + if( banks & EBI_BANK0 ) + { + BUS_RegBitWrite(&EBI->RDTIMING, _EBI_RDTIMING_PAGEMODE_SHIFT, pageMode); + BUS_RegBitWrite(&EBI->RDTIMING, _EBI_RDTIMING_PREFETCH_SHIFT, prefetch); + BUS_RegBitWrite(&EBI->RDTIMING, _EBI_RDTIMING_HALFRE_SHIFT, halfRE); + } + if( banks & EBI_BANK1 ) + { + BUS_RegBitWrite(&EBI->RDTIMING1, _EBI_RDTIMING_PAGEMODE_SHIFT, pageMode); + BUS_RegBitWrite(&EBI->RDTIMING1, _EBI_RDTIMING_PREFETCH_SHIFT, prefetch); + BUS_RegBitWrite(&EBI->RDTIMING1, _EBI_RDTIMING_HALFRE_SHIFT, halfRE); + } + if( banks & EBI_BANK2 ) + { + BUS_RegBitWrite(&EBI->RDTIMING2, _EBI_RDTIMING_PAGEMODE_SHIFT, pageMode); + BUS_RegBitWrite(&EBI->RDTIMING2, _EBI_RDTIMING_PREFETCH_SHIFT, prefetch); + BUS_RegBitWrite(&EBI->RDTIMING2, _EBI_RDTIMING_HALFRE_SHIFT, halfRE); + } + if( banks & EBI_BANK3 ) + { + BUS_RegBitWrite(&EBI->RDTIMING3, _EBI_RDTIMING_PAGEMODE_SHIFT, pageMode); + BUS_RegBitWrite(&EBI->RDTIMING3, _EBI_RDTIMING_PREFETCH_SHIFT, prefetch); + BUS_RegBitWrite(&EBI->RDTIMING3, _EBI_RDTIMING_HALFRE_SHIFT, halfRE); + } +} + +/***************************************************************************//** + * @brief + * Configure timing values of read bus accesses + * + * @param[in] banks + * Mask of memory bank(s) to configure timing for + * + * @param[in] setupCycles + * Number of clock cycles for address setup before REn is asserted + * + * @param[in] strobeCycles + * The number of cycles the REn is held active. After the specified number of + * cycles, data is read. If set to 0, 1 cycle is inserted by HW + * + * @param[in] holdCycles + * The number of cycles CSn is held active after the REn is dessarted + ******************************************************************************/ +void EBI_BankReadTimingSet(uint32_t banks, int setupCycles, int strobeCycles, int holdCycles) +{ + uint32_t readTiming; + + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + /* Check that timings are within limits */ + EFM_ASSERT(setupCycles < 4); + EFM_ASSERT(strobeCycles < 64); + EFM_ASSERT(holdCycles < 4); + + /* Configure timing values */ + readTiming = (setupCycles << _EBI_RDTIMING_RDSETUP_SHIFT) + | (strobeCycles << _EBI_RDTIMING_RDSTRB_SHIFT) + | (holdCycles << _EBI_RDTIMING_RDHOLD_SHIFT); + + if (banks & EBI_BANK0) + { + EBI->RDTIMING = (EBI->RDTIMING + & ~(_EBI_RDTIMING_RDSETUP_MASK + | _EBI_RDTIMING_RDSTRB_MASK + | _EBI_RDTIMING_RDHOLD_MASK)) + | readTiming; + } + if (banks & EBI_BANK1) + { + EBI->RDTIMING1 = (EBI->RDTIMING1 + & ~(_EBI_RDTIMING1_RDSETUP_MASK + | _EBI_RDTIMING1_RDSTRB_MASK + | _EBI_RDTIMING1_RDHOLD_MASK)) + | readTiming; + } + if (banks & EBI_BANK2) + { + EBI->RDTIMING2 = (EBI->RDTIMING2 + & ~(_EBI_RDTIMING2_RDSETUP_MASK + | _EBI_RDTIMING2_RDSTRB_MASK + | _EBI_RDTIMING2_RDHOLD_MASK)) + | readTiming; + } + if (banks & EBI_BANK3) + { + EBI->RDTIMING3 = (EBI->RDTIMING3 + & ~(_EBI_RDTIMING3_RDSETUP_MASK + | _EBI_RDTIMING3_RDSTRB_MASK + | _EBI_RDTIMING3_RDHOLD_MASK)) + | readTiming; + } +} + + +/***************************************************************************//** + * @brief + * Configure write operation parameters for selected bank + * + * @param[in] banks + * Mask of memory bank(s) to configure write timing for + * + * @param[in] writeBufDisable + * If true, disable the write buffer + * + * @param[in] halfWE + * Enables or disables half cycle WE strobe in last strobe cycle + ******************************************************************************/ +void EBI_BankWriteTimingConfig(uint32_t banks, bool writeBufDisable, bool halfWE) +{ + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + /* Configure write operation parameters */ + if( banks & EBI_BANK0 ) + { + BUS_RegBitWrite(&EBI->WRTIMING, _EBI_WRTIMING_WBUFDIS_SHIFT, writeBufDisable); + BUS_RegBitWrite(&EBI->WRTIMING, _EBI_WRTIMING_HALFWE_SHIFT, halfWE); + } + if( banks & EBI_BANK1 ) + { + BUS_RegBitWrite(&EBI->WRTIMING1, _EBI_WRTIMING_WBUFDIS_SHIFT, writeBufDisable); + BUS_RegBitWrite(&EBI->WRTIMING1, _EBI_WRTIMING_HALFWE_SHIFT, halfWE); + } + if( banks & EBI_BANK2 ) + { + BUS_RegBitWrite(&EBI->WRTIMING2, _EBI_WRTIMING_WBUFDIS_SHIFT, writeBufDisable); + BUS_RegBitWrite(&EBI->WRTIMING2, _EBI_WRTIMING_HALFWE_SHIFT, halfWE); + } + if( banks & EBI_BANK3 ) + { + BUS_RegBitWrite(&EBI->WRTIMING3, _EBI_WRTIMING_WBUFDIS_SHIFT, writeBufDisable); + BUS_RegBitWrite(&EBI->WRTIMING3, _EBI_WRTIMING_HALFWE_SHIFT, halfWE); + } +} + + +/***************************************************************************//** + * @brief + * Configure timing values of write bus accesses + * + * @param[in] banks + * Mask of memory bank(s) to configure write timing for + * + * @param[in] setupCycles + * Number of clock cycles for address setup before WEn is asserted + * + * @param[in] strobeCycles + * Number of cycles WEn is held active. If set to 0, 1 cycle is inserted by HW + * + * @param[in] holdCycles + * Number of cycles CSn is held active after the WEn is deasserted + ******************************************************************************/ +void EBI_BankWriteTimingSet(uint32_t banks, int setupCycles, int strobeCycles, int holdCycles) +{ + uint32_t writeTiming; + + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + /* Check that timings are within limits */ + EFM_ASSERT(setupCycles < 4); + EFM_ASSERT(strobeCycles < 64); + EFM_ASSERT(holdCycles < 4); + + /* Configure timing values */ + writeTiming = (setupCycles << _EBI_WRTIMING_WRSETUP_SHIFT) + | (strobeCycles << _EBI_WRTIMING_WRSTRB_SHIFT) + | (holdCycles << _EBI_WRTIMING_WRHOLD_SHIFT); + + if (banks & EBI_BANK0) + { + EBI->WRTIMING = (EBI->WRTIMING + & ~(_EBI_WRTIMING_WRSETUP_MASK + | _EBI_WRTIMING_WRSTRB_MASK + | _EBI_WRTIMING_WRHOLD_MASK)) + | writeTiming; + } + if (banks & EBI_BANK1) + { + EBI->WRTIMING1 = (EBI->WRTIMING1 + & ~(_EBI_WRTIMING1_WRSETUP_MASK + | _EBI_WRTIMING1_WRSTRB_MASK + | _EBI_WRTIMING1_WRHOLD_MASK)) + | writeTiming; + } + if (banks & EBI_BANK2) + { + EBI->WRTIMING2 = (EBI->WRTIMING2 + & ~(_EBI_WRTIMING2_WRSETUP_MASK + | _EBI_WRTIMING2_WRSTRB_MASK + | _EBI_WRTIMING2_WRHOLD_MASK)) + | writeTiming; + } + if (banks & EBI_BANK3) + { + EBI->WRTIMING3 = (EBI->WRTIMING3 + & ~(_EBI_WRTIMING3_WRSETUP_MASK + | _EBI_WRTIMING3_WRSTRB_MASK + | _EBI_WRTIMING3_WRHOLD_MASK)) + | writeTiming; + } +} + + +/***************************************************************************//** + * @brief + * Configure address operation parameters for selected bank + * + * @param[in] banks + * Mask of memory bank(s) to configure write timing for + * + * @param[in] halfALE + * Enables or disables half cycle ALE strobe in last strobe cycle + ******************************************************************************/ +void EBI_BankAddressTimingConfig(uint32_t banks, bool halfALE) +{ + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + if( banks & EBI_BANK0 ) + { + BUS_RegBitWrite(&EBI->ADDRTIMING, _EBI_ADDRTIMING_HALFALE_SHIFT, halfALE); + } + if( banks & EBI_BANK1 ) + { + BUS_RegBitWrite(&EBI->ADDRTIMING1, _EBI_ADDRTIMING_HALFALE_SHIFT, halfALE); + } + if( banks & EBI_BANK2 ) + { + BUS_RegBitWrite(&EBI->ADDRTIMING2, _EBI_ADDRTIMING_HALFALE_SHIFT, halfALE); + } + if( banks & EBI_BANK3 ) + { + BUS_RegBitWrite(&EBI->ADDRTIMING3, _EBI_ADDRTIMING_HALFALE_SHIFT, halfALE); + } +} + + +/***************************************************************************//** + * @brief + * Configure timing values of address latch bus accesses + * + * @param[in] banks + * Mask of memory bank(s) to configure address timing for + * + * @param[in] setupCycles + * Sets the number of cycles the address is held after ALE is asserted + * + * @param[in] holdCycles + * Sets the number of cycles the address is driven onto the ADDRDAT bus before + * ALE is asserted. If set 0, 1 cycle is inserted by HW + ******************************************************************************/ +void EBI_BankAddressTimingSet(uint32_t banks, int setupCycles, int holdCycles) +{ + uint32_t addressLatchTiming; + + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + /* Check that timing values are within limits */ + EFM_ASSERT(setupCycles < 4); + EFM_ASSERT(holdCycles < 4); + + /* Configure address latch timing values */ + addressLatchTiming = (setupCycles << _EBI_ADDRTIMING_ADDRSETUP_SHIFT) + | (holdCycles << _EBI_ADDRTIMING_ADDRHOLD_SHIFT); + + if (banks & EBI_BANK0) + { + EBI->ADDRTIMING = (EBI->ADDRTIMING + & ~(_EBI_ADDRTIMING_ADDRSETUP_MASK + | _EBI_ADDRTIMING_ADDRHOLD_MASK)) + | addressLatchTiming; + } + if (banks & EBI_BANK1) + { + EBI->ADDRTIMING1 = (EBI->ADDRTIMING1 + & ~(_EBI_ADDRTIMING1_ADDRSETUP_MASK + | _EBI_ADDRTIMING1_ADDRHOLD_MASK)) + | addressLatchTiming; + } + if (banks & EBI_BANK2) + { + EBI->ADDRTIMING2 = (EBI->ADDRTIMING2 + & ~(_EBI_ADDRTIMING2_ADDRSETUP_MASK + | _EBI_ADDRTIMING2_ADDRHOLD_MASK)) + | addressLatchTiming; + } + if (banks & EBI_BANK3) + { + EBI->ADDRTIMING3 = (EBI->ADDRTIMING3 + & ~(_EBI_ADDRTIMING3_ADDRSETUP_MASK + | _EBI_ADDRTIMING3_ADDRHOLD_MASK)) + | addressLatchTiming; + } +} + + +/***************************************************************************//** + * @brief + * Configure EBI pin polarity for selected bank(s) for devices with individual + * timing support + * + * @param[in] banks + * Mask of memory bank(s) to configure polarity for + * + * @param[in] line + * Which pin/line to configure + * + * @param[in] polarity + * Active high, or active low + ******************************************************************************/ +void EBI_BankPolaritySet(uint32_t banks, EBI_Line_TypeDef line, EBI_Polarity_TypeDef polarity) +{ + uint32_t bankSet = 0; + volatile uint32_t *polRegister = 0; + + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + while (banks) + { +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + if (banks & EBI_BANK0) + { + polRegister = &EBI->POLARITY; + bankSet = EBI_BANK0; + } + if (banks & EBI_BANK1) + { + polRegister = &EBI->POLARITY1; + bankSet = EBI_BANK1; + } + if (banks & EBI_BANK2) + { + polRegister = &EBI->POLARITY2; + bankSet = EBI_BANK2; + } + if (banks & EBI_BANK3) + { + polRegister = &EBI->POLARITY3; + bankSet = EBI_BANK3; + } +#else + polRegister = &EBI->POLARITY; + banks = 0; +#endif + + /* What line to configure */ + switch (line) + { + case ebiLineARDY: + BUS_RegBitWrite(polRegister, _EBI_POLARITY_ARDYPOL_SHIFT, polarity); + break; + case ebiLineALE: + BUS_RegBitWrite(polRegister, _EBI_POLARITY_ALEPOL_SHIFT, polarity); + break; + case ebiLineWE: + BUS_RegBitWrite(polRegister, _EBI_POLARITY_WEPOL_SHIFT, polarity); + break; + case ebiLineRE: + BUS_RegBitWrite(polRegister, _EBI_POLARITY_REPOL_SHIFT, polarity); + break; + case ebiLineCS: + BUS_RegBitWrite(polRegister, _EBI_POLARITY_CSPOL_SHIFT, polarity); + break; +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + case ebiLineBL: + BUS_RegBitWrite(polRegister, _EBI_POLARITY_BLPOL_SHIFT, polarity); + break; + case ebiLineTFTVSync: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_VSYNCPOL_SHIFT, polarity); + break; + case ebiLineTFTHSync: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_HSYNCPOL_SHIFT, polarity); + break; + case ebiLineTFTDataEn: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_DATAENPOL_SHIFT, polarity); + break; + case ebiLineTFTDClk: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_DCLKPOL_SHIFT, polarity); + break; + case ebiLineTFTCS: + BUS_RegBitWrite(&(EBI->TFTPOLARITY), _EBI_TFTPOLARITY_CSPOL_SHIFT, polarity); + break; +#endif + default: + EFM_ASSERT(0); + break; + } + banks = banks & ~bankSet; + } +} + + +/***************************************************************************//** + * @brief + * Configure Byte Lane Enable for select banks + * timing support + * + * @param[in] banks + * Mask of memory bank(s) to configure polarity for + * + * @param[in] enable + * Flag + ******************************************************************************/ +void EBI_BankByteLaneEnable(uint32_t banks, bool enable) +{ + /* Verify only valid banks are used */ + EFM_ASSERT((banks & ~(EBI_BANK0 | EBI_BANK1 | EBI_BANK2 | EBI_BANK3)) == 0); + + /* Configure byte lane support for each selected bank */ + if (banks & EBI_BANK0) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BL_SHIFT, enable); + } + if (banks & EBI_BANK1) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BL1_SHIFT, enable); + } + if (banks & EBI_BANK2) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BL2_SHIFT, enable); + } + if (banks & EBI_BANK3) + { + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_BL3_SHIFT, enable); + } +} + + +/***************************************************************************//** + * @brief + * Configure Alternate Address Map support + * Enables or disables 256MB address range for all banks + * + * @param[in] enable + * Set or clear address map extension + ******************************************************************************/ +void EBI_AltMapEnable(bool enable) +{ + BUS_RegBitWrite(&(EBI->CTRL), _EBI_CTRL_ALTMAP_SHIFT, enable); +} + +#endif + +/** @} (end addtogroup EBI) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(EBI_COUNT) && (EBI_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_emu.c b/cpu/efm32_common/emlib/src/em_emu.c new file mode 100644 index 0000000000000..25489006649f3 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_emu.c @@ -0,0 +1,1805 @@ +/***************************************************************************//** + * @file em_emu.c + * @brief Energy Management Unit (EMU) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include + +#include "em_emu.h" +#if defined( EMU_PRESENT ) && ( EMU_COUNT > 0 ) + +#include "em_cmu.h" +#include "em_system.h" +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup EMU + * @brief Energy Management Unit (EMU) Peripheral API + * @{ + ******************************************************************************/ + +/* Consistency check, since restoring assumes similar bitpositions in */ +/* CMU OSCENCMD and STATUS regs */ +#if (CMU_STATUS_AUXHFRCOENS != CMU_OSCENCMD_AUXHFRCOEN) +#error Conflict in AUXHFRCOENS and AUXHFRCOEN bitpositions +#endif +#if (CMU_STATUS_HFXOENS != CMU_OSCENCMD_HFXOEN) +#error Conflict in HFXOENS and HFXOEN bitpositions +#endif +#if (CMU_STATUS_LFRCOENS != CMU_OSCENCMD_LFRCOEN) +#error Conflict in LFRCOENS and LFRCOEN bitpositions +#endif +#if (CMU_STATUS_LFXOENS != CMU_OSCENCMD_LFXOEN) +#error Conflict in LFXOENS and LFXOEN bitpositions +#endif + + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/* Fix for errata EMU_E107 - non-WIC interrupt masks. */ +#if defined( _EFM32_GECKO_FAMILY ) +#define ERRATA_FIX_EMU_E107_EN +#define NON_WIC_INT_MASK_0 (~(0x0dfc0323U)) +#define NON_WIC_INT_MASK_1 (~(0x0U)) + +#elif defined( _EFM32_TINY_FAMILY ) +#define ERRATA_FIX_EMU_E107_EN +#define NON_WIC_INT_MASK_0 (~(0x001be323U)) +#define NON_WIC_INT_MASK_1 (~(0x0U)) + +#elif defined( _EFM32_GIANT_FAMILY ) +#define ERRATA_FIX_EMU_E107_EN +#define NON_WIC_INT_MASK_0 (~(0xff020e63U)) +#define NON_WIC_INT_MASK_1 (~(0x00000046U)) + +#elif defined( _EFM32_WONDER_FAMILY ) +#define ERRATA_FIX_EMU_E107_EN +#define NON_WIC_INT_MASK_0 (~(0xff020e63U)) +#define NON_WIC_INT_MASK_1 (~(0x00000046U)) + +#else +/* Zero Gecko and future families are not affected by errata EMU_E107 */ +#endif + +/* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */ +#if defined( _EFM32_HAPPY_FAMILY ) +#define ERRATA_FIX_EMU_E108_EN +#endif +/** @endcond */ + + +#if defined( _EMU_DCDCCTRL_MASK ) +/* DCDCTODVDD output range min/max */ +#define PWRCFG_DCDCTODVDD_VMIN 1200 +#define PWRCFG_DCDCTODVDD_VMAX 3000 +typedef enum +{ + errataFixDcdcHsInit, + errataFixDcdcHsTrimSet, + errataFixDcdcHsLnWaitDone +} errataFixDcdcHs_TypeDef; +errataFixDcdcHs_TypeDef errataFixDcdcHsState = errataFixDcdcHsInit; +#endif + +/******************************************************************************* + ************************** LOCAL VARIABLES ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/** + * CMU configured oscillator selection and oscillator enable status. When a + * user configures oscillators, this varaiable shall shadow the configuration. + * It is used by the EMU module in order to be able to restore the oscillator + * config after having been in certain energy modes (since HW may automatically + * alter config when going into an energy mode). It is the responsibility of + * the CMU module to keep it up-to-date (or a user if not using the CMU API + * for oscillator control). + */ +static uint32_t cmuStatus; +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) +static uint16_t cmuHfclkStatus; +#endif +#if defined( _EMU_DCDCCTRL_MASK ) +static uint16_t dcdcMaxCurrent_mA; +static uint16_t dcdcOutput_mVout; +#endif + +/** @endcond */ + + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/***************************************************************************//** + * @brief + * Restore oscillators and core clock after having been in EM2 or EM3. + ******************************************************************************/ +static void emuRestore(void) +{ + uint32_t oscEnCmd; + uint32_t cmuLocked; + + /* Although we could use the CMU API for most of the below handling, we */ + /* would like this function to be as efficient as possible. */ + + /* CMU registers may be locked */ + cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED; + CMU_Unlock(); + + /* AUXHFRCO are automatically disabled (except if using debugger). */ + /* HFRCO, USHFRCO and HFXO are automatically disabled. */ + /* LFRCO/LFXO may be disabled by SW in EM3. */ + /* Restore according to status prior to entering energy mode. */ + oscEnCmd = 0; + oscEnCmd |= ((cmuStatus & CMU_STATUS_HFRCOENS) ? CMU_OSCENCMD_HFRCOEN : 0); + oscEnCmd |= ((cmuStatus & CMU_STATUS_AUXHFRCOENS) ? CMU_OSCENCMD_AUXHFRCOEN : 0); + oscEnCmd |= ((cmuStatus & CMU_STATUS_LFRCOENS) ? CMU_OSCENCMD_LFRCOEN : 0); + oscEnCmd |= ((cmuStatus & CMU_STATUS_HFXOENS) ? CMU_OSCENCMD_HFXOEN : 0); + oscEnCmd |= ((cmuStatus & CMU_STATUS_LFXOENS) ? CMU_OSCENCMD_LFXOEN : 0); +#if defined( _CMU_STATUS_USHFRCOENS_MASK ) + oscEnCmd |= ((cmuStatus & CMU_STATUS_USHFRCOENS) ? CMU_OSCENCMD_USHFRCOEN : 0); +#endif + CMU->OSCENCMD = oscEnCmd; + + +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) + /* Restore oscillator used for clocking core */ + switch (cmuHfclkStatus & _CMU_HFCLKSTATUS_SELECTED_MASK) + { + case CMU_HFCLKSTATUS_SELECTED_LFRCO: + /* HFRCO could only be selected if the autostart HFXO feature is not + * enabled, otherwise the HFXO would be started and selected automatically. + * Note: this error hook helps catching erroneous oscillator configurations, + * when the AUTOSTARTSELEM0EM1 is set in CMU_HFXOCTRL. */ + if (!(CMU->HFXOCTRL & CMU_HFXOCTRL_AUTOSTARTSELEM0EM1)) + { + /* Wait for LFRCO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_LFRCORDY)) + ; + CMU->HFCLKSEL = CMU_HFCLKSEL_HF_LFRCO; + } + else + { + EFM_ASSERT(0); + } + break; + + case CMU_HFCLKSTATUS_SELECTED_LFXO: + /* Wait for LFXO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_LFXORDY)) + ; + CMU->HFCLKSEL = CMU_HFCLKSEL_HF_LFXO; + break; + + case CMU_HFCLKSTATUS_SELECTED_HFXO: + /* Wait for HFXO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_HFXORDY)) + ; + CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFXO; + break; + + default: /* CMU_HFCLKSTATUS_SELECTED_HFRCO */ + /* If core clock was HFRCO core clock, it is automatically restored to */ + /* state prior to entering energy mode. No need for further action. */ + break; + } +#else + switch (cmuStatus & (CMU_STATUS_HFRCOSEL + | CMU_STATUS_HFXOSEL + | CMU_STATUS_LFRCOSEL +#if defined( CMU_STATUS_USHFRCODIV2SEL ) + | CMU_STATUS_USHFRCODIV2SEL +#endif + | CMU_STATUS_LFXOSEL)) + { + case CMU_STATUS_LFRCOSEL: + /* Wait for LFRCO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_LFRCORDY)) + ; + CMU->CMD = CMU_CMD_HFCLKSEL_LFRCO; + break; + + case CMU_STATUS_LFXOSEL: + /* Wait for LFXO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_LFXORDY)) + ; + CMU->CMD = CMU_CMD_HFCLKSEL_LFXO; + break; + + case CMU_STATUS_HFXOSEL: + /* Wait for HFXO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_HFXORDY)) + ; + CMU->CMD = CMU_CMD_HFCLKSEL_HFXO; + break; + +#if defined( CMU_STATUS_USHFRCODIV2SEL ) + case CMU_STATUS_USHFRCODIV2SEL: + /* Wait for USHFRCO to stabilize */ + while (!(CMU->STATUS & CMU_STATUS_USHFRCORDY)) + ; + CMU->CMD = _CMU_CMD_HFCLKSEL_USHFRCODIV2; + break; +#endif + + default: /* CMU_STATUS_HFRCOSEL */ + /* If core clock was HFRCO core clock, it is automatically restored to */ + /* state prior to entering energy mode. No need for further action. */ + break; + } + + /* If HFRCO was disabled before entering Energy Mode, turn it off again */ + /* as it is automatically enabled by wake up */ + if ( ! (cmuStatus & CMU_STATUS_HFRCOENS) ) + { + CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS; + } +#endif + /* Restore CMU register locking */ + if (cmuLocked) + { + CMU_Lock(); + } +} + + +#if defined( ERRATA_FIX_EMU_E107_EN ) +/* Get enable conditions for errata EMU_E107 fix. */ +static __INLINE bool getErrataFixEmuE107En(void) +{ + /* SYSTEM_ChipRevisionGet could have been used here, but we would like a + * faster implementation in this case. + */ + uint16_t majorMinorRev; + + /* CHIP MAJOR bit [3:0] */ + majorMinorRev = ((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) + >> _ROMTABLE_PID0_REVMAJOR_SHIFT) + << 8; + /* CHIP MINOR bit [7:4] */ + majorMinorRev |= ((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK) + >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) + << 4; + /* CHIP MINOR bit [3:0] */ + majorMinorRev |= (ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) + >> _ROMTABLE_PID3_REVMINORLSB_SHIFT; + +#if defined( _EFM32_GECKO_FAMILY ) + return (majorMinorRev <= 0x0103); +#elif defined( _EFM32_TINY_FAMILY ) + return (majorMinorRev <= 0x0102); +#elif defined( _EFM32_GIANT_FAMILY ) + return (majorMinorRev <= 0x0103) || (majorMinorRev == 0x0204); +#elif defined( _EFM32_WONDER_FAMILY ) + return (majorMinorRev == 0x0100); +#else + /* Zero Gecko and future families are not affected by errata EMU_E107 */ + return false; +#endif +} +#endif + + +#if defined( _EMU_DCDCCTRL_MASK ) +/* LP prepare / LN restore P/NFET count */ +static void maxCurrentUpdate(void); +#define DCDC_LP_PFET_CNT 7 +#define DCDC_LP_NFET_CNT 15 +void dcdcFetCntSet(bool lpModeSet) +{ + uint32_t tmp; + static uint32_t emuDcdcMiscCtrlReg; + + if (lpModeSet) + { + emuDcdcMiscCtrlReg = EMU->DCDCMISCCTRL; + tmp = EMU->DCDCMISCCTRL + & ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK | _EMU_DCDCMISCCTRL_NFETCNT_MASK); + tmp |= (DCDC_LP_PFET_CNT << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT) + | (DCDC_LP_NFET_CNT << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT); + EMU->DCDCMISCCTRL = tmp; + maxCurrentUpdate(); + } + else + { + EMU->DCDCMISCCTRL = emuDcdcMiscCtrlReg; + maxCurrentUpdate(); + } +} + +void dcdcHsFixLnBlock(void) +{ +#define EMU_DCDCSTATUS (* (volatile uint32_t *)(EMU_BASE + 0x7C)) + if (errataFixDcdcHsState == errataFixDcdcHsTrimSet) + { + /* Wait for LNRUNNING */ + if ((EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) == EMU_DCDCCTRL_DCDCMODE_LOWNOISE) + { + while (!(EMU_DCDCSTATUS & (0x1 << 16))); + } + errataFixDcdcHsState = errataFixDcdcHsLnWaitDone; + } +} +#endif + + +/** @endcond */ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Enter energy mode 2 (EM2). + * + * @details + * When entering EM2, the high frequency clocks are disabled, ie HFXO, HFRCO + * and AUXHFRCO (for AUXHFRCO, see exception note below). When re-entering + * EM0, HFRCO is re-enabled and the core will be clocked by the configured + * HFRCO band. This ensures a quick wakeup from EM2. + * + * However, prior to entering EM2, the core may have been using another + * oscillator than HFRCO. The @p restore parameter gives the user the option + * to restore all HF oscillators according to state prior to entering EM2, + * as well as the clock used to clock the core. This restore procedure is + * handled by SW. However, since handled by SW, it will not be restored + * before completing the interrupt function(s) waking up the core! + * + * @note + * If restoring core clock to use the HFXO oscillator, which has been + * disabled during EM2 mode, this function will stall until the oscillator + * has stabilized. Stalling time can be reduced by adding interrupt + * support detecting stable oscillator, and an asynchronous switch to the + * original oscillator. See CMU documentation. Such a feature is however + * outside the scope of the implementation in this function. + * @par + * If HFXO is re-enabled by this function, and NOT used to clock the core, + * this function will not wait for HFXO to stabilize. This must be considered + * by the application if trying to use features relying on that oscillator + * upon return. + * @par + * If a debugger is attached, the AUXHFRCO will not be disabled if enabled + * upon entering EM2. It will thus remain enabled when returning to EM0 + * regardless of the @p restore parameter. + * @par + * If HFXO autostart and select is enabled by using CMU_HFXOAutostartEnable(), + * the starting and selecting of the core clocks will be identical to the user + * independently of the value of the @p restore parameter when waking up on + * the wakeup sources corresponding to the autostart and select setting. + * + * @param[in] restore + * @li true - restore oscillators and clocks, see function details. + * @li false - do not restore oscillators and clocks, see function details. + * @par + * The @p restore option should only be used if all clock control is done + * via the CMU API. + ******************************************************************************/ +void EMU_EnterEM2(bool restore) +{ +#if defined( ERRATA_FIX_EMU_E107_EN ) + bool errataFixEmuE107En; + uint32_t nonWicIntEn[2]; +#endif + + /* Auto-update CMU status just in case before entering energy mode. */ + /* This variable is normally kept up-to-date by the CMU API. */ + cmuStatus = CMU->STATUS; +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) + cmuHfclkStatus = (uint16_t)(CMU->HFCLKSTATUS); +#endif + + /* Enter Cortex deep sleep mode */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* Fix for errata EMU_E107 - store non-WIC interrupt enable flags. + Disable the enabled non-WIC interrupts. */ +#if defined( ERRATA_FIX_EMU_E107_EN ) + errataFixEmuE107En = getErrataFixEmuE107En(); + if (errataFixEmuE107En) + { + nonWicIntEn[0] = NVIC->ISER[0] & NON_WIC_INT_MASK_0; + NVIC->ICER[0] = nonWicIntEn[0]; +#if (NON_WIC_INT_MASK_1 != (~(0x0U))) + nonWicIntEn[1] = NVIC->ISER[1] & NON_WIC_INT_MASK_1; + NVIC->ICER[1] = nonWicIntEn[1]; +#endif + } +#endif + +#if defined( _EMU_DCDCCTRL_MASK ) + dcdcFetCntSet(true); + dcdcHsFixLnBlock(); +#endif + + __WFI(); + +#if defined( _EMU_DCDCCTRL_MASK ) + dcdcFetCntSet(false); +#endif + + /* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */ +#if defined( ERRATA_FIX_EMU_E107_EN ) + if (errataFixEmuE107En) + { + NVIC->ISER[0] = nonWicIntEn[0]; +#if (NON_WIC_INT_MASK_1 != (~(0x0U))) + NVIC->ISER[1] = nonWicIntEn[1]; +#endif + } +#endif + + /* Restore oscillators/clocks if specified */ + if (restore) + { + emuRestore(); + } + /* If not restoring, and original clock was not HFRCO, we have to */ + /* update CMSIS core clock variable since core clock has changed */ + /* to using HFRCO. */ +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) + else if ((cmuHfclkStatus & _CMU_HFCLKSTATUS_SELECTED_MASK) + != CMU_HFCLKSTATUS_SELECTED_HFRCO) +#else + else if (!(cmuStatus & CMU_STATUS_HFRCOSEL)) +#endif + { + SystemCoreClockUpdate(); + } +} + + +/***************************************************************************//** + * @brief + * Enter energy mode 3 (EM3). + * + * @details + * When entering EM3, the high frequency clocks are disabled by HW, ie HFXO, + * HFRCO and AUXHFRCO (for AUXHFRCO, see exception note below). In addition, + * the low frequency clocks, ie LFXO and LFRCO are disabled by SW. When + * re-entering EM0, HFRCO is re-enabled and the core will be clocked by the + * configured HFRCO band. This ensures a quick wakeup from EM3. + * + * However, prior to entering EM3, the core may have been using another + * oscillator than HFRCO. The @p restore parameter gives the user the option + * to restore all HF/LF oscillators according to state prior to entering EM3, + * as well as the clock used to clock the core. This restore procedure is + * handled by SW. However, since handled by SW, it will not be restored + * before completing the interrupt function(s) waking up the core! + * + * @note + * If restoring core clock to use an oscillator other than HFRCO, this + * function will stall until the oscillator has stabilized. Stalling time + * can be reduced by adding interrupt support detecting stable oscillator, + * and an asynchronous switch to the original oscillator. See CMU + * documentation. Such a feature is however outside the scope of the + * implementation in this function. + * @par + * If HFXO/LFXO/LFRCO are re-enabled by this function, and NOT used to clock + * the core, this function will not wait for those oscillators to stabilize. + * This must be considered by the application if trying to use features + * relying on those oscillators upon return. + * @par + * If a debugger is attached, the AUXHFRCO will not be disabled if enabled + * upon entering EM3. It will thus remain enabled when returning to EM0 + * regardless of the @p restore parameter. + * + * @param[in] restore + * @li true - restore oscillators and clocks, see function details. + * @li false - do not restore oscillators and clocks, see function details. + * @par + * The @p restore option should only be used if all clock control is done + * via the CMU API. + ******************************************************************************/ +void EMU_EnterEM3(bool restore) +{ + uint32_t cmuLocked; + +#if defined( ERRATA_FIX_EMU_E107_EN ) + bool errataFixEmuE107En; + uint32_t nonWicIntEn[2]; +#endif + + /* Auto-update CMU status just in case before entering energy mode. */ + /* This variable is normally kept up-to-date by the CMU API. */ + cmuStatus = CMU->STATUS; +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) + cmuHfclkStatus = (uint16_t)(CMU->HFCLKSTATUS); +#endif + + /* CMU registers may be locked */ + cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED; + CMU_Unlock(); + + /* Disable LF oscillators */ + CMU->OSCENCMD = CMU_OSCENCMD_LFXODIS | CMU_OSCENCMD_LFRCODIS; + + /* Restore CMU register locking */ + if (cmuLocked) + { + CMU_Lock(); + } + + /* Enter Cortex deep sleep mode */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + /* Fix for errata EMU_E107 - store non-WIC interrupt enable flags. + Disable the enabled non-WIC interrupts. */ +#if defined( ERRATA_FIX_EMU_E107_EN ) + errataFixEmuE107En = getErrataFixEmuE107En(); + if (errataFixEmuE107En) + { + nonWicIntEn[0] = NVIC->ISER[0] & NON_WIC_INT_MASK_0; + NVIC->ICER[0] = nonWicIntEn[0]; +#if (NON_WIC_INT_MASK_1 != (~(0x0U))) + nonWicIntEn[1] = NVIC->ISER[1] & NON_WIC_INT_MASK_1; + NVIC->ICER[1] = nonWicIntEn[1]; +#endif + + } +#endif + +#if defined( _EMU_DCDCCTRL_MASK ) + dcdcFetCntSet(true); + dcdcHsFixLnBlock(); +#endif + + __WFI(); + +#if defined( _EMU_DCDCCTRL_MASK ) + dcdcFetCntSet(false); +#endif + + /* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */ +#if defined( ERRATA_FIX_EMU_E107_EN ) + if (errataFixEmuE107En) + { + NVIC->ISER[0] = nonWicIntEn[0]; +#if (NON_WIC_INT_MASK_1 != (~(0x0U))) + NVIC->ISER[1] = nonWicIntEn[1]; +#endif + } +#endif + + /* Restore oscillators/clocks if specified */ + if (restore) + { + emuRestore(); + } + /* If not restoring, and original clock was not HFRCO, we have to */ + /* update CMSIS core clock variable since core clock has changed */ + /* to using HFRCO. */ +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) + else if ((cmuHfclkStatus & _CMU_HFCLKSTATUS_SELECTED_MASK) + != CMU_HFCLKSTATUS_SELECTED_HFRCO) +#else + else if (!(cmuStatus & CMU_STATUS_HFRCOSEL)) +#endif + { + SystemCoreClockUpdate(); + } +} + + +/***************************************************************************//** + * @brief + * Enter energy mode 4 (EM4). + * + * @note + * Only a power on reset or external reset pin can wake the device from EM4. + ******************************************************************************/ +void EMU_EnterEM4(void) +{ + int i; + +#if defined( _EMU_EM4CTRL_EM4ENTRY_SHIFT ) + uint32_t em4seq2 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK) + | (2 << _EMU_EM4CTRL_EM4ENTRY_SHIFT); + uint32_t em4seq3 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK) + | (3 << _EMU_EM4CTRL_EM4ENTRY_SHIFT); +#else + uint32_t em4seq2 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK) + | (2 << _EMU_CTRL_EM4CTRL_SHIFT); + uint32_t em4seq3 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK) + | (3 << _EMU_CTRL_EM4CTRL_SHIFT); +#endif + + /* Make sure register write lock is disabled */ + EMU_Unlock(); + +#if defined( ERRATA_FIX_EMU_E108_EN ) + /* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */ + __disable_irq(); + *(volatile uint32_t *)0x400C80E4 = 0; +#endif + +#if defined( _EMU_DCDCCTRL_MASK ) + dcdcFetCntSet(true); + dcdcHsFixLnBlock(); +#endif + + for (i = 0; i < 4; i++) + { +#if defined( _EMU_EM4CTRL_EM4ENTRY_SHIFT ) + EMU->EM4CTRL = em4seq2; + EMU->EM4CTRL = em4seq3; + } + EMU->EM4CTRL = em4seq2; +#else + EMU->CTRL = em4seq2; + EMU->CTRL = em4seq3; + } + EMU->CTRL = em4seq2; +#endif +} + + +/***************************************************************************//** + * @brief + * Power down memory block. + * + * @param[in] blocks + * Specifies a logical OR of bits indicating memory blocks to power down. + * Bit 0 selects block 1, bit 1 selects block 2, etc. Memory block 0 cannot + * be disabled. Please refer to the reference manual for available + * memory blocks for a device. + * + * @note + * Only a reset can make the specified memory block(s) available for use + * after having been powered down. Function will be void for devices not + * supporting this feature. + ******************************************************************************/ +void EMU_MemPwrDown(uint32_t blocks) +{ +#if defined( _EMU_MEMCTRL_POWERDOWN_MASK ) + EFM_ASSERT(blocks <= (_EMU_MEMCTRL_POWERDOWN_MASK + >> _EMU_MEMCTRL_POWERDOWN_SHIFT)); + EMU->MEMCTRL = blocks; + +#elif defined( _EMU_MEMCTRL_RAMPOWERDOWN_MASK ) \ + && defined( _EMU_MEMCTRL_RAMHPOWERDOWN_MASK ) \ + && defined( _EMU_MEMCTRL_SEQRAMPOWERDOWN_MASK ) + EFM_ASSERT((blocks & (_EMU_MEMCTRL_RAMPOWERDOWN_MASK + | _EMU_MEMCTRL_RAMHPOWERDOWN_MASK + | _EMU_MEMCTRL_SEQRAMPOWERDOWN_MASK)) + == blocks); + EMU->MEMCTRL = blocks; + +#elif defined( _EMU_MEMCTRL_RAMPOWERDOWN_MASK ) + EFM_ASSERT((blocks & _EMU_MEMCTRL_RAMPOWERDOWN_MASK) == blocks); + EMU->MEMCTRL = blocks; + +#elif defined( _EMU_RAM0CTRL_RAMPOWERDOWN_MASK ) + EFM_ASSERT((blocks & _EMU_RAM0CTRL_RAMPOWERDOWN_MASK) == blocks); + EMU->RAM0CTRL = blocks; + +#else + (void)blocks; +#endif +} + + +/***************************************************************************//** + * @brief + * Update EMU module with CMU oscillator selection/enable status. + * + * @details + * When entering EM2 and EM3, the HW may change the core clock oscillator + * used, as well as disabling some oscillators. The user may optionally select + * to restore the oscillators after waking up from EM2 and EM3 through the + * SW API. + * + * However, in order to support this in a safe way, the EMU module must + * be kept up-to-date on the actual selected configuration. The CMU + * module must keep the EMU module up-to-date. + * + * This function is mainly intended for internal use by the CMU module, + * but if the applications changes oscillator configurations without + * using the CMU API, this function can be used to keep the EMU module + * up-to-date. + ******************************************************************************/ +void EMU_UpdateOscConfig(void) +{ + /* Fetch current configuration */ + cmuStatus = CMU->STATUS; +#if defined( _CMU_HFCLKSTATUS_RESETVALUE ) + cmuHfclkStatus = (uint16_t)(CMU->HFCLKSTATUS); +#endif +} + + +/***************************************************************************//** + * @brief + * Update EMU module with Energy Mode 2 and 3 configuration + * + * @param[in] em23Init + * Energy Mode 2 and 3 configuration structure + ******************************************************************************/ +void EMU_EM23Init(EMU_EM23Init_TypeDef *em23Init) +{ +#if defined( _EMU_CTRL_EMVREG_MASK ) + EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EMVREG) + : (EMU->CTRL & ~EMU_CTRL_EMVREG); +#elif defined( _EMU_CTRL_EM23VREG_MASK ) + EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EM23VREG) + : (EMU->CTRL & ~EMU_CTRL_EM23VREG); +#else + (void)em23Init; +#endif +} + + +#if defined( _EMU_EM4CONF_MASK ) || defined( _EMU_EM4CTRL_MASK ) +/***************************************************************************//** + * @brief + * Update EMU module with Energy Mode 4 configuration + * + * @param[in] em4Init + * Energy Mode 4 configuration structure + ******************************************************************************/ +void EMU_EM4Init(EMU_EM4Init_TypeDef *em4Init) +{ +#if defined( _EMU_EM4CONF_MASK ) + /* Init for platforms with EMU->EM4CONF register */ + uint32_t em4conf = EMU->EM4CONF; + + /* Clear fields that will be reconfigured */ + em4conf &= ~(_EMU_EM4CONF_LOCKCONF_MASK + | _EMU_EM4CONF_OSC_MASK + | _EMU_EM4CONF_BURTCWU_MASK + | _EMU_EM4CONF_VREGEN_MASK); + + /* Configure new settings */ + em4conf |= (em4Init->lockConfig << _EMU_EM4CONF_LOCKCONF_SHIFT) + | (em4Init->osc) + | (em4Init->buRtcWakeup << _EMU_EM4CONF_BURTCWU_SHIFT) + | (em4Init->vreg << _EMU_EM4CONF_VREGEN_SHIFT); + + /* Apply configuration. Note that lock can be set after this stage. */ + EMU->EM4CONF = em4conf; + +#elif defined( _EMU_EM4CTRL_MASK ) + /* Init for platforms with EMU->EM4CTRL register */ + + uint32_t em4ctrl = EMU->EM4CTRL; + + em4ctrl &= ~(_EMU_EM4CTRL_RETAINLFXO_MASK + | _EMU_EM4CTRL_RETAINLFRCO_MASK + | _EMU_EM4CTRL_RETAINULFRCO_MASK + | _EMU_EM4CTRL_EM4STATE_MASK + | _EMU_EM4CTRL_EM4IORETMODE_MASK); + + em4ctrl |= (em4Init->retainLfxo ? EMU_EM4CTRL_RETAINLFXO : 0) + | (em4Init->retainLfrco ? EMU_EM4CTRL_RETAINLFRCO : 0) + | (em4Init->retainUlfrco ? EMU_EM4CTRL_RETAINULFRCO : 0) + | (em4Init->em4State ? EMU_EM4CTRL_EM4STATE_EM4H : 0) + | (em4Init->pinRetentionMode); + + EMU->EM4CTRL = em4ctrl; +#endif +} +#endif + + +#if defined( BU_PRESENT ) +/***************************************************************************//** + * @brief + * Configure Backup Power Domain settings + * + * @param[in] bupdInit + * Backup power domain initialization structure + ******************************************************************************/ +void EMU_BUPDInit(EMU_BUPDInit_TypeDef *bupdInit) +{ + uint32_t reg; + + /* Set power connection configuration */ + reg = EMU->PWRCONF & ~(_EMU_PWRCONF_PWRRES_MASK + | _EMU_PWRCONF_VOUTSTRONG_MASK + | _EMU_PWRCONF_VOUTMED_MASK + | _EMU_PWRCONF_VOUTWEAK_MASK); + + reg |= bupdInit->resistor + | (bupdInit->voutStrong << _EMU_PWRCONF_VOUTSTRONG_SHIFT) + | (bupdInit->voutMed << _EMU_PWRCONF_VOUTMED_SHIFT) + | (bupdInit->voutWeak << _EMU_PWRCONF_VOUTWEAK_SHIFT); + + EMU->PWRCONF = reg; + + /* Set backup domain inactive mode configuration */ + reg = EMU->BUINACT & ~(_EMU_BUINACT_PWRCON_MASK); + reg |= (bupdInit->inactivePower); + EMU->BUINACT = reg; + + /* Set backup domain active mode configuration */ + reg = EMU->BUACT & ~(_EMU_BUACT_PWRCON_MASK); + reg |= (bupdInit->activePower); + EMU->BUACT = reg; + + /* Set power control configuration */ + reg = EMU->BUCTRL & ~(_EMU_BUCTRL_PROBE_MASK + | _EMU_BUCTRL_BODCAL_MASK + | _EMU_BUCTRL_STATEN_MASK + | _EMU_BUCTRL_EN_MASK); + + /* Note use of ->enable to both enable BUPD, use BU_VIN pin input and + release reset */ + reg |= bupdInit->probe + | (bupdInit->bodCal << _EMU_BUCTRL_BODCAL_SHIFT) + | (bupdInit->statusPinEnable << _EMU_BUCTRL_STATEN_SHIFT) + | (bupdInit->enable << _EMU_BUCTRL_EN_SHIFT); + + /* Enable configuration */ + EMU->BUCTRL = reg; + + /* If enable is true, enable BU_VIN input power pin, if not disable it */ + EMU_BUPinEnable(bupdInit->enable); + + /* If enable is true, release BU reset, if not keep reset asserted */ + BUS_RegBitWrite(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable); +} + + +/***************************************************************************//** + * @brief + * Configure Backup Power Domain BOD Threshold value + * @note + * These values are precalibrated + * @param[in] mode Active or Inactive mode + * @param[in] value + ******************************************************************************/ +void EMU_BUThresholdSet(EMU_BODMode_TypeDef mode, uint32_t value) +{ + EFM_ASSERT(value<8); + EFM_ASSERT(value<=(_EMU_BUACT_BUEXTHRES_MASK>>_EMU_BUACT_BUEXTHRES_SHIFT)); + + switch(mode) + { + case emuBODMode_Active: + EMU->BUACT = (EMU->BUACT & ~_EMU_BUACT_BUEXTHRES_MASK) + | (value<<_EMU_BUACT_BUEXTHRES_SHIFT); + break; + case emuBODMode_Inactive: + EMU->BUINACT = (EMU->BUINACT & ~_EMU_BUINACT_BUENTHRES_MASK) + | (value<<_EMU_BUINACT_BUENTHRES_SHIFT); + break; + } +} + + +/***************************************************************************//** + * @brief + * Configure Backup Power Domain BOD Threshold Range + * @note + * These values are precalibrated + * @param[in] mode Active or Inactive mode + * @param[in] value + ******************************************************************************/ +void EMU_BUThresRangeSet(EMU_BODMode_TypeDef mode, uint32_t value) +{ + EFM_ASSERT(value < 4); + EFM_ASSERT(value<=(_EMU_BUACT_BUEXRANGE_MASK>>_EMU_BUACT_BUEXRANGE_SHIFT)); + + switch(mode) + { + case emuBODMode_Active: + EMU->BUACT = (EMU->BUACT & ~_EMU_BUACT_BUEXRANGE_MASK) + | (value<<_EMU_BUACT_BUEXRANGE_SHIFT); + break; + case emuBODMode_Inactive: + EMU->BUINACT = (EMU->BUINACT & ~_EMU_BUINACT_BUENRANGE_MASK) + | (value<<_EMU_BUINACT_BUENRANGE_SHIFT); + break; + } +} +#endif + + +#if defined( _EMU_DCDCCTRL_MASK ) + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/***************************************************************************//** + * @brief + * Load DCDC calibration constants from DI page. Const means calibration + * data that does not change depending on other configuration parameters. + * + * @return + * False if calibration registers are locked + ******************************************************************************/ +static bool ConstCalibrationLoad(void) +{ + uint32_t val; + volatile uint32_t *reg; + + /* DI calib data in flash */ + volatile uint32_t* const diCal_EMU_DCDCLNFREQCTRL = (volatile uint32_t *)(0x0FE08038); + volatile uint32_t* const diCal_EMU_DCDCLNVCTRL = (volatile uint32_t *)(0x0FE08040); + volatile uint32_t* const diCal_EMU_DCDCLPCTRL = (volatile uint32_t *)(0x0FE08048); + volatile uint32_t* const diCal_EMU_DCDCLPVCTRL = (volatile uint32_t *)(0x0FE08050); + volatile uint32_t* const diCal_EMU_DCDCTRIM0 = (volatile uint32_t *)(0x0FE08058); + volatile uint32_t* const diCal_EMU_DCDCTRIM1 = (volatile uint32_t *)(0x0FE08060); + + if (DEVINFO->DCDCLPVCTRL0 != UINT_MAX) + { + val = *(diCal_EMU_DCDCLNFREQCTRL + 1); + reg = (volatile uint32_t *)*diCal_EMU_DCDCLNFREQCTRL; + *reg = val; + + val = *(diCal_EMU_DCDCLNVCTRL + 1); + reg = (volatile uint32_t *)*diCal_EMU_DCDCLNVCTRL; + *reg = val; + + val = *(diCal_EMU_DCDCLPCTRL + 1); + reg = (volatile uint32_t *)*diCal_EMU_DCDCLPCTRL; + *reg = val; + + val = *(diCal_EMU_DCDCLPVCTRL + 1); + reg = (volatile uint32_t *)*diCal_EMU_DCDCLPVCTRL; + *reg = val; + + val = *(diCal_EMU_DCDCTRIM0 + 1); + reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM0; + *reg = val; + + val = *(diCal_EMU_DCDCTRIM1 + 1); + reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM1; + *reg = val; + + return true; + } + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; +} + + +/***************************************************************************//** + * @brief + * Set recommended and validated current optimization settings + * + ******************************************************************************/ +void ValidatedConfigSet(void) +{ +#define EMU_DCDCSMCTRL (* (volatile uint32_t *)(EMU_BASE + 0x44)) + + uint32_t dcdcTiming; + SYSTEM_PartFamily_TypeDef family; + SYSTEM_ChipRevision_TypeDef rev; + + /* Enable duty cycling of the bias */ + EMU->DCDCLPCTRL |= EMU_DCDCLPCTRL_LPVREFDUTYEN; + + /* Set low-noise RCO for EFM32 and EFR32 */ +#if defined( _EFR_DEVICE ) + /* 7MHz is recommended for all EFR32 parts with DCDC */ + EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK) + | (EMU_DcdcLnRcoBand_7MHz << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT); +#else + /* 3MHz is recommended for all EFM32 parts with DCDC */ + EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK) + | (EMU_DcdcLnRcoBand_3MHz << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT); +#endif + + EMU->DCDCTIMING &= ~_EMU_DCDCTIMING_DUTYSCALE_MASK; + + family = SYSTEM_GetFamily(); + SYSTEM_ChipRevisionGet(&rev); + if ((((family >= systemPartFamilyMighty1P) + && (family <= systemPartFamilyFlex1V)) + || (family == systemPartFamilyEfm32Pearl1B) + || (family == systemPartFamilyEfm32Jade1B)) + && ((rev.major == 1) && (rev.minor < 3)) + && (errataFixDcdcHsState == errataFixDcdcHsInit)) + { + /* LPCMPWAITDIS = 1 */ + EMU_DCDCSMCTRL |= 1; + + dcdcTiming = EMU->DCDCTIMING; + dcdcTiming &= ~(_EMU_DCDCTIMING_LPINITWAIT_MASK + |_EMU_DCDCTIMING_LNWAIT_MASK + |_EMU_DCDCTIMING_BYPWAIT_MASK); + + dcdcTiming |= ((180 << _EMU_DCDCTIMING_LPINITWAIT_SHIFT) + | (12 << _EMU_DCDCTIMING_LNWAIT_SHIFT) + | (180 << _EMU_DCDCTIMING_BYPWAIT_SHIFT)); + EMU->DCDCTIMING = dcdcTiming; + + errataFixDcdcHsState = errataFixDcdcHsTrimSet; + } +} + + +/***************************************************************************//** + * @brief + * Calculate and update EMU->DCDCMISCCTRL for maximum DCDC current based + * on the slice configuration and user set maximum. + ******************************************************************************/ +static void maxCurrentUpdate(void) +{ + uint32_t lncLimImSel; + uint32_t lpcLimImSel; + uint32_t pFetCnt; + + pFetCnt = (EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_PFETCNT_MASK) + >> _EMU_DCDCMISCCTRL_PFETCNT_SHIFT; + + /* Equation from Reference Manual section 11.5.20, in the register + field description for LNCLIMILIMSEL and LPCLIMILIMSEL. */ + lncLimImSel = (dcdcMaxCurrent_mA / (5 * (pFetCnt + 1))) - 1; + /* 80mA as recommended in Application Note AN0948 */ + lpcLimImSel = (80 / (5 * (pFetCnt + 1))) - 1; + + lncLimImSel <<= _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_SHIFT; + lpcLimImSel <<= _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_SHIFT; + EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK + | _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK)) + | (lncLimImSel | lpcLimImSel); +} + + +/***************************************************************************//** + * @brief + * Set static variable that holds the user set maximum current. Update + * DCDC configuration. + * + * @param[in] mAmaxCurrent + * Maximum allowed current drawn by the DCDC from VREGVDD in mA. + ******************************************************************************/ +static void maxCurrentSet(uint32_t mAmaxCurrent) +{ + dcdcMaxCurrent_mA = mAmaxCurrent; + maxCurrentUpdate(); +} + + +/***************************************************************************//** + * @brief + * Load EMU_DCDCLPCTRL_LPCMPHYSSEL depending on LP bias, LP feedback + * attenuation and DEVINFOREV. + * + * @param[in] attSet + * LP feedback attenuation. + * @param[in] lpCmpBias + * lpCmpBias selection + ******************************************************************************/ +static bool LpCmpHystCalibrationLoad(bool lpAttenuation, uint32_t lpCmpBias) +{ + uint8_t devinfoRev; + uint32_t lpcmpHystSel; + + /* Get calib data revision */ + devinfoRev = SYSTEM_GetDevinfoRev(); + + /* Load LPATT indexed calibration data */ + if (devinfoRev < 4) + { + lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL0; + + if (lpAttenuation) + { + lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT1_MASK) + >> _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT1_SHIFT; + } + else + { + lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT0_MASK) + >> _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT0_SHIFT; + } + } + /* devinfoRev >= 4 + Load LPCMPBIAS indexed calibration data */ + else + { + lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL1; + switch (lpCmpBias) + { + case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0: + lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_MASK) + >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_SHIFT; + break; + + case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1: + lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_MASK) + >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_SHIFT; + break; + + case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS2: + lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_MASK) + >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_SHIFT; + break; + + case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS3: + lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_MASK) + >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_SHIFT; + break; + + default: + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + } + + /* Make sure the sel value is within the field range. */ + lpcmpHystSel <<= _EMU_DCDCLPCTRL_LPCMPHYSSEL_SHIFT; + if (lpcmpHystSel & ~_EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + EMU->DCDCLPCTRL = (EMU->DCDCLPCTRL & ~_EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK) | lpcmpHystSel; + + return true; +} + + +/** @endcond */ + +/***************************************************************************//** + * @brief + * Set DCDC regulator operating mode + * + * @param[in] dcdcMode + * DCDC mode + ******************************************************************************/ +void EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode) +{ + while(EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY); + BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, dcdcMode == emuDcdcMode_Bypass ? 0 : 1); + EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | dcdcMode; +} + + +/***************************************************************************//** + * @brief + * Configure DCDC regulator + * + * @note + * Use the function EMU_DCDCPowerDown() to if the power circuit is configured + * for NODCDC as decribed in Section 11.3.4.3 in the Reference Manual. + * + * @param[in] dcdcInit + * DCDC initialization structure + * + * @return + * True if initialization parameters are valid + ******************************************************************************/ +bool EMU_DCDCInit(EMU_DCDCInit_TypeDef *dcdcInit) +{ + uint32_t lpCmpBiasSel; + + /* Set external power configuration. This enables writing to the other + DCDC registers. */ + EMU->PWRCFG = dcdcInit->powerConfig; + + /* EMU->PWRCFG is write-once and POR reset only. Check that + we could set the desired power configuration. */ + if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != dcdcInit->powerConfig) + { + /* If this assert triggers unexpectedly, please power cycle the + kit to reset the power configuration. */ + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + + /* Load DCDC calibration data from the DI page */ + ConstCalibrationLoad(); + + /* Check current parameters */ + EFM_ASSERT(dcdcInit->maxCurrent_mA <= 200); + EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= dcdcInit->maxCurrent_mA); + + /* DCDC low-noise supports max 200mA */ + if (dcdcInit->dcdcMode == emuDcdcMode_LowNoise) + { + EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 200); + } + + /* EM2, 3 and 4 current above 100uA is not supported */ + EFM_ASSERT(dcdcInit->em234LoadCurrent_uA <= 100); + + /* Decode LP comparator bias for EM0/1 and EM2/3 */ + lpCmpBiasSel = EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1; + if (dcdcInit->em234LoadCurrent_uA <= 10) + { + lpCmpBiasSel = EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0; + } + + /* Set DCDC low-power mode comparator bias selection */ + EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LPCMPBIAS_MASK + | _EMU_DCDCMISCCTRL_LNFORCECCM_MASK)) + | ((uint32_t)lpCmpBiasSel + | (uint32_t)dcdcInit->lnTransientMode); + + /* Set recommended and validated current optimization settings */ + ValidatedConfigSet(); + + /* Set the maximum current that the DCDC can draw from the power source */ + maxCurrentSet(dcdcInit->maxCurrent_mA); + + /* Optimize LN slice based on given load current estimate */ + EMU_DCDCOptimizeSlice(dcdcInit->em01LoadCurrent_mA); + + /* Set DCDC output voltage */ + dcdcOutput_mVout = dcdcInit->mVout; + if (!EMU_DCDCOutputVoltageSet(dcdcOutput_mVout, true, true)) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + + /* Set EM0 DCDC operating mode. Output voltage set in EMU_DCDCOutputVoltageSet() + above takes effect if mode is changed from bypass here. */ + EMU_DCDCModeSet(dcdcInit->dcdcMode); + + /* Select analog peripheral power supply */ + BUS_RegBitWrite(&EMU->PWRCTRL, _EMU_PWRCTRL_ANASW_SHIFT, dcdcInit->anaPeripheralPower ? 1 : 0); + + return true; +} + + +/***************************************************************************//** + * @brief + * Set DCDC output voltage + * + * @param[in] mV + * Target DCDC output voltage in mV + * + * @return + * True if the mV parameter is valid + ******************************************************************************/ +bool EMU_DCDCOutputVoltageSet(uint32_t mV, + bool setLpVoltage, + bool setLnVoltage) +{ +#if defined( _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK ) + + bool validOutVoltage; + uint8_t lnMode; + bool attSet; + uint32_t attMask; + uint32_t vrefLow = 0; + uint32_t vrefHigh = 0; + uint32_t vrefVal = 0; + uint32_t mVlow = 0; + uint32_t mVhigh = 0; + uint32_t vrefShift; + uint32_t lpcmpBias; + volatile uint32_t* ctrlReg; + + /* Check that the set voltage is within valid range. + Voltages are obtained from the datasheet. */ + validOutVoltage = false; + if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) == EMU_PWRCFG_PWRCFG_DCDCTODVDD) + { + validOutVoltage = ((mV >= PWRCFG_DCDCTODVDD_VMIN) + && (mV <= PWRCFG_DCDCTODVDD_VMAX)); + } + + if (!validOutVoltage) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + + /* Populate both LP and LN registers, set control reg pointer and VREF shift. */ + for (lnMode = 0; lnMode <= 1; lnMode++) + { + if (((lnMode == 0) && !setLpVoltage) + || ((lnMode == 1) && !setLnVoltage)) + { + continue; + } + + ctrlReg = (lnMode ? &EMU->DCDCLNVCTRL : &EMU->DCDCLPVCTRL); + vrefShift = (lnMode ? _EMU_DCDCLNVCTRL_LNVREF_SHIFT + : _EMU_DCDCLPVCTRL_LPVREF_SHIFT); + + /* Set attenuation to use */ + attSet = (mV > 1800); + if (attSet) + { + mVlow = 1800; + mVhigh = 3000; + attMask = (lnMode ? EMU_DCDCLNVCTRL_LNATT : EMU_DCDCLPVCTRL_LPATT); + } + else + { + mVlow = 1200; + mVhigh = 1800; + attMask = 0; + } + + /* Get 2-point calib data from DEVINFO, calculate trimming and set voltege */ + if (lnMode) + { + /* Set low-noise DCDC output voltage tuning */ + if (attSet) + { + vrefLow = DEVINFO->DCDCLNVCTRL0; + vrefHigh = (vrefLow & _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK) + >> _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_MASK) + >> _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_SHIFT; + } + else + { + vrefLow = DEVINFO->DCDCLNVCTRL0; + vrefHigh = (vrefLow & _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_MASK) + >> _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_MASK) + >> _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_SHIFT; + } + } + else + { + /* Set low-power DCDC output voltage tuning */ + + /* Get LPCMPBIAS and make sure masks are not overlayed */ + lpcmpBias = EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LPCMPBIAS_MASK; + EFM_ASSERT(!(_EMU_DCDCMISCCTRL_LPCMPBIAS_MASK & attMask)); + switch (attMask | lpcmpBias) + { + case EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0: + vrefLow = DEVINFO->DCDCLPVCTRL2; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS0_MASK) + >> _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS0_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS0_MASK) + >> _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS0_SHIFT; + break; + + case EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1: + vrefLow = DEVINFO->DCDCLPVCTRL2; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS1_MASK) + >> _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS1_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS1_MASK) + >> _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS1_SHIFT; + break; + + case EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS2: + vrefLow = DEVINFO->DCDCLPVCTRL3; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS2_MASK) + >> _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS2_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS2_MASK) + >> _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS2_SHIFT; + break; + + case EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS3: + vrefLow = DEVINFO->DCDCLPVCTRL3; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS3_MASK) + >> _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS3_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS3_MASK) + >> _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS3_SHIFT; + break; + + case EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0: + vrefLow = DEVINFO->DCDCLPVCTRL0; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS0_MASK) + >> _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS0_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS0_MASK) + >> _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS0_SHIFT; + break; + + case EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1: + vrefLow = DEVINFO->DCDCLPVCTRL0; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS1_MASK) + >> _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS1_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS1_MASK) + >> _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS1_SHIFT; + break; + + case EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS2: + vrefLow = DEVINFO->DCDCLPVCTRL1; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS2_MASK) + >> _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS2_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS2_MASK) + >> _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS2_SHIFT; + break; + + case EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS3: + vrefLow = DEVINFO->DCDCLPVCTRL1; + vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS3_MASK) + >> _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS3_SHIFT; + vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS3_MASK) + >> _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS3_SHIFT; + break; + + default: + EFM_ASSERT(false); + break; + } + + /* Load LP comparator hysteresis calibration */ + if(!(LpCmpHystCalibrationLoad(attSet, lpcmpBias >> _EMU_DCDCMISCCTRL_LPCMPBIAS_SHIFT))) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + } /* Low-nise / low-power mode */ + + + /* Check for valid 2-point trim values */ + if ((vrefLow == 0xFF) && (vrefHigh == 0xFF)) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + + /* Calculate and set voltage trim */ + vrefVal = ((mV - mVlow) * (vrefHigh - vrefLow)) / (mVhigh - mVlow); + vrefVal += vrefLow; + + /* Range check */ + if ((vrefVal > vrefHigh) || (vrefVal < vrefLow)) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + + /* Update DCDCLNVCTRL/DCDCLPVCTRL */ + *ctrlReg = (vrefVal << vrefShift) | attMask; + } +#endif + return true; +} + + +/***************************************************************************//** + * @brief + * Optimize DCDC slice count based on the estimated average load current + * in EM0 + * + * @param[in] mAEm0LoadCurrent + * Estimated average EM0 load current in mA. + ******************************************************************************/ +void EMU_DCDCOptimizeSlice(uint32_t mAEm0LoadCurrent) +{ + uint32_t sliceCount = 0; + uint32_t rcoBand = (EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK) + >> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT; + + /* Set recommended slice count */ + if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand >= EMU_DcdcLnRcoBand_5MHz)) + { + if (mAEm0LoadCurrent < 20) + { + sliceCount = 4; + } + else if ((mAEm0LoadCurrent >= 20) && (mAEm0LoadCurrent < 40)) + { + sliceCount = 8; + } + else + { + sliceCount = 16; + } + } + else if ((!(EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK)) && (rcoBand <= EMU_DcdcLnRcoBand_4MHz)) + { + if (mAEm0LoadCurrent < 10) + { + sliceCount = 4; + } + else if ((mAEm0LoadCurrent >= 10) && (mAEm0LoadCurrent < 20)) + { + sliceCount = 8; + } + else + { + sliceCount = 16; + } + } + else if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand <= EMU_DcdcLnRcoBand_4MHz)) + { + if (mAEm0LoadCurrent < 40) + { + sliceCount = 8; + } + else + { + sliceCount = 16; + } + } + else + { + /* This configuration is not recommended. EMU_DCDCInit() applies a recommended + configuration. */ + EFM_ASSERT(false); + } + + /* The selected silices are PSLICESEL + 1 */ + sliceCount--; + + /* Apply slice count to both N and P slice */ + sliceCount = (sliceCount << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT + | sliceCount << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT); + EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK + | _EMU_DCDCMISCCTRL_NFETCNT_MASK)) + | sliceCount; + + /* Update current limit configuration as it depends on the slice configuration. */ + maxCurrentUpdate(); +} + +/***************************************************************************//** + * @brief + * Set DCDC Low-noise RCO band. + * + * @param[in] band + * RCO band to set. + ******************************************************************************/ +void EMU_DCDCLnRcoBandSet(EMU_DcdcLnRcoBand_TypeDef band) +{ + EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK) + | (band << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT); +} + +/***************************************************************************//** + * @brief + * Power off the DCDC regulator. + * + * @details + * This function powers off the DCDC controller. This function should only be + * used if the external power circuit is wired for no DCDC. If the external power + * circuit is wired for DCDC usage, then use EMU_DCDCInit() and set the + * DCDC in bypass mode to disable DCDC. + * + * @return + * Return false if the DCDC could not be disabled. + ******************************************************************************/ +bool EMU_DCDCPowerOff(void) +{ + /* Set power configuration to hard bypass */ + EMU->PWRCFG = 0xF; + if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != 0xF) + { + EFM_ASSERT(false); + /* Return when assertions are disabled */ + return false; + } + + /* Set DCDC to OFF and disable LP in EM2/3/4 */ + EMU->DCDCCTRL = EMU_DCDCCTRL_DCDCMODE_OFF; + return true; +} +#endif + + +#if defined( EMU_STATUS_VMONRDY ) +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +__STATIC_INLINE uint32_t vmonMilliVoltToCoarseThreshold(int mV) +{ + return (mV - 1200) / 200; +} + +__STATIC_INLINE uint32_t vmonMilliVoltToFineThreshold(int mV, uint32_t coarseThreshold) +{ + return (mV - 1200 - (coarseThreshold * 200)) / 20; +} +/** @endcond */ + +/***************************************************************************//** + * @brief + * Initialize VMON channel. + * + * @details + * Initialize a VMON channel without hysteresis. If the channel supports + * separate rise and fall triggers, both thresholds will be set to the same + * value. + * + * @param[in] vmonInit + * VMON initialization struct + ******************************************************************************/ +void EMU_VmonInit(EMU_VmonInit_TypeDef *vmonInit) +{ + uint32_t thresholdCoarse, thresholdFine; + EFM_ASSERT((vmonInit->threshold >= 1200) && (vmonInit->threshold <= 3980)); + + thresholdCoarse = vmonMilliVoltToCoarseThreshold(vmonInit->threshold); + thresholdFine = vmonMilliVoltToFineThreshold(vmonInit->threshold, thresholdCoarse); + + switch(vmonInit->channel) + { + case emuVmonChannel_AVDD: + EMU->VMONAVDDCTRL = (thresholdCoarse << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT) + | (thresholdFine << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT) + | (thresholdCoarse << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT) + | (thresholdFine << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT) + | (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0) + | (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0) + | (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0); + break; + case emuVmonChannel_ALTAVDD: + EMU->VMONALTAVDDCTRL = (thresholdCoarse << _EMU_VMONALTAVDDCTRL_THRESCOARSE_SHIFT) + | (thresholdFine << _EMU_VMONALTAVDDCTRL_THRESFINE_SHIFT) + | (vmonInit->riseWakeup ? EMU_VMONALTAVDDCTRL_RISEWU : 0) + | (vmonInit->fallWakeup ? EMU_VMONALTAVDDCTRL_FALLWU : 0) + | (vmonInit->enable ? EMU_VMONALTAVDDCTRL_EN : 0); + break; + case emuVmonChannel_DVDD: + EMU->VMONDVDDCTRL = (thresholdCoarse << _EMU_VMONDVDDCTRL_THRESCOARSE_SHIFT) + | (thresholdFine << _EMU_VMONDVDDCTRL_THRESFINE_SHIFT) + | (vmonInit->riseWakeup ? EMU_VMONDVDDCTRL_RISEWU : 0) + | (vmonInit->fallWakeup ? EMU_VMONDVDDCTRL_FALLWU : 0) + | (vmonInit->enable ? EMU_VMONDVDDCTRL_EN : 0); + break; + case emuVmonChannel_IOVDD0: + EMU->VMONIO0CTRL = (thresholdCoarse << _EMU_VMONIO0CTRL_THRESCOARSE_SHIFT) + | (thresholdFine << _EMU_VMONIO0CTRL_THRESFINE_SHIFT) + | (vmonInit->retDisable ? EMU_VMONIO0CTRL_RETDIS : 0) + | (vmonInit->riseWakeup ? EMU_VMONIO0CTRL_RISEWU : 0) + | (vmonInit->fallWakeup ? EMU_VMONIO0CTRL_FALLWU : 0) + | (vmonInit->enable ? EMU_VMONIO0CTRL_EN : 0); + break; + default: + EFM_ASSERT(false); + return; + } +} + +/***************************************************************************//** + * @brief + * Initialize VMON channel with hysteresis (separate rise and fall triggers). + * + * @details + * Initialize a VMON channel which supports hysteresis. The AVDD channel is + * the only channel to support separate rise and fall triggers. + * + * @param[in] vmonInit + * VMON Hysteresis initialization struct + ******************************************************************************/ +void EMU_VmonHystInit(EMU_VmonHystInit_TypeDef *vmonInit) +{ + uint32_t riseThresholdCoarse, riseThresholdFine, fallThresholdCoarse, fallThresholdFine; + /* VMON supports voltages between 1200 mV and 3980 mV (inclusive) in 20 mV increments */ + EFM_ASSERT((vmonInit->riseThreshold >= 1200) && (vmonInit->riseThreshold < 4000)); + EFM_ASSERT((vmonInit->fallThreshold >= 1200) && (vmonInit->fallThreshold < 4000)); + /* Fall threshold has to be lower than rise threshold */ + EFM_ASSERT(vmonInit->fallThreshold <= vmonInit->riseThreshold); + + riseThresholdCoarse = vmonMilliVoltToCoarseThreshold(vmonInit->riseThreshold); + riseThresholdFine = vmonMilliVoltToFineThreshold(vmonInit->riseThreshold, riseThresholdCoarse); + fallThresholdCoarse = vmonMilliVoltToCoarseThreshold(vmonInit->fallThreshold); + fallThresholdFine = vmonMilliVoltToFineThreshold(vmonInit->fallThreshold, fallThresholdCoarse); + + switch(vmonInit->channel) + { + case emuVmonChannel_AVDD: + EMU->VMONAVDDCTRL = (riseThresholdCoarse << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT) + | (riseThresholdFine << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT) + | (fallThresholdCoarse << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT) + | (fallThresholdFine << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT) + | (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0) + | (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0) + | (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0); + break; + default: + EFM_ASSERT(false); + return; + } +} + +/***************************************************************************//** + * @brief + * Enable or disable a VMON channel + * + * @param[in] channel + * VMON channel to enable/disable + * + * @param[in] enable + * Whether to enable or disable + ******************************************************************************/ +void EMU_VmonEnable(EMU_VmonChannel_TypeDef channel, bool enable) +{ + uint32_t volatile * reg; + uint32_t bit; + + switch(channel) + { + case emuVmonChannel_AVDD: + reg = &(EMU->VMONAVDDCTRL); + bit = _EMU_VMONAVDDCTRL_EN_SHIFT; + break; + case emuVmonChannel_ALTAVDD: + reg = &(EMU->VMONALTAVDDCTRL); + bit = _EMU_VMONALTAVDDCTRL_EN_SHIFT; + break; + case emuVmonChannel_DVDD: + reg = &(EMU->VMONDVDDCTRL); + bit = _EMU_VMONDVDDCTRL_EN_SHIFT; + break; + case emuVmonChannel_IOVDD0: + reg = &(EMU->VMONIO0CTRL); + bit = _EMU_VMONIO0CTRL_EN_SHIFT; + break; + default: + EFM_ASSERT(false); + return; + } + + BUS_RegBitWrite(reg, bit, enable); +} + +/***************************************************************************//** + * @brief + * Get the status of a voltage monitor channel. + * + * @param[in] channel + * VMON channel to get status for + * + * @return + * Status of the selected VMON channel. True if channel is triggered. + ******************************************************************************/ +bool EMU_VmonChannelStatusGet(EMU_VmonChannel_TypeDef channel) +{ + uint32_t bit; + switch(channel) + { + case emuVmonChannel_AVDD: + bit = _EMU_STATUS_VMONAVDD_SHIFT; + break; + case emuVmonChannel_ALTAVDD: + bit = _EMU_STATUS_VMONALTAVDD_SHIFT; + break; + case emuVmonChannel_DVDD: + bit = _EMU_STATUS_VMONDVDD_SHIFT; + break; + case emuVmonChannel_IOVDD0: + bit = _EMU_STATUS_VMONIO0_SHIFT; + break; + default: + EFM_ASSERT(false); + bit = 0; + } + + return BUS_RegBitRead(&EMU->STATUS, bit); +} +#endif /* EMU_STATUS_VMONRDY */ + +/** @} (end addtogroup EMU) */ +/** @} (end addtogroup EM_Library) */ +#endif /* __EM_EMU_H */ diff --git a/cpu/efm32_common/emlib/src/em_gpio.c b/cpu/efm32_common/emlib/src/em_gpio.c new file mode 100644 index 0000000000000..46db33557428d --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_gpio.c @@ -0,0 +1,320 @@ +/***************************************************************************//** + * @file em_gpio.c + * @brief General Purpose IO (GPIO) peripheral API + * devices. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + + +#include "em_gpio.h" + +#if defined(GPIO_COUNT) && (GPIO_COUNT > 0) + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup GPIO + * @brief General Purpose Input/Output (GPIO) API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of pin typically usable in assert statements. */ +#define GPIO_DRIVEMODE_VALID(mode) ((mode) <= 3) +#define GPIO_STRENGHT_VALID(strenght) (!((strenght) & \ + ~(_GPIO_P_CTRL_DRIVESTRENGTH_MASK \ + | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK))) +/** @endcond */ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Sets the pin location of the debug pins (Serial Wire interface). + * + * @note + * Changing the pins used for debugging uncontrolled, may result in a lockout. + * + * @param[in] location + * The debug pin location to use (0-3). + ******************************************************************************/ +void GPIO_DbgLocationSet(unsigned int location) +{ +#if defined ( _GPIO_ROUTE_SWLOCATION_MASK ) + EFM_ASSERT(location < AFCHANLOC_MAX); + + GPIO->ROUTE = (GPIO->ROUTE & ~_GPIO_ROUTE_SWLOCATION_MASK) | + (location << _GPIO_ROUTE_SWLOCATION_SHIFT); +#else + (void)location; +#endif +} + +#if defined (_GPIO_P_CTRL_DRIVEMODE_MASK) +/***************************************************************************//** + * @brief + * Sets the drive mode for a GPIO port. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] mode + * Drive mode to use for port. + ******************************************************************************/ +void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode) +{ + EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_DRIVEMODE_VALID(mode)); + + GPIO->P[port].CTRL = (GPIO->P[port].CTRL & ~(_GPIO_P_CTRL_DRIVEMODE_MASK)) + | (mode << _GPIO_P_CTRL_DRIVEMODE_SHIFT); +} +#endif + + +#if defined (_GPIO_P_CTRL_DRIVESTRENGTH_MASK) +/***************************************************************************//** + * @brief + * Sets the drive strength for a GPIO port. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] strength + * Drive strength to use for port. + ******************************************************************************/ +void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port, + GPIO_DriveStrength_TypeDef strength) +{ + EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_STRENGHT_VALID(strength)); + BUS_RegMaskedWrite(&GPIO->P[port].CTRL, + _GPIO_P_CTRL_DRIVESTRENGTH_MASK | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK, + strength); +} +#endif + +/***************************************************************************//** + * @brief + * Configure GPIO interrupt. + * + * @details + * If reconfiguring a GPIO interrupt that is already enabled, it is generally + * recommended to disable it first, see GPIO_Disable(). + * + * The actual GPIO interrupt handler must be in place before enabling the + * interrupt. + * + * Notice that any pending interrupt for the selected pin is cleared by this + * function. + * + * @note + * A certain pin number can only be associated with one port. Ie, if GPIO + * interrupt 1 is assigned to port A/pin 1, then it is not possibly to use + * pin 1 from any other ports for interrupts. Please refer to the reference + * manual. + * + * @param[in] port + * The port to associate with @p pin. + * + * @param[in] pin + * The GPIO interrupt number (= port pin). + * + * @param[in] risingEdge + * Set to true if interrupts shall be enabled on rising edge, otherwise false. + * + * @param[in] fallingEdge + * Set to true if interrupts shall be enabled on falling edge, otherwise false. + * + * @param[in] enable + * Set to true if interrupt shall be enabled after configuration completed, + * false to leave disabled. See GPIO_IntDisable() and GPIO_IntEnable(). + ******************************************************************************/ +void GPIO_IntConfig(GPIO_Port_TypeDef port, + unsigned int pin, + bool risingEdge, + bool fallingEdge, + bool enable) +{ + uint32_t tmp; + + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); + + /* There are two registers controlling the interrupt configuration: + * The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls + * pins 8-15. */ + if (pin < 8) + { + BUS_RegMaskedWrite(&GPIO->EXTIPSELL, + 0xF << (4 * pin), + port << (4 * pin)); + } + else + { + tmp = pin - 8; + BUS_RegMaskedWrite(&GPIO->EXTIPSELH, + 0xF << (4 * tmp), + port << (4 * tmp)); + } + + /* Enable/disable rising edge */ + BUS_RegBitWrite(&(GPIO->EXTIRISE), pin, risingEdge); + + /* Enable/disable falling edge */ + BUS_RegBitWrite(&(GPIO->EXTIFALL), pin, fallingEdge); + + /* Clear any pending interrupt */ + GPIO->IFC = 1 << pin; + + /* Finally enable/disable interrupt */ + BUS_RegBitWrite(&(GPIO->IEN), pin, enable); +} + + +/***************************************************************************//** + * @brief + * Set the mode for a GPIO pin. + * + * @param[in] port + * The GPIO port to access. + * + * @param[in] pin + * The pin number in the port. + * + * @param[in] mode + * The desired pin mode. + * + * @param[in] out + * Value to set for pin in DOUT register. The DOUT setting is important for + * even some input mode configurations, determining pull-up/down direction. + ******************************************************************************/ +void GPIO_PinModeSet(GPIO_Port_TypeDef port, + unsigned int pin, + GPIO_Mode_TypeDef mode, + unsigned int out) +{ + EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin)); + + /* If disabling pin, do not modify DOUT in order to reduce chance for */ + /* glitch/spike (may not be sufficient precaution in all use cases) */ + if (mode != gpioModeDisabled) + { + if (out) + { + GPIO_PinOutSet(port, pin); + } + else + { + GPIO_PinOutClear(port, pin); + } + } + + /* There are two registers controlling the pins for each port. The MODEL + * register controls pins 0-7 and MODEH controls pins 8-15. */ + if (pin < 8) + { + BUS_RegMaskedWrite(&GPIO->P[port].MODEL, + 0xF << (pin * 4), + mode << (pin * 4)); + } + else + { + BUS_RegMaskedWrite(&GPIO->P[port].MODEH, + 0xF << ((pin - 8) * 4), + mode << ((pin - 8) * 4)); + } + + if (mode == gpioModeDisabled) + { + if (out) + { + GPIO_PinOutSet(port, pin); + } + else + { + GPIO_PinOutClear(port, pin); + } + } +} + +#if defined( _GPIO_EM4WUEN_MASK ) +/**************************************************************************//** + * @brief + * Enable GPIO pin wake-up from EM4. When the function exits, + * EM4 mode can be safely entered. + * + * @note + * It is assumed that the GPIO pin modes are set correctly. + * Valid modes are @ref gpioModeInput and @ref gpioModeInputPull. + * + * @param[in] pinmask + * Bitmask containing the bitwise logic OR of which GPIO pin(s) to enable. + * Refer to Reference Manuals for pinmask to GPIO port/pin mapping. + * @param[in] polaritymask + * Bitmask containing the bitwise logic OR of GPIO pin(s) wake-up polarity. + * Refer to Reference Manuals for pinmask to GPIO port/pin mapping. + *****************************************************************************/ +void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask) +{ + EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0); + +#if defined( _GPIO_EM4WUPOL_MASK ) + EFM_ASSERT((polaritymask & ~_GPIO_EM4WUPOL_MASK) == 0); + GPIO->EM4WUPOL &= ~pinmask; /* Set wakeup polarity */ + GPIO->EM4WUPOL |= pinmask & polaritymask; +#elif defined( _GPIO_EXTILEVEL_MASK ) + EFM_ASSERT((polaritymask & ~_GPIO_EXTILEVEL_MASK) == 0); + GPIO->EXTILEVEL &= ~pinmask; + GPIO->EXTILEVEL |= pinmask & polaritymask; +#endif + GPIO->EM4WUEN |= pinmask; /* Enable wakeup */ + + GPIO_EM4SetPinRetention(true); /* Enable pin retention */ + +#if defined( _GPIO_CMD_EM4WUCLR_MASK ) + GPIO->CMD = GPIO_CMD_EM4WUCLR; /* Clear wake-up logic */ +#elif defined( _GPIO_IFC_EM4WU_MASK ) + GPIO_IntClear(pinmask); +#endif +} +#endif + +/** @} (end addtogroup GPIO) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_i2c.c b/cpu/efm32_common/emlib/src/em_i2c.c new file mode 100644 index 0000000000000..9a55ec2a1ee63 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_i2c.c @@ -0,0 +1,874 @@ +/***************************************************************************//** + * @file em_i2c.c + * @brief Inter-integrated Circuit (I2C) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_i2c.h" +#if defined(I2C_COUNT) && (I2C_COUNT > 0) + +#include "em_cmu.h" +#include "em_bus.h" +#include "em_assert.h" + + #include + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup I2C + * @brief Inter-integrated Circuit (I2C) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of I2C register block pointer reference for assert statements. */ +#if (I2C_COUNT == 1) +#define I2C_REF_VALID(ref) ((ref) == I2C0) +#elif (I2C_COUNT == 2) +#define I2C_REF_VALID(ref) ((ref == I2C0) || (ref == I2C1)) +#elif (I2C_COUNT == 3) +#define I2C_REF_VALID(ref) ((ref == I2C0) || (ref == I2C1)|| (ref == I2C2)) +#endif + +/** Error flags indicating I2C transfer has failed somehow. */ +/* Notice that I2C_IF_TXOF (transmit overflow) is not really possible with */ +/* this SW supporting master mode. Likewise for I2C_IF_RXUF (receive underflow) */ +/* RXUF is only likely to occur with this SW if using a debugger peeking into */ +/* RXDATA register. Thus, we ignore those types of fault. */ +#define I2C_IF_ERRORS (I2C_IF_BUSERR | I2C_IF_ARBLOST) + +/* Max I2C transmission rate constant */ +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) +#define I2C_CR_MAX 4 +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) +#define I2C_CR_MAX 8 +#else +#warning "Max I2C transmission rate constant is not defined" +#endif + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Master mode transfer states. */ +typedef enum +{ + i2cStateStartAddrSend, /**< Send start + (first part of) address. */ + i2cStateAddrWFAckNack, /**< Wait for ACK/NACK on (first part of) address. */ + i2cStateAddrWF2ndAckNack, /**< Wait for ACK/NACK on second part of 10 bit address. */ + i2cStateRStartAddrSend, /**< Send repeated start + (first part of) address. */ + i2cStateRAddrWFAckNack, /**< Wait for ACK/NACK on address sent after repeated start. */ + i2cStateDataSend, /**< Send data. */ + i2cStateDataWFAckNack, /**< Wait for ACK/NACK on data sent. */ + i2cStateWFData, /**< Wait for data. */ + i2cStateWFStopSent, /**< Wait for STOP to have been transmitted. */ + i2cStateDone /**< Transfer completed successfully. */ +} I2C_TransferState_TypeDef; + +/** @endcond */ + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Structure used to store state information on an ongoing master mode transfer. */ +typedef struct +{ + /** Current state. */ + I2C_TransferState_TypeDef state; + + /** Result return code. */ + I2C_TransferReturn_TypeDef result; + + /** Offset in current sequence buffer. */ + uint16_t offset; + + /* Index to current sequence buffer in use. */ + uint8_t bufIndx; + + /** Reference to I2C transfer sequence definition provided by user. */ + I2C_TransferSeq_TypeDef *seq; +} I2C_Transfer_TypeDef; + +/** @endcond */ + +/******************************************************************************* + ***************************** LOCAL DATA *******^************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** + * Lookup table for Nlow + Nhigh setting defined by CLHR. Set undefined + * index (0x3) to reflect default setting just in case. + */ +static const uint8_t i2cNSum[] = { 4 + 4, 6 + 3, 11 + 6, 4 + 4 }; + +/** Transfer state info for ongoing master mode transfer */ +static I2C_Transfer_TypeDef i2cTransfer[I2C_COUNT]; + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get current configured I2C bus frequency. + * + * @details + * This frequency is only of relevance when acting as master. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * Current I2C frequency in Hz. + ******************************************************************************/ +uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c) +{ + uint32_t freqHfper; + uint32_t n; + + /* Max frequency is given by freqScl = freqHfper/((Nlow + Nhigh)(DIV + 1) + I2C_CR_MAX) + * More details can be found in the reference manual, + * I2C Clock Generation chapter. */ + freqHfper = CMU_ClockFreqGet(cmuClock_HFPER); + /* n = Nlow + Nhigh */ + n = (uint32_t)(i2cNSum[(i2c->CTRL & _I2C_CTRL_CLHR_MASK) >> _I2C_CTRL_CLHR_SHIFT]); + + return (freqHfper / ((n * (i2c->CLKDIV + 1)) + I2C_CR_MAX)); +} + + +/***************************************************************************//** + * @brief + * Set I2C bus frequency. + * + * @details + * The bus frequency is only of relevance when acting as a master. The bus + * frequency should not be set higher than the max frequency accepted by the + * slowest device on the bus. + * + * Notice that due to asymmetric requirements on low and high I2C clock + * cycles by the I2C specification, the actual max frequency allowed in order + * to comply with the specification may be somewhat lower than expected. + * + * Please refer to the reference manual, details on I2C clock generation, + * for max allowed theoretical frequencies for different modes. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] freqRef + * I2C reference clock frequency in Hz that will be used. If set to 0, + * then HFPER clock is used. Setting it to a higher than actual configured + * value only has the consequence of reducing the real I2C frequency. + * + * @param[in] freqScl + * Bus frequency to set (actual bus speed may be lower due to integer + * prescaling). Safe (according to I2C specification) max frequencies for + * standard, fast and fast+ modes are available using I2C_FREQ_ defines. + * (Using I2C_FREQ_ defines requires corresponding setting of @p type.) + * Slowest slave device on bus must always be considered. + * + * @param[in] i2cMode + * Clock low to high ratio type to use. If not using i2cClockHLRStandard, + * make sure all devices on the bus support the specified mode. Using a + * non-standard ratio is useful to achieve higher bus clock in fast and + * fast+ modes. + ******************************************************************************/ +void I2C_BusFreqSet(I2C_TypeDef *i2c, + uint32_t freqRef, + uint32_t freqScl, + I2C_ClockHLR_TypeDef i2cMode) +{ + uint32_t n, minFreq; + int32_t div; + + /* Avoid divide by 0 */ + EFM_ASSERT(freqScl); + if (!freqScl) + { + return; + } + + /* Set the CLHR (clock low to high ratio). */ + i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK; + BUS_RegMaskedWrite(&i2c->CTRL, + _I2C_CTRL_CLHR_MASK, + i2cMode <<_I2C_CTRL_CLHR_SHIFT); + + if (!freqRef) + { + freqRef = CMU_ClockFreqGet(cmuClock_HFPER); + } + + /* Check minumum HF peripheral clock */ + minFreq = UINT_MAX; + if (i2c->CTRL & I2C_CTRL_SLAVE) + { + switch(i2cMode) + { + case i2cClockHLRStandard: +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + minFreq = 4200000; break; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + minFreq = 2000000; break; +#endif + case i2cClockHLRAsymetric: +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + minFreq = 11000000; break; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + minFreq = 5000000; break; +#endif + case i2cClockHLRFast: +#if defined( _SILICON_LABS_32B_PLATFORM_1 ) + minFreq = 24400000; break; +#elif defined( _SILICON_LABS_32B_PLATFORM_2 ) + minFreq = 14000000; break; +#endif + } + } + else + { + /* For master mode, platform 1 and 2 share the same + min frequencies */ + switch(i2cMode) + { + case i2cClockHLRStandard: + minFreq = 2000000; break; + case i2cClockHLRAsymetric: + minFreq = 9000000; break; + case i2cClockHLRFast: + minFreq = 20000000; break; + } + } + + /* Frequency most be larger-than */ + EFM_ASSERT(freqRef > minFreq); + + /* SCL frequency is given by + * freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_C_MAX) + * + * Thus + * DIV = ((freqRef - (I2C_C_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1 + * + * More details can be found in the reference manual, + * I2C Clock Generation chapter. */ + + /* n = Nlow + Nhigh */ + n = (uint32_t)(i2cNSum[i2cMode]); + div = ((freqRef - (I2C_CR_MAX * freqScl)) / (n * freqScl)) - 1; + EFM_ASSERT(div >= 0); + EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK); + + /* Clock divisor must be at least 1 in slave mode according to reference */ + /* manual (in which case there is normally no need to set bus frequency). */ + if ((i2c->CTRL & I2C_CTRL_SLAVE) && !div) + { + div = 1; + } + i2c->CLKDIV = (uint32_t)div; +} + + +/***************************************************************************//** + * @brief + * Enable/disable I2C. + * + * @note + * After enabling the I2C (from being disabled), the I2C is in BUSY state. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] enable + * true to enable counting, false to disable. + ******************************************************************************/ +void I2C_Enable(I2C_TypeDef *i2c, bool enable) +{ + EFM_ASSERT(I2C_REF_VALID(i2c)); + + BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable); +} + + +/***************************************************************************//** + * @brief + * Initialize I2C. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] init + * Pointer to I2C initialization structure. + ******************************************************************************/ +void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init) +{ + EFM_ASSERT(I2C_REF_VALID(i2c)); + + i2c->IEN = 0; + i2c->IFC = _I2C_IFC_MASK; + + /* Set SLAVE select mode */ + BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, init->master ? 0 : 1); + + I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr); + + BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, init->enable); +} + + +/***************************************************************************//** + * @brief + * Reset I2C 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] i2c + * Pointer to I2C peripheral register block. + ******************************************************************************/ +void I2C_Reset(I2C_TypeDef *i2c) +{ + i2c->CTRL = _I2C_CTRL_RESETVALUE; + i2c->CLKDIV = _I2C_CLKDIV_RESETVALUE; + i2c->SADDR = _I2C_SADDR_RESETVALUE; + i2c->SADDRMASK = _I2C_SADDRMASK_RESETVALUE; + i2c->IEN = _I2C_IEN_RESETVALUE; + i2c->IFC = _I2C_IFC_MASK; + /* Do not reset route register, setting should be done independently */ +} + + +/***************************************************************************//** + * @brief + * Continue an initiated I2C transfer (single master mode only). + * + * @details + * This function is used repeatedly after a I2C_TransferInit() in order to + * complete a transfer. It may be used in polled mode as the below example + * shows: + * @verbatim + * I2C_TransferReturn_TypeDef ret; + * + * // Do a polled transfer + * ret = I2C_TransferInit(I2C0, seq); + * while (ret == i2cTransferInProgress) + * { + * ret = I2C_Transfer(I2C0); + * } + * @endverbatim + * It may also be used in interrupt driven mode, where this function is invoked + * from the interrupt handler. Notice that if used in interrupt mode, NVIC + * interrupts must be configured and enabled for the I2C bus used. I2C + * peripheral specific interrupts are managed by this SW. + * + * @note + * Only single master mode is supported. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * Returns status for ongoing transfer. + * @li #i2cTransferInProgress - indicates that transfer not finished. + * @li #i2cTransferDone - transfer completed successfully. + * @li otherwise some sort of error has occurred. + * + ******************************************************************************/ +I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c) +{ + uint32_t tmp; + uint32_t pending; + I2C_Transfer_TypeDef *transfer; + I2C_TransferSeq_TypeDef *seq; + + EFM_ASSERT(I2C_REF_VALID(i2c)); + + /* Support up to 2 I2C buses */ + if (i2c == I2C0) + { + transfer = i2cTransfer; + } +#if (I2C_COUNT > 1) + else if (i2c == I2C1) + { + transfer = i2cTransfer + 1; + } +#endif + else + { + return i2cTransferUsageFault; + } + + seq = transfer->seq; + for (;; ) + { + pending = i2c->IF; + + /* If some sort of fault, abort transfer. */ + if (pending & I2C_IF_ERRORS) + { + if (pending & I2C_IF_ARBLOST) + { + /* If arbitration fault, it indicates either a slave device */ + /* not responding as expected, or other master which is not */ + /* supported by this SW. */ + transfer->result = i2cTransferArbLost; + } + else if (pending & I2C_IF_BUSERR) + { + /* A bus error indicates a misplaced start or stop, which should */ + /* not occur in master mode controlled by this SW. */ + transfer->result = i2cTransferBusErr; + } + + /* If error situation occurred, it is difficult to know */ + /* exact cause and how to resolve. It will be up to a wrapper */ + /* to determine how to handle a fault/recovery if possible. */ + transfer->state = i2cStateDone; + goto done; + } + + switch (transfer->state) + { + /***************************************************/ + /* Send first start+address (first byte if 10 bit) */ + /***************************************************/ + case i2cStateStartAddrSend: + if (seq->flags & I2C_FLAG_10BIT_ADDR) + { + tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0; + + /* In 10 bit address mode, the address following the first */ + /* start always indicate write. */ + } + else + { + tmp = (uint32_t)(seq->addr) & 0xfe; + + if (seq->flags & I2C_FLAG_READ) + { + /* Indicate read request */ + tmp |= 1; + } + } + + transfer->state = i2cStateAddrWFAckNack; + i2c->TXDATA = tmp; /* Data not transmitted until START sent */ + i2c->CMD = I2C_CMD_START; + goto done; + + /*******************************************************/ + /* Wait for ACK/NACK on address (first byte if 10 bit) */ + /*******************************************************/ + case i2cStateAddrWFAckNack: + if (pending & I2C_IF_NACK) + { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } + else if (pending & I2C_IF_ACK) + { + i2c->IFC = I2C_IFC_ACK; + + /* If 10 bit address, send 2nd byte of address. */ + if (seq->flags & I2C_FLAG_10BIT_ADDR) + { + transfer->state = i2cStateAddrWF2ndAckNack; + i2c->TXDATA = (uint32_t)(seq->addr) & 0xff; + } + else + { + /* Determine whether receiving or sending data */ + if (seq->flags & I2C_FLAG_READ) + { + transfer->state = i2cStateWFData; + if(seq->buf[transfer->bufIndx].len==1) + { + i2c->CMD = I2C_CMD_NACK; + } + } + else + { + transfer->state = i2cStateDataSend; + continue; + } + } + } + goto done; + + /******************************************************/ + /* Wait for ACK/NACK on second byte of 10 bit address */ + /******************************************************/ + case i2cStateAddrWF2ndAckNack: + if (pending & I2C_IF_NACK) + { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } + else if (pending & I2C_IF_ACK) + { + i2c->IFC = I2C_IFC_ACK; + + /* If using plain read sequence with 10 bit address, switch to send */ + /* repeated start. */ + if (seq->flags & I2C_FLAG_READ) + { + transfer->state = i2cStateRStartAddrSend; + } + /* Otherwise expected to write 0 or more bytes */ + else + { + transfer->state = i2cStateDataSend; + } + continue; + } + goto done; + + /*******************************/ + /* Send repeated start+address */ + /*******************************/ + case i2cStateRStartAddrSend: + if (seq->flags & I2C_FLAG_10BIT_ADDR) + { + tmp = ((seq->addr >> 8) & 0x06) | 0xf0; + } + else + { + tmp = seq->addr & 0xfe; + } + + /* If this is a write+read combined sequence, then read is about to start */ + if (seq->flags & I2C_FLAG_WRITE_READ) + { + /* Indicate read request */ + tmp |= 1; + } + + transfer->state = i2cStateRAddrWFAckNack; + /* We have to write START cmd first since repeated start, otherwise */ + /* data would be sent first. */ + i2c->CMD = I2C_CMD_START; + i2c->TXDATA = tmp; + goto done; + + /**********************************************************************/ + /* Wait for ACK/NACK on repeated start+address (first byte if 10 bit) */ + /**********************************************************************/ + case i2cStateRAddrWFAckNack: + if (pending & I2C_IF_NACK) + { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } + else if (pending & I2C_IF_ACK) + { + i2c->IFC = I2C_IFC_ACK; + + /* Determine whether receiving or sending data */ + if (seq->flags & I2C_FLAG_WRITE_READ) + { + transfer->state = i2cStateWFData; + } + else + { + transfer->state = i2cStateDataSend; + continue; + } + } + goto done; + + /*****************************/ + /* Send a data byte to slave */ + /*****************************/ + case i2cStateDataSend: + /* Reached end of data buffer? */ + if (transfer->offset >= seq->buf[transfer->bufIndx].len) + { + /* Move to next message part */ + transfer->offset = 0; + transfer->bufIndx++; + + /* Send repeated start when switching to read mode on 2nd buffer */ + if (seq->flags & I2C_FLAG_WRITE_READ) + { + transfer->state = i2cStateRStartAddrSend; + continue; + } + + /* Only writing from one buffer, or finished both buffers */ + if ((seq->flags & I2C_FLAG_WRITE) || (transfer->bufIndx > 1)) + { + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + goto done; + } + + /* Reprocess in case next buffer is empty */ + continue; + } + + /* Send byte */ + i2c->TXDATA = (uint32_t)(seq->buf[transfer->bufIndx].data[transfer->offset++]); + transfer->state = i2cStateDataWFAckNack; + goto done; + + /*********************************************************/ + /* Wait for ACK/NACK from slave after sending data to it */ + /*********************************************************/ + case i2cStateDataWFAckNack: + if (pending & I2C_IF_NACK) + { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } + else if (pending & I2C_IF_ACK) + { + i2c->IFC = I2C_IFC_ACK; + transfer->state = i2cStateDataSend; + continue; + } + goto done; + + /****************************/ + /* Wait for data from slave */ + /****************************/ + case i2cStateWFData: + if (pending & I2C_IF_RXDATAV) + { + uint8_t data; + unsigned int rxLen = seq->buf[transfer->bufIndx].len; + + /* Must read out data in order to not block further progress */ + data = (uint8_t)(i2c->RXDATA); + + /* Make sure not storing beyond end of buffer just in case */ + if (transfer->offset < rxLen) + { + seq->buf[transfer->bufIndx].data[transfer->offset++] = data; + } + + /* If we have read all requested data, then the sequence should end */ + if (transfer->offset >= rxLen) + { + /* If there is only one byte to receive we need to transmit the + NACK now, before the stop. */ + if (1 == rxLen) + { + i2c->CMD = I2C_CMD_NACK; + } + + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } + else + { + /* Send ACK and wait for next byte */ + i2c->CMD = I2C_CMD_ACK; + + if ( (1offset == (rxLen-1)) ) + { + /* If there is more than one byte to receive and this is the next + to last byte we need to transmit the NACK now, before receiving + the last byte. */ + i2c->CMD = I2C_CMD_NACK; + } + } + } + goto done; + + /***********************************/ + /* Wait for STOP to have been sent */ + /***********************************/ + case i2cStateWFStopSent: + if (pending & I2C_IF_MSTOP) + { + i2c->IFC = I2C_IFC_MSTOP; + transfer->state = i2cStateDone; + } + goto done; + + /******************************/ + /* Unexpected state, SW fault */ + /******************************/ + default: + transfer->result = i2cTransferSwFault; + transfer->state = i2cStateDone; + goto done; + } + } + + done: + + if (transfer->state == i2cStateDone) + { + /* Disable interrupt sources when done */ + i2c->IEN = 0; + + /* Update result unless some fault already occurred */ + if (transfer->result == i2cTransferInProgress) + { + transfer->result = i2cTransferDone; + } + } + /* Until transfer is done keep returning i2cTransferInProgress */ + else + { + return i2cTransferInProgress; + } + + return transfer->result; +} + + +/***************************************************************************//** + * @brief + * Prepare and start an I2C transfer (single master mode only). + * + * @details + * This function must be invoked in order to start an I2C transfer + * sequence. In order to actually complete the transfer, I2C_Transfer() must + * be used either in polled mode or by adding a small driver wrapper utilizing + * interrupts. + * + * @note + * Only single master mode is supported. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] seq + * Pointer to sequence structure defining the I2C transfer to take place. The + * referenced structure must exist until the transfer has fully completed. + * + * @return + * Returns status for ongoing transfer: + * @li #i2cTransferInProgress - indicates that transfer not finished. + * @li otherwise some sort of error has occurred. + ******************************************************************************/ +I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c, + I2C_TransferSeq_TypeDef *seq) +{ + I2C_Transfer_TypeDef *transfer; + + EFM_ASSERT(I2C_REF_VALID(i2c)); + EFM_ASSERT(seq); + + /* Support up to 2 I2C buses */ + if (i2c == I2C0) + { + transfer = i2cTransfer; + } +#if (I2C_COUNT > 1) + else if (i2c == I2C1) + { + transfer = i2cTransfer + 1; + } +#endif + else + { + return i2cTransferUsageFault; + } + + /* Check if in busy state. Since this SW assumes single master, we can */ + /* just issue an abort. The BUSY state is normal after a reset. */ + if (i2c->STATE & I2C_STATE_BUSY) + { + i2c->CMD = I2C_CMD_ABORT; + } + + /* Make sure user is not trying to read 0 bytes, it is not */ + /* possible according to I2C spec, since slave will always start */ + /* sending first byte ACK on address. The read operation can */ + /* only be stopped by NACKing a received byte, ie minimum 1 byte. */ + if (((seq->flags & I2C_FLAG_READ) && !(seq->buf[0].len)) || + ((seq->flags & I2C_FLAG_WRITE_READ) && !(seq->buf[1].len)) + ) + { + return i2cTransferUsageFault; + } + + /* Prepare for a transfer */ + transfer->state = i2cStateStartAddrSend; + transfer->result = i2cTransferInProgress; + transfer->offset = 0; + transfer->bufIndx = 0; + transfer->seq = seq; + + /* Ensure buffers are empty */ + i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX; + if (i2c->IF & I2C_IF_RXDATAV) + { + (void)i2c->RXDATA; + } + + /* Clear all pending interrupts prior to starting transfer. */ + i2c->IFC = _I2C_IFC_MASK; + + /* Enable those interrupts we are interested in throughout transfer. */ + /* Notice that the I2C interrupt must also be enabled in the NVIC, but */ + /* that is left for an additional driver wrapper. */ + i2c->IEN = I2C_IF_NACK | I2C_IF_ACK | I2C_IF_MSTOP | + I2C_IF_RXDATAV | I2C_IF_ERRORS; + + /* Start transfer */ + return I2C_Transfer(i2c); +} + +/** @} (end addtogroup I2C) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_idac.c b/cpu/efm32_common/emlib/src/em_idac.c new file mode 100644 index 0000000000000..b471e46ef0ac2 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_idac.c @@ -0,0 +1,372 @@ +/***************************************************************************//** + * @file em_idac.c + * @brief Current Digital to Analog Converter (IDAC) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_idac.h" +#if defined(IDAC_COUNT) && (IDAC_COUNT > 0) +#include "em_cmu.h" +#include "em_assert.h" +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup IDAC + * @brief Current Digital to Analog Conversion (IDAC) Peripheral API + * @{ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/* Fix for errata IDAC_E101 - IDAC output current degradation */ +#if defined(_EFM32_ZERO_FAMILY) || defined(_EFM32_HAPPY_FAMILY) +#define ERRATA_FIX_IDAC_E101_EN +#endif +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ +/***************************************************************************//** + * @brief + * Initialize IDAC. + * + * @details + * Initializes IDAC according to the initialization structure parameter, and + * sets the default calibration value stored in the DEVINFO structure. + * + * @note + * This function will disable the IDAC prior to configuration. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] init + * Pointer to IDAC initialization structure. + ******************************************************************************/ +void IDAC_Init(IDAC_TypeDef *idac, const IDAC_Init_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(IDAC_REF_VALID(idac)); + + tmp = (uint32_t)(init->prsSel); + + tmp |= init->outMode; + + if (init->enable) + { + tmp |= IDAC_CTRL_EN; + } + if (init->prsEnable) + { + tmp |= IDAC_CTRL_OUTENPRS; + } + if (init->sinkEnable) + { + tmp |= IDAC_CTRL_CURSINK; + } + + idac->CTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Enable/disable IDAC. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] enable + * true to enable IDAC, false to disable. + ******************************************************************************/ +void IDAC_Enable(IDAC_TypeDef *idac, bool enable) +{ + volatile uint32_t *reg; + + EFM_ASSERT(IDAC_REF_VALID(idac)); + + reg = &(idac->CTRL); + + BUS_RegBitWrite(reg, _IDAC_CTRL_EN_SHIFT, enable); +} + + +/***************************************************************************//** + * @brief + * Reset IDAC to same state as after a HW reset. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + ******************************************************************************/ +void IDAC_Reset(IDAC_TypeDef *idac) +{ + EFM_ASSERT(IDAC_REF_VALID(idac)); + +#if defined(ERRATA_FIX_IDAC_E101_EN) + /* Fix for errata IDAC_E101 - IDAC output current degradation: + Instead of disabling it we will put it in it's lowest power state (50 nA) + to avoid degradation over time */ + + /* Make sure IDAC is enabled with disabled output */ + idac->CTRL = _IDAC_CTRL_RESETVALUE | IDAC_CTRL_EN; + + /* Set lowest current (50 nA) */ + idac->CURPROG = IDAC_CURPROG_RANGESEL_RANGE0 | + (0x0 << _IDAC_CURPROG_STEPSEL_SHIFT); + + /* Enable duty-cycling for all energy modes */ + idac->DUTYCONFIG = IDAC_DUTYCONFIG_DUTYCYCLEEN; +#else + idac->CTRL = _IDAC_CTRL_RESETVALUE; + idac->CURPROG = _IDAC_CURPROG_RESETVALUE; + idac->DUTYCONFIG = _IDAC_DUTYCONFIG_RESETVALUE; +#endif +#if defined ( _IDAC_CAL_MASK ) + idac->CAL = _IDAC_CAL_RESETVALUE; +#endif +} + + +/***************************************************************************//** + * @brief + * Enable/disable Minimal Output Transition mode. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] enable + * true to enable Minimal Output Transition mode, false to disable. + ******************************************************************************/ +void IDAC_MinimalOutputTransitionMode(IDAC_TypeDef *idac, bool enable) +{ + volatile uint32_t *reg; + + EFM_ASSERT(IDAC_REF_VALID(idac)); + + reg = &(idac->CTRL); + + BUS_RegBitWrite(reg, _IDAC_CTRL_MINOUTTRANS_SHIFT, enable); +} + + +/***************************************************************************//** + * @brief + * Set the current range of the IDAC output. + * + * @details + * This function sets the current range of the IDAC output. The function + * also updates the IDAC calibration register (IDAC_CAL) with the default + * calibration value (from DEVINFO, factory setting) corresponding to the + * specified range. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] range + * Current range value. + ******************************************************************************/ +void IDAC_RangeSet(IDAC_TypeDef *idac, const IDAC_Range_TypeDef range) +{ + uint32_t tmp; +#if defined( _IDAC_CURPROG_TUNING_MASK ) + uint32_t diCal0 = 0; + uint32_t diCal1 = 0; +#endif + + EFM_ASSERT(IDAC_REF_VALID(idac)); + EFM_ASSERT(((uint32_t)range >> _IDAC_CURPROG_RANGESEL_SHIFT) + <= (_IDAC_CURPROG_RANGESEL_MASK >> _IDAC_CURPROG_RANGESEL_SHIFT)); + +#if defined ( _IDAC_CAL_MASK ) + + /* Load proper calibration data depending on selected range */ + switch ((IDAC_Range_TypeDef)range) + { + case idacCurrentRange0: + idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE0_MASK) + >> _DEVINFO_IDAC0CAL0_RANGE0_SHIFT; + break; + case idacCurrentRange1: + idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE1_MASK) + >> _DEVINFO_IDAC0CAL0_RANGE1_SHIFT; + break; + case idacCurrentRange2: + idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE2_MASK) + >> _DEVINFO_IDAC0CAL0_RANGE2_SHIFT; + break; + case idacCurrentRange3: + idac->CAL = (DEVINFO->IDAC0CAL0 & _DEVINFO_IDAC0CAL0_RANGE3_MASK) + >> _DEVINFO_IDAC0CAL0_RANGE3_SHIFT; + break; + } + + tmp = idac->CURPROG & ~_IDAC_CURPROG_RANGESEL_MASK; + tmp |= (uint32_t)range; + +#elif defined( _IDAC_CURPROG_TUNING_MASK ) + + /* Load calibration data depending on selected range and sink/source mode */ + /* TUNING (calibration) field in CURPROG register. */ + if (idac == IDAC0) + { + diCal0 = DEVINFO->IDAC0CAL0; + diCal1 = DEVINFO->IDAC0CAL1; + } + else + { + EFM_ASSERT(false); + } + + tmp = idac->CURPROG & ~(_IDAC_CURPROG_TUNING_MASK + | _IDAC_CURPROG_RANGESEL_MASK); + if (idac->CTRL & IDAC_CTRL_CURSINK) + { + switch (range) + { + case idacCurrentRange0: + tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_MASK) + >> _DEVINFO_IDAC0CAL1_SINKRANGE0TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + + case idacCurrentRange1: + tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_MASK) + >> _DEVINFO_IDAC0CAL1_SINKRANGE1TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + + case idacCurrentRange2: + tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_MASK) + >> _DEVINFO_IDAC0CAL1_SINKRANGE2TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + + case idacCurrentRange3: + tmp |= ((diCal1 & _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_MASK) + >> _DEVINFO_IDAC0CAL1_SINKRANGE3TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + } + } + else + { + switch (range) + { + case idacCurrentRange0: + tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_MASK) + >> _DEVINFO_IDAC0CAL0_SOURCERANGE0TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + + case idacCurrentRange1: + tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_MASK) + >> _DEVINFO_IDAC0CAL0_SOURCERANGE1TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + + case idacCurrentRange2: + tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_MASK) + >> _DEVINFO_IDAC0CAL0_SOURCERANGE2TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + + case idacCurrentRange3: + tmp |= ((diCal0 & _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_MASK) + >> _DEVINFO_IDAC0CAL0_SOURCERANGE3TUNING_SHIFT) + << _IDAC_CURPROG_TUNING_SHIFT; + break; + } + } + + tmp |= (uint32_t)range; + +#else +#warning "IDAC calibration register definition unknown." +#endif + + idac->CURPROG = tmp; +} + + +/***************************************************************************//** + * @brief + * Set the current step of the IDAC output. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] step + * Step value for IDAC output. Valid range is 0-31. + ******************************************************************************/ +void IDAC_StepSet(IDAC_TypeDef *idac, const uint32_t step) +{ + uint32_t tmp; + + EFM_ASSERT(IDAC_REF_VALID(idac)); + EFM_ASSERT(step <= (_IDAC_CURPROG_STEPSEL_MASK >> _IDAC_CURPROG_STEPSEL_SHIFT)); + + tmp = idac->CURPROG & ~_IDAC_CURPROG_STEPSEL_MASK; + tmp |= step << _IDAC_CURPROG_STEPSEL_SHIFT; + + idac->CURPROG = tmp; +} + + +/***************************************************************************//** + * @brief + * Enable/disable the IDAC OUT pin. + * + * @param[in] idac + * Pointer to IDAC peripheral register block. + * + * @param[in] enable + * true to enable the IDAC OUT pin, false to disable. + ******************************************************************************/ +void IDAC_OutEnable(IDAC_TypeDef *idac, bool enable) +{ + volatile uint32_t *reg; + + EFM_ASSERT(IDAC_REF_VALID(idac)); + + reg = &(idac->CTRL); + + BUS_RegBitWrite(reg, _IDAC_CTRL_OUTEN_SHIFT, enable); +} + + +/** @} (end addtogroup IDAC) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(IDAC_COUNT) && (IDAC_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_int.c b/cpu/efm32_common/emlib/src/em_int.c new file mode 100644 index 0000000000000..cab8c34164434 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_int.c @@ -0,0 +1,73 @@ +/**************************************************************************//** + * @file em_int.c + * @brief Interrupt enable/disable unit API + * @version 4.2.1 + ****************************************************************************** + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include +#include "em_int.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup INT + * @brief Safe nesting of interrupt disable/enable API + * @{ + * @details + * This module contains functions to safely disable and enable interrupts + * at CPU level. INT_Disable() disables interrupts globally and increments a lock + * level counter (counting semaphore). INT_Enable() decrements the lock level + * counter and enable interrupts if the counter reaches zero. + * + * These functions would normally be used to secure critical regions, and + * to make sure that a critical section that calls into another critical + * section does not unintentionally terminate the callee critical section. + * + * These functions should also be used inside interrupt handlers: + * @verbatim + * void SysTick_Handler(void) + * { + * INT_Disable(); + * . + * . + * . + * INT_Enable(); + * } + * @endverbatim + ******************************************************************************/ + +/** Interrupt lock level counter. Set to zero initially as we normally enter + * main with interrupts enabled */ +uint32_t INT_LockCnt = 0; + +/** @} (end addtogroup INT) */ +/** @} (end addtogroup EM_Library) */ diff --git a/cpu/efm32_common/emlib/src/em_lcd.c b/cpu/efm32_common/emlib/src/em_lcd.c new file mode 100644 index 0000000000000..8730332742b67 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_lcd.c @@ -0,0 +1,763 @@ +/***************************************************************************//** + * @file em_lcd.c + * @brief Liquid Crystal Display (LCD) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_lcd.h" +#if defined(LCD_COUNT) && (LCD_COUNT > 0) +#include "em_assert.h" +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LCD + * @brief Liquid Crystal Display (LCD) Peripheral API + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initalize Liquid Crystal Display (LCD) controller + * + * @details + * This function call will only configure the LCD controller. You must enable + * it afterwards, potentially configuring Frame Control and interrupts first + * according to requirements. + * + * @param[in] lcdInit + * Pointer to initialization structure which configures LCD controller. + * + ******************************************************************************/ +void LCD_Init(const LCD_Init_TypeDef *lcdInit) +{ + uint32_t dispCtrl = LCD->DISPCTRL; + + EFM_ASSERT(lcdInit != (void *) 0); + + /* Disable controller before reconfiguration */ + LCD_Enable(false); + + /* Make sure we don't touch other bit fields (i.e. voltage boost) */ + dispCtrl &= ~(0 +#if defined(LCD_DISPCTRL_MUXE) + | _LCD_DISPCTRL_MUXE_MASK +#endif + | _LCD_DISPCTRL_MUX_MASK + | _LCD_DISPCTRL_BIAS_MASK + | _LCD_DISPCTRL_WAVE_MASK + | _LCD_DISPCTRL_VLCDSEL_MASK + | _LCD_DISPCTRL_CONCONF_MASK); + + /* Configure controller according to initialization structure */ + dispCtrl |= lcdInit->mux; /* also configures MUXE */ + dispCtrl |= lcdInit->bias; + dispCtrl |= lcdInit->wave; + dispCtrl |= lcdInit->vlcd; + dispCtrl |= lcdInit->contrast; + + /* Update display controller */ + LCD->DISPCTRL = dispCtrl; + + /* Enable controller if wanted */ + if (lcdInit->enable) + { + LCD_Enable(true); + } +} + + +/***************************************************************************//** + * @brief + * Select source for VLCD + * + * @param[in] vlcd + * Select source for VLD voltage + ******************************************************************************/ +void LCD_VLCDSelect(LCD_VLCDSel_TypeDef vlcd) +{ + uint32_t dispctrl = LCD->DISPCTRL; + + /* Select VEXT or VDD */ + dispctrl &= ~_LCD_DISPCTRL_VLCDSEL_MASK; + switch (vlcd) + { + case lcdVLCDSelVExtBoost: + dispctrl |= LCD_DISPCTRL_VLCDSEL_VEXTBOOST; + break; + case lcdVLCDSelVDD: + dispctrl |= LCD_DISPCTRL_VLCDSEL_VDD; + break; + default: + break; + } + + LCD->DISPCTRL = dispctrl; +} + + +/***************************************************************************//** + * @brief + * Configure Update Control + * + * @param[in] ud + * Configures LCD update method + ******************************************************************************/ +void LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud) +{ + LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud; +} + + +/***************************************************************************//** + * @brief + * Initialize LCD Frame Counter + * + * @param[in] fcInit + * Pointer to Frame Counter initialization structure + ******************************************************************************/ +void LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef *fcInit) +{ + uint32_t bactrl = LCD->BACTRL; + + EFM_ASSERT(fcInit != (void *) 0); + + /* Verify FC Top Counter to be within limits */ + EFM_ASSERT(fcInit->top < 64); + + /* Reconfigure frame count configuration */ + bactrl &= ~(_LCD_BACTRL_FCTOP_MASK + | _LCD_BACTRL_FCPRESC_MASK); + bactrl |= (fcInit->top << _LCD_BACTRL_FCTOP_SHIFT); + bactrl |= fcInit->prescale; + + /* Set Blink and Animation Control Register */ + LCD->BACTRL = bactrl; + + LCD_FrameCountEnable(fcInit->enable); +} + + +/***************************************************************************//** + * @brief + * Configures LCD controller Animation feature + * + * @param[in] animInit + * Pointer to LCD Animation initialization structure + ******************************************************************************/ +void LCD_AnimInit(const LCD_AnimInit_TypeDef *animInit) +{ + uint32_t bactrl = LCD->BACTRL; + + EFM_ASSERT(animInit != (void *) 0); + + /* Set Animation Register Values */ + LCD->AREGA = animInit->AReg; + LCD->AREGB = animInit->BReg; + + /* Configure Animation Shift and Logic */ + bactrl &= ~(_LCD_BACTRL_AREGASC_MASK + | _LCD_BACTRL_AREGBSC_MASK + | _LCD_BACTRL_ALOGSEL_MASK); + + bactrl |= (animInit->AShift << _LCD_BACTRL_AREGASC_SHIFT); + bactrl |= (animInit->BShift << _LCD_BACTRL_AREGBSC_SHIFT); + bactrl |= animInit->animLogic; + +#if defined(LCD_BACTRL_ALOC) + bactrl &= ~(_LCD_BACTRL_ALOC_MASK); + + if(animInit->startSeg == 0) + { + bactrl |= LCD_BACTRL_ALOC_SEG0TO7; + } + else if(animInit->startSeg == 8) + { + bactrl |= LCD_BACTRL_ALOC_SEG8TO15; + } +#endif + + /* Reconfigure */ + LCD->BACTRL = bactrl; + + /* Enable */ + LCD_AnimEnable(animInit->enable); +} + + +/***************************************************************************//** + * @brief + * Enables update of this range of LCD segment lines + * + * @param[in] segmentRange + * Range of 4 LCD segments lines to enable or disable, for all enabled COM + * lines + * + * @param[in] enable + * Bool true to enable segment updates, false to disable updates + ******************************************************************************/ +void LCD_SegmentRangeEnable(LCD_SegmentRange_TypeDef segmentRange, bool enable) +{ + if (enable) + { + LCD->SEGEN |= segmentRange; + } + else + { + LCD->SEGEN &= ~((uint32_t)segmentRange); + } +} + + +/***************************************************************************//** + * @brief + * Turn on or clear a segment + * + * @note + * On Gecko Family, max configuration is (COM-lines x Segment-Lines) 4x40 + * On Tiny Family, max configuration is 8x20 or 4x24 + * On Giant Family, max configuration is 8x36 or 4x40 + * + * @param[in] com + * COM line to change + * + * @param[in] bit + * Bit index of which field to change + * + * @param[in] enable + * When true will set segment, when false will clear segment + ******************************************************************************/ +void LCD_SegmentSet(int com, int bit, bool enable) +{ +#if defined(_LCD_SEGD7L_MASK) + /* Tiny and Giant Family supports up to 8 COM lines */ + EFM_ASSERT(com < 8); +#else + /* Gecko Family supports up to 4 COM lines */ + EFM_ASSERT(com < 4); +#endif + +#if defined(_LCD_SEGD0H_MASK) + EFM_ASSERT(bit < 40); +#else + /* Tiny Gecko Family supports only "low" segment registers */ + EFM_ASSERT(bit < 32); +#endif + + /* Use bitband access for atomic bit set/clear of segment */ + switch (com) + { + case 0: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD0L), bit, enable); + } +#if defined(_LCD_SEGD0H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD0H), bit, enable); + } +#endif + break; + case 1: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD1L), bit, enable); + } +#if defined(_LCD_SEGD1H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD1H), bit, enable); + } +#endif + break; + case 2: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD2L), bit, enable); + } +#if defined(_LCD_SEGD2H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD2H), bit, enable); + } +#endif + break; + case 3: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD3L), bit, enable); + } +#if defined(_LCD_SEGD3H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD3H), bit, enable); + } +#endif + break; +#if defined(_LCD_SEGD4L_MASK) + case 4: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD4L), bit, enable); + } +#if defined(_LCD_SEGD4H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD4H), bit, enable); + } +#endif + break; +#endif +#if defined(_LCD_SEGD5L_MASK) + case 5: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD5L), bit, enable); + } +#if defined(_LCD_SEGD5H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD5H), bit, enable); + } +#endif + break; +#endif + case 6: +#if defined(_LCD_SEGD6L_MASK) + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD6L), bit, enable); + } +#if defined(_LCD_SEGD6H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD6H), bit, enable); + } +#endif + break; +#endif +#if defined(_LCD_SEGD7L_MASK) + case 7: + if (bit < 32) + { + BUS_RegBitWrite(&(LCD->SEGD7L), bit, enable); + } +#if defined(_LCD_SEGD7H_MASK) + else + { + bit -= 32; + BUS_RegBitWrite(&(LCD->SEGD7H), bit, enable); + } +#endif + break; +#endif + + default: + EFM_ASSERT(0); + break; + } +} + + +/***************************************************************************//** + * @brief + * Updates the 0-31 lowest segments on a given COM-line in one operation, + * according to bit mask + * + * @param[in] com + * Which COM line to update + * + * @param[in] mask + * Bit mask for segments 0-31 + * + * @param[in] bits + * Bit pattern for segments 0-31 + ******************************************************************************/ +void LCD_SegmentSetLow(int com, uint32_t mask, uint32_t bits) +{ + uint32_t segData; + + /* Maximum number of com lines */ +#if defined(_LCD_SEGD7L_MASK) + EFM_ASSERT(com < 8); +#else + /* Gecko Family supports up to 4 COM lines */ + EFM_ASSERT(com < 4); +#endif + + switch (com) + { + case 0: + segData = LCD->SEGD0L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD0L = segData; + break; + case 1: + segData = LCD->SEGD1L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD1L = segData; + break; + case 2: + segData = LCD->SEGD2L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD2L = segData; + break; + case 3: + segData = LCD->SEGD3L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD3L = segData; + break; +#if defined(_LCD_SEGD4L_MASK) + case 4: + segData = LCD->SEGD4L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD4L = segData; + break; +#endif +#if defined(_LCD_SEGD5L_MASK) + case 5: + segData = LCD->SEGD5L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD5L = segData; + break; +#endif +#if defined(_LCD_SEGD6L_MASK) + case 6: + segData = LCD->SEGD6L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD6L = segData; + break; +#endif +#if defined(_LCD_SEGD7L_MASK) + case 7: + segData = LCD->SEGD7L; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD7L = segData; + break; +#endif + default: + EFM_ASSERT(0); + break; + } +} + + +#if defined(_LCD_SEGD0H_MASK) +/***************************************************************************//** + * @brief + * Updated the high (32-39) segments on a given COM-line in one operation + * + * @param[in] com + * Which COM line to update + * + * @param[in] mask + * Bit mask for segments 32-39 + * + * @param[in] bits + * Bit pattern for segments 32-39 + ******************************************************************************/ +void LCD_SegmentSetHigh(int com, uint32_t mask, uint32_t bits) +{ + uint32_t segData; + +#if defined(_LCD_SEGD7H_MASK) + EFM_ASSERT(com < 8); +#else + EFM_ASSERT(com < 4); +#endif + + /* Maximum number of com lines */ + switch (com) + { + case 0: + segData = LCD->SEGD0H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD0H = segData; + break; + case 1: + segData = LCD->SEGD1H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD1H = segData; + break; + case 2: + segData = LCD->SEGD2H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD2H = segData; + break; + case 3: + segData = LCD->SEGD3H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD3H = segData; + break; +#if defined(_LCD_SEGD4H_MASK) + case 4: + segData = LCD->SEGD4H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD4H = segData; + break; +#endif +#if defined(_LCD_SEGD5H_MASK) + case 5: + segData = LCD->SEGD5H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD5H = segData; + break; +#endif +#if defined(_LCD_SEGD6H_MASK) + case 6: + segData = LCD->SEGD6H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD6H = segData; + break; +#endif +#if defined(_LCD_SEGD7H_MASK) + case 7: + segData = LCD->SEGD7H; + segData &= ~(mask); + segData |= (mask & bits); + LCD->SEGD7H = segData; + break; +#endif + default: + break; + } +} +#endif + +/***************************************************************************//** + * @brief + * Configure contrast level on LCD panel + * + * @param[in] level + * Contrast level in the range 0-31 + ******************************************************************************/ +void LCD_ContrastSet(int level) +{ + EFM_ASSERT(level < 32); + + LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONLEV_MASK) + | (level << _LCD_DISPCTRL_CONLEV_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Configure voltage booster + * + * The resulting voltage level is described in each part number's data sheet + * + * @param[in] vboost + * Voltage boost level + ******************************************************************************/ +void LCD_VBoostSet(LCD_VBoostLevel_TypeDef vboost) +{ + /* Reconfigure Voltage Boost */ + LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_VBLEV_MASK) | vboost; +} + + +#if defined(LCD_CTRL_DSC) +/***************************************************************************//** + * @brief + * Configure bias level for a specific segment line for Direct Segment Control + * + * @note + * When DSC is active, each configuration takes up 4 bits in the Segment + * Registers (SEGD0L/SEGD1H) which defines bias level. + * For optimal use of this feature, the entire SEGD-registers should be set + * at once in a optimized routine, so this function is mainly here to + * demonstrate how to correctly configure the bias levels, and should be used + * with care. + * + * @param[in] segmentLine + * Segment line number + * + * @param[in] biasLevel + * Bias configuration level, 0-4. This value must be within the constraint + * defined by the LCD_DISPCTRL bias setting, see Reference Manual/Datasheet + ******************************************************************************/ +void LCD_BiasSegmentSet(int segmentLine, int biasLevel) +{ + int biasRegister; + int bitShift; + volatile uint32_t *segmentRegister; + +#if !defined(_LCD_SEGD0H_MASK) + EFM_ASSERT(segmentLine < 20); + + /* Bias config for 8 segment lines per SEGDnL register */ + biasRegister = segmentLine / 8; + bitShift = (segmentLine % 8) * 4; + + switch (biasRegister) + { + case 0: + segmentRegister = &LCD->SEGD0L; + break; + case 1: + segmentRegister = &LCD->SEGD1L; + break; + case 2: + segmentRegister = &LCD->SEGD2L; + break; + case 3: + segmentRegister = &LCD->SEGD3L; + break; + default: + segmentRegister = (uint32_t *)0x00000000; + EFM_ASSERT(0); + break; + } +#else + EFM_ASSERT(segmentLine < 40); + + /* Bias config for 10 segment lines per SEGDn L+H registers */ + biasRegister = segmentLine / 10; + bitShift = (segmentLine % 10) * 4; + + switch (biasRegister) + { + case 0: + if (bitShift < 32) + { + segmentRegister = &LCD->SEGD0L; + } + else + { + segmentRegister = &LCD->SEGD0H; + bitShift -= 32; + } + break; + case 1: + if (bitShift < 32) + { + segmentRegister = &LCD->SEGD1L; + } + else + { + segmentRegister = &LCD->SEGD1H; + bitShift -= 32; + } + break; + case 2: + if (bitShift < 32) + { + segmentRegister = &LCD->SEGD2L; + } + else + { + segmentRegister = &LCD->SEGD1H; + bitShift -= 32; + } + break; + case 3: + if (bitShift < 32) + { + segmentRegister = &LCD->SEGD3L; + } + else + { + segmentRegister = &LCD->SEGD3H; + bitShift -= 32; + } + break; + default: + segmentRegister = (uint32_t *)0x00000000; + EFM_ASSERT(0); + break; + } +#endif + + /* Configure new bias setting */ + *segmentRegister = (*segmentRegister & ~(0xF << bitShift)) | (biasLevel << bitShift); +} +#endif + + +#if defined(LCD_CTRL_DSC) +/***************************************************************************//** + * @brief + * Configure bias level for a specific segment line + * + * @note + * When DSC is active, each configuration takes up 4 bits in the Segment + * Registers (SEGD4L/SEGD4H) which defines bias level. + * For optimal use of this feature, the entire SEGD-registers should be set + * at once in a optimized routine, so this function is mainly here to + * demonstrate how to correctly configure the bias levels, and should be used + * with care. + * + * @param[in] comLine + * COM line number, 0-7 + * + * @param[in] biasLevel + * Bias configuration level, 0-4. This value must be within the constraint + * defined by the LCD_DISPCTRL bias setting, see Reference Manual/Datasheet + ******************************************************************************/ +void LCD_BiasComSet(int comLine, int biasLevel) +{ + int bitShift; + EFM_ASSERT(comLine < 8); + + bitShift = comLine * 4; + LCD->SEGD4L = (LCD->SEGD4L & ~(0xF << bitShift)) | (biasLevel << bitShift); +} +#endif + +/** @} (end addtogroup LCD) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(LCD_COUNT) && (LCD_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_ldma.c b/cpu/efm32_common/emlib/src/em_ldma.c new file mode 100644 index 0000000000000..139fb8d089b90 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_ldma.c @@ -0,0 +1,499 @@ +/***************************************************************************//** + * @file em_ldma.c + * @brief Direct memory access (LDMA) module peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_ldma.h" + +#if defined( LDMA_PRESENT ) && ( LDMA_COUNT == 1 ) + +#include +#include "em_assert.h" +#include "em_bus.h" +#include "em_cmu.h" +#include "em_int.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + @addtogroup LDMA + @brief Direct Memory Access (LDMA) Peripheral API + @details + The LDMA API functions provide full support for the LDMA peripheral. + + The LDMA supports these DMA transfer types: + + @li Memory to memory. + @li Memory to peripheral. + @li Peripheral to memory. + @li Peripheral to peripheral. + @li Constant value to memory. + + The LDMA supports linked lists of DMA descriptors allowing: + + @li Circular and ping-pong buffer transfers. + @li Scatter-gather transfers. + @li Looped transfers. + + The LDMA has some advanced features: + + @li Intra-channel synchronization (SYNC), allowing hardware events to + pause and restart a DMA sequence. + @li Immediate-write (WRI), allowing the DMA to write a constant anywhere + in the memory map. + @li Complex flow control allowing if-else constructs. + + A basic understanding of the LDMA controller is assumed. Please refer to + the reference manual for further details. The LDMA examples described + in the reference manual are particularly helpful in understanding LDMA + operations. + + In order to use the DMA controller, the initialization function @ref + LDMA_Init() must have been executed once (normally during system init). + + DMA transfers are initiated by a call to @ref LDMA_StartTransfer(), the + transfer properties are controlled by the contents of @ref LDMA_TransferCfg_t + and @ref LDMA_Descriptor_t structure parameters. + The @htmlonly LDMA_Descriptor_t @endhtmlonly structure parameter may be a + pointer to an array of descriptors, the descriptors in the array should + be linked together as needed. + + Transfer and descriptor initialization macros are provided for the most common + transfer types. Due to the flexibility of the LDMA peripheral only a small + subset of all possible initializer macros are provided, the user should create + new one's when needed. + + @note The LDMA module does not implement the LDMA interrupt handler. A + template for a handler is include in the code. + + Examples of LDMA usage: + + A simple memory to memory transfer: + @verbatim + // A single transfer of 4 half words. + + const LDMA_TransferCfg_t memTransfer = LDMA_TRANSFER_CFG_MEMORY(); + const LDMA_Descriptor_t xfer = LDMA_DESCRIPTOR_SINGLE_M2M_HALF( src, dest, 4 ); + LDMA_Init_t init = LDMA_INIT_DEFAULT; + + LDMA_Init( &init ); + + LDMA_StartTransfer( 0, (void*)&memTransfer, (void*)&xfer ); + @endverbatim + + @n A list of two memory to memory transfers: + @verbatim + // A transfer of 4 half words which links to another transfer of 4 half words. + + const LDMA_TransferCfg_t memTransfer = LDMA_TRANSFER_CFG_MEMORY(); + + const LDMA_Descriptor_t xfer[] = + { + LDMA_DESCRIPTOR_LINKREL_M2M_HALF( src, dest , 4, 1 ), + LDMA_DESCRIPTOR_SINGLE_M2M_HALF ( src, dest + 10, 4 ) + }; + + LDMA_Init_t init = LDMA_INIT_DEFAULT; + + LDMA_Init( &init ); + + LDMA_StartTransfer( 0, (void*)&memTransfer, (void*)&xfer ); + @endverbatim + + @n A list of three memory to memory transfers: + @verbatim + // A transfer of 4 half words which links to another transfer of 4 half words, + // which again links to a third transfer of 4 half words. + + const LDMA_TransferCfg_t memTransfer = LDMA_TRANSFER_CFG_MEMORY(); + + const LDMA_Descriptor_t xfer[] = + { + LDMA_DESCRIPTOR_LINKREL_M2M_HALF( src , dest , 4, 1 ), + LDMA_DESCRIPTOR_LINKREL_M2M_HALF( src + 2, dest + 5 , 4, 1 ), + LDMA_DESCRIPTOR_SINGLE_M2M_HALF ( src + 4, dest + 10, 4 ) + }; + + LDMA_Init_t init = LDMA_INIT_DEFAULT; + + LDMA_Init( &init ); + + LDMA_StartTransfer( 0, (void*)&memTransfer, (void*)&xfer ); + @endverbatim + + @n DMA from serial port peripheral to memory: + @verbatim + // Transfer 4 chars from USART1. + + const LDMA_TransferCfg_t periTransferRx = + LDMA_TRANSFER_CFG_PERIPHERAL( ldmaPeripheralSignal_USART1_RXDATAV ); + + const LDMA_Descriptor_t xfer = + LDMA_DESCRIPTOR_SINGLE_P2M_BYTE( &USART1->RXDATA, // Peripheral address + dest, // Destination (SRAM) + 4 ); // Number of bytes + + LDMA_Init_t init = LDMA_INIT_DEFAULT; + + LDMA_Init( &init ); + + LDMA_StartTransfer( 0, (void*)&periTransferRx, (void*)&xfer ); + @endverbatim + + @n Ping pong DMA from serial port peripheral to memory: + @verbatim + // Ping Pong transfer from USART1. + + static char buff1[5]; + static char buff2[5]; + + const LDMA_TransferCfg_t periTransferRx = + LDMA_TRANSFER_CFG_PERIPHERAL( ldmaPeripheralSignal_USART1_RXDATAV ); + + const LDMA_Descriptor_t xfer[] = + { + // Note the 1 and -1 link jump increments. This will cause each DMA transfer + // to link to the other one in an endless loop. + LDMA_DESCRIPTOR_LINKREL_P2M_BYTE( &USART1->RXDATA, // Peripheral address + buff1, // Destination (SRAM) + 5, // Number of bytes + 1 ), // Next descriptor + LDMA_DESCRIPTOR_LINKREL_P2M_BYTE( &USART1->RXDATA, // Peripheral address + buff2, // Destination (SRAM) + 5, // Number of bytes + -1 ) // Next descriptor + }; + + LDMA_Init_t init = LDMA_INIT_DEFAULT; + + LDMA_Init( &init ); + + LDMA_StartTransfer( 0, (void*)&periTransferRx, (void*)&xfer ); + @endverbatim + * @{ *************************************************************************/ + +#if defined( LDMA_IRQ_HANDLER_TEMPLATE ) +/***************************************************************************//** + * @brief + * Template for an LDMA IRQ handler. + ******************************************************************************/ +void LDMA_IRQHandler( void ) +{ + uint32_t pending, chnum, chmask; + + /* Get all pending and enabled interrupts */ + pending = LDMA->IF; + pending &= LDMA->IEN; + + /* Check for LDMA error */ + if ( pending & LDMA_IF_ERROR ) + { + /* Loop here to enable the debugger to see what has happened */ + while (1) + ; + } + + /* Iterate over all LDMA channels. */ + for ( chnum = 0, chmask = 1; + chnum < DMA_CHAN_COUNT; + chnum++, chmask <<= 1 ) + { + if ( pending & chmask ) + { + /* Clear interrupt flag. */ + LDMA->IFC = chmask; + + /* Do more stuff here, execute callbacks etc. */ + } + } +} +#endif + +/***************************************************************************//** + * @brief + * De-initialize the LDMA controller. + * + * LDMA interrupts are disabled and the LDMA clock is stopped. + ******************************************************************************/ +void LDMA_DeInit( void ) +{ + NVIC_DisableIRQ( LDMA_IRQn ); + LDMA->IEN = 0; + LDMA->CHEN = 0; + CMU_ClockEnable( cmuClock_LDMA, false ); +} + +/***************************************************************************//** + * @brief + * Initialize the LDMA controller. + * + * @param[in] init + * Pointer to initialization structure used to configure the setup. + ******************************************************************************/ +void LDMA_Init( LDMA_Init_t *init ) +{ + EFM_ASSERT( init != NULL ); + EFM_ASSERT( !( ( init->ldmaInitCtrlNumFixed << _LDMA_CTRL_NUMFIXED_SHIFT ) + & ~_LDMA_CTRL_NUMFIXED_MASK ) ); + EFM_ASSERT( !( ( init->ldmaInitCtrlSyncPrsClrEn << _LDMA_CTRL_SYNCPRSCLREN_SHIFT ) + & ~_LDMA_CTRL_SYNCPRSCLREN_MASK ) ); + EFM_ASSERT( !( ( init->ldmaInitCtrlSyncPrsSetEn << _LDMA_CTRL_SYNCPRSSETEN_SHIFT ) + & ~_LDMA_CTRL_SYNCPRSSETEN_MASK ) ); + EFM_ASSERT( init->ldmaInitIrqPriority < ( 1 << __NVIC_PRIO_BITS ) ); + + CMU_ClockEnable( cmuClock_LDMA, true ); + + LDMA->CTRL = ( init->ldmaInitCtrlNumFixed << _LDMA_CTRL_NUMFIXED_SHIFT ) + | ( init->ldmaInitCtrlSyncPrsClrEn << _LDMA_CTRL_SYNCPRSCLREN_SHIFT ) + | ( init->ldmaInitCtrlSyncPrsSetEn << _LDMA_CTRL_SYNCPRSSETEN_SHIFT ); + + LDMA->CHEN = 0; + LDMA->DBGHALT = 0; + LDMA->REQDIS = 0; + + /* Enable LDMA error interrupt. */ + LDMA->IEN = LDMA_IEN_ERROR; + LDMA->IFC = 0xFFFFFFFF; + + NVIC_ClearPendingIRQ( LDMA_IRQn ); + + /* Range is 0..7, 0 is highest priority. */ + NVIC_SetPriority( LDMA_IRQn, init->ldmaInitIrqPriority ); + + NVIC_EnableIRQ( LDMA_IRQn ); +} + +/***************************************************************************//** + * @brief + * Start a DMA transfer. + * + * @param[in] ch + * DMA channel. + * + * @param[in] transfer + * Initialization structure used to configure the transfer. + * + * @param[in] descriptor + * Transfer descriptor, can be an array of descriptors linked together. + ******************************************************************************/ +void LDMA_StartTransfer( int ch, + LDMA_TransferCfg_t *transfer, + LDMA_Descriptor_t *descriptor ) +{ + uint32_t tmp; + uint32_t chMask = 1 << ch; + + EFM_ASSERT( ch < DMA_CHAN_COUNT ); + EFM_ASSERT( transfer != NULL ); + EFM_ASSERT( !( transfer->ldmaReqSel & ~_LDMA_CH_REQSEL_MASK ) ); + + EFM_ASSERT( !( ( transfer->ldmaCtrlSyncPrsClrOff << _LDMA_CTRL_SYNCPRSCLREN_SHIFT ) + & ~_LDMA_CTRL_SYNCPRSCLREN_MASK ) ); + EFM_ASSERT( !( ( transfer->ldmaCtrlSyncPrsClrOn << _LDMA_CTRL_SYNCPRSCLREN_SHIFT ) + & ~_LDMA_CTRL_SYNCPRSCLREN_MASK ) ); + EFM_ASSERT( !( ( transfer->ldmaCtrlSyncPrsSetOff << _LDMA_CTRL_SYNCPRSSETEN_SHIFT ) + & ~_LDMA_CTRL_SYNCPRSSETEN_MASK ) ); + EFM_ASSERT( !( ( transfer->ldmaCtrlSyncPrsSetOn << _LDMA_CTRL_SYNCPRSSETEN_SHIFT ) + & ~_LDMA_CTRL_SYNCPRSSETEN_MASK ) ); + + EFM_ASSERT( !( ( transfer->ldmaCfgArbSlots << _LDMA_CH_CFG_ARBSLOTS_SHIFT ) + & ~_LDMA_CH_CFG_ARBSLOTS_MASK ) ); + EFM_ASSERT( !( ( transfer->ldmaCfgSrcIncSign << _LDMA_CH_CFG_SRCINCSIGN_SHIFT ) + & ~_LDMA_CH_CFG_SRCINCSIGN_MASK ) ); + EFM_ASSERT( !( ( transfer->ldmaCfgDstIncSign << _LDMA_CH_CFG_DSTINCSIGN_SHIFT ) + & ~_LDMA_CH_CFG_DSTINCSIGN_MASK ) ); + EFM_ASSERT( !( ( transfer->ldmaLoopCnt << _LDMA_CH_LOOP_LOOPCNT_SHIFT ) + & ~_LDMA_CH_LOOP_LOOPCNT_MASK ) ); + + LDMA->CH[ ch ].REQSEL = transfer->ldmaReqSel; + + LDMA->CH[ ch ].LOOP = + ( transfer->ldmaLoopCnt << _LDMA_CH_LOOP_LOOPCNT_SHIFT ); + + LDMA->CH[ ch ].CFG = + ( transfer->ldmaCfgArbSlots << _LDMA_CH_CFG_ARBSLOTS_SHIFT ) + | ( transfer->ldmaCfgSrcIncSign << _LDMA_CH_CFG_SRCINCSIGN_SHIFT ) + | ( transfer->ldmaCfgDstIncSign << _LDMA_CH_CFG_DSTINCSIGN_SHIFT ); + + /* Set descriptor address. */ + LDMA->CH[ ch ].LINK = (uint32_t)descriptor & _LDMA_CH_LINK_LINKADDR_MASK; + + /* Clear pending channel interrupt. */ + LDMA->IFC = chMask; + + /* Critical region. */ + INT_Disable(); + + /* Enable channel interrupt. */ + LDMA->IEN |= chMask; + + if ( transfer->ldmaReqDis ) + { + LDMA->REQDIS |= chMask; + } + + if ( transfer->ldmaDbgHalt ) + { + LDMA->DBGHALT |= chMask; + } + + tmp = LDMA->CTRL; + + if ( transfer->ldmaCtrlSyncPrsClrOff ) + { + tmp &= ~_LDMA_CTRL_SYNCPRSCLREN_MASK + | (~transfer->ldmaCtrlSyncPrsClrOff << _LDMA_CTRL_SYNCPRSCLREN_SHIFT); + } + + if ( transfer->ldmaCtrlSyncPrsClrOn ) + { + tmp |= transfer->ldmaCtrlSyncPrsClrOn << _LDMA_CTRL_SYNCPRSCLREN_SHIFT; + } + + if ( transfer->ldmaCtrlSyncPrsSetOff ) + { + tmp &= ~_LDMA_CTRL_SYNCPRSSETEN_MASK + | (~transfer->ldmaCtrlSyncPrsSetOff << _LDMA_CTRL_SYNCPRSSETEN_SHIFT); + } + + if ( transfer->ldmaCtrlSyncPrsSetOn ) + { + tmp |= transfer->ldmaCtrlSyncPrsSetOn << _LDMA_CTRL_SYNCPRSSETEN_SHIFT; + } + + LDMA->CTRL = tmp; + + BUS_RegMaskedClear(&LDMA->CHDONE, chMask); /* Clear the done flag. */ + LDMA->LINKLOAD = chMask; /* Enable descriptor load. */ + BUS_RegMaskedSet(&LDMA->CHEN, chMask); /* Enable channel. */ + + /* Critical region end. */ + INT_Enable(); +} + +/***************************************************************************//** + * @brief + * Stop a DMA transfer. + * + * @note + * The DMA will complete the current AHB burst transfer before stopping. + * + * @param[in] ch + * DMA channel to stop. + ******************************************************************************/ +void LDMA_StopTransfer( int ch ) +{ + uint32_t chMask = 1 << ch; + + EFM_ASSERT( ch < DMA_CHAN_COUNT ); + + INT_Disable(); + + LDMA->IEN &= ~chMask; + BUS_RegMaskedClear(&LDMA->CHEN, chMask); + + INT_Enable(); +} + +/***************************************************************************//** + * @brief + * Check if a DMA transfer has completed. + * + * @param[in] ch + * DMA channel to check. + * + * @return + * True if transfer has completed, false if not. + ******************************************************************************/ +bool LDMA_TransferDone( int ch ) +{ + bool retVal = false; + uint32_t chMask = 1 << ch; + + EFM_ASSERT( ch < DMA_CHAN_COUNT ); + + INT_Disable(); + if ( ( ( LDMA->CHEN & chMask ) == 0 ) + && ( ( LDMA->CHDONE & chMask ) == chMask ) ) + { + retVal = true; + } + INT_Enable(); + return retVal; +} + +/***************************************************************************//** + * @brief + * Get number of items remaining in a transfer. + * + * @note + * This function is does not take into account that a DMA transfers with + * a chain of linked transfers might be ongoing. It will only check the + * count for the current transfer. + * + * @param[in] ch + * The channel number of the transfer to check. + * + * @return + * Number of items remaining in the transfer. + ******************************************************************************/ +uint32_t LDMA_TransferRemainingCount( int ch ) +{ + uint32_t remaining, done, iflag; + uint32_t chMask = 1 << ch; + + EFM_ASSERT( ch < DMA_CHAN_COUNT ); + + INT_Disable(); + iflag = LDMA->IF; + done = LDMA->CHDONE; + remaining = LDMA->CH[ ch ].CTRL; + INT_Enable(); + + iflag &= chMask; + done &= chMask; + remaining = ( remaining + & _LDMA_CH_CTRL_XFERCNT_MASK ) + >> _LDMA_CH_CTRL_XFERCNT_SHIFT; + + if ( done || ( ( remaining == 0 ) && iflag ) ) + { + return 0; + } + + return remaining + 1; +} + +/** @} (end addtogroup LDMA) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined( LDMA_PRESENT ) && ( LDMA_COUNT == 1 ) */ diff --git a/cpu/efm32_common/emlib/src/em_lesense.c b/cpu/efm32_common/emlib/src/em_lesense.c new file mode 100644 index 0000000000000..686efa6a65e10 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_lesense.c @@ -0,0 +1,1120 @@ +/***************************************************************************//** + * @file em_lesense.c + * @brief Low Energy Sensor (LESENSE) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_lesense.h" +#if defined(LESENSE_COUNT) && (LESENSE_COUNT > 0) +#include "em_assert.h" +#include "em_bus.h" +#include "em_cmu.h" + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +#if !defined(UINT32_MAX) +#define UINT32_MAX ((uint32_t)(0xFFFFFFFF)) +#endif +/** @endcond */ + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LESENSE + * @brief Low Energy Sensor (LESENSE) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initialize the LESENSE module. + * + * @details + * This function configures the main parameters of the LESENSE interface. + * Please refer to the initialization parameter type definition + * (@ref LESENSE_Init_TypeDef) for more details. + * + * @note + * @ref LESENSE_Init() has been designed for initializing LESENSE once in an + * operation cycle. Be aware of the effects of reconfiguration if using this + * function from multiple sources in your code. This function has not been + * designed to be re-entrant. + * Requesting reset by setting @p reqReset to true is required in each reset + * or power-on cycle in order to configure the default values of the RAM + * mapped LESENSE registers. + * Notice that GPIO pins used by the LESENSE module must be properly + * configured by the user explicitly, in order for the LESENSE 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 + * LESENSE initialization structure. + * + * @param[in] reqReset + * Request to call @ref LESENSE_Reset() first in order to initialize all + * LESENSE registers with the default value. + ******************************************************************************/ +void LESENSE_Init(LESENSE_Init_TypeDef const *init, bool const reqReset) +{ + /* Sanity check of initialization values */ + EFM_ASSERT((uint32_t)init->timeCtrl.startDelay < 4U); + EFM_ASSERT((uint32_t)init->perCtrl.dacPresc < 32U); + + /* Reset LESENSE registers if requested. */ + if (reqReset) + { + LESENSE_Reset(); + } + + /* Set sensor start delay for each channel. */ + LESENSE_StartDelaySet((uint32_t)init->timeCtrl.startDelay); + + /* LESENSE core control configuration. + * Set PRS source, SCANCONF register usage strategy, interrupt and + * DMA trigger level condition, DMA wakeup condition, bias mode, + * enable/disable to sample both ACMPs simultaneously, enable/disable to store + * SCANRES in CNT_RES after each scan, enable/disable to always write to the + * result buffer, even if it is full, enable/disable LESENSE running in debug + * mode. */ + LESENSE->CTRL = + ((uint32_t)init->coreCtrl.prsSel << _LESENSE_CTRL_PRSSEL_SHIFT) + | (uint32_t)init->coreCtrl.scanConfSel + | (uint32_t)init->coreCtrl.bufTrigLevel + | (uint32_t)init->coreCtrl.wakeupOnDMA + | ((uint32_t)init->coreCtrl.invACMP0 << _LESENSE_CTRL_ACMP0INV_SHIFT) + | ((uint32_t)init->coreCtrl.invACMP1 << _LESENSE_CTRL_ACMP1INV_SHIFT) + | ((uint32_t)init->coreCtrl.dualSample << _LESENSE_CTRL_DUALSAMPLE_SHIFT) + | ((uint32_t)init->coreCtrl.storeScanRes << _LESENSE_CTRL_STRSCANRES_SHIFT) + | ((uint32_t)init->coreCtrl.bufOverWr << _LESENSE_CTRL_BUFOW_SHIFT) + | ((uint32_t)init->coreCtrl.debugRun << _LESENSE_CTRL_DEBUGRUN_SHIFT); + + /* Set scan mode in the CTRL register using the provided function, don't + * start scanning immediately. */ + LESENSE_ScanModeSet((LESENSE_ScanMode_TypeDef)init->coreCtrl.scanStart, false); + + /* LESENSE peripheral control configuration. + * Set DAC0 and DAC1 data source, conversion mode, output mode. Set DAC + * prescaler and reference. Set ACMP0 and ACMP1 control mode. Set ACMP and DAC + * duty cycle (warm up) mode. */ + LESENSE->PERCTRL = + ((uint32_t)init->perCtrl.dacCh0Data << _LESENSE_PERCTRL_DACCH0DATA_SHIFT) + | ((uint32_t)init->perCtrl.dacCh0ConvMode << _LESENSE_PERCTRL_DACCH0CONV_SHIFT) + | ((uint32_t)init->perCtrl.dacCh0OutMode << _LESENSE_PERCTRL_DACCH0OUT_SHIFT) + | ((uint32_t)init->perCtrl.dacCh1Data << _LESENSE_PERCTRL_DACCH1DATA_SHIFT) + | ((uint32_t)init->perCtrl.dacCh1ConvMode << _LESENSE_PERCTRL_DACCH1CONV_SHIFT) + | ((uint32_t)init->perCtrl.dacCh1OutMode << _LESENSE_PERCTRL_DACCH1OUT_SHIFT) + | ((uint32_t)init->perCtrl.dacPresc << _LESENSE_PERCTRL_DACPRESC_SHIFT) + | (uint32_t)init->perCtrl.dacRef + | ((uint32_t)init->perCtrl.acmp0Mode << _LESENSE_PERCTRL_ACMP0MODE_SHIFT) + | ((uint32_t)init->perCtrl.acmp1Mode << _LESENSE_PERCTRL_ACMP1MODE_SHIFT) + | (uint32_t)init->perCtrl.warmupMode; + + /* LESENSE decoder general control configuration. + * Set decoder input source, select PRS input for decoder bits. + * Enable/disable the decoder to check the present state. + * Enable/disable decoder to channel interrupt mapping. + * Enable/disable decoder hysteresis on PRS output. + * Enable/disable decoder hysteresis on count events. + * Enable/disable decoder hysteresis on interrupt requests. + * Enable/disable count mode on LESPRS0 and LESPRS1. */ + LESENSE->DECCTRL = + (uint32_t)init->decCtrl.decInput + | ((uint32_t)init->decCtrl.prsChSel0 << _LESENSE_DECCTRL_PRSSEL0_SHIFT) + | ((uint32_t)init->decCtrl.prsChSel1 << _LESENSE_DECCTRL_PRSSEL1_SHIFT) + | ((uint32_t)init->decCtrl.prsChSel2 << _LESENSE_DECCTRL_PRSSEL2_SHIFT) + | ((uint32_t)init->decCtrl.prsChSel3 << _LESENSE_DECCTRL_PRSSEL3_SHIFT) + | ((uint32_t)init->decCtrl.chkState << _LESENSE_DECCTRL_ERRCHK_SHIFT) + | ((uint32_t)init->decCtrl.intMap << _LESENSE_DECCTRL_INTMAP_SHIFT) + | ((uint32_t)init->decCtrl.hystPRS0 << _LESENSE_DECCTRL_HYSTPRS0_SHIFT) + | ((uint32_t)init->decCtrl.hystPRS1 << _LESENSE_DECCTRL_HYSTPRS1_SHIFT) + | ((uint32_t)init->decCtrl.hystPRS2 << _LESENSE_DECCTRL_HYSTPRS2_SHIFT) + | ((uint32_t)init->decCtrl.hystIRQ << _LESENSE_DECCTRL_HYSTIRQ_SHIFT) + | ((uint32_t)init->decCtrl.prsCount << _LESENSE_DECCTRL_PRSCNT_SHIFT); + + /* Set initial LESENSE decoder state. */ + LESENSE_DecoderStateSet((uint32_t)init->decCtrl.initState); + + /* LESENSE bias control configuration. */ + LESENSE->BIASCTRL = (uint32_t)init->coreCtrl.biasMode; +} + + +/***************************************************************************//** + * @brief + * Set scan frequency for periodic scanning. + * + * @details + * This function only applies to LESENSE if period counter is being used as + * a trigger for scan start. + * The calculation is based on the following formula: + * Fscan = LFACLKles / ((1+PCTOP)*2^PCPRESC) + * + * @note + * Note that the calculation does not necessarily result in the requested + * scan frequency due to integer division. Check the return value for the + * resulted scan frequency. + * + * @param[in] refFreq + * Select reference LFACLK clock frequency in Hz. If set to 0, the current + * clock frequency is being used as a reference. + * + * @param[in] scanFreq + * Set the desired scan frequency in Hz. + * + * @return + * Frequency in Hz calculated and set by this function. Users can use this to + * compare the requested and set values. + ******************************************************************************/ +uint32_t LESENSE_ScanFreqSet(uint32_t refFreq, uint32_t const scanFreq) +{ + uint32_t tmp; + uint32_t pcPresc = 0UL; /* Period counter prescaler. */ + uint32_t clkDiv = 1UL; /* Clock divisor value (2^pcPresc). */ + uint32_t pcTop = 63UL; /* Period counter top value (max. 63). */ + uint32_t calcScanFreq; /* Variable for testing the calculation algorithm. */ + + + /* If refFreq is set to 0, the currently configured reference clock is + * assumed. */ + if (!refFreq) + { + refFreq = CMU_ClockFreqGet(cmuClock_LESENSE); + } + + /* Max. value of pcPresc is 128, thus using reference frequency less than + * 33554431Hz (33.554431MHz), the frequency calculation in the while loop + * below will not overflow. */ + EFM_ASSERT(refFreq < ((uint32_t)UINT32_MAX / 128UL)); + + /* Sanity check of scan frequency value. */ + EFM_ASSERT((scanFreq > 0U) && (scanFreq <= refFreq)); + + /* Calculate the minimum necessary prescaler value in order to provide the + * biggest possible resolution for setting scan frequency. + * Maximum number of calculation cycles is 7 (value of lesenseClkDiv_128). */ + while ((refFreq / ((uint32_t)scanFreq * clkDiv) > (pcTop + 1UL)) + && (pcPresc < lesenseClkDiv_128)) + { + ++pcPresc; + clkDiv = (uint32_t)1UL << pcPresc; + } + + /* Calculate pcTop value. */ + pcTop = ((uint32_t)refFreq / ((uint32_t)scanFreq * clkDiv)) - 1UL; + + /* Clear current PCPRESC and PCTOP settings. Be aware of the effect of + * non-atomic Read-Modify-Write on LESENSE->TIMCRTL. */ + tmp = LESENSE->TIMCTRL & (~_LESENSE_TIMCTRL_PCPRESC_MASK + & ~_LESENSE_TIMCTRL_PCTOP_MASK); + + /* Set new values in tmp while reserving other settings. */ + tmp |= ((uint32_t)pcPresc << _LESENSE_TIMCTRL_PCPRESC_SHIFT) + | ((uint32_t)pcTop << _LESENSE_TIMCTRL_PCTOP_SHIFT); + + /* Set values in LESENSE_TIMCTRL register. */ + LESENSE->TIMCTRL = tmp; + + /* For testing the calculation algorithm. */ + calcScanFreq = ((uint32_t)refFreq / ((uint32_t)(1UL + pcTop) * clkDiv)); + + return calcScanFreq; +} + + +/***************************************************************************//** + * @brief + * Set scan mode of the LESENSE channels. + * + * @details + * This function configures how the scan start is being triggered. It can be + * used for re-configuring the scan mode while running the application but it + * is also used by LESENSE_Init() for initialization. + * + * @note + * Users can configure the scan mode by LESENSE_Init() function, but only with + * a significant overhead. This simple function serves the purpose of + * controlling this parameter after the channel has been configured. + * Please be aware the effects of the non-atomic Read-Modify-Write cycle! + * + * @param[in] scanMode + * Select where to map LESENSE alternate excitation channels. + * @li lesenseScanStartPeriodic - New scan is started each time the period + * counter overflows. + * @li lesenseScanStartOneShot - Single scan is performed when + * LESENSE_ScanStart() is called. + * @li lesenseScanStartPRS - New scan is triggered by pulse on PRS channel. + * + * @param[in] start + * If true, LESENSE_ScanStart() is immediately issued after configuration. + ******************************************************************************/ +void LESENSE_ScanModeSet(LESENSE_ScanMode_TypeDef const scanMode, + bool const start) +{ + uint32_t tmp; /* temporary storage of the CTRL register value */ + + + /* Save the CTRL register value to tmp. + * Please be aware the effects of the non-atomic Read-Modify-Write cycle! */ + tmp = LESENSE->CTRL & ~(_LESENSE_CTRL_SCANMODE_MASK); + /* Setting the requested scanMode to the CTRL register. Casting signed int + * (enum) to unsigned long (uint32_t). */ + tmp |= (uint32_t)scanMode; + + /* Write the new value to the CTRL register. */ + LESENSE->CTRL = tmp; + + /* Start sensor scanning if requested. */ + if (start) + { + LESENSE_ScanStart(); + } +} + + +/***************************************************************************//** + * @brief + * Set start delay of sensor interaction on each channel. + * + * @details + * This function sets start delay of sensor interaction on each channel. + * It can be used for adjusting the start delay while running the application + * but it is also used by LESENSE_Init() for initialization. + * + * @note + * Users can configure the start delay by LESENSE_Init() function, but only + * with a significant overhead. This simple function serves the purpose of + * controlling this parameter after the channel has been configured. + * Please be aware the effects of the non-atomic Read-Modify-Write cycle! + * + * @param[in] startDelay + * Number of LFACLK cycles to delay. Valid range: 0-3 (2 bit). + ******************************************************************************/ +void LESENSE_StartDelaySet(uint8_t const startDelay) +{ + uint32_t tmp; /* temporary storage of the TIMCTRL register value */ + + + /* Sanity check of startDelay. */ + EFM_ASSERT(startDelay < 4U); + + /* Save the TIMCTRL register value to tmp. + * Please be aware the effects of the non-atomic Read-Modify-Write cycle! */ + tmp = LESENSE->TIMCTRL & ~(_LESENSE_TIMCTRL_STARTDLY_MASK); + /* Setting the requested startDelay to the TIMCTRL register. */ + tmp |= (uint32_t)startDelay << _LESENSE_TIMCTRL_STARTDLY_SHIFT; + + /* Write the new value to the TIMCTRL register. */ + LESENSE->TIMCTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Set clock division for LESENSE timers. + * + * @details + * Use this function to configure the clock division for the LESENSE timers + * used for excitation timing. + * The division setting is global, but the clock source can be selected for + * each channel using LESENSE_ChannelConfig() function, please refer to the + * documentation of it for more details. + * + * @note + * If AUXHFRCO is used for excitation timing, LFACLK can not exceed 500kHz. + * LFACLK can not exceed 50kHz if the ACMP threshold level (ACMPTHRES) is not + * equal for all channels. + * + * @param[in] clk + * Select clock to prescale. + * @li lesenseClkHF - set AUXHFRCO clock divisor for HF timer. + * @li lesenseClkLF - set LFACLKles clock divisor for LF timer. + * + * @param[in] clkDiv + * Clock divisor value. Valid range depends on the @p clk value. + ******************************************************************************/ +void LESENSE_ClkDivSet(LESENSE_ChClk_TypeDef const clk, + LESENSE_ClkPresc_TypeDef const clkDiv) +{ + uint32_t tmp; + + + /* Select clock to prescale */ + switch (clk) + { + case lesenseClkHF: + /* Sanity check of clock divisor for HF clock. */ + EFM_ASSERT((uint32_t)clkDiv <= lesenseClkDiv_8); + + /* Clear current AUXPRESC settings. */ + tmp = LESENSE->TIMCTRL & ~(_LESENSE_TIMCTRL_AUXPRESC_MASK); + + /* Set new values in tmp while reserving other settings. */ + tmp |= ((uint32_t)clkDiv << _LESENSE_TIMCTRL_AUXPRESC_SHIFT); + + /* Set values in LESENSE_TIMCTRL register. */ + LESENSE->TIMCTRL = tmp; + break; + + case lesenseClkLF: + /* Clear current LFPRESC settings. */ + tmp = LESENSE->TIMCTRL & ~(_LESENSE_TIMCTRL_LFPRESC_MASK); + + /* Set new values in tmp while reserving other settings. */ + tmp |= ((uint32_t)clkDiv << _LESENSE_TIMCTRL_LFPRESC_SHIFT); + + /* Set values in LESENSE_TIMCTRL register. */ + LESENSE->TIMCTRL = tmp; + break; + + default: + EFM_ASSERT(0); + break; + } +} + + +/***************************************************************************//** + * @brief + * Configure all (16) LESENSE sensor channels. + * + * @details + * This function configures all the sensor channels of LESENSE interface. + * Please refer to the configuration parameter type definition + * (LESENSE_ChAll_TypeDef) for more details. + * + * @note + * Channels can be configured individually using LESENSE_ChannelConfig() + * function. + * Notice that pins used by the LESENSE module must be properly configured + * by the user explicitly, in order for the LESENSE 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] confChAll + * Configuration structure for all (16) LESENSE sensor channels. + ******************************************************************************/ +void LESENSE_ChannelAllConfig(LESENSE_ChAll_TypeDef const *confChAll) +{ + uint32_t i; + + /* Iterate through all the 16 channels */ + for (i = 0U; i < 16U; ++i) + { + /* Configure scan channels. */ + LESENSE_ChannelConfig(&confChAll->Ch[i], i); + } +} + + +/***************************************************************************//** + * @brief + * Configure a single LESENSE sensor channel. + * + * @details + * This function configures a single sensor channel of the LESENSE interface. + * Please refer to the configuration parameter type definition + * (LESENSE_ChDesc_TypeDef) for more details. + * + * @note + * This function has been designed to minimize the effects of sensor channel + * reconfiguration while LESENSE is in operation, however one shall be aware + * of these effects and the right timing of calling this function. + * Parameter @p useAltEx must be true in the channel configuration in order to + * use alternate excitation pins. + * + * @param[in] confCh + * Configuration structure for a single LESENSE sensor channel. + * + * @param[in] chIdx + * Channel index to configure (0-15). + ******************************************************************************/ +void LESENSE_ChannelConfig(LESENSE_ChDesc_TypeDef const *confCh, + uint32_t const chIdx) +{ + uint32_t tmp; /* Service variable. */ + + + /* Sanity check of configuration parameters */ + EFM_ASSERT(chIdx < 16U); + EFM_ASSERT(confCh->exTime < 64U); + EFM_ASSERT(confCh->sampleDelay < 128U); + EFM_ASSERT(confCh->measDelay < 128U); + /* Not a complete assert, as the max. value of acmpThres depends on other + * configuration parameters, check the parameter description of acmpThres for + * for more details! */ + EFM_ASSERT(confCh->acmpThres < 4096U); + EFM_ASSERT(!(confCh->chPinExMode == lesenseChPinExDACOut + && (chIdx != 2U) + && (chIdx != 3U) + && (chIdx != 4U) + && (chIdx != 5U))); + EFM_ASSERT(!(confCh->chPinIdleMode == lesenseChPinIdleDACCh1 + && ((chIdx != 12U) + && (chIdx != 13U) + && (chIdx != 14U) + && (chIdx != 15U)))); + EFM_ASSERT(!(confCh->chPinIdleMode == lesenseChPinIdleDACCh0 + && ((chIdx != 0U) + && (chIdx != 1U) + && (chIdx != 2U) + && (chIdx != 3U)))); + + /* Configure chIdx setup in LESENSE idle phase. + * Read-modify-write in order to support reconfiguration during LESENSE + * operation. */ + tmp = (LESENSE->IDLECONF & ~((uint32_t)0x3UL << (chIdx * 2UL))); + tmp |= ((uint32_t)confCh->chPinIdleMode << (chIdx * 2UL)); + LESENSE->IDLECONF = tmp; + + /* Channel specific timing configuration on scan channel chIdx. + * Set excitation time, sampling delay, measurement delay. */ + LESENSE_ChannelTimingSet(chIdx, + (uint32_t)confCh->exTime, + (uint32_t)confCh->sampleDelay, + (uint32_t)confCh->measDelay); + + /* Channel specific configuration of clocks, sample mode, excitation pin mode + * alternate excitation usage and interrupt mode on scan channel chIdx in + * LESENSE_CHchIdx_INTERACT. */ + LESENSE->CH[chIdx].INTERACT = + ((uint32_t)confCh->exClk << _LESENSE_CH_INTERACT_EXCLK_SHIFT) + | ((uint32_t)confCh->sampleClk << _LESENSE_CH_INTERACT_SAMPLECLK_SHIFT) + | (uint32_t)confCh->sampleMode + | (uint32_t)confCh->intMode + | (uint32_t)confCh->chPinExMode + | ((uint32_t)confCh->useAltEx << _LESENSE_CH_INTERACT_ALTEX_SHIFT); + + /* Configure channel specific counter comparison mode, optional result + * forwarding to decoder, optional counter value storing and optional result + * inverting on scan channel chIdx in LESENSE_CHchIdx_EVAL. */ + LESENSE->CH[chIdx].EVAL = + (uint32_t)confCh->compMode + | ((uint32_t)confCh->shiftRes << _LESENSE_CH_EVAL_DECODE_SHIFT) + | ((uint32_t)confCh->storeCntRes << _LESENSE_CH_EVAL_STRSAMPLE_SHIFT) + | ((uint32_t)confCh->invRes << _LESENSE_CH_EVAL_SCANRESINV_SHIFT); + + /* Configure analog comparator (ACMP) threshold and decision threshold for + * counter separately with the function provided for that. */ + LESENSE_ChannelThresSet(chIdx, + (uint32_t)confCh->acmpThres, + (uint32_t)confCh->cntThres); + + /* Enable/disable interrupts on channel */ + BUS_RegBitWrite(&(LESENSE->IEN), chIdx, confCh->enaInt); + + /* Enable/disable CHchIdx pin. */ + BUS_RegBitWrite(&(LESENSE->ROUTE), chIdx, confCh->enaPin); + + /* Enable/disable scan channel chIdx. */ + BUS_RegBitWrite(&(LESENSE->CHEN), chIdx, confCh->enaScanCh); +} + + +/***************************************************************************//** + * @brief + * Configure the LESENSE alternate excitation modes. + * + * @details + * This function configures the alternate excitation channels of the LESENSE + * interface. Please refer to the configuration parameter type definition + * (LESENSE_ConfAltEx_TypeDef) for more details. + * + * @note + * Parameter @p useAltEx must be true in the channel configuration structrure + * (LESENSE_ChDesc_TypeDef) in order to use alternate excitation pins on the + * channel. + * + * @param[in] confAltEx + * Configuration structure for LESENSE alternate excitation pins. + ******************************************************************************/ +void LESENSE_AltExConfig(LESENSE_ConfAltEx_TypeDef const *confAltEx) +{ + uint32_t i; + uint32_t tmp; + + + /* Configure alternate excitation mapping. + * Atomic read-modify-write using BUS_RegBitWrite function in order to + * support reconfiguration during LESENSE operation. */ + BUS_RegBitWrite(&(LESENSE->CTRL), + _LESENSE_CTRL_ALTEXMAP_SHIFT, + confAltEx->altExMap); + + switch (confAltEx->altExMap) + { + case lesenseAltExMapALTEX: + /* Iterate through the 8 possible alternate excitation pin descriptors. */ + for (i = 0U; i < 8U; ++i) + { + /* Enable/disable alternate excitation pin i. + * Atomic read-modify-write using BUS_RegBitWrite function in order to + * support reconfiguration during LESENSE operation. */ + BUS_RegBitWrite(&(LESENSE->ROUTE), + (16UL + i), + confAltEx->AltEx[i].enablePin); + + /* Setup the idle phase state of alternate excitation pin i. + * Read-modify-write in order to support reconfiguration during LESENSE + * operation. */ + tmp = (LESENSE->ALTEXCONF & ~((uint32_t)0x3UL << (i * 2UL))); + tmp |= ((uint32_t)confAltEx->AltEx[i].idleConf << (i * 2UL)); + LESENSE->ALTEXCONF = tmp; + + /* Enable/disable always excite on channel i */ + BUS_RegBitWrite(&(LESENSE->ALTEXCONF), + (16UL + i), + confAltEx->AltEx[i].alwaysEx); + } + break; + + case lesenseAltExMapACMP: + /* Iterate through all the 16 alternate excitation channels */ + for (i = 0U; i < 16U; ++i) + { + /* Enable/disable alternate ACMP excitation channel pin i. */ + /* Atomic read-modify-write using BUS_RegBitWrite function in order to + * support reconfiguration during LESENSE operation. */ + BUS_RegBitWrite(&(LESENSE->ROUTE), + i, + confAltEx->AltEx[i].enablePin); + } + break; + default: + /* Illegal value. */ + EFM_ASSERT(0); + break; + } +} + + +/***************************************************************************//** + * @brief + * Enable/disable LESENSE scan channel and the pin assigned to it. + * + * @details + * Use this function to enable/disable a selected LESENSE scan channel and the + * pin assigned to. + * + * @note + * Users can enable/disable scan channels and the channel pin by + * LESENSE_ChannelConfig() function, but only with a significant overhead. + * This simple function serves the purpose of controlling these parameters + * after the channel has been configured. + * + * @param[in] chIdx + * Identifier of the scan channel. Valid range: 0-15. + * + * @param[in] enaScanCh + * Enable/disable the selected scan channel by setting this parameter to + * true/false respectively. + * + * @param[in] enaPin + * Enable/disable the pin assigned to the channel selected by @p chIdx. + ******************************************************************************/ +void LESENSE_ChannelEnable(uint8_t const chIdx, + bool const enaScanCh, + bool const enaPin) +{ + /* Enable/disable the assigned pin of scan channel chIdx. + * Note: BUS_RegBitWrite() function is used for setting/clearing single + * bit peripheral register bitfields. Read the function description in + * em_bus.h for more details. */ + BUS_RegBitWrite(&(LESENSE->ROUTE), chIdx, enaPin); + + /* Enable/disable scan channel chIdx. */ + BUS_RegBitWrite(&(LESENSE->CHEN), chIdx, enaScanCh); +} + + +/***************************************************************************//** + * @brief + * Enable/disable LESENSE scan channel and the pin assigned to it. + * + * @details + * Use this function to enable/disable LESENSE scan channels and the pins + * assigned to them using a mask. + * + * @note + * Users can enable/disable scan channels and channel pins by using + * LESENSE_ChannelAllConfig() function, but only with a significant overhead. + * This simple function serves the purpose of controlling these parameters + * after the channel has been configured. + * + * @param[in] chMask + * Set the corresponding bit to 1 to enable, 0 to disable the selected scan + * channel. + * + * @param[in] pinMask + * Set the corresponding bit to 1 to enable, 0 to disable the pin on selected + * channel. + ******************************************************************************/ +void LESENSE_ChannelEnableMask(uint16_t chMask, uint16_t pinMask) +{ + /* Enable/disable all channels at once according to the mask. */ + LESENSE->CHEN = chMask; + /* Enable/disable all channel pins at once according to the mask. */ + LESENSE->ROUTE = pinMask; +} + + +/***************************************************************************//** + * @brief + * Set LESENSE channel timing parameters. + * + * @details + * Use this function to set timing parameters on a selected LESENSE channel. + * + * @note + * Users can configure the channel timing parameters by + * LESENSE_ChannelConfig() function, but only with a significant overhead. + * This simple function serves the purpose of controlling these parameters + * after the channel has been configured. + * + * @param[in] chIdx + * Identifier of the scan channel. Valid range: 0-15. + * + * @param[in] exTime + * Excitation time on chIdx. Excitation will last exTime+1 excitation clock + * cycles. Valid range: 0-63 (6 bits). + * + * @param[in] sampleDelay + * Sample delay on chIdx. Sampling will occur after sampleDelay+1 sample clock + * cycles. Valid range: 0-127 (7 bits). + * + * @param[in] measDelay + * Measure delay on chIdx. Sensor measuring is delayed for measDelay+1 + * excitation clock cycles. Valid range: 0-127 (7 bits). + ******************************************************************************/ +void LESENSE_ChannelTimingSet(uint8_t const chIdx, + uint8_t const exTime, + uint8_t const sampleDelay, + uint8_t const measDelay) +{ + /* Sanity check of parameters. */ + EFM_ASSERT(exTime < 64U); + EFM_ASSERT(sampleDelay < 128U); + EFM_ASSERT(measDelay < 128U); + + /* Channel specific timing configuration on scan channel chIdx. + * Setting excitation time, sampling delay, measurement delay. */ + LESENSE->CH[chIdx].TIMING = + ((uint32_t)exTime << _LESENSE_CH_TIMING_EXTIME_SHIFT) + | ((uint32_t)sampleDelay << _LESENSE_CH_TIMING_SAMPLEDLY_SHIFT) + | ((uint32_t)measDelay << _LESENSE_CH_TIMING_MEASUREDLY_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Set LESENSE channel threshold parameters. + * + * @details + * Use this function to set threshold parameters on a selected LESENSE + * channel. + * + * @note + * Users can configure the channel threshold parameters by + * LESENSE_ChannelConfig() function, but only with a significant overhead. + * This simple function serves the purpose of controlling these parameters + * after the channel has been configured. + * + * @param[in] chIdx + * Identifier of the scan channel. Valid range: 0-15. + * + * @param[in] acmpThres + * ACMP threshold. + * @li If perCtrl.dacCh0Data or perCtrl.dacCh1Data is set to + * #lesenseDACIfData, acmpThres defines the 12-bit DAC data in the + * corresponding data register of the DAC interface (DACn_CH0DATA and + * DACn_CH1DATA). In this case, the valid range is: 0-4095 (12 bits). + * + * @li If perCtrl.dacCh0Data or perCtrl.dacCh1Data is set to + * #lesenseACMPThres, acmpThres defines the 6-bit Vdd scaling factor of ACMP + * negative input (VDDLEVEL in ACMP_INPUTSEL register). In this case, the + * valid range is: 0-63 (6 bits). + * + * @param[in] cntThres + * Decision threshold for counter comparison. + * Valid range: 0-65535 (16 bits). + ******************************************************************************/ +void LESENSE_ChannelThresSet(uint8_t const chIdx, + uint16_t const acmpThres, + uint16_t const cntThres) +{ + uint32_t tmp; /* temporary storage */ + + + /* Sanity check for acmpThres only, cntThres is 16bit value. */ + EFM_ASSERT(acmpThres < 4096U); + /* Sanity check for LESENSE channel id. */ + EFM_ASSERT(chIdx < 16); + + /* Save the INTERACT register value of channel chIdx to tmp. + * Please be aware the effects of the non-atomic Read-Modify-Write cycle! */ + tmp = LESENSE->CH[chIdx].INTERACT & ~(_LESENSE_CH_INTERACT_ACMPTHRES_MASK); + /* Set the ACMP threshold value to the INTERACT register of channel chIdx. */ + tmp |= (uint32_t)acmpThres << _LESENSE_CH_INTERACT_ACMPTHRES_SHIFT; + /* Write the new value to the INTERACT register. */ + LESENSE->CH[chIdx].INTERACT = tmp; + + /* Save the EVAL register value of channel chIdx to tmp. + * Please be aware the effects of the non-atomic Read-Modify-Write cycle! */ + tmp = LESENSE->CH[chIdx].EVAL & ~(_LESENSE_CH_EVAL_COMPTHRES_MASK); + /* Set the counter threshold value to the INTERACT register of channel chIdx. */ + tmp |= (uint32_t)cntThres << _LESENSE_CH_EVAL_COMPTHRES_SHIFT; + /* Write the new value to the EVAL register. */ + LESENSE->CH[chIdx].EVAL = tmp; +} + + +/***************************************************************************//** + * @brief + * Configure all LESENSE decoder states. + * + * @details + * This function configures all the decoder states of the LESENSE interface. + * Please refer to the configuration parameter type definition + * (LESENSE_DecStAll_TypeDef) for more details. + * + * @note + * Decoder states can be configured individually using + * LESENSE_DecoderStateConfig() function. + * + * @param[in] confDecStAll + * Configuration structure for all (16) LESENSE decoder states. + ******************************************************************************/ +void LESENSE_DecoderStateAllConfig(LESENSE_DecStAll_TypeDef const *confDecStAll) +{ + uint32_t i; + + /* Iterate through all the 16 decoder states. */ + for (i = 0U; i < 16U; ++i) + { + /* Configure decoder state i. */ + LESENSE_DecoderStateConfig(&confDecStAll->St[i], i); + } +} + + +/***************************************************************************//** + * @brief + * Configure a single LESENSE decoder state. + * + * @details + * This function configures a single decoder state of the LESENSE interface. + * Please refer to the configuration parameter type definition + * (LESENSE_DecStDesc_TypeDef) for more details. + * + * @param[in] confDecSt + * Configuration structure for a single LESENSE decoder state. + * + * @param[in] decSt + * Decoder state index to configure (0-15). + ******************************************************************************/ +void LESENSE_DecoderStateConfig(LESENSE_DecStDesc_TypeDef const *confDecSt, + uint32_t const decSt) +{ + /* Sanity check of configuration parameters */ + EFM_ASSERT(decSt < 16U); + EFM_ASSERT((uint32_t)confDecSt->confA.compMask < 16U); + EFM_ASSERT((uint32_t)confDecSt->confA.compVal < 16U); + EFM_ASSERT((uint32_t)confDecSt->confA.nextState < 16U); + EFM_ASSERT((uint32_t)confDecSt->confB.compMask < 16U); + EFM_ASSERT((uint32_t)confDecSt->confB.compVal < 16U); + EFM_ASSERT((uint32_t)confDecSt->confB.nextState < 16U); + + /* Configure state descriptor A (LESENSE_STi_TCONFA) for decoder state i. + * Setting sensor compare value, sensor mask, next state index, + * transition action, interrupt flag option and state descriptor chaining + * configurations. */ + LESENSE->ST[decSt].TCONFA = + (uint32_t)confDecSt->confA.prsAct + | ((uint32_t)confDecSt->confA.compMask << _LESENSE_ST_TCONFA_MASK_SHIFT) + | ((uint32_t)confDecSt->confA.compVal << _LESENSE_ST_TCONFA_COMP_SHIFT) + | ((uint32_t)confDecSt->confA.nextState << _LESENSE_ST_TCONFA_NEXTSTATE_SHIFT) + | ((uint32_t)confDecSt->confA.setInt << _LESENSE_ST_TCONFA_SETIF_SHIFT) + | ((uint32_t)confDecSt->chainDesc << _LESENSE_ST_TCONFA_CHAIN_SHIFT); + + /* Configure state descriptor Bi (LESENSE_STi_TCONFB). + * Setting sensor compare value, sensor mask, next state index, transition + * action and interrupt flag option configurations. */ + LESENSE->ST[decSt].TCONFB = + (uint32_t)confDecSt->confB.prsAct + | ((uint32_t)confDecSt->confB.compMask << _LESENSE_ST_TCONFB_MASK_SHIFT) + | ((uint32_t)confDecSt->confB.compVal << _LESENSE_ST_TCONFB_COMP_SHIFT) + | ((uint32_t)confDecSt->confB.nextState << _LESENSE_ST_TCONFB_NEXTSTATE_SHIFT) + | ((uint32_t)confDecSt->confB.setInt << _LESENSE_ST_TCONFB_SETIF_SHIFT); +} + + +/***************************************************************************//** + * @brief + * Set LESENSE decoder state. + * + * @details + * This function can be used for setting the initial state of the LESENSE + * decoder. + * + * @note + * Make sure the LESENSE decoder state is initialized by this function before + * enabling the decoder! + * + * @param[in] decSt + * Decoder state to set as current state. Valid range: 0-15 + ******************************************************************************/ +void LESENSE_DecoderStateSet(uint32_t decSt) +{ + EFM_ASSERT(decSt < 16U); + + LESENSE->DECSTATE = decSt & _LESENSE_DECSTATE_DECSTATE_MASK; +} + + +/***************************************************************************//** + * @brief + * Get the current state of the LESENSE decoder. + * + * @return + * This function returns the value of LESENSE_DECSTATE register that + * represents the current state of the LESENSE decoder. + ******************************************************************************/ +uint32_t LESENSE_DecoderStateGet(void) +{ + return LESENSE->DECSTATE & _LESENSE_DECSTATE_DECSTATE_MASK; +} + + +/***************************************************************************//** + * @brief + * Start scanning of sensors. + * + * @note + * This function will wait for any pending previous write operation to the + * CMD register to complete before accessing the CMD register. It will also + * wait for the write operation to the CMD register to complete before + * returning. Each write operation to the CMD register may take up to 3 LF + * clock cycles, so the user should expect some delay. The user may implement + * a separate function to write multiple command bits in the CMD register + * in one single operation in order to optimize an application. + ******************************************************************************/ +void LESENSE_ScanStart(void) +{ + /* Wait for any pending previous write operation to the CMD register to + complete before accessing the CMD register. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; + + /* Start scanning of sensors */ + LESENSE->CMD = LESENSE_CMD_START; + + /* Wait for the write operation to the CMD register to complete before + returning. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; +} + + +/***************************************************************************//** + * @brief + * Stop scanning of sensors. + * + * @note + * This function will wait for any pending previous write operation to the + * CMD register to complete before accessing the CMD register. It will also + * wait for the write operation to the CMD register to complete before + * returning. Each write operation to the CMD register may take up to 3 LF + * clock cycles, so the user should expect some delay. The user may implement + * a separate function to write multiple command bits in the CMD register + * in one single operation in order to optimize an application. + * + * @note + * If issued during a scan, the command takes effect after scan completion. + ******************************************************************************/ +void LESENSE_ScanStop(void) +{ + /* Wait for any pending previous write operation to the CMD register to + complete before accessing the CMD register. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; + + /* Stop scanning of sensors */ + LESENSE->CMD = LESENSE_CMD_STOP; + + /* Wait for the write operation to the CMD register to complete before + returning. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; +} + + +/***************************************************************************//** + * @brief + * Start LESENSE decoder. + * + * @note + * This function will wait for any pending previous write operation to the + * CMD register to complete before accessing the CMD register. It will also + * wait for the write operation to the CMD register to complete before + * returning. Each write operation to the CMD register may take up to 3 LF + * clock cycles, so the user should expect some delay. The user may implement + * a separate function to write multiple command bits in the CMD register + * in one single operation in order to optimize an application. + ******************************************************************************/ +void LESENSE_DecoderStart(void) +{ + /* Wait for any pending previous write operation to the CMD register to + complete before accessing the CMD register. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; + + /* Start decoder */ + LESENSE->CMD = LESENSE_CMD_DECODE; + + /* Wait for the write operation to the CMD register to complete before + returning. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; +} + + +/***************************************************************************//** + * @brief + * Clear result buffer. + * + * @note + * This function will wait for any pending previous write operation to the + * CMD register to complete before accessing the CMD register. It will also + * wait for the write operation to the CMD register to complete before + * returning. Each write operation to the CMD register may take up to 3 LF + * clock cycles, so the user should expect some delay. The user may implement + * a separate function to write multiple command bits in the CMD register + * in one single operation in order to optimize an application. + ******************************************************************************/ +void LESENSE_ResultBufferClear(void) +{ + /* Wait for any pending previous write operation to the CMD register to + complete before accessing the CMD register. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; + + LESENSE->CMD = LESENSE_CMD_CLEARBUF; + + /* Wait for the write operation to the CMD register to complete before + returning. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; +} + + +/***************************************************************************//** + * @brief + * Reset the LESENSE module. + * + * @details + * Use this function to reset the LESENSE registers. + * + * @note + * Resetting LESENSE registers is required in each reset or power-on cycle in + * order to configure the default values of the RAM mapped LESENSE registers. + * LESENSE_Reset() can be called on initialization by setting the @p reqReset + * parameter to true in LESENSE_Init(). + ******************************************************************************/ +void LESENSE_Reset(void) +{ + uint32_t i; + + /* Disable all LESENSE interrupts first */ + LESENSE->IEN = _LESENSE_IEN_RESETVALUE; + + /* Clear all pending LESENSE interrupts */ + LESENSE->IFC = _LESENSE_IFC_MASK; + + /* Stop the decoder */ + LESENSE->DECCTRL |= LESENSE_DECCTRL_DISABLE; + + /* Wait for any pending previous write operation to the CMD register to + complete before accessing the CMD register. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; + + /* Stop sensor scan and clear result buffer */ + LESENSE->CMD = (LESENSE_CMD_STOP | LESENSE_CMD_CLEARBUF); + + /* Reset LESENSE configuration registers */ + LESENSE->CTRL = _LESENSE_CTRL_RESETVALUE; + LESENSE->PERCTRL = _LESENSE_PERCTRL_RESETVALUE; + LESENSE->DECCTRL = _LESENSE_DECCTRL_RESETVALUE; + LESENSE->BIASCTRL = _LESENSE_BIASCTRL_RESETVALUE; + LESENSE->CHEN = _LESENSE_CHEN_RESETVALUE; + LESENSE->IDLECONF = _LESENSE_IDLECONF_RESETVALUE; + LESENSE->ALTEXCONF = _LESENSE_ALTEXCONF_RESETVALUE; + + /* Disable LESENSE to control GPIO pins */ + LESENSE->ROUTE = _LESENSE_ROUTE_RESETVALUE; + + /* Reset all channel configuration registers */ + for (i = 0U; i < 16U; ++i) + { + LESENSE->CH[i].TIMING = _LESENSE_CH_TIMING_RESETVALUE; + LESENSE->CH[i].INTERACT = _LESENSE_CH_INTERACT_RESETVALUE; + LESENSE->CH[i].EVAL = _LESENSE_CH_EVAL_RESETVALUE; + } + + /* Reset all decoder state configuration registers */ + for (i = 0U; i < 16U; ++i) + { + LESENSE->ST[i].TCONFA = _LESENSE_ST_TCONFA_RESETVALUE; + LESENSE->ST[i].TCONFB = _LESENSE_ST_TCONFB_RESETVALUE; + } + + /* Wait for the write operation to the CMD register to complete before + returning. */ + while (LESENSE_SYNCBUSY_CMD & LESENSE->SYNCBUSY) + ; +} + + +/** @} (end addtogroup LESENSE) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined(LESENSE_COUNT) && (LESENSE_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_letimer.c b/cpu/efm32_common/emlib/src/em_letimer.c new file mode 100644 index 0000000000000..29da057b6d796 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_letimer.c @@ -0,0 +1,544 @@ +/***************************************************************************//** + * @file em_letimer.c + * @brief Low Energy Timer (LETIMER) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_letimer.h" +#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) +#include "em_cmu.h" +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LETIMER + * @brief Low Energy Timer (LETIMER) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of valid comparator register for assert statements. */ +#define LETIMER_COMP_REG_VALID(reg) (((reg) <= 1)) + +/** Validation of LETIMER register block pointer reference for assert statements. */ +#define LETIMER_REF_VALID(ref) ((ref) == LETIMER0) + +/** Validation of valid repeat counter register for assert statements. */ +#define LETIMER_REP_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. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] mask + * Bitmask corresponding to SYNCBUSY register defined bits, indicating + * registers that must complete any ongoing synchronization. + ******************************************************************************/ +__STATIC_INLINE void regSync(LETIMER_TypeDef *letimer, uint32_t mask) +{ +#if defined(_LETIMER_FREEZE_MASK) + /* Avoid deadlock if modifying the same register twice when freeze mode is */ + /* activated. */ + if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE) + return; +#endif + + /* Wait for any pending previous write operation to have been completed */ + /* in low frequency domain, only required for Gecko Family of devices */ + while (letimer->SYNCBUSY & mask) + ; +} +#endif + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get LETIMER compare register value. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] comp + * Compare register to get, either 0 or 1 + * + * @return + * Compare register value, 0 if invalid register selected. + ******************************************************************************/ +uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp) +{ + uint32_t ret; + + EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_COMP_REG_VALID(comp)); + + /* Initialize selected compare value */ + switch (comp) + { + case 0: + ret = letimer->COMP0; + break; + + case 1: + ret = letimer->COMP1; + break; + + default: + /* Unknown compare register selected */ + ret = 0; + break; + } + + return(ret); +} + + +/***************************************************************************//** + * @brief + * Set LETIMER compare register value. + * + * @note + * The setting of a compare register requires synchronization into the + * low frequency domain. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. This only applies to the Gecko Family, see + * comment in the LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] comp + * Compare register to set, either 0 or 1 + * + * @param[in] value + * Initialization value (<= 0x0000ffff) + ******************************************************************************/ +void LETIMER_CompareSet(LETIMER_TypeDef *letimer, + unsigned int comp, + uint32_t value) +{ + volatile uint32_t *compReg; + + EFM_ASSERT(LETIMER_REF_VALID(letimer) + && LETIMER_COMP_REG_VALID(comp) + && ((value & ~(_LETIMER_COMP0_COMP0_MASK + >> _LETIMER_COMP0_COMP0_SHIFT)) + == 0)); + + /* Initialize selected compare value */ + switch (comp) + { + case 0: + compReg = &(letimer->COMP0); + break; + + case 1: + compReg = &(letimer->COMP1); + break; + + default: + /* Unknown compare register selected, abort */ + return; + } + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, comp ? LETIMER_SYNCBUSY_COMP1 : LETIMER_SYNCBUSY_COMP0); +#endif + + *compReg = value; +} + + +/***************************************************************************//** + * @brief + * Start/stop LETIMER. + * + * @note + * The enabling/disabling of the LETIMER modifies the LETIMER CMD register + * which requires synchronization into the low frequency domain. If this + * register is modified before a previous update to the same register has + * completed, this function will stall until the previous synchronization has + * completed. This only applies to the Gecko Family, see comment in the + * LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] enable + * true to enable counting, false to disable. + ******************************************************************************/ +void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable) +{ + EFM_ASSERT(LETIMER_REF_VALID(letimer)); + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CMD); +#endif + + if (enable) + { + letimer->CMD = LETIMER_CMD_START; + } + else + { + letimer->CMD = LETIMER_CMD_STOP; + } +} + +#if defined(_LETIMER_FREEZE_MASK) +/***************************************************************************//** + * @brief + * LETIMER register synchronization freeze control. + * + * @details + * Some LETIMER registers require synchronization into the low frequency (LF) + * domain. The freeze feature allows for several such registers to be + * modified before passing them to the LF domain simultaneously (which + * takes place when the freeze mode is disabled). + * + * @note + * When enabling freeze mode, this function will wait for all current + * ongoing LETIMER synchronization to 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] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] enable + * @li true - enable freeze, modified registers are not propagated to the + * LF domain + * @li false - disables freeze, modified registers are propagated to LF + * domain + ******************************************************************************/ +void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable) +{ + 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 (letimer->SYNCBUSY) + ; + + letimer->FREEZE = LETIMER_FREEZE_REGFREEZE; + } + else + { + letimer->FREEZE = 0; + } +} +#endif /* defined(_LETIMER_FREEZE_MASK) */ + +/***************************************************************************//** + * @brief + * Initialize LETIMER. + * + * @details + * Note that the compare/repeat values must be set separately with + * LETIMER_CompareSet() and LETIMER_RepeatSet(). That should probably be done + * prior to the use of this function if configuring the LETIMER to start when + * initialization is completed. + * + * @note + * The initialization of the LETIMER modifies the LETIMER CTRL/CMD registers + * which require synchronization into the low frequency domain. If any of those + * registers are modified before a previous update to the same register has + * completed, this function will stall until the previous synchronization has + * completed. This only applies to the Gecko Family, see comment in the + * LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block. + * + * @param[in] init + * Pointer to LETIMER initialization structure. + ******************************************************************************/ +void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init) +{ + uint32_t tmp = 0; + + EFM_ASSERT(LETIMER_REF_VALID(letimer)); + + /* Stop timer if specified to be disabled and running */ + if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING)) + { +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CMD); +#endif + letimer->CMD = LETIMER_CMD_STOP; + } + + /* Configure DEBUGRUN flag, sets whether or not counter should be + * updated when debugger is active */ + if (init->debugRun) + { + tmp |= LETIMER_CTRL_DEBUGRUN; + } + +#if defined(LETIMER_CTRL_RTCC0TEN) + if (init->rtcComp0Enable) + { + tmp |= LETIMER_CTRL_RTCC0TEN; + } + + if (init->rtcComp1Enable) + { + tmp |= LETIMER_CTRL_RTCC1TEN; + } +#endif + + if (init->comp0Top) + { + tmp |= LETIMER_CTRL_COMP0TOP; + } + + if (init->bufTop) + { + tmp |= LETIMER_CTRL_BUFTOP; + } + + if (init->out0Pol) + { + tmp |= LETIMER_CTRL_OPOL0; + } + + if (init->out1Pol) + { + tmp |= LETIMER_CTRL_OPOL1; + } + + tmp |= init->ufoa0 << _LETIMER_CTRL_UFOA0_SHIFT; + tmp |= init->ufoa1 << _LETIMER_CTRL_UFOA1_SHIFT; + tmp |= init->repMode << _LETIMER_CTRL_REPMODE_SHIFT; + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CTRL); +#endif + letimer->CTRL = tmp; + + /* Start timer if specified to be enabled and not already running */ + if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING)) + { +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, LETIMER_SYNCBUSY_CMD); +#endif + letimer->CMD = LETIMER_CMD_START; + } +} + + +/***************************************************************************//** + * @brief + * Get LETIMER repeat register value. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] rep + * Repeat register to get, either 0 or 1 + * + * @return + * Repeat register value, 0 if invalid register selected. + ******************************************************************************/ +uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep) +{ + uint32_t ret; + + EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_REP_REG_VALID(rep)); + + /* Initialize selected compare value */ + switch (rep) + { + case 0: + ret = letimer->REP0; + break; + + case 1: + ret = letimer->REP1; + break; + + default: + /* Unknown compare register selected */ + ret = 0; + break; + } + + return(ret); +} + + +/***************************************************************************//** + * @brief + * Set LETIMER repeat counter register value. + * + * @note + * The setting of a repeat counter register requires synchronization into the + * low frequency domain. If the same register is modified before a previous + * update has completed, this function will stall until the previous + * synchronization has completed. This only applies to the Gecko Family, see + * comment in the LETIMER_Sync() internal function call. + * + * @param[in] letimer + * Pointer to LETIMER peripheral register block + * + * @param[in] rep + * Repeat counter register to set, either 0 or 1 + * + * @param[in] value + * Initialization value (<= 0x0000ffff) + ******************************************************************************/ +void LETIMER_RepeatSet(LETIMER_TypeDef *letimer, + unsigned int rep, + uint32_t value) +{ + volatile uint32_t *repReg; +#if defined(_EFM32_GECKO_FAMILY) + uint32_t syncbusy; +#endif + EFM_ASSERT(LETIMER_REF_VALID(letimer) + && LETIMER_REP_REG_VALID(rep) + && ((value & ~(_LETIMER_REP0_REP0_MASK + >> _LETIMER_REP0_REP0_SHIFT)) + == 0)); + + /* Initialize selected compare value */ + switch (rep) + { + case 0: + repReg = &(letimer->REP0); +#if defined(_EFM32_GECKO_FAMILY) + syncbusy = LETIMER_SYNCBUSY_REP0; +#endif + break; + + case 1: + repReg = &(letimer->REP1); +#if defined(_EFM32_GECKO_FAMILY) + syncbusy = LETIMER_SYNCBUSY_REP1; +#endif + break; + + default: + /* Unknown compare register selected, abort */ + return; + } + +#if defined(_EFM32_GECKO_FAMILY) + /* LF register about to be modified require sync. busy check */ + regSync(letimer, syncbusy); +#endif + + *repReg = value; +} + + +/***************************************************************************//** + * @brief + * Reset LETIMER 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] letimer + * Pointer to LETIMER peripheral register block. + ******************************************************************************/ +void LETIMER_Reset(LETIMER_TypeDef *letimer) +{ +#if defined(_LETIMER_FREEZE_MASK) + /* Freeze registers to avoid stalling for LF synchronization */ + LETIMER_FreezeEnable(letimer, true); +#endif + + /* Make sure disabled first, before resetting other registers */ + letimer->CMD = LETIMER_CMD_STOP | LETIMER_CMD_CLEAR + | LETIMER_CMD_CTO0 | LETIMER_CMD_CTO1; + letimer->CTRL = _LETIMER_CTRL_RESETVALUE; + letimer->COMP0 = _LETIMER_COMP0_RESETVALUE; + letimer->COMP1 = _LETIMER_COMP1_RESETVALUE; + letimer->REP0 = _LETIMER_REP0_RESETVALUE; + letimer->REP1 = _LETIMER_REP1_RESETVALUE; + letimer->IEN = _LETIMER_IEN_RESETVALUE; + letimer->IFC = _LETIMER_IFC_MASK; + /* Do not reset route register, setting should be done independently */ + +#if defined(_LETIMER_FREEZE_MASK) + /* Unfreeze registers, pass new settings on to LETIMER */ + LETIMER_FreezeEnable(letimer, false); +#endif +} + + +/** @} (end addtogroup LETIMER) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_leuart.c b/cpu/efm32_common/emlib/src/em_leuart.c new file mode 100644 index 0000000000000..c744c51a5cb2f --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_leuart.c @@ -0,0 +1,705 @@ +/***************************************************************************//** + * @file em_leuart.c + * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) + * Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_leuart.h" +#if defined(LEUART_COUNT) && (LEUART_COUNT > 0) + +#include "em_cmu.h" +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup LEUART + * @brief Low Energy Universal Asynchronous Receiver/Transmitter (LEUART) + * Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + + +/** Validation of LEUART register block pointer reference + * for assert statements. */ +#if (LEUART_COUNT == 1) +#define LEUART_REF_VALID(ref) ((ref) == LEUART0) +#elif (LEUART_COUNT == 2) +#define LEUART_REF_VALID(ref) (((ref) == LEUART0) || ((ref) == LEUART1)) +#else +#error "Undefined number of low energy UARTs (LEUART)." +#endif + +/** @endcond */ + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/***************************************************************************//** + * @brief + * Wait for ongoing sync of register(s) to low frequency domain to complete. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block + * + * @param[in] mask + * Bitmask corresponding to SYNCBUSY register defined bits, indicating + * registers that must complete any ongoing synchronization. + ******************************************************************************/ +__STATIC_INLINE void LEUART_Sync(LEUART_TypeDef *leuart, uint32_t mask) +{ + /* Avoid deadlock if modifying the same register twice when freeze mode is */ + /* activated. */ + if (leuart->FREEZE & LEUART_FREEZE_REGFREEZE) + { + return; + } + + /* Wait for any pending previous write operation to have been completed */ + /* in low frequency domain */ + while (leuart->SYNCBUSY & mask) + ; +} + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Calculate baudrate for LEUART given reference frequency and clock division. + * + * @details + * This function returns the baudrate that a LEUART module will use if + * configured with the given frequency and clock divisor. 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 + * LEUART peripheral frequency used. + * + * @param[in] clkdiv + * Clock division factor to be used. + * + * @return + * Baudrate with given settings. + ******************************************************************************/ +uint32_t LEUART_BaudrateCalc(uint32_t refFreq, uint32_t clkdiv) +{ + uint32_t divisor; + uint32_t remainder; + uint32_t quotient; + uint32_t br; + + /* Mask out unused bits */ + clkdiv &= _LEUART_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 is given by: + * + * br = fLEUARTn/(1 + (CLKDIV / 256)) + * + * which can be rewritten to + * + * br = (256 * fLEUARTn)/(256 + CLKDIV) + * + * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow + * if using 32 bit arithmetic. However, since fLEUARTn may be derived from + * HFCORECLK as well, we must consider overflow when using integer arithmetic. + */ + + /* + * The basic problem with integer division in the above formula is that + * the dividend (256 * fLEUARTn) 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 fLEUARTn 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 = 256a / b = 256(qb + r)/b = 256q + 256r/b + * + * where a is 'refFreq' and b is 'divisor', referring to variable names. + */ + + divisor = 256 + clkdiv; + quotient = refFreq / divisor; + remainder = refFreq % divisor; + + /* Since divisor >= 256, the below cannot exceed max 32 bit value. */ + br = 256 * quotient; + + /* + * Remainder < (256 + clkdiv), which means dividend (256 * remainder) worst case is + * 256*(256 + 0x7ff8) = 0x80F800. + */ + br += (256 * remainder) / divisor; + + return br; +} + + +/***************************************************************************//** + * @brief + * Get current baudrate for LEUART. + * + * @details + * This function returns the actual baudrate (not considering oscillator + * inaccuracies) used by a LEUART peripheral. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * Current baudrate. + ******************************************************************************/ +uint32_t LEUART_BaudrateGet(LEUART_TypeDef *leuart) +{ + uint32_t freq; + CMU_Clock_TypeDef clock; + + /* Get current frequency */ + if (leuart == LEUART0) + { + clock = cmuClock_LEUART0; + } +#if (LEUART_COUNT > 1) + else if (leuart == LEUART1) + { + clock = cmuClock_LEUART1; + } +#endif + else + { + EFM_ASSERT(0); + return 0; + } + + freq = CMU_ClockFreqGet(clock); + + return LEUART_BaudrateCalc(freq, leuart->CLKDIV); +} + + +/***************************************************************************//** + * @brief + * Configure baudrate (or as close as possible to specified baudrate). + * + * @note + * The setting of a baudrate 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. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] refFreq + * LEUART 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 LEUART. + ******************************************************************************/ +void LEUART_BaudrateSet(LEUART_TypeDef *leuart, + uint32_t refFreq, + uint32_t baudrate) +{ + uint32_t clkdiv; + CMU_Clock_TypeDef clock; + + /* 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*(fLEUARTn/br - 1) = ((256*fLEUARTn)/br) - 256 + * + * Normally, with fLEUARTn appr 32768Hz, there is no problem with overflow + * if using 32 bit arithmetic. However, since fLEUARTn may be derived from + * HFCORECLK as well, we must consider overflow when using integer arithmetic. + * + * The basic problem with integer division in the above formula is that + * the dividend (256 * fLEUARTn) 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 fLEUARTn value either. + * + * Since the last 3 bits of CLKDIV are don't care, we can base our + * integer arithmetic on the below formula + * + * CLKDIV/8 = ((32*fLEUARTn)/br) - 32 + * + * and calculate 1/8 of CLKDIV first. This allows for fLEUARTn + * up to 128MHz without overflowing a 32 bit value! + */ + + /* Get current frequency? */ + if (!refFreq) + { + if (leuart == LEUART0) + { + clock = cmuClock_LEUART0; + } +#if (LEUART_COUNT > 1) + else if (leuart == LEUART1) + { + clock = cmuClock_LEUART1; + } +#endif + else + { + EFM_ASSERT(0); + return; + } + + refFreq = CMU_ClockFreqGet(clock); + } + + /* Calculate and set CLKDIV with fractional bits */ + clkdiv = (32 * refFreq) / baudrate; + clkdiv -= 32; + clkdiv *= 8; + + /* Verify that resulting clock divider is within limits */ + EFM_ASSERT(clkdiv <= _LEUART_CLKDIV_MASK); + + /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ + clkdiv &= _LEUART_CLKDIV_MASK; + + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_CLKDIV); + + leuart->CLKDIV = clkdiv; +} + + +/***************************************************************************//** + * @brief + * Enable/disable LEUART 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). + * + * @note + * Enabling/disabling 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. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] enable + * Select status for receiver/transmitter. + ******************************************************************************/ +void LEUART_Enable(LEUART_TypeDef *leuart, LEUART_Enable_TypeDef enable) +{ + uint32_t tmp; + + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(LEUART_REF_VALID(leuart)); + + /* Disable as specified */ + tmp = ~((uint32_t)(enable)); + tmp &= (_LEUART_CMD_RXEN_MASK | _LEUART_CMD_TXEN_MASK); + tmp <<= 1; + /* Enable as specified */ + tmp |= (uint32_t)(enable); + + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD); + + leuart->CMD = tmp; +} + + +/***************************************************************************//** + * @brief + * LEUART register synchronization freeze control. + * + * @details + * Some LEUART 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 LEUART 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] leuart + * Pointer to LEUART 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 LEUART_FreezeEnable(LEUART_TypeDef *leuart, bool enable) +{ + 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 (leuart->SYNCBUSY) + ; + + leuart->FREEZE = LEUART_FREEZE_REGFREEZE; + } + else + { + leuart->FREEZE = 0; + } +} + + +/***************************************************************************//** + * @brief + * Init LEUART. + * + * @details + * This function will configure basic settings in order to operate in normal + * asynchronous mode. Consider using LEUART_Reset() prior to this function if + * state of configuration is not known, since only configuration settings + * specified by @p init are set. + * + * Special control setup not covered by this function may be done either + * before or after using this function (but normally before enabling) + * by direct modification of the CTRL register. + * + * Notice that pins used by the LEUART module must be properly configured + * by the user explicitly, in order for the LEUART 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.) + * + * @note + * Initializing 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. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] init + * Pointer to initialization structure used to configure basic async setup. + ******************************************************************************/ +void LEUART_Init(LEUART_TypeDef *leuart, LEUART_Init_TypeDef const *init) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(LEUART_REF_VALID(leuart)); + + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_CMD); + + /* Ensure disabled while doing config */ + leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS; + + /* Freeze registers to avoid stalling for LF synchronization */ + LEUART_FreezeEnable(leuart, true); + + /* Configure databits and stopbits */ + leuart->CTRL = (leuart->CTRL & ~(_LEUART_CTRL_PARITY_MASK + | _LEUART_CTRL_STOPBITS_MASK)) + | (uint32_t)(init->databits) + | (uint32_t)(init->parity) + | (uint32_t)(init->stopbits); + + /* Configure baudrate */ + LEUART_BaudrateSet(leuart, init->refFreq, init->baudrate); + + /* Finally enable (as specified) */ + leuart->CMD = (uint32_t)init->enable; + + /* Unfreeze registers, pass new settings on to LEUART */ + LEUART_FreezeEnable(leuart, false); +} + + +/***************************************************************************//** + * @brief + * Reset LEUART to same state as after a HW reset. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + ******************************************************************************/ +void LEUART_Reset(LEUART_TypeDef *leuart) +{ + /* Make sure the module exists on the selected chip */ + EFM_ASSERT(LEUART_REF_VALID(leuart)); + + /* Freeze registers to avoid stalling for LF synchronization */ + LEUART_FreezeEnable(leuart, true); + + /* Make sure disabled first, before resetting other registers */ + leuart->CMD = LEUART_CMD_RXDIS | LEUART_CMD_TXDIS | LEUART_CMD_RXBLOCKDIS + | LEUART_CMD_CLEARTX | LEUART_CMD_CLEARRX; + leuart->CTRL = _LEUART_CTRL_RESETVALUE; + leuart->CLKDIV = _LEUART_CLKDIV_RESETVALUE; + leuart->STARTFRAME = _LEUART_STARTFRAME_RESETVALUE; + leuart->SIGFRAME = _LEUART_SIGFRAME_RESETVALUE; + leuart->IEN = _LEUART_IEN_RESETVALUE; + leuart->IFC = _LEUART_IFC_MASK; + leuart->PULSECTRL = _LEUART_PULSECTRL_RESETVALUE; +#if defined(_LEUART_ROUTEPEN_MASK) + leuart->ROUTEPEN = _LEUART_ROUTEPEN_RESETVALUE; + leuart->ROUTELOC0 = _LEUART_ROUTELOC0_RESETVALUE; +#else + leuart->ROUTE = _LEUART_ROUTE_RESETVALUE; +#endif + + /* Unfreeze registers, pass new settings on to LEUART */ + LEUART_FreezeEnable(leuart, false); +} + + +/***************************************************************************//** + * @brief + * Receive one 8 bit frame, (or part of 9 bit frame). + * + * @details + * This function is normally used to receive one frame when operating with + * frame length 8 bits. Please refer to LEUART_RxExt() for reception of + * 9 bit frames. + * + * Notice that possible parity/stop bits are not considered part of specified + * frame bit length. + * + * @note + * This function will stall if buffer is empty, until data is received. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +uint8_t LEUART_Rx(LEUART_TypeDef *leuart) +{ + while (!(leuart->STATUS & LEUART_STATUS_RXDATAV)) + ; + + return (uint8_t)leuart->RXDATA; +} + + +/***************************************************************************//** + * @brief + * Receive one 8-9 bit frame, with extended information. + * + * @details + * This function is normally used to receive one frame and additional RX + * status information is required. + * + * @note + * This function will stall if buffer is empty, until data is received. + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @return + * Data received. + ******************************************************************************/ +uint16_t LEUART_RxExt(LEUART_TypeDef *leuart) +{ + while (!(leuart->STATUS & LEUART_STATUS_RXDATAV)) + ; + + return (uint16_t)leuart->RXDATAX; +} + + +/***************************************************************************//** + * @brief + * Transmit one frame. + * + * @details + * Depending on frame length configuration, 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 LEUART_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] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] data + * Data to transmit. See details above for further info. + ******************************************************************************/ +void LEUART_Tx(LEUART_TypeDef *leuart, uint8_t data) +{ + /* Check that transmit buffer is empty */ + while (!(leuart->STATUS & LEUART_STATUS_TXBL)) + ; + + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATA); + + leuart->TXDATA = (uint32_t)data; +} + + +/***************************************************************************//** + * @brief + * Transmit one 8-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] leuart + * Pointer to LEUART 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 reference manual (set to 0 if not used). + ******************************************************************************/ +void LEUART_TxExt(LEUART_TypeDef *leuart, uint16_t data) +{ + /* Check that transmit buffer is empty */ + while (!(leuart->STATUS & LEUART_STATUS_TXBL)) + ; + + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_TXDATAX); + + leuart->TXDATAX = (uint32_t)data; +} + +/***************************************************************************//** + * @brief + * Enables handling of LEUART TX by DMA in EM2 + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] enable + * true - enables functionality + * false - disables functionality + * + ******************************************************************************/ +void LEUART_TxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable) +{ + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL); + + if (enable) + { + leuart->CTRL |= LEUART_CTRL_TXDMAWU; + } + else + { + leuart->CTRL &= ~LEUART_CTRL_TXDMAWU; + } +} + +/***************************************************************************//** + * @brief + * Enables handling of LEUART RX by DMA in EM2 + * + * @param[in] leuart + * Pointer to LEUART peripheral register block. + * + * @param[in] enable + * true - enables functionality + * false - disables functionality + * + ******************************************************************************/ +void LEUART_RxDmaInEM2Enable(LEUART_TypeDef *leuart, bool enable) +{ + /* LF register about to be modified require sync. busy check */ + LEUART_Sync(leuart, LEUART_SYNCBUSY_CTRL); + + if (enable) + { + leuart->CTRL |= LEUART_CTRL_RXDMAWU; + } + else + { + leuart->CTRL &= ~LEUART_CTRL_RXDMAWU; + } +} + + +/** @} (end addtogroup LEUART) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(LEUART_COUNT) && (LEUART_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_mpu.c b/cpu/efm32_common/emlib/src/em_mpu.c new file mode 100644 index 0000000000000..02d0ee3d571ea --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_mpu.c @@ -0,0 +1,124 @@ +/***************************************************************************//** + * @file em_mpu.c + * @brief Memory Protection Unit (MPU) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_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/cpu/efm32_common/emlib/src/em_msc.c b/cpu/efm32_common/emlib/src/em_msc.c new file mode 100644 index 0000000000000..b002c2444e155 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_msc.c @@ -0,0 +1,862 @@ +/***************************************************************************//** + * @file em_msc.c + * @brief Flash controller (MSC) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_msc.h" +#if defined( MSC_COUNT ) && ( MSC_COUNT > 0 ) + +#include "em_system.h" +#include "em_int.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 + +typedef enum { + mscWriteIntSafe, + mscWriteFast, +} MSC_WriteStrategy_Typedef; + +MSC_FUNC_PREFIX static MSC_Status_TypeDef + MSC_WriteWordI(uint32_t *address, + void const *data, + uint32_t numBytes, + MSC_WriteStrategy_Typedef writeStrategy) MSC_FUNC_POSTFIX; + +MSC_FUNC_PREFIX __STATIC_INLINE MSC_Status_TypeDef + MSC_LoadWriteData(uint32_t* data, + uint32_t numWords, + MSC_WriteStrategy_Typedef writeStrategy) MSC_FUNC_POSTFIX; + +MSC_FUNC_PREFIX __STATIC_INLINE MSC_Status_TypeDef + MSC_LoadVerifyAddress(uint32_t* address) MSC_FUNC_POSTFIX; + +/** @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. + * @note + * This function calls SystemCoreClockGet in order to set the global variable + * SystemCoreClock which is used in subseqent calls of MSC_WriteWord to make + * sure the frequency is sufficiently high for flash operations. If the clock + * frequency is changed then software is responsible for calling MSC_Init or + * SystemCoreClockGet in order to set the SystemCoreClock variable to the + * correct value. + ******************************************************************************/ +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; + + /* Call SystemCoreClockGet in order to set the global variable SystemCoreClock + which is used in MSC_LoadWriteData to make sure the frequency is + sufficiently high. If the clock frequency is changed then software is + responsible for calling MSC_Init or SystemCoreClockGet in order to set the + SystemCoreClock variable to the correct value. */ + SystemCoreClockGet(); + +#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; +} + + +#if !defined( _EFM32_GECKO_FAMILY ) +/***************************************************************************//** + * @brief + * Set MSC code execution configuration + * + * @param[in] execConfig + * Code execution configuration + ******************************************************************************/ +void MSC_ExecConfigSet(MSC_ExecConfig_TypeDef *execConfig) +{ + uint32_t mscReadCtrl; + + mscReadCtrl = MSC->READCTRL & ~(0 +#if defined( MSC_READCTRL_SCBTP ) + | MSC_READCTRL_SCBTP +#endif +#if defined( MSC_READCTRL_USEHPROT ) + | MSC_READCTRL_USEHPROT +#endif +#if defined( MSC_READCTRL_PREFETCH ) + | MSC_READCTRL_PREFETCH +#endif +#if defined( MSC_READCTRL_ICCDIS ) + | MSC_READCTRL_ICCDIS +#endif +#if defined( MSC_READCTRL_AIDIS ) + | MSC_READCTRL_AIDIS +#endif +#if defined( MSC_READCTRL_IFCDIS ) + | MSC_READCTRL_IFCDIS +#endif + ); + mscReadCtrl |= (0 +#if defined( MSC_READCTRL_SCBTP ) + | (execConfig->scbtEn ? MSC_READCTRL_SCBTP : 0) +#endif +#if defined( MSC_READCTRL_USEHPROT ) + | (execConfig->useHprot ? MSC_READCTRL_USEHPROT : 0) +#endif +#if defined( MSC_READCTRL_PREFETCH ) + | (execConfig->prefetchEn ? MSC_READCTRL_PREFETCH : 0) +#endif +#if defined( MSC_READCTRL_ICCDIS ) + | (execConfig->iccDis ? MSC_READCTRL_ICCDIS : 0) +#endif +#if defined( MSC_READCTRL_AIDIS ) + | (execConfig->aiDis ? MSC_READCTRL_AIDIS : 0) +#endif +#if defined( MSC_READCTRL_IFCDIS ) + | (execConfig->ifcDis ? MSC_READCTRL_IFCDIS : 0) +#endif + ); + MSC->READCTRL = mscReadCtrl; +} +#endif + +/** @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. + * @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_Status_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 + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#elif 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 +__ramfunc +#endif +#endif /* !EM_MSC_RUN_FROM_FLASH */ +__STATIC_INLINE MSC_Status_TypeDef MSC_LoadVerifyAddress(uint32_t* address) +{ + uint32_t status; + uint32_t timeOut; + + /* Wait for the MSC to become ready. */ + timeOut = MSC_PROGRAM_TIMEOUT; + while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) + { + timeOut--; + } + + /* Check for timeout */ + if (timeOut == 0) + { + return mscReturnTimeOut; + } + /* 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 +#elif defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code +#endif /* __CC_ARM */ + + +/***************************************************************************//** + * @brief + * Perform a Flash data write phase. + * @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. + * @param[in] data + * Pointer to the first data word to load. + * @param[in] numWords + * Number of data words (32-bit) to load. + * @param[in] writeStrategy + * Write strategy to apply. + * @return + * Returns the status of the data load operation + * @verbatim + * mscReturnOk - Operation completed successfully. + * mscReturnTimeOut - Operation timed out waiting for flash operation + * to complete. + * @endverbatim + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#elif 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 +__ramfunc +#endif +#endif /* !EM_MSC_RUN_FROM_FLASH */ +__STATIC_INLINE MSC_Status_TypeDef + MSC_LoadWriteData(uint32_t* data, + uint32_t numWords, + MSC_WriteStrategy_Typedef writeStrategy) +{ + uint32_t timeOut; + uint32_t wordIndex; + uint32_t wordsPerDataPhase; + MSC_Status_TypeDef retval = mscReturnOk; + +#if defined(_MSC_WRITECTRL_LPWRITE_MASK) && defined(_MSC_WRITECTRL_WDOUBLE_MASK) + /* If LPWRITE (Low Power Write) is NOT enabled, set WDOUBLE (Write Double word) */ + if (!(MSC->WRITECTRL & MSC_WRITECTRL_LPWRITE)) + { + /* If the number of words to be written are odd, we need to align by writing + a single word first, before setting the WDOUBLE bit. */ + if (numWords & 0x1) + { + /* Wait for the MSC to become ready for the next word. */ + timeOut = MSC_PROGRAM_TIMEOUT; + while ((!(MSC->STATUS & MSC_STATUS_WDATAREADY)) && (timeOut != 0)) + { + timeOut--; + } + /* Check for timeout */ + if (timeOut == 0) + { + return mscReturnTimeOut; + } + /* Clear double word option, in order to write the initial single word. */ + MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE; + /* Write first data word. */ + MSC->WDATA = *data++; + MSC->WRITECMD = MSC_WRITECMD_WRITEONCE; + + /* Wait for the operation to finish. It may be required to change the WDOUBLE + config after the initial write. It should not be changed while BUSY. */ + timeOut = MSC_PROGRAM_TIMEOUT; + while((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) + { + timeOut--; + } + /* Check for timeout */ + if (timeOut == 0) + { + return mscReturnTimeOut; + } + /* Subtract this initial odd word for the write loop below */ + numWords -= 1; + retval = mscReturnOk; + } + /* Now we can set the double word option in order to write two words per + data phase. */ + MSC->WRITECTRL |= MSC_WRITECTRL_WDOUBLE; + wordsPerDataPhase = 2; + } + else +#endif /* defined( _MSC_WRITECTRL_LPWRITE_MASK ) && defined( _MSC_WRITECTRL_WDOUBLE_MASK ) */ + { + wordsPerDataPhase = 1; + } + + /* Write the rest as double word write if wordsPerDataPhase == 2 */ + if (numWords > 0) + { + /**** Write strategy: mscWriteIntSafe ****/ + if (writeStrategy == mscWriteIntSafe) + { + /* Requires a system core clock at 1MHz or higher */ + EFM_ASSERT(SystemCoreClockGet() >= 1000000); + wordIndex = 0; + while(wordIndex < numWords) + { + MSC->WDATA = *data++; + wordIndex++; + if (wordsPerDataPhase == 2) + { + while (!(MSC->STATUS & MSC_STATUS_WDATAREADY)); + MSC->WDATA = *data++; + wordIndex++; + } + MSC->WRITECMD = MSC_WRITECMD_WRITEONCE; + + /* Wait for the transaction to finish. */ + timeOut = MSC_PROGRAM_TIMEOUT; + while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) + { + timeOut--; + } + /* Check for timeout */ + if (timeOut == 0) + { + retval = mscReturnTimeOut; + break; + } +#if defined( _EFM32_GECKO_FAMILY ) + MSC->ADDRB += 4; + MSC->WRITECMD = MSC_WRITECMD_LADDRIM; +#endif + } + } + + /**** Write strategy: mscWriteFast ****/ + else + { +#if defined( _EFM32_GECKO_FAMILY ) + /* Gecko does not have auto-increment of ADDR. */ + EFM_ASSERT(0); +#else + /* Requires a system core clock at 14MHz or higher */ + EFM_ASSERT(SystemCoreClockGet() >= 14000000); + + wordIndex = 0; + INT_Disable(); + while(wordIndex < numWords) + { + /* Wait for the MSC to be ready for the next word. */ + while (!(MSC->STATUS & MSC_STATUS_WDATAREADY)) + { + /* If the write to MSC->WDATA below missed the 30us timeout and the + following MSC_WRITECMD_WRITETRIG command arrived while + MSC_STATUS_BUSY is 1, then the MSC_WRITECMD_WRITETRIG could be ignored by + the MSC. In this case, MSC_STATUS_WORDTIMEOUT is set to 1 + and MSC_STATUS_BUSY is 0. A new trigger is therefore needed here to + complete write of data in MSC->WDATA. + If WDATAREADY became high since entry into this loop, exit and continue + to the next WDATA write. + */ + if ((MSC->STATUS & (MSC_STATUS_WORDTIMEOUT + | MSC_STATUS_BUSY + | MSC_STATUS_WDATAREADY)) + == MSC_STATUS_WORDTIMEOUT) + { + MSC->WRITECMD = MSC_WRITECMD_WRITETRIG; + } + } + MSC->WDATA = *data; + if ((wordsPerDataPhase == 1) + || ((wordsPerDataPhase == 2) && (wordIndex & 0x1))) + { + MSC->WRITECMD = MSC_WRITECMD_WRITETRIG; + } + data++; + wordIndex++; + } + INT_Enable(); + + /* Wait for the transaction to finish. */ + timeOut = MSC_PROGRAM_TIMEOUT; + while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) + { + timeOut--; + } + /* Check for timeout */ + if (timeOut == 0) + { + retval = mscReturnTimeOut; + } +#endif + } /* writeStrategy */ + } + +#if defined( _MSC_WRITECTRL_WDOUBLE_MASK ) + /* Clear double word option, which should not be left on when returning. */ + MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE; +#endif + + return retval; +} +#if defined(__ICCARM__) +#pragma diag_default=Ta022 +#pragma diag_default=Ta023 +#elif defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code +#endif /* __CC_ARM */ + + +/***************************************************************************//** + * @brief + * Internal flash write function with select write strategy parameter + * @param[in] address + * Write address + * @param[in] data + * Pointer to the first data word to load. + * @param[in] numWords + * Number of data words (32-bit) to load. + * @param[in] writeStrategy + * Write strategy to apply. + * @return + * Returns the status of the data load operation + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#elif 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 +#endif /* !EM_MSC_RUN_FROM_FLASH */ +static MSC_Status_TypeDef MSC_WriteWordI(uint32_t *address, + void const *data, + uint32_t numBytes, + MSC_WriteStrategy_Typedef writeStrategy) +{ + uint32_t wordCount; + uint32_t numWords; + uint32_t pageWords; + uint32_t* pData; + MSC_Status_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; + EFM_ASSERT(numWords > 0); + + /* 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, pData = (uint32_t *)data; 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 = MSC_LoadVerifyAddress(address + wordCount); + if (mscReturnOk != retval) + { + return retval; + } + /* Compute the number of words to write to the current page. */ + pageWords = + (FLASH_PAGE_SIZE - + (((uint32_t) (address + wordCount)) & (FLASH_PAGE_SIZE - 1))) + / sizeof(uint32_t); + if (pageWords > numWords - wordCount) + { + pageWords = numWords - wordCount; + } + /* Now write the data in the current page. */ + retval = MSC_LoadWriteData(pData, pageWords, writeStrategy); + if (mscReturnOk != retval) + { + break; + } + wordCount += pageWords; + pData += pageWords; + } + + /* Disable writing to the MSC */ + MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN; + +#if defined( _MSC_WRITECTRL_WDOUBLE_MASK ) +#if ( WORDS_PER_DATA_PHASE == 2 ) + /* Turn off double word write cycle support. */ + MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE; +#endif +#endif + + return retval; +} +#if defined(__ICCARM__) +#pragma diag_default=Ta022 +#pragma diag_default=Ta023 +#elif defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code +#endif /* __CC_ARM */ + +/** @endcond */ + + +/***************************************************************************//** + * @brief + * Erases a page in flash memory. + * @note + * It is recommended to run this code from RAM. On the Gecko family, it is required + * to run this function from RAM. + * + * For IAR, Rowley, SimplicityStudio, Atollic and armgcc this will be achieved + * automatically by using attributes in the function proctype. For Keil uVision 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_Status_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 + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#elif 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 +#endif /* !EM_MSC_RUN_FROM_FLASH */ +MSC_Status_TypeDef MSC_ErasePage(uint32_t *startAddress) +{ + uint32_t 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 +#elif defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code +#endif /* __CC_ARM */ + + +/***************************************************************************//** + * @brief + * Writes data to flash memory. This function is interrupt safe, but slower than + * MSC_WriteWordFast(), which writes to flash with interrupts disabled. + * Write data must be aligned to words and contain a number of bytes that is + * divisable by four. + * @note + * It is recommended to erase the flash page before performing a write. + * + * It is recommended to run this code from RAM. On the Gecko family, it is required + * to run this function from RAM. + * + * For IAR, Rowley, SimplicityStudio, Atollic and armgcc this will be achieved + * automatically by using attributes in the function proctype. For Keil uVision you + * must define a section called "ram_code" and place this manually in your + * project's scatter file. + * + * This function requires a ystem core clock at 1MHz or higher. + * + * @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 + * @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. Or the MSC timed out waiting for the software to write + * the next word into the DWORD register. + * @endverbatim + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#elif 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 +#endif /* !EM_MSC_RUN_FROM_FLASH */ +MSC_Status_TypeDef MSC_WriteWord(uint32_t *address, + void const *data, + uint32_t numBytes) +{ + return MSC_WriteWordI(address, data, numBytes, mscWriteIntSafe); +} +#if defined(__ICCARM__) +#pragma diag_default=Ta022 +#pragma diag_default=Ta023 +#elif defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code +#endif /* __CC_ARM */ + + +#if !defined( _EFM32_GECKO_FAMILY ) +/***************************************************************************//** + * @brief + * Writes data to flash memory. This function is faster than MSC_WriteWord(), + * but it disables interrupts. Write data must be aligned to words and contain + * a number of bytes that is divisable by four. + * @note + * It is recommended to erase the flash page before performing a write. + + * It is recommended to run this code from RAM. On the Gecko family, it is required + * to run this function from RAM. + * + * For IAR, Rowley, SimplicityStudio, Atollic and armgcc this will be achieved + * automatically by using attributes in the function proctype. For Keil uVision 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 + * @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. Or the MSC timed out waiting for the software to write + * the next word into the DWORD register. + * @endverbatim + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#elif 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 +#endif /* !EM_MSC_RUN_FROM_FLASH */ +MSC_Status_TypeDef MSC_WriteWordFast(uint32_t *address, + void const *data, + uint32_t numBytes) +{ + return MSC_WriteWordI(address, data, numBytes, mscWriteFast); +} +#if defined(__ICCARM__) +#pragma diag_default=Ta022 +#pragma diag_default=Ta023 +#elif defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code +#endif /* __CC_ARM */ +#endif + + +#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. + ******************************************************************************/ +#if !defined(EM_MSC_RUN_FROM_FLASH) +#if defined(__CC_ARM) /* MDK-ARM compiler */ +#pragma arm section code="ram_code" +#endif /* __CC_ARM */ +#endif /* !EM_MSC_RUN_FROM_FLASH */ +MSC_Status_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)) && defined( _MSC_WRITECMD_ERASEMAIN1_MASK )) + /* 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; +} +#if defined(__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/cpu/efm32_common/emlib/src/em_opamp.c b/cpu/efm32_common/emlib/src/em_opamp.c new file mode 100644 index 0000000000000..c9b71ee51ed76 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_opamp.c @@ -0,0 +1,429 @@ +/**************************************************************************//** + * @file em_opamp.c + * @brief Operational Amplifier (OPAMP) peripheral API + * @version 4.2.1 + ****************************************************************************** + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_opamp.h" +#if defined(OPAMP_PRESENT) && (OPAMP_COUNT == 1) + +#include "em_system.h" +#include "em_assert.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. + * + * @note + * The value of the alternate output enable bit mask in the OPAMP_Init_TypeDef + * structure should consist of one or more of the + * DAC_OPA[opa#]MUX_OUTPEN_OUT[output#] flags + * (defined in \_dac.h) OR'ed together. @n @n + * For OPA0: + * @li DAC_OPA0MUX_OUTPEN_OUT0 + * @li DAC_OPA0MUX_OUTPEN_OUT1 + * @li DAC_OPA0MUX_OUTPEN_OUT2 + * @li DAC_OPA0MUX_OUTPEN_OUT3 + * @li DAC_OPA0MUX_OUTPEN_OUT4 + * + * For OPA1: + * @li DAC_OPA1MUX_OUTPEN_OUT0 + * @li DAC_OPA1MUX_OUTPEN_OUT1 + * @li DAC_OPA1MUX_OUTPEN_OUT2 + * @li DAC_OPA1MUX_OUTPEN_OUT3 + * @li DAC_OPA1MUX_OUTPEN_OUT4 + * + * For OPA2: + * @li DAC_OPA2MUX_OUTPEN_OUT0 + * @li DAC_OPA2MUX_OUTPEN_OUT1 + * + * E.g: @n + * init.outPen = DAC_OPA0MUX_OUTPEN_OUT0 | DAC_OPA0MUX_OUTPEN_OUT4; + * + * @param[in] dac + * 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/cpu/efm32_common/emlib/src/em_pcnt.c b/cpu/efm32_common/emlib/src/em_pcnt.c new file mode 100644 index 0000000000000..8cef76cdd134d --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_pcnt.c @@ -0,0 +1,847 @@ +/***************************************************************************//** + * @file em_pcnt.c + * @brief Pulse Counter (PCNT) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_pcnt.h" +#if defined(PCNT_COUNT) && (PCNT_COUNT > 0) + +#include "em_cmu.h" +#include "em_assert.h" +#include "em_bus.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 */ + BUS_RegBitWrite(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1); + + /* Disable reset of CNT and TOP register */ + BUS_RegBitWrite(&(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: + BUS_RegBitWrite(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, enable); + break; + + /* Enable/disable PRS input S1. */ + case pcntPRSInputS1: + BUS_RegBitWrite(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, 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); + break; + } + 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. */ + BUS_RegBitWrite(&(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. */ + BUS_RegBitWrite(&(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. */ + BUS_RegBitWrite(&(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); + BUS_RegBitWrite(&(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 */ +} + +#if defined(PCNT_OVSCFG_FILTLEN_DEFAULT) +/***************************************************************************//** + * @brief + * Set filter configuration. + * + * @details + * This function will configure the PCNT input filter, when the PCNT mode is + * configured to take an LFA-derived clock as input clock. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] config + * Pointer to configuration structure to be applied. + * + * @param[in] enable + * Whether to enable or disable filtering + ******************************************************************************/ +void PCNT_FilterConfiguration(PCNT_TypeDef *pcnt, const PCNT_Filter_TypeDef *config, bool enable) { + uint32_t ovscfg = 0; + + EFM_ASSERT(PCNT_REF_VALID(pcnt)); + + /* Construct new filter setting value */ + ovscfg = ((config->filtLen & _PCNT_OVSCFG_FILTLEN_MASK) << _PCNT_OVSCFG_FILTLEN_SHIFT) + | ((config->flutterrm & 0x1) << _PCNT_OVSCFG_FLUTTERRM_SHIFT); + + /* Set new configuration. LF register requires sync check before writing. */ + PCNT_Sync(pcnt, PCNT_SYNCBUSY_OVSCFG); + pcnt->OVSCFG = ovscfg; + + + /* Set new state of filter. LF register requires sync check before writing. */ + PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); + if(enable) + { + pcnt->CTRL |= PCNT_CTRL_FILT; + } + else + { + pcnt->CTRL &= ~PCNT_CTRL_FILT; + } +} +#endif + +#if defined(PCNT_CTRL_TCCMODE_DEFAULT) +/***************************************************************************//** + * @brief + * Set Triggered Compare and Clear configuration. + * + * @details + * This function will configure the PCNT TCC (Triggered Compare and Clear) + * module. This module can, upon a configurable trigger source, compare the + * current counter value with the configured TOP value. Upon match, the counter + * will be reset, and the TCC PRS output and TCC interrupt flag will be set. + * + * Since there is a comparison with the TOP value, the counter will not stop + * counting nor wrap when hitting the TOP value, but it will keep on counting + * until its maximum value. Then, it will not wrap, but instead stop counting + * and set the overflow flag. + * + * @param[in] pcnt + * Pointer to PCNT peripheral register block. + * + * @param[in] config + * Pointer to configuration structure to be applied. + ******************************************************************************/ +void PCNT_TCCConfiguration(PCNT_TypeDef *pcnt, const PCNT_TCC_TypeDef *config){ + uint32_t ctrl = 0; + uint32_t mask = _PCNT_CTRL_TCCMODE_MASK + | _PCNT_CTRL_TCCPRESC_MASK + | _PCNT_CTRL_TCCCOMP_MASK + | _PCNT_CTRL_PRSGATEEN_MASK + | _PCNT_CTRL_TCCPRSPOL_MASK + | _PCNT_CTRL_TCCPRSSEL_MASK; + + EFM_ASSERT(PCNT_REF_VALID(pcnt)); + + /* construct TCC part of configuration register */ + ctrl |= (config->mode << _PCNT_CTRL_TCCMODE_SHIFT ) & _PCNT_CTRL_TCCMODE_MASK; + ctrl |= (config->prescaler << _PCNT_CTRL_TCCPRESC_SHIFT ) & _PCNT_CTRL_TCCPRESC_MASK; + ctrl |= (config->compare << _PCNT_CTRL_TCCCOMP_SHIFT ) & _PCNT_CTRL_TCCCOMP_MASK; + ctrl |= (config->tccPRS << _PCNT_CTRL_TCCPRSSEL_SHIFT ) & _PCNT_CTRL_TCCPRSSEL_MASK; + ctrl |= (config->prsPolarity << _PCNT_CTRL_TCCPRSPOL_SHIFT ) & _PCNT_CTRL_TCCPRSPOL_MASK; + ctrl |= (config->prsGateEnable << _PCNT_CTRL_PRSGATEEN_SHIFT ) & _PCNT_CTRL_PRSGATEEN_MASK; + + /* Load new TCC config to PCNT. LF register requires sync check before write. */ + PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL); + pcnt->CTRL = (pcnt->CTRL & (~mask)) | ctrl; +} +#endif + +/***************************************************************************//** + * @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/cpu/efm32_common/emlib/src/em_prs.c b/cpu/efm32_common/emlib/src/em_prs.c new file mode 100644 index 0000000000000..00ae6e2e00e65 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_prs.c @@ -0,0 +1,126 @@ +/***************************************************************************//** + * @file em_prs.c + * @brief Peripheral Reflex System (PRS) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_prs.h" +#if defined(PRS_COUNT) && (PRS_COUNT > 0) + +#include "em_assert.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 not supported on EFM32GxxxFyyy parts. + * 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/cpu/efm32_common/emlib/src/em_rmu.c b/cpu/efm32_common/emlib/src/em_rmu.c new file mode 100644 index 0000000000000..2432c387d2412 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_rmu.c @@ -0,0 +1,297 @@ +/***************************************************************************//** + * @file em_rmu.c + * @brief Reset Management Unit (RMU) peripheral module peripheral API + * + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_rmu.h" +#if defined(RMU_COUNT) && (RMU_COUNT > 0) + +#include "em_common.h" +#include "em_emu.h" +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup RMU + * @brief Reset Management Unit (RMU) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ***************************** DEFINES ********************************* + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/* Reset cause "don't care" definitions. + 1's mark the bits that must be zero, zeros are "don't cares". */ +#if (_RMU_RSTCAUSE_MASK == 0x0000007FUL) +#define RMU_RSTCAUSE_PORST_XMASK (0x00000000) /**0b0000000000000000 < Power On Reset */ +#define RMU_RSTCAUSE_BODUNREGRST_XMASK (0x00000081) /**0b0000000010000001 < Brown Out Detector Unregulated Domain Reset */ +#define RMU_RSTCAUSE_BODREGRST_XMASK (0x00000091) /**0b0000000010010001 < Brown Out Detector Regulated Domain Reset */ +#define RMU_RSTCAUSE_EXTRST_XMASK (0x00000001) /**0b0000000000000001 < External Pin Reset */ +#define RMU_RSTCAUSE_WDOGRST_XMASK (0x00000003) /**0b0000000000000011 < Watchdog Reset */ +#define RMU_RSTCAUSE_LOCKUPRST_XMASK (0x0000EFDF) /**0b1110111111011111 < LOCKUP Reset */ +#define RMU_RSTCAUSE_SYSREQRST_XMASK (0x0000EF9F) /**0b1110111110011111 < System Request Reset */ +#define NUM_RSTCAUSES (7) + +#elif (_RMU_RSTCAUSE_MASK == 0x000007FFUL) +#define RMU_RSTCAUSE_PORST_XMASK (0x00000000) /**0b0000000000000000 < Power On Reset */ +#define RMU_RSTCAUSE_BODUNREGRST_XMASK (0x00000081) /**0b0000000010000001 < Brown Out Detector Unregulated Domain Reset */ +#define RMU_RSTCAUSE_BODREGRST_XMASK (0x00000091) /**0b0000000010010001 < Brown Out Detector Regulated Domain Reset */ +#define RMU_RSTCAUSE_EXTRST_XMASK (0x00000001) /**0b0000000000000001 < External Pin Reset */ +#define RMU_RSTCAUSE_WDOGRST_XMASK (0x00000003) /**0b0000000000000011 < Watchdog Reset */ +#define RMU_RSTCAUSE_LOCKUPRST_XMASK (0x0000EFDF) /**0b1110111111011111 < LOCKUP Reset */ +#define RMU_RSTCAUSE_SYSREQRST_XMASK (0x0000EF9F) /**0b1110111110011111 < System Request Reset */ +#define RMU_RSTCAUSE_EM4RST_XMASK (0x00000719) /**0b0000011100011001 < EM4 Reset */ +#define RMU_RSTCAUSE_EM4WURST_XMASK (0x00000619) /**0b0000011000011001 < EM4 Wake-up Reset */ +#define RMU_RSTCAUSE_BODAVDD0_XMASK (0x0000041F) /**0b0000010000011111 < AVDD0 Bod Reset. */ +#define RMU_RSTCAUSE_BODAVDD1_XMASK (0x0000021F) /**0b0000001000011111 < AVDD1 Bod Reset. */ +#define NUM_RSTCAUSES (11) + +#elif (_RMU_RSTCAUSE_MASK == 0x0000FFFFUL) +#define RMU_RSTCAUSE_PORST_XMASK (0x00000000) /**0b0000000000000000 < Power On Reset */ +#define RMU_RSTCAUSE_BODUNREGRST_XMASK (0x00000081) /**0b0000000010000001 < Brown Out Detector Unregulated Domain Reset */ +#define RMU_RSTCAUSE_BODREGRST_XMASK (0x00000091) /**0b0000000010010001 < Brown Out Detector Regulated Domain Reset */ +#define RMU_RSTCAUSE_EXTRST_XMASK (0x00000001) /**0b0000000000000001 < External Pin Reset */ +#define RMU_RSTCAUSE_WDOGRST_XMASK (0x00000003) /**0b0000000000000011 < Watchdog Reset */ +#define RMU_RSTCAUSE_LOCKUPRST_XMASK (0x0000EFDF) /**0b1110111111011111 < LOCKUP Reset */ +#define RMU_RSTCAUSE_SYSREQRST_XMASK (0x0000EF9F) /**0b1110111110011111 < System Request Reset */ +#define RMU_RSTCAUSE_EM4RST_XMASK (0x00000719) /**0b0000011100011001 < EM4 Reset */ +#define RMU_RSTCAUSE_EM4WURST_XMASK (0x00000619) /**0b0000011000011001 < EM4 Wake-up Reset */ +#define RMU_RSTCAUSE_BODAVDD0_XMASK (0x0000041F) /**0b0000010000011111 < AVDD0 Bod Reset */ +#define RMU_RSTCAUSE_BODAVDD1_XMASK (0x0000021F) /**0b0000001000011111 < AVDD1 Bod Reset */ +#define RMU_RSTCAUSE_BUBODVDDDREG_XMASK (0x00000001) /**0b0000000000000001 < Backup Brown Out Detector, VDD_DREG */ +#define RMU_RSTCAUSE_BUBODBUVIN_XMASK (0x00000001) /**0b0000000000000001 < Backup Brown Out Detector, BU_VIN */ +#define RMU_RSTCAUSE_BUBODUNREG_XMASK (0x00000001) /**0b0000000000000001 < Backup Brown Out Detector Unregulated Domain */ +#define RMU_RSTCAUSE_BUBODREG_XMASK (0x00000001) /**0b0000000000000001 < Backup Brown Out Detector Regulated Domain */ +#define RMU_RSTCAUSE_BUMODERST_XMASK (0x00000001) /**0b0000000000000001 < Backup mode reset */ +#define NUM_RSTCAUSES (16) + +#elif ((_RMU_RSTCAUSE_MASK & 0x0FFFFFFF) == 0x00010F1DUL) +#define RMU_RSTCAUSE_PORST_XMASK (0x00000000) /**0b0000000000000000 < Power On Reset */ +#define RMU_RSTCAUSE_BODAVDD_XMASK (0x00000001) /**0b0000000000000001 < AVDD Bod Reset */ +#define RMU_RSTCAUSE_BODDVDD_XMASK (0x00000003) /**0b0000000000000011 < DVDD Bod Reset */ +#define RMU_RSTCAUSE_BODREGRST_XMASK (0x0000000F) /**0b0000000000001111 < Brown Out Detector Regulated Domain Reset */ +#define RMU_RSTCAUSE_EXTRST_XMASK (0x0000000F) /**0b0000000000001111 < External Pin Reset */ +#define RMU_RSTCAUSE_LOCKUPRST_XMASK (0x0000001F) /**0b0000000000011111 < LOCKUP Reset */ +#define RMU_RSTCAUSE_SYSREQRST_XMASK (0x0000001F) /**0b0000000000011111 < System Request Reset */ +#define RMU_RSTCAUSE_WDOGRST_XMASK (0x0000001F) /**0b0000000000011111 < Watchdog Reset */ +#define RMU_RSTCAUSE_EM4RST_XMASK (0x00000003) /**0b0000000000000011 < EM4H/S Reset */ +#define NUM_RSTCAUSES (9) + +#else +#warning "RMU_RSTCAUSE XMASKs are not defined for this family." +#endif + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** Reset cause mask type. */ +typedef struct +{ + uint32_t resetCauseMask; + uint32_t dontCareMask; +} RMU_ResetCauseMasks_Typedef; + + +/******************************************************************************* + ******************************* TYPEDEFS ********************************** + ******************************************************************************/ + +/** Reset cause mask table. */ +static const RMU_ResetCauseMasks_Typedef resetCauseMasks[NUM_RSTCAUSES] = + { + { RMU_RSTCAUSE_PORST, RMU_RSTCAUSE_PORST_XMASK }, +#if defined(RMU_RSTCAUSE_BODUNREGRST) + { RMU_RSTCAUSE_BODUNREGRST, RMU_RSTCAUSE_BODUNREGRST_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_BODREGRST) + { RMU_RSTCAUSE_BODREGRST, RMU_RSTCAUSE_BODREGRST_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_AVDDBOD) + { RMU_RSTCAUSE_AVDDBOD, RMU_RSTCAUSE_BODAVDD_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_DVDDBOD) + { RMU_RSTCAUSE_DVDDBOD, RMU_RSTCAUSE_BODDVDD_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_DECBOD) + { RMU_RSTCAUSE_DECBOD, RMU_RSTCAUSE_BODREGRST_XMASK }, +#endif + { RMU_RSTCAUSE_EXTRST, RMU_RSTCAUSE_EXTRST_XMASK }, + { RMU_RSTCAUSE_WDOGRST, RMU_RSTCAUSE_WDOGRST_XMASK }, + { RMU_RSTCAUSE_LOCKUPRST, RMU_RSTCAUSE_LOCKUPRST_XMASK }, + { RMU_RSTCAUSE_SYSREQRST, RMU_RSTCAUSE_SYSREQRST_XMASK }, +#if defined(RMU_RSTCAUSE_EM4RST) + { RMU_RSTCAUSE_EM4RST, RMU_RSTCAUSE_EM4RST_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_EM4WURST) + { RMU_RSTCAUSE_EM4WURST, RMU_RSTCAUSE_EM4WURST_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_BODAVDD0) + { RMU_RSTCAUSE_BODAVDD0, RMU_RSTCAUSE_BODAVDD0_XMASK }, +#endif +#if defined(RMU_RSTCAUSE_BODAVDD1) + { RMU_RSTCAUSE_BODAVDD1, RMU_RSTCAUSE_BODAVDD1_XMASK }, +#endif +#if defined(BU_PRESENT) + { RMU_RSTCAUSE_BUBODVDDDREG, RMU_RSTCAUSE_BUBODVDDDREG_XMASK }, + { RMU_RSTCAUSE_BUBODBUVIN, RMU_RSTCAUSE_BUBODBUVIN_XMASK }, + { RMU_RSTCAUSE_BUBODUNREG, RMU_RSTCAUSE_BUBODUNREG_XMASK }, + { RMU_RSTCAUSE_BUBODREG, RMU_RSTCAUSE_BUBODREG_XMASK }, + { RMU_RSTCAUSE_BUMODERST, RMU_RSTCAUSE_BUMODERST_XMASK }, +#endif + }; + + +/******************************************************************************* + ******************************** TEST ******************************** + ******************************************************************************/ +#if defined(EMLIB_REGRESSION_TEST) +/* Test variable that replaces the RSTCAUSE cause register when testing + the RMU_ResetCauseGet function. */ +extern uint32_t rstCause; +#endif + + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Disable/enable reset for various peripherals and signal sources + * + * @param[in] reset Reset types to enable/disable + * + * @param[in] mode Reset mode + ******************************************************************************/ +void RMU_ResetControl(RMU_Reset_TypeDef reset, RMU_ResetMode_TypeDef mode) +{ + /* Note that the RMU supports bit-band access, but not peripheral bit-field set/clear */ +#if defined(_RMU_CTRL_PINRMODE_MASK) + uint32_t val; +#endif + uint32_t shift; + + shift = EFM32_CTZ((uint32_t)reset); +#if defined(_RMU_CTRL_PINRMODE_MASK) + val = (uint32_t)mode << shift; + RMU->CTRL = (RMU->CTRL & ~reset) | val; +#else + BUS_RegBitWrite(&RMU->CTRL, (uint32_t)shift, mode ? 1 : 0); +#endif +} + + +/***************************************************************************//** + * @brief + * Clear the reset cause register. + * + * @details + * This function clears all the reset cause bits of the RSTCAUSE register. + * The reset cause bits must be cleared by SW before a new reset occurs, + * otherwise reset causes may accumulate. See @ref RMU_ResetCauseGet(). + ******************************************************************************/ +void RMU_ResetCauseClear(void) +{ + RMU->CMD = RMU_CMD_RCCLR; + +#if defined(EMU_AUXCTRL_HRCCLR) + { + uint32_t locked; + + /* 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(); + } + + BUS_RegBitWrite(&(EMU->AUXCTRL), _EMU_AUXCTRL_HRCCLR_SHIFT, 1); + BUS_RegBitWrite(&(EMU->AUXCTRL), _EMU_AUXCTRL_HRCCLR_SHIFT, 0); + + if (locked) + { + EMU_Lock(); + } + } +#endif +} + + +/***************************************************************************//** + * @brief + * Get the cause of the last reset. + * + * @details + * In order to be useful, the reset cause must be cleared by software before a new + * reset occurs, otherwise reset causes may accumulate. See @ref + * RMU_ResetCauseClear(). This function call will return the main cause for + * reset, which can be a bit mask (several causes), and clear away "noise". + * + * @return + * Reset cause mask. Please consult the reference manual for description + * of the reset cause mask. + ******************************************************************************/ +uint32_t RMU_ResetCauseGet(void) +{ +#if !defined(EMLIB_REGRESSION_TEST) + uint32_t rstCause = RMU->RSTCAUSE; +#endif + uint32_t validRstCause = 0; + uint32_t i; + + for (i = 0; i < NUM_RSTCAUSES; i++) + { + /* Checks to see if rstCause matches a RSTCAUSE and is not excluded by the X-mask */ + if ((rstCause & resetCauseMasks[i].resetCauseMask) + && !(rstCause & resetCauseMasks[i].dontCareMask)) + { + /* Adds the reset-cause to list of real reset-causes */ + validRstCause |= resetCauseMasks[i].resetCauseMask; + } + } + return validRstCause; +} + + +/** @} (end addtogroup RMU) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(RMU_COUNT) && (RMU_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_rtc.c b/cpu/efm32_common/emlib/src/em_rtc.c new file mode 100644 index 0000000000000..02ce80c6b131b --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_rtc.c @@ -0,0 +1,377 @@ +/***************************************************************************//** + * @file em_rtc.c + * @brief Real Time Counter (RTC) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_rtc.h" +#if defined(RTC_COUNT) && (RTC_COUNT > 0) + +#include "em_assert.h" +#include "em_bus.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 regSync(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 regSync() 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 */ + regSync(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 regSync() 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 */ + regSync(RTC_SYNCBUSY_CTRL); +#endif + + BUS_RegBitWrite(&(RTC->CTRL), _RTC_CTRL_EN_SHIFT, enable); + +#if defined(_EFM32_GECKO_FAMILY) + /* Wait for CTRL to be updated before returning, because calling code may + depend upon that the CTRL register is updated after this function has + returned. */ + regSync(RTC_SYNCBUSY_CTRL); +#endif +} + + +/***************************************************************************//** + * @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 regSync() 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 */ + regSync(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; + +#if defined(_EFM32_GECKO_FAMILY) + /* Wait for CTRL, COMP0 and COMP1 to be updated before returning, because the + calling code may depend upon that the register values are updated after + this function has returned. */ + regSync(RTC_SYNCBUSY_CTRL | RTC_SYNCBUSY_COMP0 | RTC_SYNCBUSY_COMP1); +#endif +} + + + +/***************************************************************************//** + * @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/cpu/efm32_common/emlib/src/em_rtcc.c b/cpu/efm32_common/emlib/src/em_rtcc.c new file mode 100644 index 0000000000000..c2b0fb57f8f63 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_rtcc.c @@ -0,0 +1,180 @@ +/***************************************************************************//** + * @file + * @brief Real Time Counter with Calendar (RTCC) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_rtcc.h" +#if defined( RTCC_COUNT ) && ( RTCC_COUNT == 1 ) +#include "em_bus.h" + +/***************************************************************************//** + * @addtogroup EM_Library + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup RTCC + * @brief Real Time Counter (RTCC) Peripheral API + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/******************************************************************************* + ************************** LOCAL FUNCTIONS ******************************** + ******************************************************************************/ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Configure the selected capture/compare channel of the RTCC. + * + * @details + * Use this function to configure a RTCC channel. + * Select capture/compare mode, match output action, overflow output action + * and PRS input configuration. + * Refer to the configuration structure @ref RTCC_CCChConf_TypeDef for more + * details. + * + * @param[in] ch + * Channel selector. + * + * @param[in] confPtr + * Pointer to configuration structure. + ******************************************************************************/ +void RTCC_ChannelInit( int ch, RTCC_CCChConf_TypeDef const *confPtr ) +{ + EFM_ASSERT( RTCC_CH_VALID( ch ) ); + EFM_ASSERT( (uint32_t)confPtr->compMask + < ( _RTCC_CC_CTRL_COMPMASK_MASK >> _RTCC_CC_CTRL_COMPMASK_SHIFT ) + + 1 ); + + /** Configure the selected capture/compare channel. */ + RTCC->CC[ch].CTRL = ( (uint32_t)confPtr->chMode << _RTCC_CC_CTRL_MODE_SHIFT ) + | ( (uint32_t)confPtr->compMatchOutAction << _RTCC_CC_CTRL_CMOA_SHIFT ) + | ( (uint32_t)confPtr->prsSel << _RTCC_CC_CTRL_PRSSEL_SHIFT ) + | ( (uint32_t)confPtr->inputEdgeSel << _RTCC_CC_CTRL_ICEDGE_SHIFT ) + | ( (uint32_t)confPtr->compBase << _RTCC_CC_CTRL_COMPBASE_SHIFT ) + | ( (uint32_t)confPtr->compMask << _RTCC_CC_CTRL_COMPMASK_SHIFT ) + | ( (uint32_t)confPtr->dayCompMode << _RTCC_CC_CTRL_DAYCC_SHIFT ); +} + +/***************************************************************************//** + * @brief + * Enable/disable RTCC. + * + * @param[in] enable + * True to enable RTCC, false to disable. + ******************************************************************************/ +void RTCC_Enable( bool enable ) +{ + /* Bitbanding the enable bit in the CTRL register (atomic). */ + BUS_RegBitWrite((&RTCC->CTRL), _RTCC_CTRL_ENABLE_SHIFT, enable); +} + +/***************************************************************************//** + * @brief + * Initialize RTCC. + * + * @details + * Note that the compare values must be set separately with RTCC_CompareSet(). + * That should probably be done prior to the use of this function if + * configuring the RTCC to start when initialization is completed. + * + * @param[in] init + * Pointer to RTCC initialization structure. + ******************************************************************************/ +void RTCC_Init( const RTCC_Init_TypeDef *init ) +{ + RTCC->CTRL = ( (uint32_t)init->enable << _RTCC_CTRL_ENABLE_SHIFT ) + | ( (uint32_t)init->debugRun << _RTCC_CTRL_DEBUGRUN_SHIFT ) + | ( (uint32_t)init->precntWrapOnCCV0 << _RTCC_CTRL_PRECCV0TOP_SHIFT ) + | ( (uint32_t)init->cntWrapOnCCV1 << _RTCC_CTRL_CCV1TOP_SHIFT ) + | ( (uint32_t)init->presc << _RTCC_CTRL_CNTPRESC_SHIFT ) + | ( (uint32_t)init->prescMode << _RTCC_CTRL_CNTTICK_SHIFT ) +#if defined(_RTCC_CTRL_BUMODETSEN_MASK) + | ( (uint32_t)init->enaBackupModeSet << _RTCC_CTRL_BUMODETSEN_SHIFT ) +#endif + | ( (uint32_t)init->enaOSCFailDetect << _RTCC_CTRL_OSCFDETEN_SHIFT ) + | ( (uint32_t)init->cntMode << _RTCC_CTRL_CNTMODE_SHIFT ) + | ( (uint32_t)init->disLeapYearCorr << _RTCC_CTRL_LYEARCORRDIS_SHIFT ); +} + +/***************************************************************************//** + * @brief + * Restore RTCC to its reset state. + ******************************************************************************/ +void RTCC_Reset( void ) +{ + int i; + + /* Restore all RTCC registers to their default values. */ + RTCC_Unlock(); + RTCC->CTRL = _RTCC_CTRL_RESETVALUE; + RTCC->PRECNT = _RTCC_PRECNT_RESETVALUE; + RTCC->CNT = _RTCC_CNT_RESETVALUE; + RTCC->TIME = _RTCC_TIME_RESETVALUE; + RTCC->DATE = _RTCC_DATE_RESETVALUE; + RTCC->IEN = _RTCC_IEN_RESETVALUE; + RTCC->IFC = _RTCC_IFC_MASK; + RTCC_StatusClear(); + RTCC->EM4WUEN = _RTCC_EM4WUEN_RESETVALUE; + + for (i = 0; i < 3; i++) + { + RTCC->CC[i].CTRL = _RTCC_CC_CTRL_RESETVALUE; + RTCC->CC[i].CCV = _RTCC_CC_CCV_RESETVALUE; + RTCC->CC[i].TIME = _RTCC_CC_TIME_RESETVALUE; + RTCC->CC[i].DATE = _RTCC_CC_DATE_RESETVALUE; + } +} + +/***************************************************************************//** + * @brief + * Clear STATUS register. + ******************************************************************************/ +void RTCC_StatusClear( void ) +{ + while ( RTCC->SYNCBUSY & RTCC_SYNCBUSY_CMD ) + { + // Wait for syncronization. + } + RTCC->CMD = RTCC_CMD_CLRSTATUS; +} + +/** @} (end addtogroup RTCC) */ +/** @} (end addtogroup EM_Library) */ + +#endif /* defined( RTCC_COUNT ) && ( RTCC_COUNT == 1 ) */ diff --git a/cpu/efm32_common/emlib/src/em_system.c b/cpu/efm32_common/emlib/src/em_system.c new file mode 100644 index 0000000000000..98e8a0ab61188 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_system.c @@ -0,0 +1,121 @@ +/***************************************************************************//** + * @file em_system.c + * @brief System Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_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); + + /* CHIP FAMILY bit [5:2] */ + tmp = (((ROMTABLE->PID1 & _ROMTABLE_PID1_FAMILYMSB_MASK) >> _ROMTABLE_PID1_FAMILYMSB_SHIFT) << 2); + /* CHIP FAMILY bit [1:0] */ + tmp |= ((ROMTABLE->PID0 & _ROMTABLE_PID0_FAMILYLSB_MASK) >> _ROMTABLE_PID0_FAMILYLSB_SHIFT); + rev->family = tmp; + + /* CHIP MAJOR bit [3:0] */ + rev->major = (ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) >> _ROMTABLE_PID0_REVMAJOR_SHIFT; + + /* CHIP MINOR bit [7:4] */ + tmp = (((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK) >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4); + /* CHIP MINOR bit [3:0] */ + tmp |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) >> _ROMTABLE_PID3_REVMINORLSB_SHIFT); + rev->minor = tmp; +} + + +#if defined(CALIBRATE) +/***************************************************************************//** + * @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++; + } +} +#endif /* defined (CALIBRATE) */ + +/** @} (end addtogroup SYSTEM) */ +/** @} (end addtogroup EM_Library) */ diff --git a/cpu/efm32_common/emlib/src/em_timer.c b/cpu/efm32_common/emlib/src/em_timer.c new file mode 100644 index 0000000000000..24485ca1a4515 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_timer.c @@ -0,0 +1,260 @@ +/***************************************************************************//** + * @file em_timer.c + * @brief Timer/counter (TIMER) Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_timer.h" +#if defined(TIMER_COUNT) && (TIMER_COUNT > 0) + +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup 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). + * @{ + ******************************************************************************/ + + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @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) + | ((uint32_t)(init->prsSel) << _TIMER_CC_CTRL_PRSSEL_SHIFT) + | ((uint32_t)(init->cufoa) << _TIMER_CC_CTRL_CUFOA_SHIFT) + | ((uint32_t)(init->cofoa) << _TIMER_CC_CTRL_COFOA_SHIFT) + | ((uint32_t)(init->cmoa) << _TIMER_CC_CTRL_CMOA_SHIFT) + | ((uint32_t)(init->mode) << _TIMER_CC_CTRL_MODE_SHIFT) + | (init->filter ? TIMER_CC_CTRL_FILT_ENABLE : 0) + | (init->prsInput ? TIMER_CC_CTRL_INSEL_PRS : 0) + | (init->coist ? TIMER_CC_CTRL_COIST : 0) + | (init->outInvert ? TIMER_CC_CTRL_OUTINV : 0); +} + + +#if defined(_TIMER_DTCTRL_MASK) +/***************************************************************************//** + * @brief + * Initialize the TIMER DTI unit. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] init + * Pointer to TIMER DTI initialization structure. + ******************************************************************************/ +void TIMER_InitDTI(TIMER_TypeDef *timer, const TIMER_InitDTI_TypeDef *init) +{ + EFM_ASSERT(TIMER0 == timer); + + /* Make sure the DTI unit is disabled while initializing. */ + TIMER_EnableDTI (timer, false); + + /* Setup the DTCTRL register. + The enable bit will be set at the end of the function if specified. */ + timer->DTCTRL = + (init->autoRestart ? TIMER_DTCTRL_DTDAS : 0) + | (init->activeLowOut ? TIMER_DTCTRL_DTIPOL : 0) + | (init->invertComplementaryOut ? TIMER_DTCTRL_DTCINV : 0) + | (init->enablePrsSource ? TIMER_DTCTRL_DTPRSEN : 0) + | ((uint32_t)(init->prsSel) << _TIMER_DTCTRL_DTPRSSEL_SHIFT); + + /* Setup the DTTIME register. */ + timer->DTTIME = + ((uint32_t)(init->prescale) << _TIMER_DTTIME_DTPRESC_SHIFT) + | ((uint32_t)(init->riseTime) << _TIMER_DTTIME_DTRISET_SHIFT) + | ((uint32_t)(init->fallTime) << _TIMER_DTTIME_DTFALLT_SHIFT); + + /* Setup the DTFC register. */ + timer->DTFC = + (init->enableFaultSourceCoreLockup ? TIMER_DTFC_DTLOCKUPFEN : 0) + | (init->enableFaultSourceDebugger ? TIMER_DTFC_DTDBGFEN : 0) + | (init->enableFaultSourcePrsSel0 ? TIMER_DTFC_DTPRS0FEN : 0) + | (init->enableFaultSourcePrsSel1 ? TIMER_DTFC_DTPRS1FEN : 0) + | ((uint32_t)(init->faultAction) << _TIMER_DTFC_DTFA_SHIFT) + | ((uint32_t)(init->faultSourcePrsSel0) << _TIMER_DTFC_DTPRS0FSEL_SHIFT) + | ((uint32_t)(init->faultSourcePrsSel1) << _TIMER_DTFC_DTPRS1FSEL_SHIFT); + + /* Setup the DTOGEN register. */ + timer->DTOGEN = init->outputsEnableMask; + + /* Clear any previous DTI faults. */ + TIMER_ClearDTIFault(timer, TIMER_GetDTIFault(timer)); + + /* Enable/disable before returning. */ + TIMER_EnableDTI (timer, init->enable); +} +#endif + + +/***************************************************************************//** + * @brief + * Reset 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 */ + +#if defined(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 +} + + +/** @} (end addtogroup TIMER) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(TIMER_COUNT) && (TIMER_COUNT > 0) */ diff --git a/cpu/efm32_common/emlib/src/em_usart.c b/cpu/efm32_common/emlib/src/em_usart.c new file mode 100644 index 0000000000000..1ea5e40468356 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_usart.c @@ -0,0 +1,1215 @@ +/***************************************************************************//** + * @file em_usart.c + * @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART) + * Peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_usart.h" +#if defined(USART_COUNT) && (USART_COUNT > 0) + +#include "em_cmu.h" +#include "em_bus.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) && defined(USART2) +#define USART_REF_VALID(ref) (((ref) == USART1) || ((ref) == USART2)) + +#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)) +#elif (USART_COUNT == 5) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ + ((ref) == USART2) || ((ref) == USART3) || \ + ((ref) == USART4)) +#elif (USART_COUNT == 6) +#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) || \ + ((ref) == USART2) || ((ref) == USART3) || \ + ((ref) == USART4) || ((ref) == USART5)) +#else +#error "Undefined number of USARTs." +#endif + +#if defined(USARTRF_COUNT) && (USARTRF_COUNT > 0) +#if (USARTRF_COUNT == 1) && defined(USARTRF0) +#define USARTRF_REF_VALID(ref) ((ref) == USARTRF0) +#elif (USARTRF_COUNT == 1) && defined(USARTRF1) +#define USARTRF_REF_VALID(ref) ((ref) == USARTRF1) +#else +#define USARTRF_REF_VALID(ref) (0) +#endif +#else +#define USARTRF_REF_VALID(ref) (0) +#endif + +#if defined(_EZR32_HAPPY_FAMILY) +#define USART_IRDA_VALID(ref) ((ref) == USART0) +#elif defined(_EFM32_HAPPY_FAMILY) +#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) +#elif 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(_EZR32_HAPPY_FAMILY) +#define USART_I2S_VALID(ref) ((ref) == USART0) +#elif defined(_EFM32_HAPPY_FAMILY) +#define USART_I2S_VALID(ref) (((ref) == USART0) || ((ref) == USART1)) +#elif defined(_EFM32_TINY_FAMILY) || defined(_EFM32_ZERO_FAMILY) || defined(_SILICON_LABS_32B_PLATFORM_2) +#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 or 3 bits of CLKDIV are don't care, we can base our + * integer arithmetic on the below formula + * + * CLKDIV / 64 = (4 * fHFPERCLK)/(oversample * br) - 4 (3 bits dont care) + * or + * CLKDIV / 8 = (32 * fHFPERCLK)/(oversample * br) - 32 (6 bits dont care) + * + * 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. */ +#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) + clkdiv = 32 * refFreq + (oversample * baudrate) / 2; + clkdiv /= (oversample * baudrate); + clkdiv -= 32; + clkdiv *= 8; +#else + clkdiv = 4 * refFreq + (oversample * baudrate) / 2; + clkdiv /= (oversample * baudrate); + clkdiv -= 4; + clkdiv *= 64; +#endif + + /* Verify that resulting clock divider is within limits */ + EFM_ASSERT(clkdiv <= _USART_CLKDIV_DIV_MASK); + + /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ + clkdiv &= _USART_CLKDIV_DIV_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; + uint64_t divisor; + uint64_t factor; + uint64_t remainder; + uint64_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 larger than a 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 <= 0xFFFFF8 */ + /* 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. However, factor * remainder can become larger than 32-bit */ + /* because of the size of _USART_CLKDIV_DIV_MASK on some families. */ + br = (uint32_t)(factor * quotient); + + /* + * factor <= 128 and remainder < (oversample*(256 + clkdiv)), which + * means dividend (factor * remainder) worst case is + * 128 * (3 * (256 + _USART_CLKDIV_DIV_MASK)) = 0x1_8001_7400. + */ + br += (uint32_t)((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) +{ +#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) + uint64_t clkdiv; +#else + uint32_t clkdiv; +#endif + + /* Inhibit divide by 0 */ + EFM_ASSERT(baudrate); + + /* + * CLKDIV in synchronous mode is given by: + * + * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1) + */ + + /* HFPERCLK used to clock all USART/UART peripheral modules */ + if (!refFreq) + { + refFreq = CMU_ClockFreqGet(cmuClock_HFPER); + } + +#if defined(_USART_CLKDIV_DIV_MASK) && (_USART_CLKDIV_DIV_MASK >= 0x7FFFF8UL) + /* Calculate and set CLKDIV without fractional bits */ + clkdiv = 2 * baudrate; + clkdiv = (0x100ULL * (uint64_t)refFreq) / clkdiv; + + /* Round up by not subtracting 256 and mask off fractional part */ + clkdiv &= ~0xFF; +#else + /* 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; +#endif + + /* Verify that resulting clock divider is within limits */ + EFM_ASSERT(!(clkdiv & ~_USART_CLKDIV_DIV_MASK)); + + /* If EFM_ASSERT is not enabled, make sure we don't write to reserved bits */ + clkdiv &= _USART_CLKDIV_DIV_MASK; + + BUS_RegMaskedWrite(&usart->CLKDIV, _USART_CLKDIV_DIV_MASK, 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) + || USARTRF_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) + || USARTRF_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); + +#if defined(_USART_TIMING_CSHOLD_MASK) + usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT) + & _USART_TIMING_CSHOLD_MASK) + | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT) + & _USART_TIMING_CSSETUP_MASK); + if (init->autoCsEnable) + { + usart->CTRL |= USART_CTRL_AUTOCS; + } +#endif + /* 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) || USARTRF_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_CTRL_AUTOTX_MASK) + usart->CTRL |= init->autoTx ? USART_CTRL_AUTOTX : 0; +#endif + +#if defined(_USART_INPUT_RXPRS_MASK) + /* Configure PRS input mode. */ + if (init->prsRxEnable) + { + usart->INPUT = (uint32_t)init->prsRxCh | USART_INPUT_RXPRS; + } +#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; + } + +#if defined(_USART_TIMING_CSHOLD_MASK) + usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT) + & _USART_TIMING_CSHOLD_MASK) + | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT) + & _USART_TIMING_CSSETUP_MASK); + if (init->autoCsEnable) + { + usart->CTRL |= USART_CTRL_AUTOCS; + } +#endif + + 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) + || USARTRF_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; +#if defined(_USART_ROUTEPEN_MASK) || defined(_UART_ROUTEPEN_MASK) + usart->ROUTEPEN = _USART_ROUTEPEN_RESETVALUE; + usart->ROUTELOC0 = _USART_ROUTELOC0_RESETVALUE; + usart->ROUTELOC1 = _USART_ROUTELOC1_RESETVALUE; +#else + usart->ROUTE = _USART_ROUTE_RESETVALUE; +#endif + + 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 @ref 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 the buffer is empty, until data is received. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDataGet() to read the RXDATA + * register directly. + * + * @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 @ref 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. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDoubleGet() to read the RXDOUBLE + * register directly. + * + * @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. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDoubleXGet() to read the RXDOUBLEX + * register directly. + * + * @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. + * Alternatively the user can explicitly check whether data is available, and + * if data is avaliable, call @ref USART_RxDataXGet() to read the RXDATAX + * register directly. + * + * @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 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 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/cpu/efm32_common/emlib/src/em_vcmp.c b/cpu/efm32_common/emlib/src/em_vcmp.c new file mode 100644 index 0000000000000..8f7d8edff7bb1 --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_vcmp.c @@ -0,0 +1,184 @@ +/***************************************************************************//** + * @file em_vcmp.c + * @brief Voltage Comparator (VCMP) peripheral API + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_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/cpu/efm32_common/emlib/src/em_wdog.c b/cpu/efm32_common/emlib/src/em_wdog.c new file mode 100644 index 0000000000000..b8e0a823b742e --- /dev/null +++ b/cpu/efm32_common/emlib/src/em_wdog.c @@ -0,0 +1,232 @@ +/***************************************************************************//** + * @file em_wdog.c + * @brief Watchdog (WDOG) peripheral API + * devices. + * @version 4.2.1 + ******************************************************************************* + * @section License + * (C) Copyright 2015 Silicon Labs, http://www.silabs.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: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_wdog.h" +#if defined(WDOG_COUNT) && (WDOG_COUNT > 0) + +#if defined(WDOG0) +#define WDOG WDOG0 +#if (WDOG_COUNT > 1) +#warning "Multiple watchdogs not supported" +#endif +#endif + +#include "em_bus.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) + ; + } + BUS_RegBitWrite(&(WDOG->CTRL), _WDOG_CTRL_EN_SHIFT, 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 + { + BUS_RegBitWrite(&(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 */ + BUS_RegBitWrite(&(WDOG->CTRL), _WDOG_CTRL_LOCK_SHIFT, 1); +} + + +/** @} (end addtogroup WDOG) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */