Communicate with I2C devices in Linux C

Introduction

When you want to use an I2C device with Raspberry Pi or Jetson nano, RasPi has libraries such as pigpio and WiringPi, but Jetson has only Jetson.GPIO, and you may have trouble when you want to write in C / C ++. (Are you in trouble ??)

For such a case, I wrote an article about how to communicate with an I2C device using a general-purpose Linux I2C driver.

Confirmation environment

Source code

There seem to be several ways to communicate with the I2C device, but this code uses the ioctl I2C_RDWR.

i2c_example.c


#include <stdint.h>
// ...
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

static const char* dev_name = "/dev/i2c-1";

/*!Read data from I2C slave device.
 * @param[in] dev_addr device address.
 * @param[in] reg_addr register address.
 * @param[out]data A pointer to the storage location of the data to be read.
 * @param[in]length Length of data to read.
 */
int8_t i2c_read(
    uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
  /*Open the I2C device. */
  int32_t fd = open(dev_name, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_read: failed to open: %s\n", strerror(errno));
    return -1;
  }

  /* I2C-Create a Read message. */
  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr },         /*Set register address. */
      { dev_addr, I2C_M_RD, length, data },  /*Read length bytes into data. */
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  /* I2C-Read. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 2) {
    fprintf(stderr, "i2c_read: failed to ioctl: %s\n", strerror(errno));
    close(fd);
    return -1;
  }

  close(fd);
  return 0;
}

/*!Write data to I2C slave device.
 * @param[in] dev_addr device address.
 * @param[in] reg_addr register address.
 * @param[in]data A pointer to the storage location of the data to be written.
 * @param[in]length Length of data to write.
 */
int8_t i2c_write(
    uint8_t dev_addr, uint8_t reg_addr, const uint8_t* data, uint16_t length) {
  /*Open the I2C device. */
  int32_t fd = open(dev_name, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_write: failed to open: %s\n", strerror(errno));
    return -1;
  }

  /* I2C-Prepare a buffer for Write. */
  uint8_t* buffer = (uint8_t*)malloc(length + 1);
  if (buffer == NULL) {
    fprintf(stderr, "i2c_write: failed to memory allocate\n");
    close(fd);
    return -1;
  }
  buffer[0] = reg_addr;              /*Set the register address in the 1st byte. */
  memcpy(&buffer[1], data, length);  /*Set data after the second byte. */

  /* I2C-Create a Write message. */
  struct i2c_msg message = { dev_addr, 0, length + 1, buffer };
  struct i2c_rdwr_ioctl_data ioctl_data = { &message, 1 };

  /* I2C-Write. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 1) {
    fprintf(stderr, "i2c_write: failed to ioctl: %s\n", strerror(errno));
    free(buffer);
    close(fd);
    return -1;
  }

  free(buffer);
  close(fd);
  return 0;
}

Commentary

First of all, the header include and the device name definition.

python


#include <linux/i2c-dev.h>
#include <linux/i2c.h>

static const char* dev_name = "/dev/i2c-1";

The pin assignments for the RasPi and Jetson nano I2C Bus1 are the same, and the device name is the same for / dev / i2c-1.

Read process

python


  /* I2C-Create a Read message. */
  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr },         /*Set register address. */
      { dev_addr, I2C_M_RD, length, data },  /*Read length bytes into data. */
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  /* I2C-Read. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 2) {

Two i2c_msg structures are required for Read. Set the register address first, The size to be read second and the storage location of the data are specified.

Write process

python


  /* I2C-Prepare a buffer for Write. */
  uint8_t* buffer = (uint8_t*)malloc(length + 1);
  buffer[0] = reg_addr;              /*Set the register address in the 1st byte. */
  memcpy(&buffer[1], data, length);  /*Set data after the second byte. */

  /* I2C-Create a Write message. */
  struct i2c_msg message = { dev_addr, 0, length + 1, buffer };
  struct i2c_rdwr_ioctl_data ioctl_data = { &message, 1 };

  /* I2C-Write. */
  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 1) {

At the time of Write, a buffer of "length of data to be written + 1" is required. Set the register address in the 1st byte, Set the data after the second byte.

The i2c_msg structure will be one.

in conclusion

I was able to communicate with the I2C device on both the RasPi and Jetson Nano with this code, but please note that the code and content may be incorrect.

Recommended Posts

Communicate with I2C devices in Linux C
Segfault with 16 characters in C language
X86 assembler on Linux (linkage with C)
[C] [python] Read with AquesTalk on Linux
Create Amazon Linux with AWS EC2 and log in
Try embedding Python in a C ++ program with pybind11
Library for measuring execution time in Linux C applications
Handle signals in C
Container-like # 1 made with C
Linux permissions in Java
Debugging C / C ++ with gdb
Linux (Lubuntu) with OneMix3S
Access MongoDB in C
Seurat in Linux (installation)
C API in Python 3
Build Azure Pipelies with Azure DevOps in a Linux self-hosted environment
Access the C structure field with the name reserved in Go.