ESP32 прямое управление портом

Дорогие StackOverflowers,

Я пытаюсь использовать HX8357D 3.5 "TFT от Adafruit (ссылка) с esp32. Драйвер TFT имеет два интерфейса: SPI и 8-битный параллельный. Предоставляемая библиотека от Adafruit (ссылка) поддерживает SPI только на esp32. Мне нужно имеют более высокую скорость отображения, поэтому я решил сам попробовать добавить поддержку esp32. У меня совсем не было опыта в этом виде программирования, но мне понравилась задача.

Я выяснил, как работает 8-битный интерфейс, путем обратного проектирования поддержки Arduino Uno / Mega. Чтобы добавить поддержку esp32, мне нужен способ напрямую управлять регистрами, управляющими портами gpio esp32. Я поискал в интернете, но примеров того, как это сделать, очень мало. Техническое справочное руководство Espressif (ссылка) содержит всю необходимую информацию, но я недостаточно квалифицирован, чтобы понять, как преобразовать это в код.

Для программирования esp32 я использую ядро ​​esp32 Arduino. В этом примере (ссылка) показано, как установить выводы gpio в качестве вывода и сделать их ВЫСОКИМИ и НИЗКИЕ напрямую с помощью регистров. Проблема в том, что мне нужно иметь возможность установить 8 контактов в качестве вывода, записать на них данные, сделать их вводом, а затем прочитать данные из них, используя регистры вместо функций pinMode, digitalRead и digitalWrite.

Мне ясно, как это работает на Arduino Uno / Mega, есть три регистра, которые управляют портом:

  • DDR * для чтения / записи
  • ПОРТ * для установки ВЫСОКОГО / НИЗКОГО gpio
  • PIN * для чтения ВЫСОКИЙ / НИЗКИЙ, если gpio - ВХОД.

Но как это работает на esp32 и как я могу использовать регистры для создания этой 8-битной параллельной связи?

Если есть кто-то, кто имеет больше ноу-хау, чем я, по этой теме, я был бы очень благодарен за объяснение. Заранее спасибо.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
11 880
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Есть много способов сделать это. Я часто делаю это построчно.

Один простой способ - создать свой собственный «регистр», определив переменную. Если регистр 8-битный, определите байтовую переменную:

unsigned char disp_register;

Затем вы записываете в этот регистр, как если бы он существовал в аппаратном обеспечении дисплея. Конечно, затем вы должны вывести этот регистр на контакты GPIO ESP32. Поскольку штифты на всем протяжении, вы должны делать это по очереди. Определите ваши аппаратные контакты для удобочитаемости:

/* OUTPUTS (numbers mean GPIO port) */
#define REGISTER_BIT7_ON_PIN        9
#define REGISTER_BIT6_ON_PIN        10
#define REGISTER_BIT5_ON_PIN        5
// continue with all the pins you need

Где-нибудь в начале вашей программы установите эти контакты как выходные и, возможно, сделайте их значение по умолчанию равным '0':

io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pin_bit_mask =  ((1ULL<< REGISTER_BIT7_ON_PIN) | (1ULL<< REGISTER_BIT6_ON_PIN) | (1ULL<< REGISTER_BIT5_ON_PIN)); // of course, do like this all the pins
gpio_config(&io_conf);

gpio_set_level(REGISTER_BIT7_ON_PIN, 0); // do like this all the pins you need to set the boot-up value, pin-by-pin

Затем вам понадобится ваша функция для копирования вашего реестра во внешний мир контактов GPIO:

/*
 * wrote this simply for ease of understanding, feel free to do this in a loop
 * or shifting bit by bit
 */
void copy_register_to_GPIO_pins(unsigned char disp_register)
{
    gpio_set_level(REGISTER_BIT7_ON_PIN, (disp_register & 0x80) >> 7);
    gpio_set_level(REGISTER_BIT6_ON_PIN, (disp_register & 0x40) >> 6);
    gpio_set_level(REGISTER_BIT5_ON_PIN, (disp_register & 0x20) >> 5);
    gpio_set_level(REGISTER_BIT4_ON_PIN, (disp_register & 0x10) >> 4);
    gpio_set_level(REGISTER_BIT3_ON_PIN, (disp_register & 0x08) >> 3);
    gpio_set_level(REGISTER_BIT2_ON_PIN, (disp_register & 0x04) >> 2);
    gpio_set_level(REGISTER_BIT1_ON_PIN, (disp_register & 0x02) >> 1);
    gpio_set_level(REGISTER_BIT0_ON_PIN, (disp_register & 0x01));
}

Затем, после того, как вы что-нибудь записали в свой регистр, вызовите свою функцию, чтобы вывести это:

disp_register = 0x2A; // example value you want to send to display
copy_register_to_GPIO_pins(disp_register);

// or, output byte WITHOUT using any register:
copy_register_to_GPIO_pins(0x2A);

Надеюсь, вы сможете сделать обратное самостоятельно, считывание выводов выполняется другой функцией, где вы копируете каждое значение вывода GPIO и собираете его в байтовую переменную. Конечно, на этом этапе контакты должны быть установлены на входы. В общем:

/*
 * wrote this simply for ease of understanding
 */
unsigned char copy_GPIO_pins_to_register(void)
{
    unsigned char retval = 0;

    retval |= gpio_get_level(REGISTER_BIT7_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT6_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT5_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT4_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT3_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT2_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT1_ON_PIN);
    retval = retval << 1;
    retval |= gpio_get_level(REGISTER_BIT0_ON_PIN);

    return retval;
}

Спасибо за ответ, вы все прояснили. Дело в том, что ищу способ напрямую записывать данные на все порты gpio. 8 контактов, которые мне нужны, можно выбрать бесплатно, а у esp32 32 контакта на один регистр, так что это не должно быть проблемой, верно? Я не совсем понимаю, почему вы сказали: «Все контакты кончились, поэтому вам придется делать это по очереди»? Я подумал, что если у вас есть правильная битовая маска, вы можете писать на несколько контактов gpio, напрямую записывая в регистр. В таблице данных я обнаружил, что эти регистры называются W1TS и W1TC. Вы знаете, как это сделать?

Daan van Driel 17.09.2018 09:03

Я не знаю, есть ли у ESP32 возможность прямого параллельного порта для использования, как вы хотите (попробуйте их техническую поддержку - они полезны).

EmbeddedGuy 17.09.2018 17:24

Чтобы минимизировать вычислительную нагрузку при работе с 8 контактами, вам нужно, чтобы эти контакты соответствовали последовательным номерам GPIO (например, от GPIO12 до GPIO19). Ниже представлена ​​реализация, которая управляет несколькими выводами ввода / вывода параллельно и работает, если выполняется указанное выше требование (последовательные номера GPIO) и если все номера GPIO находятся в диапазоне 0–31; Я использовал GPIO12 - GPIO19 (GPIO12 соответствует биту 0 в 8-битных значениях ввода / вывода), которые удобно использовать, если у вас есть плата разработчика ESP32 с модулем ESP-WROOM-32 или ESP32-WROVER. Итак, я определил GPIO, соответствующий биту 0, как показано ниже:

#define PARALLEL_0  12

При инициализации вам необходимо настроить все 8 контактов GPIO, например. установив их все как входные:

void setup() {
  for (int i = 0; i < 8; i++) {
    pinMode(PARALLEL_0 + i, INPUT);
  }
}

После этого вы можете использовать следующие функции для установки 8 контактов в качестве входов или выходов, а также для чтения входных значений и записи выходных значений:

void parallel_set_inputs(void) {
  REG_WRITE(GPIO_ENABLE_W1TC_REG, 0xFF << PARALLEL_0);
}

void parallel_set_outputs(void) {
  REG_WRITE(GPIO_ENABLE_W1TS_REG, 0xFF << PARALLEL_0);
}

uint8_t parallel_read(void) {
  uint32_t input = REG_READ(GPIO_IN_REG);

  return (input >> PARALLEL_0);
}

void parallel_write(uint8_t value) {
  uint32_t output =
    (REG_READ(GPIO_OUT_REG) & ~(0xFF << PARALLEL_0)) | (((uint32_t)value) << PARALLEL_0);

  REG_WRITE(GPIO_OUT_REG, output);
}

Для параллельного вывода данных с высокой пропускной способностью вы можете исследовать режим ЖК-дисплея периферийного устройства ESP32 I2S.

См. Раздел 12.5.1 в ESP32 TRM и главу 4 о привязке периферийного устройства к желаемым контактам. Хорошая вещь в этом подходе заключается в том, что вы можете сопоставить до 24 бит вывода с периферийного устройства на выходной контакт.

В разделе 12.4.4 говорится:

The ESP32 I2S module carries out a data-transmit operation [...] Clock out data serially, or in parallel, as configured by the user

Помните, что вам может понадобиться 9-й вывод «стробоскопа» для вывода, чтобы сообщить принимающему устройству, что остальные 8 контактов действительны сейчас.

Другие вопросы по теме