Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLE493D getting stuck (maybe defective) #6

Closed
bluetiger9 opened this issue Aug 18, 2018 · 14 comments
Closed

TLE493D getting stuck (maybe defective) #6

bluetiger9 opened this issue Aug 18, 2018 · 14 comments

Comments

@bluetiger9
Copy link

Hi,

I have some problems with a TLE493D / this library acting very strange. I'm not sure if the sensor is defective or it's a software problem.

Not sure which variant of the TLE493D it is, but here is a closeup:
https://hackster.imgix.net/uploads/attachments/500027/20180610_094241_8VdXg5Id29.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max

So, sometimes the TLE493D / library works fine, but sometimes the sensor just gets stuck and nothing helps. Neither a complete power off on both the sensor and the micro-controller.

When the sensor is stuck the symptoms are one of the following:

  • .updateData() returns Tle493d_Error::TLE493D_BUS_ERROR
  • .updateData() returns with Tle493d_Error::TLE493D_NO_ERROR, but .getX(), .getY(), .getZ() all returns ZERO-s

When it works fine .updateData() return TLE493D_NO_ERROR and I have correct sensor values.

Currently, I'm using the TLE493D connected directly to a nRF51, but I had this problem Sensor2Go board too.

I would need some help investigating this issue.

Thanks,
Attila

@mhanuel26
Copy link

Hello @bluetiger9 ,

It seems you are experience similar issue to me, reported here.

After some experimenting I have found that it has nothing to do with XMC for Arduino Core, now I am trying to read the sensor from Arduino Zero and I got TLE493D_BUS_ERROR

I am noticing the BUS error is because the address command after the I2C start returns NACK, then the rest of the I2C command don't show up.
gw0000

Also my sensor works since I can see the /INT signal on SCL line going low,
gw0003

Because I cannot even communicate with this sensor I cannot modify the /INT behavior.

Notice that thou the library seems to fail in the address command, I have been able to detect the address of my sensor (A1 = 0x22) using the I2C_scanner code such as
log_scan

I have other two different sensors on my board.

I am using a custom board with TLE493D_W2B6A1 sensor.
In the XMC MS2Go board, I have the A0 sensor with address 0x35, and the problem is similar to the one you are experiencing, sometimes I plug the board and it works sometimes not.

@bluetiger9
Copy link
Author

bluetiger9 commented Aug 19, 2018

Just checked and I have these interrupt(?) signals on the CLK line too:
interrupt

Additionally, I see normal I2C clock signals too:
i2c_clock

But the sensor just returns ZERO-s. It may be some issue with the initialization. I'm not sure if the .begin() function completed successfully or not. The function returns void.

@Infineon, could this interrupt signals mess up the I2C communication? Is there is know workaround for this problem? We would really need help on this.

Thanks,
Attila

@mhanuel26
Copy link

@bluetiger9 ,

It might happen that the Interrupt /INT signal screw up the I2C communication but in this case you would see it in the scope, for example look here for clock stretching and collision avoidance.

Have you examine the SDA line ? You should see at least the start and address waveform.
What initialization does is to read the first six registers and adjustm some bits depending on your setup.

This is what begins does

void Tle493d::begin(TwoWire &bus, TypeAddress_e slaveAddress, bool reset, uint8_t oneByteRead)
{
	//TURN ON THE SENSOR
	// pinMode(LED2, OUTPUT);
	// digitalWrite(LED2, HIGH);

	initInterface(&mInterface, &bus, slaveAddress, tle493d::resetValues);

	mInterface.bus->begin();
	
	mInterface.bus->setClock(100000);

	if(reset == true)
	{
		resetSensor();
	}

	// get all register data from sensor
	tle493d::readOut(&mInterface);

	//1-byte protocol -> PR = 1
	setRegBits(tle493d::PR, oneByteRead);
	
	//correct reset values for other product types
	switch(mProductType)
	{
		case TLE493D_A1:
			setRegBits(tle493d::IICadr, 0x01);
			setRegBits(tle493d::ID, 0x01);
			break;
		case TLE493D_A2:
			setRegBits(tle493d::IICadr, 0x10);
			setRegBits(tle493d::ID, 0x10);
			break;
		case TLE493D_A3:
			setRegBits(tle493d::IICadr, 0x11);
			setRegBits(tle493d::ID, 0x11);
			break;
		default: break;
	}

	// default: master controlled mode
	setAccessMode(mMode);
	calcParity(tle493d::CP);
	calcParity(tle493d::FP);

	//write out the configuration register
	tle493d::writeOut(&mInterface, 0x10);
	//write out MOD1 register
	tle493d::writeOut(&mInterface, 0x11);

	delay(TLE493D_STARTUPDELAY);
}

