GNU/Linux >> Belajar Linux >  >> Linux

I2C_SLAVE tujuan ioctl

Jika Anda menggunakan read() dan write() metode, memanggil ioctl dengan I2C_SLAVE sekali sudah cukup. Anda juga dapat menggunakan I2C_SLAVE_FORCE jika perangkat sudah digunakan.

Namun saya belum menemukan cara yang konsisten untuk membaca register khusus untuk setiap perangkat menggunakan read()/write() metode.


Ada tiga metode utama untuk berkomunikasi dengan perangkat i2c dari ruang pengguna.

1. IOCTL I2C_RDWR

Metode ini memungkinkan untuk membaca/menulis secara bersamaan dan mengirim urutan pesan tanpa gangguan. Tidak semua perangkat i2c mendukung metode ini.

Sebelum melakukan i/o dengan metode ini, Anda harus memeriksa apakah perangkat mendukung metode ini menggunakan ioctl I2C_FUNCS operasi.

Dengan menggunakan metode ini, Anda tidak perlu melakukan ioctl I2C_SLAVE operasi -- dilakukan di balik layar menggunakan informasi yang disematkan dalam pesan.

2. SMBUS IOCTL

Metode i/o ini lebih bertenaga tetapi kode yang dihasilkan lebih bertele-tele. Metode ini dapat digunakan jika perangkat tidak mendukung I2C_RDWR metode.

Dengan menggunakan metode ini, Anda lakukan perlu melakukan ioctl I2C_SLAVE operasi (atau, jika perangkat sedang sibuk, sebuah I2C_SLAVE_FORCE operasi).

3. SYSFS I/O

Metode ini menggunakan panggilan sistem i/o file dasar read() dan write() . Operasi berurutan yang tidak terputus tidak dimungkinkan menggunakan metode ini. Metode ini dapat digunakan jika perangkat tidak mendukung I2C_RDWR metode.

Dengan menggunakan metode ini, Anda lakukan perlu melakukan ioctl I2C_SLAVE operasi (atau, jika perangkat sedang sibuk, sebuah I2C_SLAVE_FORCE operasi).

Saya tidak dapat memikirkan situasi apa pun ketika metode ini lebih disukai daripada yang lain, kecuali jika Anda memerlukan chip untuk diperlakukan seperti file.

Contoh IOCTL Lengkap

Saya belum menguji contoh ini, tetapi ini menunjukkan aliran konseptual penulisan ke perangkat i2c.-- secara otomatis mendeteksi apakah akan menggunakan ioctl I2C_RDWR atau teknik 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;
}

Saya tidak terlalu yakin apakah ini membantu karena saya tidak menggunakan ioctl I2C_RDWR tetapi saya telah berhasil menggunakan kode berikut:

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

Yang saya lakukan hanyalah menyetel I2C_SLAVE_FORCE satu kali di awal dan saya dapat membaca dan menulis sebanyak yang saya mau setelah itu.

PS - Ini hanyalah contoh kode dan jelas Anda harus memeriksa pengembalian semua fungsi ini. Saya menggunakan kode ini untuk berkomunikasi dengan chip I/O digital. Kedua fungsi i2c_* hanyalah pembungkus yang memanggil ioctl(fd, I2C_SMBUS, &args); di mana args adalah tipe struct i2c_smbus_ioctl_data.


Linux
  1. Buat gambar Server Cloud v1 Tujuan Umum

  2. Bermigrasi ke server Tujuan Umum atau I/O

  3. Apa tujuan dari pengguna "mysql.sys@localhost"?

  1. Apa tujuan dari grup "roda" di Linux?

  2. Apa tujuan dari file .bashrc di Linux?

  3. Pengandar perangkat IOCTL Linux

  1. Dapatkan alamat IPv6 di linux menggunakan ioctl

  2. Apa tujuan dari cd ` (backtick)?

  3. Apa tujuan dari direktori setgid?