【问题标题】:Read a block of data from a specific register(fifo) using c/c++ and i2c in raspberry Pi在树莓派中使用 c/c++ 和 i2c 从特定寄存器(fifo)读取数据块
【发布时间】:2019-05-03 20:27:29
【问题描述】:

我需要在树莓派上使用 c/c++ 和 I2C 从 MAX30100 芯片读取 4 个字节的数据。我试着用python来做。它奏效了。但问题是数据速率太慢,我需要更新数据至少 250 次/秒,这意味着读取频率 >= 250Hz。所以,我将阅读从 python 切换到 c/c++。

wiringPi 只读取或写入一个字节是没有问题的。但是,我需要从 fifo 读取 4 个字节(fifo 的地址是 0x04),wiringPi 不提供读取块数据的功能。只有读取字节/字功能。

然后,我尝试使用 SMBus 进行块读取,可以在这里找到: https://github.com/leon-anavi/rpi-examples/blob/master/BMP180/c/smbus.c

但是,只要我调用 i2c_smbus_read_block_data(),我的树莓派就会完全冻结。

这是我添加到wiringPiI2C.c中的读取块数据:

void i2c_smbus_read_block_data(int fd, int command, uint8_t *values, int length)
{
    union i2c_smbus_data data;
    int i, err;

    err = i2c_smbus_access(fd, I2C_SMBUS_READ, command,
                   I2C_SMBUS_BLOCK_DATA, &data);
    if (err < 0)
        return;
    printf("test1");
    for (i = 1; i <= length; i++)
        values[i-1] = data.block[i];
}

wiringPiI2C.c 可以在这里找到:https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPiI2C.c

有人知道那里发生了什么吗?或者有更好的解决方案?

【问题讨论】:

  • 既然需求简单,或许可以直接使用I2C接口。 fopen设备->使用ioctl设置客户端地址->使用读/写接口
  • 嗨,你能说得更具体一点吗?我尝试了这种方法,但我无法从 I2C 获取任何数据。 ioctl是只设置设备地址,还是也可以设置fifo的寄存器地址?
  • I2C 对寄存器一无所知。访问寄存器意味着使用寄存器号进行写入,然后进行非特定的读取。它们可以合并,但如果不合并,客户必须支持。但是还有一个用于组合写/读的ioctl,
  • 分享您的代码。问题很可能就在那里。
  • 嗨 user58697,我已经更新了我的问题,并添加了代码。我将上面提到的功能添加到了wiringPiI2C.c文件中。但是只要我的代码调用它,树莓派就会冻结。

标签: c raspberry-pi i2c


【解决方案1】:

对于仍然感兴趣的人。这对我来说是一个可行的解决方案,我只对其进行了简要测试。 (C++)

#include <unistd.h>        //Needed for I2C port
#include <fcntl.h>          //Needed for I2C port
#include <sys/ioctl.h>      //Needed for I2C port
#include <linux/i2c-dev.h>  //Needed for I2C port
#include <linux/i2c.h>      //Needed for I2C port

#include <iostream>
#include <iomanip>
#include <string>
#include <cerrno>
#include <cstdint>
#include <cstring>

const std::string i2c_filename = "/dev/i2c-1";
const int i2c_addr = 0x5b;          //<<<<<The I2C address of the slave

static inline int i2c_rdwr_block(int fd, uint8_t reg, uint8_t read_write, uint8_t length, unsigned char* buffer)
{
    struct i2c_smbus_ioctl_data ioctl_data;
    union i2c_smbus_data smbus_data;

    int rv; 

    if(length > I2C_SMBUS_BLOCK_MAX) 
    {
        std::cerr << "Requested Length is greater than the maximum specified" << std::endl;
        return -1;
    }

    // First byte is always the size to write and to receive 
    // https://github.com/torvalds/linux/blob/master/drivers/i2c/i2c-core-smbus.c  
    // (See i2c_smbus_xfer_emulated CASE:I2C_SMBUS_I2C_BLOCK_DATA)
    smbus_data.block[0] = length;

    if ( read_write != I2C_SMBUS_READ )
    {
        for(int i = 0; i < length; i++)
        {
            smbus_data.block[i + 1] = buffer[i];
        }
    }


    ioctl_data.read_write = read_write;
    ioctl_data.command = reg;
    ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA;
    ioctl_data.data = &smbus_data;

    rv = ioctl (fd, I2C_SMBUS, &ioctl_data);
    if (rv < 0)
    {
        std::cerr << "Accessing I2C Read/Write failed! Error is: " << strerror(errno) << std::endl;
        return rv;
    }

    if (read_write == I2C_SMBUS_READ)
    {
        for(int i = 0; i < length; i++)
        {
            // Skip the first byte, which is the length of the rest of the block.
            buffer[i] = smbus_data.block[i+1];
        }
    }

    return rv;
}

