写入 I2C 的缓冲区在读取时不返回
Written Buffer to I2C Not Returned when Read
我试图在写入后从 I2C 总线上的内存位置读取一个值。当我在终端中 运行 时,我得到了奇怪的输出。
这是我的程序
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int file;
int addr = 0X00; /* XGPIOPS_DATA_LOW_OFFSET */
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus");
return -1;
}
if(ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Unable to open device as slave %s", strerror(errno));
return -1;
}
char buf[10];
buf[0] = addr;
buf[1] = 0x10;
if(write(file, buf, 2) != 2) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 2) != 2) {
printf("Failed to read from the i2c bus. %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf(buf);
printf("\n\n");
}
return 0;
}
The output from the program looks like this
Successful write
Successful read ��
在我的终端上,这些块看起来更像是钻石内部的问号。我不确定在 ASCII 中对应的是什么。
为什么我不读回我最初写入的地址字节后的第二个字节 0x10?
根据第一组答案,更新后的代码如下:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int file;
long addr, reg_addr;
char *end;
if(argc == 3) {
addr = strtol(argv[1], &end, 16);
printf("Value of addr is: %ld\n", addr);
reg_addr = strtol(argv[2], &end, 16);
printf("Value of reg_addr is: %ld\n", reg_addr);
}
else {
printf("arg failed\n\n.");
addr = 0x00;
}
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus\n");
return -1;
}
if(ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Unable to open device as slave \n%s\n", strerror(errno));
return -1;
}
char buf[10];
buf[0] = addr;
buf[1] = reg_addr;
buf[2] = 0x10;
if(write(file, buf, 3) != 3) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 3) != 3) {
printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Buf = [%02X,%02X,%02X]\n", buf[0], buf[1], buf[2]);
printf("\n\n");
}
return 0;
}
此时,每当我使用 0x00 作为地址时,无论 argv[2] 是什么,我都会得到 FF、FF、FF 作为输出。这是设备树文件的适用部分。请注意,这是被模拟的,所以我无法探测物理设备。
&i2c0 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
i2cswitch@74 {
compatible = "nxp,pca9548";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x74>;
i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
si570: clock-generator@5d {
#clock-cells = <0>;
compatible = "silabs,si570";
temperature-stability = <50>;
reg = <0x5d>;
factory-fout = <156250000>;
clock-frequency = <148500000>;
};
};
i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
eeprom@54 {
compatible = "at,24c08";
reg = <0x54>;
};
};
i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
gpio@21 {
compatible = "ti,tca6416";
reg = <0x21>;
gpio-controller;
#gpio-cells = <2>;
};
};
i2c@4 {
#address-cells = <1>;
#size-cells = <0>;
reg = <4>;
rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
i2c@7 {
#address-cells = <1>;
#size-cells = <0>;
reg = <7>;
hwmon@52 {
compatible = "ti,ucd9248";
reg = <52>;
};
hwmon@53 {
compatible = "ti,ucd9248";
reg = <53>;
};
hwmon@54 {
compatible = "ti,ucd9248";
reg = <54>;
};
};
};
};
这里有几个示例测试
尝试测试 SiLabs 时钟发生器
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x54 0x00
Value of addr is: 84
Value of reg_addr is: 0
Unable to open device as slave
Device or resource busy
尝试测试eeprom设备
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x5d 0x00
Value of addr is: 93
Value of reg_addr is: 0
Unable to open device as slave
Device or resource busy
这是我第三次尝试的程序。考虑到答案中的注释后,我写了这个
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define DEVICE_ADDRESS 0x54
int main (int argc, char *argv[])
{
int file;
uint8_t reg, value;
char *end;
printf("The device address on the bus: %d", DEVICE_ADDRESS);
if(argc == 3) {
reg = strtol(argv[1], &end, 16);
printf("Value of register address: %d\n", reg);
value = strtol(argv[2], &end, 16);
printf("value to write is: %d\n", value);
}
else {
printf("arg failed\n\n.");
}
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus\n");
return -1;
}
if(ioctl(file, I2C_SLAVE, DEVICE_ADDRESS) < 0) {
printf("Unable to open device as slave \n%s\n", strerror(errno));
return -1;
}
char buf[10];
buf[0] = reg;
buf[1] = value;
if(write(file, buf, 2) != 2) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 2) != 2) {
printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Buf = [%02X,%02X,%02X]\n", buf[0], buf[1], buf[2]);
printf("\n\n");
}
return 0;
}
不幸的是,我仍然遇到同样的错误。
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x00 0x10
The device address on the bus: 84Value of register address: 0
value to write is: 16
Unable to open device as slave
Device or resource busy
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x30 0x10
The device address on the bus: 84Value of register address: 48
value to write is: 16
Unable to open device as slave
Device or resource busy
编辑 2:我认为您可能没有正确设置 I2C 设备地址。你的 I2C_ADAPTER
("/dev/i2c-0"
) 表示设备在哪条 I2C 总线上。您甚至没有使用 I2C_DEVICE
宏,但这是您应该传递给 ioctl
调用的内容(例如 ioctl(file, I2C_SLAVE, I2C_DEVICE);
),它应该是您想要的设备的 I2C 地址访问(例如 0x5D
用于时钟发生器)而不是 0x00
。
我也认为你的reads/writes不正确。一旦你通过 open()
和 ioctl()
指定了总线和设备,你就不需要再担心这些了。您只需要担心要访问的寄存器(如果您的 I2C 设备使用寄存器)和实际数据。
要写入您的 I2C 设备,假设它使用一个字节的寄存器,写入两个字节的缓冲区:第一个是寄存器,第二个是您要写入的值:
bool i2cdev_byte_write(int file, uint8_t reg, uint8_t val)
{
uint8_t bytes[2];
bytes[0] = reg;
bytes[1] = val;
/* Write the register followed by the value */
if (write(file, bytes, 2) != 2)
return false;
return true;
}
要从您的 I2C 设备读取,假设它使用一个字节的寄存器,写入一个字节的缓冲区(寄存器地址),然后读取一个或多个字节的缓冲区(该寄存器和后续寄存器的值) ):
bool i2cdev_bytes_read(int file, uint8_t reg, unsigned int count, uint8_t *out_buf)
{
if (!out_buf)
return false;
/* Write the register */
if (write(file, ®, 1) != 1)
{
printf("Failed to write register value\n");
return false;
}
/* Read the specified number of bytes */
if (read(file, out_buf, count) != count)
{
printf("Failed to read from the i2c bus\n");
return false;
}
return true;
}
再次注意,以上所有评论都取决于它是一个使用单字节寄存器地址的 I2C 设备,并且它支持在一次读取多个字节时自动递增寄存器地址。您需要查看 I2C 设备的数据表以确定需要如何访问它。
编辑:这是一个 printf()
新手失败。您不能只尝试 printf
一个字节数组。这不是 printf()
的工作方式。
试试这个:
printf("Buf = [%02X,%02X]\n", buf[0], buf[1]);
此外,正如我在原始回复中所写,您可能需要在读取寄存器内容之前再次写回寄存器地址。
i2c 协议要求您指定设备地址(即 0x00)和寄存器地址。然后您可以将值(在您的情况下为 0x10)写入该寄存器地址。试试这个:
char buf[10];
buf[0] = addr;
buf[1] = [REGISTER ADDRESS];
buf[2] = 0x10;
if(write(file, buf, 3) != 3) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf("Addr: %02x Subaddr: %02x Value: %02x\n\n", buf[0], buf[1], buf[2]);
}
完成此写入后,您应该可以阅读:
if(read(file, buf, 1) != 1) {
printf("Failed to read from the i2c bus. %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Value: %02x\n\n", buf[0]);
}
根据设备树,需要提供给IOCtl 的地址是i2cswitch mux 地址。这个地址是0x74,可以在设备树中看到。打开 i2c-0 设备文件对应于设备树中的 i2c0 条目,它是 mux 的父级。写入 EEPROM 时,缓冲区中的第一个字节应该是设备地址,如@AndrewCottrell 所述。这个地址是 0x54。第二个字节应该是你要写入的数据
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_SWITHC_MUX_ADDRESS 0x74
#define DEVICE_ADDRESS 0x54
...
file = open(I2C_ADAPTER, O_RDWR); /* Check for error */
ioctl(file, I2C_SLAVE_FORCE, I2C_SWITHC_MUX_ADDRESS); /* Check for error */
uint8_t reg, value;
reg = DEVICE_ADDRESS;
buf[0] = reg;
buf[1] = value;
write(file, buf, 2); /* Check for error */
read(file, buf, 1); /* Check for error */
/* buf[0] should be value*/
我在从 NTAG 5 boost 组件的内存中读取数据时也遇到了问题,我在查找此问题的原因时发现了此线程。事实上,对于我在组件上执行的每个 I2C 读取,我都会收到“FF”值。我没有在此线程中找到答案,但也许你在 .
中回答了我的问题
我试图在写入后从 I2C 总线上的内存位置读取一个值。当我在终端中 运行 时,我得到了奇怪的输出。
这是我的程序
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int file;
int addr = 0X00; /* XGPIOPS_DATA_LOW_OFFSET */
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus");
return -1;
}
if(ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Unable to open device as slave %s", strerror(errno));
return -1;
}
char buf[10];
buf[0] = addr;
buf[1] = 0x10;
if(write(file, buf, 2) != 2) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 2) != 2) {
printf("Failed to read from the i2c bus. %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf(buf);
printf("\n\n");
}
return 0;
}
The output from the program looks like this
Successful write
Successful read ��
在我的终端上,这些块看起来更像是钻石内部的问号。我不确定在 ASCII 中对应的是什么。
为什么我不读回我最初写入的地址字节后的第二个字节 0x10?
根据第一组答案,更新后的代码如下:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int file;
long addr, reg_addr;
char *end;
if(argc == 3) {
addr = strtol(argv[1], &end, 16);
printf("Value of addr is: %ld\n", addr);
reg_addr = strtol(argv[2], &end, 16);
printf("Value of reg_addr is: %ld\n", reg_addr);
}
else {
printf("arg failed\n\n.");
addr = 0x00;
}
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus\n");
return -1;
}
if(ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Unable to open device as slave \n%s\n", strerror(errno));
return -1;
}
char buf[10];
buf[0] = addr;
buf[1] = reg_addr;
buf[2] = 0x10;
if(write(file, buf, 3) != 3) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 3) != 3) {
printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Buf = [%02X,%02X,%02X]\n", buf[0], buf[1], buf[2]);
printf("\n\n");
}
return 0;
}
此时,每当我使用 0x00 作为地址时,无论 argv[2] 是什么,我都会得到 FF、FF、FF 作为输出。这是设备树文件的适用部分。请注意,这是被模拟的,所以我无法探测物理设备。
&i2c0 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
i2cswitch@74 {
compatible = "nxp,pca9548";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x74>;
i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
si570: clock-generator@5d {
#clock-cells = <0>;
compatible = "silabs,si570";
temperature-stability = <50>;
reg = <0x5d>;
factory-fout = <156250000>;
clock-frequency = <148500000>;
};
};
i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
eeprom@54 {
compatible = "at,24c08";
reg = <0x54>;
};
};
i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
gpio@21 {
compatible = "ti,tca6416";
reg = <0x21>;
gpio-controller;
#gpio-cells = <2>;
};
};
i2c@4 {
#address-cells = <1>;
#size-cells = <0>;
reg = <4>;
rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
i2c@7 {
#address-cells = <1>;
#size-cells = <0>;
reg = <7>;
hwmon@52 {
compatible = "ti,ucd9248";
reg = <52>;
};
hwmon@53 {
compatible = "ti,ucd9248";
reg = <53>;
};
hwmon@54 {
compatible = "ti,ucd9248";
reg = <54>;
};
};
};
};
这里有几个示例测试
尝试测试 SiLabs 时钟发生器
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x54 0x00
Value of addr is: 84
Value of reg_addr is: 0
Unable to open device as slave
Device or resource busy
尝试测试eeprom设备
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x5d 0x00
Value of addr is: 93
Value of reg_addr is: 0
Unable to open device as slave
Device or resource busy
这是我第三次尝试的程序。考虑到答案中的注释后,我写了这个
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define DEVICE_ADDRESS 0x54
int main (int argc, char *argv[])
{
int file;
uint8_t reg, value;
char *end;
printf("The device address on the bus: %d", DEVICE_ADDRESS);
if(argc == 3) {
reg = strtol(argv[1], &end, 16);
printf("Value of register address: %d\n", reg);
value = strtol(argv[2], &end, 16);
printf("value to write is: %d\n", value);
}
else {
printf("arg failed\n\n.");
}
if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
printf("Failed to open the bus\n");
return -1;
}
if(ioctl(file, I2C_SLAVE, DEVICE_ADDRESS) < 0) {
printf("Unable to open device as slave \n%s\n", strerror(errno));
return -1;
}
char buf[10];
buf[0] = reg;
buf[1] = value;
if(write(file, buf, 2) != 2) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf(buf);
printf("\n\n");
}
if(read(file, buf, 2) != 2) {
printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Buf = [%02X,%02X,%02X]\n", buf[0], buf[1], buf[2]);
printf("\n\n");
}
return 0;
}
不幸的是,我仍然遇到同样的错误。
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x00 0x10
The device address on the bus: 84Value of register address: 0
value to write is: 16
Unable to open device as slave
Device or resource busy
root@plnx_arm:~# /usr/bin/i2c-test-mem-location 0x30 0x10
The device address on the bus: 84Value of register address: 48
value to write is: 16
Unable to open device as slave
Device or resource busy
编辑 2:我认为您可能没有正确设置 I2C 设备地址。你的 I2C_ADAPTER
("/dev/i2c-0"
) 表示设备在哪条 I2C 总线上。您甚至没有使用 I2C_DEVICE
宏,但这是您应该传递给 ioctl
调用的内容(例如 ioctl(file, I2C_SLAVE, I2C_DEVICE);
),它应该是您想要的设备的 I2C 地址访问(例如 0x5D
用于时钟发生器)而不是 0x00
。
我也认为你的reads/writes不正确。一旦你通过 open()
和 ioctl()
指定了总线和设备,你就不需要再担心这些了。您只需要担心要访问的寄存器(如果您的 I2C 设备使用寄存器)和实际数据。
要写入您的 I2C 设备,假设它使用一个字节的寄存器,写入两个字节的缓冲区:第一个是寄存器,第二个是您要写入的值:
bool i2cdev_byte_write(int file, uint8_t reg, uint8_t val)
{
uint8_t bytes[2];
bytes[0] = reg;
bytes[1] = val;
/* Write the register followed by the value */
if (write(file, bytes, 2) != 2)
return false;
return true;
}
要从您的 I2C 设备读取,假设它使用一个字节的寄存器,写入一个字节的缓冲区(寄存器地址),然后读取一个或多个字节的缓冲区(该寄存器和后续寄存器的值) ):
bool i2cdev_bytes_read(int file, uint8_t reg, unsigned int count, uint8_t *out_buf)
{
if (!out_buf)
return false;
/* Write the register */
if (write(file, ®, 1) != 1)
{
printf("Failed to write register value\n");
return false;
}
/* Read the specified number of bytes */
if (read(file, out_buf, count) != count)
{
printf("Failed to read from the i2c bus\n");
return false;
}
return true;
}
再次注意,以上所有评论都取决于它是一个使用单字节寄存器地址的 I2C 设备,并且它支持在一次读取多个字节时自动递增寄存器地址。您需要查看 I2C 设备的数据表以确定需要如何访问它。
编辑:这是一个 printf()
新手失败。您不能只尝试 printf
一个字节数组。这不是 printf()
的工作方式。
试试这个:
printf("Buf = [%02X,%02X]\n", buf[0], buf[1]);
此外,正如我在原始回复中所写,您可能需要在读取寄存器内容之前再次写回寄存器地址。
i2c 协议要求您指定设备地址(即 0x00)和寄存器地址。然后您可以将值(在您的情况下为 0x10)写入该寄存器地址。试试这个:
char buf[10];
buf[0] = addr;
buf[1] = [REGISTER ADDRESS];
buf[2] = 0x10;
if(write(file, buf, 3) != 3) {
printf("Failed to write to bus %s.\n\n", strerror(errno));
}
else {
printf("Successful write\n");
printf("Addr: %02x Subaddr: %02x Value: %02x\n\n", buf[0], buf[1], buf[2]);
}
完成此写入后,您应该可以阅读:
if(read(file, buf, 1) != 1) {
printf("Failed to read from the i2c bus. %s\n\n", strerror(errno));
}
else {
printf("Successful read\n");
printf("Value: %02x\n\n", buf[0]);
}
根据设备树,需要提供给IOCtl 的地址是i2cswitch mux 地址。这个地址是0x74,可以在设备树中看到。打开 i2c-0 设备文件对应于设备树中的 i2c0 条目,它是 mux 的父级。写入 EEPROM 时,缓冲区中的第一个字节应该是设备地址,如@AndrewCottrell 所述。这个地址是 0x54。第二个字节应该是你要写入的数据
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_SWITHC_MUX_ADDRESS 0x74
#define DEVICE_ADDRESS 0x54
...
file = open(I2C_ADAPTER, O_RDWR); /* Check for error */
ioctl(file, I2C_SLAVE_FORCE, I2C_SWITHC_MUX_ADDRESS); /* Check for error */
uint8_t reg, value;
reg = DEVICE_ADDRESS;
buf[0] = reg;
buf[1] = value;
write(file, buf, 2); /* Check for error */
read(file, buf, 1); /* Check for error */
/* buf[0] should be value*/
我在从 NTAG 5 boost 组件的内存中读取数据时也遇到了问题,我在查找此问题的原因时发现了此线程。事实上,对于我在组件上执行的每个 I2C 读取,我都会收到“FF”值。我没有在此线程中找到答案,但也许你在