ldab / KXTJ3-1057

KXTJ3-1057 Tri-axis Digital Accelerometer - Arduino Library
MIT License
14 stars 8 forks source link

Interrupt isn't working #4

Closed gitvita28 closed 1 year ago

gitvita28 commented 1 year ago

Am running the same exact code (the example code) with interrupt pin on GPIO32, but nothing's happening! Does your interrupt work?

ldab commented 1 year ago

I currently don’t have the hardware and can’t confirm, will leave the issue open in case someone sees it.

pbdaran commented 1 year ago

The interrupt is always high or low based on the initial configuration, irrespective of the threshold set. I suspect the interrupt is latched but not cleared. The interrupt will be cleared if we read INT_REL register. I'm not sure if it is happening.

ldab commented 1 year ago

@nomakewan since you seem active on this, are you able to check this issue?

nomakewan commented 1 year ago

I would be more than happy to check, but as I don't use interrupts in the code I'm working with, I would need a way to replicate the issue at hand and confirm the findings.

@pbdaran @gitvita28 Could you walk me through what I would need to do to inspect this issue?

pbdaran commented 1 year ago

@nomakewan You can simply run the example program. At the beginning, keep the sensor in the idle position and read the signal from pin 5 (INT) of the sensor. If the interrupt polarity is set to HIGH in the program, the expectation is that the pin 5 data should be LOW. When we give movement to the sensor and it crosses the interrupt threshold, the pin 5 must go HIGH. The problem we are facing is that the pin 5 signal is not changing. It is stuck at the same level as the initial condition.

nomakewan commented 1 year ago

Okay, I'm going to have to bust out the breadboard for that one. As soon as I have more information I will let you all know.

nomakewan commented 1 year ago

I decided to do a code review before I even try the breadboard (since my lab setup right now is configured for a totally different project, so getting it back to the IMU is a pain). I did find that the bitwise operator for setting the polarity is incorrect at present. Right now the HIGH polarity setting instead sets the interrupt enable bit to 1, which it already was since we're already enabling the interrupt. I've fixed this on my end by just changing line 483 of kxtj3-1057.cpp to dataToWrite |= (0x01 << 4); // Active HIGH and that will resolve the interrupt polarity issue.

The output data rate for the wake-up function (which is relied upon by the Interrupt handler per the description of WAKEUP_COUNTER in the datasheet), is never changed by this library. As such it is always its default value of 000, which is 0.781Hz. The example sketch uses a WAKEUP_COUNTER value of 1, and since 1 count = 1 / WUODR, that means 1 count is a period of ~1.28 seconds where motion must be detected. Additionally, this setting also determines how long the INT pin will stay changed for, which in this case is also ~1.28 seconds.

The example sketch sets NA_COUNTER to 10 (0x0A), and just like WAKEUP_COUNTER above, the period is determined by 1 / WUODR, so 10 counts would be ~12.8 seconds. This means there must be ~12.8 seconds of no activity (that is, activity below the detection threshold) before a follow-up interrupt can be set.

The threshold is set to 123 in the example sketch. By default, Kionix ships the IMU with the threshold set to 128, which the datasheet states is 0.5g. The threshold register is 12 bits wide, giving a maximum value of 4095. A bit of math thus says that the maximum value would be 16g, which tracks with the specification of this IMU. As such, a value of 123 would equate to 0.48g. The code to set the threshold does work as expected for setting the low and high bytes.

So in short, there was only one actual bug code-wise that I was able to find, which was setting the LOW/HIGH state for the interrupt. The rest of the code appears to be correct, and sets an interrupt that will trigger if any axis moves beyond 0.48g for longer than 1.28 seconds, which will trigger the INT pin for 1.28 seconds before returning the INT pin to normal, then wait for 12.8 seconds of no reading beyond 0.48g before resetting.

