Contiki 2.5
adxl345.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, Swedish Institute of Computer Science.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * Device drivers for adxl345 accelerometer in Zolertia Z1.
36  * \author
37  * Marcus Lundén, SICS <mlunden@sics.se>
38  * Enric M. Calvo, Zolertia <ecalvo@zolertia.com>
39  */
40 
41 
42 #include <stdio.h>
43 #include "contiki.h"
44 #include "adxl345.h"
45 #include "cc2420.h"
46 #include "i2cmaster.h"
47 
48 /* Callback pointers when interrupt occurs */
49 extern void (*accm_int1_cb)(u8_t reg);
50 extern void (*accm_int2_cb)(u8_t reg);
51 
52 /* Bitmasks for the interrupts */
53 static uint16_t int1_mask = 0, int2_mask = 0;
54 
55 /* Keep track of when the interrupt was last seen in order to reduce the amount
56  of interrupts. Kind of like button debouncing. This can't be per int-pin, as
57  there can be several very different int per pin (eg tap && freefall). */
58 // XXX Not used now, only one global timer.
59 //static volatile clock_time_t ints_lasttime[] = {0, 0, 0, 0, 0, 0, 0, 0};
60 
61 /* Bitmasks and bit flag variable for keeping track of adxl345 status. */
62 enum ADXL345_STATUSTYPES {
63  /* must be a bit and not more, not using 0x00. */
64  INITED = 0x01,
65  RUNNING = 0x02,
66  STOPPED = 0x04,
67  LOW_POWER = 0x08,
68  AAA = 0x10, // available to extend this...
69  BBB = 0x20, // available to extend this...
70  CCC = 0x40, // available to extend this...
71  DDD = 0x80, // available to extend this...
72 };
73 static enum ADXL345_STATUSTYPES _ADXL345_STATUS = 0x00;
74 
75 /* Default values for adxl345 at startup. This will be sent to the adxl345 in a
76  stream at init to set it up in a default state */
77 static uint8_t adxl345_default_settings[] = {
78  /* Note, as the two first two bulks are to be written in a stream, they contain
79  the register address as first byte in that section. */
80  /* 0--14 are in one stream, start at ADXL345_THRESH_TAP */
81  ADXL345_THRESH_TAP, // XXX NB Register address, not register value!!
82  ADXL345_THRESH_TAP_DEFAULT,
83  ADXL345_OFSX_DEFAULT,
84  ADXL345_OFSY_DEFAULT,
85  ADXL345_OFSZ_DEFAULT,
86  ADXL345_DUR_DEFAULT,
87  ADXL345_LATENT_DEFAULT,
88  ADXL345_WINDOW_DEFAULT,
89  ADXL345_THRESH_ACT_DEFAULT,
90  ADXL345_THRESH_INACT_DEFAULT,
91  ADXL345_TIME_INACT_DEFAULT,
92  ADXL345_ACT_INACT_CTL_DEFAULT,
93  ADXL345_THRESH_FF_DEFAULT,
94  ADXL345_TIME_FF_DEFAULT,
95  ADXL345_TAP_AXES_DEFAULT,
96 
97  /* 15--19 start at ADXL345_BW_RATE */
98  ADXL345_BW_RATE, // XXX NB Register address, not register value!!
99  ADXL345_BW_RATE_DEFAULT,
100  ADXL345_POWER_CTL_DEFAULT,
101  ADXL345_INT_ENABLE_DEFAULT,
102  ADXL345_INT_MAP_DEFAULT,
103 
104  /* These two: 20, 21 write separately */
105  ADXL345_DATA_FORMAT_DEFAULT,
106  ADXL345_FIFO_CTL_DEFAULT
107 };
108 
109 
110 /*---------------------------------------------------------------------------*/
111 PROCESS(accmeter_process, "Accelerometer process");
112 /*---------------------------------------------------------------------------*/
113 /* Write to a register.
114  args:
115  reg register to write to
116  val value to write
117 */
118 
119 void
120 accm_write_reg(u8_t reg, u8_t val) {
121  u8_t tx_buf[] = {reg, val};
122 
123  i2c_transmitinit(ADXL345_ADDR);
124  while (i2c_busy());
125  PRINTFDEBUG("I2C Ready to TX\n");
126 
127  i2c_transmit_n(2, tx_buf);
128  while (i2c_busy());
129  PRINTFDEBUG("WRITE_REG 0x%02X @ reg 0x%02X\n", val, reg);
130 }
131 /*---------------------------------------------------------------------------*/
132 /* Write several registers from a stream.
133  args:
134  len number of bytes to read
135  data pointer to where the data is read from
136 
137  First byte in stream must be the register address to begin writing to.
138  The data is then written from second byte and increasing. */
139 
140 void
141 accm_write_stream(u8_t len, u8_t *data) {
142  i2c_transmitinit(ADXL345_ADDR);
143  while (i2c_busy());
144  PRINTFDEBUG("I2C Ready to TX(stream)\n");
145 
146  i2c_transmit_n(len, data); // start tx and send conf reg
147  while (i2c_busy());
148  PRINTFDEBUG("WRITE_STR %u B to 0x%02X\n", len, data[0]);
149 }
150 
151 /*---------------------------------------------------------------------------*/
152 /* Read one register.
153  args:
154  reg what register to read
155  returns the value of the read register
156 */
157 
158 u8_t
159 accm_read_reg(u8_t reg) {
160  u8_t retVal = 0;
161  u8_t rtx = reg;
162  PRINTFDEBUG("READ_REG 0x%02X\n", reg);
163 
164  /* transmit the register to read */
165  i2c_transmitinit(ADXL345_ADDR);
166  while (i2c_busy());
167  i2c_transmit_n(1, &rtx);
168  while (i2c_busy());
169 
170  /* receive the data */
171  i2c_receiveinit(ADXL345_ADDR);
172  while (i2c_busy());
173  i2c_receive_n(1, &retVal);
174  while (i2c_busy());
175 
176  return retVal;
177 }
178 
179 /*---------------------------------------------------------------------------*/
180 /* Read several registers in a stream.
181  args:
182  reg what register to start reading from
183  len number of bytes to read
184  whereto pointer to where the data is saved
185 */
186 
187 void
188 accm_read_stream(u8_t reg, u8_t len, u8_t *whereto) {
189  u8_t rtx = reg;
190  PRINTFDEBUG("READ_STR %u B from 0x%02X\n", len, reg);
191 
192  /* transmit the register to start reading from */
193  i2c_transmitinit(ADXL345_ADDR);
194  while (i2c_busy());
195  i2c_transmit_n(1, &rtx);
196  while (i2c_busy());
197 
198  /* receive the data */
199  i2c_receiveinit(ADXL345_ADDR);
200  while (i2c_busy());
201  i2c_receive_n(len, whereto);
202  while (i2c_busy());
203 }
204 
205 /*---------------------------------------------------------------------------*/
206 /* Read an axis of the accelerometer (x, y or z). Return value is a signed 10 bit int.
207  The resolution of the acceleration measurement can be increased up to 13 bit, but
208  will change the data format of this read out. Refer to the data sheet if so is
209  wanted/needed. */
210 
211 int16_t
212 accm_read_axis(enum ADXL345_AXIS axis){
213  int16_t rd = 0;
214  u8_t tmp[2];
215  if(axis > Z_AXIS){
216  return 0;
217  }
218  accm_read_stream(ADXL345_DATAX0 + axis, 2, &tmp[0]);
219  rd = (int16_t)(tmp[0] | (tmp[1]<<8));
220  return rd;
221 }
222 
223 /*---------------------------------------------------------------------------*/
224 /* Sets the g-range, ie the range the accelerometer measures (ie 2g means -2 to +2 g
225  on every axis). Possible values:
226  ADXL345_RANGE_2G
227  ADXL345_RANGE_4G
228  ADXL345_RANGE_8G
229  ADXL345_RANGE_16G
230  Example:
231  accm_set_grange(ADXL345_RANGE_4G);
232  */
233 
234 void
235 accm_set_grange(u8_t grange){
236  if(grange > ADXL345_RANGE_16G) {
237  // invalid g-range.
238  PRINTFDEBUG("ADXL grange invalid: %u\n", grange);
239  return;
240  }
241  u8_t tempreg = 0;
242 
243  /* preserve the previous contents of the register */
244  tempreg = (accm_read_reg(ADXL345_DATA_FORMAT) & 0xFC); // zero out the last two bits (grange)
245  tempreg |= grange; // set new range
246  accm_write_reg(ADXL345_DATA_FORMAT, tempreg);
247 }
248 
249 /*---------------------------------------------------------------------------*/
250 /* Init the accelerometer: ports, pins, registers, interrupts (none enabled), I2C,
251  default threshold values etc. */
252 
253 void
254 accm_init(void) {
255  if(!(_ADXL345_STATUS & INITED)){
256  PRINTFDEBUG("ADXL345 init\n");
257  _ADXL345_STATUS |= INITED;
258  accm_int1_cb = NULL;
259  accm_int2_cb = NULL;
260  int1_event = process_alloc_event();
261  int2_event = process_alloc_event();
262 
263  /* Set up ports and pins for interrups. */
264  ADXL345_DIR &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN);
265  ADXL345_SEL &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN);
266  ADXL345_SEL2 &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN);
267 
268  /* Set up ports and pins for I2C communication */
269  i2c_enable();
270 
271  /* set default register values. */
272  accm_write_stream(15, &adxl345_default_settings[0]);
273  accm_write_stream(5, &adxl345_default_settings[15]);
274  accm_write_reg(ADXL345_DATA_FORMAT, adxl345_default_settings[20]);
275  accm_write_reg(ADXL345_FIFO_CTL, adxl345_default_settings[21]);
276 
277  process_start(&accmeter_process, NULL);
278 
279  /* Enable msp430 interrupts on the two interrupt pins. */
280  dint();
281  ADXL345_IES &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN); // low to high transition interrupts
282  ADXL345_IE |= (ADXL345_INT1_PIN | ADXL345_INT2_PIN); // enable interrupts
283  eint();
284  }
285 }
286 
287 /*---------------------------------------------------------------------------*/
288 /* Map interrupt (FF, tap, dbltap etc) to interrupt pin (IRQ_INT1, IRQ_INT2).
289  This must come after accm_init() as the registers will otherwise be overwritten. */
290 
291 void
292 accm_set_irq(uint8_t int1, uint8_t int2){
293  /* Set the corresponding interrupt mapping to INT1 or INT2 */
294  PRINTFDEBUG("IRQs set to INT1: 0x%02X IRQ2: 0x%02X\n", int1, int2);
295 
296  int1_mask = int1;
297  int2_mask = int2;
298 
299  accm_write_reg(ADXL345_INT_ENABLE, (int1 | int2));
300  accm_write_reg(ADXL345_INT_MAP, int2); // int1 bits are zeroes in the map register so this is for both ints
301 }
302 
303 /*---------------------------------------------------------------------------*/
304 #if 0
305 /* now unused code that is later supposed to be turned into keeping track of every
306  interrupt by themselves instead of only one per INT1/2 */
307 
308 /* XXX MUST HAVE some way of resetting the time so that we are not suppressing
309  erronous due to clock overflow.... XXX XXX XXX */
310 /* Table with back off time periods */
311 static volatile clock_time_t ints_backoffs[] = {ADXL345_INT_OVERRUN_BACKOFF, ADXL345_INT_WATERMARK_BACKOFF,
312  ADXL345_INT_FREEFALL_BACKOFF, ADXL345_INT_INACTIVITY_BACKOFF,
313  ADXL345_INT_ACTIVITY_BACKOFF, ADXL345_INT_DOUBLETAP_BACKOFF,
314  ADXL345_INT_TAP_BACKOFF, ADXL345_INT_DATAREADY_BACKOFF};
315 
316 /*---------------------------------------------------------------------------*/
317 /* Checks to see if an event occurred after backoff period (returns time period
318  past since) or not (returns 0) */
319 
320 static clocktime_t
321 backoff_passed(clocktime_t happenedAt, const clocktime_t backoff){
322  if(timenow-lasttime >= backoff) {
323  return 0;
324  } else {
325  return (timenow-lasttime);
326  }
327 }
328 #endif
329 /*---------------------------------------------------------------------------*/
330 /* Invoked after an interrupt happened. Reads the interrupt source reg at the
331  accelerometer, which resets the interrupts, and invokes the corresponding
332  callback. It passes the source register value so the callback can determine
333  what interrupt happened, if several interrupts are mapped to the same pin. */
334 
335 static void
336 poll_handler(void){
337  uint8_t ireg = 0;
338  ireg = accm_read_reg(ADXL345_INT_SOURCE);
339  //printf("0x%02X, 0x%02X, 0x%02X, 0x%02X\n", ireg, ireg2, int1_mask, int2_mask);
340 
341  /* Invoke callbacks for the corresponding interrupts */
342  if(ireg & int1_mask){
343  if(accm_int1_cb != NULL){
344  PRINTFDEBUG("INT1 cb invoked\n");
345  accm_int1_cb(ireg);
346  }
347  } else if(ireg & int2_mask){
348  if(accm_int2_cb != NULL){
349  PRINTFDEBUG("INT2 cb invoked\n");
350  accm_int2_cb(ireg);
351  }
352  }
353 }
354 
355 /*---------------------------------------------------------------------------*/
356 /* This process is sleeping until an interrupt from the accelerometer occurs, which
357  polls this process from the interrupt service routine. */
358 
359 PROCESS_THREAD(accmeter_process, ev, data) {
360  PROCESS_POLLHANDLER(poll_handler());
362  PROCESS_BEGIN();
363  while(1){
364  PROCESS_WAIT_EVENT_UNTIL(0); // should do nothing in while loop.
365  }
366  PROCESS_END();
367 }
368 
369 /*---------------------------------------------------------------------------*/
370 /* XXX This interrupt vector is shared with the interrupts from CC2420, so that
371  was moved here but should find a better home. XXX */
372 
373 #if 1
374 static struct timer suppressTimer1, suppressTimer2;
375 
376 #ifdef __IAR_SYSTEMS_ICC__
377 #pragma vector=PORT1_VECTOR
378 __interrupt void
379 #else
380 interrupt (PORT1_VECTOR)
381 #endif
382 port1_isr (void) {
383  ENERGEST_ON(ENERGEST_TYPE_IRQ);
384  /* ADXL345_IFG.x goes high when interrupt occurs, use to check what interrupted */
385  if ((ADXL345_IFG & ADXL345_INT1_PIN) && !(ADXL345_IFG & BV(CC2420_FIFOP_PIN))){
386  /* Check if this should be suppressed or not */
387  if(timer_expired(&suppressTimer1)) {
388  timer_set(&suppressTimer1, SUPPRESS_TIME_INT1);
389  ADXL345_IFG &= ~ADXL345_INT1_PIN; // clear interrupt flag
390  process_poll(&accmeter_process);
391  LPM4_EXIT;
392  }
393  } else if ((ADXL345_IFG & ADXL345_INT2_PIN) && !(ADXL345_IFG & BV(CC2420_FIFOP_PIN))){
394  /* Check if this should be suppressed or not */
395  if(timer_expired(&suppressTimer2)) {
396  timer_set(&suppressTimer2, SUPPRESS_TIME_INT2);
397  ADXL345_IFG &= ~ADXL345_INT2_PIN; // clear interrupt flag
398  process_poll(&accmeter_process);
399  LPM4_EXIT;
400  }
401  } else {
402  /* CC2420 interrupt */
403  if(cc2420_interrupt()) {
404  LPM4_EXIT;
405  }
406  }
407  ENERGEST_OFF(ENERGEST_TYPE_IRQ);
408 }
409 
410 /*---------------------------------------------------------------------------*/
411 
412 #endif