I2C_SLAVE ioctl цель

Я пишу код для реализации простой i2c функции чтения / записи с использованием общего i2c драйвера linux linux/i2c-dev.h

Меня смущает ioctl: I2C_SLAVE

В документации ядра указано следующее:

Вы можете выполнять простые транзакции i2c, используя вызовы read (2) и write (2). Байт адреса передавать не нужно; вместо этого установите его через ioctl I2C_SLAVE, прежде чем пытаться получить доступ к устройству

Однако я использую ioctl I2C_RDWR, где я снова устанавливаю адрес подчиненного устройства с помощью i2c_msg.addr.

В документации ядра также упоминается следующее:

Некоторые вызовы ioctl () предназначены для административных задач и обрабатываются i2c-dev напрямую. Примеры включают I2C_SLAVE

Так нужно ли использовать ioctl I2C_SLAVE? Если да, нужно ли мне устанавливать его только один раз или каждый раз, когда я выполняю чтение и запись?

Если бы у меня было i2c устройство, я бы просто протестировал код на устройстве и не беспокоил бы вас, ребята, но, к сожалению, у меня его сейчас нет.

Спасибо за помощь.


person zacurry    schedule 02.04.2012    source источник


Ответы (4)


Существует три основных метода связи с устройствами i2c из пользовательского пространства.

1. IOCTL I2C_RDWR

Этот метод позволяет одновременно читать / писать и отправлять непрерывную последовательность сообщений. Не все устройства i2c поддерживают этот метод.

Перед выполнением ввода-вывода с помощью этого метода вы должны проверить, поддерживает ли устройство этот метод, с помощью операции ioctl I2C_FUNCS.

Используя этот метод, вам не нужно выполнять операцию ioctl I2C_SLAVE - это делается за кулисами с использованием информации, встроенной в сообщения.

2. IOCTL SMBUS

Этот метод ввода-вывода более мощный, но результирующий код более подробный. Этот метод можно использовать, если устройство не поддерживает метод I2C_RDWR.

Используя этот метод, вам необходимо выполнить операцию ioctl I2C_SLAVE (или, если устройство занято, операцию I2C_SLAVE_FORCE).

3. Ввод-вывод SYSFS

В этом методе используются основные системные вызовы файлового ввода-вывода read() и _ 7_. При использовании этого метода невозможны непрерывные последовательные операции. Этот метод можно использовать, если устройство не поддерживает метод I2C_RDWR.

Используя этот метод, вам необходимо выполнить операцию ioctl I2C_SLAVE (или, если устройство занято, операцию I2C_SLAVE_FORCE).

Я не могу представить себе ситуацию, когда этот метод был бы предпочтительнее других, если только вам не нужно, чтобы чип обрабатывался как файл.


Полный пример IOCTL

Я не тестировал этот пример, но он показывает концептуальный поток записи на устройство i2c. - автоматическое определение, использовать ли метод ioctl I2C_RDWR или smbus.

#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

