ESP32 direct port manipulation

Daan van Driel picture Daan van Driel · Sep 15, 2018 · Viewed 8.6k times · Source

Dear StackOverflowers,

I am trying to use HX8357D 3.5" TFT from Adafruit (link) with an esp32. The TFT driver has two interfaces: SPI and 8-bit parallel. The provided library from Adafruit (link) only supports SPI on the esp32. I need to have higher display speeds, so I decided to try and add support for the esp32 myself. I'm not experienced at all with this kind of programming, but I liked the challenge.

I figured out how the 8-bit interface work by reverse engineering the Arduino Uno/Mega support. To add the esp32 support I need a way to directly manipulate the registers controlling the gpio ports of the esp32. I looked around on the internet, but there are very little examples of how to do this. The technical reference manual of Espressif (link) contains all the information needed, but I'm not skilled enough to figure out how to translate this into code.

To program the esp32 I use the esp32 Arduino core. This example (link) shows how to set gpio pins as output and make them HIGH and LOW directly using registers. The problem is that I need to be able to set 8 pins as output, write data to them, make them input and then read data from them, all using registers instead of using the pinMode, digitalRead and digitalWrite functions.

The way it works on the Arduino Uno/Mega is clear to me, there are three registers that control a port:

  • DDR* to read/write
  • PORT* to set gpio HIGH/LOW
  • PIN* to read HIGH/LOW if the gpio is INPUT.

But how does this work on the esp32 and how can I make use of the registers to create this 8-bit parallel communication?

If there is anybody that has more know-how than me on this topic I would super be grateful for an explanation. Thanks in advance.

Answer

Francesco Lavra picture Francesco Lavra · Oct 24, 2018

In order to minimize computational burden when operating the 8 pins, you will want these pins to correspond to consecutive GPIO numbers (e.g. GPIO12 to GPIO19). Below is an implementation that operates multiple input/output pins in parallel and works if the above requirement (consecutive GPIO numbers) is met and if GPIO numbers are all in the range 0-31; I used GPIO12 to GPIO19 (GPIO12 corresponds to bit 0 in the input/output 8-bit values), which are handy to use if you have an ESP32 dev board with an ESP-WROOM-32 or ESP32-WROVER module. So I defined the GPIO corresponding to bit 0 as below:

#define PARALLEL_0  12

At initialization, you need to configure all 8 pins a GPIOs, e.g. by setting them all as inputs:

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

After that, you can use the following functions to set the 8 pins as inputs or outputs, and to read the input values and write the output values:

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);
}