I2C for Linux and FreeBSD C++

Post Reply
marius
Site Admin
Posts: 18
Joined: Tue Sep 10, 2019 2:21 pm

I2C for Linux and FreeBSD C++

Post by marius » Fri Sep 27, 2019 8:34 pm

I2C C++ GENERIC CLASS FOR LINUX AND FREEBSD

Code: Select all

#include <iostream>
#include <string>
#include <string.h>
#include <fstream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#if defined (__linux__)
#   include <linux/i2c.h>
#   include <linux/i2c-dev.h>
#endif
#if defined (__FreeBSD__)
#   include <dev/iicbus/iic.h>
#endif

class I2CGen
{
public:
    I2CGen(const char* i2c, uint8_t addr, bool noerr=false):_sysfs(i2c),_addr(addr),_noerr(noerr){};
    ~I2CGen(){};
    bool write(uint16_t reg_addr, const uint8_t *reg_data, uint16_t length);
    bool read(uint16_t reg_addr, uint8_t *reg_data, uint16_t length);
    static void scan(const char* fn);
private:
    std::string     _sysfs;
    uint8_t         _addr;
    bool			_noerr;
};


#if defined (__linux__)
bool I2CGen::write(uint16_t reg_addr, const uint8_t *reg_data, uint16_t length)
{
    int file = ::open(_sysfs.c_str(), O_RDWR);
    if(file < 0)
    {
        ::perror("Failed to open i2c file");
        return false;
    }
    if(ioctl(file, I2C_SLAVE, _addr))
    {
        ::perror("Failed to I2C_SLAVE i2c:");
        ::close(file);
        return false;

    }
    struct i2c_rdwr_ioctl_data pcks;
    struct i2c_msg mss[1];
    uint8_t buff[length + 1];
    buff[0] = reg_addr;
    ::memcpy(buff+ 1, reg_data, length);
    mss[0].addr = _addr;
    mss[0].flags = 0;
    mss[0].len = length + 1;
    mss[0].buf = buff;
    pcks.msgs = mss;
    pcks.nmsgs = 1;
    if (ioctl(file, I2C_RDWR, &pcks) < 0)
    {
        if(_noerr==false)
            perror("write");
        ::close(file);
        return false;
    }
    ::close(file);
    return true;
}

bool  I2CGen::read(uint16_t reg_addr, uint8_t *reg_data, uint16_t length)
{
    int file = ::open(_sysfs.c_str(), O_RDWR);
    if(file < 0)
    {
        ::perror("Failed to open i2c file");
        return false;
    }
    int err = ioctl(file, I2C_SLAVE, _addr);
    if(err)
    {
        if(_noerr==false)
            perror("Failed I2C_SLAVE");
        ::close(file);
        return false;
    }

    struct i2c_rdwr_ioctl_data pcks;
    struct i2c_msg mss[2];
    //reg
    mss[0].addr = _addr;
    mss[0].flags = 0;
    mss[0].len = 1;
    mss[0].buf = (uint8_t*)&reg_addr;
    //data
    mss[1].addr = _addr;
    mss[1].flags = I2C_M_RD;///* | I2C_M_NOSTART*/;
    mss[1].len = length;
    mss[1].buf = reg_data;

    pcks.msgs = mss;
    pcks.nmsgs = 2;
    if (ioctl(file, I2C_RDWR, &pcks) < 0)
    {
        if(_noerr==false)
            perror("read: failed to I2C_RDWR");
        ::close(file);
        return false;
    }
    ::close(file);
    return true;
}
#endif // __linux__

#if defined __FreeBSD__

bool I2CGen::write(uint16_t reg_addr, const uint8_t *reg_data, uint16_t length)
{
    int file = ::open(_sysfs.c_str(), O_RDWR);
    if(file < 0)
    {
        ::perror("Failed to open i2c file");
        return false;
    }
    uint8_t local[length+1] = {0};
    uint8_t addr = _addr << 1;
    uint8_t result[2] = {0,0};
    struct iic_msg msgs[3] = {{uint16_t(addr|IIC_M_WR), IIC_M_WR, (uint16_t)length+1, local},
    {uint16_t(addr|IIC_M_RD), IIC_M_RD, (uint16_t)2, result}};
    struct iic_rdwr_data data = {msgs, 1};

    ::memcpy(local+1, reg_data, length);
    local[0] = (uint8_t)reg_addr;
    if (ioctl(file, I2CRDWR, &data) < 0)
    {
        if(_noerr==false)
            ::perror("/dev/iic2 I2CRDWR writing");
        ::close(file);
        return false;
    }
    ::close(file);
    return true;
}

bool  I2CGen::read(uint16_t reg_addr, uint8_t *reg_data, uint16_t length)
{
    int file = ::open(_sysfs.c_str(), O_RDWR);
    if(file < 0)
    {
        ::perror("Failed to open i2c file");
        return false;
    }
    uint8_t addr   = _addr << 1;
    uint8_t reg[1] = {reg_addr};
    struct iic_msg msgs[3] = {{uint16_t(addr|IIC_M_WR), IIC_M_WR, 1,     reg},
    {uint16_t(addr|IIC_M_RD), IIC_M_RD, length, reg_data}};
    struct iic_rdwr_data regsel = {&msgs[0], 1};
    struct iic_rdwr_data data   = {&msgs[1], 1};

    if (ioctl(file, I2CRDWR, &regsel) < 0)
    {
        if(_noerr==false)
            ::perror("/dev/iic2 I2CRDWR selecting register");
        ::close(file);
        return 0;
    }
    if (ioctl(file, I2CRDWR, &data) < 0)
    {
        if(_noerr==false)
            perror("/dev/iic2 I2CRDWR reading");
        ::close(file);
        return 0;
    }
    ::close(file);
    return 1;
}
#endif
void I2CGen::scan(const char* i2cdev)
{
    int col=0;
    std::cout << "scanning: " << i2cdev << "\n";

    for(uint8_t addr=0; addr<16;addr++)
    {
        printf("%02x   ",addr);
    }
    printf("\n%02x ",col++);
    for(uint8_t addr=0x1; addr<0x80;addr++)
    {
        uint8_t dummy[2]={0,0};
        I2CGen  i2c(i2cdev, addr,true);

        if(i2c.read(0, dummy,2))
        {
            printf("0x%02x ",addr);
        }
        else
        {
            printf(".    ");
        }
        if(addr%16==0)
        {
            printf("\n%02x ",col++);
        }
    }
    printf("\n");
}

int main(int argc, char *argv[])
{
    if(argc==2)
    {
        I2CGen::scan(argv[1]);
    }
    else {
        std::cout << "pass the /dev/i2c* device\n";
    }
    return 0;
}


Result for i2c display on I2C2.
Linux:
Image

... then I made som' noiz'

Post Reply