GNU/Linux >> Tutoriales Linux >  >> Linux

Propósito I2C_SLAVE ioctl

Si usa el read() y write() métodos, llamando a ioctl con I2C_SLAVE una vez es suficiente. También puedes usar I2C_SLAVE_FORCE si el dispositivo ya está en uso.

Sin embargo, aún no he encontrado una forma consistente de leer registros específicos para cada dispositivo usando el read()/write() métodos.


Existen tres métodos principales para comunicarse con dispositivos i2c desde el espacio de usuario.

1. IOCTL I2C_RDWR

Este método permite la lectura/escritura simultánea y el envío de una secuencia ininterrumpida de mensajes. No todos los dispositivos i2c admiten este método.

Antes de realizar operaciones de E/S con este método, debe comprobar si el dispositivo es compatible con este método utilizando un ioctl I2C_FUNCS operación.

Con este método, no necesita realizar un ioctl I2C_SLAVE operación:se realiza entre bastidores utilizando la información incrustada en los mensajes.

2. SMBUS IOCTL

Este método de E/S es más poderoso pero el código resultante es más detallado. Este método se puede utilizar si el dispositivo no es compatible con I2C_RDWR método.

Con este método, haces necesita realizar un ioctl I2C_SLAVE operación (o, si el dispositivo está ocupado, un I2C_SLAVE_FORCE operación).

3. E/S SYSFS

Este método utiliza el archivo básico de llamadas al sistema de E/S read() y write() . Las operaciones secuenciales ininterrumpidas no son posibles con este método. Este método se puede utilizar si el dispositivo no es compatible con I2C_RDWR método.

Con este método, haces necesita realizar un ioctl I2C_SLAVE operación (o, si el dispositivo está ocupado, un I2C_SLAVE_FORCE operación).

No puedo pensar en ninguna situación en la que este método sea preferible a otros, a menos que necesite que el chip sea tratado como un archivo.

Ejemplo completo de IOCTL

No he probado este ejemplo, pero muestra el flujo conceptual de escribir en un dispositivo i2c.-- detectando automáticamente si usar el ioctl I2C_RDWR o técnica 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;
}

No estoy muy seguro de si esto ayuda porque no uso ioctl I2C_RDWR pero he estado usando el siguiente código con éxito:

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, ___);

Todo lo que hago es configurar I2C_SLAVE_FORCE una vez al principio y puedo leer y escribir tanto como quiera después de eso.

PD:esto es solo una muestra de código y, obviamente, debe verificar los retornos de todas estas funciones. Estoy usando este código para comunicarme con un chip de E/S digital. Las dos funciones i2c_* son simplemente envoltorios que llaman a ioctl(fd, I2C_SMBUS, &args); donde args es un tipo de estructura i2c_smbus_ioctl_data.


Linux
  1. ¿El Propósito De Usar Un Fifo Vs Un Archivo Temporal O Una Tubería?

  2. ¿El propósito del comando 'instalar'?

  3. Linux – ¿Propósito del directorio /net?

  4. ¿El propósito del comando hash?

  5. ¿Propósito de usar Setuid() en programas Suid?

Crear un servidor en la nube de propósito general

¿Cuál es el propósito del archivo .bashrc en Linux?

¿Cómo uso ioctl() para manipular mi módulo kernel?

¿Propósito de la dirección de envío para el socket sin formato C?

¿Cuál es el propósito de cd ` (comilla grave)?

¿Cuál es el propósito del directorio setgid?