一般的なLinuxi2c
ドライバーi2c
を使用して単純なlinux/i2c-dev.h
読み取り/書き込み関数を実装するためのコードを書いています。
ioctl
:I2C_SLAVE
について混乱しています
カーネルのドキュメントには次のように記載されています。
Read(2)およびwrite(2)呼び出しを使用して、プレーンなi2cトランザクションを実行できます。アドレスバイトを渡す必要はありません。代わりに、デバイスにアクセスする前に、ioctlI2C_SLAVEを使用して設定してください
ただし、ioctl I2C_RDWR
を使用しており、i2c_msg.addr
を使用してスレーブアドレスを再度設定しています。
カーネルのドキュメントには、次のことも記載されています。
一部のioctl()呼び出しは管理タスク用であり、i2c-devによって直接処理されます。例にはI2C_SLAVEが含まれます
では、ioctl I2C_SLAVE
を使用する必要がありますか?その場合、読み取りと書き込みを実行するたびに1回だけ設定する必要がありますか?
i2c
デバイスがあれば、デバイスでコードをテストするだけで、気にならなかったでしょうが、残念ながら、現在は持っていません。
助けてくれてありがとう。
ユーザースペースからi2cデバイスと通信する主な方法は3つあります。
この方法では、メッセージの連続した読み取り/書き込みと送信を同時に行うことができます。すべてのi2cデバイスがこの方法をサポートしているわけではありません。
このメソッドでI/Oを実行する前に、デバイスがioctl _I2C_FUNCS
_操作を使用してこのメソッドをサポートしているかどうかを確認する必要があります。
このメソッドを使用すると、ioctl _I2C_SLAVE
_操作を実行する必要はありません。これは、メッセージに埋め込まれた情報を使用してバックグラウンドで実行されます。
このI/Oの方法はより強力ですが、結果のコードはより冗長になります。このメソッドは、デバイスが_I2C_RDWR
_メソッドをサポートしていない場合に使用できます。
このメソッドを使用して、doはioctl _I2C_SLAVE
_操作(または、デバイスがビジーの場合は_I2C_SLAVE_FORCE
_操作)を実行する必要があります。
このメソッドは、基本的なファイルI/Oシステムコールを使用します read()
および write()
。この方法では、中断のない順次操作はできません。このメソッドは、デバイスが_I2C_RDWR
_メソッドをサポートしていない場合に使用できます。
このメソッドを使用して、doはioctl _I2C_SLAVE
_操作(または、デバイスがビジーの場合は_I2C_SLAVE_FORCE
_操作)を実行する必要があります。
チップをファイルのように扱う必要がない限り、この方法が他の方法よりも好ましい状況は考えられません。
この例はテストしていませんが、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(®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;
}
_
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-これは単なるコードサンプルであり、明らかにこれらすべての関数の戻り値を確認する必要があります。このコードを使用して、デジタルI/Oチップと通信しています。 2つのi2c_ *関数は、ioctl(fd、I2C_SMBUS、&args);を呼び出す単なるラッパーです。ここで、argsはstructi2c_smbus_ioctl_dataタイプです。
read()
メソッドとwrite()
メソッドを使用する場合は、ioctl
を_I2C_SLAVE
_で1回呼び出すだけで十分です。デバイスがすでに使用されている場合は、_I2C_SLAVE_FORCE
_を使用することもできます。
ただし、read()/write()
メソッドを使用して、すべてのデバイスの特定のレジスタを読み取る一貫した方法をまだ見つけていません。
興味のある方は、問題のデバイスがすでにカーネルドライバーによって管理されている場合に、SLAVE_FORCEが使用されます。 (i2cdetectはそのアドレスのUUを表示します)