Closed bluetiger9 closed 6 years ago
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_BUSERROR
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.
Also my sensor works since I can see the /INT signal on SCL line going low,
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
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.
Just checked and I have these interrupt(?) signals on the CLK line too:
Additionally, I see normal I2C clock signals too:
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
@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?
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.
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);
}
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
A closer look (not all the 23 )
The first two
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.
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: (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
@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
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.
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.
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
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.
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.
Someone can enlighten us here please.
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
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.
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
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();
The Log looks like this
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.
Hi can you try the newest code again? The solution is proposed here (tested with A2B6): https://github.com/Infineon/TLE493D-3DMagnetic-Sensor/blob/2c6aeaf28df34a7c5a3ef057c64f35418326ca81/src/Tle493d.cpp#L79
To avoid the glitch just write out the register twice.
If you have any problems with the aforementioned workaround, please reopen or send a PR
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()
returnsTle493d_Error::TLE493D_BUS_ERROR
.updateData()
returns withTle493d_Error::TLE493D_NO_ERROR
, but.getX()
,.getY()
,.getZ()
all returns ZERO-sWhen it works fine
.updateData()
returnTLE493D_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