The function setAccessMode set or clear some bits such CA and /INT depending on your setting.

The problem is that is not taken effect since the sensor is responding with NACK.

Could you check if your sensor respond with NACK too?

@techpaul
Copy link

Having had some time to read the TLE493D-W2B6 User Manual

I would suggest looking at page 28 Loss of VDD impact on I2C bus and if you are seeing NACKs then device is not responding and no further data should be sent from a microcontroller or read from the sensor as per I2C specification, the whole point of the NACK on the address stage is to signify the device is not there or not responding. Returning zeros and no error code is not helpful and more likely returning zeros as not been updated with values as the unit does not respond

I suggest looking at least at Section 2.3 (Page 25 of the manual) *Sensor reset by I2C", how to reset the sensor when the sensor gets confused..

Possibly always doing this some time after startup with a small delay before doing a begin

Word of warning you have to hard code toggling of GPIO lines to achieve this, and not use the I2C functions to force the clock and data to behave as needed ignoring NACKs and address/data types to proceed.

@techpaul
Copy link

techpaul commented Aug 20, 2018

Brief look at the code shows that the inbuilt reset sensor function does NOTHING , perhaps this should be implemented by someone.

/* CAUTION: If the microcontroller is reset, the communication with the sensor may be corrupted, possibly causing the
	sensor to enter an incorrect state. After a reset, the sensor must be reconfigured to the desired settings.
*/
void Tle493d::resetSensor()
{
	//TODO: tle493d-w2b6 freezes after being reset
	// mInterface.bus->begin();
	// mInterface.bus->write(0xFF);
	// mInterface.bus->end();
	
	// mInterface.bus->begin();
	// mInterface.bus->write(0xFF);
	// mInterface.bus->end();

	// mInterface.bus->begin();
	// mInterface.bus->write(0x00);
	// mInterface.bus->end();

	// mInterface.bus->begin();
	// mInterface.bus->write(0x00);
	// mInterface.bus->end();
	
	delayMicroseconds(TLE493D_RESETDELAY);
}

@mhanuel26
Copy link

Perhaps is because there is some issue for this sensor during reset as reported #4

I haven't tried myself the commented code since recently is when I got my sensor to respond the address
command, without changing anything it start working. I have notice I need to wait a minute before plugging again the sensor (the system) so it respond with ACK the address command.

The reset command is actually called before anything so it might be a reason.

void Tle493d::begin(TwoWire &bus, TypeAddress_e slaveAddress, bool reset, uint8_t oneByteRead)
{
	//TURN ON THE SENSOR
	// pinMode(LED2, OUTPUT);
	// digitalWrite(LED2, HIGH);

	initInterface(&mInterface, &bus, slaveAddress, tle493d::resetValues);

	mInterface.bus->begin();
	
	mInterface.bus->setClock(100000);

	if(reset == true)
	{
		resetSensor();
	}

	// get all register data from sensor
	tle493d::readOut(&mInterface);

	//1-byte protocol -> PR = 1
	setRegBits(tle493d::PR, oneByteRead);
	
	//correct reset values for other product types
	switch(mProductType)
	{
		case TLE493D_A1:
			setRegBits(tle493d::IICadr, 0x01);
			setRegBits(tle493d::ID, 0x01);
			break;
		case TLE493D_A2:
			setRegBits(tle493d::IICadr, 0x10);
			setRegBits(tle493d::ID, 0x10);
			break;
		case TLE493D_A3:
			setRegBits(tle493d::IICadr, 0x11);
			setRegBits(tle493d::ID, 0x11);
			break;
		default: break;
	}

	// default: master controlled mode
	setAccessMode(mMode);
	calcParity(tle493d::CP);
	calcParity(tle493d::FP);

	//write out the configuration register
	tle493d::writeOut(&mInterface, 0x10);
	//write out MOD1 register
	tle493d::writeOut(&mInterface, 0x11);

	delay(TLE493D_STARTUPDELAY);
}

