EFM32 Happy Gecko Software Documentation  efm32hg-doc-4.2.1
caplesense.c
Go to the documentation of this file.
1 /**************************************************************************/
19 /* EM header files */
20 #include "em_device.h"
21 
22 /* Drivers */
23 #include "caplesense.h"
24 #include "em_emu.h"
25 #include "em_acmp.h"
26 #include "em_assert.h"
27 #include "em_cmu.h"
28 #include "em_emu.h"
29 #include "em_gpio.h"
30 #include "em_int.h"
31 #include "em_lesense.h"
32 
33 /* Capacitive sense configuration */
34 #include "caplesenseconfig.h"
35 
36 /**************************************************************************/
40 static volatile uint32_t channelValues[LESENSE_CHANNELS] =
41 {
42 /* Ch0, Ch1, Ch2, Ch3, Ch4, Ch5, Ch6, Ch7 */
43  0, 0, 0, 0, 0, 0, 0, 0,
44 /* Ch8, Ch9, Ch10, Ch11, Ch12, Ch13, Ch14, Ch15 */
45  0, 0, 0, 0, 0, 0, 0, 0
46 };
47 
48 
49 /**************************************************************************/
53 static volatile uint32_t channelMaxValues[LESENSE_CHANNELS] =
54 {
55 /* Ch0, Ch1, Ch2, Ch3, Ch4, Ch5, Ch6, Ch7 */
56  1, 1, 1, 1, 1, 1, 1, 1,
57 /* Ch8, Ch9, Ch11, Ch11, Ch12, Ch13, Ch14, Ch15 */
58  1, 1, 1, 1, 1, 1, 1, 1
59 };
60 
61 /**************************************************************************/
65 static const bool channelsInUse[LESENSE_CHANNELS] = LESENSE_CAPSENSE_CH_IN_USE;
66 
67 /**************************************************************************/
70 void CAPLESENSE_setupCMU(void);
71 void CAPLESENSE_setupGPIO(void);
72 void CAPLESENSE_setupACMP(void);
73 
74 
75 /**************************************************************************/
79 static void (*lesenseScanCb)(void);
81 static void (*lesenseChCb)(void);
82 
84 static volatile uint8_t currentChannel;
85 
86 
87 
88 /**************************************************************************/
92 {
93  /* Ensure core frequency has been updated */
95 
96  /* Select clock source for HF clock. */
98  /* Select clock source for LFA clock. */
100  /* Select clock source for LFB clock. */
102 
103  /* Enable HF peripheral clock. */
105  /* Enable clock for GPIO. */
107  /* Enable clock for ACMP0. */
109  /* Enable clock for ACMP1. */
110  CMU_ClockEnable(cmuClock_ACMP1, 1);
111  /* Enable CORELE clock. */
113  /* Enable clock for LESENSE. */
114  CMU_ClockEnable(cmuClock_LESENSE, 1);
115 
116  /* Enable clock divider for LESENSE. */
117  CMU_ClockDivSet(cmuClock_LESENSE, cmuClkDiv_1);
118 }
119 
120 
121 /**************************************************************************/
125 {
126  /* Configure the drive strength of the ports for the light sensor. */
127  GPIO_DriveModeSet(CAPLESENSE_SLIDER_PORT0, gpioDriveModeStandard);
128 
129  /* Initialize the 4 GPIO pins of the touch slider for using them as LESENSE
130  * scan channels for capacitive sensing. */
131  GPIO_PinModeSet(CAPLESENSE_SLIDER_PORT0, CAPLESENSE_SLIDER0_PIN, gpioModeDisabled, 0);
132  GPIO_PinModeSet(CAPLESENSE_SLIDER_PORT0, CAPLESENSE_SLIDER1_PIN, gpioModeDisabled, 0);
133  GPIO_PinModeSet(CAPLESENSE_SLIDER_PORT0, CAPLESENSE_SLIDER2_PIN, gpioModeDisabled, 0);
134  GPIO_PinModeSet(CAPLESENSE_SLIDER_PORT0, CAPLESENSE_SLIDER3_PIN, gpioModeDisabled, 0);
135 }
136 
137 
138 /**************************************************************************/
142 {
143  /* ACMP capsense configuration constant table. */
144  static const ACMP_CapsenseInit_TypeDef initACMP =
145  {
146  .fullBias = false,
147  .halfBias = false,
148  .biasProg = 0x7,
149  .warmTime = acmpWarmTime512,
150  .hysteresisLevel = acmpHysteresisLevel7,
151  .resistor = acmpResistor0,
152  .lowPowerReferenceEnabled = false,
153  .vddLevel = 0x3D,
154  .enable = false
155  };
156 
157 
158  /* Configure ACMP locations, ACMP output to pin disabled. */
159  ACMP_GPIOSetup(ACMP0, 0, false, false);
160  ACMP_GPIOSetup(ACMP1, 0, false, false);
161 
162  /* Initialize ACMPs in capacitive sense mode. */
163  ACMP_CapsenseInit(ACMP0, &initACMP);
164  ACMP_CapsenseInit(ACMP1, &initACMP);
165 
166  /* Don't enable ACMP, LESENSE controls it! */
167 }
168 
169 
170 /**************************************************************************/
174 void CAPLESENSE_setupLESENSE(bool sleep)
175 {
176  uint8_t i;
177  static bool init = true;
178 
179  /* Array for storing the calibration values. */
180  static uint16_t capsenseCalibrateVals[4];
181 
182  /* LESENSE channel configuration constant table in sense mode. */
183  static const LESENSE_ChAll_TypeDef initChsSense = LESENSE_CAPSENSE_SCAN_CONF_SENSE;
184  /* LESENSE channel configuration constant table in sleep mode. */
185  static const LESENSE_ChAll_TypeDef initChsSleep = LESENSE_CAPSENSE_SCAN_CONF_SLEEP;
186  /* LESENSE central configuration constant table. */
187  static const LESENSE_Init_TypeDef initLESENSE =
188  {
189  .coreCtrl =
190  {
191  .scanStart = lesenseScanStartPeriodic,
192  .prsSel = lesensePRSCh0,
193  .scanConfSel = lesenseScanConfDirMap,
194  .invACMP0 = false,
195  .invACMP1 = false,
196  .dualSample = false,
197  .storeScanRes = false,
198  .bufOverWr = true,
199  .bufTrigLevel = lesenseBufTrigHalf,
200  .wakeupOnDMA = lesenseDMAWakeUpDisable,
201  .biasMode = lesenseBiasModeDutyCycle,
202  .debugRun = false
203  },
204 
205  .timeCtrl =
206  {
207  .startDelay = 0U
208  },
209 
210  .perCtrl =
211  {
212  .dacCh0Data = lesenseDACIfData,
213  .dacCh0ConvMode = lesenseDACConvModeDisable,
214  .dacCh0OutMode = lesenseDACOutModeDisable,
215  .dacCh1Data = lesenseDACIfData,
216  .dacCh1ConvMode = lesenseDACConvModeDisable,
217  .dacCh1OutMode = lesenseDACOutModeDisable,
218  .dacPresc = 0U,
219  .dacRef = lesenseDACRefBandGap,
220  .acmp0Mode = lesenseACMPModeMuxThres,
221  .acmp1Mode = lesenseACMPModeMuxThres,
222  .warmupMode = lesenseWarmupModeNormal
223  },
224 
225  .decCtrl =
226  {
227  .decInput = lesenseDecInputSensorSt,
228  .chkState = false,
229  .intMap = true,
230  .hystPRS0 = false,
231  .hystPRS1 = false,
232  .hystPRS2 = false,
233  .hystIRQ = false,
234  .prsCount = true,
235  .prsChSel0 = lesensePRSCh0,
236  .prsChSel1 = lesensePRSCh1,
237  .prsChSel2 = lesensePRSCh2,
238  .prsChSel3 = lesensePRSCh3
239  }
240  };
241 
242  /* Only initialize main LESENSE parameters once. */
243  if (init)
244  {
245  /* Initialize LESENSE interface with RESET. */
246  LESENSE_Init(&initLESENSE, true);
247  }
248 
249  /* Different configuration for "sleep" and "sense" modes. */
250  if (sleep)
251  {
252  /* Stop LESENSE before configuration. */
253  LESENSE_ScanStop();
254 
255  /* Wait until the currently active scan is finished. */
256  while (LESENSE_STATUS_SCANACTIVE & LESENSE_StatusGet()) ;
257 
258  /* Clear result buffer. */
259  LESENSE_ResultBufferClear();
260 
261  /* Set scan frequency (in Hz). */
262  (void) LESENSE_ScanFreqSet(0U, 4U);
263 
264  /* Set clock divisor for LF clock. */
265  LESENSE_ClkDivSet(lesenseClkLF, lesenseClkDiv_1);
266 
267  /* Configure scan channels. */
268  LESENSE_ChannelAllConfig(&initChsSleep);
269 
270  /* Restore calibration values. */
271  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER0_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[0]);
272  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER1_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[1]);
273  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER2_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[2]);
274  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER3_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[3]);
275 
276  /* Disable scan complete interrupt. */
277  LESENSE_IntDisable(LESENSE_IEN_SCANCOMPLETE);
278  }
279  else
280  {
281  /* Stop LESENSE before configuration. */
282  LESENSE_ScanStop();
283 
284  /* Wait until the currently active scan is finished. */
285  while (LESENSE_STATUS_SCANACTIVE & LESENSE_StatusGet()) ;
286 
287  /* Clean scan complete interrupt flag. */
288  LESENSE_IntClear(LESENSE_IEN_SCANCOMPLETE);
289 
290  /* Clear result buffer. */
291  LESENSE_ResultBufferClear();
292 
293  /* Set scan frequency (in Hz). */
294  (void) LESENSE_ScanFreqSet(0U, 64U);
295 
296  /* Set clock divisor for LF clock. */
297  LESENSE_ClkDivSet(lesenseClkLF, lesenseClkDiv_8);
298 
299  /* Configure scan channels. */
300  LESENSE_ChannelAllConfig(&initChsSense);
301 
302  /* Enable scan complete interrupt. */
303  LESENSE_IntEnable(LESENSE_IEN_SCANCOMPLETE);
304  }
305 
306  /* Enable LESENSE interrupt in NVIC. */
307  NVIC_EnableIRQ(LESENSE_IRQn);
308 
309  /* Start scanning LESENSE channels. */
310  LESENSE_ScanStart();
311 
312  /* Run it only once. */
313  if (init)
314  {
315  /* Assuming that the pads are not touched at first, we can use the result as
316  * the threshold value to calibrate the capacitive sensing in LESENSE. */
317  init = false;
318 
319  /* Waiting for buffer to be full. */
320  while (!(LESENSE->STATUS & LESENSE_STATUS_BUFHALFFULL)) ;
321 
322  /* Read out steady state values from LESENSE for calibration. */
323  for (i = 0U; i < CAPLESENSE_NUMOF_SLIDERS; i++)
324  {
325  capsenseCalibrateVals[i] = LESENSE_ScanResultDataBufferGet(i) - CAPLESENSE_SENSITIVITY_OFFS;
326  }
327 
328  /* Set calibration values. */
329  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER0_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[0]);
330  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER1_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[1]);
331  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER2_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[2]);
332  LESENSE_ChannelThresSet(CAPLESENSE_SLIDER3_PIN, CAPLESENSE_ACMP_VDD_SCALE, capsenseCalibrateVals[3]);
333  }
334 }
335 
336 
337 /**************************************************************************/
342 void CAPLESENSE_setupCallbacks(void (*scanCb)(void), void (*chCb)(void))
343 {
344  lesenseScanCb = scanCb;
345  lesenseChCb = chCb;
346 }
347 
348 
349 /**************************************************************************/
353 {
354  uint32_t count;
355 
356 
357  /* LESENSE scan complete interrupt. */
358  if (LESENSE_IF_SCANCOMPLETE & LESENSE_IntGetEnabled())
359  {
360  LESENSE_IntClear(LESENSE_IF_SCANCOMPLETE);
361 
362  /* Iterate trough all channels */
363  for (currentChannel = 0; currentChannel < LESENSE_CHANNELS; currentChannel++)
364  {
365  /* If this channel is not in use, skip to the next one */
367  {
368  continue;
369  }
370 
371  /* Read out value from LESENSE buffer */
372  count = LESENSE_ScanResultDataGet();
373 
374  /* Store value in channelValues */
375  channelValues[currentChannel] = count;
376 
377  /* Update channelMaxValues */
378  if (count > channelMaxValues[currentChannel])
379  {
381  }
382  }
383 
384  /* Call callback function. */
385  if (lesenseScanCb != 0x00000000)
386  {
387  lesenseScanCb();
388  }
389  }
390 
391  /* LESENSE channel interrupt. */
392  if (CAPLESENSE_CHANNEL_INT & LESENSE_IntGetEnabled())
393  {
394  /* Clear flags. */
395  LESENSE_IntClear(CAPLESENSE_CHANNEL_INT);
396 
397  /* Call callback function. */
398  if (lesenseChCb != 0x00000000)
399  {
400  lesenseChCb();
401  }
402  }
403 }
404 
405 
406 /**************************************************************************/
411 uint8_t CAPLESENSE_getSegmentChannel(uint8_t capSegment)
412 {
413  uint8_t channel;
414 
415  switch (capSegment)
416  {
417  case(0):
418  channel = SLIDER_PART0_CHANNEL;
419  break;
420  case(1):
421  channel = SLIDER_PART1_CHANNEL;
422  break;
423  case(2):
424  channel = SLIDER_PART2_CHANNEL;
425  break;
426  default:
427  channel = SLIDER_PART3_CHANNEL;
428  break;
429  }
430  return channel;
431 
432 }
433 
434 
435 /**************************************************************************/
440 uint32_t CAPLESENSE_getVal(uint8_t channel)
441 {
442  return channelValues[channel];
443 }
444 
445 /**************************************************************************/
450 uint32_t CAPLESENSE_getNormalizedVal(uint8_t channel)
451 {
452  uint32_t max = channelMaxValues[channel];
453  return (channelValues[channel] << 8) / max;
454 }
455 
456 
457 
458 /**************************************************************************/
464 {
465  int i;
466  int minPos = -1;
467  uint32_t minVal = 236; /* adjust it */
468  /* Values used for interpolation. There is two more which represents the edges.
469  * This makes the interpolation code a bit cleaner as we do not have to make special
470  * cases for handling them */
471  uint32_t interpol[6] = { 255, 255, 255, 255, 255, 255 };
472  uint32_t channelPattern[] = { 0, SLIDER_PART0_CHANNEL + 1,
473  SLIDER_PART1_CHANNEL + 1,
474  SLIDER_PART2_CHANNEL + 1,
475  SLIDER_PART3_CHANNEL + 1 };
476 
477  /* The calculated slider position. */
478  int position;
479 
480  /* Iterate through the 4 slider bars and calculate the current value divided by
481  * the maximum value multiplied by 256.
482  * Note that there is an offset of 1 between channelValues and interpol.
483  * This is done to make interpolation easier.
484  */
485  for (i = 1; i < CAPLESENSE_NUMOF_SLIDERS + 1; i++)
486  {
487  /* interpol[i] will be in the range 0-256 depending on channelMax */
488  interpol[i] = channelValues[channelPattern[i] - 1] << 8;
489  interpol[i] /= channelMaxValues[channelPattern[i] - 1];
490  /* Find the minimum value and position */
491  if (interpol[i] < minVal)
492  {
493  minVal = interpol[i];
494  minPos = i;
495  }
496  }
497  /* Check if the slider has not been touched */
498  if (minPos == -1)
499  return -1;
500 
501  /* Start position. Shift by 4 to get additional resolution. */
502  /* Because of the interpol trick earlier we have to substract one to offset that effect */
503  position = (minPos - 1) << 4;
504 
505  /* Interpolate with pad to the left */
506  position -= ((256 - interpol[minPos - 1]) << 3)
507  / (256 - interpol[minPos]);
508 
509  /* Interpolate with pad to the right */
510  position += ((256 - interpol[minPos + 1]) << 3)
511  / (256 - interpol[minPos]);
512 
513  return position;
514 }
515 
516 
517 /**************************************************************************/
521 {
522  /* Go to EM2 and wait for the measurement to complete. */
523  EMU_EnterEM2(true);
524 }
525 
526 
527 /**************************************************************************/
531 void CAPLESENSE_Init(bool sleep)
532 {
533  /* Disable interrupts */
534  INT_Disable();
535 
536  /* Setup CMU. */
538  /* Setup GPIO. */
540  /* Setup ACMP. */
542  /* Setup LESENSE. */
544 
545  /* Initialization done, enable interrupts globally. */
546  INT_Enable();
547 }
548 
549 
Clock management unit (CMU) API.
void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
Select reference clock/oscillator used for a clock branch.
Definition: em_cmu.c:2406
uint32_t CAPLESENSE_getVal(uint8_t channel)
Get the current channelValue for a channel.
Definition: caplesense.c:440
Emlib peripheral API "assert" implementation.
void CAPLESENSE_Sleep(void)
Send the capacative sense system to sleep mode.
Definition: caplesense.c:520
uint32_t CAPLESENSE_getNormalizedVal(uint8_t channel)
Get the current normalized channelValue for a channel.
Definition: caplesense.c:450
static volatile uint8_t currentChannel
Definition: caplesense.c:84
static __INLINE void SystemCoreClockUpdate(void)
Update CMSIS SystemCoreClock variable.
__STATIC_INLINE uint32_t INT_Enable(void)
Enable interrupts.
Definition: em_int.h:94
void EMU_EnterEM2(bool restore)
Enter energy mode 2 (EM2).
Definition: em_emu.c:413
static void(* lesenseScanCb)(void)
Definition: caplesense.c:79
static void(* lesenseChCb)(void)
Definition: caplesense.c:81
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
Capacitive sense driver.
void CAPLESENSE_setupCallbacks(void(*scanCb)(void), void(*chCb)(void))
LESENSE callback setup.
Definition: caplesense.c:342
void LESENSE_IRQHandler(void)
LESENSE interrupt handler.
Definition: caplesense.c:352
void CAPLESENSE_setupACMP(void)
Setup the ACMP.
Definition: caplesense.c:141
Interrupt enable/disable unit API.
void CAPLESENSE_setupGPIO(void)
Setup the GPIO.
Definition: caplesense.c:124
void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode)
Sets the drive mode for a GPIO port.
Definition: em_gpio.c:101
#define ACMP0
void GPIO_PinModeSet(GPIO_Port_TypeDef port, unsigned int pin, GPIO_Mode_TypeDef mode, unsigned int out)
Set the mode for a GPIO pin.
Definition: em_gpio.c:226
void ACMP_CapsenseInit(ACMP_TypeDef *acmp, const ACMP_CapsenseInit_TypeDef *init)
Sets up the ACMP for use in capacative sense applications.
Definition: em_acmp.c:110
General Purpose IO (GPIO) peripheral API.
void CAPLESENSE_setupCMU(void)
Setup the CMU.
Definition: caplesense.c:91
void CAPLESENSE_setupLESENSE(bool sleep)
Setup the LESENSE for capavitive sensing.
Definition: caplesense.c:174
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
Enable/disable a clock.
Definition: em_cmu.c:1369
static const bool channelsInUse[LESENSE_CHANNELS]
A bit vector which represents the channels to iterate through.
Definition: caplesense.c:65
int32_t CAPLESENSE_getSliderPosition(void)
Get the position of the slider.
Definition: caplesense.c:463
Analog Comparator (ACMP) peripheral API.
Energy management unit (EMU) peripheral API.
#define cmuClkDiv_1
Definition: em_cmu.h:148
static volatile uint32_t channelValues[LESENSE_CHANNELS]
This vector stores the latest read values from LESENSE.
Definition: caplesense.c:40
Low Energy Sensor (LESENSE) peripheral API.
__STATIC_INLINE uint32_t INT_Disable(void)
Disable interrupts.
Definition: em_int.h:71
void CAPLESENSE_Init(bool sleep)
Initializes the capacative sense system without LESENSE.
Definition: caplesense.c:531
static volatile uint32_t channelMaxValues[LESENSE_CHANNELS]
This stores the maximum values seen by a channel.
Definition: caplesense.c:53
uint8_t CAPLESENSE_getSegmentChannel(uint8_t capSegment)
Get the channelValue for a sensor segment.
Definition: caplesense.c:411
void ACMP_GPIOSetup(ACMP_TypeDef *acmp, uint32_t location, bool enable, bool invert)
Sets up GPIO output from the ACMP.
Definition: em_acmp.c:280
void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div)
Set clock divisor/prescaler.
Definition: em_cmu.c:1141