Infineon / TLE493D-3DMagnetic-Sensor

Library for Infineons's 3D magnetic sensor TLE493D for Arduino.
MIT License
27 stars 14 forks source link

Reset sequence with Arduino Wire library. #9

Closed Koepel closed 1 year ago

Koepel commented 5 years ago

In the function "Tle493d::resetSensor()" in the file "src/Tle493d.cpp" the reset sequence is commented out at the moment.

User 3Dgeo on the arduino.cc forum made a sequence with the Arduino Wire library to put the 0xFF and 0x00 on the I2C bus.

Wire.requestFrom(0xFF, 0);
Wire.requestFrom(0xFF, 0);
Wire.beginTransmission(0x00);
Wire.endTransmission();
Wire.beginTransmission(0x00);
Wire.endTransmission();
delayMicroseconds(30);

The 0xFF is with a read bit, and the 0x00 is with the write bit. Using Arduino functions to read and write makes the sequence.

elcritch commented 4 years ago

The snippet above doesn't work on my device (Cortex m4) as found on the ItsyBitsy m4. Looking at the code, the Wire.requestFrom(0xFF, 0); piece doesn't run as there is a check in the requestFrom function that returns immediately if quantity is 0 as in the snippet. The previous snippet of code may work on other Arduino compatible boards, but not on the SAMD / Cortex M boards. Check the Wire.cpp file to see how the requestFrom behavior works.

To get around this, on the Cortex M devices you need to access the lower level SERCOM device. I'm not sure if this is specific to the Cortex Arduino devices or not.

Here's my code for resetting the sensor:

int wire_send_custom(SERCOM *sercom, uint8_t address, uint32_t flag) {
     // Start I2C transmission
    if ( !sercom->startTransmissionWIRE(address, (SercomWireReadWriteFlag) flag) )
    {
      sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
      return 2; // Address error
    }

    sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);

    return 0;
}

void tle_reset(SERCOM *sercom) {
  wire_send_custom(sercom, 0xFF, 1);
  wire_send_custom(sercom, 0xFF, 1);
  wire_send_custom(sercom, 0x00, 0);
  wire_send_custom(sercom, 0x00, 0);
  delayMicroseconds(30);

  Wire.setClock(100000);
}

On the ItsyBisty m4 board, the SERCOM device is specified as sercom2 global object, but it varies per device. To find it you need to lookup the Wire.cpp file in the board support under the arduino15 folder, something like $HOME/Library/Arduino15/packages/adafruit/hardware/samd/1.5.13 on MacOS. To use the code, I call it like tle_reset(&sercom2).

The output on the scope gives start condition, 0xXX, ack bit, stop condition with that pattern for each of the 0xFF, 0xFF, 0x00, 0x00. The datasheet's do not specify if the ack bit is required, but it appears to work. Before using that block of code, my sensor would only work once in a while after power on. Now they're working reliably. 🤞

I suspect that similar could be done to my code for other Arduino devices, but you need to have access the underlying SERCOM device (or similar) otherwise the Wire.h wrapper won't let you write pure 0xFF/0x00.

Hopefully this tidbit helps others! As a side note and warning for others, the hardware once function is great, but the datasheets are entirely incorrect on the I2C addresses for the devices! Also, they appear to use a single i2c address for read and write contrary to the datasheets I could find. I'd recommend using a I2C scanner to verify the I2C address for your model of TLE493D. The library has correct addresses too.

P.S. One other tidbit for others, when writing the MOD register the parity bit seem to need to be set correctly otherwise the sensor won't work. You can calculate it, or just guess 0/1 like I did. Luckily it's constant based on the other settings. :-)

Resonanz commented 2 years ago

Agreed that the Infineon datasheet is terrible. The A1B6 and A2B6 datasheet appear to have been written in entirely different universes.

To perform the 0xFF 0xFF 0x00 0x00 reset sequence + 30 us delay on SAMD21 (not Arduino), I have used the following code:

/*
 * Using io_read and io_write with length parameter = 0 to send just the i2c device address.
 * On the scope, this is seen as
 *
 *     0xFF: <start_condition><SDA high for 9 clocks><end_condition>
 *     0x00: <start_condition><SDA low for 8 clocks><SDA high for 9th clock><end_condition>
 * 
 * The 8th clock pulse is the R/W bit (low = write) thus by using io_read and io_write the
 * 8th bit can be set high or low. The 9th bit is ACK which fails (NACK = high) for these bytes
 * as there are no devices at address 0x00 or 0xFF on the bus.
 *
 */

    uint8_t data = 0;                                   // Reset sequence required by TLE device
    io_i2c_set_address(tle->io, 0xFF);                  // First and second TLE493D reset bytes
    io_read(tle->io, (uint8_t *)&data, 0);              // Send i2c data
    io_read(tle->io, (uint8_t *)&data, 0);
    io_i2c_set_address(tle->io, 0x00);                  // Third and fourth TLE493D reset bytes
    io_write(tle->io, (uint8_t *)&data, 0);             // Send i2c data
    io_write(tle->io, (uint8_t *)&data, 0);
    delay_us(30);                                                   // Required TLE493D delay