@pbdaran Make sure you have updated your library via the Arduino library manager (I just checked and version 0.0.2 is live now), then open kxtj3-1057.cpp in your editor of choice and change line 483 to the following:

dataToWrite |= (0x01 << 4); // Active HIGH

Then retest, understanding the expected output from what I wrote above. Does it work for you now?

pbdaran commented 1 year ago

@nomakewan I tried by editing the kxtj3-1057.cpp as you said. But getting interrupt of 0 at all instances.

image

ldab commented 1 year ago

Please share your code and what register did you read for Interrupt status

pbdaran commented 1 year ago

For interrupt, I didn't read any register. I just connected the INT pin of the sensor to microcontroller and read the logic status of the pin. I used the example code in the repository, just added/ formatted the print statements.

ldab commented 1 year ago

Is your micro-controller input set as interrupt? what are the timestamp from the log you sent? I'm asking because there is a possibility that by the time your microcontroller read the input status, the interrupt is already reset.

ldab commented 1 year ago

the code you shared does not result on the output shared earlier, please share the code, preferably in text format and preformated.

ldab commented 1 year ago

I don't remember fully how it works, but by reading the comments on myIMU.intConf(123, 1, 10, HIGH);, my interpretation is:

The KXTJ3-1057 interrupt is set to Unlatched, which accordingly to table 15 of the product datasheet "INT pin is pulsed and automatically cleared after 1/OWUF", meaning that the INT pin will Pulse for 160ms after the movement is detected.

On your code you have several delays that could be shading the event.

I have 2 recommendations:

  1. Use your microcontroller as interrupt;
  2. Measure with a multimeter.
pbdaran commented 1 year ago

I measured the INT pin signal using oscilloscope. Couldn't see any pulse.

ldab commented 1 year ago

on that case my last suggestion would be to double check your hardware and check if the chipset is mounted properly, i do not have the hardware and the code seems correct, maybe others will check, the issue remains open

nomakewan commented 1 year ago

I don't remember fully how it works, but by reading the comments on myIMU.intConf(123, 1, 10, HIGH);, my interpretation is:

* `1` as the interrupt duration, means that the interrupt will trigger after `1/6.25` (160ms) after the event **is happening**

* `10` non movement time before for another interrupt to the triggered which should result in 1.6 seconds.

The KXTJ3-1057 interrupt is set to Unlatched, which accordingly to table 15 of the product datasheet "INT pin is pulsed and automatically cleared after 1/OWUF", meaning that the INT pin will Pulse for 160ms after the movement is detected.

This math is incorrect. Per my comment earlier, since this library does not ever alter CTRL_REG2, that means ODRWU will always be its default value of 0.781 Hz, not 6.25 Hz. As such, the interrupt will trigger after 1/0.781 (1.28 seconds) assuming that total change in acceleration remains above the threshold value for the entire 1.28 seconds. Should the change in acceleration fall below that value at any point before 1.28 seconds expires, the timer resets.

This value is also how long the pin will stay triggered for, which is 1.28 seconds. So when it fires, you should see a pulse that is exactly 1.28 seconds long.

The '10' non-movement time means that 10/0.781 (12.8 seconds) of movement less than the treshold must occur before the interrupt routine will reset and allow another trigger of the pin.

nomakewan commented 1 year ago

@pbdaran I just noticed your code is putting the IMU to sleep for 2 seconds every loop. This is likely what's preventing the interrupt from firing, since you only allow the IMU to be up for ~300 ms at a time, which is less than 1.28 seconds. Further, the comments in your code about movement and non-activity are incorrect (see my above comments).

Please remove myIMU.standby(true); from your code, move myIMU.standby(false); into setup(), and change the delay at the end of the loop to 500ms instead of 2000.

Then test again.

nomakewan commented 1 year ago

I have pushed an experimental branch with a few updates to the interrupt routine. This is just for testing at the moment as my end goal is to allow the end user to more completely configure the interrupt routine, including selecting arbitrary wake-up data rates and using latched or pulsed interrupts.

