Contiki 3.x
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 #include "isr_compat.h"
48 
49 /* Callback pointers when interrupt occurs */
50 void (*accm_int1_cb)(uint8_t reg);
51 void (*accm_int2_cb)(uint8_t reg);
52 
53 process_event_t int1_event, int2_event;
54 
55 /* Bitmasks for the interrupts */
56 static uint16_t int1_mask = 0, int2_mask = 0;
57 
58 /* Keep track of when the interrupt was last seen in order to reduce the amount
59  of interrupts. Kind of like button debouncing. This can't be per int-pin, as
60  there can be several very different int per pin (eg tap && freefall). */
61 // XXX Not used now, only one global timer.
62 //static volatile clock_time_t ints_lasttime[] = {0, 0, 0, 0, 0, 0, 0, 0};
63 
64 /* Bitmasks and bit flag variable for keeping track of adxl345 status. */
65 enum ADXL345_STATUSTYPES {
66  /* must be a bit and not more, not using 0x00. */
67  INITED = 0x01,
68  RUNNING = 0x02,
69  STOPPED = 0x04,
70  LOW_POWER = 0x08,
71  AAA = 0x10, // available to extend this...
72  BBB = 0x20, // available to extend this...
73  CCC = 0x40, // available to extend this...
74  DDD = 0x80, // available to extend this...
75 };
76 static enum ADXL345_STATUSTYPES _ADXL345_STATUS = 0x00;
77 
78 /* Default values for adxl345 at startup. This will be sent to the adxl345 in a
79  stream at init to set it up in a default state */
80 static uint8_t adxl345_default_settings[] = {
81  /* Note, as the two first two bulks are to be written in a stream, they contain
82  the register address as first byte in that section. */
83  /* 0--14 are in one stream, start at ADXL345_THRESH_TAP */
84  ADXL345_THRESH_TAP, // XXX NB Register address, not register value!!
85  ADXL345_THRESH_TAP_DEFAULT,
86  ADXL345_OFSX_DEFAULT,
87  ADXL345_OFSY_DEFAULT,
88  ADXL345_OFSZ_DEFAULT,
89  ADXL345_DUR_DEFAULT,
90  ADXL345_LATENT_DEFAULT,
91  ADXL345_WINDOW_DEFAULT,
92  ADXL345_THRESH_ACT_DEFAULT,
93  ADXL345_THRESH_INACT_DEFAULT,
94  ADXL345_TIME_INACT_DEFAULT,
95  ADXL345_ACT_INACT_CTL_DEFAULT,
96  ADXL345_THRESH_FF_DEFAULT,
97  ADXL345_TIME_FF_DEFAULT,
98  ADXL345_TAP_AXES_DEFAULT,
99 
100  /* 15--19 start at ADXL345_BW_RATE */
101  ADXL345_BW_RATE, // XXX NB Register address, not register value!!
102  ADXL345_BW_RATE_DEFAULT,
103  ADXL345_POWER_CTL_DEFAULT,
104  ADXL345_INT_ENABLE_DEFAULT,
105  ADXL345_INT_MAP_DEFAULT,
106 
107  /* These two: 20, 21 write separately */
108  ADXL345_DATA_FORMAT_DEFAULT,
109  ADXL345_FIFO_CTL_DEFAULT
110 };
111 
112 
113 /*---------------------------------------------------------------------------*/
114 PROCESS(accmeter_process, "Accelerometer process");
115 /*---------------------------------------------------------------------------*/
116 /* Write to a register.
117  args:
118  reg register to write to
119  val value to write
120 */
121 
122 void
123 accm_write_reg(uint8_t reg, uint8_t val) {
124  uint8_t tx_buf[] = {reg, val};
125 
126  i2c_transmitinit(ADXL345_ADDR);
127  while (i2c_busy());
128  PRINTFDEBUG("I2C Ready to TX\n");
129 
130  i2c_transmit_n(2, tx_buf);
131  while (i2c_busy());
132  PRINTFDEBUG("WRITE_REG 0x%02X @ reg 0x%02X\n", val, reg);
133 }
134 /*---------------------------------------------------------------------------*/
135 /* Write several registers from a stream.
136  args:
137  len number of bytes to read
138  data pointer to where the data is read from
139 
140  First byte in stream must be the register address to begin writing to.
141  The data is then written from second byte and increasing. */
142 
143 void
144 accm_write_stream(uint8_t len, uint8_t *data) {
145  i2c_transmitinit(ADXL345_ADDR);
146  while (i2c_busy());
147  PRINTFDEBUG("I2C Ready to TX(stream)\n");
148 
149  i2c_transmit_n(len, data); // start tx and send conf reg
150  while (i2c_busy());
151  PRINTFDEBUG("WRITE_STR %u B to 0x%02X\n", len, data[0]);
152 }
153 
154 /*---------------------------------------------------------------------------*/
155 /* Read one register.
156  args:
157  reg what register to read
158  returns the value of the read register
159 */
160 
161 uint8_t
162 accm_read_reg(uint8_t reg) {
163  uint8_t retVal = 0;
164  uint8_t rtx = reg;
165  PRINTFDEBUG("READ_REG 0x%02X\n", reg);
166 
167  /* transmit the register to read */
168  i2c_transmitinit(ADXL345_ADDR);
169  while (i2c_busy());
170  i2c_transmit_n(1, &rtx);
171  while (i2c_busy());
172 
173  /* receive the data */
174  i2c_receiveinit(ADXL345_ADDR);
175  while (i2c_busy());
176  i2c_receive_n(1, &retVal);
177  while (i2c_busy());
178 
179  return retVal;
180 }
181 
182 /*---------------------------------------------------------------------------*/
183 /* Read several registers in a stream.
184  args:
185  reg what register to start reading from
186  len number of bytes to read
187  whereto pointer to where the data is saved
188 */
189 
190 void
191 accm_read_stream(uint8_t reg, uint8_t len, uint8_t *whereto) {
192  uint8_t rtx = reg;
193  PRINTFDEBUG("READ_STR %u B from 0x%02X\n", len, reg);
194 
195  /* transmit the register to start reading from */
196  i2c_transmitinit(ADXL345_ADDR);
197  while (i2c_busy());
198  i2c_transmit_n(1, &rtx);
199  while (i2c_busy());
200 
201  /* receive the data */
202  i2c_receiveinit(ADXL345_ADDR);
203  while (i2c_busy());
204  i2c_receive_n(len, whereto);
205  while (i2c_busy());
206 }
207 
208 /*---------------------------------------------------------------------------*/
209 /* Read an axis of the accelerometer (x, y or z). Return value is a signed 10 bit int.
210  The resolution of the acceleration measurement can be increased up to 13 bit, but
211  will change the data format of this read out. Refer to the data sheet if so is
212  wanted/needed. */
213 
214 int16_t
215 accm_read_axis(enum ADXL345_AXIS axis){
216  int16_t rd = 0;
217  uint8_t tmp[2];
218  if(axis > Z_AXIS){
219  return 0;
220  }
221  accm_read_stream(ADXL345_DATAX0 + axis, 2, &tmp[0]);
222  rd = (int16_t)(tmp[0] | (tmp[1]<<8));
223  return rd;
224 }
225 
226 /*---------------------------------------------------------------------------*/
227 /* Sets the g-range, ie the range the accelerometer measures (ie 2g means -2 to +2 g
228  on every axis). Possible values:
229  ADXL345_RANGE_2G
230  ADXL345_RANGE_4G
231  ADXL345_RANGE_8G
232  ADXL345_RANGE_16G
233  Example:
234  accm_set_grange(ADXL345_RANGE_4G);
235  */
236 
237 void
238 accm_set_grange(uint8_t grange){
239  if(grange > ADXL345_RANGE_16G) {
240  // invalid g-range.
241  PRINTFDEBUG("ADXL grange invalid: %u\n", grange);
242  return;
243  }
244  uint8_t tempreg = 0;
245 
246  /* preserve the previous contents of the register */
247  tempreg = (accm_read_reg(ADXL345_DATA_FORMAT) & 0xFC); // zero out the last two bits (grange)
248  tempreg |= grange; // set new range
249  accm_write_reg(ADXL345_DATA_FORMAT, tempreg);
250 }
251 
252 /*---------------------------------------------------------------------------*/
253 /* Init the accelerometer: ports, pins, registers, interrupts (none enabled), I2C,
254  default threshold values etc. */
255 
256 void
257 accm_init(void) {
258  if(!(_ADXL345_STATUS & INITED)){
259  PRINTFDEBUG("ADXL345 init\n");
260  _ADXL345_STATUS |= INITED;
261  accm_int1_cb = NULL;
262  accm_int2_cb = NULL;
263  int1_event = process_alloc_event();
264  int2_event = process_alloc_event();
265 
266  /* Set up ports and pins for interrups. */
267  ADXL345_DIR &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN);
268  ADXL345_SEL &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN);
269  ADXL345_SEL2 &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN);
270 
271  /* Set up ports and pins for I2C communication */
272  i2c_enable();
273 
274  /* set default register values. */
275  accm_write_stream(15, &adxl345_default_settings[0]);
276  accm_write_stream(5, &adxl345_default_settings[15]);
277  accm_write_reg(ADXL345_DATA_FORMAT, adxl345_default_settings[20]);
278  accm_write_reg(ADXL345_FIFO_CTL, adxl345_default_settings[21]);
279 
280  process_start(&accmeter_process, NULL);
281 
282  /* Enable msp430 interrupts on the two interrupt pins. */
283  dint();
284  ADXL345_IES &=~ (ADXL345_INT1_PIN | ADXL345_INT2_PIN); // low to high transition interrupts
285  ADXL345_IE |= (ADXL345_INT1_PIN | ADXL345_INT2_PIN); // enable interrupts
286  eint();
287  }
288 }
289 
290 /*---------------------------------------------------------------------------*/
291 /* Map interrupt (FF, tap, dbltap etc) to interrupt pin (IRQ_INT1, IRQ_INT2).
292  This must come after accm_init() as the registers will otherwise be overwritten. */
293 
294 void
295 accm_set_irq(uint8_t int1, uint8_t int2){
296  /* Set the corresponding interrupt mapping to INT1 or INT2 */
297  PRINTFDEBUG("IRQs set to INT1: 0x%02X IRQ2: 0x%02X\n", int1, int2);
298 
299  int1_mask = int1;
300  int2_mask = int2;
301 
302  accm_write_reg(ADXL345_INT_ENABLE, (int1 | int2));
303  accm_write_reg(ADXL345_INT_MAP, int2); // int1 bits are zeroes in the map register so this is for both ints
304 }
305 
306 /*---------------------------------------------------------------------------*/
307 #if 0
308 /* now unused code that is later supposed to be turned into keeping track of every
309  interrupt by themselves instead of only one per INT1/2 */
310 
311 /* XXX MUST HAVE some way of resetting the time so that we are not suppressing
312  erronous due to clock overflow.... XXX XXX XXX */
313 /* Table with back off time periods */
314 static volatile clock_time_t ints_backoffs[] = {ADXL345_INT_OVERRUN_BACKOFF, ADXL345_INT_WATERMARK_BACKOFF,
315  ADXL345_INT_FREEFALL_BACKOFF, ADXL345_INT_INACTIVITY_BACKOFF,
316  ADXL345_INT_ACTIVITY_BACKOFF, ADXL345_INT_DOUBLETAP_BACKOFF,
317  ADXL345_INT_TAP_BACKOFF, ADXL345_INT_DATAREADY_BACKOFF};
318 
319 /*---------------------------------------------------------------------------*/
320 /* Checks to see if an event occurred after backoff period (returns time period
321  past since) or not (returns 0) */
322 
323 static clocktime_t
324 backoff_passed(clocktime_t happenedAt, const clocktime_t backoff){
325  if(timenow-lasttime >= backoff) {
326  return 0;
327  } else {
328  return (timenow-lasttime);
329  }
330 }
331 #endif
332 /*---------------------------------------------------------------------------*/
333 /* Invoked after an interrupt happened. Reads the interrupt source reg at the
334  accelerometer, which resets the interrupts, and invokes the corresponding
335  callback. It passes the source register value so the callback can determine
336  what interrupt happened, if several interrupts are mapped to the same pin. */
337 
338 static void
339 poll_handler(void){
340  uint8_t ireg = 0;
341  ireg = accm_read_reg(ADXL345_INT_SOURCE);
342  //printf("0x%02X, 0x%02X, 0x%02X, 0x%02X\n", ireg, ireg2, int1_mask, int2_mask);
343 
344  /* Invoke callbacks for the corresponding interrupts */
345  if(ireg & int1_mask){
346  if(accm_int1_cb != NULL){
347  PRINTFDEBUG("INT1 cb invoked\n");
348  accm_int1_cb(ireg);
349  }
350  } else if(ireg & int2_mask){
351  if(accm_int2_cb != NULL){
352  PRINTFDEBUG("INT2 cb invoked\n");
353  accm_int2_cb(ireg);
354  }
355  }
356 }
357 
358 /*---------------------------------------------------------------------------*/
359 /* This process is sleeping until an interrupt from the accelerometer occurs, which
360  polls this process from the interrupt service routine. */
361 
362 PROCESS_THREAD(accmeter_process, ev, data) {
363  PROCESS_POLLHANDLER(poll_handler());
365  PROCESS_BEGIN();
366  while(1){
367  PROCESS_WAIT_EVENT_UNTIL(0); // should do nothing in while loop.
368  }
369  PROCESS_END();
370 }
371 
372 /*---------------------------------------------------------------------------*/
373 /* XXX This interrupt vector is shared with the interrupts from CC2420, so that
374  was moved here but should find a better home. XXX */
375 
376 #if 1
377 static struct timer suppressTimer1, suppressTimer2;
378 
379 ISR(PORT1, port1_isr)
380 {
381  ENERGEST_ON(ENERGEST_TYPE_IRQ);
382  /* ADXL345_IFG.x goes high when interrupt occurs, use to check what interrupted */
383  if ((ADXL345_IFG & ADXL345_INT1_PIN) && !(ADXL345_IFG & BV(CC2420_FIFOP_PIN))){
384  /* Check if this should be suppressed or not */
385  if(timer_expired(&suppressTimer1)) {
386  timer_set(&suppressTimer1, SUPPRESS_TIME_INT1);
387  ADXL345_IFG &= ~ADXL345_INT1_PIN; // clear interrupt flag
388  process_poll(&accmeter_process);
389  LPM4_EXIT;
390  }
391  } else if ((ADXL345_IFG & ADXL345_INT2_PIN) && !(ADXL345_IFG & BV(CC2420_FIFOP_PIN))){
392  /* Check if this should be suppressed or not */
393  if(timer_expired(&suppressTimer2)) {
394  timer_set(&suppressTimer2, SUPPRESS_TIME_INT2);
395  ADXL345_IFG &= ~ADXL345_INT2_PIN; // clear interrupt flag
396  process_poll(&accmeter_process);
397  LPM4_EXIT;
398  }
399  } else {
400  /* CC2420 interrupt */
401  if(cc2420_interrupt()) {
402  LPM4_EXIT;
403  }
404  }
405  ENERGEST_OFF(ENERGEST_TYPE_IRQ);
406 }
407 
408 /*---------------------------------------------------------------------------*/
409 
410 #endif
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
A timer.
Definition: timer.h:86
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition: timer.c:64
#define NULL
The null pointer.
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition: process.c:93
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
void i2c_enable(void)
Configure serial controller in I2C mode and set I2C speed.
Definition: i2c.c:52
Device drivers header file for adxl345 accelerometer in Zolertia Z1.
#define PROCESS_EXITHANDLER(handler)
Specify an action when a process exits.
Definition: process.h:254
#define PROCESS_POLLHANDLER(handler)
Specify an action when a process is polled.
Definition: process.h:242
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition: process.h:157
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
int timer_expired(struct timer *t)
Check if a timer has expired.
Definition: timer.c:121
I2C communication device driver header file for Zolertia Z1 sensor node.