Si vous utilisez le read()
et write()
méthodes, appelant ioctl
avec I2C_SLAVE
Une fois est assez. Vous pouvez également utiliser I2C_SLAVE_FORCE
si l'appareil est déjà utilisé.
Cependant, je n'ai pas encore trouvé de moyen cohérent de lire des registres spécifiques pour chaque appareil en utilisant le read()/write()
méthodes.
Il existe trois méthodes principales de communication avec les appareils i2c depuis l'espace utilisateur.
1. IOCTL I2C_RDWR
Cette méthode permet une lecture/écriture simultanée et l'envoi d'une séquence ininterrompue de messages. Tous les appareils i2c ne prennent pas en charge cette méthode.
Avant d'effectuer des E/S avec cette méthode, vous devez vérifier si le périphérique prend en charge cette méthode à l'aide d'un ioctl I2C_FUNCS
opération.
En utilisant cette méthode, vous ne faites pas besoin d'effectuer un ioctl I2C_SLAVE
opération :elle est effectuée en coulisses à l'aide des informations intégrées dans les messages.
2. IOCTL SMBUS
Cette méthode d'e/s est plus puissante mais le code résultant est plus verbeux. Cette méthode peut être utilisée si l'appareil ne prend pas en charge le I2C_RDWR
méthode.
En utilisant cette méthode, vous faites besoin d'effectuer un ioctl I2C_SLAVE
opération (ou, si l'appareil est occupé, un I2C_SLAVE_FORCE
opération).
3. E/S SYSFS
Cette méthode utilise les appels système d'e/s de fichier de base read()
et write()
. Des opérations séquentielles ininterrompues ne sont pas possibles avec cette méthode. Cette méthode peut être utilisée si l'appareil ne prend pas en charge le I2C_RDWR
méthode.
En utilisant cette méthode, vous faites besoin d'effectuer un ioctl I2C_SLAVE
opération (ou, si l'appareil est occupé, un I2C_SLAVE_FORCE
opération).
Je ne vois aucune situation où cette méthode serait préférable à d'autres, à moins que vous n'ayez besoin que la puce soit traitée comme un fichier.
Exemple IOCTL complet
Je n'ai pas testé cet exemple, mais il montre le flux conceptuel d'écriture sur un périphérique i2c.-- détectant automatiquement s'il faut utiliser l'ioctl I2C_RDWR
ou technique 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;
}
Je ne sais pas trop si cela aide car je n'utilise pas ioctl I2C_RDWR mais j'utilise le code suivant avec succès :
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, ___);
Tout ce que je fais est de définir I2C_SLAVE_FORCE une fois au début et je peux lire et écrire autant que je veux après cela.
PS - Ceci n'est qu'un exemple de code et vous devriez évidemment vérifier les retours de toutes ces fonctions. J'utilise ce code pour communiquer avec une puce d'E/S numérique. Les deux fonctions i2c_* ne sont que des wrappers qui appellent ioctl(fd, I2C_SMBUS, &args); où args est un type struct i2c_smbus_ioctl_data.