Embedded System Design 2 - Project
ADXL362.c
Go to the documentation of this file.
1 /***************************************************************************//**
2  * @file ADXL362.c
3  * @brief All code for the ADXL362 accelerometer.
4  * @version 3.1
5  * @author Brecht Van Eeckhoudt
6  *
7  * ******************************************************************************
8  *
9  * @section Versions
10  *
11  * @li v1.0: Started with the code from https://github.com/Fescron/Project-LabEmbeddedDesign1/tree/master/code/SLSTK3400A_ADXL362
12  * Changed file name from accel.c to ADXL362.c.
13  * @li v1.1: Changed PinModeSet `out` value to 0 in initADXL_VCC.
14  * @li v1.2: Changed last argument in GPIO_PinModeSet in method initADXL_VCC to
15  * change the pin mode and enable the pin in one statement.
16  * @li v1.3: Changed some methods and global variables to be static (~hidden).
17  * @li v1.4: Changed delay method and cleaned up includes.
18  * @li v1.5: Added get/set method for the static variable `ADXL_triggered`.
19  * @li v1.6: Changed a lot of things...
20  * @li v1.7: Updated documentation and chanced `USART0` to `ADXL_SPI`.
21  * @li v1.8: Updated code with new DEFINE checks.
22  * @li v1.9: Started using custom enum for range & ODR configuration methods.
23  * @li v2.0: Added testing method to go through all the settings, moved the register definitions.
24  * @li v2.1: Disabled SPI pins on *hard* reset, cleaned up some code.
25  * @li v2.2: Added functionality to check the number of interrupts.
26  * @li v2.3: Updated documentation.
27  * @li v2.4: Changed error numbering.
28  * @li v2.5: Updated ODR enum and changed masking logic for register settings.
29  * @li v3.0: Added functionality to exit methods after `error` call and updated version number.
30  * @li v3.1: Removed `static` before the local variables (not necessary).
31  *
32  * ******************************************************************************
33  *
34  * @todo
35  * **Future improvements:**@n
36  * - Check configurations by reading the registers again and return true/false when the registers have/don't have the correct values.
37  * - Enable wake-up mode (`writeADXL(ADXL_REG_POWER_CTL, 0b00001000); // 5th bit`)
38  * - Enable loop mode (with act/inact time registers?) so interrupts don't need to be acknowledged by host (`writeADXL(ADXL_REG_ACT_INACT_CTL, 0b00110000);`)
39  *
40  * ******************************************************************************
41  *
42  * @section License
43  *
44  * **Copyright (C) 2019 - Brecht Van Eeckhoudt**
45  *
46  * This program is free software: you can redistribute it and/or modify
47  * it under the terms of the **GNU General Public License** as published by
48  * the Free Software Foundation, either **version 3** of the License, or
49  * (at your option) any later version.
50  *
51  * This program is distributed in the hope that it will be useful,
52  * but WITHOUT ANY WARRANTY; without even the implied warranty of
53  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54  * GNU General Public License for more details.
55  *
56  * *A copy of the GNU General Public License can be found in the `LICENSE`
57  * file along with this source code.*
58  *
59  * @n
60  *
61  * Some methods use code obtained from examples from [Silicon Labs' GitHub](https://github.com/SiliconLabs/peripheral_examples).
62  * These sections are licensed under the Silabs License Agreement. See the file
63  * "Silabs_License_Agreement.txt" for details. Before using this software for
64  * any purpose, you must agree to the terms of that agreement.
65  *
66  ******************************************************************************/
67 
68 
69 #include <stdint.h> /* (u)intXX_t */
70 #include <stdbool.h> /* "bool", "true", "false" */
71 #include "em_cmu.h" /* Clock Management Unit */
72 #include "em_gpio.h" /* General Purpose IO (GPIO) peripheral API */
73 #include "em_usart.h" /* Universal synchr./asynchr. receiver/transmitter (USART/UART) Peripheral API */
74 
75 #include "ADXL362.h" /* Corresponding header file */
76 #include "pin_mapping.h" /* PORT and PIN definitions */
77 #include "debug_dbprint.h" /* Enable or disable printing to UART */
78 #include "delay.h" /* Delay functionality */
79 #include "util.h" /* Utility functionality */
80 
81 
82 /* Local definitions - ADXL362 register definitions */
83 #define ADXL_REG_DEVID_AD 0x00 /* Reset: 0xAD */
84 #define ADXL_REG_DEVID_MST 0x01 /* Reset: 0x1D */
85 #define ADXL_REG_PARTID 0x02 /* Reset: 0xF2 */
86 #define ADXL_REG_REVID 0x03 /* Reset: 0x01 (can be incremented) */
87 #define ADXL_REG_XDATA 0x08
88 #define ADXL_REG_YDATA 0x09
89 #define ADXL_REG_ZDATA 0x0A
90 #define ADXL_REG_STATUS 0x0B
91 #define ADXL_REG_TEMP_L 0x14
92 #define ADXL_REG_TEMP_H 0x15
93 #define ADXL_REG_SOFT_RESET 0x1F /* Needs to be 0x52 ("R") written to for a soft reset */
94 #define ADXL_REG_THRESH_ACT_L 0x20 /* 7:0 bits used */
95 #define ADXL_REG_THRESH_ACT_H 0x21 /* 2:0 bits used */
96 #define ADXL_REG_ACT_INACT_CTL 0x27 /* Activity/Inactivity control register: XX - XX - LINKLOOP - LINKLOOP - INACT_REF - INACT_EN - ACT_REF - ACT_EN */
97 #define ADXL_REG_INTMAP1 0x2A /* INT_LOW -- AWAKE -- INACT -- ACT -- FIFO_OVERRUN -- FIFO_WATERMARK -- FIFO_READY -- DATA_READY */
98 #define ADXL_REG_INTMAP2 0x2B /* INT_LOW -- AWAKE -- INACT -- ACT -- FIFO_OVERRUN -- FIFO_WATERMARK -- FIFO_READY -- DATA_READY */
99 #define ADXL_REG_FILTER_CTL 0x2C /* Write FFxx xxxx (FF = 00 for +-2g, 01 for =-4g, 1x for +- 8g) for measurement range selection */
100 #define ADXL_REG_POWER_CTL 0x2D /* Write xxxx xxMM (MM = 10) to: measurement mode */
101 
102 
103 /* Local variables */
104 volatile bool ADXL_triggered = false; /* Volatile because it's modified by an interrupt service routine */
105 volatile uint16_t ADXL_triggercounter = 0; /* Volatile because it's modified by an interrupt service routine */
106 int8_t XYZDATA[3] = { 0x00, 0x00, 0x00 };
108 bool ADXL_VDD_initialized = false;
109 
110 
111 /* Local prototypes */
112 static void powerADXL (bool enabled);
113 static void initADXL_SPI (void);
114 static void softResetADXL (void);
115 static void resetHandlerADXL (void);
116 static uint8_t readADXL (uint8_t address);
117 static void writeADXL (uint8_t address, uint8_t data);
118 static void readADXL_XYZDATA (void);
119 static bool checkID_ADXL (void);
120 static int32_t convertGRangeToGValue (int8_t sensorValue);
121 
122 
123 /**************************************************************************//**
124  * @brief
125  * Initialize the accelerometer.
126  *
127  * @details
128  * This method calls all the other internal necessary functions.
129  * Clock enable functionality is gathered here instead of in
130  * *lower* (static) functions.
131  *****************************************************************************/
132 void initADXL (void)
133 {
134  /* Enable necessary clocks (just in case) */
135  CMU_ClockEnable(cmuClock_HFPER, true); /* GPIO and USART0/1 are High Frequency Peripherals */
136  CMU_ClockEnable(cmuClock_GPIO, true);
137 
138  /* Initialize and power VDD pin */
139  powerADXL(true);
140 
141  /* Power-up delay of 40 ms */
142  delay(40);
143 
144  /* Enable necessary clock (just in case) */
145  if (ADXL_SPI == USART0) CMU_ClockEnable(cmuClock_USART0, true);
146  else if (ADXL_SPI == USART1) CMU_ClockEnable(cmuClock_USART1, true);
147  else
148  {
149 
150 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
151  dbcrit("Wrong peripheral selected!");
152 #endif /* DEBUG_DBPRINT */
153 
154  /* Disable power to the VDD pin */
155  powerADXL(false);
156 
157  error(21);
158 
159  /* Exit function */
160  return;
161  }
162 
163  /* Initialize USART0/1 as SPI slave (also initialize CS pin) */
164  initADXL_SPI();
165 
166  /* Soft reset ADXL handler */
168 }
169 
170 
171 /**************************************************************************//**
172  * @brief
173  * Getter for the `ADXL_triggercounter` variable.
174  *
175  * @return
176  * The value of `ADXL_triggercounter`.
177  *****************************************************************************/
178 uint16_t ADXL_getCounter (void)
179 {
180  return (ADXL_triggercounter);
181 }
182 
183 
184 /**************************************************************************//**
185  * @brief
186  * Method to set the `ADXL_triggercounter` variable back to zero.
187  *****************************************************************************/
188 void ADXL_clearCounter (void)
189 {
191 }
192 
193 
194 /**************************************************************************//**
195  * @brief
196  * Setter for the `ADXL_triggered` variable.
197  *
198  * @param[in] triggered
199  * @li `true` - Set `ADXL_triggered` to `true`.
200  * @li `false` - Set `ADXL_triggered` to `false`.
201  *****************************************************************************/
202 void ADXL_setTriggered (bool triggered)
203 {
204  ADXL_triggered = triggered;
206 }
207 
208 
209 /**************************************************************************//**
210  * @brief
211  * Getter for the `ADXL_triggered` variable.
212  *
213  * @return
214  * The value of `ADXL_triggered`.
215  *****************************************************************************/
216 bool ADXL_getTriggered (void)
217 {
218  return (ADXL_triggered);
219 }
220 
221 
222 /**************************************************************************//**
223  * @brief
224  * Acknowledge the interrupt from the accelerometer.
225  *
226  * @details
227  * Read a certain register (necessary if the accelerometer is not in
228  * linked-loop mode) and clear the variable.
229  *****************************************************************************/
230 void ADXL_ackInterrupt (void)
231 {
233  ADXL_triggered = false;
234 }
235 
236 
237 /**************************************************************************//**
238  * @brief
239  * Enable or disable the SPI pins and USART0/1 clock and peripheral to the accelerometer.
240  *
241  * @param[in] enabled
242  * @li `true` - Enable the SPI pins and USART0/1 clock and peripheral to the accelerometer.
243  * @li `false` - Disable the SPI pins and USART0/1 clock and peripheral to the accelerometer.
244  *****************************************************************************/
245 void ADXL_enableSPI (bool enabled)
246 {
247  if (enabled)
248  {
249  /* Enable USART clock and peripheral */
250  if (ADXL_SPI == USART0) CMU_ClockEnable(cmuClock_USART0, true);
251  else if (ADXL_SPI == USART1) CMU_ClockEnable(cmuClock_USART1, true);
252  else
253  {
254 
255 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
256  dbcrit("Wrong peripheral selected!");
257 #endif /* DEBUG_DBPRINT */
258 
259  error(22);
260 
261  /* Exit function */
262  return;
263  }
264 
265  USART_Enable(ADXL_SPI, usartEnable);
266 
267  /* In the case of gpioModePushPull", the last argument directly sets the pin state */
268  GPIO_PinModeSet(ADXL_CLK_PORT, ADXL_CLK_PIN, gpioModePushPull, 0); /* US0_CLK is push pull */
269  GPIO_PinModeSet(ADXL_NCS_PORT, ADXL_NCS_PIN, gpioModePushPull, 1); /* US0_CS is push pull */
270  GPIO_PinModeSet(ADXL_MOSI_PORT, ADXL_MOSI_PIN, gpioModePushPull, 1); /* US0_TX (MOSI) is push pull */
271  GPIO_PinModeSet(ADXL_MISO_PORT, ADXL_MISO_PIN, gpioModeInput, 1); /* US0_RX (MISO) is input */
272  }
273  else
274  {
275  /* Disable USART clock and peripheral */
276  if (ADXL_SPI == USART0) CMU_ClockEnable(cmuClock_USART0, false);
277  else if (ADXL_SPI == USART1) CMU_ClockEnable(cmuClock_USART1, false);
278  else
279  {
280 
281 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
282  dbcrit("Wrong peripheral selected!");
283 #endif /* DEBUG_DBPRINT */
284 
285  error(23);
286 
287  /* Exit function */
288  return;
289  }
290 
291  USART_Enable(ADXL_SPI, usartDisable);
292 
293  /* gpioModeDisabled: Pull-up if DOUT is set. */
294  GPIO_PinModeSet(ADXL_CLK_PORT, ADXL_CLK_PIN, gpioModeDisabled, 0);
295  GPIO_PinModeSet(ADXL_NCS_PORT, ADXL_NCS_PIN, gpioModeDisabled, 1);
296  GPIO_PinModeSet(ADXL_MOSI_PORT, ADXL_MOSI_PIN, gpioModeDisabled, 1);
297  GPIO_PinModeSet(ADXL_MISO_PORT, ADXL_MISO_PIN, gpioModeDisabled, 1);
298  }
299 }
300 
301 
302 /**************************************************************************//**
303  * @brief
304  * Enable or disable measurement mode.
305  *
306  * @param[in] enabled
307  * @li `true` - Enable measurement mode.
308  * @li `false` - Disable measurement mode (standby).
309  *****************************************************************************/
310 void ADXL_enableMeasure (bool enabled)
311 {
312  if (enabled)
313  {
314  /* Get value in register */
315  uint8_t reg = readADXL(ADXL_REG_POWER_CTL);
316 
317  /* AND with mask to keep the bits we don't want to change */
318  reg &= 0b11111100;
319 
320  /* Enable measurements (OR with new setting bits) */
321  writeADXL(ADXL_REG_POWER_CTL, reg | 0b00000010); /* Last 2 bits are measurement mode */
322 
323 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
324  dbinfo("ADXL362: Measurement enabled");
325 #endif /* DEBUG_DBPRINT */
326 
327  }
328  else
329  {
330  /* Get value in register */
331  uint8_t reg = readADXL(ADXL_REG_POWER_CTL);
332 
333  /* AND with mask to keep the bits we don't want to change */
334  reg &= 0b11111100;
335 
336  /* Disable measurements (OR with new setting bits) */
337  writeADXL(ADXL_REG_POWER_CTL, reg | 0b00000000); /* Last 2 bits are measurement mode */
338 
339 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
340  dbinfo("ADXL362: Measurement disabled (standby)");
341 #endif /* DEBUG_DBPRINT */
342 
343  }
344 }
345 
346 
347 /**************************************************************************//**
348  * @brief
349  * Configure the measurement range and store the selected one in
350  * a global variable for later (internal) use.
351  *
352  * @details
353  * When a range of, for example "2g" is selected, the real range is "+-2g".
354  *
355  * @param[in] givenRange
356  * The selected range.
357  *****************************************************************************/
359 {
360  /* Get value in register */
361  uint8_t reg = readADXL(ADXL_REG_FILTER_CTL);
362 
363  /* AND with mask to keep the bits we don't want to change */
364  reg &= 0b00111111;
365 
366  /* Set measurement range (OR with new setting bits, first two bits) */
367  if (givenRange == ADXL_RANGE_2G)
368  {
369  writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000000));
371  }
372  else if (givenRange == ADXL_RANGE_4G)
373  {
374  writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b01000000));
376  }
377  else if (givenRange == ADXL_RANGE_8G)
378  {
379  writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b10000000));
381  }
382  else
383  {
384 
385 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
386  dbcrit("Non-existing range selected!");
387 #endif /* DEBUG_DBPRINT */
388 
389  error(24);
390 
391  /* Exit function */
392  return;
393  }
394 
395 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
396  if (range == ADXL_RANGE_2G) dbinfo("ADXL362: Measurement mode +- 2g selected");
397  else if (range == ADXL_RANGE_4G) dbinfo("ADXL362: Measurement mode +- 4g selected");
398  else if (range == ADXL_RANGE_8G) dbinfo("ADXL362: Measurement mode +- 8g selected");
399 #endif /* DEBUG_DBPRINT */
400 
401 }
402 
403 
404 /**************************************************************************//**
405  * @brief
406  * Configure the Output Data Rate (ODR).
407  *
408  * @param[in] givenODR
409  * The selected ODR.
410  *****************************************************************************/
411 void ADXL_configODR (ADXL_ODR_t givenODR)
412 {
413  /* Get value in register */
414  uint8_t reg = readADXL(ADXL_REG_FILTER_CTL);
415 
416  /* AND with mask to keep the bits we don't want to change */
417  reg &= 0b11111000;
418 
419  /* Set ODR (OR with new setting bits, last three bits) */
420  if (givenODR == ADXL_ODR_12_5_HZ) writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000000));
421  else if (givenODR == ADXL_ODR_25_HZ) writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000001));
422  else if (givenODR == ADXL_ODR_50_HZ) writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000010));
423  else if (givenODR == ADXL_ODR_100_HZ) writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000011));
424  else if (givenODR == ADXL_ODR_200_HZ) writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000100));
425  else if (givenODR == ADXL_ODR_400_HZ) writeADXL(ADXL_REG_FILTER_CTL, (reg | 0b00000101));
426  else
427  {
428 
429 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
430  dbcrit("Non-existing ODR selected!");
431 #endif /* DEBUG_DBPRINT */
432 
433  error(25);
434 
435  /* Exit function */
436  return;
437  }
438 
439 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
440  if (givenODR == ADXL_ODR_12_5_HZ) dbinfo("ADXL362: ODR set at 12.5 Hz");
441  else if (givenODR == ADXL_ODR_25_HZ) dbinfo("ADXL362: ODR set at 25 Hz");
442  else if (givenODR == ADXL_ODR_50_HZ) dbinfo("ADXL362: ODR set at 50 Hz");
443  else if (givenODR == ADXL_ODR_100_HZ) dbinfo("ADXL362: ODR set at 100 Hz");
444  else if (givenODR == ADXL_ODR_200_HZ) dbinfo("ADXL362: ODR set at 200 Hz");
445  else if (givenODR == ADXL_ODR_400_HZ) dbinfo("ADXL362: ODR set at 400 Hz");
446 #endif /* DEBUG_DBPRINT */
447 
448 }
449 
450 
451 /**************************************************************************//**
452  * @brief
453  * Configure the accelerometer to work in (referenced) activity threshold mode.
454  *
455  * @details
456  * Route activity detector to INT1 pin using INTMAP1, isolate bits
457  * and write settings to both threshold registers.
458  *
459  * **Referenced** means that during the initialization a *reference acceleration* gets
460  * measured (like for example `1 g` on a certain axis) and stored internally. This value
461  * always gets internally subtracted from a measured acceleration value to calculate
462  * the final value and check if it exceeds the set threshold:
463  * - `ABS(acceleration - reference) > threshold`
464  *
465  * @param[in] gThreshold
466  * Threshold [g].
467  *****************************************************************************/
468 void ADXL_configActivity (uint8_t gThreshold)
469 {
470  /* Map activity detector to INT1 pin */
471  writeADXL(ADXL_REG_INTMAP1, 0b00010000); /* Bit 4 selects activity detector */
472 
473  /* Enable referenced activity threshold mode (last two bits) */
474  writeADXL(ADXL_REG_ACT_INACT_CTL, 0b00000011);
475 
476  /* Convert g value to "codes"
477  * THRESH_ACT [codes] = Threshold Value [g] × Scale Factor [LSB per g] */
478  uint8_t threshold;
479 
480  if (range == ADXL_RANGE_2G) threshold = gThreshold * 1000;
481  else if (range == ADXL_RANGE_4G) threshold = gThreshold * 500;
482  else if (range == ADXL_RANGE_8G) threshold = gThreshold * 250;
483  else
484  {
485 
486 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
487  dbcrit("Range wrong, can't set gThreshold!");
488 #endif /* DEBUG_DBPRINT */
489 
490  error(26);
491 
492  /* Exit function */
493  return;
494  }
495 
496  /* Isolate bits using masks and shifting */
497  uint8_t low = (threshold & 0b0011111111);
498  uint8_t high = (threshold & 0b1100000000) >> 8;
499 
500  /* Set threshold register values (total: 11bit unsigned) */
501  writeADXL(ADXL_REG_THRESH_ACT_L, low); /* 7:0 bits used */
502  writeADXL(ADXL_REG_THRESH_ACT_H, high); /* 2:0 bits used */
503 
504 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
505  dbinfoInt("ADXL362: Activity configured: ", gThreshold, "g");
506 #endif /* DEBUG_DBPRINT */
507 
508 }
509 
510 
511 /**************************************************************************//**
512  * @brief
513  * Read and display "g" values forever with a 100ms interval.
514  *****************************************************************************/
515 void ADXL_readValues (void)
516 {
517  uint32_t counter = 0;
518 
519  /* Enable measurement mode */
520  ADXL_enableMeasure(true);
521 
522  /* Infinite loop */
523  while (1)
524  {
525  led(true); /* Enable LED */
526 
527  readADXL_XYZDATA(); /* Read XYZ sensor data */
528 
529 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
530  /* Print XYZ sensor data */
531  dbprint("\r[");
532  dbprintInt(counter);
533  dbprint("] X: ");
535  dbprint(" mg | Y: ");
537  dbprint(" mg | Z: ");
539  dbprint(" mg "); /* Extra spacing is to overwrite other data if it's remaining (see \r) */
540 #endif /* DEBUG_DBPRINT */
541 
542  led(false); /* Disable LED */
543 
544  counter++;
545 
546  delay(100);
547  }
548 }
549 
550 
551 /**************************************************************************//**
552  * @brief
553  * This method goes through all of the ODR settings to see the influence
554  * they have on power usage. The measurement range is the default one (+-2g).
555  *
556  * @details
557  * To get the "correct" currents the delay method puts the MCU to EM2/3 sleep
558  * and the SPI lines are disabled. The order of the test is:
559  * - Wait 5 seconds
560  * - Soft reset the accelerometer
561  * - One second in "standby" mode
562  * - Enable "measurement" mode
563  * - One second in ODR 12.5 Hz
564  * - One second in ODR 25 Hz
565  * - One second in ODR 50 Hz
566  * - One second in ODR 100 Hz
567  * - One second in ODR 200 Hz
568  * - One second in ODR 400 Hz
569  * - Soft reset the accelerometer
570  *****************************************************************************/
571 void testADXL (void)
572 {
573  delay(5000);
574 
575  /* Soft reset ADXL */
576  softResetADXL();
577 
578  /* Standby */
579  ADXL_enableSPI(false); /* Disable SPI functionality */
580  delay(1000);
581  ADXL_enableSPI(true); /* Enable SPI functionality */
582 
583  /* ODR 12,5Hz */
584  writeADXL(ADXL_REG_FILTER_CTL, 0b00010000); /* last 3 bits are ODR */
585 
586  /* Enable measurements */
587  writeADXL(ADXL_REG_POWER_CTL, 0b00000010); /* last 2 bits are measurement mode */
588 
589  ADXL_enableSPI(false); /* Disable SPI functionality */
590  delay(1000);
591  ADXL_enableSPI(true); /* Enable SPI functionality */
592 
593  /* ODR 25Hz */
594  writeADXL(ADXL_REG_FILTER_CTL, 0b00010001); /* last 3 bits are ODR */
595 
596  ADXL_enableSPI(false); /* Disable SPI functionality */
597  delay(1000);
598  ADXL_enableSPI(true); /* Enable SPI functionality */
599 
600  /* ODR 50Hz */
601  writeADXL(ADXL_REG_FILTER_CTL, 0b00010010); /* last 3 bits are ODR */
602 
603  ADXL_enableSPI(false); /* Disable SPI functionality */
604  delay(1000);
605  ADXL_enableSPI(true); /* Enable SPI functionality */
606 
607  /* ODR 100Hz (default) */
608  writeADXL(ADXL_REG_FILTER_CTL, 0b00010011); /* last 3 bits are ODR */
609 
610  ADXL_enableSPI(false); /* Disable SPI functionality */
611  delay(1000);
612  ADXL_enableSPI(true); /* Enable SPI functionality */
613 
614  /* ODR 200Hz */
615  writeADXL(ADXL_REG_FILTER_CTL, 0b00010100); /* last 3 bits are ODR */
616 
617  ADXL_enableSPI(false); /* Disable SPI functionality */
618  delay(1000);
619  ADXL_enableSPI(true); /* Enable SPI functionality */
620 
621  /* ODR 400Hz */
622  writeADXL(ADXL_REG_FILTER_CTL, 0b00010101); /* last 3 bits are ODR */
623 
624  ADXL_enableSPI(false); /* Disable SPI functionality */
625  delay(1000);
626  ADXL_enableSPI(true); /* Enable SPI functionality */
627 
628  /* Soft reset ADXL */
629  softResetADXL();
630 }
631 
632 
633 /**************************************************************************//**
634  * @brief
635  * Initialize USARTx in SPI mode according to the settings required
636  * by the accelerometer.
637  *
638  * @details
639  * Configure pins, configure USARTx in SPI mode, route
640  * the pins, enable USARTx and set CS high.
641  * Necessary clocks are enabled in a previous method.
642  *
643  * @note
644  * This is a static method because it's only internally used in this file
645  * and called by other methods if necessary.
646  *****************************************************************************/
647 static void initADXL_SPI (void)
648 {
649  /* Configure GPIO */
650  /* In the case of gpioModePushPull", the last argument directly sets the pin state */
651  GPIO_PinModeSet(ADXL_CLK_PORT, ADXL_CLK_PIN, gpioModePushPull, 0); /* US0_CLK is push pull */
652  GPIO_PinModeSet(ADXL_NCS_PORT, ADXL_NCS_PIN, gpioModePushPull, 1); /* US0_CS is push pull */
653  GPIO_PinModeSet(ADXL_MOSI_PORT, ADXL_MOSI_PIN, gpioModePushPull, 1); /* US0_TX (MOSI) is push pull */
654  GPIO_PinModeSet(ADXL_MISO_PORT, ADXL_MISO_PIN, gpioModeInput, 1); /* US0_RX (MISO) is input */
655 
656  /* Start with default config */
657  USART_InitSync_TypeDef config = USART_INITSYNC_DEFAULT;
658 
659  /* Modify some settings */
660  config.enable = false; /* making sure to keep USART disabled until we've set everything up */
661  config.refFreq = 0; /* USART/UART reference clock assumed when configuring baud rate setup. Set to 0 to use the currently configured reference clock. */
662  config.baudrate = 4000000; /* CLK freq is 1 MHz (1000000) */
663  config.databits = usartDatabits8; /* master mode */
664  config.master = true; /* master mode */
665  config.msbf = true; /* send MSB first */
666  config.clockMode = usartClockMode0; /* clock idle low, sample on rising/first edge (Clock polarity/phase mode = CPOL/CPHA) */
667  config.prsRxEnable = false; /* If enabled: Enable USART Rx via PRS. */
668  config.autoTx = false; /* If enabled: Enable AUTOTX mode. Transmits as long as RX is not full. Generates underflows if TX is empty. */
669  config.autoCsEnable = false; /* CS pin controlled by hardware, not firmware */
670 
671  /* Initialize USART0/1 with the configured parameters */
672  USART_InitSync(ADXL_SPI, &config);
673 
674  /* Set the pin location for USART0/1 (before: USART_ROUTE_LOCATION_LOC0) */
675  ADXL_SPI->ROUTE = USART_ROUTE_CLKPEN | USART_ROUTE_CSPEN | USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | ADXL_SPI_LOC;
676 
677  /* Enable USART0/1 */
678  USART_Enable(ADXL_SPI, usartEnable);
679 
680  /* Set CS high (active low!) */
681  GPIO_PinOutSet(gpioPortE, 13);
682 }
683 
684 
685 /**************************************************************************//**
686  * @brief
687  * Soft reset accelerometer handler.
688  *
689  * @details
690  * If the first ID check fails, the MCU is put on hold for one second
691  * and the ID gets checked again.
692  *
693  * @note
694  * This is a static method because it's only internally used in this file
695  * and called by other methods if necessary.
696  *****************************************************************************/
697 static void resetHandlerADXL (void)
698 {
699  uint8_t retries = 0;
700 
701  /* Soft reset ADXL */
702  softResetADXL();
703 
704  /* First try to get the correct ID failed */
705  if (!checkID_ADXL())
706  {
707  retries++;
708 
709  delay(1000);
710 
711  /* Soft reset */
712  softResetADXL();
713 
714  /* Second try to get the correct ID failed */
715  if (!checkID_ADXL())
716  {
717  retries++;
718 
719  delay(1000);
720 
721  /* Soft reset */
722  softResetADXL();
723 
724  /* Third try to get the correct ID failed
725  * resorting to "hard" reset! */
726  if (!checkID_ADXL())
727  {
728  retries++;
729 
730  powerADXL(false);
731  ADXL_enableSPI(false); /* Make sure the accelerometer doesn't get power through the SPI pins */
732 
733  delay(1000);
734 
735  powerADXL(true);
736  ADXL_enableSPI(true);
737 
738  delay(1000);
739 
740  /* Soft reset */
741  softResetADXL();
742 
743  /* Last try to get the correct ID failed */
744  if (!checkID_ADXL())
745  {
746 
747 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
748  dbcrit("ADXL362 initialization failed");
749 #endif /* DEBUG_DBPRINT */
750 
751  error(20);
752 
753  /* Exit function */
754  return;
755  }
756  }
757  }
758  }
759 
760 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
761  if (retries < 2) dbinfoInt("ADXL362 initialized (", retries, " soft reset retries)");
762  else dbwarnInt("ADXL362 initialized (had to \"hard reset\", ", retries, " soft reset retries)");
763 #endif /* DEBUG_DBPRINT */
764 
765 }
766 
767 
768 /**************************************************************************//**
769  * @brief
770  * Read an SPI byte from the accelerometer (8 bits) using a given address.
771  *
772  * @note
773  * This is a static method because it's only internally used in this file
774  * and called by other methods if necessary.
775  *
776  * @param[in] address
777  * The register address to read from.
778  *
779  * @return
780  * The response (one byte, `uint8_t`).
781  *****************************************************************************/
782 static uint8_t readADXL (uint8_t address)
783 {
784  uint8_t response;
785 
786  /* Set CS low (active low!) */
787  GPIO_PinOutClear(ADXL_NCS_PORT, ADXL_NCS_PIN);
788 
789  /* 3-byte operation according to datasheet */
790  USART_SpiTransfer(ADXL_SPI, 0x0B); /* "read" instruction */
791  USART_SpiTransfer(ADXL_SPI, address); /* Address */
792  response = USART_SpiTransfer(ADXL_SPI, 0x00); /* Read response */
793 
794  /* Set CS high */
795  GPIO_PinOutSet(ADXL_NCS_PORT, ADXL_NCS_PIN);
796 
797  return (response);
798 }
799 
800 
801 /**************************************************************************//**
802  * @brief
803  * Write an SPI byte to the accelerometer (8 bits) using a given address
804  * and specified data.
805  *
806  * @note
807  * This is a static method because it's only internally used in this file
808  * and called by other methods if necessary.
809  *
810  * @param[in] address
811  * The register address to write the data to (one byte, `uint8_t`).
812  *
813  * @param[in] data
814  * The data to write to the address (one byte, `uint8_t`).
815  *****************************************************************************/
816 static void writeADXL (uint8_t address, uint8_t data)
817 {
818  /* Set CS low (active low!) */
819  GPIO_PinOutClear(ADXL_NCS_PORT, ADXL_NCS_PIN);
820 
821  /* 3-byte operation according to datasheet */
822  USART_SpiTransfer(ADXL_SPI, 0x0A); /* "write" instruction */
823  USART_SpiTransfer(ADXL_SPI, address); /* Address */
824  USART_SpiTransfer(ADXL_SPI, data); /* Data */
825 
826  /* Set CS high */
827  GPIO_PinOutSet(ADXL_NCS_PORT, ADXL_NCS_PIN);
828 }
829 
830 
831 /**************************************************************************//**
832  * @brief
833  * Read the X-Y-Z data registers in the `XYZDATA[]` field using burst reads.
834  *
835  * @details
836  * Response data gets put in `XYZDATA` array (global volatile variable).
837  *
838  * @note
839  * This is a static method because it's only internally used in this file
840  * and called by other methods if necessary.
841  *****************************************************************************/
842 static void readADXL_XYZDATA (void)
843 {
844  /* CS low (active low!) */
845  GPIO_PinOutClear(ADXL_NCS_PORT, ADXL_NCS_PIN);
846 
847  /* Burst read (address auto-increments) */
848  USART_SpiTransfer(ADXL_SPI, 0x0B); /* "read" instruction */
849  USART_SpiTransfer(ADXL_SPI, ADXL_REG_XDATA); /* Address */
850  XYZDATA[0] = USART_SpiTransfer(ADXL_SPI, 0x00); /* Read response */
851  XYZDATA[1] = USART_SpiTransfer(ADXL_SPI, 0x00); /* Read response */
852  XYZDATA[2] = USART_SpiTransfer(ADXL_SPI, 0x00); /* Read response */
853 
854  /* CS high */
855  GPIO_PinOutSet(ADXL_NCS_PORT, ADXL_NCS_PIN);
856 }
857 
858 
859 /**************************************************************************//**
860  * @brief
861  * Enable or disable the power to the accelerometer.
862  *
863  * @details
864  * This method also initializes the pin-mode if necessary.
865  * Necessary clocks are enabled in a previous method.
866  *
867  * @note
868  * This is a static method because it's only internally used in this file
869  * and called by other methods if necessary.
870  *
871  * @param[in] enabled
872  * @li `true` - Enable the GPIO pin connected to the VDD pin of the accelerometer.
873  * @li `false` - Disable the GPIO pin connected to the VDD pin of the accelerometer.
874  *****************************************************************************/
875 static void powerADXL (bool enabled)
876 {
877  /* Initialize VDD pin if not already the case */
879  {
880  /* In the case of gpioModePushPull", the last argument directly sets the pin state */
881  GPIO_PinModeSet(ADXL_VDD_PORT, ADXL_VDD_PIN, gpioModePushPull, enabled);
882 
883  ADXL_VDD_initialized = true;
884  }
885  else
886  {
887  if (enabled) GPIO_PinOutSet(ADXL_VDD_PORT, ADXL_VDD_PIN); /* Enable VDD pin */
888  else GPIO_PinOutClear(ADXL_VDD_PORT, ADXL_VDD_PIN); /* Disable VDD pin */
889  }
890 }
891 
892 
893 /**************************************************************************//**
894  * @brief
895  * Soft reset accelerometer.
896  *
897  * @note
898  * This is a static method because it's only internally used in this file
899  * and called by other methods if necessary.
900  *****************************************************************************/
901 static void softResetADXL (void)
902 {
903  writeADXL(ADXL_REG_SOFT_RESET, 0x52); /* 0x52 = "R" */
904 }
905 
906 
907 /**************************************************************************//**
908  * @brief Check if the ID is correct.
909  *
910  * @note
911  * This is a static method because it's only internally used in this file
912  * and called by other methods if necessary.
913  *
914  * @return
915  * @li `true` - Correct ID returned.
916  * @li `false` - Incorrect ID returned.
917  *****************************************************************************/
918 static bool checkID_ADXL (void)
919 {
920  return (readADXL(ADXL_REG_DEVID_AD) == 0xAD);
921 }
922 
923 
924 /**************************************************************************//**
925  * @brief
926  * Convert sensor value in +-g range to mg value.
927  *
928  * @note
929  * Info found at http://ozzmaker.com/accelerometer-to-g/ @n
930  * This is a static method because it's only internally used in this file
931  * and called by other methods if necessary.
932  *
933  * @param[in] sensorValue
934  * Value in g-range returned by sensor.
935  *
936  * @return
937  * The calculated mg value.
938  *****************************************************************************/
939 static int32_t convertGRangeToGValue (int8_t sensorValue)
940 {
941  /* Explanation of the used numbers:
942  * - 255 = (-) 128 + 127
943  * - 2 = "+" & "-"
944  * - 1000 = "m"g */
945 
946  if (range == ADXL_RANGE_2G) return ((2*2*1000 / 255) * sensorValue);
947  else if (range == ADXL_RANGE_4G) return ((2*4*1000 / 255) * sensorValue);
948  else if (range == ADXL_RANGE_8G) return ((2*8*1000 / 255) * sensorValue);
949  else
950  {
951 
952 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
953  dbcrit("Range wrong, can't calculate mg value!");
954 #endif /* DEBUG_DBPRINT */
955 
956  error(27);
957 
958  /* Exit function */
959  return (0);
960  }
961 }
void ADXL_ackInterrupt(void)
Acknowledge the interrupt from the accelerometer.
Definition: ADXL362.c:230
void dbprint(char *message)
Print a string (char array) to USARTx.
Definition: dbprint.c:373
static void writeADXL(uint8_t address, uint8_t data)
Write an SPI byte to the accelerometer (8 bits) using a given address and specified data...
Definition: ADXL362.c:816
#define ADXL_CLK_PORT
Definition: pin_mapping.h:72
void dbinfoInt(char *message1, int32_t value, char *message2)
Print an info value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:567
#define ADXL_NCS_PIN
Definition: pin_mapping.h:75
#define ADXL_SPI
Definition: pin_mapping.h:66
#define ADXL_REG_THRESH_ACT_L
Definition: ADXL362.c:94
#define ADXL_VDD_PIN
Definition: pin_mapping.h:82
bool ADXL_getTriggered(void)
Getter for the ADXL_triggered variable.
Definition: ADXL362.c:216
#define ADXL_CLK_PIN
Definition: pin_mapping.h:73
static void resetHandlerADXL(void)
Soft reset accelerometer handler.
Definition: ADXL362.c:697
#define ADXL_REG_THRESH_ACT_H
Definition: ADXL362.c:95
All code for the ADXL362 accelerometer.
#define ADXL_VDD_PORT
Definition: pin_mapping.h:81
#define ADXL_MOSI_PORT
Definition: pin_mapping.h:68
bool ADXL_VDD_initialized
Definition: ADXL362.c:108
void ADXL_readValues(void)
Read and display "g" values forever with a 100ms interval.
Definition: ADXL362.c:515
#define ADXL_NCS_PORT
Definition: pin_mapping.h:74
MeasurementData_t data
Definition: main.c:189
void ADXL_enableMeasure(bool enabled)
Enable or disable measurement mode.
Definition: ADXL362.c:310
void dbwarnInt(char *message1, int32_t value, char *message2)
Print a warning value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:598
#define ADXL_REG_XDATA
Definition: ADXL362.c:87
#define ADXL_MISO_PIN
Definition: pin_mapping.h:71
int8_t XYZDATA[3]
Definition: ADXL362.c:106
#define ADXL_REG_FILTER_CTL
Definition: ADXL362.c:99
#define ADXL_REG_STATUS
Definition: ADXL362.c:90
void dbinfo(char *message)
Print an info string (char array) to USARTx and go to the next line.
Definition: dbprint.c:503
void ADXL_configODR(ADXL_ODR_t givenODR)
Configure the Output Data Rate (ODR).
Definition: ADXL362.c:411
ADXL_Range_t range
Definition: ADXL362.c:107
static void readADXL_XYZDATA(void)
Read the X-Y-Z data registers in the XYZDATA[] field using burst reads.
Definition: ADXL362.c:842
uint16_t ADXL_getCounter(void)
Getter for the ADXL_triggercounter variable.
Definition: ADXL362.c:178
static int32_t convertGRangeToGValue(int8_t sensorValue)
Convert sensor value in +-g range to mg value.
Definition: ADXL362.c:939
void ADXL_configActivity(uint8_t gThreshold)
Configure the accelerometer to work in (referenced) activity threshold mode.
Definition: ADXL362.c:468
void error(uint8_t number)
Error method.
Definition: util.c:131
void testADXL(void)
This method goes through all of the ODR settings to see the influence they have on power usage...
Definition: ADXL362.c:571
#define ADXL_REG_POWER_CTL
Definition: ADXL362.c:100
static void powerADXL(bool enabled)
Enable or disable the power to the accelerometer.
Definition: ADXL362.c:875
volatile uint16_t ADXL_triggercounter
Definition: ADXL362.c:105
Utility functionality.
#define ADXL_SPI_LOC
Definition: pin_mapping.h:67
static void initADXL_SPI(void)
Initialize USARTx in SPI mode according to the settings required by the accelerometer.
Definition: ADXL362.c:647
The pin definitions for the regular and custom Happy Gecko board.
#define ADXL_REG_INTMAP1
Definition: ADXL362.c:97
Delay functionality.
#define ADXL_MOSI_PIN
Definition: pin_mapping.h:69
#define ADXL_MISO_PORT
Definition: pin_mapping.h:70
void ADXL_configRange(ADXL_Range_t givenRange)
Configure the measurement range and store the selected one in a global variable for later (internal) ...
Definition: ADXL362.c:358
Enable or disable printing to UART with dbprint.
void delay(uint32_t msDelay)
Wait for a certain amount of milliseconds in EM2/3.
Definition: delay.c:124
void dbprintInt(int32_t value)
Print a number in decimal notation to USARTx.
Definition: dbprint.c:738
void ADXL_clearCounter(void)
Method to set the ADXL_triggercounter variable back to zero.
Definition: ADXL362.c:188
#define ADXL_REG_DEVID_AD
Definition: ADXL362.c:83
enum adxl_range ADXL_Range_t
#define ADXL_REG_ACT_INACT_CTL
Definition: ADXL362.c:96
enum adxl_odr ADXL_ODR_t
static uint8_t readADXL(uint8_t address)
Read an SPI byte from the accelerometer (8 bits) using a given address.
Definition: ADXL362.c:782
#define ADXL_REG_SOFT_RESET
Definition: ADXL362.c:93
void initADXL(void)
Initialize the accelerometer.
Definition: ADXL362.c:132
void ADXL_setTriggered(bool triggered)
Setter for the ADXL_triggered variable.
Definition: ADXL362.c:202
volatile bool ADXL_triggered
Definition: ADXL362.c:104
void ADXL_enableSPI(bool enabled)
Enable or disable the SPI pins and USART0/1 clock and peripheral to the accelerometer.
Definition: ADXL362.c:245
void dbcrit(char *message)
Print a critical error string (char array) in red to USARTx and go to the next line.
Definition: dbprint.c:539
void led(bool enabled)
Enable or disable the LED.
Definition: util.c:103
static bool checkID_ADXL(void)
Check if the ID is correct.
Definition: ADXL362.c:918
static void softResetADXL(void)
Soft reset accelerometer.
Definition: ADXL362.c:901