All it does for now is fix the interrupt polarity and INT_CTRL_REG1 bits, as well as add a new routine that attempts to make the Wake-Up Output Data Rate match the actual Output Data Rate of the IMU (up to 100 Hz; if ODR >= 100 then WUODR will be 100).

This means that in the demo sketch, which sets the ODR to 6.25 Hz, my new code will read this value from DATA_CTRL_REG and then set the WUODR to 6.25 Hz as well. This will mean that the interrupt will fire if there is motion beyond 0.48g for more than 160ms, the INT pin will change state for 160ms then reset to its original state, and then the IMU will wait for no activity above 0.48g for 1.6 seconds before allowing the interrupt to fire again.

Do note that this means the detection window for the interrupt will be significantly smaller (160ms versus 1.28 seconds!), so you will need non-blocking code in order to properly detect the interrupt pin changing state using your Arduino. Something like the following in your main loop, without using any delays anywhere:

#include "kxtj3-1057.h"
#include "Wire.h"

// Accelerometer provides different Power modes by changing output bit resolution
#define LOW_POWER

/*Accelerometer definition*/
KXTJ3 myIMU(0x0F);
float   sampleRate = 6.25;  // HZ - Samples per second - 0.781, 1.563, 3.125, 6.25, 12.5, 25, 50, 100, 200, 400, 800, 1600Hz
uint8_t accelRange = 2;     // Accelerometer range = 2, 4, 8, 16g
bool detectedInterrupt = false;

/*Setup*/
void setup(void)
{
  Serial.begin(115200);
  Wire.begin(5,6);

  pinMode(1, INPUT); // Accelerometer interrupt

  /********************************************************/
  /*Accelerometer*/
  /********************************************************/
  if( myIMU.begin(sampleRate, accelRange) != 0 )
  {
    Serial.print("Failed to initialize IMU.\n");
  }
  else
  {
    Serial.print("IMU initialized.\n");
  }

  /**
  *Detection threshold, movement, duration and polarity ==> intConf(Threshold, Movement, Non-Activity duration, Polarity)
  *Threshold: This value can be anything from 1 to 4095. i.e 0.0039 (1/256) to 16 g (4095/256)
  *Movement (Interrup duration): This value can be anything from 1 to 255. i.e 5 event_counts / 6.25 Hz = 0.8 seconds
  *Non-activity duration : This value can be anything from 1 to 255. i.e 5 event_counts / 6.25 Hz = 0.8 seconds
  *Polarity: Logic level output of interrupt pin
  */

  myIMU.intConf(123, 1, 10, HIGH);

  uint8_t readData = 0;

  /*Get the ID of Accelerometer */
  myIMU.readRegister(&readData, KXTJ3_WHO_AM_I);
  Serial.print("Accelerometer: Who am I? 0x");
  Serial.println(readData, HEX);
  Serial.println();
}

void loop()
{
  if (digitalRead(1) == HIGH && !detectedInterrupt)
  {
    Serial.println("Interrupt fired!");
    detectedInterrupt = true;
  }
  else if (digitalRead(1) == LOW && detectedInterrupt)
  {
    detectedInterrupt = false;
  }
}

Feel free to give it a try.

https://github.com/nomakewan/KXTJ3-1057/blob/interruptfix/src/kxtj3-1057.cpp

pbdaran commented 1 year ago

Great! @nomakewan The code works. Just ran the demo code you provided in above comment. Below is the response on serial terminal.

IMU initialized.
                Accelerometer: Who am I? 0x35

Interrupt fired!
Interrupt fired!
Interrupt fired!
Interrupt fired!
nomakewan commented 1 year ago

@pbdaran Awesome!! So yeah, the problem was a combination of incorrect documentation, a missed bitshift, and just general confusion over signal timing.

Glad it worked out! Now I can focus on implementing all the other interrupt-based features. Thank you so much for testing!