SpenceKonde / megaTinyCore

Arduino core for the tinyAVR 0/1/2-series - Ones's digit 2,4,5,7 (pincount, 8,14,20,24), tens digit 0, 1, or 2 (featureset), preceded by flash in kb. Library maintainers: porting help available!
Other
557 stars 144 forks source link

Attiny1614 with mlx90614 - i2c problem #635

Closed rdtaps closed 2 years ago

rdtaps commented 2 years ago

Attiny1614, 20MHz internal, 5V, pullup 4k7 on SDA,SCL, serialUPDI 230400 CH340 Win

Unable to communicate with mlx90614 sensor via i2c. I tested with the Adafruit_MLX90614 library and also tried my own. A communication error occurs when calling the following sequence of functions.

Adafruit library

uint16_t Adafruit_MLX90614::read16(uint8_t a) { uint8_t buffer[3]; buffer[0] = a; bool status = i2c_dev->write_then_read(buffer, 1, buffer, 3); if (!status) return 0; return uint16_t(buffer[0]) | (uint16_t(buffer[1]) << 8); }

bool Adafruit_I2CDevice :: write_then_read (const uint8_t write_buffer, size_t write_len, uint8_t read_buffer, size_t read_len, bool stop) { if (! write (write_buffer, write_len, stop)) { return false; } return read (read_buffer, read_len); }

Or my own library .. Wire.beginTransmission ((uint8_t) _i2caddr); Wire.write (buffer [0]); Wire.endTransmission (false); bool status = read (buffer, 3);

bool MLX90614 :: read (uint8_t * buffer, uint8_t len) { if (Wire.requestFrom ((uint8_t) _i2caddr, (uint8_t) len) == 0) return false; for (uint16_t i = 0; i <len; i ++) buffer [i] = Wire.read (); return true; }

The problem is writing without stop bit and then reading.

Both examples work on Arduino Nano and Attiny85. I also tried other sensors (BME280, SHT35, etc.) and they work well. But there, the write is always followed by a stop bit and then a read.

I enclose a record of this communication using a logic analyzer. There is a long delay between the end of writing and the beginning of reading on Attiny1614.

Thanks in advance for your help.

attiny1614-mlx90614 attiny1614-mlx90614

nano-mlx90614 nano-mlx90614

Saleae Logic.zip

SpenceKonde commented 2 years ago

Can you please retest on megaTinyCore 2.4.2 so that we know whether it is an old bug that predates the near-complete reimplementation of Wire (and thus that @MX682X was reimplementing incorrect behavior) or whether it is a new bug introduced in the 2.5.x versions?

Without knowing that it's hard to know what to look for in fixing it.

Thanks

rdtaps commented 2 years ago

Version megaTinyCore 2.4.2 with Adafruit_MLX90614 mlxtest example not linked.

_C:\Users\roman\AppData\Local\Temp\ccZ7Nfh4.ltrans0.ltrans.o: In function `main':

:(.text.startup+0x180): undefined reference to `operator delete(void*, unsigned int)' C:\Users\roman\AppData\Local\Temp\ccZ7Nfh4.ltrans0.ltrans.o: In function `_GLOBAL__sub_D_mlx': :(.text.exit+0x10): undefined reference to `operator delete(void*, unsigned int)' collect2.exe: error: ld returned 1 exit status exit status 1 Error compiling for board ATtiny3224/1624/1614/1604/824/814/804/424/414/404/214/204._ With my own mlx90614 library compiled and linked well, but the sensor still does not communicate. Trace from logic analyzer is identical with trace megaTinyCore 2.5.9 Roman
MX682X commented 2 years ago

Lovely, another user besides me having problems with this sensor and Attiny1614.

Jokes aside, the reason I started looking into the library was because of this sensor. When I tried to change the clock, I found the first bug in the old library that locked it up. Then I though, screw it, you can rewrite the rest too...

Anyway, what helped me was changing the Clockspeeds to 120 or 125kHz - weird numbers I know, especially since it is documentated to only work up to 100kHz. But for some weir reason, basically all other frequencies were getting a NACK on my end too. I measured all the timings, too. Of course, you might try to do an Wire.endTransmission(true), which would add a STOP after the Write.

If it helps, my MLX is a DAA5366913

Edit: that is basically the code I used to interact with the sensor,

#include "Wire.h"

uint16_t i = 0;
int16_t tempA;
int16_t tempO;

void setup() {
   Serial1.begin(500000); 

   Wire.begin();
   Wire.setClock(125000);
}

void loop() {
   digitalWrite(PIN_PC6, HIGH);   // Activity
   tempA = getTempAmbient();
   tempO = getTempObject();
   digitalWrite(PIN_PC6, LOW);
   Serial1.print(tempA);
   Serial1.print("  ");
   Serial1.println(tempO);
}

int16_t getTempAmbient()
{
   return (getData(0x06) * 2) - 27315;
}

int16_t getTempObject()
{
   return (getData(0x07) * 2) - 27315;
}

uint16_t getData(uint8_t reg)
{
   Wire.beginTransmission(0x5A);
   Wire.write(reg);
   Wire.endTransmission(false);
   Wire.requestFrom(0x5A, 3, 1);

   while (Wire.available() < 3) {}
   uint8_t lsb = Wire.read();
   uint8_t msb = Wire.read();
   Wire.read();   //pec

   return (uint16_t) ((msb<<8) | lsb) ;
}
rdtaps commented 2 years ago

Thank you so much.

I set the clock speed to 110kHz and the sensor works. It's crazy. I don't understand why it works properly with other chips? I tested the sensor with Arduino Nano, Attiny85, Esp32-wroom at 100kHz and it always worked.

I enclose a comparison of communication on i2c.

logic analyzer

MX682X commented 2 years ago

No Problem!

Since you pointed out that there is quite some time between start and actual sending of bytes, I think the problem lies in the way the TWi module was implemented Hardware wise. When an address is written to the register, the TWI module takes care of sending the START condition and the address. I guess the statemashine of the TWI is clocked by the same prescaler that is used for the SCL generation.

The Sensor is SMBus compatible and they have some timeouts in the specification. I'm not sure how exactly Melexis implemented the sensor though, but if they check the time between two bytes, an increase of the frquency might be enough to not trigger the TO on the sensor, while a too big frequency (>130kHz) is too far out of specs and doesn't work there either.

Best thing to do: Add a notice to the TWI readme about the fact that there is quite some time between START and Address that might confuse some devices in a REPSTART transaction. Workaround: Send Stop after a write/ increase frequency.

rdtaps commented 2 years ago

Wire.endTransmission(true) - Stop bit does not work after writing. I also tested this on Arduino Nano, Attiny85, Esp32-wroom. The Adafruit library therefore uses the function - _i2c_dev->write_thenread(buffer, 1, buffer, 3) with default bool stop=false.

MX682X commented 2 years ago

Thank you for trying it out. I guess it just confirms that the SM is just too slow? I don't know how else the MCu is ignoring this line in the Master_Write function

  if ((send_stop != 0) || (TWI_ERR_SUCCESS != TWI_GET_ERROR)) {
    module->MCTRLB = TWI_MCMD_STOP_gc;                        // Send STOP
  }
  return TWI_GET_ERROR;

but still sends STOPs when there is no Master_Read after that. If you want to help confirming it (I would have to set up the stuff first), add a delay of 1ms between Wire.endTransmission(true) and Wire.requestFrom() at 100kHz.

rdtaps commented 2 years ago

I set a delay of 1ms between Wire.endTransmission(true) and Wire.requestFrom() at 100 kHz. The returned values are incorrect. (255,255,255) It looks like the sensor responds to a stop bit with a data loss.

attiny1614-mlx90614-I2C_100kHz_stop_bit_delay_1ms

SpenceKonde commented 2 years ago

This is clearly a very picky part and that causes a lot of problems. I don't understand why, but the long delays in the start and stop conditions are suspicious as is the very narrow spike near the end of the first byte I do not know if any of those are something that can be addressed on the current silicon.

rdtaps commented 2 years ago

The sensor works at 110kHz. I've been testing for the last 24 hours and no transmission error. I suggest closing the issue.