Search code examples
c++linuxi2c

sending i2c command from C++ application


I want to send a signal to i2c device though C++ application. I have tried to use system() function, but it take about 7-10ms to return. so I have found this library but it doesn't allow me to send the port number.

this is the command that i want to send

i2cset -f -y 0 0x74 2 0x00

where, 2 is the port number. 0x00: is the command that I need to set in destination device.

So my question is is there any way to send a direct way to communicate with i2c device the same as i2cset application does?


Solution

  • Yes, there is a way. You can read some documentation here: https://www.kernel.org/doc/Documentation/i2c/dev-interface

    Basically you have to first open the I2C device for reading and writing, on Raspberry Pi (which is where I have used this) it is:

    int m_busFD = open("/dev/i2c-0", O_RDWR);

    Then there are two ways:

    • Either use ioctl to set the address and then read() or write() to read or write to the line. This can look like so:
    bool receiveBytes(const int addr, uint8_t *buf, const int len)
    {
        if (ioctl(busFD, I2C_SLAVE, addr) < 0)
            return -1;
        int res = read(busFD, buf, len);
        return res == len;
    }
    
    • Or use the i2c_msg/i2c_rdwr_ioctl_data struct interface with ioctl. This looks more complicated, but allows you to do more complex operations such as a write-restart-read operation. Here is the same read as before, but using this interface:
    bool receiveBytes(const int addr, uint8_t *buf, const int len)
    {
        i2c_msg msgs[1] = {
            {.addr = static_cast<uint16_t>(addr),
                .flags = I2C_M_RD,
                .len = static_cast<uint16_t>(len),
                .buf = buf}};
    
        i2c_rdwr_ioctl_data wrapper = {
            .msgs = msgs,
            .nmsgs = 1};
    
        if (ioctl(m_busFD, I2C_RDWR, &wrapper) < 0)
            return false;
        return (msgs[0].len == len);
    }
    

    And here is an example of a write-restart-read:

    bool sendRecBytes(
        const int addr,
        uint8_t *sbuf, const int slen,
        uint8_t *rbuf, const int rlen)
    {
        i2c_msg msgs[2] = {
            {.addr = static_cast<uint16_t>(addr),
                .flags = {},
                .len = static_cast<uint16_t>(slen),
                .buf = sbuf},
            {.addr = static_cast<uint16_t>(addr),
                .flags = I2C_M_RD,
                .len = static_cast<uint16_t>(rlen),
                .buf = rbuf}};
    
        i2c_rdwr_ioctl_data wrapper = {
            .msgs = msgs,
            .nmsgs = 2};
    
        if (ioctl(m_busFD, I2C_RDWR, &wrapper) < 0)
            return false;
        return (msgs[0].len == slen) && (msgs[1].len == rlen);
    }
    

    Edit: Forgot to mention that this all requires:

    #include <sys/ioctl.h>
    #include <linux/i2c-dev.h>
    #include <linux/i2c.h>