ESP32直接端口操作

时间:2018-09-15 21:14:23

标签: c arduino display adafruit esp32

尊敬的StackOverflowers,

我正在尝试将Adafruit(link)的HX8357D 3.5“ TFT与esp32配合使用。TFT驱动程序具有两个接口:SPI和8位并行。Adafruit(link提供的库)仅在esp32上支持SPI。我需要具有更高的显示速度,因此我决定自己尝试添加对esp32的支持。我完全没有这种编程经验,但是我喜欢挑战。

我通过对Arduino Uno / Mega支持进行反向工程来弄清楚8位接口是如何工作的。为了增加对esp32的支持,我需要一种直接操作控制esp32的gpio端口的寄存器的方法。我在互联网上四处张望,但是很少有如何执行此操作的示例。 Espressif(link)的技术参考手册包含所有需要的信息,但是我不够熟练,无法弄清楚如何将其转换为代码。

要编程esp32,我使用esp32 Arduino内核。此示例(link)显示了如何将gpio引脚设置为输出,并直接使用寄存器将其设置为高电平和低电平。问题是我需要能够将8个引脚设置为输出,向它们写入数据,使其成为输入,然后从它们读取数据,所有这些都使用寄存器而不是使用pinMode,digitalRead和digitalWrite函数。

我很清楚它在Arduino Uno / Mega上的工作方式,其中有三个控制端口的寄存器:

  • 要读取/写入的DDR *
  • PORT *将gpio设置为高/低
  • 如果gpio为INPUT,则通过PIN *读取HIGH / LOW。

但是这在esp32上如何工作,如何利用寄存器创建这种8位并行通信?

如果有人在此主题上的知识比我更多,我将不胜感激。预先感谢。

4 个答案:

答案 0 :(得分:3)

有很多方法可以做到这一点。我经常一针一线地做。

一种简单的方法是通过定义变量来创建自己的“寄存器”。如果寄存器是8位宽,则定义字节变量:

unsigned char disp_register;

然后,您像在显示硬件中一样写入该寄存器。当然,接下来您必须将此寄存器输出到ESP32的GPIO引脚。由于所有引脚都已固定,因此您必须逐个引脚执行此操作。定义硬件引脚以提高可读性:

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

答案 1 :(得分:3)

为了最大程度地减少操作8个引脚时的计算负担,您将希望这些引脚与连续的GPIO编号相对应(例如GPIO12至GPIO19)。 以下是一种实现方式,它可以并行操作多个输入/输出引脚,并且如果满足上述要求(连续的GPIO编号)并且GPIO编号均在0-31范围内,则可以工作;我使用了GPIO12到GPIO19(GPIO12对应于输入/输出8位值中的位0),如果您的ESP32开发板带有ESP-WROOM-32或ESP32-WROVER模块,则可以方便地使用它们。 因此,我定义了与位0对应的GPIO,如下所示:

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

答案 2 :(得分:0)

对于高带宽并行数据输出,您可能需要研究ESP32的I2S外设的LCD模式。

有关将外围设备映射到所需引脚的信息,请参见ESP32 TRM中的12.5.1节和第4章。这种方法的优点在于,您可以将多达24位的输出从外设映射到输出引脚。

第12.4.4节规定:

  

ESP32 I2S模块执行数据传输操作,根据用户的配置串行或并行输出数据

答案 3 :(得分:0)

请记住,您可能需要第 9 个“选通”引脚用于输出,以告诉接收设备其他 8 个引脚现在有效。