/***************************************************************************//** * @file * @brief Low Energy Sensor (LESENSE) Peripheral API ******************************************************************************* * # License * Copyright 2018 Silicon Laboratories Inc. www.silabs.com ******************************************************************************* * * SPDX-License-Identifier: Zlib * * The licensor of this software is Silicon Laboratories Inc. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * ******************************************************************************/ #include "em_lesense.h" #if defined(LESENSE_COUNT) && (LESENSE_COUNT > 0) #include "sl_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 lesense LESENSE - Low Energy Sensor * @brief Low Energy Sensor (LESENSE) Peripheral API * @details * This module contains functions to control the LESENSE peripheral of Silicon * Labs 32-bit MCUs and SoCs. LESENSE is a low-energy sensor interface capable * of autonomously collecting and processing data from multiple sensors even * when in EM2. * @{ ******************************************************************************/ /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) #if defined(_LESENSE_ROUTE_MASK) #define GENERIC_LESENSE_ROUTE LESENSE->ROUTE #else #define GENERIC_LESENSE_ROUTE LESENSE->ROUTEPEN #endif #else #define GENERIC_LESENSE_ROUTE GPIO->LESENSEROUTE.ROUTEEN #endif #if defined(_SILICON_LABS_32B_SERIES_0) /* DACOUT mode only available on channel 0, 1, 2, 3, 12, 13, 14, 15 */ #define DACOUT_SUPPORT 0xF00F #elif defined(_SILICON_LABS_32B_SERIES_1) /* DACOUT mode only available on channel 4, 5, 7, 10, 12, 13 */ #define DACOUT_SUPPORT 0x34B0 #else /* DACOUT mode only available on channel 0, 1, 2 */ #define DACOUT_SUPPORT 0x7 #endif /** @endcond */ /******************************************************************************* ************************** LOCAL FUNCTIONS ******************************** ******************************************************************************/ /******************************************************************************* ************************** GLOBAL FUNCTIONS ******************************* ******************************************************************************/ /***************************************************************************//** * @brief * Initialize the LESENSE module. * * @details * This function configures the main parameters of the LESENSE interface. * See the initialization parameter type definition * (@ref LESENSE_Init_TypeDef) for more details. * * @note * @ref LESENSE_Init() is designed to initialize 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 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 for the LESENSE to work as * intended. * (When configuring pins, one should remember to consider the sequence of * configuration to avoid unintended pulses/glitches on output * pins.) * * @param[in] init * The LESENSE initialization structure. * * @param[in] reqReset * Request to call @ref LESENSE_Reset() first to initialize all * LESENSE registers with default values. ******************************************************************************/ void LESENSE_Init(const LESENSE_Init_TypeDef * init, bool reqReset) { /* Sanity check of initialization values. */ EFM_ASSERT((uint32_t)init->timeCtrl.startDelay < 4U); #if defined(_LESENSE_PERCTRL_DACPRESC_MASK) EFM_ASSERT((uint32_t)init->perCtrl.dacPresc < 32U); #endif #if defined(_SILICON_LABS_32B_SERIES_2) if (LESENSE->EN != 0U) { /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* Reset LESENSE registers if requested. */ if (reqReset) { LESENSE_Reset(); } /* Set sensor start delay for each channel. */ LESENSE_StartDelaySet((uint32_t)init->timeCtrl.startDelay); #if defined(_LESENSE_TIMCTRL_AUXSTARTUP_MASK) /* Configure the AUXHRFCO startup delay. */ LESENSE->TIMCTRL = (LESENSE->TIMCTRL & (~_LESENSE_TIMCTRL_AUXSTARTUP_MASK)) | (init->timeCtrl.delayAuxStartup << _LESENSE_TIMCTRL_AUXSTARTUP_SHIFT); #endif /* LESENSE core control configuration. * Set the 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, and enable/disable LESENSE running in debug * mode. */ #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) 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 #if defined(_LESENSE_CTRL_ACMP0INV_MASK) | ((uint32_t)init->coreCtrl.invACMP0 << _LESENSE_CTRL_ACMP0INV_SHIFT) | ((uint32_t)init->coreCtrl.invACMP1 << _LESENSE_CTRL_ACMP1INV_SHIFT) #endif | ((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); #else EFM_ASSERT(init->coreCtrl.fifoTrigLevel < 16); LESENSE->CFG = (uint32_t)init->coreCtrl.scanConfSel | (uint32_t)init->coreCtrl.wakeupOnDMA | ((uint32_t)init->coreCtrl.fifoTrigLevel << _LESENSE_CFG_RESFIDL_SHIFT) | ((uint32_t)init->coreCtrl.dualSample << _LESENSE_CFG_DUALSAMPLE_SHIFT) | ((uint32_t)init->coreCtrl.storeScanRes << _LESENSE_CFG_STRSCANRES_SHIFT) | ((uint32_t)init->coreCtrl.debugRun << _LESENSE_CFG_DEBUGRUN_SHIFT); /* Set PRS input */ PRS->CONSUMER_LESENSE_START = init->coreCtrl.prsSel; #endif /* Set scan mode in the CTRL/CFG register using the provided function. Don't * start scanning immediately. */ LESENSE_ScanModeSet((LESENSE_ScanMode_TypeDef)init->coreCtrl.scanStart, false); /* The LESENSE peripheral control configuration. * Set DAC0 and DAC1 data source, conversion mode, and output mode. Set the DAC * prescaler and reference. Set ACMP0 and ACMP1 control mode. Set the ACMP and DAC * duty cycle (warm up) mode. */ LESENSE->PERCTRL = 0 #if defined(_LESENSE_PERCTRL_DACCH0EN_MASK) | ((uint32_t)init->perCtrl.dacCh0En << _LESENSE_PERCTRL_DACCH0EN_SHIFT) | ((uint32_t)init->perCtrl.dacCh1En << _LESENSE_PERCTRL_DACCH1EN_SHIFT) #endif | ((uint32_t)init->perCtrl.dacCh0Data << _LESENSE_PERCTRL_DACCH0DATA_SHIFT) #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) | ((uint32_t)init->perCtrl.dacCh1Data << _LESENSE_PERCTRL_DACCH1DATA_SHIFT) #endif #if defined(_LESENSE_PERCTRL_DACCH0CONV_MASK) | ((uint32_t)init->perCtrl.dacCh0ConvMode << _LESENSE_PERCTRL_DACCH0CONV_SHIFT) | ((uint32_t)init->perCtrl.dacCh0OutMode << _LESENSE_PERCTRL_DACCH0OUT_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 #endif #if defined(_LESENSE_PERCTRL_DACCONVTRIG_MASK) | ((uint32_t)init->perCtrl.dacStartupHalf << _LESENSE_PERCTRL_DACSTARTUP_SHIFT) | ((uint32_t)init->perCtrl.dacScan << _LESENSE_PERCTRL_DACCONVTRIG_SHIFT) #endif | ((uint32_t)init->perCtrl.acmp0Mode << _LESENSE_PERCTRL_ACMP0MODE_SHIFT) | ((uint32_t)init->perCtrl.acmp1Mode << _LESENSE_PERCTRL_ACMP1MODE_SHIFT) #if defined(_LESENSE_PERCTRL_ACMP0INV_MASK) | ((uint32_t)init->coreCtrl.invACMP0 << _LESENSE_PERCTRL_ACMP0INV_SHIFT) | ((uint32_t)init->coreCtrl.invACMP1 << _LESENSE_PERCTRL_ACMP1INV_SHIFT) #endif #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) | (uint32_t)init->perCtrl.warmupMode; #else ; #endif /* The LESENSE decoder general control configuration. * Set the decoder input source and 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 the 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 = #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) (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) | #endif ((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); #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) /* Set the initial LESENSE decoder state. */ LESENSE_DecoderStateSet((uint32_t)init->decCtrl.initState); /* The LESENSE bias control configuration. */ LESENSE->BIASCTRL = (uint32_t)init->coreCtrl.biasMode; #endif #if defined(_SILICON_LABS_32B_SERIES_2) LESENSE->EN_SET = LESENSE_EN_EN; #endif } /***************************************************************************//** * @brief * Set the scan frequency for periodic scanning. * * @details * This function only applies to LESENSE if a period counter is 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 scanFreq) { uint32_t tmp; uint32_t pcPresc = 0UL; /* Period counter prescaler. */ uint32_t clkDiv = 1UL; /* Clock divisor value (2^pcPresc). */ uint32_t calcScanFreq; /* Variable for testing the calculation algorithm. */ #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_5) uint32_t pcTop = 255UL; /* Period counter top value (max. 255). */ #else uint32_t pcTop = 63UL; /* Period counter top value (max. 63). */ #endif #if defined(_SILICON_LABS_32B_SERIES_2) if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* If refFreq is set to 0, the currently-configured reference clock is * assumed. */ if (!refFreq) { #if defined(_SILICON_LABS_32B_SERIES_1) \ || defined(_SILICON_LABS_32B_SERIES_0) refFreq = CMU_ClockFreqGet(cmuClock_LESENSE); #elif defined(_SILICON_LABS_32B_SERIES_2) refFreq = CMU_ClockFreqGet(cmuClock_LESENSECLK); #endif } /* The maximum value of pcPresc is 128. AS a result, using the reference frequency less than * 33554431 Hz (33.554431 MHz), the frequency calculation in the while loop * below will not overflow. */ EFM_ASSERT(refFreq < ((uint32_t)UINT32_MAX / 128UL)); /* A sanity check of scan frequency value. */ EFM_ASSERT((scanFreq > 0U) && (scanFreq <= refFreq)); /* Calculate the minimum necessary prescaler value to provide the * biggest possible resolution for setting the scan frequency. * The 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 the pcTop value. */ pcTop = ((uint32_t)refFreq / ((uint32_t)scanFreq * clkDiv)) - 1UL; EFM_ASSERT(pcTop <= (_LESENSE_TIMCTRL_PCTOP_MASK >> _LESENSE_TIMCTRL_PCTOP_SHIFT)); /* 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 the LESENSE_TIMCTRL register. */ LESENSE->TIMCTRL = tmp; /* For testing the calculation algorithm. */ calcScanFreq = ((uint32_t)refFreq / ((uint32_t)(1UL + pcTop) * clkDiv)); #if defined(_SILICON_LABS_32B_SERIES_2) /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif return calcScanFreq; } /***************************************************************************//** * @brief * Set scan mode of the LESENSE channels. * * @details * This function configures how the scan start is 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. * Be aware of the effects of the non-atomic Read-Modify-Write cycle. * * @param[in] scanMode * Select the location to map LESENSE alternate excitation channels. * @li lesenseScanStartPeriodic - A new scan is started each time the period * counter overflows. * @li lesenseScanStartOneShot - A single scan is performed when * LESENSE_ScanStart() is called. * @li lesenseScanStartPRS - A new scan is triggered by pulse on the PRS channel. * * @param[in] start * If true, LESENSE_ScanStart() is immediately issued after configuration. ******************************************************************************/ void LESENSE_ScanModeSet(LESENSE_ScanMode_TypeDef scanMode, bool start) { uint32_t tmp; /* temporary storage of the CTRL register value */ #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) /* Save the CTRL register value to tmp. * Be aware of 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 * (enumeration) to unsigned long (uint32_t). */ tmp |= (uint32_t)scanMode; /* Write the new value to the CTRL register. */ LESENSE->CTRL = tmp; #else if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } /* Save the CTRL register value to tmp. * Be aware of the effects of the non-atomic Read-Modify-Write cycle. */ tmp = LESENSE->CFG & ~(_LESENSE_CFG_SCANMODE_MASK); /* Setting the requested scanMode to the CTRL register. Casting signed int * (enumeration) to unsigned long (uint32_t). */ tmp |= (uint32_t)scanMode; /* Write the new value to the CTRL register. */ LESENSE->CFG = tmp; /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled || start) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif /* Start the sensor scanning if requested. */ if (start) { LESENSE_ScanStart(); } } /***************************************************************************//** * @brief * Set the start delay of the sensor interaction on each channel. * * @details * This function sets the start delay of the 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. * Be aware of the effects of the non-atomic Read-Modify-Write cycle. * * @param[in] startDelay * A number of LFACLK cycles to delay. A valid range: 0-3 (2 bit). ******************************************************************************/ void LESENSE_StartDelaySet(uint8_t startDelay) { uint32_t tmp; /* Temporary storage of the TIMCTRL register value */ #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif /* Sanity check of the startDelay. */ EFM_ASSERT(startDelay < 4U); #if defined(_SILICON_LABS_32B_SERIES_2) if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* Save the TIMCTRL register value to tmp. * Be aware of 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; #if defined(_SILICON_LABS_32B_SERIES_2) /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif } /***************************************************************************//** * @brief * Set the 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. See * documentation for more details. * * @note * If AUXHFRCO is used for excitation timing, LFACLK can't exceed 500 kHz. * LFACLK can't exceed 50 kHz if the ACMP threshold level (ACMPTHRES) is not * equal for all channels. * * @param[in] clk * Select the clock to prescale. * @li lesenseClkHF - set AUXHFRCO clock divisor for HF timer. * @li lesenseClkLF - set LFACLKles clock divisor for LF timer. * * @param[in] clkDiv * The clock divisor value. A valid range depends on the @p clk value. ******************************************************************************/ void LESENSE_ClkDivSet(LESENSE_ChClk_TypeDef clk, LESENSE_ClkPresc_TypeDef clkDiv) { uint32_t tmp; #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* Select the clock to prescale. */ switch (clk) { case lesenseClkHF: /* A sanity check of the clock divisor for the HF clock. */ EFM_ASSERT((uint32_t)clkDiv <= lesenseClkDiv_8); /* Clear the current AUXPRESC settings. */ tmp = LESENSE->TIMCTRL & ~(_LESENSE_TIMCTRL_AUXPRESC_MASK); /* Set the 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 the LESENSE_TIMCTRL register. */ LESENSE->TIMCTRL = tmp; break; default: EFM_ASSERT(0); break; } #if defined(_SILICON_LABS_32B_SERIES_2) /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif } /***************************************************************************//** * @brief * Configure all (16) LESENSE sensor channels. * * @details * This function configures all sensor channels of the LESENSE interface. * See 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 for LESENSE to work as intended. * (When configuring pins, consider the sequence of the * configuration to avoid unintended pulses/glitches on output * pins.) * * @param[in] confChAll * A configuration structure for all (16) LESENSE sensor channels. ******************************************************************************/ void LESENSE_ChannelAllConfig(const LESENSE_ChAll_TypeDef * confChAll) { uint32_t i; /* Iterate through all 16 channels. */ for (i = 0U; i < LESENSE_NUM_CHANNELS; ++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. * See 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, be aware * of these effects and the right timing to call this function. * Parameter @p useAltEx must be true in the channel configuration to * use alternate excitation pins. * * @param[in] confCh * A configuration structure for a single LESENSE sensor channel. * * @param[in] chIdx * A channel index to configure (0-15). ******************************************************************************/ void LESENSE_ChannelConfig(const LESENSE_ChDesc_TypeDef * confCh, uint32_t chIdx) { uint32_t tmp; /* A service variable. */ #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* A sanity check of configuration parameters. */ EFM_ASSERT(chIdx < LESENSE_NUM_CHANNELS); EFM_ASSERT(confCh->exTime <= (_LESENSE_CH_TIMING_EXTIME_MASK >> _LESENSE_CH_TIMING_EXTIME_SHIFT)); EFM_ASSERT(confCh->measDelay <= (_LESENSE_CH_TIMING_MEASUREDLY_MASK >> _LESENSE_CH_TIMING_MEASUREDLY_SHIFT)); #if defined(_LESENSE_CH_INTERACT_OFFSET_MASK) EFM_ASSERT(confCh->offset <= (_LESENSE_CH_INTERACT_OFFSET_MASK >> _LESENSE_CH_INTERACT_OFFSET_SHIFT)); #endif #if defined(_SILICON_LABS_32B_SERIES_0) // Sample delay on other devices are 8 bits which fits perfectly in uint8_t. EFM_ASSERT(confCh->sampleDelay <= (_LESENSE_CH_TIMING_SAMPLEDLY_MASK >> _LESENSE_CH_TIMING_SAMPLEDLY_SHIFT)); #endif /* Not a complete assert, as the maximum value of acmpThres depends on other * configuration parameters. Check the parameter description of acmpThres * for more details. */ EFM_ASSERT(confCh->acmpThres < 4096U); if (confCh->chPinExMode == lesenseChPinExDACOut) { EFM_ASSERT((0x1 << chIdx) & DACOUT_SUPPORT); } #if defined(_LESENSE_IDLECONF_CH0_DACCH0) 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)))); #endif /* Configure the chIdx setup in LESENSE idle phase. * Read-modify-write to support reconfiguration during the LESENSE * operation. */ tmp = (LESENSE->IDLECONF & ~((uint32_t)0x3UL << (chIdx * 2UL))); tmp |= ((uint32_t)confCh->chPinIdleMode << (chIdx * 2UL)); LESENSE->IDLECONF = tmp; /* A channel-specific timing configuration on scan channel chIdx. * Set excitation time, sampling delay, and measurement delay. */ LESENSE_ChannelTimingSet(chIdx, confCh->exTime, confCh->sampleDelay, confCh->measDelay); /* A 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) #if defined(_LESENSE_CH_INTERACT_OFFSET_MASK) | ((uint32_t)confCh->offset << _LESENSE_CH_INTERACT_OFFSET_SHIFT) #endif ; /* Configure the 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. */ #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) 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) #if defined(_LESENSE_CH_EVAL_MODE_MASK) | ((uint32_t)confCh->evalMode << _LESENSE_CH_EVAL_MODE_SHIFT) #endif ; #else LESENSE->CH[chIdx].EVALCFG = (uint32_t)confCh->compMode | ((uint32_t)confCh->shiftRes << _LESENSE_CH_EVALCFG_DECODE_SHIFT) | ((uint32_t)confCh->storeCntRes << _LESENSE_CH_EVALCFG_STRSAMPLE_SHIFT) | ((uint32_t)confCh->invRes << _LESENSE_CH_EVALCFG_SCANRESINV_SHIFT) | ((uint32_t)confCh->evalMode << _LESENSE_CH_EVALCFG_MODE_SHIFT) ; #endif /* Configure the analog comparator (ACMP) threshold and decision threshold for * the counter separately with the function provided for that. */ LESENSE_ChannelThresSet(chIdx, confCh->acmpThres, confCh->cntThres); /* Enable/disable interrupts on channel */ BUS_RegBitWrite(&LESENSE->IEN, chIdx, confCh->enaInt); /* Enable/disable CHchIdx pin. */ BUS_RegBitWrite(&GENERIC_LESENSE_ROUTE, chIdx, confCh->enaPin); /* Enable/disable scan channel chIdx. */ BUS_RegBitWrite(&LESENSE->CHEN, chIdx, confCh->enaScanCh); #if defined(_SILICON_LABS_32B_SERIES_2) /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif } /***************************************************************************//** * @brief * Configure the LESENSE alternate excitation modes. * * @details * This function configures the alternate excitation channels of the LESENSE * interface. See the configuration parameter type definition * (LESENSE_ConfAltEx_TypeDef) for more details. * * @note * The @p useAltEx parameter must be true in the channel configuration structure * (LESENSE_ChDesc_TypeDef) to use alternate excitation pins on the * channel. * * @param[in] confAltEx * A configuration structure for LESENSE alternate excitation pins. ******************************************************************************/ void LESENSE_AltExConfig(const LESENSE_ConfAltEx_TypeDef * confAltEx) { uint32_t i; #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) uint32_t tmp; /* Configure the alternate excitation mapping. * Atomic read-modify-write using BUS_RegBitWrite function to * support reconfiguration during the 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 the alternate excitation pin i. * Atomic read-modify-write using BUS_RegBitWrite function to * support reconfiguration during the LESENSE operation. */ BUS_RegBitWrite(&GENERIC_LESENSE_ROUTE, (16UL + i), confAltEx->AltEx[i].enablePin); /* Set up the idle phase state of the alternate excitation pin i. * Read-modify-write to support reconfiguration during the 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; #if defined(_LESENSE_CTRL_ALTEXMAP_ACMP) case lesenseAltExMapACMP: #else case lesenseAltExMapCH: #endif /* Iterate through all 16 alternate excitation channels. */ for (i = 0U; i < 16U; ++i) { /* Enable/disable the alternate ACMP excitation channel pin i. */ /* An atomic read-modify-write using BUS_RegBitWrite function to * support reconfiguration during the LESENSE operation. */ BUS_RegBitWrite(&GENERIC_LESENSE_ROUTE, i, confAltEx->AltEx[i].enablePin); } break; default: /* An illegal value. */ EFM_ASSERT(0); break; } #else /* Iterate through all 16 alternate excitation channels. */ for (i = 0U; i < 16U; ++i) { /* Enable/disable the alternate excitation channel pin i. */ /* An atomic read-modify-write using BUS_RegBitWrite function to * support reconfiguration during the LESENSE operation. */ BUS_RegBitWrite(&GENERIC_LESENSE_ROUTE, i, confAltEx->AltEx[i].enablePin); } #endif } /***************************************************************************//** * @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 it. * * @note * Users can enable/disable scan channels and the channel pin with * the LESENSE_ChannelConfig() function, but only with a significant overhead. * This simple function controls these parameters * after the channel has been configured. * * @param[in] chIdx * An identifier of the scan channel. A 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 chIdx, bool enaScanCh, bool enaPin) { /* Enable/disable the assigned pin of scan channel chIdx. * Note: BUS_RegBitWrite() function is used for setting/clearing single * bit peripheral register bit fields. Read the function description in * em_bus.h for more details. */ BUS_RegBitWrite(&GENERIC_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 the * LESENSE_ChannelAllConfig() function, but only with a significant overhead. * This simple function controls 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. */ GENERIC_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 with the * LESENSE_ChannelConfig() function, but only with a significant overhead. * This simple function controls these parameters * after the channel has been configured. * * @param[in] chIdx * An identifier of the scan channel. A valid range is 0-15. * * @param[in] exTime * An excitation time on chIdx. The excitation will last exTime+1 excitation clock * cycles. A valid range is 0-63 (6 bits). * * @param[in] sampleDelay * Sample delay on chIdx. Sampling will occur after sampleDelay+1 sample clock * cycles. A valid range is 0-127 (7 bits). * * @param[in] measDelay * A measure delay on chIdx. Sensor measuring is delayed for measDelay+1 * excitation clock cycles. A valid range is 0-127 (7 bits). ******************************************************************************/ void LESENSE_ChannelTimingSet(uint8_t chIdx, uint8_t exTime, uint8_t sampleDelay, uint16_t measDelay) { #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif /* A sanity check of parameters. */ EFM_ASSERT(exTime <= (_LESENSE_CH_TIMING_EXTIME_MASK >> _LESENSE_CH_TIMING_EXTIME_SHIFT)); EFM_ASSERT(measDelay <= (_LESENSE_CH_TIMING_MEASUREDLY_MASK >> _LESENSE_CH_TIMING_MEASUREDLY_SHIFT)); #if defined(_SILICON_LABS_32B_SERIES_0) // A sample delay on other devices is 8 bits which fits perfectly in uint8_t. EFM_ASSERT(sampleDelay <= (_LESENSE_CH_TIMING_SAMPLEDLY_MASK >> _LESENSE_CH_TIMING_SAMPLEDLY_SHIFT)); #endif #if defined(_SILICON_LABS_32B_SERIES_2) if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* A channel-specific timing configuration on the scan channel chIdx. * Setting excitation time, sampling delay, and 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); #if defined(_SILICON_LABS_32B_SERIES_2) /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif } /***************************************************************************//** * @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 with the * LESENSE_ChannelConfig() function, but only with a significant overhead. * This simple function serves controls these parameters * after the channel has been configured. * * @param[in] chIdx * An identifier of the scan channel. A valid range is 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 * A decision threshold for counter comparison. * A valid range is 0-65535 (16 bits). ******************************************************************************/ void LESENSE_ChannelThresSet(uint8_t chIdx, uint16_t acmpThres, uint16_t cntThres) { uint32_t tmp; /* A temporary storage */ #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif /* A sanity check for acmpThres only, cntThres is a 16 bit value. */ EFM_ASSERT(acmpThres < 4096U); /* A sanity check for the LESENSE channel ID. */ EFM_ASSERT(chIdx < LESENSE_NUM_CHANNELS); #if defined(_SILICON_LABS_32B_SERIES_2) if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif /* Save the INTERACT register value of channel chIdx to tmp. * Be aware of the effects of the non-atomic Read-Modify-Write cycle. */ tmp = LESENSE->CH[chIdx].INTERACT & ~(0xFFF); /* Set the ACMP threshold value to the INTERACT register of the channel chIdx. */ tmp |= (uint32_t)acmpThres; /* Write the new value to the INTERACT register. */ LESENSE->CH[chIdx].INTERACT = tmp; #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) /* Save the EVAL register value of channel chIdx to tmp. * Be aware of 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 the channel chIdx. */ tmp |= (uint32_t)cntThres << _LESENSE_CH_EVAL_COMPTHRES_SHIFT; /* Write the new value to the EVAL register. */ LESENSE->CH[chIdx].EVAL = tmp; #else LESENSE->EN_SET = LESENSE_EN_EN; LESENSE->CH[chIdx].EVALTHRES = (uint32_t)cntThres << _LESENSE_CH_EVALTHRES_EVALTHRES_SHIFT; while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } if (!enabled) { /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif } #if defined(_LESENSE_CH_EVAL_MODE_MASK) || defined(_LESENSE_CH_EVALCFG_MODE_MASK) /***************************************************************************//** * @brief * Configure a Sliding Window evaluation mode for a specific channel. * * @details * This function will configure the evaluation mode, the initial * sensor measurement (COMPTHRES), and the window size. For other channel-related * configuration, see the @ref LESENSE_ChannelConfig() function. * * @warning * Note that the step size and window size configuration are global to all * LESENSE channels and use the same register field in the hardware. This * means that any windowSize configuration passed to this function will * apply for all channels and override all other stepSize/windowSize * configurations. * * @param[in] chIdx * An identifier of the scan channel. A valid range is 0-15. * * @param[in] windowSize * A window size to be used on all channels. * * @param[in] initValue * The initial sensor value for the channel. ******************************************************************************/ void LESENSE_ChannelSlidingWindow(uint8_t chIdx, uint32_t windowSize, uint32_t initValue) { LESENSE_CH_TypeDef * ch = &LESENSE->CH[chIdx]; #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif LESENSE_WindowSizeSet(windowSize); #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) ch->EVAL = (ch->EVAL & ~(_LESENSE_CH_EVAL_COMPTHRES_MASK | _LESENSE_CH_EVAL_MODE_MASK)) | (initValue << _LESENSE_CH_EVAL_COMPTHRES_SHIFT) | LESENSE_CH_EVAL_MODE_SLIDINGWIN; #else if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } /* Set Channel Sliding Window config */ ch->EVALCFG = (ch->EVALCFG & ~_LESENSE_CH_EVALCFG_MODE_MASK) | LESENSE_CH_EVALCFG_MODE_SLIDINGWIN; LESENSE->EN_SET = LESENSE_EN_EN; ch->EVALTHRES = (ch->EVALTHRES & ~_LESENSE_CH_EVALTHRES_EVALTHRES_MASK) | (initValue << _LESENSE_CH_EVALTHRES_EVALTHRES_SHIFT); while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } if (!enabled) { /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif } /***************************************************************************//** * @brief * Configure the step detection evaluation mode for a specific channel. * * @details * This function will configure the evaluation mode, the initial * sensor measurement (COMPTHRES) and the window size. For other channel-related * configuration, see the @ref LESENSE_ChannelConfig() function. * * @warning * Note that the step size and window size configuration are global to all * LESENSE channels and use the same register field in the hardware. This * means that any stepSize configuration passed to this function will * apply for all channels and override all other stepSize/windowSize * configurations. * * @param[in] chIdx * An identifier of the scan channel. A valid range is 0-15. * * @param[in] stepSize * A step size to be used on all channels. * * @param[in] initValue * The initial sensor value for the channel. ******************************************************************************/ void LESENSE_ChannelStepDetection(uint8_t chIdx, uint32_t stepSize, uint32_t initValue) { LESENSE_CH_TypeDef * ch = &LESENSE->CH[chIdx]; #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; #endif LESENSE_StepSizeSet(stepSize); #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) ch->EVAL = (ch->EVAL & ~(_LESENSE_CH_EVAL_COMPTHRES_MASK | _LESENSE_CH_EVAL_MODE_MASK)) | (initValue << _LESENSE_CH_EVAL_COMPTHRES_SHIFT) | LESENSE_CH_EVAL_MODE_STEPDET; #else if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } ch->EVALCFG = (ch->EVALCFG & ~_LESENSE_CH_EVALCFG_MODE_MASK) | LESENSE_CH_EVALCFG_MODE_STEPDET; LESENSE->EN_SET = LESENSE_EN_EN; ch->EVALTHRES = (ch->EVALTHRES & ~_LESENSE_CH_EVALTHRES_EVALTHRES_MASK) | (initValue << _LESENSE_CH_EVALTHRES_EVALTHRES_SHIFT); while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } if (!enabled) { /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif } /***************************************************************************//** * @brief * Set the window size for all LESENSE channels. * * @details * The window size is used by all channels that are configured as * @ref lesenseEvalModeSlidingWindow. * * @warning * The window size configuration is using the same register field as the * step detection size. As a result, the window size configuration will have an * effect on channels configured with the @ref lesenseEvalModeStepDetection * evaluation mode as well. * * @param[in] windowSize * The window size to use for all channels. ******************************************************************************/ void LESENSE_WindowSizeSet(uint32_t windowSize) { #if defined(_SILICON_LABS_32B_SERIES_2) bool enabled = false; if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } #endif LESENSE->EVALCTRL = (LESENSE->EVALCTRL & ~_LESENSE_EVALCTRL_WINSIZE_MASK) | windowSize; #if defined(_SILICON_LABS_32B_SERIES_2) /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif } /***************************************************************************//** * @brief * Set the step size for all LESENSE channels. * * @details * The step size is configured using the same register field as used to * configure window size. Therefore, calling this function will overwrite any * previously configured window size as done by the * @ref LESENSE_WindowSizeSet() function. * * @param[in] stepSize * The step size to use for all channels. ******************************************************************************/ void LESENSE_StepSizeSet(uint32_t stepSize) { LESENSE_WindowSizeSet(stepSize); } #endif /***************************************************************************//** * @brief * Configure all LESENSE decoder states. * * @details * This function configures all the decoder states of the LESENSE interface. * See the configuration parameter type definition * (LESENSE_DecStAll_TypeDef) for more details. * * @param[in] confDecStAll * A configuration structure for all (16 or 32) LESENSE decoder states. * * @note * Decoder states can be configured individually using * LESENSE_DecoderStateConfig() function. * Starting from Series 2 Config 3 (xG23 and higher), this function configures * a transition ARC instead of a decoder state. ******************************************************************************/ void LESENSE_DecoderStateAllConfig(const LESENSE_DecStAll_TypeDef * confDecStAll) { uint32_t i; #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) /* Iterate through all 16 or 32 decoder states. */ for (i = 0U; i < LESENSE_NUM_DECODER_STATES; ++i) { /* A configure decoder for state i. */ LESENSE_DecoderStateConfig(&confDecStAll->St[i], i); } #else /* Iterate through all 64 transition arcs. */ for (i = 0U; i < LESENSE_NUM_ARCS; ++i) { /* A configure decoder for arc i. */ LESENSE_DecoderStateConfig(&confDecStAll->St[i], i); } #endif } /***************************************************************************//** * @brief * Configure a single LESENSE decoder state. * * @details * This function configures a single decoder state of the LESENSE interface. * See the configuration parameter type definition * (LESENSE_DecStDesc_TypeDef) for more details. * * @param[in] confDecSt * A configuration structure for a single LESENSE decoder state. * * @param[in] decSt * A decoder state index to configure (0-15) or (0-31) depending on the device. * * @note * Starting from Series 2 Config 3 (xG23 and higher), this function configures * a transition ARC instead of a decoder state. ******************************************************************************/ void LESENSE_DecoderStateConfig(const LESENSE_DecStDesc_TypeDef * confDecSt, uint32_t decSt) { #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) /* Sanity check of configuration parameters */ EFM_ASSERT(decSt < LESENSE_NUM_DECODER_STATES); EFM_ASSERT((uint32_t)confDecSt->confA.compMask < 16U); EFM_ASSERT((uint32_t)confDecSt->confA.compVal < 16U); EFM_ASSERT((uint32_t)confDecSt->confA.nextState < LESENSE_NUM_DECODER_STATES); EFM_ASSERT((uint32_t)confDecSt->confB.compMask < 16U); EFM_ASSERT((uint32_t)confDecSt->confB.compVal < 16U); EFM_ASSERT((uint32_t)confDecSt->confB.nextState < LESENSE_NUM_DECODER_STATES); /* Configure the state descriptor A (LESENSE_STi_TCONFA) for the 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 the 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); #else bool enabled = false; /* Sanity check of configuration parameters */ EFM_ASSERT(decSt < LESENSE_NUM_ARCS); EFM_ASSERT((uint32_t)confDecSt->compMask < 16U); EFM_ASSERT((uint32_t)confDecSt->compVal < 16U); EFM_ASSERT((uint32_t)confDecSt->curState < LESENSE_NUM_DECODER_STATES); EFM_ASSERT((uint32_t)confDecSt->nextState < LESENSE_NUM_DECODER_STATES); if (LESENSE->EN != 0U) { enabled = true; /* Wait for synchronization before writing to EN register */ while (LESENSE->SYNCBUSY) { /* Wait for all synchronizations to finish */ } /* Disable LESENSE module */ LESENSE->EN_CLR = LESENSE_EN_EN; while (LESENSE->EN & _LESENSE_EN_DISABLING_MASK) { /* Wait for disabling to finish */ } } LESENSE->ST[decSt].ARC = (uint32_t)confDecSt->prsAct | ((uint32_t)confDecSt->compMask << _LESENSE_ST_ARC_SMASK_SHIFT) | ((uint32_t)confDecSt->compVal << _LESENSE_ST_ARC_SCOMP_SHIFT) | ((uint32_t)confDecSt->curState << _LESENSE_ST_ARC_CURSTATE_SHIFT) | ((uint32_t)confDecSt->nextState << _LESENSE_ST_ARC_NEXTSTATE_SHIFT) | ((uint32_t)confDecSt->setInt << _LESENSE_ST_ARC_SETIF_SHIFT); /* Re-Enable LESENSE module if it was enabled in the first place. */ if (enabled == true) { LESENSE->EN_SET = LESENSE_EN_EN; } #endif } #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) /***************************************************************************//** * @brief * Set the 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 * A decoder state to set as the current state. A valid range is 0-15 or 0-31, * depending on the device. ******************************************************************************/ void LESENSE_DecoderStateSet(uint32_t decSt) { EFM_ASSERT(decSt <= _LESENSE_DECSTATE_DECSTATE_MASK); LESENSE->DECSTATE = decSt & _LESENSE_DECSTATE_DECSTATE_MASK; } #endif /***************************************************************************//** * @brief * Get the current state of the LESENSE decoder. * * @return * This function returns the value of the LESENSE_DECSTATE register that * represents the current state of the LESENSE decoder. ******************************************************************************/ uint32_t LESENSE_DecoderStateGet(void) { return LESENSE->DECSTATE & _LESENSE_DECSTATE_DECSTATE_MASK; } #if defined(_LESENSE_PRSCTRL_MASK) /***************************************************************************//** * @brief * Enable or disable the PRS output from the LESENSE decoder. * * @param[in] enable * Enable/disable the PRS output from the LESENSE decoder. True to enable and * false to disable. * * @param[in] decMask * A decoder state compare value mask. * * @param[in] decVal * A decoder state comparison value. ******************************************************************************/ void LESENSE_DecoderPrsOut(bool enable, uint32_t decMask, uint32_t decVal) { LESENSE->PRSCTRL = (enable << _LESENSE_PRSCTRL_DECCMPEN_SHIFT) | (decMask << _LESENSE_PRSCTRL_DECCMPMASK_SHIFT) | (decVal << _LESENSE_PRSCTRL_DECCMPVAL_SHIFT); } #endif /***************************************************************************//** * @brief * Start scanning 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 expect some delay. The user may implement * a separate function to write multiple command bits in the CMD register * in one single operation 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 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 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 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 the 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 expect some delay. The user may implement * a separate function to write multiple command bits in the CMD register * in one single operation 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 the 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 the 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 expect some delay. The user may implement * a separate function to write multiple command bits in the CMD register * in one single operation 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) { } #if defined(_SILICON_LABS_32B_SERIES_2) while (LESENSE->STATUS & _LESENSE_STATUS_FLUSHING_MASK) ; #endif } /***************************************************************************//** * @brief * Reset the LESENSE module. * * @details * Use this function to reset LESENSE registers. * * @note * Resetting LESENSE registers is required in each reset or power-on cycle * 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(). * Starting from Series 2 Config 3 (xG23 and higher), this function leaves * LESENSE in the disabled state. ******************************************************************************/ void LESENSE_Reset(void) { #if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) 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 the sensor scan and clear the 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; #if defined(_LESENSE_EVALCTRL_MASK) LESENSE->EVALCTRL = _LESENSE_EVALCTRL_RESETVALUE; LESENSE->PRSCTRL = _LESENSE_PRSCTRL_RESETVALUE; #endif LESENSE->CHEN = _LESENSE_CHEN_RESETVALUE; LESENSE->IDLECONF = _LESENSE_IDLECONF_RESETVALUE; LESENSE->ALTEXCONF = _LESENSE_ALTEXCONF_RESETVALUE; /* Reset SENSORSTATE register */ LESENSE->SENSORSTATE = _LESENSE_SENSORSTATE_RESETVALUE; /* Disable LESENSE to control GPIO pins. */ #if defined(_LESENSE_ROUTE_MASK) LESENSE->ROUTE = _LESENSE_ROUTE_RESETVALUE; #else LESENSE->ROUTEPEN = _LESENSE_ROUTEPEN_RESETVALUE; #endif /* Reset all channel configuration registers. */ for (i = 0U; i < LESENSE_NUM_CHANNELS; ++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 < LESENSE_NUM_DECODER_STATES; ++i) { LESENSE->ST[i].TCONFA = _LESENSE_ST_TCONFA_RESETVALUE; LESENSE->ST[i].TCONFB = _LESENSE_ST_TCONFB_RESETVALUE; } /* Wait for the write operation to complete before returning. */ while (LESENSE->SYNCBUSY & LESENSE_SYNCBUSY_CMD) { } #else LESENSE->SWRST_SET = LESENSE_SWRST_SWRST; while (LESENSE->SWRST & _LESENSE_SWRST_RESETTING_MASK) { } #endif } /** @} (end addtogroup lesense) */ #endif /* defined(LESENSE_COUNT) && (LESENSE_COUNT > 0) */