I2C oscilloscope

Hi!

I hooked up an oscilloscope to the SDA and SCL lines of an Arduino Pro Mini to observe the data sent since my sensor is not behaving as expected. Can anyone explain the small fluctuations?

Hello there!

Can you post a screen shot from your oscilloscope with some arrows to indicate what you mean by fluctuations?

The explanation is that something is terribly wrong :astonished:
It seems to be a problem with the ACK by the sensor. Perhaps your pullup resistors are too strong or the sensor gets the wrong voltage.

Please tell us everything. What kind of Pro Mini do you use (5V or 3.3V). Is there a level shifter ? How long are your wires ? Can you make a photo of your wiring ? Which sensor is it ? How is the sensor powered ? What kind of pullup resistors do you use ? Are other things connected to the I2C bus ? Was the I2C Scanner able to detect the sensor ?

I am using the Arduino Pro Mini (3.3V 328p). There is no level shifter and I did try to minimize most of the wires. The sensor is an AK09970N hall magnetic sensor.

The I2C scanner does detect the device at 0x0C. In the image, you will see an extra resistor for if/when I decide to connect the ODINT pin for interrupts. I have attached the datasheet to see the recommended wiring in the last few pages. I followed that.

The sensor is powered through a USB where the programmer is.

AK09970N-E-00.pdf (1.08 MB)

Can you measure the value of both pullup resistors for SDA and SCL with a multimeter ? I can't see the color coding in the photo :frowning:
Can you try without those pullup resistors ?

What you have can be explained by:

  • Pullup resistors for SDA and SCL that are very low, for example 300 ohms.
  • A breadboard with bad contacts. For example the 3.3V or GND for the sensor. Can you measure the voltage of the sensor? and the GND connection to the sensor?
  • A broken sensor. When a chip is broken, it might work only half. It often does not completely stop working.
  • ... other causes

