Page 1 of 2

I2C Interrupt

Posted: Fri Dec 08, 2023 8:10 am
by zeynep01
Hi,
I want to use I2C interrupt on esp-idf. What can I do? I couldn’t find any examples. (bare-metal)

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 11:35 am
by zeynep01
I tried with this code but I don't work.

Code: Select all

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/i2c.h>
#include "esp_log.h"
#include "esp_intr_alloc.h"

#include <soc/i2c_reg.h>
#include <esp32/rom/gpio.h>
#include <soc/gpio_sig_map.h>
#include <hal/i2c_ll.h>
#include <hal/i2c_hal.h>
#include <hal/gpio_hal.h>

#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_SCL_IO 2
#define I2C_MASTER_SDA_IO 8
#define I2C_MASTER_FREQ_HZ 100000

#define DEVICE_ADDRESS 0x03

static const char *TAG = "I2C_TASK";

uint8_t receivedData;

static void i2c_master_init()
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
        .clk_flags = 0,
    };
    i2c_param_config(I2C_MASTER_NUM, &conf);
    i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
}

static void i2c_master_read_data()
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (DEVICE_ADDRESS << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &receivedData, I2C_MASTER_ACK);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(50));
    i2c_cmd_link_delete(cmd);
    if (ret == ESP_OK)
    {
        ESP_LOGI(TAG, "I2C data read successfully");
        ESP_LOGI(TAG, "value: %d\n", receivedData);
    }
    else
    {
        ESP_LOGE(TAG, "I2C data read failed.");
    }

    vTaskDelay(pdMS_TO_TICKS(500));
}

static void IRAM_ATTR i2c_master_isr_handler(void *arg)
{
    i2c_master_read_data();
}

void app_main()
{
    i2c_master_init();
    esp_intr_alloc(I2C_MASTER_NUM, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, i2c_master_isr_handler, NULL, NULL);
}

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 12:38 pm
by MicroController
zeynep01 wrote:
Fri Dec 08, 2023 8:10 am
Hi,
I want to read values via master I2C with interrupt. How can I do it? I couldn't find any examples.
No, you don't want that. And you can't while still using the IDF's I2C driver.

You can create a dedicated task which handles the I2C communication and processes read data as desired, including calling a callback or posting a message to a queue for another task to consume.

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 12:42 pm
by zeynep01
MicroController wrote:
Fri Dec 08, 2023 12:38 pm
zeynep01 wrote:
Fri Dec 08, 2023 8:10 am
Hi,
I want to read values via master I2C with interrupt. How can I do it? I couldn't find any examples.
No, you don't want that. And you can't while still using the IDF's I2C driver.
why ?

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 12:52 pm
by MicroController
1) The I2C driver controls the I2C peripheral, and for that it uses its own I2C interrupt handler.
2) This interrupt handler does all the low-level interaction with the I2C peripheral, which you don't want to implement yourself.
3) The driver's API is designed to be blocking. This precludes any use from within an ISR, but allows for convenient use inside of a task.
4) You probably don't actually want to write an ISR but rather have some kind of 'I'm done' callback function approach. If you really want to use that pattern, you can 'emulate' it from a custom task.

(For reference, this is the I2C driver's ISR. You don't want to re-implement that :))

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 1:13 pm
by MicroController
When I press my button on my slave device I send value to my master device.
With I2C, the master is in charge of all communication. A slave cannot send a value to the master by itself, it can only respond to a read request from the master. So either the master has to loop polling the slave for the value of interest, or the slave needs some other way to notify the master that it has new data ready for the master to read. Many I2C slave chips/devices have a dedicated 'interrupt' output pin which can be used by the master to know when to issue a new read to the slave.

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 1:47 pm
by zeynep01
zeynep01 wrote:
Fri Dec 08, 2023 12:42 pm
MicroController wrote:
Fri Dec 08, 2023 12:38 pm
zeynep01 wrote:
Fri Dec 08, 2023 8:10 am
Hi,
I want to read values via master I2C with interrupt. How can I do it? I couldn't find any examples.
No, you don't want that. And you can't while still using the IDF's I2C driver.
why ?

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 1:54 pm
by zeynep01
zeynep01 wrote:
Fri Dec 08, 2023 11:35 am
I am using two microcontroller. I communicate via I2C. When I press my button on my slave device I send value to my master device. I can do it without interrupt, isr.

But I want to use Interrupt. I tried with this code but I don't work. When I send a value I can't read .

Code: Select all

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/i2c.h>
#include "esp_log.h"
#include "esp_intr_alloc.h"

#include <soc/i2c_reg.h>
#include <esp32/rom/gpio.h>
#include <soc/gpio_sig_map.h>
#include <hal/i2c_ll.h>
#include <hal/i2c_hal.h>
#include <hal/gpio_hal.h>

#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_SCL_IO 2
#define I2C_MASTER_SDA_IO 8
#define I2C_MASTER_FREQ_HZ 100000

#define DEVICE_ADDRESS 0x03

static const char *TAG = "I2C_TASK";

uint8_t receivedData;

static void i2c_master_init()
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
        .clk_flags = 0,
    };
    i2c_param_config(I2C_MASTER_NUM, &conf);
    i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
}

static void i2c_master_read_data()
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (DEVICE_ADDRESS << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &receivedData, I2C_MASTER_ACK);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(50));
    i2c_cmd_link_delete(cmd);
    if (ret == ESP_OK)
    {
        ESP_LOGI(TAG, "I2C data read successfully");
        ESP_LOGI(TAG, "value: %d\n", receivedData);
    }
    else
    {
        ESP_LOGE(TAG, "I2C data read failed.");
    }

    vTaskDelay(pdMS_TO_TICKS(500));
}

static void IRAM_ATTR i2c_master_isr_handler(void *arg)
{
    i2c_master_read_data();
}

void app_main()
{
    i2c_master_init();
    esp_intr_alloc(I2C_MASTER_NUM, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, i2c_master_isr_handler, NULL, NULL);
}
so @MicroController is this code a faulty algorithm?

Re: I2C Interrupt

Posted: Fri Dec 08, 2023 2:00 pm
by MicroController
As I said, I see two main issues with this code:
1) You can't allocate the I2C interrupt to your own handler and use the IDF's I2C driver, and
2) you cannot call i2c_master_cmd_begin(...) from an ISR.
And possibly
3) I2C will not do anything (interrupt...) while the master isn't actively performing a transaction.

Re: I2C Interrupt

Posted: Mon Dec 11, 2023 6:38 am
by zeynep01
MicroController wrote:
Fri Dec 08, 2023 2:00 pm
As I said, I see two main issues with this code:
1) You can't allocate the I2C interrupt to your own handler and use the IDF's I2C driver, and
2) you cannot call i2c_master_cmd_begin(...) from an ISR.
And possibly
3) I2C will not do anything (interrupt...) while the master isn't actively performing a transaction.
I don't use FreeRTOS I use bare-metal. I want to use I2C Interrupt. What can I do?