Table of Contents
Blog List
Some time ago I wrote two blogs about programming ESP32 devices. One of them was about using ADC (Analog to Digital converters) and another was using UART (Universal Asynchronous Receiver Transmitter) peripheral. These peripherals were simple in terms of complexity. They are very basic peripherals. In this tutorial I am going to write about I2C (Inter integrated Circuit) peripheral in ESP32 devices.
I2C is simple and one of the quite commonly used protocol for data transfer between master and slave devices. The I2C is two wire interface, namely SCL (Serial Clock Line) for clock signal and SDA (Serial Data Line) for transferring data. Each I2C device has 7-bit address assigned to them.
Introduction to I2C hardware
Before going deep into the working of I2C, let's have a look at the I2C hardware block in ESP32 devices. The ESP32 series of devices have dedicated hardware block for I2C peripheral. These peripherals are divided into master and slave blocks. In this tutorial the focus is on ESP32 as master devices only. As you could see in the following block diagram of I2C peripheral there are several things in the peripheral block. One of them is command controller that controls the I2C device commands. These commands are then sent over the I2C bus. The FSM (Finite state machine) will fetch these commands from the list and send them over to the bus. Similarly as you could see in the following block, there is start/stop detector, data shifter, and acknowledge detector. The block has 32x8 bits of separate memory for TX and RX data that can store the data from the transmit and receive operations.
I2C API in ESP-IDF
The I2C API in ESP-IDF is designed to work with master/slave I2C device function. The ESP32 can act as master, or it can act as slave to other master such as other SoC or microrontroller. The most commonly used function is I2C master. In this the ESP32 device will communicate with other slave device such as sensors based on I2C protocol. The figure below shows the wiring between the master (controller) and the slave (device) functions. Sometimes pull-up resistors are required to pull the device line status up by default if the peripheral lines are not in use. The ESP32 has internal pull-ups that can be enabled in software to avoid external pull-ups.
Programming sequence
The programming sequence requires the respective API to be enabled in the CmakeList.txt file. Include the following lines in the CMakeList.txt file to enable the API.
PRIV_REQUIRES esp_driver_i2c
PRIV_REQUIRES esp_driver_ledc
PRIV_REQUIRES esp_driver_gpio
#include "driver/i2c_master.h" #include "driver/ledc.h"

The I2C requires some configuration parameters to work. The API has i2c_master_bus_config_t structure that can be configured to I2C I/O pins and parameters as shown in the following code snippet.
i2c_master_bus_config_t i2c_mst_config = { .clk_source = I2C_CLK_SRC_DEFAULT, .i2c_port = -1, .scl_io_num = 39, .sda_io_num = 40, .glitch_ignore_cnt = 7, .flags.enable_internal_pullup = true, }; i2c_master_bus_handle_t bus_handle; i2c_new_master_bus(&i2c_mst_config, &bus_handle);
/* * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include <stdio.h> #include <inttypes.h> #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_chip_info.h" #include "esp_flash.h" #include "esp_system.h" #include "driver/i2c_master.h" #include "driver/ledc.h" #include "esp_log.h" void app_main(void) { int ret; printf("Hello world!\n"); ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_LOW_SPEED_MODE, .duty_resolution = LEDC_TIMER_1_BIT, .timer_num = LEDC_TIMER_0, .freq_hz = 24000000, .clk_cfg = LEDC_AUTO_CLK }; ledc_timer_config(&ledc_timer); ledc_channel_config_t ledc_channel = { .speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, .intr_type = LEDC_INTR_DISABLE, .gpio_num = 10, .duty = 1, .hpoint = 0, }; ledc_channel_config(&ledc_channel); i2c_master_bus_config_t i2c_mst_config = { .clk_source = I2C_CLK_SRC_DEFAULT, .i2c_port = -1, .scl_io_num = 39, .sda_io_num = 40, .glitch_ignore_cnt = 7, .flags.enable_internal_pullup = true, }; i2c_master_bus_handle_t bus_handle; i2c_new_master_bus(&i2c_mst_config, &bus_handle); i2c_master_dev_handle_t dev_handle; while (1) { uint8_t slave = 0x00; printf("Start\n"); for(int i = 0; i<=127; i++) { ret = i2c_master_probe(bus_handle, slave, -1); printf("slave device address found on 0x%x with err code %x\n", slave, ret); vTaskDelay(50); slave = slave + 1; } } }

Top Comments