diff --git a/efm32/.cproject b/efm32/.cproject
index 3ecf84c..0f9b67d 100644
--- a/efm32/.cproject
+++ b/efm32/.cproject
@@ -23,7 +23,7 @@
-
+
@@ -150,7 +150,7 @@
-
+
@@ -218,7 +218,7 @@
-
+
diff --git a/efm32/EFM32.hwconf b/efm32/EFM32.hwconf
index c535aab..cb9fa1c 100644
--- a/efm32/EFM32.hwconf
+++ b/efm32/EFM32.hwconf
@@ -1,6 +1,7 @@
+
diff --git a/efm32/emlib/em_adc.c b/efm32/emlib/em_adc.c
new file mode 100644
index 0000000..10e9840
--- /dev/null
+++ b/efm32/emlib/em_adc.c
@@ -0,0 +1,1092 @@
+/***************************************************************************//**
+ * @file em_adc.c
+ * @brief Analog to Digital Converter (ADC) Peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. 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_assert.h"
+#include "em_cmu.h"
+#include
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup ADC
+ * @brief Analog to Digital Converter (ADC) Peripheral API
+ * @details
+ * This module contains functions to control the ADC peripheral of Silicon
+ * Labs 32-bit MCUs and SoCs. The ADC is used to convert analog signals into a
+ * digital representation.
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Validation of ADC register block pointer reference for assert statements. */
+#if (ADC_COUNT == 1)
+#define ADC_REF_VALID(ref) ((ref) == ADC0)
+#elif (ADC_COUNT == 2)
+#define ADC_REF_VALID(ref) (((ref) == ADC0) || ((ref) == ADC1))
+#endif
+
+/** Max ADC clock */
+#if defined(_SILICON_LABS_32B_SERIES_0)
+#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
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+#define FIX_ADC_TEMP_BIAS_EN
+#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;
+ __IM uint32_t * diCalReg;
+
+ 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;
+
+ if (adc == ADC0) {
+ diCalReg = &DEVINFO->ADC0CAL0;
+ }
+#if defined(ADC1)
+ else if (adc == ADC1) {
+ diCalReg = &DEVINFO->ADC1CAL0;
+ }
+#endif
+ else {
+ return;
+ }
+
+ switch (ref) {
+ case adcRef1V25:
+ newCal |= ((diCalReg[0] & DEVINFO_ADC0_GAIN1V25_MASK)
+ >> DEVINFO_ADC0_GAIN1V25_SHIFT)
+ << _ADC_CAL_SINGLEGAIN_SHIFT;
+ newCal |= ((diCalReg[0] & DEVINFO_ADC0_OFFSET1V25_MASK)
+ >> DEVINFO_ADC0_OFFSET1V25_SHIFT)
+ << _ADC_CAL_SINGLEOFFSET_SHIFT;
+#if defined(_ADC_CAL_SINGLEOFFSETINV_MASK)
+ newCal |= ((diCalReg[0] & _DEVINFO_ADC0CAL0_NEGSEOFFSET1V25_MASK)
+ >> _DEVINFO_ADC0CAL0_NEGSEOFFSET1V25_SHIFT)
+ << _ADC_CAL_SINGLEOFFSETINV_SHIFT;
+#endif
+ break;
+
+ case adcRef2V5:
+ newCal |= ((diCalReg[0] & DEVINFO_ADC0_GAIN2V5_MASK)
+ >> DEVINFO_ADC0_GAIN2V5_SHIFT)
+ << _ADC_CAL_SINGLEGAIN_SHIFT;
+ newCal |= ((diCalReg[0] & DEVINFO_ADC0_OFFSET2V5_MASK)
+ >> DEVINFO_ADC0_OFFSET2V5_SHIFT)
+ << _ADC_CAL_SINGLEOFFSET_SHIFT;
+#if defined(_ADC_CAL_SINGLEOFFSETINV_MASK)
+ newCal |= ((diCalReg[0] & _DEVINFO_ADC0CAL0_NEGSEOFFSET2V5_MASK)
+ >> _DEVINFO_ADC0CAL0_NEGSEOFFSET2V5_SHIFT)
+ << _ADC_CAL_SINGLEOFFSETINV_SHIFT;
+#endif
+ break;
+
+ case adcRefVDD:
+ newCal |= ((diCalReg[1] & DEVINFO_ADC0_GAINVDD_MASK)
+ >> DEVINFO_ADC0_GAINVDD_SHIFT)
+ << _ADC_CAL_SINGLEGAIN_SHIFT;
+ newCal |= ((diCalReg[1] & DEVINFO_ADC0_OFFSETVDD_MASK)
+ >> DEVINFO_ADC0_OFFSETVDD_SHIFT)
+ << _ADC_CAL_SINGLEOFFSET_SHIFT;
+#if defined(_ADC_CAL_SINGLEOFFSETINV_MASK)
+ newCal |= ((diCalReg[1] & _DEVINFO_ADC0CAL1_NEGSEOFFSETVDD_MASK)
+ >> _DEVINFO_ADC0CAL1_NEGSEOFFSETVDD_SHIFT)
+ << _ADC_CAL_SINGLEOFFSETINV_SHIFT;
+#endif
+ break;
+
+ case adcRef5VDIFF:
+ newCal |= ((diCalReg[1] & DEVINFO_ADC0_GAIN5VDIFF_MASK)
+ >> DEVINFO_ADC0_GAIN5VDIFF_SHIFT)
+ << _ADC_CAL_SINGLEGAIN_SHIFT;
+ newCal |= ((diCalReg[1] & DEVINFO_ADC0_OFFSET5VDIFF_MASK)
+ >> DEVINFO_ADC0_OFFSET5VDIFF_SHIFT)
+ << _ADC_CAL_SINGLEOFFSET_SHIFT;
+#if defined(_ADC_CAL_SINGLEOFFSETINV_MASK)
+ newCal |= ((diCalReg[1] & _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 |= ((diCalReg[2] & DEVINFO_ADC0_OFFSET2XVDD_MASK)
+ >> DEVINFO_ADC0_OFFSET2XVDD_SHIFT)
+ << _ADC_CAL_SINGLEOFFSET_SHIFT;
+#if defined(_ADC_CAL_SINGLEOFFSETINV_MASK)
+ newCal |= ((diCalReg[2] & _DEVINFO_ADC0CAL2_NEGSEOFFSET2XVDD_MASK)
+ >> _DEVINFO_ADC0CAL2_NEGSEOFFSET2XVDD_SHIFT)
+ << _ADC_CAL_SINGLEOFFSETINV_SHIFT;
+#endif
+ break;
+
+#if defined(_ADC_SINGLECTRLX_VREFSEL_VDDXWATT)
+ case adcRefVddxAtt:
+ newCal |= ((diCalReg[1] & DEVINFO_ADC0_GAINVDD_MASK)
+ >> DEVINFO_ADC0_GAINVDD_SHIFT)
+ << _ADC_CAL_SINGLEGAIN_SHIFT;
+ newCal |= ((diCalReg[1] & DEVINFO_ADC0_OFFSETVDD_MASK)
+ >> DEVINFO_ADC0_OFFSETVDD_SHIFT)
+ << _ADC_CAL_SINGLEOFFSET_SHIFT;
+ newCal |= ((diCalReg[1] & _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 @ref ADC_InitSingle() and @ref ADC_InitScan() respectively.
+ * For ADC architectures with the ADCn->SCANINPUTSEL register, use
+ * @ref ADC_ScanSingleEndedInputAdd() to configure single-ended scan inputs or
+ * @ref ADC_ScanDifferentialInputAdd() to configure differential scan inputs.
+ * @ref ADC_ScanInputClear() is also provided for applications that need to update
+ * the input configuration.
+ *
+ * @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;
+ uint8_t presc = init->prescale;
+
+ EFM_ASSERT(ADC_REF_VALID(adc));
+
+ if (presc == 0) {
+ /* Assume maximum ADC clock for prescaler 0 */
+ presc = ADC_PrescaleCalc(ADC_MAX_CLOCK, 0);
+ } else {
+ /* Check prescaler bounds against ADC_MAX_CLOCK and ADC_MIN_CLOCK */
+#if defined(_ADC_CTRL_ADCCLKMODE_MASK)
+ if (ADC0->CTRL & ADC_CTRL_ADCCLKMODE_SYNC)
+#endif
+ {
+ EFM_ASSERT(presc >= ADC_PrescaleCalc(ADC_MAX_CLOCK, 0));
+ EFM_ASSERT(presc <= ADC_PrescaleCalc(ADC_MIN_CLOCK, 0));
+ }
+ }
+
+ /* 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)(presc) << _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);
+#endif
+
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+ /* A debugger can trigger the SCANUF interrupt on EFM32xG1 or EFR32xG1 */
+ 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 = ADC_SCANINPUTSEL_NONE;
+ 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 == ADC_SCANINPUTSEL_GROUP_NONE) {
+ 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.
+ * For ADC architectures with the ADCn->SCANINPUTSEL register, use
+ * @ref ADC_ScanSingleEndedInputAdd() to configure single-ended scan inputs or
+ * @ref ADC_ScanDifferentialInputAdd() to configure differential scan inputs.
+ * @ref ADC_ScanInputClear() is also provided for applications that need to update
+ * the input configuration.
+ *
+ * @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_GECKO_INTERNAL_SDID_80)
+ /* Scan repeat mode does not work on EFM32JG1, EFM32PG1 or EFR32xG1x devices.
+ * The errata is called ADC_E211 in the errata document. */
+ 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)
+ /* Check for valid scan input configuration. Use @ref ADC_ScanInputClear()
+ @ref ADC_ScanSingleEndedInputAdd() and @ref ADC_ScanDifferentialInputAdd() to set
+ scan input configuration. */
+ EFM_ASSERT(init->scanInputConfig.scanInputSel != ADC_SCANINPUTSEL_NONE);
+ 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.
+ *
+ * @cond DOXYDOC_P2_DEVICE
+ * @note
+ * This function will set the BIASPROG_GPBIASACC bit when selecting the
+ * internal temperature sensor and clear the bit otherwise. Any
+ * application that depends on the state of the BIASPROG_GPBIASACC bit should
+ * modify it after a call to this function.
+ * @endcond
+ *
+ * @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;
+ }
+
+#if defined(_ADC_SINGLECTRL_POSSEL_TEMP)
+ /* Force at least 8 cycle acquisition time when reading internal temperature
+ * sensor with 1.25V reference */
+ if ((init->posSel == adcPosSelTEMP)
+ && (init->reference == adcRef1V25)
+ && (init->acqTime < adcAcqTime8)) {
+ tmp = (tmp & ~_ADC_SINGLECTRL_AT_MASK)
+ | (adcAcqTime8 << _ADC_SINGLECTRL_AT_SHIFT);
+ }
+#endif
+
+ /* 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(&adc->CTRL, _ADC_CTRL_SINGLEDMAWU_SHIFT, init->singleDmaEm2Wu);
+#endif
+
+#if defined(_ADC_BIASPROG_GPBIASACC_MASK) && defined(FIX_ADC_TEMP_BIAS_EN)
+ if (init->posSel == adcPosSelTEMP) {
+ /* ADC should always use low accuracy setting when reading the internal
+ * temperature sensor on platform 2 generation 1 devices. Using high
+ * accuracy setting can introduce a glitch. */
+ BUS_RegBitWrite(&adc->BIASPROG, _ADC_BIASPROG_GPBIASACC_SHIFT, 1);
+ } else {
+ BUS_RegBitWrite(&adc->BIASPROG, _ADC_BIASPROG_GPBIASACC_SHIFT, 0);
+ }
+#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 emlib) */
+#endif /* defined(ADC_COUNT) && (ADC_COUNT > 0) */
diff --git a/efm32/emlib/em_ldma.c b/efm32/emlib/em_ldma.c
new file mode 100644
index 0000000..57bc322
--- /dev/null
+++ b/efm32/emlib/em_ldma.c
@@ -0,0 +1,355 @@
+/***************************************************************************//**
+ * @file em_ldma.c
+ * @brief Direct memory access (LDMA) module peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. 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_core.h"
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup LDMA
+ * @{
+ ******************************************************************************/
+
+#if defined(LDMA_IRQ_HANDLER_TEMPLATE)
+/***************************************************************************//**
+ * @brief
+ * Template for an LDMA IRQ handler.
+ ******************************************************************************/
+void LDMA_IRQHandler(void)
+{
+ uint32_t ch;
+ /* Get all pending and enabled interrupts. */
+ uint32_t pending = LDMA_IntGetEnabled();
+
+ /* Loop here on an LDMA error to enable debugging. */
+ while (pending & LDMA_IF_ERROR) {
+ }
+
+ /* Iterate over all LDMA channels. */
+ for (ch = 0; ch < DMA_CHAN_COUNT; ch++) {
+ uint32_t mask = 0x1 << ch;
+ if (pending & mask) {
+ /* Clear interrupt flag. */
+ LDMA->IFC = mask;
+
+ /* 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
+ * Enable or disable a LDMA channel request.
+ *
+ * @details
+ * Use this function to enable or disable a LDMA channel request. This will
+ * prevent the LDMA from proceeding after its current transaction if disabled.
+ *
+ * @param[in] channel
+ * LDMA channel to enable or disable requests on.
+ *
+ * @param[in] enable
+ * If 'true' request will be enabled. If 'false' request will be disabled.
+ ******************************************************************************/
+void LDMA_EnableChannelRequest(int ch, bool enable)
+{
+ EFM_ASSERT(ch < DMA_CHAN_COUNT);
+
+ BUS_RegBitWrite(&LDMA->REQDIS, ch, !enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Initialize the LDMA controller.
+ *
+ * @details
+ * This function will disable all the LDMA channels and enable the LDMA bus
+ * clock in the CMU. This function will also enable the LDMA IRQ in the NVIC
+ * and set the LDMA IRQ priority to a user configurable priority. The LDMA
+ * interrupt priority is configured using the @ref LDMA_Init_t structure.
+ *
+ * @note
+ * Since this function enables the LDMA IRQ you should always add a custom
+ * LDMA_IRQHandler to the application in order to handle any interrupts
+ * from LDMA.
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure the LDMA.
+ ******************************************************************************/
+void LDMA_Init(const 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,
+ const LDMA_TransferCfg_t *transfer,
+ const LDMA_Descriptor_t *descriptor)
+{
+ uint32_t tmp;
+ CORE_DECLARE_IRQ_STATE;
+ 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. */
+ CORE_ENTER_ATOMIC();
+
+ /* 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; /* Start transfer by loading descriptor. */
+
+ /* Critical region end. */
+ CORE_EXIT_ATOMIC();
+}
+
+/***************************************************************************//**
+ * @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);
+
+ CORE_ATOMIC_SECTION(
+ LDMA->IEN &= ~chMask;
+ BUS_RegMaskedClear(&LDMA->CHEN, chMask);
+ )
+}
+
+/***************************************************************************//**
+ * @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);
+
+ CORE_ATOMIC_SECTION(
+ if (((LDMA->CHEN & chMask) == 0)
+ && ((LDMA->CHDONE & chMask) == chMask)) {
+ retVal = true;
+ }
+ )
+ 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);
+
+ CORE_ATOMIC_SECTION(
+ iflag = LDMA->IF;
+ done = LDMA->CHDONE;
+ remaining = LDMA->CH[ch].CTRL;
+ )
+
+ 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 emlib) */
+#endif /* defined( LDMA_PRESENT ) && ( LDMA_COUNT == 1 ) */
diff --git a/efm32/inc/crypto-config.h b/efm32/inc/crypto-config.h
index d5bf24a..315d36b 100644
--- a/efm32/inc/crypto-config.h
+++ b/efm32/inc/crypto-config.h
@@ -253,7 +253,7 @@
#define MBEDTLS_ECP_RANDOMIZE_JAC_ALT
#define MBEDTLS_ECP_DEVICE_ADD_MIXED_ALT
-
+//#define MBEDTLS_ENTROPY_ALT
//#define MBEDTLS_MPI_MUL_MPI_ALT // doesnt seem to be implemented
//#define MBEDTLS_MPI_MUL_INT_ALT // makes no difference or slightly slower
diff --git a/efm32/src/InitDevice.c b/efm32/src/InitDevice.c
index def79ae..7a921cb 100644
--- a/efm32/src/InitDevice.c
+++ b/efm32/src/InitDevice.c
@@ -19,6 +19,7 @@
#include "em_device.h"
#include "em_chip.h"
#include "em_assert.h"
+#include "em_adc.h"
#include "em_cryotimer.h"
#include "em_crypto.h"
#include "em_gpio.h"
@@ -35,6 +36,7 @@ extern void enter_DefaultMode_from_RESET(void) {
EMU_enter_DefaultMode_from_RESET();
CMU_enter_DefaultMode_from_RESET();
+ ADC0_enter_DefaultMode_from_RESET();
USART0_enter_DefaultMode_from_RESET();
USART1_enter_DefaultMode_from_RESET();
LDMA_enter_DefaultMode_from_RESET();
@@ -127,6 +129,9 @@ extern void CMU_enter_DefaultMode_from_RESET(void) {
/* Enable clock for HF peripherals */
CMU_ClockEnable(cmuClock_HFPER, true);
+ /* Enable clock for ADC0 */
+ CMU_ClockEnable(cmuClock_ADC0, true);
+
/* Enable clock for CRYOTIMER */
CMU_ClockEnable(cmuClock_CRYOTIMER, true);
@@ -171,6 +176,16 @@ extern void CMU_enter_DefaultMode_from_RESET(void) {
extern void ADC0_enter_DefaultMode_from_RESET(void) {
// $[ADC0_Init]
+ ADC_Init_TypeDef ADC0_init = ADC_INIT_DEFAULT;
+
+ ADC0_init.ovsRateSel = adcOvsRateSel2;
+ ADC0_init.warmUpMode = adcWarmupNormal;
+ ADC0_init.timebase = ADC_TimebaseCalc(0);
+ ADC0_init.prescale = ADC_PrescaleCalc(7000000, 0);
+ ADC0_init.tailgate = 0;
+ ADC0_init.em2ClockConfig = adcEm2Disabled;
+
+ ADC_Init(ADC0, &ADC0_init);
// [ADC0_Init]$
// $[ADC0_InputConfiguration]
diff --git a/efm32/src/crypto.c b/efm32/src/crypto.c
index f21c876..9c002fa 100644
--- a/efm32/src/crypto.c
+++ b/efm32/src/crypto.c
@@ -8,12 +8,14 @@
#include "util.h"
#include "crypto.h"
+#include "em_adc.h"
#include "sha256.h"
#include "uECC.h"
#include "aes.h"
#include "ctap.h"
+#include "log.h"
#include MBEDTLS_CONFIG_FILE
#include "sha256_alt.h"
@@ -26,8 +28,8 @@ const uint8_t attestation_key[];
const uint16_t attestation_key_size;
-static SHA256_CTX sha256_ctx;
-mbedtls_sha256_context embed_sha256_ctx;
+static mbedtls_sha256_context embed_sha256_ctx;
+static mbedtls_ctr_drbg_context ctr_drbg;
static const struct uECC_Curve_t * _es256_curve = NULL;
static const uint8_t * _signing_key = NULL;
@@ -132,13 +134,51 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
crypto_sha256_final(hmac);
}
-mbedtls_ctr_drbg_context ctr_drbg;
+
+
+
+uint8_t adc_rng(void)
+{
+ int i;
+ uint8_t random = 0;
+
+ /* Random number generation */
+ for (i=0; i<3; i++)
+ {
+ ADC_Start(ADC0, adcStartSingle);
+ while ((ADC0->IF & ADC_IF_SINGLE) == 0);
+ random |= ((ADC_DataSingleGet(ADC0) & 0x07) << (i * 3));
+ }
+
+ return random;
+}
+
+// Generate @num bytes of random numbers to @dest
+// return 1 if success, error otherwise
+int ctap_generate_rng(uint8_t * dst, size_t num)
+{
+ return mbedtls_ctr_drbg_random(&ctr_drbg,dst,num) == 0;
+}
+
+int adc_entropy_func( void *data, unsigned char *output, size_t len )
+{
+ while(len--)
+ *output++ = adc_rng();
+ return 0;
+}
void crypto_ecc256_init()
{
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
_es256_curve = uECC_secp256r1();
mbedtls_ctr_drbg_init(&ctr_drbg);
+
+ if ( mbedtls_ctr_drbg_seed(&ctr_drbg, adc_entropy_func, NULL,
+ master_secret,32 ) != 0 ) {
+ printf2(TAG_ERR, "mbedtls_ctr_drbg_seed failed\n");
+ exit(1);
+ }
+
}
@@ -507,6 +547,7 @@ void crypto_aes256_encrypt(uint8_t * buf, int length)
}
+
const uint8_t attestation_cert_der[] =
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
diff --git a/efm32/src/device.c b/efm32/src/device.c
index 407fc8d..2c913d5 100644
--- a/efm32/src/device.c
+++ b/efm32/src/device.c
@@ -11,6 +11,8 @@
#include "em_chip.h"
#include "em_gpio.h"
#include "em_usart.h"
+#include "em_adc.h"
+#include "em_cmu.h"
#include "cbor.h"
#include "log.h"
@@ -21,17 +23,6 @@
#define RDY_PIN gpioPortC,10
#define RW_PIN gpioPortD,11
-// Generate @num bytes of random numbers to @dest
-// return 1 if success, error otherwise
-int ctap_generate_rng(uint8_t * dst, size_t num)
-{
- int i;
- for (i = 0; i < num; i++)
- {
- *dst++ = rand();
- }
- return 1;
-}
uint32_t _c1 = 0, _c2 = 0;
uint32_t ctap_atomic_count(int sel)
@@ -185,6 +176,29 @@ void GPIO_ODD_IRQHandler()
}
+void init_adc()
+{
+ /* Enable ADC Clock */
+ CMU_ClockEnable(cmuClock_ADC0, true);
+ ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
+ ADC_InitSingle_TypeDef singleInit = ADC_INITSINGLE_DEFAULT;
+
+ /* Initialize the ADC with the required values */
+ init.timebase = ADC_TimebaseCalc(0);
+ init.prescale = ADC_PrescaleCalc(7000000, 0);
+ ADC_Init(ADC0, &init);
+
+ /* Initialize for single conversion specific to RNG */
+ singleInit.reference = adcRefVEntropy;
+ singleInit.diff = true;
+ singleInit.posSel = adcPosSelVSS;
+ singleInit.negSel = adcNegSelVSS;
+ ADC_InitSingle(ADC0, &singleInit);
+
+ /* Set VINATT to maximum value and clear FIFO */
+ ADC0->SINGLECTRLX |= _ADC_SINGLECTRLX_VINATT_MASK;
+ ADC0->SINGLEFIFOCLEAR = ADC_SINGLEFIFOCLEAR_SINGLEFIFOCLEAR;
+}
void device_init(void)
{
/* Chip errata */
@@ -218,12 +232,18 @@ void device_init(void)
printing_init();
+ init_adc();
+
CborEncoder test;
- uint8_t buf[20];
+ uint8_t buf[64];
cbor_encoder_init(&test, buf, 20, 0);
printf("Device init\r\n");
int i=0;
-
+ for (i = 0; i < sizeof(buf); i++)
+ {
+ buf[i] = adc_rng();
+ }
+ dump_hex(buf,sizeof(buf));
}