# 专用 GPIO

[English]

## 概述

专用 GPIO 专为 CPU 与 GPIO 矩阵和 IO MUX 交互而设计。任何配置为“专用”的 GPIO 都可以通过 CPU 指令直接访问，从而轻松提高 GPIO 翻转速度，方便用户以 bit-banging 的方式模拟串行/并行接口。通过 CPU 指令的方式控制 GPIO 的软件开销非常小，因此能够胜任一些特殊场合，比如通过示波器观测 “GPIO 翻转信号” 来间接测量某些性能指标。

## 创建/销毁 GPIO 捆绑包

GPIO 捆绑包是一组 GPIO，该组 GPIO 可以在一个 CPU 周期内同时操作。一个包能够包含 GPIO 的最大数量受每个 CPU 的限制。另外，GPIO 捆绑包与派生它的 CPU 有很强的相关性。注意，任何对 GPIO 捆绑包操作的任务都必须运行在 GPIO 捆绑包所属的 CPU 内核。 同理，只有那些安装在同一个 CPU 内核上的 ISR 才允许对该 GPIO 捆绑包进行操作。

备注

专用 GPIO 更像是 CPU 外设，因此与 CPU 内核关系密切。强烈建议在 pin-to-core 任务中安装和操作 GPIO 捆绑包。例如，如果 GPIO_A 连接到了 CPU_0，而专用的 GPIO 指令却是从 CPU_1 发出的，那么就无法控制 GPIO_A。

安装 GPIO 捆绑包需要调用 `dedic_gpio_new_bundle()` 来分配软件资源并将专用通道连接到用户选择的 GPIO。GPIO 捆绑包的配置在 `dedic_gpio_bundle_config_t` 结构体中：

- `dedic_gpio_bundle_config_t::gpio_array`：包含 GPIO 编号的数组。

- `dedic_gpio_bundle_config_t::array_size`: `dedic_gpio_bundle_config_t::gpio_array` 的元素个数。

- `dedic_gpio_bundle_config_t::in_en` 和 `dedic_gpio_bundle_config_t::out_en` 用于选择是否开启输入输出功能（这两个功能可以同时开启）。

- `dedic_gpio_bundle_config_t::in_invert` 和 `dedic_gpio_bundle_config_t::out_invert` 用于选择是否反转 GPIO 信号。

以下代码展示了如何安装只有输出功能的 GPIO 捆绑包：

    // 创建 bundleA，仅输出
    dedic_gpio_bundle_handle_t bundleA = NULL;
    dedic_gpio_bundle_config_t bundleA_config = {
        .gpio_array = bundleA_gpios,
        .array_size = sizeof(bundleA_gpios) / sizeof(bundleA_gpios[0]),
        .flags = {
            .out_en = 1,
        },
    };
    ESP_ERROR_CHECK(dedic_gpio_new_bundle(&bundleA_config, &bundleA));

如需卸载 GPIO 捆绑包，可调用 `dedic_gpio_del_bundle()`。

## GPIO 捆绑包操作

操作 | 函数
--- | ---
以掩码的方式指定 GPIO 捆绑包的输出 | `dedic_gpio_bundle_write()`
读取 GPIO 捆绑包实际输出的电平值 | `dedic_gpio_bundle_read_out()`
读取 GPIO 捆绑包中输入的电平值 | `dedic_gpio_bundle_read_in()`

备注

由于函数调用的开销和内部涉及的位操作，使用上述函数可能无法获得较高的 GPIO 翻转速度。用户可以尝试 通过编写汇编代码操作 GPIO 来减少开销，但应自行注意线程安全。

## 通过编写汇编代码操作 GPIO

高阶用户可以通过编写汇编代码或调用 CPU 低层 API 来操作 GPIO。常见步骤为：

1. 分配一个 GPIO 捆绑包：`dedic_gpio_new_bundle()`

2. 查询该包占用的掩码：`dedic_gpio_get_out_mask()` 和/或 `dedic_gpio_get_in_mask()`

3. 调用 CPU LL apis（如 cpu_ll_write_dedic_gpio_mask）或使用该掩码编写汇编代码

4. 切换 IO 的最快捷方式是使用专用的“设置/清除”指令：

   > - 设置 GPIO 位：`set_bit_gpio_out imm[7:0]`

   > - 清除 GPIO 位：`clr_bit_gpio_out imm[7:0]`

   > - 注意：立即数宽度取决于专用 GPIO 通道的数量

有关支持的专用 GPIO 指令的详细信息，请参考 ESP32-S2 技术参考手册 > IO MUX 和 GPIO 矩阵 (GPIO, IO_MUX) [PDF].

一些专用的 CPU 指令也包含在 `hal/dedic_gpio_cpu_ll.h` 中，作为辅助内联函数。

备注

由于自定义指令在不同目标上可能会有不同的格式，在应用程序中编写汇编代码可能会让代码难以在不同的芯片架构之间移植。

## 中断处理

专用 GPIO 还可以在特定输入事件时触发中断。`dedic_gpio_intr_type_t` 定义了所有支持的事件。

