Skip to content

Instantly share code, notes, and snippets.

@cibomahto
Created July 4, 2021 18:41
Show Gist options
  • Save cibomahto/41c0d5703782938108408d9f02c25d13 to your computer and use it in GitHub Desktop.
Save cibomahto/41c0d5703782938108408d9f02c25d13 to your computer and use it in GitHub Desktop.
esp32 i2c with mutex
#include "master_i2c.h"
#include <driver/i2c.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#define ACK_CHECK_EN 0x1 //!< I2C master will check ack from slave
#define ACK_CHECK_DIS 0x0 //!< I2C master will not check ack from slave
static SemaphoreHandle_t i2c_semaphore = NULL;
esp_err_t master_i2c_init()
{
// Don't initialize twice
if(i2c_semaphore != NULL)
return ESP_FAIL;
i2c_semaphore = xSemaphoreCreateMutex();
if(i2c_semaphore == NULL)
return ESP_FAIL;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = CONFIG_I2C_SDA_GPIO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = CONFIG_I2C_SCL_GPIO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = CONFIG_I2C_FREQ_KHZ * 1000,
};
esp_err_t ret;
ret = i2c_param_config(I2C_NUM_1, &conf);
if(ret != ESP_OK)
return ret;
ret = i2c_driver_install(I2C_NUM_1, conf.mode, 0, 0, 0);
if(ret != ESP_OK)
return ret;
return ESP_OK;
}
void master_i2c_write(uint8_t address, uint8_t reg, uint8_t* data, uint32_t size)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, address << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
i2c_master_write(cmd, data, size, ACK_CHECK_EN);
i2c_master_stop(cmd);
xSemaphoreTake(i2c_semaphore, portMAX_DELAY);
i2c_master_cmd_begin(I2C_NUM_1, cmd, 0);
xSemaphoreGive(i2c_semaphore);
i2c_cmd_link_delete(cmd);
}
void master_i2c_read(uint8_t address, uint8_t reg, uint8_t* data, uint32_t size)
{
// Write the active address to the I2C device
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, address << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
// Then read the contents of that address
i2c_master_start(cmd);
i2c_master_write_byte(cmd, address << 1 | I2C_MASTER_READ, ACK_CHECK_EN);
i2c_master_read(cmd, data, size, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
xSemaphoreTake(i2c_semaphore, portMAX_DELAY);
i2c_master_cmd_begin(I2C_NUM_1, cmd, 0);
xSemaphoreGive(i2c_semaphore);
i2c_cmd_link_delete(cmd);
}
#pragma once
#include <stdint.h>
#include <esp_err.h>
//! @defgroup master_i2c Master I2C driver
//!
//! @brief Thread-safe Master I2C driver
//!
//! This driver uses the I2C_NUM_1 peripheral on the ESP32. Functions are multi-
//! thread safe, protected by an internal semaphore.
//! @{
//! @brief Initialize the I2C bus in master mode
//!
esp_err_t master_i2c_init();
//! @brief Make a write transaction on the I2C bus
//!
//! @param[in] address Address of I2C bus to write to (7-bit address, right-aligned)
//! @param[in] reg I2C register to write to
//! @param[in] data Pointer to memory location to read data from
//! @param[in] size Number of bytes to write
void master_i2c_write(uint8_t address, uint8_t reg, uint8_t* data, uint32_t size);
//! @brief Make a read transaction on the I2C bus
//!
//! @param[in] address Address of I2C bus to read from(7-bit address, right-aligned)
//! @param[in] reg I2C register to read from
//! @param[out] data Pointer to memory location to write data to
//! @param[in] size Number of bytes to read
void master_i2c_read(uint8_t address, uint8_t reg, uint8_t* data, uint32_t size);
//! @}
@RWBRWB
Copy link

RWBRWB commented May 17, 2024

Found this while looking for solutions to some corrupt I2C reads while running internet streaming & Bluetooth A2DP code that uses both cores.

Seems like I need to protect the I2C reads so they are not interrupted while running.

I'm using the ESP32 with the Arduino environment via Platform IO.

How do I best put this code to use? Just add the .h & .c pages to my project and use the master_i2c_read commands instead of the wire commands since I'm currently using the EPS32 Arduino environment Wire library which is not thread safe.

Just looking to get I2C stable so I can move onto other task and I assume you created this because you had a similar issue you needed to solve.

Any advice is appreciated.

@cibomahto
Copy link
Author

Hope you've got this working already. I don't have experience with the Arduino environment for ESP, but you 'just' need to put a semaphore around any calls to I2C. I think it would be better to make a wrapper around your calls to wire, rather than using the ESP I2C interface directly. Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment