【问题标题】:I2C IOCTL Write FailureI2C IOCTL 写入失败
【发布时间】:2017-10-24 14:16:31
【问题描述】:

嘿,我正在尝试为运行 PetaLinux(嵌入式 Linux 操作系统)的嵌入式系统编写用户空间应用程序以将一些数据移动到 I2C,尽管我认为这不是影响问题的原因。我收到连接超时和分段错误。

该函数具有指示它写入第一个 I2C 总线的宏。我在 main 中指定要写入的数据并将其传递给 i2c_write,然后将其传递给 i2c_ioctl_write。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.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
#define REG_ADDR 0x00


int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
    printf("i2c_ioctl_write\n");
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0])));
    if (buf == NULL) {
        return -ENOMEM;
    }
    printf("\tBuffer Allocation Successful...\n");

    buf[j ++] = regaddr;
    for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }
    printf("\tBuffer Setup Successful...\n");

    struct i2c_msg messages[] = {
        {
            .addr = dev,
            .buf = buf,
            .len = sizeof(buf) / sizeof(buf[0]),
        },
    };
    printf("\tSetup I2C Messages...\n");

    struct i2c_rdwr_ioctl_data payload = {
        .msgs = messages,
        .nmsgs = sizeof(messages) / sizeof(messages[0]),
    };
    printf("\tSetup I2C IOCTL Payload...\n");


    ret = ioctl(fd, I2C_RDWR, &payload);
    printf("\tWrote with IOCTL...\n");
    if (ret < 0) {
        ret = -errno;
    }

    free (buf);
    return ret;
}

int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
    printf("i2c_ioctl_smbus_write\n");
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(2 * (sizeof(data) / sizeof(data[0])));
    if (buf == NULL) {
        return -ENOMEM;
    }

    for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }

    struct i2c_smbus_ioctl_data payload = {
        .read_write = I2C_SMBUS_WRITE,
        .size = I2C_SMBUS_WORD_DATA,
        .command = regaddr,
        .data = (void *) buf,
    };

    ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

    ret = ioctl (fd, I2C_SMBUS, &payload);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

exit:
    free(buf);
    return ret;
}

int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
    printf("i2x_write\n");
    uint64_t funcs;

    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
        return -errno;
    }

    if (funcs & I2C_FUNC_I2C) {
        return i2c_ioctl_write (fd, dev, regaddr, data);
    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
        return i2c_ioctl_smbus_write (fd, dev, regaddr, data);
    } else {
        return -ENOSYS;
    }
}

int main (int argc, char *argv[])
{
    printf("main\n");
    uint8_t regaddr;
    int fd;
    int ret = 0;

    uint16_t data[] = {1, 2, 4};

    fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
    ret = i2c_write(fd, I2C_DEVICE, REG_ADDR, data);
    close(fd);

    if (ret) {
        fprintf (stderr, "%s.\n", strerror(-ret));
    }

    free(data);

    return ret;
}

当我在 QEMU 上运行程序时,我得到以下输出:

主要 i2x_write i2c_ioctl_write 缓冲区分配成功... 缓冲区设置成功... 设置 I2C 消息 设置 I2C IOCTL 负载 cdns-i2c e0004000.i2c:超时等待完成 用 IOCTL 编写 连接超时。 分段错误

我认为它在线上失败了

ret = ioctl(fd, I2C_RDWR, &payload);

但我不知道为什么。有效载荷是否构造不当?

更新:这是当前代码:

 #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 fd;
    int ret = 0;


    fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);

    uint64_t funcs;

    int addr = 0X00;

    if (ioctl(fd, I2C_SLAVE, addr) < 0) {
        /* ERROR HANDLING; you can check errno to see what went wrong */
            printf("Cannot setup as slave");
            exit(1);
         }

    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
        printf("ioctl failed");
        return -errno;
    }

    printf("funcs & I2C_FUNC_I2C:   %llu\n", funcs & I2C_FUNC_I2C);
    printf("funcs & I2C_FUNC_SMBUS_WORD_DATA:   %llu\n", funcs & I2C_FUNC_SMBUS_WORD_DATA);

    __u8 reg = 0x10;
    __s32 res;

    if (funcs & I2C_FUNC_I2C) {
        char buf[10];
        printf("Attempting to write to I2C bus via I2C protocol...\n");
        buf[0] = reg;
        buf[1] = 0x43;
        buf[2] = 0x65;
        int bytes_written = write(fd, buf, 3);
        if(bytes_written != 3) {
            printf("Wrote %d bytes", bytes_written);
            printf("\tFailed to write to I2C Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tSuccesful write to I2C Bus\n");
        }

        char buf2[10];
        printf("Attempting to read from I2C bus via I2C protocol...\n");
        if(read(fd, buf2, 1) != 1) {
            printf("\tFailed to do I2C read from Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tRead successful. Comparing read results from original write buffer...");
            printf("\t\tWritten value: %c", buf[0]);
            printf("\t\tRead value: %c", buf2[0]);
        }


        return 0;


    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
        printf("Attempting to write to I2C bus via SMBus protocol...\n");
        //res = i2c_smbus_write_word_data(fd, REG_ADDR, 0x6543);
        res = 1;
        if(res < 0) {
            printf("\tFailed to write to I2C Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tSuccesful write to I2C Bus\n");
        }

        //res = i2c_smbus_read_word_data(fd, REG_ADDR);
        if(res < 0) {
            printf("\tFailed to read from I2C Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tRead successful. Comparing read results from original write buffer...");
            printf("\t\tWritten value: %c", 0x6543);
            printf("\t\tRead value: %c", res);
        }
    } else {
        printf("Cannot write to I2C");
        return -ENOSYS;
    }

    close(fd);

    if (ret) {
        fprintf (stderr, "%s.\n", strerror(-ret));
    }

    return ret;
}