可以通过调用 `dedic_gpio_bundle_set_interrupt_and_callback()` 来启用和注册中断回调。`dedic_gpio_isr_callback_t` 定义了回调函数的原型。如果唤醒了一些高优先级任务，回调应该返回 true。

    // 用户定义 ISR 回调
    IRAM_ATTR bool dedic_gpio_isr_callback(dedic_gpio_bundle_handle_t bundle, uint32_t index, void *args)
    {
        SemaphoreHandle_t sem = (SemaphoreHandle_t)args;
        BaseType_t high_task_wakeup = pdFALSE;
        xSemaphoreGiveFromISR(sem, &high_task_wakeup);
        return high_task_wakeup == pdTRUE;
    }

    // 在捆绑包中的第二个 GPIO 上（即索引为 1）启用上升沿中断
    ESP_ERROR_CHECK(dedic_gpio_bundle_set_interrupt_and_callback(bundle, BIT(1), DEDIC_GPIO_INTR_POS_EDGE, dedic_gpio_isr_callback, sem));

    // 等待完成信号量
    xSemaphoreTake(sem, portMAX_DELAY);

## 应用示例

- 通过汇编代码使用专用的 CPU 指令来操作 GPIO 以模拟 UART/I2C/SPI 协议（Bit Banging） peripherals/dedicated_gpio.

- peripherals/gpio/matrix_keyboard 演示了如何使用专用 GPIO API 驱动矩阵键盘，例如改变 GPIO 的电平、触发边缘中断以及读取 GPIO 的电平。

- peripherals/dedicated_gpio/soft_i2c 演示了如何配置和使用专用/快速 GPIO 来模拟 I2C 主机设备、执行总线上的读写操作、以及通过将某些函数放在 IRAM 中来满足严格的时序要求。

- peripherals/dedicated_gpio/soft_uart 演示了如何使用专用/快速 GPIO 在 ESP32-S2 上模拟 UART 总线。可以通过 TX 管脚和 RX 管脚在 UART 总线上发送和接收字符，还可以通过 menuconfig 来调整波特率和其他配置。

## API 参考

### Header File

- components/esp_driver_gpio/include/driver/dedic_gpio.h

- This header file can be included with:

  >     #include "driver/dedic_gpio.h"

- This header file is a part of the API provided by the `esp_driver_gpio` component. To declare that your component depends on `esp_driver_gpio`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_gpio

  > or

  >     PRIV_REQUIRES esp_driver_gpio

### Functions

esp_err_t dedic_gpio_get_out_mask(dedic_gpio_bundle_handle_t bundle, uint32_t *mask)

  Get allocated channel mask.

  备注

  Each bundle should have at least one mask (in or/and out), based on bundle configuration.

  备注

  With the returned mask, user can directly invoke LL function like "dedic_gpio_cpu_ll_write_mask" or write assembly code with dedicated GPIO instructions, to get better performance on GPIO manipulation.

  参数:

    - bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

    - mask -- [out] Returned mask value for on specific direction (in or out)

  返回:

    - ESP_OK: Get channel mask successfully

    - ESP_ERR_INVALID_ARG: Get channel mask failed because of invalid argument

    - ESP_FAIL: Get channel mask failed because of other error

esp_err_t dedic_gpio_get_in_mask(dedic_gpio_bundle_handle_t bundle, uint32_t *mask)

esp_err_t dedic_gpio_get_out_offset(dedic_gpio_bundle_handle_t bundle, uint32_t *offset)

  Get the channel offset of the GPIO bundle.

  A GPIO bundle maps the GPIOS of a particular direction to a consecutive set of channels within a particular GPIO bank of a particular CPU. This function returns the offset to the bundle's first channel of a particular direction within the bank.

  参数:

    - bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

    - offset -- [out] Offset value to the first channel of a specific direction (in or out)

  返回:

    - ESP_OK: Get channel offset successfully

    - ESP_ERR_INVALID_ARG: Get channel offset failed because of invalid argument

    - ESP_FAIL: Get channel offset failed because of other error

esp_err_t dedic_gpio_get_in_offset(dedic_gpio_bundle_handle_t bundle, uint32_t *offset)

esp_err_t dedic_gpio_new_bundle(const dedic_gpio_bundle_config_t *config, dedic_gpio_bundle_handle_t *ret_bundle)

  Create GPIO bundle and return the handle.

  备注

  One has to enable at least input or output mode in "config" parameter.

  参数:

    - config -- [in] Configuration of GPIO bundle

    - ret_bundle -- [out] Returned handle of the new created GPIO bundle

  返回:

    - ESP_OK: Create GPIO bundle successfully

    - ESP_ERR_INVALID_ARG: Create GPIO bundle failed because of invalid argument

    - ESP_ERR_NO_MEM: Create GPIO bundle failed because of no capable memory

    - ESP_ERR_NOT_FOUND: Create GPIO bundle failed because of no enough continuous dedicated channels

    - ESP_FAIL: Create GPIO bundle failed because of other error

