I2C ведомая цель ioctl
Я пишу код для реализации простой i2c
функция чтения / записи с помощью общего linux i2c
водитель 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
устройство я мог бы просто проверить код на устройстве и не беспокоил бы вас, ребята, но, к сожалению, у меня его сейчас нет.
Спасибо за помощь.
3 ответов
существует три основных метода связи с устройствами i2c из пользовательского пространства.
1. ФУНКЦИИ IOCTL I2C_RDWR
этот метод позволяет одновременно читать / писать и отправлять непрерывную последовательность сообщений. Не все устройства i2c поддерживают этот метод.
перед выполнением ввода-вывода с помощью этого метода необходимо проверить, поддерживает ли устройство этот метод с помощью ioctl I2C_FUNCS
операции.
используя этот метод, вы сделать не необходимо выполнить ioctl I2C_SLAVE
операция -- это делается за кулисами, используя информацию, встроенную в сообщения.
2. ФУНКЦИИ IOCTL ШИНЫ SMBUS
этот метод ввода-вывода более мощный, но полученный код более подробный. Этот метод можно использовать, если устройство не поддерживает I2C_RDWR
метод.
используя этот метод, вы do необходимо выполнить ioctl I2C_SLAVE
операция (или, если устройство занято,I2C_SLAVE_FORCE
операция.)
3. SYSFS I / O
этот метод использует основные системные вызовы ввода-выводаread()
и write()
. Непрерывные последовательные операции невозможны с помощью этого метода. Этот метод можно использовать, если устройство не поддерживает I2C_RDWR
метод.
используя этот метод, вы do необходимо выполнить ioctl I2C_SLAVE
операция (или, если устройство занято,I2C_SLAVE_FORCE
операции).
I не могу придумать ни одной ситуации, когда этот метод был бы предпочтительнее других, если вам не нужно, чтобы чип обрабатывался как файл.
полный пример 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(®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;
}
если вы используете read()
и write()
методы, называя ioctl
С I2C_SLAVE
одного раза достаточно. Вы также можете использовать I2C_SLAVE_FORCE
если устройство уже используется.
однако я еще не нашел последовательный способ чтения конкретных регистров для каждого устройства с помощью read()/write()
методы.
Я не слишком уверен, что это помогает, потому что я не использую 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 является типом структуры i2c_smbus_ioctl_data.