analogdevicesinc / libm2k

A C++ library (bindings for Python and C#) for interfacing with the ADALM2000
http://analogdevicesinc.github.io/libm2k
GNU Lesser General Public License v2.1
34 stars 32 forks source link

i2c has incorrect timing, causing i2c communication to not function #365

Open phsdv opened 2 months ago

phsdv commented 2 months ago

According to https://www.nxp.com/docs/en/user-guide/UM10204.pdf section 3.1.3: The data on the SDA line must be stable during the HIGH period of the clock. The HIGH or LOW state of the data line can only change when the clock signal on the SCL line is LOW

However in the code (tools/communication/src/i2c.cpp) both SDA and SCL are changing at the same time, which is a violation of the standard. Therefore i2c communication does not work, at least not with the devices I tested.

In writeBit for example, the SCL must be low, then SDA can change, and after a little time the SCL can go high, wait half a clock cycle then SCL can go low again.

In below image it can be seen that SCL in SDA change at the same time (using libiio 0.25 and libm2k 0.8.0) afbeelding

[edit]this concerns the i2c in the tools / digital communications. My code that generated above image is based on https://github.com/analogdevicesinc/libm2k/blob/master/bindings/python/examples/i2c.py [edit2] clarified which code and fixed a typo 'now' -> 'not'

proposal for improvement: The code for writeBit in tools/communication/src/i2c.cpp should be something like shown below, as SDA should already be valid before SCL goes high. As there are now 4 changes per sample, samplesPerHalfBit might need to be changed and I renamed it to samplesPerQuartBit. Further it would be clearer if writebyte calls writebit 8 times and then only this change has to be made.

static void writeBit(struct i2c_desc *desc, std::vector<unsigned short> &buffer, bool bit)
{
    auto *m2KI2CDesc = (m2k_i2c_desc *) desc->extra;
    auto samplesPerQuartBit = (unsigned int) (m2KI2CDesc->sample_rate / desc->max_speed_hz) / 4;
    //scl LOW
    for (unsigned int i = 0; i < samplesPerQuartBit; ++i) {
        unsigned short sample = 0;
        if (bit) {
            setBit(sample, m2KI2CDesc->sda);
        }
        buffer.push_back(sample);
    }
    //scl HIGH for 2 quarts
    for (unsigned int i = 0; i < (samplesPerQuartBit*2); ++i) {
        unsigned short sample = 0;
        setBit(sample, m2KI2CDesc->scl);
        if (bit) {
            setBit(sample, m2KI2CDesc->sda);
        }
        buffer.push_back(sample);
    }
    //scl LOW
    for (unsigned int i = 0; i < samplesPerQuartBit; ++i) {
        unsigned short sample = 0;
        if (bit) {
            setBit(sample, m2KI2CDesc->sda);
        }
        buffer.push_back(sample);
    }
}
phsdv commented 1 month ago

The following code works correctly for me:

static void writeBit(struct i2c_desc *desc, std::vector<unsigned short> &buffer, bool bit)
{
    auto *m2KI2CDesc = (m2k_i2c_desc *) desc->extra;
    auto samplesPerQuartBit = (unsigned int) (m2KI2CDesc->sample_rate / desc->max_speed_hz) / 4;

    //scl low
    for (unsigned int i = 0; i < samplesPerQuartBit; ++i) {
        unsigned short sample = 0;
        if (bit) {
            setBit(sample, m2KI2CDesc->sda);
        }
        buffer.push_back(sample);
    }

    //scl high
    for (unsigned int i = 0; i < ((samplesPerQuartBit*2) ); ++i) { 
        unsigned short sample = 0;
        setBit(sample, m2KI2CDesc->scl);
        if (bit) {
            setBit(sample, m2KI2CDesc->sda);
        }
        buffer.push_back(sample);
    }

    //scl low
    for (unsigned int i = 0; i < samplesPerQuartBit ; ++i) {
        unsigned short sample = 0;
        if (bit) {
            setBit(sample, m2KI2CDesc->sda);
        }
        buffer.push_back(sample);
    }
}

static void writeByte(struct i2c_desc *desc, std::vector<unsigned short> &buffer, uint8_t byte)
{
    for (int i = 0; i < 8; i++) {
        writeBit(desc, buffer, getBit(byte, 7 - i));
    }
}