使用 c/c++ 和 raspberry Pi 中的 i2c 从特定寄存器 (fifo) 读取数据块
Read a block of data from a specific register(fifo) using c/c++ and i2c in raspberry Pi
我需要在 raspberry Pi 上使用 c/c++ 和 I2C 从 MAX30100 芯片读取 4 个字节的数据。我试着用 python 来做。它奏效了。但问题是数据速率太慢,我需要至少更新数据 250 times/sec,这意味着读取频率 >= 250Hz。因此,我将读数从 python 切换为 c/c++。
用wiringPi读写一个字节是没有问题的。但是,我需要从 fifo 中读取 4 个字节(fifo 的地址是 0x04),wiringPi 没有提供读取块数据的功能。只有读取 byte/word 个函数。
然后,我尝试使用 SMBus 进行块读取,可以在这里找到:
https://github.com/leon-anavi/rpi-examples/blob/master/BMP180/c/smbus.c
但是,一旦我调用 i2c_smbus_read_block_data(),我的 raspberry Pi 就会完全冻结。
这是我添加到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
有人知道那里发生了什么吗?或者有更好的解决办法?
对于仍然感兴趣的人。这是一个对我有用的解决方案,我只是简单地测试了一下。 (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内核源码
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数据)
更多可以参考linux内核文档i2c device interface, as well as the i2c driver源码
我需要在 raspberry Pi 上使用 c/c++ 和 I2C 从 MAX30100 芯片读取 4 个字节的数据。我试着用 python 来做。它奏效了。但问题是数据速率太慢,我需要至少更新数据 250 times/sec,这意味着读取频率 >= 250Hz。因此,我将读数从 python 切换为 c/c++。
用wiringPi读写一个字节是没有问题的。但是,我需要从 fifo 中读取 4 个字节(fifo 的地址是 0x04),wiringPi 没有提供读取块数据的功能。只有读取 byte/word 个函数。
然后,我尝试使用 SMBus 进行块读取,可以在这里找到: https://github.com/leon-anavi/rpi-examples/blob/master/BMP180/c/smbus.c
但是,一旦我调用 i2c_smbus_read_block_data(),我的 raspberry Pi 就会完全冻结。
这是我添加到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
有人知道那里发生了什么吗?或者有更好的解决办法?
对于仍然感兴趣的人。这是一个对我有用的解决方案,我只是简单地测试了一下。 (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内核源码 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数据)
更多可以参考linux内核文档i2c device interface, as well as the i2c driver源码