This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DM365 I2C Problem

There are several I2C device on my DM365 Board.

Their I2C device address are 0x18, 0x2c, 0x2f,0x20,0x31,0x35.

 

Through the experiment of CCS and ARM operation. I found It can read and write the Device 0x18,0x2c,0x2f.

It can write to the device 0x20.(After writing the registers, the video mode can be changed. So I think the writing is right) But It can't read from the device 0x20(NO ACK, through the CCS ARM and oscillograph experiment ).

But when I connect the I2C bus with another board (such as DM355 board). DM355 can read and write the device 0x20 rightly.

 

DM365 can read and write device 0x31(It have ack). But all the values read are 0. But when I connect the I2C bus with another board (supplied by Analogix). The external board can read and write the devcie 0x31 correctly. (After operation, Video mode is correct)

 

The frequecy of I2C clock of DM355 is about 17KHz. The frequecy of I2C clock of DM365 is about 21KHz.

 

So what's about the I2C bus of DM365? Or is there some problem about my hardware?

  • Usually when I have seen I2C failure with various TI processors it has been related to the device that is being communicated with not having as flexible timing capabilities as the processor, in general what I have seen is that the slave device needs more time between I2C events than the TI processor gives leading to failed accesses or worse a locked up I2C bus. In the past I have seen this dealt with by putting delays in the I2C access code to slow things down, so that may be worth a try here, since you can communicate with the other I2C slaves from an external board I suspect this is just a timing issue of the DM365 going too fast for the I2C slaves.

  • I am seeing the problem using the dm365. That is, we have a device on our board that I can i2c write registers, but i2c reads always result in errors. Using the same code I can simply change the address to another device and the reads are successful.

    Bernie, can you clarify exactly where in the i2c access code (in drivers/i2c/busses/i2c-davinci.c I assume) I could add such delays to possibly work around this issue?

    Regards,

    -Craig

  • CraigSmith said:
    Bernie, can you clarify exactly where in the i2c access code (in drivers/i2c/busses/i2c-davinci.c I assume) I could add such delays to possibly work around this issue?

    Looking closely at the i2c-davinci.c it seems there may not be such an easy answer, the code uses an XRDY interrupt based mechanism for shifting out the data which leaves no space in the current code for putting any delays (it is generally not a good idea to put delays in an ISR). Of course keep in mind that my suggestion here is based on what I have seen with older devices (namely DM642), and so it is possible you are seeing some entirely different issue here.

    To go into detail about where you would have inserted the delays, they would have been between each step in the I2C process, i.e. after the start condition, after a byte of data transferred, after the stop condition, and again before giving up the I2C for the next transfer. In this case you would have to significantly modify the code structure to do this so it may not be practical to do so without verifying that the particular I2C device you are trying to access cannot cope with the I2C timings you are getting out of the DM365 based on its documentation, in addition to ensuring the device is up and in a proper state to receive such I2C accesses.

  • Hmmmm........we are able to read and write to the device on our board using an external test fixture. The timing documentation for the device suggests that it should be able handle the timing out of the dm365 (verified on the scope). So I don't think adding any delays will help anyway.

    The I2C ISR status values after the (failed) read show NACK with AL (arbitration lost).

    i2c_adapter i2c-0: master_xfer[0] W, addr=0x35, len=1
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer: msgs: 1
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x5
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x6
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer:374 ret: 1
    i2c_adapter i2c-0: master_xfer[0] R, addr=0x35, len=1
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer: msgs: 1
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x2   <=== NACK
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x3   <=== NACK + AL
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer_msg: cmd_err:0x2 flags:0x1

    Here's a successful write to the device:

    i2c_adapter i2c-0: master_xfer[0] W, addr=0x35, len=2
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer: msgs: 1
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x5
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x5
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x6
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer:372 ret: 2

    Here is a succesfull read to a different device:

    i2c_adapter i2c-0: master_xfer[0] W, addr=0x2c, len=1
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer: msgs: 1
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x5
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x6
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer:372 ret: 1
    i2c_adapter i2c-0: master_xfer[0] R, addr=0x2c, len=1
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer: msgs: 1
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x4 <=== ARDY
    i2c_davinci i2c_davinci.1: i2c_davinci_isr: stat=0x6 <=== ARDY+NACK
    i2c_davinci i2c_davinci.1: i2c_davinci_xfer:372 ret: 1

    Still debugging. Let me know if you have any other suggestions. Thanks!

     

     

  • Hi Bernie, I think we figured out what is going on. The device is expecting a "combined format" i2c read. That is without a stop condition and just a repeated start condition then the read address. This is a valid format according to the i2c spec (Figure 13). The i2c driver for davinci is doing 2 separate transactions (a write of the address we want to read, a stop, then a start and then the read sequence). The stop in the middle is what is causing the read to not be ack'ed.

    So, my questions are:

    1. Is the "combined format" supported by the dm365 i2c controller? I believe it is (sprufh3.pdf, section 1.2), I just want to confirm.

    2. Does the davinci driver already support this mode or is this something I need to modify the code to do?

    Thanks,

    -Craig

  • This certainly sounds like it could be the problem.

    CraigSmith said:
    1. Is the "combined format" supported by the dm365 i2c controller? I believe it is (sprufh3.pdf, section 1.2), I just want to confirm.

    I agree with your assesment, you should be able to independently generate start and stop conditions using the MDR register.

    CraigSmith said:
    2. Does the davinci driver already support this mode or is this something I need to modify the code to do?

    From what I have seen in the driver it does not, so you would have to modify the I2C driver to support this sort of access. The i2c_davinci_xfer_msg() function in i2c-davinci.c seems to do the low level setup of the interface along with the i2c_davinci_isr() interrupt function actually writing the data, so you could start by modifying the xfer_msg function to perform your particular sequence, or the ISR itself to prevent the stop condition on every end of buffer situation, though you may end up impacting other I2C communications so this would have to be handled carefully.

  • Problem solved! No driver changes necessary. See below for details....

    Although most of the drivers don't seem to do this for their xxx_i2c_read functions (I guess because most i2c slave devices don't seem to care about the stop in between a write/read pair), you can actually pass multiple i2c_msg structures to a singe i2c_transfer() call and the stop bit will only be sent on the last command. For example, this is how I got the read to work fr this device:

        unsigned char data[2];
        struct i2c_msg msg[2] = {
            { client->addr, 0,  1, &data[0] },
            { client->addr, I2C_M_RD, 1, &data[1] },
        };

        data[0] = reg;
        data[1] = 0xff; /* just init to some value */
        err = i2c_transfer(client->adapter, msg, 2);
        if (err >= 0) {
            *val = data[1];
        }

     

    Instead of doing this (2 separate calls, which is the code prevalent in most of the drivers):

        struct i2c_msg msg[1];
        unsigned char data[1];

            msg->addr = client->addr;
            msg->flags = 0;
            msg->len = 1;
            msg->buf = data;
            data[0] = reg;
            err = i2c_transfer(client->adapter, msg, 1);
            if (err >= 0) {
                msg->flags = I2C_M_RD;
                msg->len = 1;
                err = i2c_transfer(client->adapter, msg, 1);
                if (err >= 0) {
                    *val = data[0];
                }
            }
        }

     

  • I am glad to hear that works for your device, this makes sense though I think you would only get a single start condition when doing this sort of transfer, as the ISR does not appear to initiate a new start condition. In any case if this is working than it is probably good enough, hopefully this can help out other users as well.

  • One more comment.....there are definately 2 start conditions (verified with a scope). I believe the starts (and stops for that matter) are generated by calls to i2c_davinci_xfer_msg and not the isr (which gets called twice if num=2 in call to i2c_transfer).