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