Wire issue with ADXL345

Hi,

I have an arduino UNO with an accelerometer ADXL345, which works through the TWI bus.

Using the Enerlib library to sleep arduino, if it is slept in PowerDown mode, I can't wake it by an interruption from the accelerometer.

Only works (with an interruption) when:

  • Watchdog finishes every cycle but not when is completely slept.
  • If I disconnect the accelerometer, the arduino always awakes.
  • I'm working with internal interruption from digital pins. It's tested that it works fine, also with external ones.

Probably, the microcontroller disable the TWI bus when it is in PowerDown mode, and there is an issue there, blocking the accelerometer to send the interruption.

Could anyone help me, please??

Do you expect the Arduino to wake up from power down mode by a signal on the I2C bus? This only works if the Arduino is an I2C slave. The ADXL345 is not able to be an I2C master, so your setup won't work. Correct me if I got something wrong.

What's possible, is to connect the INT1 or INT2 of the ADXL345 to either INT0 or INT1 of the Arduino. The interrupt pins are able to wake an ATmega chip from power down sleep mode.

Hi,

the ADXL's INT1 pin is connected to pin 11 in the Arduino UNO, to wake up it with a PCINT (internal interruption). But instead of wake up the Arduino, it remains sleep until it wakes up by the watchdog.

//sda.-a4
//scl.-a5
#include <ADXL345.h>
#include <Enerlib.h> 
#include <Wire.h>
#include <PinChangeInt.h>
#include <PinChangeIntConfig.h>

#define PIN_ADXL_ 11
#define pin 13
#define MAX_VALUE       1023.0
#define Vcc 5
#define TiempoVibracion   900
#define NumeroVibraciones 5
#define TiempoEnvioDatos 5

boolean firstTime = true;    // If 'True' is the first time, then change to 'False'
volatile unsigned long firstSend;
unsigned long init_int_time = 0;
unsigned long last_int_time = 0;

ADXL345 adxl; //variable adxl is an instance of the ADXL345 library
int contAdx = 0;
unsigned long tiempoAdxActual = 0;
unsigned long tiempoAdxAnterior = 0;
unsigned long tiempoAdxResta = 0;

Energy energy;

byte interrupts;
unsigned long Tiempo = (TiempoEnvioDatos * 60) / 8;
unsigned long NumeroDesbordamientos = 0;
volatile int activo = LOW;  

//___________________________________________________________________________________________
  // WATCHDOG

ISR(WDT_vect) {
  NumeroDesbordamientos++;
  
  if(NumeroDesbordamientos > Tiempo) {
    activo = HIGH;
  }
}

//___________________________________________________________________________________________
void setup(void) {
    
  pinMode(pin, OUTPUT);

  pinMode(PIN_ADXL_, INPUT); 
  digitalWrite(PIN_ADXL_, HIGH);
  ConfigurarAdx();
  PCintPort::attachInterrupt(PIN_ADXL_, Adx, RISING);
  
  NumeroDesbordamientos = 0;  // reseteo contador
  activo = LOW;  // reseteo estado. Iniciar dormido.
  
  // Reference to 5V  
  analogReference(DEFAULT);
  
  Serial.begin(38400);

  WDTCSR = 0x00; // Deactivate Watchdog
  delay(1000);
  watchdog();  // configuracion del wacthdog
  
  activo = HIGH;
}
//___________________________________________________________________________________________
void loop(void) {  

  interrupts= adxl.getInterruptSource();
  
  if(activo) {
    WDTCSR = 0x00; // Deactivate Watchdog
    NumeroDesbordamientos = 0;  // reseteo contador
    
    Envio();
     
    activo = LOW;  
    WDTCSR = 0x71; // Reactivate Watchdog
  }

  energy.PowerDown();    //maximo ahorro de energia
}  
//___________________________________________________________________________________________
void watchdog() {
  MCUSR &= ~(1 << WDRF);
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  WDTCSR = 0x71;
} 
//___________________________________________________________________________________________
void Envio() { 
      Serial.print("\tAdxl345_Accel: ");
      Serial.println(contAdx);
}
//___________________________________________________________________________________________
void Adx()
{ 
  init_int_time = millis();
   
  if (init_int_time - last_int_time > 200) {
    tiempoAdxActual = millis();
    tiempoAdxResta = tiempoAdxActual - tiempoAdxAnterior;    // REVISAR
    tiempoAdxAnterior = tiempoAdxActual;
    
    if(tiempoAdxResta <= TiempoVibracion) {
      contAdx++;
      
      if(contAdx >= NumeroVibraciones) {
        activo = HIGH;
      }
    }
    else {
      contAdx = 0;
    }
  
  last_int_time = init_int_time; 
  } 
}
//___________________________________________________________________________________________
void ConfigurarAdx() {
  adxl.powerOn();
 
  //set activity/ inactivity thresholds (0-255)
  adxl.setActivityThreshold(75); //62.5mg per increment
  adxl.setInactivityThreshold(75); //62.5mg per increment
  adxl.setTimeInactivity(10); // how many seconds of no activity is inactive?
 
  //look of activity movement on this axes - 1 == on; 0 == off 
  adxl.setActivityX(1);
  adxl.setActivityY(1);
  adxl.setActivityZ(1);
 
  //look of inactivity movement on this axes - 1 == on; 0 == off
  adxl.setInactivityX(1);
  adxl.setInactivityY(1);
  adxl.setInactivityZ(1);
 
  //look of tap movement on this axes - 1 == on; 0 == off
  adxl.setTapDetectionOnX(1);
  adxl.setTapDetectionOnY(1);
  adxl.setTapDetectionOnZ(1);
 
  //set values for what is a tap, and what is a double tap (0-255)
  adxl.setTapThreshold(10); //62.5mg per increment
  adxl.setTapDuration(15); //625?s per increment
  adxl.setDoubleTapLatency(10); //1.25ms per increment
  adxl.setDoubleTapWindow(80); //1.25ms per increment
 
  //set values for what is considered freefall (0-255)
  adxl.setFreeFallThreshold(7); //(5 - 9) recommended - 62.5mg per increment
  adxl.setFreeFallDuration(45); //(20 - 70) recommended - 5ms per increment
 
  //setting all interupts to take place on int pin 1
  //I had issues with int pin 2, was unable to reset it
  adxl.setInterruptMapping( ADXL345_INT_SINGLE_TAP_BIT,   ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_DOUBLE_TAP_BIT,   ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_FREE_FALL_BIT,    ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_ACTIVITY_BIT,     ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_INACTIVITY_BIT,   ADXL345_INT1_PIN );
 
  //register interupt actions - 1 == on; 0 == off  
  adxl.setInterrupt( ADXL345_INT_SINGLE_TAP_BIT, 1);
  adxl.setInterrupt( ADXL345_INT_DOUBLE_TAP_BIT, 1);
  adxl.setInterrupt( ADXL345_INT_FREE_FALL_BIT,  0);
  adxl.setInterrupt( ADXL345_INT_ACTIVITY_BIT,   0);
  adxl.setInterrupt( ADXL345_INT_INACTIVITY_BIT, 0);
}

Have you checked if you really get an interrupt in INT1 of the ADXL?

  • I'm working with internal interruption from digital pins. It's tested that it works fine, also with external ones.

What does that mean? What's the distinction between internal and external interruption?

You're aware that the interrupt line is only activated if a tap is detected, are you?

if the accelerometer device has an "interrupt" output, that doesn't go through the I2C bus, that would be a separate
wired connection from the "interrupt" output pin on the accelerometer chip or breakout board, to some pin on the
Arduino capable of recognising the interrupt state change.

That's right.

ADXL's INT1 pin is connected to digital pin of the Arduino board, so I can use an internal interrupt and wake up the MCU when a tap occurs.

I wrote down that interruptions occurs when MCU is awake, and when WDT finishes each cycle, but when is completely asleep, interruption doesn't 'jump'.

I'm kinda new to the AVRs, so forgive me if I say something stupid. You seem to be wanting it to wake up by using a PCINTx (interrupt on pin change) instead of INT0 or INT1 which are on fixed pins and can't be reassigned. The datasheet says that if the external device attempting to wake up the AVR doesn't hold the signal long enough for the AVR to wake up and get going, that the chip will wake up but NOT generate an interrupt. Could this be what you are seeing? What happens if you force it by just touching a wire to the pin?

I wrote down that interruptions occurs when MCU is awake, and when WDT finishes each cycle, but when is completely asleep, interruption doesn't 'jump'.

What does "doesn't jump" mean? Does it mean the ADXL345 isn't pulling the INT1 high for the interrupt? Or does it mean it pulls the pin high but the ATmega doesn't react on it?

How is your I2C bus wired? Do you use internal pull-ups or external ones? There's a difference between the two setups when the MCU goes asleep (start condition for the ADXL345).

Hi,

and thanks all!

@afremont:

  • If I force the interrupt by a wire, it works perfectly.
  • With MCU always awake, ADXL's interrupt works fine.

@pylon:

  • 'Doesn't jump', I mean ATmega doesn't react on it.
  • I'm using internal pull-ups. Could this be the problem?

I'm still trying to discover why is this happening, although I'm going to some of these things.

  • 'Doesn't jump', I mean ATmega doesn't react on it.

That's strange because that denies my theory below.

  • I'm using internal pull-ups. Could this be the problem?

That could be a problem.
Using the internal pullups, the I2C bus will go in a floating state when the ATmega goes asleep. It may set the ADXL into any state, that's why I assumed that the INT1 will never be activated in this case (see above). I would install external 4k7 pullups and try again. As long as there's no activity on the bus this shouldn't consume much power.

The interrupt from the accelerometer chip or breakout board is NOT using the I2C bus. If you use it,
it is connected directly to one of the digital pins of the arduino.

The interrupt from the accelerometer chip or breakout board is NOT using the I2C bus. If you use it,
it is connected directly to one of the digital pins of the arduino.

This is clear already, there's a separate line (INT1) that has to go to a GPIO on the Arduino, we discussed this earlier in the thread.

But the whole chip is controlled by the I2C bus. If the OP is using the internal pull-ups for driving the I2C bus, the bus goes to a floating, not defined state when the ATmega goes asleep. Do you know how the ADXL345 reacts on that? Me not, because it's probably also not defined. That's why I suggested using external pull-ups.

I agree with pylon, put in the external pull-ups. If the I2C inputs float, it's anyone's guess as to what might happen. Chances are though that whatever happens, won't do it consistently. :wink:

Also, I want to reiterate that the CPU won't call the ISR unless the int-on-change pin still reflects the change. It appears that the ADXL345 will hold the INT output pin high until the internal registers are read. This should allow for any length of wake-up time the CPU needs.

Hi,

thanks to all!!

With an external pull-up, it works!!

I have to verify it better, but for the first time (with a 4k7 resistor) it is working.

Regards!