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.

Need response concerning ADS1015 protocol

Other Parts Discussed in Thread: ADS1015

Hello,

I need as straightforward an explanation as possible concerning what to expect re: the ADS1015 protocol. In particular when reading from and writing to the conversion and/or threshold registers.

A little context, I believe I am comprehending the pointer register aspect of it easy enough.

What I am struggling a bit with (pardon the pun) is: word-to-byte-endianness of the 16-bit config, conversion, and threshold registers. Additionally, do I still need to take a twos complement?

So if I have something like this, forgiving the shorthand. It's also later than it needs to be where I am, but I need/want to get this addressed ASAP. Assuming I have already written to the pointer register as part of a ADS1015 transaction:

#include <linux/swab.h>
u16 config_;
//...setup the config...
i2c_write_word_swapped(addr_, __swab16(config));

And to read:

__s32 status;
u16 value_ = 0;
//Would be better to do error checking, but I just need the basic guidance right now...
if ((status = i2c_read_word_swapped(addr_)) >= 0)
  value_ = static_cast<u16>(status);
value_ = __swab16(value_);

Something like this?

I found some kernel code as well which might shed some light on it as well.

Regards,

Michael Powell

  • Hi Michael,

    Big Endian. I would read the conversion data results into a short int (s16) which differs from the u16 you show. As the last four bits are 0, you will need to shift the data and sign extend appropriately.  As far as the config register, you don't need to worry about two's complement and can stay u16.  The threshold registers can be done several ways, so whatever makes sense to you for this one.

    If you were to connect a scope or logic analyizer to the I2C lines you would see a START followed by address, pointer register, and data then STOP. The registers and data are always transmitted MSB first.  I would suggest you read the Config register to verify the default conditions.  This will show if you are communicating correctly.

    Best regards,

    Bob B

  • Still a little perplexed by the lack of command byte (i.e. register addressing). With our I2C interface, and with many of the devices we're using, this is more the rule than the exception. There is one read/write byte "no command" oriented function, which hopefully will do the trick.

    Writing the pointer register, simple enough I believe. Here's the code we're using for writing. This is preceded by a write to the pointer register as part of an ADS1015 driver "transaction" (using C++ lambda goodness).

    void i2c_bus_driver::write_word_swapped_no_reg(address_type addr, __u16_t datum) {


        __s32 status;
        as_slave(addr);

        //In MSB order.
        __u8 hi_ = static_cast<__u8>((datum>>8)&masks::byte_);
        __u8 lo_ = static_cast<__u8>(datum&masks::byte_);

        verify_error(status = ::i2c_smbus_write_byte(m_file, hi_));
        verify_error(status = ::i2c_smbus_write_byte(m_file, lo_));
    }

    And for reading:

    void i2c_bus_driver::read_word_swapped_no_reg(address_type addr, __u16_t& value) {

        __s32 status;
        as_slave(addr);

        __u8 hi_;
        __u8 lo_;

        //Read in MSB order.
        verify_error(status = ::i2c_smbus_read_byte(m_file));
        hi_ = static_cast<__u8>(status&0xff);

        verify_error(status = ::i2c_smbus_read_byte(m_file));
        lo_ = static_cast<__u8>(status&0xff);

        //Assemble the high order and low order bytes.
        value = static_cast<__u16_t>((hi_<<8)|lo_);
    }

    The one problem I seem to be having now is that I believe I am writing to the config register. However, can't seem to read it back for verification after (supposedly) a single-shot conversion is done. I verified I am writing a C787. However, this is reading back as the "default" 8585 (or whatever it was set to previously).

    I am working loosely from an example driver done in C, adapting the important bits into C++ goodness: https://github.com/gp-b2g/gp-keon-kernel/blob/master/drivers/hwmon/ads1015.c

    We're working on hooking up an Aardvark for I2C debugging in the meantime like you suggested.

    And for completeness while we're here, the interface that writes one byte (for the pointer register):

    void i2c_bus_driver::write_one_no_reg(address_type addr, byte datum) {

        __s32 status;
        as_slave(addr);

        verify_error(status = ::i2c_smbus_write_byte(m_file, datum));
    }

  • After discussion with our hardware vendor, the configuration I'd like to go with is rather C607 to start continuous mode. Then C707 to power down. However, still getting zeroes from the unit.

    It's possible we've got hardware issues (it's fresh off the manufacturing line, virtually anyhow), I can't rule it out. I just want to be sure I am doing everything I know how to do in software control at the same time.

  • Hi Michael,

    Your C code doesn't really show me all that much.  The thing to remember is everything is register based, including the commands that are contained in the Config register.  The Pointer sets the position of which register you are reading or writing.  The Config register sets various actions. You need to follow the procedure of the Quick Start on page 8 of the datasheet.

    If you tell me the action and byte values you are trying to communicate, and then capture a scope plot and send it to me of the actions from SDA and SCL I can determine if the communication is correct.

    Best regards,

    Bob B

  • What I can tell you is that every other I2C device we've integrated to this point has had a clearly outlined protocol. None without their quirks, but all basically in agreement: they start with bus address, command (register), followed by some data, usually a byte, sometimes a word (two bytes).

    We have a version of inline I2C API that facilitates this without a great deal of effort. We do wrap it in C++ classes for convenient access throughout the application. We are using an i2c-dev.h header with the following signature: /* $Id: i2c-dev.h 5361 2008-10-19 09:47:02Z khali $ */. It's a little dated, as you can tell, but basically it works for what we need.

    The API that I think should apply are:

    static inline __s32 i2c_smbus_read_word_data(int file, __u8 command);
    static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);

    Where file is an open file to the bus. We've already slaved the device with a call to ioctl:

    ::ioctl(m_file, I2C_SLAVE, addr)

    Then we subsequently want to call either the read or the write.

    However, reading the ADS1015 docs, leads me to believe I might need the following API, which I can tell you is highly unusual, at least with the several devices we've integrated thus far:

    static inline __s32 i2c_smbus_read_byte(int file);
    static inline __s32 i2c_smbus_write_byte(int file, __u8 value);

    Which doesn't seem quite right to me, at least at the level of abstraction I'd like to be at.

    Does this help at all?

  • Hi Michael,

    The I2C protocol is in byte format, one byte with an ACK, so I don't know why you find it surprising that you might need to transfer in bytes instead of words.  Believe it or not, there are many microcontrollers that only have byte size buffers, and some that don't even have an I2C bus which requires bit-banging.

    In this case the problem is the Pointer register, which is one byte.  It may be possible to send the Pointer address in the MSB of the word with dummy data in the LSB.  Then you issue a STOP.  Once the pointer is set, you should have no problem when using word transactions.

    Best regards,

    Bob B

  • Much appreciated the speedy response. Just trying to sort this out is all.

    I'm not sure what you mean by "ACK" or "STOP". Conceptually, I know. And I've dealt with ACK/NAK messages in other protocols (RS232, or TCP/IP, for instance). However, is that something the SMBUS API handles for me? Or I need to manually write a byte, type thing?

    Edit: Another plausible explanation is that the "address" byte not only serves as the thing we slave, but is also part of the data-protocol, that we're writing on the bus to address the device. ... Still no clue what a start/stop condition might be.

    Would you mind explaining that a bit?

    Thank you...

  • Oh, well duh! From the kernel code:

    static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg)
    {
      s32 data = i2c_smbus_read_word_data(client, reg);
      return (data < 0) ? data : swab16(data);
    }
    static s32 ads1015_write_reg(struct i2c_client *client, unsigned int reg, u16 val)
    {
      return i2c_smbus_write_word_data(client, reg, swab16(val));
    }

    Which is using the read/write word data methods from the I2C API! Duh!

    Which if you examine the calls, pretty much answers my question.

    What the docs, and what you, are calling "pointer register byte", is actually simply "reg" as far as the SMBUS API is concerned.

    Which is pretty much what I thought. I'm not sure why the docs are so obscure about that.

    Edit: Well, I'm pretty sure. I am going to run with that idea and verify it anyway. I need to test it yet.
  • Verified, that did the trick. Of course, the key is also handling endianness correctly, but this is easily dealt with in thin verniers.