Difference between revisions of "I2C i2c-dev"

[quality revision] [pending revision]
m (Replaced content with "<noinclude> {{ArticleMainWriter|Pierre-YvesM}} {{ArticleApprovedVersion | Jean-ChristopheT | Nobody | No previous approved version | Automatic approval (article under cons...")
(Tag: Replaced)
m
 
Under construction.png Coming soon

This section will describe how to access an I2C peripheral using the i2c-dev built-in kernel driver.
It's a driver offering I2C bus access to user space application.
This driver is a character device driver creating an access trough /dev/i2c-x to access peripherals.

1 Open/Close an I2C bus[edit]

Open an I2C bus through /dev/i2c-x with x the bus number connected to the peripheral :


int fd;
const char *filename = "/dev/i2c-x"; /* x is the bus number */

/* open i2c-x bus */
if ((fd = open(filename, O_RDWR)) < 0) {
	perror("Failed to open i2c bus");
	exit(EXIT_FAILURE);
}

Close the bus :


/* close i2c-x bus */
if(close(fd) < 0) {
	perror("Failed to close i2c bus");
	exit(EXIT_FAILURE);
}

2 Set the peripheral address[edit]

Set a peripheral slave address for the communication


int slaveAddr = 0xXX;                /* XX is the slave address */

if (ioctl(fd, I2C_SLAVE, slaveAddr) < 0) {
	perror("fail to set slave address");
	close(fd);
	exit(EXIT_FAILURE);
}

3 Check I2C functionalities[edit]

If you try to access an adapter from a userspace program, you will have to check whether the functionality you need is supported. This is done using the I2C_FUNCS ioctl.


int32_t funcs;

if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
	perror("error handling");
	exit(EXIT_FAILURE);
}
if (!(funcs & I2C_FUNC_SMBUS_QUICK)) {
	/* Oops, the needed functionality (SMBus write_quick function) is
        not available! */
	exit(EXIT_FAILURE);
}

Functionality macros are defined in /include/uapi/linux/i2c.h

4 Access I2C peripheral data[edit]

i2c-dev kernel driver is a character device driver allowing access to I2C peripherals through the /dev interface.
This driver creates a file descriptor for each I2C bus like /dev/i2c-0.
Common Linux system calls (read(), write() and ioctl()) are available to access all peripherals through those file descriptors.

4.1 SMBus commands[edit]

It's possible to access an I2C peripheral using SMBus protocol with a specific ioctl() command.
C example reading a designated register addr :


struct i2c_smbus_ioctl_data args;
union i2c_smbus_data data;
char addr = 0xXX;			/* XX is the register peripheral address */
char value = 0x00;
int ret;

args.read_write = I2C_SMBUS_READ;
args.command = addr;
args.size = I2C_SMBUS_BYTE_DATA;
args.data = &data;

ret = ioctl(fd, I2C_SMBUS, &args);
if (ret < 0)
	perror("error handling");

value = 0x0FF & data.byte;

Macros and data types are described into /include/uapi/linux/i2c-dev.h and /include/uapi/linux/i2c.h.

In order to simplify C style using SMBus ioctl, i2c-tools package provides an interface for using SMBus protocol.
Extract of smbus.h included on i2c-tools package :


extern __s32 i2c_smbus_write_quick(int file, __u8 value);
extern __s32 i2c_smbus_read_byte(int file);
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);

Example using SMBus functions :


/* Read a byte */
char value;
int registerAddr = 0xXX;             /* XX is the register peripheral address */
value = i2c_smbus_read_byte_data(fd, registerAddr);

/* Write a byte */
char registerAddr = 0xXX;            /* XX is the register peripheral address */
char value = 0xXX;                   /* XX is the value to write */
int err;
err = i2c_smbus_write_byte_data(fd, registerAddr, value);

SMBus API and C example at /Documentation/i2c/dev-interface.
relations between SMBus functions and protocol at /Documentation/i2c/smbus-protocol

4.2 read/write system calls[edit]

Use write() system call to set the index reader position at the register address to read and use read() system call to read data from the index reader.
Both functions return negative errno, or else the number of bytes write/read.


/**
 * set_pointer - perform a write to set the address pointer for future read
 * @fd: file descriptor of the i2c adapter
 * @addr: device address to point
 **/
int set_pointer(int fd, char addr)
{
	int ret;

	ret = write(fd, &addr, 1);
	if (ret < 0)
		perror("write failed");

	return ret;
}

/**
 * read_register() - Read register using read system call
 * @fd: file descriptor of the i2c adapter
 * @addr: device address to read
 * @buf: buffer to store the result
 **/
int read_register(int fd, char addr, char *buf)
{
	int ret;

	ret = set_pointer(fd, addr);
	if (ret < 0) {
		printf("set pointer failed\n");
		return ret;
	}

	ret = read(fd, buf, 1);
	if (ret < 0) {
		perror("read failed");
	}

	return ret;
}

/**
 * write_register() - Write register using write system call
 * @fd: file descriptor of the i2c adapter
 * @addr: device address to write
 * @value: value to write
 **/
int write_register(int fd, char addr, char value)
{
	int ret;
	char buf[2];
	buf[0] = addr;
	buf[1] = value;

	ret = write(fd, buf, 2);
	if (ret < 0)
		perror("write failed");

	return ret;
}

5 Documentations and programming examples[edit]

Further functions descriptions + code example :
http://elinux.org/Interfacing_with_I2C_Devices

5.1 API[edit]

https://www.kernel.org/doc/Documentation/i2c/dev-interface

5.2 Full example[edit]

Implementation architecture

The application example below communicates with a sensor expansion board for STM32 Nucleo.
This board contains four sensor communicating via I2C :

  • A capacitive digital sensor for relative humidity and temperature (HTS221),
  • A 3D accelerometer and 3D gyroscope (LSM6DS0),
  • A 3D magnetometer (LIS3MDL),
  • And a pressure sensor (LPS25HB).

This example informs the user if all peripherals included on the expansion board are correctly detected by reading their name registers using different methods.
Methods used in this example are :

  • Read a register using the SMBus protocol trough an ioctl() system call.
  • Read a register using a SMBus function included into the i2c-tools library.
  • Read a register using common read()/write() system calls.

Result of the application can be :


[SUCCESS]: HTS221  sensor is correctly detected
[SUCCESS]: LPS25HB sensor is correctly detected
[SUCCESS]: LSM6DS0 sensor is correctly detected
[SUCCESS]: LIS3MDL sensor is correctly detected

or,


dmesg
[FAILED]: HTS221 sensor  is not detected, ID=0x0 (expected 0xBC)
[FAILED]: LPS25HB sensor is not detected, ID=0x0 (expected 0xBD)
[FAILED]: LSM6DS0 sensor is not detected, ID=0x0 (expected 0x68)
[FAILED]: LIS3MDL sensor is not detected, ID=0x0 (expected 0x3D)

download source code



#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

#define ID_REG				0x0F

#define HTS221_ID			0xBC
#define LPS25HB_ID			0xBD
#define LSM6DS0_ID			0x68
#define LIS3MDL_ID			0x3D

#define HTS221_ADDRESS		0x5F
#define LPS25HB_ADDRESS		0x5D
#define LSM6DS0_ADDRESS		0x6B
#define LIS3MDL_ADDRESS		0x1E

static int nucleo_read_smbus(int fd, char addr, char *buf)
{
	struct i2c_smbus_ioctl_data args;
	union i2c_smbus_data data;
	int ret;

	args.read_write = I2C_SMBUS_READ;
	args.command = addr;
	args.size = I2C_SMBUS_BYTE_DATA;
	args.data = &data;

	ret = ioctl(fd, I2C_SMBUS, &args);
	if (ret < 0)
		perror("failed ioctl");

	*buf = 0x0FF & data.byte;

	return ret;

}

static int nucleo_read_register(int fd, char addr, char *buf)
{
	int ret;
	char out_buf = addr;

	/* Use write function to put index reader at the ID register */
	ret = write(fd, &out_buf, 1);
	if (ret < 0) {
		perror("write failed");
	} else {
		ret = read(fd, buf, 1);
		if (ret < 0) {
			perror("read failed");
		}
	}
	return ret;
}

int main(int argc, char *argv[])
{
	int fd;
	int ret;
	char who_is_it = 0x00;
	const char *filename = "/dev/i2c-0";

	/* open i2c-0 bus */
	fd = open(filename, O_RDWR);
	if (fd < 0) {
		perror("Failed to open i2c bus");
		return EXIT_FAILURE;
	}

	/*****************************************************************************
	 *					Detect HTS221 device using smbus ioctl
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, HTS221_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform smbus command to read the ID register */
	ret = nucleo_read_smbus(fd, ID_REG, &who_is_it);
	if (ret < 0)
		perror("Failed to read device using smbus command");

	/* verify if the device found is the good one */
	if (who_is_it == HTS221_ID)
		printf("[SUCCESS]: HTS221  sensor is correctly detected\n");
	else
		printf("[FAILED]: HTS221 sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, HTS221_ID);

	/*****************************************************************************
	 *					Detect LPS25HB device using smbus
	 *					function included in smbus.h
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, LPS25HB_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform smbus function to read the ID register */
	who_is_it = i2c_smbus_read_byte_data(fd, ID_REG);
	
	/* verify if the device found is the good one */
	if (who_is_it == LPS25HB_ID)
		printf("[SUCCESS]: LPS25HB sensor is correctly detected\n");
	else
		printf("[FAILED]: LPS25HB sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, LPS25HB_ID);

	/*****************************************************************************
	 *				Detect LSM6DS0 device using write/read methods
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, LSM6DS0_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform write/read command to read the ID register */
	ret = nucleo_read_register(fd, ID_REG, &who_is_it);
	if (ret < 0)
		perror("Failed to read device using smbus command");

	/* verify if the device found is the good one */
	if (who_is_it == LSM6DS0_ID)
		printf("[SUCCESS]: LSM6DS0 sensor is correctly detected\n");
	else
		printf("[FAILED]: LSM6DS0 sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, LSM6DS0_ID);

	/*****************************************************************************
	 *				Detect LIS3MDL device using write/read methods
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, LIS3MDL_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform write/read command to read the ID register */
	ret = nucleo_read_register(fd, ID_REG, &who_is_it);
	if (ret < 0)
		perror("Failed to read device using smbus command");

	/* verify if the device found is the good one */
	if (who_is_it == LIS3MDL_ID)
		printf("[SUCCESS]: LIS3MDL sensor is correctly detected\n");
	else
		printf("[FAILED]: LIS3MDL sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, LIS3MDL_ID);

	/* close i2c-0 bus */
	if (close(fd) < 0) {
		perror("Failed to close i2c bus");
		return EXIT_FAILURE;
	} else {
		return 0;
	}
}


Help for compilation : BitBake_cheat_sheet

<noinclude>

{{ArticleMainWriter|Pierre-YvesM}}
{{ArticleApprovedVersion | Jean-ChristopheT | Nobody | No previous approved version | Automatic approval (article under construction) | 19Feb’19}}
[[Category:I2C]]</noinclude>

{{UnderConstruction}}This section will describe '''how to access an I2C peripheral''' using the i2c-dev built-in kernel driver.<br />

It's a driver offering I2C bus access to user space application.<br />

This driver is a character device driver creating an access trough {{Red| /dev/i2c-x}} to access peripherals.
{{ReviewsComments|FGA W839: Discussed in domain weekly: Article should be renamed as and How to...}}
== Open/Close an I2C bus ==
Open an I2C bus through {{Red| /dev/i2c-x}} with x the bus number connected to the peripheral :<pre class="brush:c; gutter:true; highlight: [5];">

int fd;
const char *filename = "/dev/i2c-x"; /* x is the bus number */

/* open i2c-x bus */
if ((fd = open(filename, O_RDWR)) < 0) {
	perror("Failed to open i2c bus");
	exit(EXIT_FAILURE);
}</pre>

Close the bus :<pre class="brush:c; gutter:true; highlight: [2];">

/* close i2c-x bus */
if(close(fd) < 0) {
	perror("Failed to close i2c bus");
	exit(EXIT_FAILURE);
}</pre>

== Set the peripheral address ==
Set a peripheral slave address for the communication<pre class="brush:c; gutter:true; highlight: [3];">

int slaveAddr = 0xXX;                /* XX is the slave address */

if (ioctl(fd, I2C_SLAVE, slaveAddr) < 0) {
	perror("fail to set slave address");
	close(fd);
	exit(EXIT_FAILURE);
}</pre>

== Check I2C functionalities ==
If you try to access an adapter from a userspace program, you will have to check whether the functionality you need is supported. This is done using the I2C_FUNCS ioctl.<pre class="brush:c; gutter:true; highlight: [3,7];">

int32_t funcs;

if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
	perror("error handling");
	exit(EXIT_FAILURE);
}
if (!(funcs & I2C_FUNC_SMBUS_QUICK)) {
	/* Oops, the needed functionality (SMBus write_quick function) is
        not available! */
	exit(EXIT_FAILURE);
}</pre>
Functionality macros are defined in {{Red| /include/uapi/linux/i2c.h}}<br />
<div class="NavFrame collapsed">
<div class="NavHead">'''All functionalities'''</div>
<div class="NavContent">
<pre class="brush:c; gutter:true; tab-size: 8">

/* To determine what functionality is present */

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_IGNORE_NAK etc. */
#define I2C_FUNC_SMBUS_PEC		0x00000008
#define I2C_FUNC_NOSTART		0x00000010 /* I2C_M_NOSTART */
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK		0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE		(I2C_FUNC_SMBUS_READ_BYTE | \
					 I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA	(I2C_FUNC_SMBUS_READ_BYTE_DATA | \
					 I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA	(I2C_FUNC_SMBUS_READ_WORD_DATA | \
					 I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA	(I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
					 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK	(I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
					 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

#define I2C_FUNC_SMBUS_EMUL		(I2C_FUNC_SMBUS_QUICK | \
					 I2C_FUNC_SMBUS_BYTE | \
					 I2C_FUNC_SMBUS_BYTE_DATA | \
					 I2C_FUNC_SMBUS_WORD_DATA | \
					 I2C_FUNC_SMBUS_PROC_CALL | \
					 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
					 I2C_FUNC_SMBUS_I2C_BLOCK | \
					 I2C_FUNC_SMBUS_PEC)</pre>
</div>
</div>


== Access I2C peripheral data ==
i2c-dev kernel driver is a character device driver allowing access to I2C peripherals through the {{Red| /dev}} interface.<br />

This driver creates a file descriptor for each I2C bus like {{Red| /dev/i2c-0}}.<br />

Common Linux system calls ([http://man7.org/linux/man-pages/man2/read.2.html '''read()'''], [http://man7.org/linux/man-pages/man2/write.2.html '''write()'''] and [http://man7.org/linux/man-pages/man2/ioctl.2.html '''ioctl()''']) are available to access all peripherals through those file descriptors.
===  SMBus commands ===
It's possible to access an I2C peripheral using SMBus protocol with a specific [http://man7.org/linux/man-pages/man2/ioctl.2.html ioctl()] command.<br />

C example reading a designated register '''addr''' :<pre class="brush:c; gutter:true; highlight: [12];">

struct i2c_smbus_ioctl_data args;
union i2c_smbus_data data;
char addr = 0xXX;			/* XX is the register peripheral address */
char value = 0x00;
int ret;

args.read_write = I2C_SMBUS_READ;
args.command = addr;
args.size = I2C_SMBUS_BYTE_DATA;
args.data = &data;

ret = ioctl(fd, I2C_SMBUS, &args);
if (ret < 0)
	perror("error handling");

value = 0x0FF & data.byte;</pre>
Macros and data types are described into {{Red| /include/uapi/linux/i2c-dev.h}} and {{red| /include/uapi/linux/i2c.h}}.<br />
<div class="NavFrame collapsed">
<div class="NavHead">'''struct i2c_smbus_ioctl_data''' from i2c-dev.h</div>
<div class="NavContent">
<pre class="brush:c; gutter:true; tab-size: 8">

/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
	__u8 read_write;
	__u8 command;
	__u32 size;
	union i2c_smbus_data __user *data;
};</pre>
</div>
</div>
<div class="NavFrame collapsed">
<div class="NavHead">'''union i2c_smbus_data''' from i2c.h</div>
<div class="NavContent">
<pre class="brush:c; gutter:true; tab-size: 8">

/*
 * Data for SMBus Messages
 */
#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */
union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
			       /* and one more for user-space compatibility */
};</pre>
</div>
</div>


In order to simplify C style using SMBus ioctl, [https://github.com/groeck/i2c-tools i2c-tools] package provides an interface for using SMBus protocol.<br />

Extract of '''smbus.h''' included on i2c-tools package :<pre class="brush:c; gutter:true;">

extern __s32 i2c_smbus_write_quick(int file, __u8 value);
extern __s32 i2c_smbus_read_byte(int file);
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);</pre>


Example using SMBus functions :<pre class="brush:c; gutter:true; highlight: [4,10];">

/* Read a byte */
char value;
int registerAddr = 0xXX;             /* XX is the register peripheral address */
value = i2c_smbus_read_byte_data(fd, registerAddr);

/* Write a byte */
char registerAddr = 0xXX;            /* XX is the register peripheral address */
char value = 0xXX;                   /* XX is the value to write */
int err;
err = i2c_smbus_write_byte_data(fd, registerAddr, value);</pre>

SMBus API and C example at [https://www.kernel.org/doc/Documentation/i2c/dev-interface /Documentation/i2c/dev-interface].<br />

relations between SMBus functions and protocol at [https://www.kernel.org/doc/Documentation/i2c/smbus-protocol /Documentation/i2c/smbus-protocol]<br />


=== read/write system calls ===
Use [http://man7.org/linux/man-pages/man2/write.2.html '''write()'''] system call to set the index reader position at the register address to read and use [http://man7.org/linux/man-pages/man2/read.2.html '''read()'''] system call to read data from the index reader.<br />

Both functions return negative errno, or else the number of bytes write/read.<pre class="brush:c; gutter:true; tab-size: 8; highlight: [10,33,54];">

/**
 * set_pointer - perform a write to set the address pointer for future read
 * @fd: file descriptor of the i2c adapter
 * @addr: device address to point
 **/
int set_pointer(int fd, char addr)
{
	int ret;

	ret = write(fd, &addr, 1);
	if (ret < 0)
		perror("write failed");

	return ret;
}

/**
 * read_register() - Read register using read system call
 * @fd: file descriptor of the i2c adapter
 * @addr: device address to read
 * @buf: buffer to store the result
 **/
int read_register(int fd, char addr, char *buf)
{
	int ret;

	ret = set_pointer(fd, addr);
	if (ret < 0) {
		printf("set pointer failed\n");
		return ret;
	}

	ret = read(fd, buf, 1);
	if (ret < 0) {
		perror("read failed");
	}

	return ret;
}

/**
 * write_register() - Write register using write system call
 * @fd: file descriptor of the i2c adapter
 * @addr: device address to write
 * @value: value to write
 **/
int write_register(int fd, char addr, char value)
{
	int ret;
	char buf[2];
	buf[0] = addr;
	buf[1] = value;

	ret = write(fd, buf, 2);
	if (ret < 0)
		perror("write failed");

	return ret;
}</pre>


==Documentations and programming examples==
Further functions descriptions + code example :<br />

http://elinux.org/Interfacing_with_I2C_Devices

===API===
https://www.kernel.org/doc/Documentation/i2c/dev-interface

===Full example===
[[File:I2c-dev-example.png|right|363 px|link=|Implementation architecture]]

The application example below communicates with a [http://www.st.com/web/en/resource/technical/document/datasheet/DM00134909.pdf sensor expansion board] for STM32 Nucleo.<br />

This board contains four sensor communicating via I2C :
* A capacitive digital sensor for relative humidity and temperature ([http://www.st.com/content/ccc/resource/technical/document/datasheet/4d/9a/9c/ad/25/07/42/34/DM00116291.pdf/files/DM00116291.pdf/jcr:content/translations/en.DM00116291.pdf HTS221]),
* A 3D accelerometer and 3D gyroscope ([http://www.st.com/content/ccc/resource/technical/document/datasheet/6e/09/28/0b/01/06/42/24/DM00101533.pdf/files/DM00101533.pdf/jcr:content/translations/en.DM00101533.pdf LSM6DS0]),
* A 3D magnetometer ([http://www.st.com/content/ccc/resource/technical/document/datasheet/54/2a/85/76/e3/97/42/18/DM00075867.pdf/files/DM00075867.pdf/jcr:content/translations/en.DM00075867.pdf LIS3MDL]),
* And a pressure sensor ([http://www.st.com/content/ccc/resource/technical/document/datasheet/9a/4c/aa/72/1f/45/4e/24/DM00141379.pdf/files/DM00141379.pdf/jcr:content/translations/en.DM00141379.pdf LPS25HB]).

This example informs the user if all peripherals included on the expansion board are correctly detected by reading their name registers using different methods.<br />

Methods used in this example are :
* Read a register using the SMBus protocol trough an ioctl() system call.
* Read a register using a SMBus function included into the i2c-tools library.
* Read a register using common read()/write() system calls.

Result of the application can be :<pre>

[SUCCESS]: HTS221  sensor is correctly detected
[SUCCESS]: LPS25HB sensor is correctly detected
[SUCCESS]: LSM6DS0 sensor is correctly detected
[SUCCESS]: LIS3MDL sensor is correctly detected</pre>

or,<pre>

dmesg
[FAILED]: HTS221 sensor  is not detected, ID=0x0 (expected 0xBC)
[FAILED]: LPS25HB sensor is not detected, ID=0x0 (expected 0xBD)
[FAILED]: LSM6DS0 sensor is not detected, ID=0x0 (expected 0x68)
[FAILED]: LIS3MDL sensor is not detected, ID=0x0 (expected 0x3D)</pre>


[[Media:Hts221_i2c-dev.c| download source code]]<br />

<pre class="brush:c; gutter:true;">


#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <linux/i2c-dev.h>

#include <linux/i2c.h>


#define ID_REG				0x0F

#define HTS221_ID			0xBC
#define LPS25HB_ID			0xBD
#define LSM6DS0_ID			0x68
#define LIS3MDL_ID			0x3D

#define HTS221_ADDRESS		0x5F
#define LPS25HB_ADDRESS		0x5D
#define LSM6DS0_ADDRESS		0x6B
#define LIS3MDL_ADDRESS		0x1E

static int nucleo_read_smbus(int fd, char addr, char *buf)
{
	struct i2c_smbus_ioctl_data args;
	union i2c_smbus_data data;
	int ret;

	args.read_write = I2C_SMBUS_READ;
	args.command = addr;
	args.size = I2C_SMBUS_BYTE_DATA;
	args.data = &data;

	ret = ioctl(fd, I2C_SMBUS, &args);
	if (ret < 0)
		perror("failed ioctl");

	*buf = 0x0FF & data.byte;

	return ret;

}

static int nucleo_read_register(int fd, char addr, char *buf)
{
	int ret;
	char out_buf = addr;

	/* Use write function to put index reader at the ID register */
	ret = write(fd, &out_buf, 1);
	if (ret < 0) {
		perror("write failed");
	} else {
		ret = read(fd, buf, 1);
		if (ret < 0) {
			perror("read failed");
		}
	}
	return ret;
}

int main(int argc, char *argv[])
{
	int fd;
	int ret;
	char who_is_it = 0x00;
	const char *filename = "/dev/i2c-0";

	/* open i2c-0 bus */
	fd = open(filename, O_RDWR);
	if (fd < 0) {
		perror("Failed to open i2c bus");
		return EXIT_FAILURE;
	}

	/*****************************************************************************
	 *					Detect HTS221 device using smbus ioctl
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, HTS221_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform smbus command to read the ID register */
	ret = nucleo_read_smbus(fd, ID_REG, &who_is_it);
	if (ret < 0)
		perror("Failed to read device using smbus command");

	/* verify if the device found is the good one */
	if (who_is_it == HTS221_ID)
		printf("[SUCCESS]: HTS221  sensor is correctly detected\n");
	else
		printf("[FAILED]: HTS221 sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, HTS221_ID);

	/*****************************************************************************
	 *					Detect LPS25HB device using smbus
	 *					function included in smbus.h
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, LPS25HB_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform smbus function to read the ID register */
	who_is_it = i2c_smbus_read_byte_data(fd, ID_REG);

	/* verify if the device found is the good one */
	if (who_is_it == LPS25HB_ID)
		printf("[SUCCESS]: LPS25HB sensor is correctly detected\n");
	else
		printf("[FAILED]: LPS25HB sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, LPS25HB_ID);

	/*****************************************************************************
	 *				Detect LSM6DS0 device using write/read methods
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, LSM6DS0_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform write/read command to read the ID register */
	ret = nucleo_read_register(fd, ID_REG, &who_is_it);
	if (ret < 0)
		perror("Failed to read device using smbus command");

	/* verify if the device found is the good one */
	if (who_is_it == LSM6DS0_ID)
		printf("[SUCCESS]: LSM6DS0 sensor is correctly detected\n");
	else
		printf("[FAILED]: LSM6DS0 sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, LSM6DS0_ID);

	/*****************************************************************************
	 *				Detect LIS3MDL device using write/read methods
	 * **************************************************************************/

	/* set slave address which we want to communicate */
	if (ioctl(fd, I2C_SLAVE, LIS3MDL_ADDRESS) < 0) {
		perror("fail to set slave address");
		close(fd);
		return EXIT_FAILURE;
	}
	/* Perform write/read command to read the ID register */
	ret = nucleo_read_register(fd, ID_REG, &who_is_it);
	if (ret < 0)
		perror("Failed to read device using smbus command");

	/* verify if the device found is the good one */
	if (who_is_it == LIS3MDL_ID)
		printf("[SUCCESS]: LIS3MDL sensor is correctly detected\n");
	else
		printf("[FAILED]: LIS3MDL sensor is not detected, ID=0x%x (expected 0x%x)\n",
			   who_is_it, LIS3MDL_ID);

	/* close i2c-0 bus */
	if (close(fd) < 0) {
		perror("Failed to close i2c bus");
		return EXIT_FAILURE;
	} else {
		return 0;
	}
}
</pre>


Help for compilation : [[BitBake_cheat_sheet]]
<noinclude>

[[Category:I2C]]</noinclude>
(One intermediate revision by the same user not shown)
Line 1: Line 1:
  +
This section will describe '''how to access an I2C peripheral''' using the i2c-dev built-in kernel driver.<br />
  +
It's a driver offering I2C bus access to user space application.<br />
  +
This driver is a character device driver creating an access trough {{Red| /dev/i2c-x}} to access peripherals.
  +
{{ReviewsComments|FGA W839: Discussed in domain weekly: Article should be renamed as and How to...}}
  +
== Open/Close an I2C bus ==
  +
Open an I2C bus through {{Red| /dev/i2c-x}} with x the bus number connected to the peripheral :
  +
<pre class="brush:c; gutter:true; highlight: [5];">
  +
int fd;
  +
const char *filename = "/dev/i2c-x"; /* x is the bus number */
  +
  +
/* open i2c-x bus */
  +
if ((fd = open(filename, O_RDWR)) < 0) {
  +
perror("Failed to open i2c bus");
  +
exit(EXIT_FAILURE);
  +
}
  +
</pre>
  +
  +
Close the bus :
  +
<pre class="brush:c; gutter:true; highlight: [2];">
  +
/* close i2c-x bus */
  +
if(close(fd) < 0) {
  +
perror("Failed to close i2c bus");
  +
exit(EXIT_FAILURE);
  +
}
  +
</pre>
  +
  +
== Set the peripheral address ==
  +
Set a peripheral slave address for the communication
  +
<pre class="brush:c; gutter:true; highlight: [3];">
  +
int slaveAddr = 0xXX;                /* XX is the slave address */
  +
  +
if (ioctl(fd, I2C_SLAVE, slaveAddr) < 0) {
  +
perror("fail to set slave address");
  +
close(fd);
  +
exit(EXIT_FAILURE);
  +
}
  +
</pre>
  +
  +
== Check I2C functionalities ==
  +
If you try to access an adapter from a userspace program, you will have to check whether the functionality you need is supported. This is done using the I2C_FUNCS ioctl.
  +
<pre class="brush:c; gutter:true; highlight: [3,7];">
  +
int32_t funcs;
  +
  +
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
  +
perror("error handling");
  +
exit(EXIT_FAILURE);
  +
}
  +
if (!(funcs & I2C_FUNC_SMBUS_QUICK)) {
  +
/* Oops, the needed functionality (SMBus write_quick function) is
  +
        not available! */
  +
exit(EXIT_FAILURE);
  +
}
  +
</pre>
  +
Functionality macros are defined in {{Red| /include/uapi/linux/i2c.h}}<br />
  +
<div class="NavFrame collapsed">
  +
  <div class="NavHead">'''All functionalities'''</div>
  +
  <div class="NavContent">
  +
<pre class="brush:c; gutter:true; tab-size: 8">
  +
/* To determine what functionality is present */
  +
  +
#define I2C_FUNC_I2C 0x00000001
  +
#define I2C_FUNC_10BIT_ADDR 0x00000002
  +
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */
  +
#define I2C_FUNC_SMBUS_PEC 0x00000008
  +
#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */
  +
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
  +
#define I2C_FUNC_SMBUS_QUICK 0x00010000
  +
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
  +
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
  +
#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
  +
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
  +
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
  +
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
  +
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
  +
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
  +
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
  +
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer  */
  +
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
  +
  +
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
  +
I2C_FUNC_SMBUS_WRITE_BYTE)
  +
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
  +
I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
  +
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
  +
I2C_FUNC_SMBUS_WRITE_WORD_DATA)
  +
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
  +
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
  +
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
  +
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
  +
  +
#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \
  +
I2C_FUNC_SMBUS_BYTE | \
  +
I2C_FUNC_SMBUS_BYTE_DATA | \
  +
I2C_FUNC_SMBUS_WORD_DATA | \
  +
I2C_FUNC_SMBUS_PROC_CALL | \
  +
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
  +
I2C_FUNC_SMBUS_I2C_BLOCK | \
  +
I2C_FUNC_SMBUS_PEC)
  +
</pre>
  +
</div>
  +
</div>
  +
  +
== Access I2C peripheral data ==
  +
i2c-dev kernel driver is a character device driver allowing access to I2C peripherals through the {{Red| /dev}} interface.<br />
  +
This driver creates a file descriptor for each I2C bus like {{Red| /dev/i2c-0}}.<br />
  +
Common Linux system calls ([http://man7.org/linux/man-pages/man2/read.2.html '''read()'''], [http://man7.org/linux/man-pages/man2/write.2.html '''write()'''] and [http://man7.org/linux/man-pages/man2/ioctl.2.html '''ioctl()''']) are available to access all peripherals through those file descriptors.
  +
===  SMBus commands ===
  +
It's possible to access an I2C peripheral using SMBus protocol with a specific [http://man7.org/linux/man-pages/man2/ioctl.2.html ioctl()] command.<br />
  +
C example reading a designated register '''addr''' :
  +
<pre class="brush:c; gutter:true; highlight: [12];">
  +
struct i2c_smbus_ioctl_data args;
  +
union i2c_smbus_data data;
  +
char addr = 0xXX; /* XX is the register peripheral address */
  +
char value = 0x00;
  +
int ret;
  +
  +
args.read_write = I2C_SMBUS_READ;
  +
args.command = addr;
  +
args.size = I2C_SMBUS_BYTE_DATA;
  +
args.data = &data;
  +
  +
ret = ioctl(fd, I2C_SMBUS, &args);
  +
if (ret < 0)
  +
perror("error handling");
  +
  +
value = 0x0FF & data.byte;
  +
</pre>
  +
Macros and data types are described into {{Red| /include/uapi/linux/i2c-dev.h}} and {{red| /include/uapi/linux/i2c.h}}.<br />
  +
<div class="NavFrame collapsed">
  +
  <div class="NavHead">'''struct i2c_smbus_ioctl_data''' from i2c-dev.h</div>
  +
  <div class="NavContent">
  +
<pre class="brush:c; gutter:true; tab-size: 8">
  +
/* This is the structure as used in the I2C_SMBUS ioctl call */
  +
struct i2c_smbus_ioctl_data {
  +
__u8 read_write;
  +
__u8 command;
  +
__u32 size;
  +
union i2c_smbus_data __user *data;
  +
};
  +
</pre>
  +
</div>
  +
</div>
  +
<div class="NavFrame collapsed">
  +
  <div class="NavHead">'''union i2c_smbus_data''' from i2c.h</div>
  +
  <div class="NavContent">
  +
<pre class="brush:c; gutter:true; tab-size: 8">
  +
/*
  +
* Data for SMBus Messages
  +
*/
  +
#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */
  +
union i2c_smbus_data {
  +
__u8 byte;
  +
__u16 word;
  +
__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
  +
      /* and one more for user-space compatibility */
  +
};
  +
</pre>
  +
</div>
  +
</div>
  +
  +
In order to simplify C style using SMBus ioctl, [https://github.com/groeck/i2c-tools i2c-tools] package provides an interface for using SMBus protocol.<br />
  +
Extract of '''smbus.h''' included on i2c-tools package :
  +
<pre class="brush:c; gutter:true;">
  +
extern __s32 i2c_smbus_write_quick(int file, __u8 value);
  +
extern __s32 i2c_smbus_read_byte(int file);
  +
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
  +
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
  +
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
  +
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
  +
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
  +
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
  +
</pre>
  +
  +
Example using SMBus functions :
  +
<pre class="brush:c; gutter:true; highlight: [4,10];">
  +
/* Read a byte */
  +
char value;
  +
int registerAddr = 0xXX;            /* XX is the register peripheral address */
  +
value = i2c_smbus_read_byte_data(fd, registerAddr);
  +
  +
/* Write a byte */
  +
char registerAddr = 0xXX;            /* XX is the register peripheral address */
  +
char value = 0xXX;                  /* XX is the value to write */
  +
int err;
  +
err = i2c_smbus_write_byte_data(fd, registerAddr, value);
  +
</pre>
  +
SMBus API and C example at [https://www.kernel.org/doc/Documentation/i2c/dev-interface /Documentation/i2c/dev-interface].<br />
  +
relations between SMBus functions and protocol at [https://www.kernel.org/doc/Documentation/i2c/smbus-protocol /Documentation/i2c/smbus-protocol]<br />
  +
  +
=== read/write system calls ===
  +
Use [http://man7.org/linux/man-pages/man2/write.2.html '''write()'''] system call to set the index reader position at the register address to read and use [http://man7.org/linux/man-pages/man2/read.2.html '''read()'''] system call to read data from the index reader.<br />
  +
Both functions return negative errno, or else the number of bytes write/read.
  +
<pre class="brush:c; gutter:true; tab-size: 8; highlight: [10,33,54];">
  +
/**
  +
* set_pointer - perform a write to set the address pointer for future read
  +
* @fd: file descriptor of the i2c adapter
  +
* @addr: device address to point
  +
**/
  +
int set_pointer(int fd, char addr)
  +
{
  +
int ret;
  +
  +
ret = write(fd, &addr, 1);
  +
if (ret < 0)
  +
perror("write failed");
  +
  +
return ret;
  +
}
  +
  +
/**
  +
* read_register() - Read register using read system call
  +
* @fd: file descriptor of the i2c adapter
  +
* @addr: device address to read
  +
* @buf: buffer to store the result
  +
**/
  +
int read_register(int fd, char addr, char *buf)
  +
{
  +
int ret;
  +
  +
ret = set_pointer(fd, addr);
  +
if (ret < 0) {
  +
printf("set pointer failed\n");
  +
return ret;
  +
}
  +
  +
ret = read(fd, buf, 1);
  +
if (ret < 0) {
  +
perror("read failed");
  +
}
  +
  +
return ret;
  +
}
  +
  +
/**
  +
* write_register() - Write register using write system call
  +
* @fd: file descriptor of the i2c adapter
  +
* @addr: device address to write
  +
* @value: value to write
  +
**/
  +
int write_register(int fd, char addr, char value)
  +
{
  +
int ret;
  +
char buf[2];
  +
buf[0] = addr;
  +
buf[1] = value;
  +
  +
ret = write(fd, buf, 2);
  +
if (ret < 0)
  +
perror("write failed");
  +
  +
return ret;
  +
}
  +
</pre>
  +
  +
==Documentations and programming examples==
  +
Further functions descriptions + code example :<br />
  +
http://elinux.org/Interfacing_with_I2C_Devices
  +
  +
===API===
  +
https://www.kernel.org/doc/Documentation/i2c/dev-interface
  +
  +
===Full example===
  +
[[File:I2c-dev-example.png|right|363 px|link=|Implementation architecture]]
  +
  +
The application example below communicates with a [http://www.st.com/web/en/resource/technical/document/datasheet/DM00134909.pdf sensor expansion board] for STM32 Nucleo.<br />
  +
This board contains four sensor communicating via I2C :
  +
* A capacitive digital sensor for relative humidity and temperature ([http://www.st.com/content/ccc/resource/technical/document/datasheet/4d/9a/9c/ad/25/07/42/34/DM00116291.pdf/files/DM00116291.pdf/jcr:content/translations/en.DM00116291.pdf HTS221]),
  +
* A 3D accelerometer and 3D gyroscope ([http://www.st.com/content/ccc/resource/technical/document/datasheet/6e/09/28/0b/01/06/42/24/DM00101533.pdf/files/DM00101533.pdf/jcr:content/translations/en.DM00101533.pdf LSM6DS0]),
  +
* A 3D magnetometer ([http://www.st.com/content/ccc/resource/technical/document/datasheet/54/2a/85/76/e3/97/42/18/DM00075867.pdf/files/DM00075867.pdf/jcr:content/translations/en.DM00075867.pdf LIS3MDL]),
  +
* And a pressure sensor ([http://www.st.com/content/ccc/resource/technical/document/datasheet/9a/4c/aa/72/1f/45/4e/24/DM00141379.pdf/files/DM00141379.pdf/jcr:content/translations/en.DM00141379.pdf LPS25HB]).
  +
  +
This example informs the user if all peripherals included on the expansion board are correctly detected by reading their name registers using different methods.<br />
  +
Methods used in this example are :
  +
* Read a register using the SMBus protocol trough an ioctl() system call.
  +
* Read a register using a SMBus function included into the i2c-tools library.
  +
* Read a register using common read()/write() system calls.
  +
  +
Result of the application can be :
  +
<pre>
  +
[SUCCESS]: HTS221  sensor is correctly detected
  +
[SUCCESS]: LPS25HB sensor is correctly detected
  +
[SUCCESS]: LSM6DS0 sensor is correctly detected
  +
[SUCCESS]: LIS3MDL sensor is correctly detected
  +
</pre>
  +
or,
  +
<pre>
  +
dmesg
  +
[FAILED]: HTS221 sensor  is not detected, ID=0x0 (expected 0xBC)
  +
[FAILED]: LPS25HB sensor is not detected, ID=0x0 (expected 0xBD)
  +
[FAILED]: LSM6DS0 sensor is not detected, ID=0x0 (expected 0x68)
  +
[FAILED]: LIS3MDL sensor is not detected, ID=0x0 (expected 0x3D)
  +
</pre>
  +
  +
[[Media:Hts221_i2c-dev.c| download source code]]<br />
  +
  +
<pre class="brush:c; gutter:true;">
  +
  +
#include <fcntl.h>
  +
#include <stdio.h>
  +
#include <stdlib.h>
  +
#include <linux/i2c-dev.h>
  +
#include <linux/i2c.h>
  +
  +
#define ID_REG 0x0F
  +
  +
#define HTS221_ID 0xBC
  +
#define LPS25HB_ID 0xBD
  +
#define LSM6DS0_ID 0x68
  +
#define LIS3MDL_ID 0x3D
  +
  +
#define HTS221_ADDRESS 0x5F
  +
#define LPS25HB_ADDRESS 0x5D
  +
#define LSM6DS0_ADDRESS 0x6B
  +
#define LIS3MDL_ADDRESS 0x1E
  +
  +
static int nucleo_read_smbus(int fd, char addr, char *buf)
  +
{
  +
struct i2c_smbus_ioctl_data args;
  +
union i2c_smbus_data data;
  +
int ret;
  +
  +
args.read_write = I2C_SMBUS_READ;
  +
args.command = addr;
  +
args.size = I2C_SMBUS_BYTE_DATA;
  +
args.data = &data;
  +
  +
ret = ioctl(fd, I2C_SMBUS, &args);
  +
if (ret < 0)
  +
perror("failed ioctl");
  +
  +
*buf = 0x0FF & data.byte;
  +
  +
return ret;
  +
  +
}
  +
  +
static int nucleo_read_register(int fd, char addr, char *buf)
  +
{
  +
int ret;
  +
char out_buf = addr;
  +
  +
/* Use write function to put index reader at the ID register */
  +
ret = write(fd, &out_buf, 1);
  +
if (ret < 0) {
  +
perror("write failed");
  +
} else {
  +
ret = read(fd, buf, 1);
  +
if (ret < 0) {
  +
perror("read failed");
  +
}
  +
}
  +
return ret;
  +
}
  +
  +
int main(int argc, char *argv[])
  +
{
  +
int fd;
  +
int ret;
  +
char who_is_it = 0x00;
  +
const char *filename = "/dev/i2c-0";
  +
  +
/* open i2c-0 bus */
  +
fd = open(filename, O_RDWR);
  +
if (fd < 0) {
  +
perror("Failed to open i2c bus");
  +
return EXIT_FAILURE;
  +
}
  +
  +
/*****************************************************************************
  +
* Detect HTS221 device using smbus ioctl
  +
* **************************************************************************/
  +
  +
/* set slave address which we want to communicate */
  +
if (ioctl(fd, I2C_SLAVE, HTS221_ADDRESS) < 0) {
  +
perror("fail to set slave address");
  +
close(fd);
  +
return EXIT_FAILURE;
  +
}
  +
/* Perform smbus command to read the ID register */
  +
ret = nucleo_read_smbus(fd, ID_REG, &who_is_it);
  +
if (ret < 0)
  +
perror("Failed to read device using smbus command");
  +
  +
/* verify if the device found is the good one */
  +
if (who_is_it == HTS221_ID)
  +
printf("[SUCCESS]: HTS221  sensor is correctly detected\n");
  +
else
  +
printf("[FAILED]: HTS221 sensor is not detected, ID=0x%x (expected 0x%x)\n",
  +
  who_is_it, HTS221_ID);
  +
  +
/*****************************************************************************
  +
* Detect LPS25HB device using smbus
  +
* function included in smbus.h
  +
* **************************************************************************/
  +
  +
/* set slave address which we want to communicate */
  +
if (ioctl(fd, I2C_SLAVE, LPS25HB_ADDRESS) < 0) {
  +
perror("fail to set slave address");
  +
close(fd);
  +
return EXIT_FAILURE;
  +
}
  +
/* Perform smbus function to read the ID register */
  +
who_is_it = i2c_smbus_read_byte_data(fd, ID_REG);
  +
  +
/* verify if the device found is the good one */
  +
if (who_is_it == LPS25HB_ID)
  +
printf("[SUCCESS]: LPS25HB sensor is correctly detected\n");
  +
else
  +
printf("[FAILED]: LPS25HB sensor is not detected, ID=0x%x (expected 0x%x)\n",
  +
  who_is_it, LPS25HB_ID);
  +
  +
/*****************************************************************************
  +
* Detect LSM6DS0 device using write/read methods
  +
* **************************************************************************/
  +
  +
/* set slave address which we want to communicate */
  +
if (ioctl(fd, I2C_SLAVE, LSM6DS0_ADDRESS) < 0) {
  +
perror("fail to set slave address");
  +
close(fd);
  +
return EXIT_FAILURE;
  +
}
  +
/* Perform write/read command to read the ID register */
  +
ret = nucleo_read_register(fd, ID_REG, &who_is_it);
  +
if (ret < 0)
  +
perror("Failed to read device using smbus command");
  +
  +
/* verify if the device found is the good one */
  +
if (who_is_it == LSM6DS0_ID)
  +
printf("[SUCCESS]: LSM6DS0 sensor is correctly detected\n");
  +
else
  +
printf("[FAILED]: LSM6DS0 sensor is not detected, ID=0x%x (expected 0x%x)\n",
  +
  who_is_it, LSM6DS0_ID);
  +
  +
/*****************************************************************************
  +
* Detect LIS3MDL device using write/read methods
  +
* **************************************************************************/
  +
  +
/* set slave address which we want to communicate */
  +
if (ioctl(fd, I2C_SLAVE, LIS3MDL_ADDRESS) < 0) {
  +
perror("fail to set slave address");
  +
close(fd);
  +
return EXIT_FAILURE;
  +
}
  +
/* Perform write/read command to read the ID register */
  +
ret = nucleo_read_register(fd, ID_REG, &who_is_it);
  +
if (ret < 0)
  +
perror("Failed to read device using smbus command");
  +
  +
/* verify if the device found is the good one */
  +
if (who_is_it == LIS3MDL_ID)
  +
printf("[SUCCESS]: LIS3MDL sensor is correctly detected\n");
  +
else
  +
printf("[FAILED]: LIS3MDL sensor is not detected, ID=0x%x (expected 0x%x)\n",
  +
  who_is_it, LIS3MDL_ID);
  +
  +
/* close i2c-0 bus */
  +
if (close(fd) < 0) {
  +
perror("Failed to close i2c bus");
  +
return EXIT_FAILURE;
  +
} else {
  +
return 0;
  +
}
  +
}
  +
  +
</pre>
  +
  +
Help for compilation : [[BitBake_cheat_sheet]]
  +
 
<noinclude>
 
<noinclude>
{{ArticleMainWriter|Pierre-YvesM}}
 
{{ArticleApprovedVersion | Jean-ChristopheT | Nobody | No previous approved version | Automatic approval (article under construction) | 19Feb’19}}
 
 
[[Category:I2C]]
 
[[Category:I2C]]
 
</noinclude>
 
</noinclude>
{{UnderConstruction}}
 

Attachments

Discussions