How to Interface Sensor Using I2C in Embedded C
To interface a sensor using
I2C in Embedded C, initialize the I2C peripheral, send the sensor's address with read/write flags, then read or write data bytes. Use I2C_Start(), I2C_Write(), I2C_Read(), and I2C_Stop() functions to communicate with the sensor over the bus.Syntax
The basic steps to communicate with an I2C sensor in Embedded C are:
- I2C_Start(): Begin communication on the I2C bus.
- I2C_Write(address): Send the 7-bit sensor address with a read/write bit.
- I2C_Read(ack): Read a byte from the sensor, sending ACK or NACK.
- I2C_Stop(): End communication on the I2C bus.
These functions are usually provided by your microcontroller's I2C driver or you can implement them using low-level register access.
c
void I2C_Start(void); void I2C_Stop(void); uint8_t I2C_Write(uint8_t data); uint8_t I2C_Read(uint8_t ack);
Example
This example shows how to read a byte from a sensor at address 0x50 using I2C in Embedded C.
c
#include <stdint.h> #include <stdio.h> // Mock functions for I2C operations void I2C_Start(void) { /* Start condition code */ } void I2C_Stop(void) { /* Stop condition code */ } uint8_t I2C_Write(uint8_t data) { /* Write byte and return ACK/NACK */ return 0; } uint8_t I2C_Read(uint8_t ack) { /* Read byte and send ACK/NACK */ return 0xAB; } int main(void) { uint8_t sensor_addr = 0x50 << 1; // 7-bit address shifted for write uint8_t data; I2C_Start(); if (I2C_Write(sensor_addr | 0) != 0) { // Write mode printf("Sensor not responding\n"); I2C_Stop(); return -1; } // Send register address or command if needed (example: 0x00) I2C_Write(0x00); // Restart for read I2C_Start(); if (I2C_Write(sensor_addr | 1) != 0) { // Read mode printf("Sensor not responding on read\n"); I2C_Stop(); return -1; } data = I2C_Read(0); // Read byte and send NACK I2C_Stop(); printf("Read data: 0x%02X\n", data); return 0; }
Output
Read data: 0xAB
Common Pitfalls
- Not shifting the 7-bit sensor address left by 1 bit before adding the read/write bit.
- Forgetting to send a repeated start condition when switching from write to read mode.
- Not checking for ACK/NACK after writing the address or data bytes.
- Sending ACK after the last byte read instead of NACK, causing the sensor to wait for more data.
- Not properly initializing the I2C peripheral or clock settings.
c
/* Wrong: Not shifting address and missing repeated start */ I2C_Start(); I2C_Write(0x50); // Missing shift and R/W bit I2C_Write(0x00); I2C_Write((0x50 << 1) | 1); // Should be repeated start instead uint8_t data = I2C_Read(1); // ACK sent instead of NACK I2C_Stop(); /* Correct: Shift address and use repeated start */ I2C_Start(); I2C_Write((0x50 << 1) | 0); // Write mode I2C_Write(0x00); I2C_Start(); // Repeated start I2C_Write((0x50 << 1) | 1); // Read mode uint8_t data = I2C_Read(0); // NACK after last byte I2C_Stop();
Quick Reference
I2C Communication Steps:
- Start: Begin communication with
I2C_Start(). - Address: Send 7-bit sensor address shifted left by 1, plus R/W bit.
- Write: Send register or command bytes if needed.
- Repeated Start: Use
I2C_Start()again before reading. - Read: Read bytes with
I2C_Read(ack), send NACK after last byte. - Stop: End communication with
I2C_Stop().
Key Takeaways
Always shift the 7-bit sensor address left by 1 before adding the read/write bit.
Use repeated start condition when switching from write to read mode on the I2C bus.
Check for ACK/NACK after every byte sent to ensure sensor communication.
Send NACK after reading the last byte to signal end of data reception.
Initialize the I2C peripheral correctly before starting communication.