0
0
Power-electronicsHow-ToBeginner · 4 min read

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.