the tle493d::readOut(&mInterface); instruction above respond the address with ACK but I don't see the default values for the registers, actually the readOut command called that way read the 23 registers of the device, here a scope view of the 23 transactions

gw0003

A closer look (not all the 23 )
gw0002

The first two

gw0000

It seems they all return FF, but I can clearly see the ACK=0 at the end of each register read.

I am not sure but perhaps the read is to have a copy of the registers values and any further modification will use those values accordingly.

@bluetiger9
Copy link
Author

bluetiger9 commented Aug 20, 2018

Hi @techpaul, @mhanuel26,

First of all, please ignore the part with .updateData() returning Tle493d_Error::TLE493D_NO_ERROR, but .getX(), .getY(), .getZ() are 0. The sensor is actually returning the correct values. It just saved the values in the wrong variables (my code got a little bit messy, in the attempts to workaround the Tle493d_Error::TLE493D_BUS_ERROR case).

@mhanuel26, the SDA line seems to be fine. Here is an example:
screenshot from 2018-08-20 19-06-52
(sorry about the image / data quality. I'm still experimenting with the Hantek 6022BE and I have some trouble with getting a proper trigger)

@techpaul, @mhanuel26 I tried to implement an I2C reset, but does not seems to have too much effect:

const int PIN_SDA = 9;
const int PIN_SCL = 3;

void i2c_Init() {
  pinMode(SDA, OUTPUT);
  pinMode(SCL, OUTPUT);
  digitalWrite(SDA, HIGH);
  digitalWrite(SCL, HIGH);
  delayMicroseconds(5);
}

void i2c_Start() {
  delayMicroseconds(2);
  digitalWrite(SDA, LOW);
  delayMicroseconds(5); 
  digitalWrite(SCL, LOW);
  delayMicroseconds(3);
}

void i2c_Stop() {
  digitalWrite(SDA, LOW);
  delayMicroseconds(3); 
  digitalWrite(SCL, HIGH);
  delayMicroseconds(5);
  digitalWrite(SDA, HIGH);
  delayMicroseconds(3);
}

void i2c_Bit(int data) {
  digitalWrite(SDA, data);
  delayMicroseconds(2);
  digitalWrite(SCL, HIGH);
  delayMicroseconds(2);
  digitalWrite(SCL, LOW);
  delayMicroseconds(2);    
}

void tle793d_i2cReset(int resetBits[4]) {
  i2c_Init();

  // 0xFF
  i2c_Start();
  for (int i = 0; i < 8; ++i) {
    i2c_Bit(resetBits[0]);
  }
  i2c_Stop();

  // 0xFF
  i2c_Start();
  for (int i = 0; i < 8; ++i) {
    i2c_Bit(resetBits[1]);
  }
  i2c_Stop();

  // 0x00
  i2c_Start();
  for (int i = 0; i < 8; ++i) {
    i2c_Bit(resetBits[2]);
  }
  i2c_Stop();

  // 0x00
  i2c_Start();
  for (int i = 0; i < 8; ++i) {
    i2c_Bit(resetBits[3]);
  }
  i2c_Stop();

  // 30 us delay
  delayMicroseconds(30);    
}

...
// reset:
int resetBits1[4] = {HIGH, HIGH, LOW, LOW};
tle793d_i2cReset(resetBits1);

I tried to call this on start and on Tle493d_Error::TLE493D_BUS_ERROR, but does not seems have an effect.

Any idea on how to continue the investigation?

Thanks,
Attila

@mhanuel26
Copy link

@bluetiger9

I found on my arduino Zero, a problem while trying to write to a specific register address,
for some reason the following snippet of code

    Wire.beginTransmission(0x22);
    Wire.write(0b00010001);           // Triger bits b000 , Address b10001       
    Wire.write(0b00110001);           // FP b0, IICadr b01, 1byte mode PR = b1, CloclStreching /INT b00, MasterController Mode b01
    error = Wire.endTransmission();

produces

gw0003

even if the register address 11h was responded with ACK, the data value and stop command is missing.

I found that some people is using twice the library begin function, which sound like dummy but actually in some cases it might work, the logic is

the begin function always try to set 1 byte command mode by using

setRegBits(tle493d::PR, oneByteRead);

Then other MOD1 flags are set as well, read here if in doubt

In my case of arduino zero, the problem might be because the write register command never goes thru.

But the begin calling twice logic is that as pointed documentation, the PR bit is set in 2 byte mode by default, and because the first time the begin is called it tries to read from address 0 the 23 registers such if it is in one byte mode.

I have a hard time to test the library on the XMC S2Go device, the I2C is somehow "buggy", for example the Scan command never get to read my device with address 0x22.

The library fails to build on a Atmel device such atmega 328.

@bluetiger9
Copy link
Author

bluetiger9 commented Aug 20, 2018

Nice. Does that code gets stuck somewhere or it returns? I see that the SCL line is not released, but I think it should be released by Wire.endTransmission(). If it run through, what's the error value returned by Wire.endTransmission()?

I tried to call .begin() twice too, but on nRF51 / my sensor does not seems to help.

I tried the I2C scan too at a moment. As I remember, the device was sometimes detected, sometimes not.

@mhanuel26
Copy link

I have done a little script to investigate if I can do some basic change in the device registers.

I was thinking the reason of getting NACK on the readOut was perhaps the PR bit, then I tried to change it manually but same result on readOut.

Register 13h is a good choice, PRD define the update frequency, I guess /INT will change accordingly. I have change it (at least I2C command goes thru) but the frequency remains the same

gw0000

The following snippet

#include <Tle493d.h>
#include <Tle493d_w2b6.h>
#include <Wire.h>

#define DEBUG
bool led = true;
bool tle493_ini_ok = false;
byte address = Tle493d::TLE493D_A0;

//Tle493d_w2b6 Tle493dMagnetic3DSensor = Tle493d_w2b6(Tle493d::MASTERCONTROLLEDMODE, Tle493d::TLE493D_A0);     
unsigned long volatile pulsecnt = 0;
unsigned long cmillis = millis();
  
void setup() {
  pinMode(26, OUTPUT);
  digitalWrite(26, LOW);    // turn the LED on

  Wire.begin();
  Wire.setClock(100000);
#ifdef DEBUG
  Serial1.begin(9600);
  while (!Serial1);
#endif
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  delay(500);
  
  Wire.beginTransmission(address);
  byte error = Wire.endTransmission();
  if (error == 0)
  {
    Serial1.print("I2C device found at address 0x");
      if (address<16) 
        Serial1.print("0");
    Serial1.print(address,HEX);
    Serial1.println("  !");
    Serial1.println("Initializing TLE493D device");
//    while(1){
//      if(digitalRead(21) == 0)
//        break;
//    }
    digitalWrite(4, HIGH);
    Wire.beginTransmission(address);
    Wire.write(0b00010011);         // 13h
    Wire.write(0b10100000);         // PRD = 101 or 3Hz
//    Wire.write(0b00010001);           // Triger bits b000 , Address b10001       
//    Wire.write(0b00110001);                // FP b0, IICadr b01, 1byte mode PR = b1, CloclStreching /INT b00, MasterController Mode b01
    error = Wire.endTransmission();
    if(error != 0){
      Serial1.println("Initialize TLE493D device FAIL");
      while(1);
    }
    Serial1.println("Set MOD2 OK");
  }
  pinMode(21, INPUT);
  attachInterrupt(digitalPinToInterrupt(21), counter, FALLING);
}

void loop() {
  if((millis() - cmillis) > 1000){
    cmillis = millis();
    noInterrupts();
    int num_pulses = pulsecnt;
    pulsecnt = 0;
    interrupts();
    Serial1.print("Pulse Per Second: ");
    Serial1.println(num_pulses);

    if(led == true){
      led = false;
    }else{
      led = true;  
    }
    digitalWrite(26, led);
  }
}

void counter(){
  pulsecnt++;
}

produces the following log.

It seems the number of /INT pulses is not affected by this.

console2

I have then tried to set CA and /INT = 11b, the pulses stop which sounds like it got the configuration, but trying to restore the 00h value seems not possible, actually the reset value should be 00b but then I cannot see pulses anymore.
console3

Someone can enlighten us here please.

@techpaul
Copy link

techpaul commented Aug 21, 2018

First of all some I2C Basics (the I2C spec is available here, which is the latest one I personally have dealt with I2C for many years from bit-banging through using bus controllers as separate chips to hardware implementations of controllers and devices in FPGAs up to 3.4 Mb speeds. I actually still have a printed copy of the original application note by Mullard dated 1980 on my shelves

I2C has various clock speeds and the main constraints on timing are

  1. Minimum clock high and low time
  2. rise/fall times
  3. Maximum time before data changes when clock is low for data or ACK/NACK phase
  4. Minimum time from Clock High to STOP
  5. Time between STOP and STARTt or before a repeated START

At 100kHz the minimum Clock times are

State Time
High 4 us
Low 4.7 us

Longer timings are allowed which just makes the bus slightly slower as 100 kHz is the max speed for that type of setting. The Clock Low time is the same as time bewteen STOP and START and RESTART. Yes you can run I2C at very slow speeds like 1Hz or slower. If in doubt use 5 us for total clock high or low times when bit banging.

At a start condition ALL devices on I2C bus should go into a state of recognising the bus is BUSY then clock in address, then for 9th Clock period ACK if the address matches the device, and is not busy internally. The only reason changing bits in the internal registers of a device should affect ACK/NACK is because it is
a. in an invalid state
b. Has no room to accept data
c. internally busy

Personally the reset the sensor sequence should have used the Software Reset function on write to address 0 (which is a RESERVED address for general call) (see Section 3.1.12 onwards in the spec) and address byte of 0xFF is Read to Device ID functions address (see Section 3.1.17).

For a stuck bus (SDA or SCL stuck low and not in clock stretching, this is covered in Section 3.1.16 Bus Clear.

I will have a look at the I2Creset code put up later which appears to have wrong timings and need to check the library functions available for what else is needed as after any device reset you need to do a begin again to set up parameters, probaly need a device.end to free up resources before reset as well.

@mhanuel26
Copy link

Hi,
I think I found something that might be a bug, not really sure since I haven't inspect the underlying function of the library, just want to throw here my found and will continue to investigate.
After reviewing the I2C trace of the begin function, I notice the writeOut function
tle493d::writeOut(&mInterface, 0x10);
was not placing the correct data on the I2C bus.
For example it produces
gw0012
for MASTERCONTROLLEDMODE with TRIG=1, the data should be 0b00010000, LSB bit might be 1 or 0 depending on odd parity of register 07h to 10h.

I decide to write the value by hand, such as

	mInterface.bus->beginTransmission(slaveAddress);
	mInterface.bus->write(0x10);
	mInterface.bus->write(0b01010001);
	mInterface.bus->endTransmission();

Followed by Register 111h in similar way

	mInterface.bus->beginTransmission(slaveAddress);
	mInterface.bus->write(0x11);
	mInterface.bus->write(0b00110001);
	mInterface.bus->endTransmission();

gw0001

The Log looks like this

log_1

Not sure where is the problem exactly but with this changes the library worked for first time in the ATSAMD21.

Thou it worked, it's not reliable sometimes works sometimes not.

@sherylll
Copy link
Contributor

sherylll commented Aug 28, 2018

Hi can you try the newest code again? The solution is proposed here (tested with A2B6):

// make sure the correct setting is written

To avoid the glitch just write out the register twice.

@sherylll
Copy link
Contributor

If you have any problems with the aforementioned workaround, please reopen or send a PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants