Сколько пинов у ESP32S3? :)

Принято считать, что 49. С номерами от 0 до 48, причем часть пинов физически отсутствует (22..25).

Но вот все функции ESP32-S3 ROM считают иначе:


// Library Function - Single Match
//  gpio_matrix_out
// 
void gpio_matrix_out(uint pin,uint signal,bool invert1,bool invert2) {

  
  if (pin < 54) {
     ...
     читаем-пишем память по адресу ADDRESS + pin * 4
     ...
  }
  return;
}


Соответственно, мы или имеем дело с косяком программиста , (что маловероятно, ибо такое место не одно) или мы имеем дело еще с какими-то скрытыми фишками ESP32-S3.

Китайцы попрятали пины!

Скорее, ушли в налоги. Или наши на таможне для “Байкала” крадуть.

эти пины просто заняты уже другими операциями ?)))

Думаю, что это бонусные пины, которые доступны только тем, кто оформит подписку сразу на год.

Вообще странно видеть “магические числа” в профессиональном коде… Разве не должно быть что-то типа
if (pin < ESP32_PIN_COUNT)

А что до того, что реально пинов меньше - может это какие-то виртуальные пины, на которые биндится какая-то логическая структура… что-то типа “bit banding” но для пинов :slight_smile:

Это после декомпилятора. Скорее всего в оригинале там макро.

Спойлер

Профессиональный код, честно говоря, на четверку с минусом. Много опечаток в названиях функций. Встречается езда по памяти, например, в драйвере wifi (ну это я отрепортил). Есть гонки. Есть косяковое место с мутексами, но оно мне самому интересно пока, поэтому тссс :). Есть код, который так написан, что никогда не исполнится. Хотя вроде как есть ситуации, когда он должен исполняться. Все закрыто одним глобальным мутексом, который дергается на любой чих - и прием пакета дергает мутекс и какие-то низкоприоритетные операции по тацмеру дергают тот же мутекс. Вроде бы BSD стек портировали они себе старенький, но концепт УРОВНЕЙ они не утянули. Сделали один уровень, закрытый мутексом. Ну такое себе. Поэтому и скорость такая, какая есть. А могла бы быть в 2 раза больше.

Очень бы хотелось узнать, какими :slight_smile:

но это ии))) а верить ему можно аккуратно…

Согласно официальной документации, у ESP32-S3 действительно 45 физических GPIO (0-21 и 26-48) . Пин 22 физически не существует, что делает проверка pin < 54 на первый взгляд странной.

Однако gpio_matrix_out работает не напрямую с ножками кристалла, а с GPIO Matrix — сложным коммутатором, который позволяет назначить любой внутренний сигнал (UART, I2C, PWM) на любой вывод .

🗺️ «Теневые» пины: что находится по адресам 22-53?
В регистровом пространстве GPIO Matrix зарезервированы слоты для всех 54 возможных каналов ввода-вывода. Слоты с 22 по 53 делятся на три типа:

Слоты 22, 23, 24, 25 (Физически отсутствуют): Если вы попытаетесь использовать их, сигнал уйдет «в никуда», не вызвав ошибки или сбоя. Физически вы просто не увидите его на ножке, потому что ножки нет.

Слоты 26-48 (Реальные пины): Здесь все стандартно — физические выводы кристалла.

Слоты 49-53 (Спецфункции): Это самое интересное. Эти номера зарезервированы для управления специальными функциями чипа, которые не выведены на внешние пины, но управляются через ту же систему матрицы. Например:

Внутренний тактовый сигнал (CLK_OUT): Сигнал с номера 49 (как вариант) может тактировать внутренние модули.

USB- Serial/JTAG: Номера могут использоваться для перенаправления отладочных сигналов внутрь контроллера USB.

⚙️ Как работает GPIO Matrix (Главный ключ к пониманию)
Главная фишка ESP32 — GPIO Matrix (или просто "IO MUX") . Это виртуальный коммутатор, который отвязывает периферию от конкретных пинов. Вы говорите процессору: "Хочу вывести сигнал UART на пин 10".

Матрица ищет слот №10 и соединяет его с нужной функцией. Матрица спроектирована так, чтобы иметь фиксированный размер памяти (54 слота), потому что переделывать архитектуру под каждую новую модель чипа (где разное количество пинов) дорого и сложно . Производителю просто оставить 54 слота "на вырост" или для внутренних нужд.

💎 Вывод
Код в ROM работает идеально. Он просто кладет данные в ячейку памяти по адресу ADDRESS + pin * 4.

Если вы укажете pin = 23 (которого нет), регистр запишется, сигнал уйдет в матрицу, но так как физического контакта снаружи нет — вы просто ничего не увидите на ножке.

Это позволяет использовать единый драйвер для всех моделей ESP32-S3, независимо от того, 45 пинов у чипа в корпусе или 54 в условной футуристической версии.
26, 32 CS и CLK для PSRAM возможно…
49 Внутренний тактовый сигнал (CLK_OUT) Сигнал для отладки тактирования
50 USB Serial/JTAG (вход) Можно перенаправить UART внутрь USB-контроллера
На платах с ESP32-S3-WROOM-2 или маркировкой R8/R16V используется Octal PSRAM . Тогда заняты 6 пинов:

Пины	Назначение
26, 27, 28, 29, 30, 31, 32	SPI1 (внутренняя шина для flash/PSRAM)
33, 34, 35, 36, 37	Octal PSRAM (старшие линии данных и DQS)
⚠️ Важно: Если у вас плата с 8 MB PSRAM (ESP32-S3-WROOM-1 N8R8 или N16R8/N16R16V), то пины 35, 36, 37 заняты и не доступны для внешнего использования .

Как определить:
N8R2 или N4R2 — Quad PSRAM (только пины 26, 32 заняты)

N8R8, N16R8, N16R16V — Octal PSRAM (пины 26-37 сильно ограничены)
#include <Arduino.h>
#include <rom/gpio.h>

// Таблица статусов пинов
const char* pinStatus[54] = {0};

void analyzeAllPins() {
    Serial.println("\n=== АНАЛИЗ ВСЕХ 54 СЛОТОВ GPIO MATRIX ===\n");
    
    for(int pin = 0; pin < 54; pin++) {
        bool existsInMatrix = true;  // Все 54 существуют в матрице
        bool hasPhysicalPin = false;
        bool isSpecial = false;
        String note = "";
        
        // Физические пины (0-21 и 26-48)
        if((pin >= 0 && pin <= 21) || (pin >= 26 && pin <= 48)) {
            hasPhysicalPin = true;
            if(pin >= 26 && pin <= 32) note = " (может быть занят PSRAM)";
            if(pin >= 43 && pin <= 44) note = " (UART0 консоль)";
            if(pin >= 45 && pin <= 46) note = " (Strapping пины)";
        }
        
        // "Дырки" - нет физического пина
        if(pin >= 22 && pin <= 25) {
            hasPhysicalPin = false;
            note = " ❌ ФИЗИЧЕСКИ ОТСУТСТВУЕТ!";
        }
        
        // Специальные слоты (49-53)
        if(pin >= 49 && pin <= 53) {
            hasPhysicalPin = false;
            switch(pin) {
                case 49: note = " (Внутренний CLK_OUT)"; break;
                case 50: note = " (USB-JTAG/Serial)"; break;
                case 51: note = " (Внутренний сигнал)"; break;
                case 52: note = " (Внутренний сигнал)"; break;
                case 53: note = " (Внутренний сигнал)"; break;
            }
        }
        
        // Вывод статуса
        Serial.printf("Слот %3d: В матрице=ДА | Физический=%s | %s\n", 
            pin,
            hasPhysicalPin ? "ДА " : "НЕТ",
            hasPhysicalPin ? (String("OK") + note).c_str() : note.c_str()
        );
    }
    
    Serial.println("\n=== ИТОГО ===");
    Serial.println("Слоты 0-53: 54 слота (все есть в GPIO Matrix)");
    Serial.println("Физические пины: 0-21 и 26-48 (всего 45 пинов)");
    Serial.println("Пустые слоты: 22-25 (4 слота - физически отсутствуют)");
    Serial.println("Спец-слоты: 49-53 (5 слотов - внутренние функции)");
}

