Working with two I2C ports on different cores

User avatar
yurriiy
Posts: 4
Joined: Wed May 31, 2023 7:00 am

Working with two I2C ports on different cores

Postby yurriiy » Tue Jun 13, 2023 9:26 pm

Hi, i have problem when running in parallel with two ports on different cores.
IDF version: v5.0.2
first task xTaskCreatePinnedToCore() run in core 1 using I2C_NUM_0
second task xTaskCreatePinnedToCore() run in core 0 using I2C_NUM_1
there may be different errors, for example,

Code: Select all

Backtrace: 0x400dac13:0x3ffe9dd0 0x400db639:0x3ffe9df0 0x400d749a:0x3ffe9e30 0x400d754d:0x3ffe9e60 0x400d6a67:0x3ffe9e80 0x4008a739:0x3ffea020
0x400dac13: i2c_cmd_log_alloc_error at C:/Users/Yuriy/esp/esp-idf/components/driver/i2c.c:1196
 (inlined by) i2c_cmd_link_append at C:/Users/Yuriy/esp/esp-idf/components/driver/i2c.c:1217
0x400db639: i2c_master_start at C:/Users/Yuriy/esp/esp-idf/components/driver/i2c.c:1232 (discriminator 2)
When I run only the first task or only the second task everything works correctly.

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: Working with two I2C ports on different cores

Postby ESP_Sprite » Wed Jun 14, 2023 2:16 am

i2c_cmd_log_alloc_error is an error routine called when the driver can't allocate any memory. Are you sure that's not the issue?

User avatar
yurriiy
Posts: 4
Joined: Wed May 31, 2023 7:00 am

Re: Working with two I2C ports on different cores

Postby yurriiy » Wed Jun 14, 2023 9:56 am

The error can be different, apparently depends on at what point in time the parallel tasks were working.
Sometimes the error occurs in the method free() -> i2c_cmd_link_delete()
At the moment I have solved the problem by using task synchronization, but I do not understand why different tasks on two different cores using different I2C ports cannot work in parallel...

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: Working with two I2C ports on different cores

Postby ESP_Sprite » Thu Jun 15, 2023 1:45 am

Could also be memory corruption then. If you share your code (preferably minimized to the smallest example that has the issue), we can take a look. It'd also help if you could post the exact error, preferably from a few lines before the guru meditation all the way to the end.

User avatar
yurriiy
Posts: 4
Joined: Wed May 31, 2023 7:00 am

Re: Working with two I2C ports on different cores

Postby yurriiy » Thu Jun 15, 2023 8:08 am

Board: ESP32-DevKitC
I2C_NUM_0 - two ADS1115
I2C_NUM_1 - one FRAM FM24CL16B-GTR

i write a small test case:

Code: Select all

#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "spi_flash_mmap.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/timers.h"
#include "soc/rtc.h"
#include "esp_timer.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_random.h"
#include "rom/gpio.h"
#include "esp_task_wdt.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "driver/i2c.h"
#include <rom/ets_sys.h>

#define ADS1115_REG_POINTER_CONVERT (0x00)
#define ADS1115_REG_POINTER_CONFIG (0x01)
#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000)
#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000)
#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000)
#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000)
#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003)
#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000)
#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000)
#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000)
#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100)
#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000)
#define ADS1115_REG_CONFIG_DR_860SPS (0x00E0)
#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000)

#define ADS_I2C_FREQ_HZ 1000000
#define FRAM_I2C_FREQ_HZ 1000000

#define ADS_I2C_NUM I2C_NUM_0
#define FRAM_I2C_NUM I2C_NUM_1

#define ADS_SCL GPIO_NUM_22
#define ADS_SDA GPIO_NUM_21

#define FRAM_SCL GPIO_NUM_18
#define FRAM_SDA GPIO_NUM_19

#define ADS1115_I2C_ADDRESS_1 0b1001000 // ADDR = GND
#define ADS1115_I2C_ADDRESS_2 0b1001001 // ADDR = VDD

#define FRAM_I2C_ADDRESS 0b1010 // base part - Slave ID

static const char *TAG = "main";

static i2c_config_t conf0 = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = ADS_SDA,
    .sda_pullup_en = GPIO_PULLUP_DISABLE,
    .scl_io_num = ADS_SCL,
    .scl_pullup_en = GPIO_PULLUP_DISABLE,
    .master.clk_speed = ADS_I2C_FREQ_HZ,
    .clk_flags = 0,
};

static i2c_config_t conf1 = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = FRAM_SDA,
    .sda_pullup_en = GPIO_PULLUP_DISABLE,
    .scl_io_num = FRAM_SCL,
    .scl_pullup_en = GPIO_PULLUP_DISABLE,
    .master.clk_speed = FRAM_I2C_FREQ_HZ,
    .clk_flags = 0,
};