I think the step on SDA signal when the device pulls down indicate a very poor GND connection. I will try to explain it but I am not sure if it will be understandable:
Sensor's GND is like 1V higher than Arduino's GND. When Arduino pulls SCL DOWN sensor supply current goes via SCL protection diode instead of GND and so the device is able to pull SDA to ~0.7V (1 diode drop over Arduino's GND/SCL level). When SCL goes HIGH the supply current must go via the device's GND pin. So the device is only able to pull SDA to 1V (it's own GND).
In other word the device pulls the SDA to its own internal GND. The GND is min("voltage on GND pin","SCL voltage + 1 diode drop").

Both of the resistors are 3.3KOhm (a resistor I used because it was the recommended pull-up resistor). The voltage of the sensor is 3.3V (does this mean that the intern GND is fine?).

All of the connections seem fine, especially since endTransmission() returns 0. The issue is that when I read preset registers, I do not get the values that I expect to get.

Should I post my code as well?

Did you measure the resistors with a multimeter ? Did you try without the pullup resistors ?

The pictures from the oscilloscope are scary. It could be a bad GND or shortcut or something like that.

Do you have another I2C device ? If possible a device that runs at 5V and 3.3V. If you run a I2C Scanner with a normal I2C device, you should see normal level on the oscilloscope.
If nothing else is wrong, the sensor might be broken.

I cleaned up the signal, and so now it looks like this:

I see three bytes on the SDA line, 0x18, 0x19, and 0x21. However, my code says:

i2c.beginTransmission(0x0C);
    i2c.write(0x19);
    i2c.write(0x21);
    i2c.endTransmission();

So I would expect there to be two bytes on the SDA line, 0x19 and 0x21.

I am using the SoftI2CMaster language.

The first byte is the 7-bit address with a read (or write) bit (e.g. 0x18 == 0x0c << 1).

Those are normal signals for I2C. I don't understand why it was not normal in the first pictures.
Can you try the normal Arduino Wire library ? I don't know which version of the SoftI2CMaster library you use and if you use it correctly.

When you encounter a problem, get rid of as many things that can influence it and try a minimal setup to find the problem. Assume that everything can be wrong.

The Arduino Wire library can not cause a shortcut on the I2C bus, so I did not ask to see your sketch. But now that you don't use the Arduino Wire library, perhaps the SoftI2CMaster is the problem. I am also wondering what else could be going on and you have not confirmed that you have actually measured the value of the pullup resistors.

There are two or three libraries that are called SoftI2CMaster: Arduino I2C libraries · Testato/SoftwareWire Wiki · GitHub.

I understand how that works, so I would expect that when I send 0x19 (a read instruction) and then 0x21 (the register I would like to read) that would be it. However, it is sending an extra 0x18 which would be the write instruction.

I am using: GitHub - todbot/SoftI2CMaster: Software I2C / TWI library for Arduino allows any two pins to be SD

I got the same extra byte when I used the I2C library.

No offence meant but you do not understand how this works. beginTransmission(0x0C) command says “Start write session to device with address 0x0C”. So the Arduino send START condition and (0x0C<<1 == 0x18) byte (address and write bit). By .write(x) command you add bytes you want to write to the device.
To read from common I2C look at examples in Arduino IDE, it is named something like master receiver. You need to use beginTransmission to set address where to read and than requestFrom to actually read the data.

hmm... let's have a look at that code

so...

Sounds like you have a I2C device that will take commands and probably need a repeated start. It will have two transactions, the first transaction is a write and does not send a stop, the second transaction is a read and has a stop. If a stop is placed between the write and read some devices will not respond, it is known as an atomic transaction, another way to see it is that the master has locked the bus and it alone can start the next transaction.

i2c.beginTransmission(0x0C); //i2c shows a start and (addr<<1)+w byte
    i2c.write(0x21); // i2c shows a byte, e.g. an i2c slave device can use this as a command 
    // do not send a stop
    uint8_t byte =i2c.requestFrom(0x0C); // i2c shows a repeated start and (addr<<1)+r byte and a byte from a slave if present
    i2c.endTransmission(); // send a stop
    
// byte should have the value from whatever the command caused

No offense taken considering how confused I am.

I don't think I understand how all the starts and stops work. I think I understand how the write function works and it looks like it would follow the image from the datasheet. However, for the read instruction, it seems to me as though there would be a stop condition before any data is sent back.

Can you please explain this to me?

for the read instruction, it seems to me as though there would be a stop condition before any data is sent back.

One of the ideas for an I2C bus is the possibility of multiple masters, and once the stop has been issued the bus is available for another master to use. Think of the start and stop as resource utilization marks, that ensure different masters can use the bus without worries of another jumping in the middle of a command sequence.

I think I understand much better how I2C works. However, I do not think I completely understand why I am getting the results that I am getting. With both the Wire library and the SoftwareI2CMaster library I received the numbers 252, 0, 0, then 255 multiple times no matter what register I request from.

Can someone please explain my results?

Wire library code: - READ = 0x19, reg is irrelevant, I get the same results.

Wire.beginTransmission(AKAddress);
  Wire.write(READ);
  Wire.write(reg);
  Serial.println(Wire.endTransmission());

  // receive information from status
  Wire.requestFrom(AKAddress, num);

  while (Wire.available() > 0)
    Serial.println(Wire.read());

Remove the
Wire.write(READ);
line. In I2C the Master always send the first byte. The first byte is (device) address + R/W bit meaning which device the Master speaks to and if it wants to send or request data. You cannot do both in one I2C transaction - if Master sends Address + R it should only read data and when it sends Address + W it should send as much data as it wants. So typical read access of an I2C device is:

  1. Dummy write to set the Slave's internal address pointer (START, Address + W, register to read address, STOP).
  2. Actual read (START, Address + R, read requested data, STOP).

It is shown on Fig. 11.13 Random address read.
The requestFrom command takes care of the whole second phase and saves the read data in a buffer in Arduino's RAM.

Just to clarify, if I would like to read register 0x00 on the device 0x0C my code would look as follows:

Wire.beginTransmission(0x0C);
Wire.write(0x00);
Wire.endTransmission();

If I would like to write to register 0x21 the data 0x28, then my code would look as follows:

Wire.beginTransmission(0x0C)
Wire.write(0x21);
Wire.write(0x28);
Wire.endTransmission()

If these are correct, does endTransmission recognize that when one byte is cued, the function should send 0x19 while if two or more bytes are cued, the function should send 0x18?

It sends 0x18 in either case. You want WRITE register to read. After this you start ANOTHER transmission with 0x19 address byte to read the data (requestFrom does this).