static int setup_i2c(std::string filename)
{
    //----- OPEN THE I2C BUS -----

    int fd;
    int rv;

    if ((fd = open(filename.c_str(), O_RDWR)) < 0)
    {
        //ERROR HANDLING: you can check errno to see what went wrong
        std::cout << "Failed to open the i2c bus. Error code: " << fd << std::endl;
        return fd;
    }

    if ((rv = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
    {
        std::cout << "Failed to acquire bus access and/or talk to slave. Error code: " << rv << std::endl;
        //ERROR HANDLING; you can check errno to see what went wrong
        return rv;
    }

    return fd;
}

int main()
{
    int fd_i2c = setup_i2c(i2c_filename);
    int i2c_data_length = 3;
    int rv;
    unsigned char buffer[i2c_data_length + 1] = {0};

    if (fd_i2c < 0)
    {
        std::cerr << "Set UP I2C Bus Error. Exit now!" << std::endl;
        return -1;
    }

    //std::cout << "File Descriptor: " << fd_i2c << std::endl;

    //rv = read_i2c(fd_i2c, buffer, i2c_data_length);
    rv = i2c_rdwr_block(fd_i2c, 0x22, I2C_SMBUS_READ, i2c_data_length, buffer);

    if (rv < 0)
    {
        std::cerr << "Reading I2C Bus Error..." << std::endl;
        return -1;
    }

    std::cout << "Buffer Value: " ;

    for (int i = 0; i < i2c_data_length; i++)
    {
        std::cout << "0x" << std::setfill('0') << std::setw(2) << std::hex << (int) buffer[i] << " " ;
    }

    std::cout << std::endl;

    unsigned char values[i2c_data_length] = {0};
    values[0] = 0x01;
    values[1] = 0x02;
    values[2] = 0x03;

    //rv = write_i2c(fd_i2c, values, i2c_data_length);
    rv = i2c_rdwr_block(fd_i2c, 0x22, I2C_SMBUS_WRITE, i2c_data_length, values);

    if (rv < 0)
    {
        std::cerr << "Writing I2C Bus Error..." << std::endl;
        return -1;
    }

    return 0;
}

此代码块的关键是选项I2C_SMBUS_I2C_BLOCK_DATA(定义在“linux/i2c-dev.h”中,另见“linux/i2c.h”)。这会将您的 SMBus 块数据转换为 I2C 块数据。具体来说,SMBus块数据是--"command, block_size, data",而I2C块数据是--"command, data",使用两线时序确定STOP信号。

请参考 Linux Kernel 源码 linux/drivers/i2c/i2c-core-smbus.c 和功能,

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
                   unsigned short flags,
                   char read_write, u8 command, int size,
                 union i2c_smbus_data *data)

(i2c_smbus_xfer 是第一个调用的函数,回退到 i2c_smbus_xfer_emulated 是适配器不具备对 SMBus 的原生支持。因此这两个函数应该实现相同的东西。)

看看

case I2C_SMBUS_I2C_BLOCK_DATA:

仔细地向您展示了从 SMBus 到 I2C 总线的转换是如何完成的。

也比较

case I2C_SMBUS_BLOCK_DATA:

看看它是如何不进行翻译的。 (直接发送SMBus数据)

更多可以参考i2c device interface上的linux内核文档,以及i2c driver源代码

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 2014-10-13
    • 2017-10-24
    相关资源
    最近更新 更多