Дорогие 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, есть три регистра, которые управляют портом:
Но как это работает на esp32 и как я могу использовать регистры для создания этой 8-битной параллельной связи?
Если есть кто-то, кто имеет больше ноу-хау, чем я, по этой теме, я был бы очень благодарен за объяснение. Заранее спасибо.





Есть много способов сделать это. Я часто делаю это построчно.
Один простой способ - создать свой собственный «регистр», определив переменную. Если регистр 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;
}
Я не знаю, есть ли у ESP32 возможность прямого параллельного порта для использования, как вы хотите (попробуйте их техническую поддержку - они полезны).
Чтобы минимизировать вычислительную нагрузку при работе с 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 контактов действительны сейчас.
Спасибо за ответ, вы все прояснили. Дело в том, что ищу способ напрямую записывать данные на все порты gpio. 8 контактов, которые мне нужны, можно выбрать бесплатно, а у esp32 32 контакта на один регистр, так что это не должно быть проблемой, верно? Я не совсем понимаю, почему вы сказали: «Все контакты кончились, поэтому вам придется делать это по очереди»? Я подумал, что если у вас есть правильная битовая маска, вы можете писать на несколько контактов gpio, напрямую записывая в регистр. В таблице данных я обнаружил, что эти регистры называются W1TS и W1TC. Вы знаете, как это сделать?