static void i2c_init()
{
    ESP_LOGI(TAG, "i2c_init");
    i2c_param_config(ADS_I2C_NUM, &conf0);
    i2c_driver_install(ADS_I2C_NUM, conf0.mode, 0, 0, 0);

    i2c_param_config(FRAM_I2C_NUM, &conf1);
    i2c_driver_install(FRAM_I2C_NUM, conf1.mode, 0, 0, 0);
}

static void feed_the_dog()
{
    // feed dog 0
    TIMERG0.wdtwprotect.val = TIMG_WDT_WKEY_VALUE; // write enable
    TIMERG0.wdtfeed.val = 1;                       // feed dog
    TIMERG0.wdtwprotect.val = 0;                   // write protect
    // feed dog 1
    TIMERG1.wdtwprotect.val = TIMG_WDT_WKEY_VALUE; // write enable
    TIMERG1.wdtfeed.val = 1;                       // feed dog
    TIMERG1.wdtwprotect.val = 0;                   // write protect
}

static uint16_t ads1115_get_raw_value(uint8_t address, uint16_t channel)
{
    uint16_t config = ADS1115_REG_CONFIG_CQUE_NONE |
                      ADS1115_REG_CONFIG_CLAT_NONLAT |
                      ADS1115_REG_CONFIG_CPOL_ACTVLOW |
                      ADS1115_REG_CONFIG_CMODE_TRAD |
                      ADS1115_REG_CONFIG_MODE_SINGLE;

    config |= ADS1115_REG_CONFIG_PGA_6_144V;
    config |= ADS1115_REG_CONFIG_DR_860SPS;
    config |= ADS1115_REG_CONFIG_OS_SINGLE;
    config |= channel;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, ADS1115_REG_POINTER_CONFIG, true);
    i2c_master_write_byte(cmd, (uint8_t)(config >> 8), true);
    i2c_master_write_byte(cmd, (uint8_t)config, true);
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(ADS_I2C_NUM, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);

    ets_delay_us(1170);

    uint8_t data_h = 0, data_l = 0;
    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, address << 1 | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &data_h, false);
    i2c_master_read_byte(cmd, &data_l, false);
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(ADS_I2C_NUM, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);
    if (data_h >> 7 != 1)
    {
        ESP_LOGE(TAG, "Данные еще не готовы");
    }

    i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, address << 1 | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, ADS1115_REG_POINTER_CONVERT, true);
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(ADS_I2C_NUM, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);

    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, address << 1 | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &data_h, false);
    i2c_master_read_byte(cmd, &data_l, false);
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(ADS_I2C_NUM, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);

    uint16_t raw_measurement = (data_h << 8 | data_l);
    int raw_measurement_as_signed_int = (INT16_MAX + 1) & raw_measurement ? 0 : raw_measurement;
    return raw_measurement_as_signed_int;
}

void ads1115_get_data(uint16_t *data_p)
{
    data_p[0] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_1, ADS1115_REG_CONFIG_MUX_SINGLE_0);
    data_p[1] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_1, ADS1115_REG_CONFIG_MUX_SINGLE_1);
    data_p[2] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_1, ADS1115_REG_CONFIG_MUX_SINGLE_2);
    data_p[3] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_1, ADS1115_REG_CONFIG_MUX_SINGLE_3);
    data_p[4] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_2, ADS1115_REG_CONFIG_MUX_SINGLE_2);
    data_p[5] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_2, ADS1115_REG_CONFIG_MUX_SINGLE_3);
    data_p[6] = ads1115_get_raw_value(ADS1115_I2C_ADDRESS_2, ADS1115_REG_CONFIG_MUX_SINGLE_0);
}

static void get_data_task(void *const pvParameters)
{
    ESP_LOGI(TAG, "get_data_task start, CoreId %d", xPortGetCoreID());

    uint8_t channels_count = 7;
    uint8_t measure_count = 20;
    uint16_t buffer[channels_count];
    uint16_t raw_data[channels_count][measure_count];
    float results[channels_count];

    while (true)
    {
        for (int m = 0; m < measure_count; m++)
        {
            ads1115_get_data(buffer);

            for (int c = 0; c < channels_count; c++)
            {
                raw_data[c][m] = buffer[c];
            }
        }
        feed_the_dog();
    }
    ESP_LOGI(TAG, "get_data_task end");
    vTaskDelete(NULL);
}

static void storage_write_uint32_internal(uint32_t value, uint8_t slave_address, uint8_t word_address)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, FRAM_I2C_ADDRESS << 4 | (slave_address << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, word_address, true);

    for (int i = 0; i < sizeof(uint32_t); i++)
    {
        i2c_master_write_byte(cmd, (value >> (8 * i)) & 0xFF, true);
    }

    i2c_master_stop(cmd);
    i2c_master_cmd_begin(FRAM_I2C_NUM, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);
}

