事件任务矩阵 (ETM)
简介
如果外设 X 需要向外设 Y 发起事件通知,一般只能通过 CPU 中断实现。在此过程中,CPU 会代表外设 X,给外设 Y 发送通知。然而,在对时间敏感的应用程序中,CPU 中断引发的延迟不容忽视。
通过引入事件任务矩阵 (ETM) 模块,部分外设可以直接通过预先设置的连接关系,将事件通知发送给其他外设,无需 CPU 中断介入。由此,外设实现精确、低延迟同步,并减轻 CPU 负担。
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-C61 中,存在许多相同的 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_config_t::edge
或gpio_etm_event_config_t::edges
决定触发事件的边沿类型,支持的边沿类型已在gpio_etm_event_edge_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 管脚的属性,如方向、高/低电平模式等。
其他外设事件
调用
esp_systick_new_etm_alarm_event()
可以从 RTOS Systick 获取 ETM 事件句柄,每个 CPU 核心可以获取一个事件句柄。要了解如何从 esp_timer 获取 ETM 事件句柄,请参阅 ESP 定时器(高分辨率定时器)。
要了解如何从 GPTimer 获取 ETM 事件句柄,请参阅 通用定时器。
要了解如何从 async memcpy 获取 ETM 事件句柄,请参阅 异步内存复制。
要了解如何从 I2S 获取 ETM 事件句柄,请参阅 I2S。
ETM 任务
ETM 任务对其操作进行了抽象,在软件中表示为 esp_etm_task_handle_t
,使任务得以用同一方式管理和表示。ETM 任务可以分配给不同外设,因此获取任务句柄的方式因外设而异。当不再需要某个任务时,请调用 esp_etm_channel_connect()
,并传递一个 NULL
事件句柄,断开与任务的连接,随后调用 esp_etm_del_event()
,释放任务资源。
GPIO 任务
GPIO 任务是最常见的任务类型。一个 GPIO 可以采取一个或多个 GPIO 操作,而一个 GPIO 任务也可以同时管理多个 GPIO 管脚。当 ETM 通道激活任务时,任务可以同时设置管理的所有 GPIO 引脚,使其设置/清除/切换状态。要创建 GPIO 任务句柄,请调用 gpio_new_etm_task()
,并使用 gpio_etm_task_config_t
提供的配置信息:
gpio_etm_task_config_t::action
或gpio_etm_task_config_t::actions
决定 ETM 任务将采取的 GPIO 操作,支持的操作类型在gpio_etm_task_action_t
中列出。如果一个 GPIO 需要采取多个 GPIO 操作,这些操作任务的创建必须通过配置gpio_etm_task_config_t::actions
的数组并在一次gpio_new_etm_task()
调用中一并完成。
接下来,需要连接 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 管脚。
其他外设任务
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
文件中定义。
电源管理
当启用电源管理时,即 CONFIG_PM_ENABLE 打开的时候,系统可能会调整或禁用时钟源,并在进入睡眠前关闭 ETM 外设依赖的电源。这会导致事件和任务之间的连接信息被丢失,ETM 通道在唤醒后无法正常工作。因此,默认情况下,驱动程序会获取电源管理锁,以禁止系统关闭 ETM 外设。
如果你想节省更多电量,可以将 esp_etm_channel_config_t::etm_chan_flags::allow_pd
设置为 true
。ETM 寄存器将在睡眠前备份,并在唤醒后恢复。请注意,启用此选项会增加内存消耗,用于保存寄存器上下文。
线程安全
ETM 驱动程序会确保工厂函数 esp_etm_new_channel()
和 gpio_new_etm_task()
的线程安全。使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。
在 ISR 环境中,不支持运行任何函数。
其他以 esp_etm_channel_handle_t
、esp_etm_task_handle_t
和 esp_etm_event_handle_t
作为首个位置参数的函数,则非线程安全,应避免从不同任务中调用此类函数。
Kconfig 选项
CONFIG_ETM_ENABLE_DEBUG_LOG 用于启用调试日志输出,启用此选项将增加固件的二进制文件大小。
API 参考
Header File
This header file can be included with:
#include "esp_etm.h"
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.
Public Members
-
struct esp_etm_channel_config_t::etm_chan_flags flags
ETM channel flags
-
struct esp_etm_channel_config_t::etm_chan_flags flags
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
This header file can be included with:
#include "driver/gpio_etm.h"
This header file is a part of the API provided by the
esp_driver_gpio
component. To declare that your component depends onesp_driver_gpio
, add the following to your CMakeLists.txt:REQUIRES esp_driver_gpio
or
PRIV_REQUIRES esp_driver_gpio
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备注
Every success call to this function will acquire a free GPIO ETM event channel
- 参数
config -- [in] GPIO ETM event configuration
ret_event -- [out] Returned ETM event handle
... -- [out] Other returned ETM event handles if any (the order of the returned event handles is aligned with the array order in field
edges
ingpio_etm_event_config_t
)
- 返回
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.备注
Every success call to this function will acquire a free GPIO ETM task channel
- 参数
config -- [in] GPIO ETM task configuration
ret_task -- [out] Returned ETM task handle
... -- [out] Other returned ETM task handles if any (the order of the returned task handles is aligned with the array order in field
actions
ingpio_etm_task_config_t
)
- 返回
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.
If more than one kind of ETM edge event want to be triggered on the same GPIO pin, you can configure them together. It helps to save GPIO ETM event channel resources for other GPIOs.
Public Members
-
gpio_etm_event_edge_t edge
Which kind of edge can trigger the ETM event module
-
gpio_etm_event_edge_t edges[GPIO_ETM_EVENT_EDGE_TYPES]
Array of kinds of edges to trigger the ETM event module on the same GPIO
-
gpio_etm_event_edge_t edge
-
struct gpio_etm_task_config_t
GPIO ETM task configuration.
If multiple actions wants to be added to the same GPIO pin, you have to configure all the GPIO ETM tasks together.
Public Members
-
gpio_etm_task_action_t action
Action to take by the ETM task module
-
gpio_etm_task_action_t actions[GPIO_ETM_TASK_ACTION_TYPES]
Array of actions to take by the ETM task module on the same GPIO
-
gpio_etm_task_action_t action
Macros
-
GPIO_ETM_EVENT_EDGE_TYPES
GPIO ETM edge events are POS/NEG/ANY
-
GPIO_ETM_TASK_ACTION_TYPES
GPIO ETM action tasks are SET/CLEAR/TOGGLE
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
-
enumerator GPIO_ETM_EVENT_EDGE_POS
Header File
This header file can be included with:
#include "esp_systick_etm.h"
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-C61 技术参考手册 > 事件任务矩阵 (ETM) [PDF]。驱动程序对通道申请数量不做限制,但当硬件资源用尽时,驱动程序将返回错误。因此,每次进行通道分配(即调用
esp_etm_new_channel()
)时,请注意检查返回值。