void testMatrixWrite(int pin) {
    Serial.printf("\n--- Тест записи в слот %d ---\n", pin);
    
    // Пытаемся записать сигнал (любой, например 0x38 - FSPI CLK)
    gpio_matrix_out(pin, 0x38, false, false);
    
    // Читаем что записалось
    uint32_t regAddr = GPIO_FUNC0_OUT_SEL_CFG_REG + (pin * 4);
    uint32_t regValue = READ_PERI_REG(regAddr);
    
    Serial.printf("Регистр 0x%08X = 0x%08X\n", regAddr, regValue);
    
    if(regValue != 0) {
        Serial.printf("✅ Слот %d: запись успешна (сигнал уйдет в матрицу)\n", pin);
        if(pin >= 22 && pin <= 25) {
            Serial.printf("⚠️  Но физического пина нет - сигнал пропадет!\n");
        } else if(pin >= 49) {
            Serial.printf("🔧 Специальный слот - сигнал уйдет внутрь чипа\n");
        }
    }
}

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    // 1. Анализ всех слотов
    analyzeAllPins();
    
    // 2. Тест записи в разные типы слотов
    testMatrixWrite(10);   // обычный физический пин
    testMatrixWrite(23);   // пустой слот (нет пина)
    testMatrixWrite(50);   // специальный слот
    
    Serial.println("\n=== ВЫВОД ===");
    Serial.println("Все 54 слота (0-53) существуют в GPIO Matrix");
    Serial.println("Физических пинов только 45 (0-21 и 26-48)");
    Serial.println("Слоты 22-25: есть в матрице, но нет ножек");
    Serial.println("Слоты 49-53: зарезервированы для внутренних функций");
}

void loop() {}

там еще и от платы зависит…

и отдельно продублирую
gpio_matrix_out() работает не с физическими ножками, а с виртуальными слотами GPIO Matrix — таблицей маршрутизации сигналов внутри чипа. Эта таблица имеет фиксированный размер 54 слота (0–53) по архитектурным причинам, независимо от того, сколько из них реально выведено на корпус. ESP32-S3 наследует GPIO Matrix от более старых чипов (ESP32, ESP32-S2). У ESP32 было больше пинов, плюс закладывали запас “на будущее”. Проще оставить старый размер матрицы, чем переписывать драйверы и рисковать несовместимостью.

Это, по большей части, галлюциноз.

не исключено, если рассуждать логически,(psram лучший тому пример) они просто заняты, иначе вывели бы, а 54 это просто… просто массив созданный и не используемый)))

GPIO26~32 — SPI flash/PSRAM (не рекомендованы для других целей)

  • GPIO33~37 — заняты при использовании Octal flash/PSRAM (модели R8/R16V)

3. GPIO19, GPIO20 — USB-JTAG

“If they are reconfigured to operate as normal GPIOs, USB-JTAG functionality will be disabled”

4. Strapping пины: GPIO0, GPIO3, GPIO45, GPIO46

Влияют на загрузку — нужно быть осторожным.

если коротко так, взято вот от сюда https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html

Espressif как-то позабыло указать, что BOOTSTRAP пинов есть еще как минимум два. В документации отсутствуют, но присутствуют в ROM коде. Один из этих недокументированных пинов включает SPI_DOWNLOAD_MODE. Помимо обычной загрузки через UART, можно еще шить напрямумую через SPI, но в деталях я не разбирался.

Вот, пины, которые ROM считает бутстрапными:

 // 100000 - GPIO3
 // 010000 - GPIO45
 // 001000 - GPIO0
 // 000100 - GPIO46
 // 000010 - GPIO1
 // 000001 - GPIO2

Их комбинации, помимо включение\отключения дебага и прочего умеют следующее:

Стандартное:
------------
DOWNLOAD(USB/UART0)
SPI_FAST_FLASH_BOOT
SPI_FLASH_BOOT

Китайские расширения. Загрузка и старт с UART, загрузка и старт с SPI:
----------------------
UART0_BOOT
SPI_DOWNLOAD_BOOT

Так же есть комбинация, которая умышленно вешает систему без возможности перезагрузиться по watchdog.

...
....
 uVar3 = MMIO_GPIO_STRAPPING;
 if ((uVar3 & 0xf) == 5) {
   ets_printf("%s %u \n","ets_main.c",0x107);
   do {
     // preved
   } while( true );
 }
...
...