ADXL343 IMU: DATA_READY interrupt won't go high again after read

Hello,

I would like to read the ADXL343 only when the sensor has new data ready to be read. I have no problem reading the sensor, but once I try to implement the interrupt the following happens:

  1. upon start, the sensor senses and puts the values in the read register
  2. this also sets the INT1 pin to HIGH
  3. microcontroller makes a read of the sensor via I2C
  4. upon being read, the sensor sets INT1 to LOW
  5. code gets stuck in while loop waiting for INT1 to go HIGH again

that's it... the sensor will not set INT1 to HIGH again. I confirm this with a scope.

Do I need to send the INT_ENABLE command after every read? That seems to defeat the purpose if I'm trying to speed things up by only reading when data has been updated.

setup is with a custom AtTiny84 board...

I'm missing your code!

Depending on the settings (p.e. FIFO mode) you have to do more than the above list to get another interrupt.

Did you check the INT_SOURCE register if there is still pending data?

Hi Pylon,

Thanks for the response! The code is so long I was hesitant to post it; I use a Bit-Bang approach that has generally been working fine. Anyways, the INT1 pin goes high in setup until I press the button. Then it goes to the main loop and it no longer triggers, presumably after the first read clears it. I bypass the FIFO explicitly, so I didn't think that was the issue.

#include <TinyWireS.h>
#include <EEPROM.h>
#include <avr/io.h>
#include <util/delay.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define input(directions,pin) (directions &= (~pin)) // set port direction for input
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin

#define I2C_slave_address 0x53 // ADXL345 alt address
#define I2C_delay() _delay_us(5)

#define SCL_pin (1 << PA3)
#define SCL_pins PINA
#define SCL_port PORTA
#define SCL_direction DDRA
#define SDA_pin (1 << PA2)
#define SDA_pins PINA
#define SDA_port PORTA
#define SDA_direction DDRA
#define LED 8
#define buttonPin 7
#define INT2 1
#define INT1 0

unsigned char data[6];
unsigned char cmd[6];
unsigned char ret;

unsigned char tiny_slave_addr = 0x26;

void setup() {
  EEPROM.write(10, tiny_slave_addr);

  CLKPR = (1 << CLKPCE);
  CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

  pinMode(LED, OUTPUT);
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);   //set pullups
  pinMode(INT1, INPUT);
  digitalWrite(INT1, LOW);   //set pullups

  I2C_init();

  cmd[0] = 0x2D; // POWER_CTL register
  cmd[1] = 8; // turn on measure bit
  ret = I2C_master_write(cmd, 2, I2C_slave_address);
  
  cmd[0] = 0x31; // format register
  cmd[1] = 11; // set +-16g range
  ret = I2C_master_write(cmd, 2, I2C_slave_address);
  
  cmd[0] = 0x38; // FIFO control register
  cmd[1] = 0; // FIFO bypass
  ret = I2C_master_write(cmd, 2, I2C_slave_address);

  cmd[0] = 0x2F; // interrupt map Register
  cmd[1] = 0; //  map DataReady interrupt to pin INT1
  ret = I2C_master_write(cmd, 2, I2C_slave_address);

  cmd[0] = 0x2E; // interrupt enable register
  cmd[1] = 128; // enable DataReady interrupt
  ret = I2C_master_write(cmd, 2, I2C_slave_address);


  while (digitalRead(buttonPin) == HIGH) {}
  
}

void loop() {
  readVals();
}

void readVals() {
  while(digitalRead(INT1) == LOW){}
  cmd[0] = 0x32; // X0 register
  ret = I2C_master_write(cmd, 1, I2C_slave_address);
  ret = I2C_master_read(data, 6, I2C_slave_address);
}


void SCL_write(char bit) {
  if (bit == 0) {
    output(SCL_direction, SCL_pin);
    clear(SCL_port, SCL_pin);
  }
  else {
    input(SCL_direction, SCL_pin);
    while (pin_test(SCL_pins, SCL_pin) == 0); // check for clock stretching
  }
}

void SDA_write(char bit) {
  if (bit == 0) {
    output(SDA_direction, SDA_pin);
    clear(SDA_port, SDA_pin);
  }
  else
    input(SDA_direction, SDA_pin);
}

void I2C_init() {
  SDA_write(1);
  SCL_write(1);
}

char I2C_master_write_byte(unsigned char byte) {

  unsigned char bit;

  for (bit = 0; bit < 8; ++bit) {
    if ((byte & 0x80) == 0)
      SDA_write(0);
    else
      SDA_write(1);
    SCL_write(1);
    I2C_delay();
    SCL_write(0);
    I2C_delay();
    byte <<= 1;
  }

  SDA_write(1);
  SCL_write(1);
  I2C_delay();
  if (pin_test(SDA_pins, SDA_pin) != 0) {
    return 1;
  }

  SCL_write(0);
  I2C_delay();
  return 0;
}

char I2C_master_write(unsigned char* data, unsigned char nbytes, unsigned char slave_address) {

  unsigned char index, ret, slave_address_write;

  SDA_write(0);
  I2C_delay();
  SCL_write(0);
  I2C_delay();

  slave_address_write = slave_address << 1;
  if (I2C_master_write_byte(slave_address_write) != 0)
    return 1;

  for (index = 0; index < nbytes; ++index) {
    ret = I2C_master_write_byte(data[index]);
    if (ret != 0)
      break;

  }
  SCL_write(1);
  I2C_delay();
  SDA_write(1);
  I2C_delay();
  return ret;
}

void I2C_master_read_byte(unsigned char* data, unsigned char index, unsigned char nbytes) {

  unsigned char byte, bit;
  SDA_write(1);
  byte = 0;

  for (bit = 0; bit < 8; ++bit)  {
    SCL_write(1);
    I2C_delay();
    if (pin_test(SDA_pins, SDA_pin) != 0)
      byte |= (1 << (7 - bit));
    SCL_write(0);
    I2C_delay();
  }
  data[index] = byte;
  if (index < (nbytes - 1)) {
    SDA_write(0);
    SCL_write(1);
    I2C_delay();
    SCL_write(0);
    SDA_write(1);
    I2C_delay();
  }
  else {
    SDA_write(1);
    SCL_write(1);
    I2C_delay();
    SCL_write(0);
    I2C_delay();
  }
}

char I2C_master_read(unsigned char* data, unsigned char nbytes, unsigned char slave_address) {

  unsigned char index, slave_address_read;
  //
  // send start
  //
  SDA_write(0);
  I2C_delay();
  SCL_write(0);
  I2C_delay();
  //
  // send slave address
  //
  slave_address_read = (slave_address << 1) + 1;
  if (I2C_master_write_byte(slave_address_read) == 1)
    //
    // no ACK, return 1
    //
    return 1;
  //
  // loop over bytes
  //
  for (index = 0; index < nbytes; ++index)
    I2C_master_read_byte(data, index, nbytes);
  //
  // send stop
  //
  SCL_write(1);
  I2C_delay();
  SDA_write(1);
  I2C_delay();
  return 0;
}

A related question I have is, is there a good way to tell the Master Master(a Rpi) communicating via I2C with my Tiny84 when there is data available, other than just not sending an ACK and waiting for the pi to timeout?

thanks again for the help!

A related question I have is, is there a good way to tell the Master Master(a Rpi) communicating via I2C with my Tiny84 when there is data available, other than just not sending an ACK and waiting for the pi to timeout?

Bad idea. Why do you think you need the Tiny if there is a Raspberry Pi that can ask the sensor directly?

I use a Bit-Bang approach that has generally been working fine.

That might work until the sensor stretches the clock (which it's allowed to do). If you have I2C in hardware why do you fiddle around with bad software emulations?

pylon:
Why do you think you need the Tiny if there is a Raspberry Pi that can ask the sensor directly?

I use the AtTiny's to go between the sensors and the pi because I need up to 12 sensors on a single bus. The tiny's allow me to address them individually.

pylon:
If you have I2C in hardware why do you fiddle around with bad software emulations?

Since I'm already using the I2C/TWI for communication with the pi, I put the sensor on a different set of pins to avoid confusing signals. the Bit-Bang approach allows for clock stretching and has been very reliable.

I appreciate your response. I'm glad to provide justification for my choices, but it's not that relevant to my original questions.

Anyways, I looked at it again with a scope, and it looks like the DATA_READY interrupt only goes high when the I2C clock and data lines go low to start a transmission. I guess I might have to start a transmission, check for the interrupt, and break if it doesn't go HIGH.

This seems counterintuitive to have to start a transmission to find out if it should start a transmission... Is there any way to confirm the timing on the sensor? I can't see anything in the datasheet about when the INT is triggered.

I use the AtTiny's to go between the sensors and the pi because I need up to 12 sensors on a single bus. The tiny's allow me to address them individually.

But 12 Tinys on the bus each asking a sensor will collapse the bus. How do you plan to avoid that Tiny1 is talking to it's sensor while Tiny2 is doing the same?

The solution is not a Tiny in between but a bus multiplexer like the PCA4548A. Together with the address pin of the ADXL343 you can address up to 16 sensors.
A single bus probably will fail for other reasons too: the complete bus length must be less than 50cm. Connecting 12 sensors doesn't leave much wire for every sensor...
With the multiplexer every sub-bus may have a length up to 50cm. But keep in mind that the 50cm are only valid in a safe environment. If you have sources of electrical noise (p.e. motors, solenoids, etc.) this will be shortened considerably.

This seems counterintuitive to have to start a transmission to find out if it should start a transmission.

I doubt that this is caused so, at least the datasheet says something different. The datasheet says that the INT is triggered when the data changes, given the configuration is correct. The bit-bang implementation doesn't check for any error condition nor clock stretching, so I wouldn't trust it.

pylon:
But 12 Tinys on the bus each asking a sensor will collapse the bus. How do you plan to avoid that Tiny1 is talking to it's sensor while Tiny2 is doing the same?

this is why I bitbang. The tiny's are on a bus with the Pi as master, and then each Tiny has a separate bus to its respective sensor. As far as the length thing goes, I know this is a problem, but so far I'm running 3m ribbon cable and it seems pretty stable. Occasionally I have to restart when I get errors, but it beats a spaghetti of wires running all over the place. If it really doesn't work, I would perhaps do some sort of cascade where the Tiny's gather data from the adjacent one to pass data down the line.

I am guessing the interrupt wont trigger if the SDA or SCL line is held high. I haven't tested yet which one. I did alter the script to simulate an I2C start and stop, and that allows the INT to trigger, after which I set a flag to read the data. Like this:

void checkForData() {
  SDA_write(0);
  I2C_delay();
  SCL_write(0);
  I2C_delay();
  SCL_write(1);
  I2C_delay();
  SDA_write(1);
  if (digitalRead(INT1) == HIGH) readReady = true;

}

Next I will just try holding them both LOW unitl I (hopefully) get the INT to go HIGH.