事件任务矩阵 (ETM)

[English]

简介

如果外设 X 需要向外设 Y 发起事件通知,一般只能通过 CPU 中断实现。在此过程中,CPU 会代表外设 X,给外设 Y 发送通知。然而,在对时间敏感的应用程序中,CPU 中断引发的延迟不容忽视。

通过引入事件任务矩阵 (ETM) 模块,部分外设可以直接通过预先设置的连接关系,将事件通知发送给其他外设,无需 CPU 中断介入。由此,外设实现精确、低延迟同步,并减轻 CPU 负担。

ETM 通道概述

ETM 模块具有多个通道,这些通道支持用户根据需要进行配置,连接特定 事件任务。激活某一事件时,ETM 将自动触发相应任务。

支持 ETM 功能的外设向 ETM 提供其事件和任务的连接关系。ETM 通道可以连接任意事件和任务,事件和任务甚至可以来自于同一个外设。然而,对于一个 ETM 通道,一次只能将一个事件与一个任务连接(即 1 对 1 关系)。如果要使用不同事件触发同一任务,则需申请多条 ETM 通道。

使用 ETM 通常可实现以下功能:

  • 当定时器报警事件发生时,翻转 GPIO 电平

  • 当在 GPIO 上监测到脉冲边沿时,启动 ADC 转换

功能概述

下文将分节概述 ETM 的功能,并介绍配置和使用 ETM 模块的基本步骤:

  • ETM 通道分配 - 介绍如何安装和卸载 ETM 通道。

  • ETM 事件 - 介绍如何分配新的 ETM 事件句柄,以及如何从不同外设获取现有句柄。

  • ETM 任务 - 介绍如何分配新的 ETM 任务句柄,以及如何从不同外设获取现有句柄。

  • ETM 通道控制 - 介绍常见的 ETM 通道控制函数。

  • 线程安全 - 列出了驱动程序中始终线程安全的 API。

  • Kconfig 选项 - 列出了 ETM 支持的 Kconfig 选项,这些选项对驱动程序的行为会产生不同影响。

ETM 通道分配

在 ESP32-C6 中,存在许多相同的 ETM 通道 1,各通道在软件中由 esp_etm_channel_handle_t 表示。可用硬件资源汇集在资源池内,由 ETM 核心驱动程序管理,无需手动管理通道的分配和释放。ETM 核心驱动程序会在调用 esp_etm_new_channel() 时自动分配通道,在调用 esp_etm_del_channel() 时删除通道。分配通道的要求通过 esp_etm_channel_config_t 配置。

在删除 ETM 通道前,请调用 esp_etm_channel_disable() 禁用要删除的通道,或确保该通道尚未由 esp_etm_channel_enable() 启用,再继续删除操作。

ETM 事件

ETM 事件对其事件源进行了抽象,屏蔽了具体事件源的细节,并在软件中表示为 esp_etm_event_handle_t,使应用程序可以更便捷地处理不同类型的事件。ETM 事件可以由各种外设产生,因此获取事件句柄的方法因外设而异。当不再需要某个事件时,请调用 esp_etm_channel_connect(),并传递一个 NULL 事件句柄,断开与事件的连接,随后调用 esp_etm_del_event(),释放事件资源。

GPIO 事件

GPIO 边沿 事件是最常见的事件类型,任何 GPIO 管脚均可触发这类事件。要创建 GPIO 事件句柄,请调用 gpio_new_etm_event(),并使用 gpio_etm_event_config_t 提供的配置信息:

接下来,请调用 gpio_etm_event_bind_gpio() 函数,连接 GPIO ETM 事件句柄与 GPIO 管脚。注意,要设置 GPIO 管脚,只能使用由 gpio_new_etm_event() 函数创建的 ETM 事件句柄。对于其他类型的 ETM 事件,调用此函数,将返回 ESP_ERR_INVALID_ARG 错误。该函数也无法完成 GPIO 的初始化,在使用 GPIO ETM 事件之前,仍需调用 gpio_config() 函数,设置 GPIO 管脚的属性,如方向、高/低电平模式等。

其他外设事件

ETM 任务

ETM 任务对其操作进行了抽象,在软件中表示为 esp_etm_task_handle_t,使任务得以用同一方式管理和表示。ETM 任务可以分配给不同外设,因此获取任务句柄的方式因外设而异。当不再需要某个任务时,请调用 esp_etm_channel_connect(),并传递一个 NULL 事件句柄,断开与任务的连接,随后调用 esp_etm_del_event(),释放任务资源。

GPIO 任务