static void storage_task(void *const pvParameters)
{
    ESP_LOGI(TAG, "storage_task start, CoreId %d", xPortGetCoreID());
    while (true)
    {
        storage_write_uint32_internal(42, 0, 0);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
    ESP_LOGI(TAG, "storage_task end");
    vTaskDelete(NULL);
}

void app_main()
{
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);

    uint32_t size_flash_chip;
    esp_flash_get_size(NULL, &size_flash_chip);

    rtc_cpu_freq_config_t c;
    rtc_clk_cpu_freq_get_config(&c);

    ESP_LOGI(TAG, "CPU clock frequency: %ld MHz", c.freq_mhz);
    ESP_LOGI(TAG, "This is %s chip with %d CPU cores, WiFi%s%s, ", CONFIG_IDF_TARGET, chip_info.cores, (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
    ESP_LOGI(TAG, "Silicon revision %d, ", chip_info.revision);
    ESP_LOGI(TAG, "%ldMB %s flash", size_flash_chip / (1024 * 1024), (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
    ESP_LOGI(TAG, "configMINIMAL_STACK_SIZE=%d", configMINIMAL_STACK_SIZE);
    ESP_LOGI(TAG, "Free memory: %ld bytes", esp_get_free_heap_size());
    ESP_LOGI(TAG, "IDF version: %s", esp_get_idf_version());

    i2c_init();

    xTaskCreatePinnedToCore(&storage_task, "storage_task", 3000, NULL, 5, NULL, 0);
    xTaskCreatePinnedToCore(&get_data_task, "get_data_task", 8000, NULL, 5, NULL, 1);
}
if you comment out the launch of one of the tasks, the rest works correctly

Code: Select all

    xTaskCreatePinnedToCore(&storage_task...
    xTaskCreatePinnedToCore(&get_data_task...
I (708) main: CPU clock frequency: 240 MHz
I (718) main: This is esp32 chip with 2 CPU cores, WiFi/BT/BLE,
I (718) main: Silicon revision 100,
I (728) main: 4MB external flash
I (728) main: configMINIMAL_STACK_SIZE=768
I (728) main: Free memory: 301396 bytes
I (738) main: IDF version: v5.0.2
I (738) main: i2c_init
I (758) main: storage_task start, CoreId 0
I (758) main: get_data_task start, CoreId 1

assert failed: i2c_cmd_link_append i2c.c:1214 (cmd_desc->cur != NULL)


Backtrace: 0x40081bfe:0x3ffb8430 0x40086ac1:0x3ffb8450 0x4008bcb1:0x3ffb8470 0x400d6e7e:0x3ffb8590 0x400d78e9:0x3ffb85b0 0x400d5d7a:0x3ffb85f0 0x400d5e7c:0x3ffb8620 0x400d5eee:0x3ffb8640 0x4008962d:0x3ffb8790
0x40081bfe: panic_abort at C:/Users/Yuriy/esp/esp-idf/components/esp_system/panic.c:423

0x40086ac1: esp_system_abort at C:/Users/Yuriy/esp/esp-idf/components/esp_system/esp_system.c:153

0x4008bcb1: __assert_func at C:/Users/Yuriy/esp/esp-idf/components/newlib/assert.c:78

0x400d6e7e: i2c_cmd_link_append at C:/Users/Yuriy/esp/esp-idf/components/driver/i2c.c:1214 (discriminator 1)

0x400d78e9: i2c_master_start at C:/Users/Yuriy/esp/esp-idf/components/driver/i2c.c:1232 (discriminator 2)

0x400d5d7a: ads1115_get_raw_value at C:/work/test/main/main.c:138 (discriminator 15)

0x400d5e7c: ads1115_get_data at C:/work/test/main/main.c:162

0x400d5eee: get_data_task at C:/work/test/main/main.c:184

0x4008962d: vPortTaskWrapper at C:/Users/Yuriy/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:154

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: Working with two I2C ports on different cores

Postby ESP_Sprite » Fri Jun 16, 2023 1:51 am

In ads1115_get_raw_value, at some point you do

Code: Select all

i2c_cmd_link_create();
where you should do

Code: Select all

cmd=i2c_cmd_link_create();

User avatar
yurriiy
Posts: 4
Joined: Wed May 31, 2023 7:00 am

Re: Working with two I2C ports on different cores

Postby yurriiy » Fri Jun 16, 2023 7:11 am

omg... my mistake :D
everything works correctly!
thx

Who is online

Users browsing this forum: No registered users and 77 guests