ESP32 direct port manipulation

做~自己de王妃 提交于 2019-12-01 00:24:44

There are many ways to do this. I often do it pin-by-pin.

One simple way is to make your own 'register' by defining a variable. If the register is 8-bit wide, define byte variable:

unsigned char disp_register;

Then you write to this register like it would exist in display hardware. Of course, next you must output this register to the GPIO pins of ESP32. Since the pins are all over, you must do this pin-by-pin. Define your hardware pins for readability:

/* 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

Somewhere at the beginning of your program, set these pins as output, and perhaps make their default value to '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

Next you need your function to copy your register to outside world of GPIO pins:

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

Then, after you wrote anything into your register, call your function to output it:

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

Hopefully, you can do the reverse by yourself, reading the pins is done by another function, where you copy each GPIO pin value and assemble it into byte variable. Of course, pins have to be set to inputs at this point. In principle:

/*
 * 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;
}

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

For high-bandwidth parallel data output, you might want to investigate the ESP32's I2S peripheral's LCD mode.

See section 12.5.1 in the ESP32 TRM and chapter 4 on mapping the peripheral to the desired pins. The nice thing about this approach is that you can map up to 24 bits of output from the peripheral to an output pin.

Section 12.4.4 states:

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!