GPIO 任务是最常见的任务类型,一个 GPIO 任务可以同时管理多个 GPIO 管脚。当 ETM 通道激活任务时,任务可以同时设置管理的所有 GPIO 引脚,使其设置/清除/切换状态。要创建 GPIO 任务句柄,请调用 gpio_new_etm_task(),并使用 gpio_etm_task_config_t 提供的配置信息:

接下来,需要连接 GPIO ETM 任务句柄与 GPIO 管脚。为此,请调用 gpio_etm_task_add_gpio() 函数。如果需要任务句柄管理更多的 GPIO 管脚,可以重复调用以上函数,注意,要设置 GPIO 管脚,只能使用由 gpio_new_etm_task() 函数创建的 ETM 任务句柄。对于其他类型的 ETM 任务,调用此函数,将返回 ESP_ERR_INVALID_ARG 错误。该函数也无法完成 GPIO 的初始化,在使用 GPIO ETM 任务之前,仍需调用 gpio_config() 函数,设置 GPIO 管脚的属性,如方向、高/低电平模式等。

要删除 GPIO ETM 任务,请调用 esp_etm_del_task()。在此之前,请确保已经调用过 gpio_etm_task_rm_gpio(),删除了所有先前添加的 GPIO 管脚。

其他外设任务

  • 要了解如何从 GPTimer 获取 ETM 任务句柄,请参阅 通用定时器

ETM 通道控制

映射事件与任务

在调用 esp_etm_channel_connect() 将它们连接到同一个 ETM 通道之前,ETM 事件与 ETM 任务之间没有任何映射关系。注意,使用 NULL 任务/事件句柄调用该函数时,会将通道与任何任务或事件解除映射。此函数可以在通道启用之前或之后调用,但在运行时调用此函数更改映射关系存在一定风险,因为此时通道可能正处于周期的中间阶段,新的映射可能无法立即生效。

启用及禁用通道

调用 esp_etm_channel_enable() 启用 ETM 通道,调用 esp_etm_channel_disable() 禁用 ETM 通道。

ETM 通道分析

要检查是否为 ETM 通道设置了正确的事件和任务,可以调用 esp_etm_dump(),输出所有工作中的 ETM 通道及其关联的事件和任务。输出格式如下:

===========ETM Dump Start==========
channel 0: event 48 ==> task 17
channel 1: event 48 ==> task 90
channel 2: event 48 ==> task 94
===========ETM Dump End============

以上输出信息打印的数字 ID 在 soc/soc_etm_source.h 文件中定义。

线程安全

ETM 驱动程序会确保工厂函数 esp_etm_new_channel()gpio_new_etm_task() 的线程安全。使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。

在 ISR 环境中,不支持运行任何函数。

其他以 esp_etm_channel_handle_tesp_etm_task_handle_tesp_etm_event_handle_t 作为首个位置参数的函数,则非线程安全,应避免从不同任务中调用此类函数。

Kconfig 选项

API 参考

Header File

Functions

esp_err_t esp_etm_new_channel(const esp_etm_channel_config_t *config, esp_etm_channel_handle_t *ret_chan)

Allocate an ETM channel.

备注

The channel can later be freed by esp_etm_del_channel

参数
  • config -- [in] ETM channel configuration

  • ret_chan -- [out] Returned ETM channel handle

返回

  • ESP_OK: Allocate ETM channel successfully

  • ESP_ERR_INVALID_ARG: Allocate ETM channel failed because of invalid argument

  • ESP_ERR_NO_MEM: Allocate ETM channel failed because of out of memory

  • ESP_ERR_NOT_FOUND: Allocate ETM channel failed because all channels are used up and no more free one

  • ESP_FAIL: Allocate ETM channel failed because of other reasons

esp_err_t esp_etm_del_channel(esp_etm_channel_handle_t chan)

Delete an ETM channel.

参数

chan -- [in] ETM channel handle that created by esp_etm_new_channel

返回

  • ESP_OK: Delete ETM channel successfully

  • ESP_ERR_INVALID_ARG: Delete ETM channel failed because of invalid argument

  • ESP_FAIL: Delete ETM channel failed because of other reasons

esp_err_t esp_etm_channel_enable(esp_etm_channel_handle_t chan)

Enable ETM channel.

备注

This function will transit the channel state from init to enable.

参数

chan -- [in] ETM channel handle that created by esp_etm_new_channel

返回

  • ESP_OK: Enable ETM channel successfully

  • ESP_ERR_INVALID_ARG: Enable ETM channel failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Enable ETM channel failed because the channel has been enabled already

  • ESP_FAIL: Enable ETM channel failed because of other reasons

esp_err_t esp_etm_channel_disable(esp_etm_channel_handle_t chan)

Disable ETM channel.