我能够通过删除 free() 来摆脱 seg 错误,所以谢谢。我已经在这里查明了 Cadence I2C 驱动程序中发生的超时的确切问题:

https://github.com/Xilinx/linux-xlnx/blob/3f3c7b60919d56119a68813998d3005bca501a40/drivers/i2c/busses/i2c-cadence.c#L825

这仍在发生。

如前所述,我向从站写入的方式可能存在一些问题,导致从站不发送 ACK,从而导致超时。我不确定我需要写哪些寄存器。我感觉需要更改 I2C_DEVICE 宏以及 addr 和 reg 变量。

【问题讨论】:

  • 段错误的明显原因是 - free(data) 在 main() 的末尾。
  • 编译时,始终启用警告,然后修复这些警告。 (对于gcc,至少使用:-Wall -Wextra -pedantic -Wconvertion -std=gnu11 ) To get you started, when the parameters from main()` 不使用,然后使用签名:int main( void )
  • 关于:for (i = 0; i &lt; (sizeof(data) / sizeof(data[0])); i ++) { 变量 'data' 只是一个指针,而不是实际的数组,所以 sizeof(data) 将返回指针的大小,而不是数组的大小和 sizeof(data[0]) will return 2, Similarity this statement: buf = malloc(2 * (sizeof(data) / sizeof(data[0])));` 变为 4/2*2 I.E. 4 因此代码将访问超出分配内存的末尾。这是未定义的行为,并且(如您所见)可能导致段错误事件
  • goto() 的使用明确表明代码逻辑需要重新评估。
  • sizeof 返回 size_t 的类型(无符号),但在此语句中:for (i = 0; i &lt; (sizeof(data) / sizeof(data[0])); i ++) 表示 size_tint (变量“i”)进行比较, 有符号和无符号变量之间的这种比较充满了问题建议尽可能限制变量范围使用:for ( size_t i = 0; i &lt; (sizeof(data) / sizeof(data[0])); i ++)

标签: c linux embedded-linux i2c petalinux


【解决方案1】:

cdns-i2c e0004000.i2c: 超时等待完成

似乎 i2c 驱动程序 (cdns-i2s) 没有收到从站的确认。当您将 I2C 从地址用作 0x00(即general call address)时,可能会发生这种情况。在使用广播呼叫地址时,发送的第二个字节具有特殊用途,在i2c-specification(第 3.1.13 节)中提到。

如果你使用通用调用地址,你需要遵循规范,否则尝试使用确切的 i2c 从地址而不是通用调用地址(0x00)。

【讨论】:

  • 当您说确切地址时,您的意思是查看 Xilinx Zynq 参考手册以找到 i2c 控制器的物理地址并写入该地址?
  • @JohnFrye 不,您必须通过 I2C 向连接到 Xilinx Znyc I2C 控制器的某个从设备发送数据。所以,我是说你参考连接到作为从机的 I2C 的外围设备的数据表。
  • 从从设备的数据表中,您将获得从设备地址。只需将 I2C_DEVICE 替换为该地址
  • 我在 QEMU 上运行,所以我不确定如何判断哪个外设连接到 I2C 以在数据表上查找。
  • 检查stackoverflow.com/questions/18501660/…。检查是否可以在 QEMU 上创建和使用虚拟 I2C 从站。
猜你喜欢
  • 1970-01-01
  • 2012-11-01
  • 1970-01-01
  • 2018-08-26
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
相关资源
最近更新 更多