Ошибка при сканировании I2C на ESP32-WROOM-32

Рейтинг: 0Ответов: 2Опубликовано: 12.02.2025

Есть DevBoard на ESP32-WROOM-32 (вариант на 30 контактов, USB-Type-C, CH340C)

ESP32-Wroom-32 введите сюда описание изображения

Connecting.....
Chip is ESP32-D0WD-V3 (revision v3.1)

Набросал код сканера:

// ESP-IDF v5.4
#include <stdio.h>

#include <esp_err.h>
#include <esp_system.h>
#include <driver/gpio.h>
#include <driver/i2c_master.h>
#include <driver/i2c_types.h>

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>


esp_err_t init_i2c(i2c_master_bus_handle_t * handle)
{
    i2c_master_bus_config_t bus = {
            .i2c_port = I2C_NUM_0,
            .sda_io_num = GPIO_NUM_21,
            .scl_io_num = GPIO_NUM_22,
            .clk_source = I2C_CLK_SRC_DEFAULT,
            .glitch_ignore_cnt = 7,
            .intr_priority = 0, // use default
            .trans_queue_depth = 0, // no async ops
            .flags.enable_internal_pullup = true,
            .flags.allow_pd = 0, // disallow power down
    };

    return i2c_new_master_bus(&bus, handle);
}

void app_main(void)
{
    i2c_master_bus_handle_t bus = NULL;

    gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT); // onboard blue led

    gpio_set_level(GPIO_NUM_2, true);

    ESP_ERROR_CHECK(init_i2c(&bus));

    for (uint16_t addr = 1; addr < 127; ++addr)
    {
        esp_err_t result = i2c_master_probe(bus, addr, -1); // -1 == infinity
        const char * reaction = "Unknown error";

        switch (result)
        {
            case ESP_OK:
                reaction = "OK";
                break;
            case ESP_ERR_NOT_FOUND:
                reaction = "NOT FOUND";
                break;
            case ESP_ERR_TIMEOUT:
                reaction = "TIMEOUT (bus failure)";
                break;
            default:
                break;
        }

        printf("%02X : %s (%d) \n", addr, reaction, result);
    }
    gpio_set_level(GPIO_NUM_2, false);

    ESP_ERROR_CHECK(i2c_del_master_bus(bus));

    printf("Restart:");
    for (int i = 10; i > 0; --i)
    {
        printf(" %d", i);
        fflush(stdout);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    printf(" NOW!\n");
    esp_restart();
}

При вызове i2c_master_probe в отладочную консоль сыпет ошибкой шины:

E (279) i2c.master: I2C hardware timeout detected
E (279) i2c.master: probe device timeout. Please check if xfer_timeout_ms and pull-ups are correctly set up

Подскажите, где я мог напортачить?

Дополнительная информация:

  • На шину подключено 2 устройства(модули Arduino, у каждого свои PULL-UP резисторы)
  • устройства рабочие - определяются на Arduino Uno и Raspberry Pi
  • при свапе GPIO_NUM_21 <-> GPIO_NUM_22 (физически + в настройках шины) ошибка пропадает, но и устройства не определяются.

Ответы

▲ 0Принят

Как оказалось, на моём экземпляре DevBoard аппаратные проблемы при использовании GPIO21 - постоянно ~2 вольта на выходе.

Подключил к другим:

  • GPIO16 - SDA(данные)
  • GPIO17 - SCL(тактовый)

esp_err_t init_i2c(i2c_master_bus_handle_t * handle)
{
    i2c_master_bus_config_t bus = {
            .i2c_port = I2C_NUM_0,
            .sda_io_num = GPIO_NUM_16,
            .scl_io_num = GPIO_NUM_17,
            .clk_source = I2C_CLK_SRC_DEFAULT,
            .glitch_ignore_cnt = 7,
            .intr_priority = 0, // use default
            .trans_queue_depth = 0, // no async ops
            .flags.enable_internal_pullup = true,
            //.flags.allow_pd = 0, // disallow power down
    };

    return i2c_new_master_bus(&bus, handle);
}

После этого код из вопроса заработал.

Кроме того, нашел код для старого варианта драйвера I2C:

#include <driver/i2c.h>
void app_main(void)
{
    {
        i2c_config_t cfg = {
            .mode = I2C_MODE_MASTER,
            .sda_io_num = GPIO_NUM_16,
            .scl_io_num = GPIO_NUM_17,
            .sda_pullup_en = GPIO_PULLUP_ENABLE,
            .scl_pullup_en = GPIO_PULLUP_ENABLE,
            .master.clk_speed = 100000,
            .clk_flags = 0
        };
        
        ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &cfg));
        
        ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER
            , 0
            , 0
            , 0
        ));
    }
    while (true) {
        printf("Start scan\n");
        esp_err_t res;
        printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
        printf("00:         ");
        for (uint8_t i = 3; i < 0x78; i++)
        {
            i2c_cmd_handle_t cmd = i2c_cmd_link_create();
            i2c_master_start(cmd);
            i2c_master_write_byte(cmd, (i << 1) | I2C_MASTER_WRITE, 1 /* expect ack */);
            i2c_master_stop(cmd);
    
            res = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
            if (i % 16 == 0)
                printf("\n%.2x:", i);
            if (res == 0)
                printf(" %.2x", i);
            else
                printf(" --");
            i2c_cmd_link_delete(cmd);
        }
        printf("\n\n");
        sleep(1);
    }
}

Подобная проблема есть и при использовании Wire из фреймворка Arduino.
Для исправления нужно явно указать используемые пины:

void setup() {
  Serial.begin(115200);
  Wire.begin(GPIO_NUM_16, GPIO_NUM_17); // sda, scl
}
▲ 0

Подозреваю, что 2вольта от того, что борщнули с pullup. Если у двух девайсов свои резисторы, да ещё и тут включено GPIO_PULLUP_ENABLE, то это гальванически выходит три в параллель. Так и до чистого VCC недолго дойти и устроить КЗ при подаче нуля.