Embedded Development in C: I2C Communication Protocol

Embedded Development in C: I2C Communication Protocol

Introduction

I2C (Inter-Integrated Circuit) is a widely used serial communication protocol, commonly utilized for communication between microcontrollers and various peripherals. It was developed by Philips in the early 1980s, primarily for short-distance, low-speed data transmission. I2C offers advantages such as simplicity, flexibility, and multi-master support, making it well-suited for applications in embedded systems.

This article will detail the basic concepts of the I2C communication protocol and demonstrate how to implement I2C communication in C through code examples.

Basics of I2C Protocol

1. I2C Bus Structure

The I2C bus consists of two signal lines:

  • SDA (Serial Data Line): The data line used for transmitting data.
  • SCL (Serial Clock Line): The clock line generated by the master device, used to synchronize data transmission.

2. Master and Slave Devices

In an I2C network, there is typically one or more master devices and one or more slave devices. The master device is responsible for initiating and controlling data transmission, while the slave devices respond to the master’s requests.

3. Address Allocation

Each slave device has a unique address, typically 7 or 10 bits long. During communication, the master device sends the address of the target slave device to select the specific slave device for interaction.

4. Data Transmission Process

  1. The master sends a start signal.
  2. The master sends the target slave address and the read/write bit.
  3. The slave acknowledges the receipt.
  4. The data exchange process (read/write) occurs.
  5. The master sends a stop signal to end communication.

Hardware Preparation

For the experiment, we need the following hardware:

  • A microcontroller development board that supports I2C interface, such as Arduino, STM32, etc.
  • A peripheral that supports the I2C protocol, such as a temperature sensor, EEPROM, etc.

Ensure that the SDA and SCL pins are properly connected, and set up the power and ground pins according to the specific hardware.

Implementing I2C Communication in C

Below, we take STM32 as an example to implement basic I2C read and write operations using the HAL library. Assume we want to read data from a temperature sensor with the address 0x48.

1. Initialize the I2C Interface

First, include the necessary header files in your project and initialize the I²C interface:

#include "stm32f4xx_hal.h"
#define TEMP_SENSOR_ADDR 0x48 << 1 // Shift left by one bit because the HAL library uses 8-bit address format
#define TEMP_REGISTER   0x00         // Temperature register address 
void MX_I2C1_Init(void) {
    hi2c1.Instance = I²C1;
    hi2c1.Init.ClockSpeed = 100000; // Set clock frequency to 100kHz
    hi2c1.Init.DutyCycle = I²C_DUTYCYCLE_16_9;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I²C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I²C_DUALADDRESS_DISABLE;
    hi2c1.Init.GeneralCallMode = I²C_GENERALCALL_DISABLE;
    HAL_I²C_Init(&hi2c1);
}

3. Writing Data to the Temperature Sensor

If you need to configure certain parameters for the temperature sensor, you can use the following function:

HAL_StatusTypeDef Write_Temp_Sensor(uint8_t reg, uint8_t data) {
    uint8_t buffer[2];
    buffer[0] = reg;   // Register address
    buffer[1] = data;   // Data to be written
    return HAL_I²C_Master_Transmit(&hi2c1, TEMP_SENSOR_ADDR, buffer, sizeof(buffer), HAL_MAX_DELAY);
}

4. Reading Data from the Temperature Sensor

Next, we can write a function to read the temperature value:

uint8_t Read_Temp_Sensor(void) {
    uint8_t temp_data;
    // First request the register content
    HAL_I²C_Master_Transmit(&hi2c1, TEMP_SENSOR_ADDR, &TEMP_REGISTER, sizeof(TEMP_REGISTER), HAL_MAX_DELAY);
    // Then read the returned data
    HAL_I²C_Master_Receive(&hi2c1, TEMP_SENSOR_ADDR, &temp_data, sizeof(temp_data), HAL_MAX_DELAY);
    return temp_data;
}

Integrating the Example Program

Finally, integrate the above functionalities into the <span>main</span> function:

int main(void) {
    HAL_Init();          // Initialize HAL library
    SystemClock_Config(); // Configure system clock
    MX_GPIO_Init();     // Initialize GPIO
    MX_IIC1_Init();      // Initialize IIC interface
    while (true) {
        Write_Temp_Sensor(0x01, some_value); // Write configuration value to register
        uint8_t temperature = Read_Temp_Sensor(); // Read value from temperature register
        /* Process temperature here */
        HAL_Delay(1000); // Delay one second before looping
    }
}

Conclusion

This article introduced what the I²C protocol is and how to use it for simple data exchange in embedded systems. Through practical code examples, you should be able to understand how to initialize and interact with external components. In real applications, you may also need to consider error handling, timeout mechanisms, and more complex data structures, but this article provides a good starting point for further exploration. I hope this helps you in your embedded development learning journey.

Leave a Comment