int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    int i, j = 0;
    int ret;
    uint8_t *buf;
    // the extra byte is for the regaddr
    size_t buff_size = 1 + size;

    buf = malloc(buff_size);
    if (buf == NULL) {
        return -ENOMEM;
    }

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

    struct i2c_msg messages[] = {
        {
            .addr = dev,
            .buf = buf,
            .len = buff_size,
        },
    };

    struct i2c_rdwr_ioctl_data payload = {
        .msgs = messages,
        .nmsgs = sizeof(messages) / sizeof(messages[0]),
    };

    ret = ioctl(fd, I2C_RDWR, &payload);
    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, size_t size)
{
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(size);
    if (buf == NULL) {
        return -ENOMEM;
    }

    for (i = 0; i < size / sizeof(uint16_t); 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, size_t size)
{
    unsigned long funcs;

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

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

int parse_args (uint8_t *regaddr, uint16_t *data, size_t size, char *argv[])
{
    char *endptr;
    int i;

    *regaddr = (uint8_t) strtol(argv[1], &endptr, 0);
    if (errno || endptr == argv[1]) {
        return -1;
    }

    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        data[i] = (uint16_t) strtol(argv[i + 2], &endptr, 0);
        if (errno || endptr == argv[i + 2]) {
            return -1;
        }
    }

    return 0;
}

void usage (int argc, char *argv[])
{
    fprintf(stderr, "Usage: %s regaddr data [data]*\n", argv[0]);
    fprintf(stderr, "  regaddr   The 8-bit register address to write to.\n");
    fprintf(stderr, "  data      The 16-bit data to be written.\n");
    exit(-1);
}

int main (int argc, char *argv[])
{
    uint8_t regaddr;
    uint16_t *data;
    size_t size;
    int fd;
    int ret = 0;

    if (argc < 3) {
        usage(argc, argv);
    }

    size = (argc - 2) * sizeof(uint16_t);
    data = malloc(size);
    if (data == NULL) {
        fprintf (stderr, "%s.\n", strerror(ENOMEM));
        return -ENOMEM;
    }

    if (parse_args(&regaddr, data, size, argv) != 0) {
        free(data);
        usage(argc, argv);
    }

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

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

    free(data);

    return ret;
}
person Woodrow Barlow    schedule 14.07.2016
comment
В примере кода используется sizeof(data), когда data является указателем, который всегда возвращает указатель sizeof (4 или 8 в обычных ОС). Вам нужно передать размер данных, нельзя использовать sizeof вот так! - person hyde; 16.11.2017
comment
Не могли бы вы добавить пример команды выполнения? .. Это будет более полезно. - person Ashok; 19.04.2018
comment
@hyde (сильно опоздал, но ...) ты прав. Я обновил образец кода, чтобы удалить эту ошибку. - person Woodrow Barlow; 12.07.2018
comment
@Naresh ./program 0xD 0x1337 0xdead 0xbeef (см. Также функцию использования) - person Woodrow Barlow; 12.07.2018

Я не уверен, поможет ли это, потому что я не использую ioctl I2C_RDWR, но я успешно использовал следующий код:

int fd;
fd = open("/dev/i2c-5", O_RDWR);
ioctl(fd, I2C_SLAVE_FORCE, 0x20);
i2c_smbus_write_word_data(fd, ___, ___);
i2c_smbus_read_word_data(fd, ___);

Все, что я делаю, это устанавливаю I2C_SLAVE_FORCE один раз в начале, и после этого я могу читать и писать столько, сколько захочу.

PS - Это всего лишь образец кода, и, очевидно, вам следует проверить возврат всех этих функций. Я использую этот код для связи с микросхемой цифрового ввода-вывода. Две функции i2c_ * - это просто оболочки, вызывающие ioctl (fd, I2C_SMBUS, & args); где args - это тип struct i2c_smbus_ioctl_data.

person Kristina    schedule 23.04.2012
comment
Почему работает I2C_SLAVE_FORCE, а не I2C_SLAVE (в случае, когда мы впервые используем драйвер)? - person AwaX; 25.11.2016
comment
Прошло много времени с тех пор, как я работал над кодом I2C, но программа, над которой я работал, была пользовательской программой для пользовательской встроенной платы. Я предполагаю, что драйвер уже был подключен к устройству, и поэтому я использовал _FORCE. - person Kristina; 04.12.2016

Если вы используете методы read() и write(), достаточно один раз вызвать ioctl с I2C_SLAVE. Вы также можете использовать I2C_SLAVE_FORCE, если устройство уже используется.

Однако я еще не нашел последовательного способа чтения определенных регистров для каждого устройства с использованием read()/write() методов.

person 3c71    schedule 02.04.2012
comment
Спасибо за информацию, но, как я уже упоминал, я использую I2C_RDWR ioctl, поэтому мне нужно использовать I2C_SLAVE ioctl? - person zacurry; 03.04.2012

Для заинтересованных SLAVE_FORCE используется, когда рассматриваемое устройство уже управляется драйвером ядра. (i2cdetect покажет UU для этого адреса)

person PaperClip    schedule 25.06.2019