备注

This function will transit the channel state from enable to init.

参数

chan -- [in] ETM channel handle that created by esp_etm_new_channel

返回

  • ESP_OK: Disable ETM channel successfully

  • ESP_ERR_INVALID_ARG: Disable ETM channel failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Disable ETM channel failed because the channel is not enabled yet

  • ESP_FAIL: Disable ETM channel failed because of other reasons

esp_err_t esp_etm_channel_connect(esp_etm_channel_handle_t chan, esp_etm_event_handle_t event, esp_etm_task_handle_t task)

Connect an ETM event to an ETM task via a previously allocated ETM channel.

备注

Setting the ETM event/task handle to NULL means to disconnect the channel from any event/task

参数
  • chan -- [in] ETM channel handle that created by esp_etm_new_channel

  • event -- [in] ETM event handle obtained from a driver/peripheral, e.g. xxx_new_etm_event

  • task -- [in] ETM task handle obtained from a driver/peripheral, e.g. xxx_new_etm_task

返回

  • ESP_OK: Connect ETM event and task to the channel successfully

  • ESP_ERR_INVALID_ARG: Connect ETM event and task to the channel failed because of invalid argument

  • ESP_FAIL: Connect ETM event and task to the channel failed because of other reasons

esp_err_t esp_etm_del_event(esp_etm_event_handle_t event)

Delete ETM event.

备注

Although the ETM event comes from various peripherals, we provide the same user API to delete the event handle seamlessly.

参数

event -- [in] ETM event handle obtained from a driver/peripheral, e.g. xxx_new_etm_event

返回

  • ESP_OK: Delete ETM event successfully

  • ESP_ERR_INVALID_ARG: Delete ETM event failed because of invalid argument

  • ESP_FAIL: Delete ETM event failed because of other reasons

esp_err_t esp_etm_del_task(esp_etm_task_handle_t task)

Delete ETM task.

备注

Although the ETM task comes from various peripherals, we provide the same user API to delete the task handle seamlessly.

参数

task -- [in] ETM task handle obtained from a driver/peripheral, e.g. xxx_new_etm_task

返回

  • ESP_OK: Delete ETM task successfully

  • ESP_ERR_INVALID_ARG: Delete ETM task failed because of invalid argument

  • ESP_FAIL: Delete ETM task failed because of other reasons

esp_err_t esp_etm_dump(FILE *out_stream)

Dump ETM channel usages to the given IO stream.

参数

out_stream -- [in] IO stream (e.g. stdout)

返回

  • ESP_OK: Dump ETM channel usages successfully

  • ESP_ERR_INVALID_ARG: Dump ETM channel usages failed because of invalid argument

  • ESP_FAIL: Dump ETM channel usages failed because of other reasons

Structures

struct esp_etm_channel_config_t

ETM channel configuration.

Type Definitions

typedef struct esp_etm_channel_t *esp_etm_channel_handle_t

ETM channel handle.

typedef struct esp_etm_event_t *esp_etm_event_handle_t

ETM event handle.

typedef struct esp_etm_task_t *esp_etm_task_handle_t

ETM task handle.

Header File

  • components/driver/gpio/include/driver/gpio_etm.h

  • This header file can be included with:

    #include "driver/gpio_etm.h"
    
  • This header file is a part of the API provided by the driver component. To declare that your component depends on driver, add the following to your CMakeLists.txt:

    REQUIRES driver
    

    or

    PRIV_REQUIRES driver
    

Functions

esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event)

Create an ETM event object for the GPIO peripheral.

备注

The created ETM event object can be deleted later by calling esp_etm_del_event

备注

The newly created ETM event object is not bind to any GPIO, you need to call gpio_etm_event_bind_gpio to bind the wanted GPIO

参数
  • config -- [in] GPIO ETM event configuration

  • ret_event -- [out] Returned ETM event handle

返回

  • ESP_OK: Create ETM event successfully

  • ESP_ERR_INVALID_ARG: Create ETM event failed because of invalid argument

  • ESP_ERR_NO_MEM: Create ETM event failed because of out of memory

  • ESP_ERR_NOT_FOUND: Create ETM event failed because all events are used up and no more free one

  • ESP_FAIL: Create ETM event failed because of other reasons

esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num)

Bind the GPIO with the ETM event.

备注

Calling this function multiple times with different GPIO number can override the previous setting immediately.

备注

Only GPIO ETM object can call this function

参数
  • event -- [in] ETM event handle that created by gpio_new_etm_event

  • gpio_num -- [in] GPIO number that can trigger the ETM event

返回

  • ESP_OK: Set the GPIO for ETM event successfully

  • ESP_ERR_INVALID_ARG: Set the GPIO for ETM event failed because of invalid argument, e.g. GPIO is not input capable, ETM event is not of GPIO type

  • ESP_FAIL: Set the GPIO for ETM event failed because of other reasons

esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task)

Create an ETM task object for the GPIO peripheral.

备注

The created ETM task object can be deleted later by calling esp_etm_del_task

备注

The GPIO ETM task works like a container, a newly created ETM task object doesn't have GPIO members to be managed. You need to call gpio_etm_task_add_gpio to put one or more GPIOs to the container.

参数
  • config -- [in] GPIO ETM task configuration

  • ret_task -- [out] Returned ETM task handle

返回

  • ESP_OK: Create ETM task successfully

  • ESP_ERR_INVALID_ARG: Create ETM task failed because of invalid argument

  • ESP_ERR_NO_MEM: Create ETM task failed because of out of memory

  • ESP_ERR_NOT_FOUND: Create ETM task failed because all tasks are used up and no more free one

  • ESP_FAIL: Create ETM task failed because of other reasons

esp_err_t gpio_etm_task_add_gpio(esp_etm_task_handle_t task, int gpio_num)

Add GPIO to the ETM task.

备注

You can call this function multiple times to add more GPIOs

备注

Only GPIO ETM object can call this function

参数
  • task -- [in] ETM task handle that created by gpio_new_etm_task

  • gpio_num -- [in] GPIO number that can be controlled by the ETM task

返回

  • ESP_OK: Add GPIO to the ETM task successfully

  • ESP_ERR_INVALID_ARG: Add GPIO to the ETM task failed because of invalid argument, e.g. GPIO is not output capable, ETM task is not of GPIO type

  • ESP_ERR_INVALID_STATE: Add GPIO to the ETM task failed because the GPIO is used by other ETM task already

  • ESP_FAIL: Add GPIO to the ETM task failed because of other reasons

esp_err_t gpio_etm_task_rm_gpio(esp_etm_task_handle_t task, int gpio_num)

Remove the GPIO from the ETM task.

备注

Before deleting the ETM task, you need to remove all the GPIOs from the ETM task by this function

备注

Only GPIO ETM object can call this function

参数
  • task -- [in] ETM task handle that created by gpio_new_etm_task

  • gpio_num -- [in] GPIO number that to be remove from the ETM task

返回

  • ESP_OK: Remove the GPIO from the ETM task successfully

  • ESP_ERR_INVALID_ARG: Remove the GPIO from the ETM task failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Remove the GPIO from the ETM task failed because the GPIO is not controlled by this ETM task

  • ESP_FAIL: Remove the GPIO from the ETM task failed because of other reasons

Structures

struct gpio_etm_event_config_t

GPIO ETM event configuration.

Public Members

gpio_etm_event_edge_t edge

Which kind of edge can trigger the ETM event module

struct gpio_etm_task_config_t

GPIO ETM task configuration.

Public Members

gpio_etm_task_action_t action

Which action to take by the ETM task module

Enumerations

enum gpio_etm_event_edge_t

GPIO edges that can be used as ETM event.

Values:

enumerator GPIO_ETM_EVENT_EDGE_POS

A rising edge on the GPIO will generate an ETM event signal

enumerator GPIO_ETM_EVENT_EDGE_NEG

A falling edge on the GPIO will generate an ETM event signal

enumerator GPIO_ETM_EVENT_EDGE_ANY

Any edge on the GPIO can generate an ETM event signal

enum gpio_etm_task_action_t

GPIO actions that can be taken by the ETM task.

Values:

enumerator GPIO_ETM_TASK_ACTION_SET

Set the GPIO level to high

enumerator GPIO_ETM_TASK_ACTION_CLR

Clear the GPIO level to low

enumerator GPIO_ETM_TASK_ACTION_TOG

Toggle the GPIO level

Header File

Functions

esp_err_t esp_systick_new_etm_alarm_event(int core_id, esp_etm_event_handle_t *out_event)

Get the ETM event handle of systick hardware's alarm/heartbeat event.

备注

The created ETM event object can be deleted later by calling esp_etm_del_event

参数
  • core_id -- [in] CPU core ID

  • out_event -- [out] Returned ETM event handle

返回

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

1

不同 ESP 芯片系列的 ETM 通道数量可能不同。要了解更多详情,请参阅 ESP32-C6 技术参考手册 > 事件任务矩阵 (ETM) [PDF]。驱动程序对通道申请数量不做限制,但当硬件资源用尽时,驱动程序将返回错误。因此,每次进行通道分配(即调用 esp_etm_new_channel())时,请注意检查返回值。