In this lesson, you will learn how to interface with I2C hardware peripherals from user-space using the Linux i2c-dev character device interface.
The I2C-Dev Interface
In Embedded Linux, the kernel handles the low-level bit-banging or hardware-controller signaling, exposing each I2C bus as a character device (e.g., /dev/i2c-1). Instead of manually toggling SCL and SDA pins, you interact with these peripherals by opening the device file and issuing ioctl() system calls to direct the kernel driver on how to orchestrate the bus traffic.
Performing Transactions
To communicate, you first open the device node, then set the slave address of your target peripheral using the I2C_SLAVE ioctl. Once the address is set, you treat the device as a standard file descriptor, using read() and write() to exchange bytes.
For more complex scenarios—such as a "repeated start" condition where you write a register address and immediately read the value without releasing the bus—the i2c_rdwr_ioctl_data structure allows you to bundle multiple messages into a single atomic transaction.
Example: Writing to a Register
The following snippet demonstrates setting a slave address and writing a single byte to an internal register:
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
int fd = open("/dev/i2c-1", O_RDWR);
int slave_addr = 0x48; // Address of your sensor
// Set the target slave address for this file descriptor
ioctl(fd, I2C_SLAVE, slave_addr);
// Buffer: [Register Address, Data Value]
unsigned char buf[2] = {0x01, 0xAF};
write(fd, buf, 2);
close(fd);
Important Considerations
Because I2C is a shared bus, it is critical to ensure that no other processes attempt to access the same peripheral simultaneously. While the kernel manages bus arbitration, application-level synchronization (using the POSIX mutexes we discussed previously) is necessary if multiple threads in your application are sharing the same I2C file descriptor.
Key Takeaway: The i2c-dev driver abstracts the complexities of the I2C protocol, allowing you to perform read/write transactions via standard file I/O system calls once the target slave address is set via ioctl.