esp_err_t dedic_gpio_del_bundle(dedic_gpio_bundle_handle_t bundle)

  Destroy GPIO bundle.

  参数:

    bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

  返回:

    - ESP_OK: Destroy GPIO bundle successfully

    - ESP_ERR_INVALID_ARG: Destroy GPIO bundle failed because of invalid argument

    - ESP_FAIL: Destroy GPIO bundle failed because of other error

void dedic_gpio_bundle_write(dedic_gpio_bundle_handle_t bundle, uint32_t mask, uint32_t value)

  Write value to GPIO bundle.

  备注

  The mask is seen from the view of GPIO bundle. For example, bundleA contains [GPIO10, GPIO12, GPIO17], to set GPIO17 individually, the mask should be 0x04.

  备注

  For performance reasons, this function doesn't check the validity of any parameters, and is placed in IRAM.

  参数:

    - bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

    - mask -- [in] Mask of the GPIOs to be written in the given bundle

    - value -- [in] Value to write to given GPIO bundle, low bit represents low member in the bundle

uint32_t dedic_gpio_bundle_read_out(dedic_gpio_bundle_handle_t bundle)

  Read the value that output from the given GPIO bundle.

  备注

  For performance reasons, this function doesn't check the validity of any parameters, and is placed in IRAM.

  参数:

    bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

  返回:

    Value that output from the GPIO bundle, low bit represents low member in the bundle

uint32_t dedic_gpio_bundle_read_in(dedic_gpio_bundle_handle_t bundle)

  Read the value that input to the given GPIO bundle.

  备注

  For performance reasons, this function doesn't check the validity of any parameters, and is placed in IRAM.

  参数:

    bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

  返回:

    Value that input to the GPIO bundle, low bit represents low member in the bundle

esp_err_t dedic_gpio_bundle_set_interrupt_and_callback(dedic_gpio_bundle_handle_t bundle, uint32_t mask, dedic_gpio_intr_type_t intr_type, dedic_gpio_isr_callback_t cb_isr, void *cb_args)

  Set interrupt and callback function for GPIO bundle.

  备注

  This function is only valid for bundle with input mode enabled. See "dedic_gpio_bundle_config_t"

  备注

  The mask is seen from the view of GPIO Bundle. For example, bundleA contains [GPIO10, GPIO12, GPIO17], to set GPIO17 individually, the mask should be 0x04.

  参数:

    - bundle -- [in] Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

    - mask -- [in] Mask of the GPIOs in the given bundle

    - intr_type -- [in] Interrupt type, set to DEDIC_GPIO_INTR_NONE can disable interrupt

    - cb_isr -- [in] Callback function, which got invoked in ISR context. A NULL pointer here will bypass the callback

    - cb_args -- [in] User defined argument to be passed to the callback function

  返回:

    - ESP_OK: Set GPIO interrupt and callback function successfully

    - ESP_ERR_INVALID_ARG: Set GPIO interrupt and callback function failed because of invalid argument

    - ESP_FAIL: Set GPIO interrupt and callback function failed because of other error

### Structures

struct dedic_gpio_bundle_config_t

  Type of Dedicated GPIO bundle configuration.

  Public Members

  const int *gpio_array

    Array of GPIO numbers, gpio_array[0] ~ gpio_array[size-1] <=> low_dedic_channel_num ~ high_dedic_channel_num

  size_t array_size

    Number of GPIOs in gpio_array

  unsigned int in_en

    Enable input

  unsigned int in_invert

    Invert input signal

  unsigned int out_en

    Enable output

  unsigned int out_invert

    Invert output signal

  struct dedic_gpio_bundle_config_t flags

    Flags to control specific behaviour of GPIO bundle

### Type Definitions

typedef struct dedic_gpio_bundle_t *dedic_gpio_bundle_handle_t

  Type of Dedicated GPIO bundle.

typedef bool (*dedic_gpio_isr_callback_t)(dedic_gpio_bundle_handle_t bundle, uint32_t index, void *args)

  Type of dedicated GPIO ISR callback function.

  Param bundle:

    Handle of GPIO bundle that returned from "dedic_gpio_new_bundle"

  Param index:

    Index of the GPIO in its corresponding bundle (count from 0)

  Param args:

    User defined arguments for the callback function. It's passed through `dedic_gpio_bundle_set_interrupt_and_callback`

  Return:

    If a high priority task is woken up by the callback function

### Enumerations

enum dedic_gpio_intr_type_t

  Supported type of dedicated GPIO interrupt.

  Values:

  enumerator DEDIC_GPIO_INTR_NONE

    No interrupt

  enumerator DEDIC_GPIO_INTR_LOW_LEVEL

    Interrupt on low level

  enumerator DEDIC_GPIO_INTR_HIGH_LEVEL

    Interrupt on high level

  enumerator DEDIC_GPIO_INTR_NEG_EDGE

    Interrupt on negedge

  enumerator DEDIC_GPIO_INTR_POS_EDGE

    Interrupt on posedge

  enumerator DEDIC_GPIO_INTR_BOTH_EDGE

    Interrupt on both negedge and posedge

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。