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(®addr, 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.